ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/root-tail/root-tail.c
(Generate patch)

Comparing root-tail/root-tail.c (file contents):
Revision 1.4 by jebusbfb, Sun May 5 20:31:11 2002 UTC vs.
Revision 1.63 by chris_moore, Thu Apr 15 00:38:13 2004 UTC

1/* 1/*
2 * Copyright 2001 by Marco d'Itri <md@linux.it> 2 * Copyright 2001 by Marco d'Itri <md@linux.it>
3 * Copyright 2000,2001,2002,2003,2004
4 * Marc Lehmann <pcg@goof.com>,
5 * Chris Moore <chris.moore@mail.com>,
6 * and many others, see README
3 * 7 *
4 * Original version by Mike Baker, then maintained by pcg@goof.com. 8 * Original version by Mike Baker.
5 * 9 *
6 * This program is free software; you can redistribute it and/or modify 10 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by 11 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or 12 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version. 13 * (at your option) any later version.
17 * along with this program; if not, write to the Free Software 21 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
19 */ 23 */
20 24
21#include "config.h" 25#include "config.h"
26#include <assert.h>
22#include <stdlib.h> 27#include <stdlib.h>
23#include <stdio.h> 28#include <stdio.h>
24#include <unistd.h> 29#include <unistd.h>
25#include <string.h> 30#include <string.h>
26#include <signal.h> 31#include <signal.h>
28#include <fcntl.h> 33#include <fcntl.h>
29#include <errno.h> 34#include <errno.h>
30#include <sys/time.h> 35#include <sys/time.h>
31#include <sys/stat.h> 36#include <sys/stat.h>
32#include <sys/types.h> 37#include <sys/types.h>
38#include <locale.h>
39#include <ctype.h>
40#include <stdarg.h>
41#include <X11/Xlib.h>
42#include <X11/Xatom.h>
43#include <X11/Xutil.h>
44
33#if HAS_REGEX 45#if HAS_REGEX
34#include <regex.h> 46#include <regex.h>
35#endif 47#endif
36#include <X11/Xlib.h> 48
37#include <X11/Xutil.h> 49#define SHADE_X 2
50#define SHADE_Y 2
51
52/* some italic fonts still go over the margin - this margin of error cleans up the mess */
53#define MARGIN_OF_ERROR 2
38 54
39/* data structures */ 55/* data structures */
40struct logfile_entry { 56struct logfile_entry
41 char *fname; /* name of file */ 57{
42 char *desc; /* alternative description */
43 FILE *fp; /* FILE struct associated with file */
44 ino_t inode; /* inode of the file opened */
45 off_t last_size; /* file size at the last check */
46 unsigned long color; /* color to be used for printing */
47 struct logfile_entry *next; 58 struct logfile_entry *next;
59
60 char *fname; /* name of file */
61 char *desc; /* alternative description */
62 char *buf; /* text read but not yet displayed */
63 const char *fontname;
64 XFontSet fontset;
65 int font_height;
66 int font_ascent;
67 FILE *fp; /* FILE struct associated with file */
68 ino_t inode; /* inode of the file opened */
69 off_t last_size; /* file size at the last check */
70 unsigned long color; /* color to be used for printing */
71 const char *colorname; /* color name/string */
72 int partial; /* true if the last line isn't complete */
73 int lastpartial; /* true if the previous output wasn't complete */
74 struct line_node *last; /* last line we output */
75 int modified; /* true if line is modified & needs displaying */
48}; 76};
49 77
50struct linematrix { 78struct line_node
51 char *line; 79{
52 unsigned long color; 80 struct line_node *next;
81 struct line_node *prev;
82 struct logfile_entry *logfile;
83
84 char *line; /* the text of the line (so far) */
85 int len; /* the length of the line (in bytes) so far */
86 int wrapped_left; /* true if wrapped from the previous line */
87 int wrapped_right; /* true if wrapped to the next line */
88 struct breakinfo *breaks; /* array of indicies to spaces if wrapped_right */
89 int num_words; /* the number of words in the line */
90 int free_pixels; /* the number of free pixels to spread out */
53}; 91};
54 92
93struct breakinfo
94{
95 int index; /* index into string of start of substring */
96 int width; /* width in pixels of start of substring */
97 int len; /* length of substring */
98};
99
100struct displaymatrix
101{
102 char *line;
103 int len;
104 int offset;
105 int buffer_size;
106 unsigned long color;
107};
55 108
56/* global variables */ 109/* global variables */
57unsigned int width = STD_WIDTH, listlen = STD_HEIGHT; 110struct line_node *linelist = NULL, *linelist_tail = NULL;
111struct displaymatrix *display;
112int continuation_width = -1;
113int continuation_color;
114int continuation_length;
115
116/* HACK - ideally listlen will start at however many '~'s will fit on
117 * the screen */
118int width = STD_WIDTH, height = STD_HEIGHT, listlen = 50;
58int win_x = LOC_X, win_y = LOC_Y; 119int win_x = LOC_X, win_y = LOC_Y;
59int w = -1, h = -1, font_width, font_height, font_descent; 120int effect_x_space, effect_y_space; /* how much space does shading / outlining take up */
121int effect_x_offset, effect_y_offset; /* and how does it offset the usable space */
60int do_reopen; 122int do_reopen;
61struct timeval interval = { 2, 400000 }; /* see Knuth */ 123struct timeval interval = { 2, 400000 };
62 124
63/* command line options */ 125/* command line options */
64int opt_noinitial, opt_shade, opt_frame, opt_reverse=0, opt_nofilename, 126int opt_noinitial, opt_shade, opt_frame, opt_reverse, opt_nofilename,
65 geom_mask, reload = 3600; 127 opt_outline, opt_noflicker, opt_whole, opt_update, opt_wordwrap,
128 opt_justify, geom_mask, opt_minspace, reload;
66const char *command = NULL, 129const char *command = NULL,
67 *fontname = USE_FONT, *dispname = NULL, *def_color = DEF_COLOR; 130 *fontname = USE_FONT, *dispname = NULL, *def_color = DEF_COLOR,
131 *continuation = "|| ", *cont_color = DEF_CONT_COLOR;
68 132
69struct logfile_entry *loglist = NULL, *loglist_tail = NULL; 133struct logfile_entry *loglist = NULL, *loglist_tail = NULL;
70 134
71Display *disp; 135Display *disp;
72Window root; 136Window root;
73GC WinGC; 137GC WinGC;
74 138
75#if HAS_REGEX 139#if HAS_REGEX
76struct re_list { 140struct re_list
141{
77 regex_t from; 142 regex_t from;
78 const char *to; 143 const char *to;
79 struct re_list *next; 144 struct re_list *next;
80}; 145};
81struct re_list *re_head, *re_tail; 146struct re_list *re_head, *re_tail;
147char *transform_to = NULL;
148regex_t *transformre;
82#endif 149#endif
83 150
84 151
85/* prototypes */ 152/* prototypes */
86void list_files(int); 153void list_files (int);
87void force_reopen(int); 154void force_reopen (int);
88void force_refresh(int); 155void force_refresh (int);
89void blank_window(int); 156void blank_window (int);
157#ifdef USE_TOON_GET_ROOT_WINDOW
158Window ToonGetRootWindow(Display *, int, Window *);
159#endif /* USE_TOON_GET_ROOT_WINDOW */
90 160
91void InitWindow(void); 161void InitWindow (void);
92unsigned long GetColor(const char *); 162unsigned long GetColor (const char *);
93void redraw(void); 163void redraw (int);
94void refresh(struct linematrix *, int, int); 164void refresh (int, int, int, int);
95 165
96void transform_line(char *s); 166void transform_line (char *s);
97int lineinput(char *, int, FILE *); 167int lineinput (struct logfile_entry *);
98void reopen(void); 168void reopen (void);
99void check_open_files(void); 169void check_open_files (void);
100FILE *openlog(struct logfile_entry *); 170FILE *openlog (struct logfile_entry *);
101void main_loop(void); 171static void main_loop (void);
102 172
103void display_version(void); 173void display_version (void);
104void display_help(char *); 174void display_help (char *);
105void install_signal(int, void (*)(int)); 175void install_signal (int, void (*)(int));
106void *xstrdup(const char *); 176void *xstrdup (const char *);
107void *xmalloc(size_t); 177void *xmalloc (size_t);
178void *xrealloc (void *, size_t);
108int daemonize(void); 179int daemonize (void);
109 180
110/* signal handlers */ 181/* signal handlers */
182void
111void list_files(int dummy) 183list_files (int dummy)
112{ 184{
185 struct logfile_entry *e;
186
187 fprintf (stderr, "Files opened:\n");
188 for (e = loglist; e; e = e->next)
189 fprintf (stderr, "\t%s (%s)\n", e->fname, e->desc);
190}
191
192void
193force_reopen (int dummy)
194{
195 do_reopen = 1;
196}
197
198void
199force_refresh (int dummy)
200{
201 redraw (1);
202}
203
204void
205blank_window (int dummy)
206{
207 XClearArea (disp, root, win_x, win_y, width + MARGIN_OF_ERROR, height, False);
208 XFlush (disp);
209 exit (0);
210}
211
212/* X related functions */
213unsigned long
214GetColor (const char *ColorName)
215{
216 XColor Color;
217 XWindowAttributes Attributes;
218
219 XGetWindowAttributes (disp, root, &Attributes);
220 Color.pixel = 0;
221
222 if (!XParseColor (disp, Attributes.colormap, ColorName, &Color))
223 fprintf (stderr, "can't parse %s\n", ColorName);
224 else if (!XAllocColor (disp, Attributes.colormap, &Color))
225 fprintf (stderr, "can't allocate %s\n", ColorName);
226
227 return Color.pixel;
228}
229
230#ifndef USE_TOON_GET_ROOT_WINDOW
231static void
232find_root_window (Display *display, int screen_number)
233{
234 if (!root)
235 {
236 Atom SWM_VROOT = XInternAtom (display, "__SWM_VROOT", False);
237 Atom NAUTILUS_DESKTOP_WINDOW_ID = XInternAtom (display, "NAUTILUS_DESKTOP_WINDOW_ID", False);
238 root = RootWindow (display, screen_number);
239
240 Window unused, *windows = 0;
241 unsigned int count;
242
243 Atom type;
244 int format;
245 unsigned long nitems, bytes_after_return;
246 unsigned char *virtual_root_window;
247
248 if (XGetWindowProperty (display, root, NAUTILUS_DESKTOP_WINDOW_ID,
249 0, 1, False, XA_WINDOW, &type, &format,
250 &nitems, &bytes_after_return,
251 &virtual_root_window) == Success
252 && type == XA_WINDOW)
253 {
254 if (XQueryTree (display, *(Window *)virtual_root_window, &unused, &unused, &windows, &count))
255 root = windows[count - 1];
256
257 XFree (virtual_root_window);
258 }
259 else if (XQueryTree (display, root, &unused, &unused, &windows, &count))
260 {
261 int i;
262
263 for (i = 0; i < count; i++)
264 {
265 if (XGetWindowProperty (display, windows[i], SWM_VROOT,
266 0, 1, False, XA_WINDOW, &type, &format,
267 &nitems, &bytes_after_return,
268 &virtual_root_window) == Success
269 && type == XA_WINDOW)
270 {
271 root = *(Window *)virtual_root_window;
272 XFree (virtual_root_window);
273 break;
274 }
275 }
276
277 XFree (windows);
278 }
279 else
280 fprintf (stderr, "Can't query tree on root window 0x%lx", root);
281 }
282}
283#endif /* USE_TOON_GET_ROOT_WINDOW */
284
285void
286InitWindow (void)
287{
288 XGCValues gcv;
289 unsigned long gcm;
290 int screen, ScreenWidth, ScreenHeight;
291 struct logfile_entry *e;
292
293 if (!(disp = XOpenDisplay (dispname)))
294 {
295 fprintf (stderr, "Can't open display %s.\n", dispname);
296 exit (1);
297 }
298
299 screen = DefaultScreen (disp);
300 ScreenHeight = DisplayHeight (disp, screen);
301 ScreenWidth = DisplayWidth (disp, screen);
302
303 find_root_window (disp, screen);
304
305 gcm = GCBackground;
306 gcv.graphics_exposures = True;
307 WinGC = XCreateGC (disp, root, gcm, &gcv);
308 XMapWindow (disp, root);
309
310 XSetForeground (disp, WinGC, GetColor (DEF_COLOR));
311
312 for (e = loglist; e; e = e->next)
313 {
314 char **missing_charset_list;
315 int missing_charset_count;
316 char *def_string;
317
318 e->fontset = XCreateFontSet (disp, e->fontname,
319 &missing_charset_list, &missing_charset_count,
320 &def_string);
321
322 if (missing_charset_count)
323 {
324 fprintf (stderr,
325 "Missing charsets in String to FontSet conversion (%s)\n",
326 missing_charset_list[0]);
327 XFreeStringList (missing_charset_list);
328 }
329
330 if (!e->fontset)
331 {
332 fprintf (stderr, "unable to create fontset for font '%s', exiting.\n", e->fontname);
333 exit (1);
334 }
335
336 {
337 XFontSetExtents *xfe = XExtentsOfFontSet (e->fontset);
338
339 e->font_height = xfe->max_logical_extent.height;
340 e->font_ascent = -xfe->max_logical_extent.y;
341 }
342
343 if (e->font_height > height - effect_y_space)
344 {
345 fprintf(stderr, "\n the display isn't tall enough to display a single line in font '%s'\n",
346 e->fontname);
347 fprintf(stderr, "\n the geometry in use is %d pixels tall\n", height);
348 fprintf(stderr, "\n font '%s' is %d pixels tall\n", e->fontname, e->font_height);
349 if (effect_y_space)
350 fprintf(stderr, "\n the shade or outline options need an extra %d pixel%s of vertical space\n",
351 effect_y_space, effect_y_space == 1 ? "" : "s");
352 fprintf(stderr, "\n");
353 exit(1);
354 }
355 }
356
357 if (geom_mask & XNegative)
358 win_x = win_x + ScreenWidth - width;
359 if (geom_mask & YNegative)
360 win_y = win_y + ScreenHeight - height;
361
362 {
113 struct logfile_entry *e; 363 struct logfile_entry *e;
114 364
115 fprintf(stderr, "Files opened:\n");
116 for (e = loglist; e; e = e->next) 365 for (e = loglist; e; e = e->next)
117 fprintf(stderr, "\t%s (%s)\n", e->fname, e->desc); 366 e->color = GetColor (e->colorname);
118}
119
120void force_reopen(int dummy)
121{
122 do_reopen = 1;
123}
124
125void force_refresh(int dummy)
126{
127 XClearWindow(disp, root);
128 redraw();
129}
130
131void blank_window(int dummy)
132{
133 XClearWindow(disp, root);
134 XFlush(disp);
135 exit(0);
136}
137
138/* X related functions */
139unsigned long GetColor(const char *ColorName)
140{
141 XColor Color;
142 XWindowAttributes Attributes;
143
144 XGetWindowAttributes(disp, root, &Attributes);
145 Color.pixel = 0;
146 if (!XParseColor(disp, Attributes.colormap, ColorName, &Color))
147 fprintf(stderr, "can't parse %s\n", ColorName);
148 else if (!XAllocColor(disp, Attributes.colormap, &Color))
149 fprintf(stderr, "can't allocate %s\n", ColorName);
150 return Color.pixel;
151}
152
153void InitWindow(void)
154{
155 XGCValues gcv;
156 Font font;
157 unsigned long gcm;
158 XFontStruct *info;
159 int screen, ScreenWidth, ScreenHeight;
160
161 if (!(disp = XOpenDisplay(dispname))) {
162 fprintf(stderr, "Can't open display %s.\n", dispname);
163 exit(1);
164 } 367 }
165 screen = DefaultScreen(disp);
166 ScreenHeight = DisplayHeight(disp, screen);
167 ScreenWidth = DisplayWidth(disp, screen);
168 root = RootWindow(disp, screen);
169 gcm = GCBackground;
170 gcv.graphics_exposures = True;
171 WinGC = XCreateGC(disp, root, gcm, &gcv);
172 XMapWindow(disp, root);
173 XSetForeground(disp, WinGC, GetColor(DEF_COLOR));
174 368
175 font = XLoadFont(disp, fontname);
176 XSetFont(disp, WinGC, font);
177 info = XQueryFont(disp, font);
178 font_width = info->max_bounds.width;
179 font_descent = info->max_bounds.descent;
180 font_height = info->max_bounds.ascent + font_descent;
181
182 w = width * font_width;
183 h = listlen * font_height;
184 if (geom_mask & XNegative)
185 win_x = win_x + ScreenWidth - w;
186 if (geom_mask & YNegative)
187 win_y = win_y + ScreenHeight - h;
188
189 XSelectInput(disp, root, ExposureMask|FocusChangeMask); 369 XSelectInput (disp, root, ExposureMask | FocusChangeMask);
190} 370}
191 371
192char * 372/*
193detabificate (char *s) 373 * if redraw () is passwd a non-zero argument, it does a complete
374 * redraw, rather than an update. if the argument is zero (and
375 * -noflicker is in effect) then only the lines which have changed
376 * since the last draw are redrawn.
377 *
378 * the rest is handled by regular refresh ()'es
379 */
380void
381redraw (int redraw_all)
194{ 382{
195 char * out; 383 XSetClipMask (disp, WinGC, None);
384 refresh (0, 32768, 1, redraw_all);
385}
386
387void draw_text (Display *disp, Window root, GC WinGC, int x, int y, struct line_node *line, int foreground)
388{
389 if (line->wrapped_right && opt_justify && line->breaks)
390 {
391 int i;
392 for (i = 0; i < line->num_words; i++)
393 XmbDrawString (disp, root, line->logfile->fontset, WinGC,
394 x + line->breaks[i].width + ((i * line->free_pixels) / (line->num_words - 1))
395 + continuation_width * line->wrapped_left, y,
396 line->line + line->breaks[i].index,
397 line->breaks[i].len);
398
399 if (line->wrapped_left)
400 {
401 if (foreground) XSetForeground (disp, WinGC, continuation_color);
402 XmbDrawString (disp, root, line->logfile->fontset, WinGC, x, y, continuation, continuation_length);
403 }
404 }
405 else
406 {
407 XmbDrawString (disp, root, line->logfile->fontset, WinGC, x + continuation_width * line->wrapped_left,
408 y, line->line, line->len);
409
410 if (line->wrapped_left)
411 {
412 if (foreground) XSetForeground (disp, WinGC, continuation_color);
413 XmbDrawString (disp, root, line->logfile->fontset, WinGC, x, y, continuation, continuation_length);
414 }
415 }
416}
417
418/* Just redraw everything without clearing (i.e. after an EXPOSE event) */
419void
420refresh (int miny, int maxy, int clear, int refresh_all)
421{
422 int lin = 0;
423 int space = height;
424 int offset;
425 unsigned long black_color = GetColor ("black");
426 struct line_node *line;
427 int step_per_line;
428 int foreground = 0;
429
430 if (opt_reverse)
431 offset = effect_y_offset;
432 else
433 offset = height + effect_y_offset;
434
435 miny -= win_y;
436 maxy -= win_y;
437
438 if (clear && !opt_noflicker)
439 XClearArea (disp, root, win_x, win_y, width + MARGIN_OF_ERROR, height, False);
440
441 for (line = linelist; line; line = line->next, lin++)
442 {
443 struct displaymatrix *display_line;
444
445 if (opt_noflicker && lin >= listlen)
446 {
447 int i = listlen;
448 listlen *= 1.5;
449 display = xrealloc(display, listlen * sizeof(struct displaymatrix));
450 for (; i < listlen; i++)
451 {
452 display[i].line = xstrdup ("");
453 display[i].len = 0;
454 display[i].offset = 0;
455 display[i].buffer_size = 0;
456 }
457 }
458
459 display_line = display + lin;
460
461 step_per_line = line->logfile->font_height + effect_y_space;
462 if (step_per_line > space)
463 break;
464
465 if (!opt_reverse)
466 offset -= step_per_line;
467
468 offset += line->logfile->font_ascent;
469
470 miny -= line->logfile->font_height;
471 maxy += line->logfile->font_height;
472
473 if (offset >= miny && offset <= maxy)
474 {
475 /* if this line is a different than it was, then it
476 * needs displaying */
477 if (!opt_noflicker
478 || refresh_all
479 || display_line->len != line->len
480 || display_line->color != line->logfile->color
481 || display_line->offset != offset
482 || memcmp (display_line->line, line->line, line->len))
483 {
484 /* don't bother updating the record of what has been
485 * displayed if -noflicker isn't in effect, since we redraw
486 * the whole display every time anyway */
487 if (opt_noflicker)
488 {
489 /* update the record of what has been displayed;
490 * first make sure the buffer is big enough */
491 if (display_line->buffer_size < line->len)
492 {
493 display_line->buffer_size = line->len;
494 display_line->line = xrealloc (display_line->line, display_line->buffer_size);
495 }
496
497 display_line->len = line->len;
498 display_line->color = line->logfile->color;
499 display_line->offset = offset;
500 memcpy (display_line->line, line->line, line->len);
501
502 if (clear)
503 {
504#ifdef DEBUG
505 static int toggle;
506 toggle = 1 - toggle;
507 XSetForeground (disp, WinGC, toggle ? GetColor ("cyan") : GetColor ("yellow"));
508 XFillRectangle (disp, root, WinGC, win_x, win_y + offset - line->logfile->font_ascent,
509 width, step_per_line);
510#else /* DEBUG */
511 XClearArea (disp, root, win_x, win_y + offset - line->logfile->font_ascent,
512 width + MARGIN_OF_ERROR, step_per_line, False);
513#endif /* DEBUG */
514 }
515 }
516
517 if (opt_outline)
518 {
519 int x, y;
520 XSetForeground (disp, WinGC, black_color);
521
522 for (x = -1; x <= 1; x += 2)
523 for (y = -1; y <= 1; y += 2)
524 draw_text (disp, root, WinGC,
525 win_x + effect_x_offset + x,
526 win_y + y + offset, line, foreground = 0);
527 }
528 else if (opt_shade)
529 {
530 XSetForeground (disp, WinGC, black_color);
531 draw_text (disp, root, WinGC,
532 win_x + effect_x_offset + SHADE_X,
533 win_y + offset + SHADE_Y, line, foreground = 0);
534 }
535
536 XSetForeground (disp, WinGC, line->logfile->color);
537 draw_text (disp, root, WinGC,
538 win_x + effect_x_offset,
539 win_y + offset, line, foreground = 1);
540 }
541 }
542
543 if (opt_reverse)
544 offset += step_per_line;
545 offset -= line->logfile->font_ascent;
546
547 miny += line->logfile->font_height;
548 maxy -= line->logfile->font_height;
549
550 space -= step_per_line;
551 }
552
553 if (space > 0 && clear)
554 {
555#ifdef DEBUG
556 XSetForeground (disp, WinGC, GetColor ("orange"));
557 XFillRectangle (disp, root, WinGC, win_x, win_y + offset - (opt_reverse ? 0 : space),
558 width, space);
559#else /* DEBUG */
560 XClearArea (disp, root, win_x, win_y + offset - (opt_reverse ? 0 : space),
561 width + MARGIN_OF_ERROR, space, False);
562#endif
563 }
564
565 /* at least one of the lines must fit in the allocated area. we've
566 * already checked at initialisation time that all the fonts are small
567 * enough to fit at least one line in the display area, but assert it
568 * again here to be sure */
569 assert(line != linelist);
570
571 /* any lines that didn't just get looked at are never going to be, so break the chain */
572 if (line) line->prev->next = 0;
573
574 /* and throw them all away */
575 while (line)
576 {
577 struct line_node *this = line;
578 line = line->next;
579 if (this->logfile && this->logfile->last == this)
580 this->logfile->last = NULL;
581 free (this->line);
582 free (this->breaks);
583 free (this);
584 }
585
586 if (opt_frame)
587 {
588 XSetForeground (disp, WinGC, GetColor (def_color));
589 /* note that XDrawRectangle() draws a rectangle one pixel bigger
590 * in both dimensions than you ask for, hence the subtractions.
591 * XFillRectangle() doesn't suffer from this problem */
592 XDrawRectangle (disp, root, WinGC, win_x - 0, win_y - 0, width - 1, height - 1);
593 }
594}
595
596#if HAS_REGEX
597void
598transform_line (char *s)
599{
600#ifdef I_AM_Md
196 int i, j; 601 int i;
197 602 if (1)
198 out = malloc (8 * strlen (s) + 1); 603 {
199 604 for (i = 16; s[i]; i++)
200 for(i = 0, j = 0; s[i]; i++) 605 s[i] = s[i + 11];
201 { 606 }
607 s[i + 1] = '\0';
608#endif
609
610 if (transformre)
611 {
612 int i;
613 regmatch_t matched[16];
614
615 i = regexec (transformre, s, 16, matched, 0);
202 if (s[i] == '\t') 616 if (i == 0)
617 { /* matched */
618 int match_start = matched[0].rm_so;
619 int match_end = matched[0].rm_eo;
620 int old_len = match_end - match_start;
621 int new_len = strlen (transform_to);
622 int old_whole_len = strlen (s);
623
624 printf ("regexp was matched by '%s' - replace with '%s'\n", s, transform_to);
625 printf ("match is from %d to %d\n", match_start, match_end);
626 if (new_len > old_len)
627 s = xrealloc (s, old_whole_len + new_len - old_len);
628
629 if (new_len != old_len)
630 {
631 memcpy (s + match_end + new_len - old_len,
632 s + match_end,
633 old_whole_len - match_end);
634 s[old_whole_len + new_len - old_len] = '\0';
635 }
636
637 memcpy (s + match_start,
638 transform_to,
639 new_len);
640 printf ("transformed to '%s'\n", s);
203 do 641 }
204 out[j++] = ' ';
205 while (j % 8);
206 else 642 else
207 out[j++] = s[i]; 643 printf ("regexp was not matched by '%s'\n", s);
208 } 644 }
209
210 out[j] = '\0';
211 return out;
212} 645}
646#endif
213 647
214/* 648/*
215 * redraw does a complete redraw, rather than an update (i.e. the area 649 * appends p2 to the end of p1, if p1 is not null
216 * gets cleared first) 650 * otherwise allocates a new string and copies p2 to it
217 * the rest is handled by regular refresh()'es
218 */ 651 */
219void redraw(void) 652char *
653concat_line (char *p1, const char *p2)
220{ 654{
221 XClearArea(disp, root, win_x, win_y, w, h + font_descent + 2, True); 655 int l1 = p1 ? strlen (p1) : 0;
222} 656 int l2 = strlen (p2);
657 char *r;
223 658
224/* Just redraw everything without clearing (i.e. after an EXPOSE event) */ 659 assert (p2);
225void refresh(struct linematrix *lines, int miny, int maxy)
226{
227 int lin;
228 int offset = (listlen + 1) * font_height;
229 unsigned long black_color = GetColor("black");
230 660
231 miny -= win_y + font_height; 661 if (p1)
232 maxy -= win_y - font_height; 662 r = xrealloc(p1, l1 + l2 + 1);
663 else
664 r = xmalloc (l2 + 1);
233 665
234 for (lin = listlen; lin--;) 666 memcpy (r + l1, p2, l2);
667 r[l1 + l2] = 0;
668
669 return r;
670}
671
672/*
673 * This routine can read a line of any length if it is called enough times.
674 */
675int
676lineinput (struct logfile_entry *logfile)
677{
678 char buff[1024], *p;
679 int ch;
680 /* HACK-2: add on the length of any partial line which we will be appending to */
681 int ofs = logfile->buf ? strlen (logfile->buf) : 0;
682
683 /* this loop ensures that the whole line is read, even if it's
684 * longer than the buffer. we need to do this because when --whole
685 * is in effect we don't know whether to display the line or not
686 * until we've seen how (ie. whether) it ends */
687 do
235 { 688 {
236 char *temp; 689 p = buff;
237 690 do
238 offset -= font_height;
239
240 if (offset < miny || offset > maxy)
241 continue;
242
243#define LIN ((opt_reverse)?(listlen-lin-1):(lin))
244 temp = detabificate (lines[LIN].line);
245
246 if (opt_shade)
247 {
248 XSetForeground (disp, WinGC, black_color);
249 XDrawString (disp, root, WinGC, win_x + 2, win_y + offset + 2,
250 temp, strlen (temp));
251 }
252
253 XSetForeground (disp, WinGC, lines[LIN].color);
254 XDrawString (disp, root, WinGC, win_x, win_y + offset,
255 temp, strlen (temp));
256 691 {
257 free (temp); 692 ch = fgetc (logfile->fp);
258 }
259 693
260 if (opt_frame) { 694 if (ch == '\n' || ch == EOF)
261 int bot_y = win_y + h + font_descent + 2; 695 break;
696 else if (ch == '\r')
697 continue; /* skip */
698 else if (ch == '\t')
699 {
700 do
701 {
702 *p++ = ' ';
703 ofs++;
704 }
705 while (ofs & 7);
706 }
707 else
708 {
709 *p++ = ch;
710 ofs++;
711 }
712 }
713 while (p < buff + (sizeof buff) - 8 - 1);
262 714
263 XDrawLine(disp, root, WinGC, win_x, win_y, win_x + w, win_y); 715 if (p == buff && ch == EOF)
264 XDrawLine(disp, root, WinGC, win_x + w, win_y, win_x + w, bot_y); 716 return 0;
265 XDrawLine(disp, root, WinGC, win_x + w, bot_y, win_x, bot_y); 717
266 XDrawLine(disp, root, WinGC, win_x, bot_y, win_x, win_y); 718 *p = 0;
719
720 p = logfile->buf = concat_line (logfile->buf, buff);
267 } 721 }
268} 722 while (ch != '\n' && ch != EOF);
723
724 logfile->lastpartial = logfile->partial;
725 /* there are 3 ways we could have exited the loop: reading '\n',
726 * reaching EOF, or filling the buffer; the 2nd and 3rd of these
727 * both result in a partial line */
728 logfile->partial = ch != '\n';
729
730 if (logfile->partial && opt_whole)
731 return 0;
269 732
270#if HAS_REGEX 733#if HAS_REGEX
271void transform_line(char *s) 734 transform_line (logfile->buf);
272{
273#ifdef I_AM_Md
274 int i;
275 if (1) {
276 for (i = 16; s[i]; i++)
277 s[i] = s[i + 11];
278 }
279 s[i + 1] = '\0';
280#endif 735#endif
281
282 if (transformre) {
283 int i;
284 regmatch_t matched[16];
285
286 i = regexec(&transformre, string, 16, matched, 0);
287 if (i == 0) { /* matched */
288 }
289 }
290}
291#endif
292
293
294/*
295 * This routine should read 'width' characters and not more. However,
296 * we really want to read width + 1 charachters if the last char is a '\n',
297 * which we should remove afterwards. So, read width+1 chars and ungetc
298 * the last character if it's not a newline. This means 'string' must be
299 * width + 2 wide!
300 */
301int lineinput(char *string, int slen, FILE *f)
302{
303 int len;
304
305 do {
306 if (fgets(string, slen, f) == NULL) /* EOF or Error */
307 return 0; 736 return 1;
308
309 len = strlen(string);
310 } while (len == 0);
311
312 if (string[len - 1] == '\n')
313 string[len - 1] = '\0'; /* erase newline */
314 else if (len >= slen - 1) {
315 ungetc(string[len - 1], f);
316 string[len - 1] = '\0';
317 }
318
319#if HAS_REGEX
320 transform_line(string);
321#endif
322 return len;
323} 737}
324 738
325/* input: reads file->fname 739/* input: reads file->fname
326 * output: fills file->fp, file->inode 740 * output: fills file->fp, file->inode
327 * returns file->fp 741 * returns file->fp
328 * in case of error, file->fp is NULL 742 * in case of error, file->fp is NULL
329 */ 743 */
744FILE *
330FILE *openlog(struct logfile_entry *file) 745openlog (struct logfile_entry * file)
331{ 746{
332 struct stat stats; 747 struct stat stats;
333 748
334 if ((file->fp = fopen(file->fname, "r")) == NULL) { 749 if ((file->fp = fopen (file->fname, "r")) == NULL)
750 {
335 file->fp = NULL; 751 file->fp = NULL;
336 return NULL; 752 return NULL;
337 } 753 }
338 754
339 fstat(fileno(file->fp), &stats); 755 fstat (fileno (file->fp), &stats);
340 if (S_ISFIFO(stats.st_mode)) { 756 if (S_ISFIFO (stats.st_mode))
757 {
341 if (fcntl(fileno(file->fp), F_SETFL, O_NONBLOCK) < 0) 758 if (fcntl (fileno (file->fp), F_SETFL, O_NONBLOCK) < 0)
342 perror("fcntl"), exit(1); 759 perror ("fcntl"), exit (1);
343 file->inode = 0; 760 file->inode = 0;
761 }
344 } else 762 else
345 file->inode = stats.st_ino; 763 file->inode = stats.st_ino;
346 764
347 if (opt_noinitial) 765 if (opt_noinitial)
348 fseek (file->fp, 0, SEEK_END); 766 fseek (file->fp, 0, SEEK_END);
349 else if (stats.st_size > (listlen + 1) * width) 767 else /* if (stats.st_size > (listlen + 1) * width)
350 { 768 * HACK - 'width' is in pixels - how are we to know how much text will fit?
351 char dummy[255];
352
353 fseek(file->fp, -((listlen + 2) * width), SEEK_END); 769 * fseek (file->fp, -((listlen + 2) * width/10), SEEK_END); */
354 /* the pointer might point halfway some line. Let's 770 fseek (file->fp, -5000, SEEK_END);
355 be nice and skip this damaged line */
356 lineinput(dummy, sizeof(dummy), file->fp);
357 }
358 771
359 file->last_size = stats.st_size; 772 file->last_size = stats.st_size;
360 return file->fp; 773 return file->fp;
361} 774}
362 775
776void
363void reopen(void) 777reopen (void)
364{ 778{
365 struct logfile_entry *e; 779 struct logfile_entry *e;
366 780
367 for (e = loglist; e; e = e->next) { 781 for (e = loglist; e; e = e->next)
782 {
368 if (!e->inode) 783 if (!e->inode)
369 continue; /* skip stdin */ 784 continue; /* skip stdin */
370 785
371 if (e->fp) 786 if (e->fp)
372 fclose(e->fp); 787 fclose (e->fp);
373 /* if fp is NULL we will try again later */ 788 /* if fp is NULL we will try again later */
374 openlog(e); 789 openlog (e);
375 } 790 }
376 791
377 do_reopen = 0; 792 do_reopen = 0;
378} 793}
379 794
795void
380void check_open_files(void) 796check_open_files (void)
381{ 797{
382 struct logfile_entry *e; 798 struct logfile_entry *e;
383 struct stat stats; 799 struct stat stats;
384 800
385 for (e = loglist; e; e = e->next) { 801 for (e = loglist; e; e = e->next)
802 {
386 if (!e->inode) 803 if (!e->inode)
387 continue; /* skip stdin */ 804 continue; /* skip stdin */
388 805
389 if (stat(e->fname, &stats) < 0) { /* file missing? */ 806 if (stat (e->fname, &stats) < 0)
807 { /* file missing? */
390 sleep(1); 808 sleep (1);
391 if (e->fp) 809 if (e->fp)
392 fclose(e->fp); 810 fclose (e->fp);
393 if (openlog(e) == NULL) 811 if (openlog (e) == NULL)
394 break; 812 continue;
395 } 813 if (fstat (fileno (e->fp), &stats) < 0)
814 continue;
815 }
396 816
397 if (stats.st_ino != e->inode) { /* file renamed? */ 817 if (stats.st_ino != e->inode)
818 { /* file renamed? */
398 if (e->fp) 819 if (e->fp)
399 fclose(e->fp); 820 fclose (e->fp);
400 if (openlog(e) == NULL) 821 if (openlog (e) == NULL)
401 break; 822 continue;
402 } 823 if (fstat (fileno (e->fp), &stats) < 0)
824 continue;
825 }
403 826
404 if (stats.st_size < e->last_size) { /* file truncated? */ 827 if (stats.st_size < e->last_size)
828 { /* file truncated? */
405 fseek(e->fp, 0, SEEK_SET); 829 fseek (e->fp, 0, SEEK_SET);
406 e->last_size = stats.st_size; 830 e->last_size = stats.st_size;
407 } 831 }
832 }
833}
834
835/*
836 * insert a single node in the list of screen lines and return a
837 * pointer to the new node.
838 * the caller MUST then fill in ret->line and ret->len with valid
839 * data.
840 */
841static struct line_node *
842new_line_node (struct logfile_entry *log)
843{
844 struct line_node *new = xmalloc (sizeof (struct line_node));
845
846 new->logfile = log;
847 new->wrapped_left = 0;
848 new->wrapped_right = 0;
849 new->breaks = 0;
850
851 assert(log);
852
853 if (!log || !log->last)
408 } 854 {
409} 855 new->next = linelist;
856 new->next->prev = new;
410 857
411#define SCROLL_UP(lines, listlen) \ 858 new->prev = NULL;
412{ \ 859 linelist = new;
413 int cur_line; \ 860 }
414 for (cur_line = 0; cur_line < (listlen - 1); cur_line++) { \ 861 else
415 strcpy(lines[cur_line].line, lines[cur_line + 1].line); \ 862 {
416 lines[cur_line].color = lines[cur_line + 1].color; \ 863 /* 2 pointers from the new node */
417 } \ 864 new->next = log->last;
418} 865 new->prev = log->last->prev;
419 866
867 /* 2 pointers back to the new node */
868 if (new->next) new->next->prev = new;
869 if (new->prev) new->prev->next = new;
870
871 /* if this is a new first entry in the list then update
872 * 'linelist' */
873 if (log->last == linelist)
874 linelist = new;
875 }
876
877 /* update the logfile record */
878 if (log)
879 log->last = new;
880
881 return new;
882}
883
884/*
885 * this is called after either adding a new line or appending to an
886 * old one. in both cases it's possible that the line no longer fits,
887 * and needs wrapping. this function checks the last line associated
888 * with the supplied logfile.
889 */
890static void
891possibly_split_long_line (struct logfile_entry *log)
892{
893 char *str = log->last->line;
894 int l = strlen (str);
895 char *p = str;
896 struct line_node *line;
897 int spaces;
898 static struct breakinfo *breaks;
899 static int break_buffer_size;
900
901 /* only calculate the continuation's width once */
902 if (continuation_width == -1)
903 {
904 continuation_length = strlen (continuation);
905 continuation_width = XmbTextEscapement (log->fontset, continuation, continuation_length);
906 continuation_color = GetColor (cont_color);
907
908 /* make an array to store information about the location of
909 * spaces in the line */
910 if (opt_justify)
911 {
912 break_buffer_size = 32;
913 breaks = xmalloc (break_buffer_size * sizeof (struct breakinfo));
914 }
915 }
916
917 do
918 {
919 const char *beg = p;
920 int start_w = log->last->wrapped_left ? continuation_width : 0;
921 int w = start_w;
922 int wrapped = 0;
923 char *break_p = NULL;
924 int width_at_break_p = 0;
925 int prefix_len;
926
927 spaces = 0;
928
929 if (opt_justify)
930 breaks[spaces].index = breaks[spaces].width = 0;
931
932 while (*p)
933 {
934 int cw, len;
935
936 /* find the length in bytes of the next multibyte character */
937 len = mblen (p, l);
938 if (len <= 0)
939 len = 1; /* ignore (don't skip) illegal character sequences */
940
941 /* find the width in pixels of the next character */
942 cw = XmbTextEscapement (log->fontset, p, len);
943 if (opt_wordwrap && len == 1 && p[0] == ' ' && p != break_p + 1)
944 {
945 break_p = p;
946 width_at_break_p = w;
947 spaces++;
948
949 if (opt_justify)
950 {
951 /* increase the size of the 'breaks' array when
952 * necessary */
953 if (spaces >= break_buffer_size)
954 {
955 break_buffer_size *= 1.5;
956 breaks = xrealloc (breaks, break_buffer_size * sizeof (struct breakinfo));
957 }
958
959 /* store information about (a) the location of each
960 * space */
961 breaks[spaces].index = p + 1 - beg;
962 /* (b) the width (in pixels) of the string up to
963 * this space */
964 breaks[spaces].width = cw + w - start_w;
965 /* (c) the length of each 'word' */
966 breaks[spaces-1].len = breaks[spaces].index - breaks[spaces-1].index;
967 }
968 }
969
970 if (cw + w > width - effect_x_space)
971 {
972 if (p == beg)
973 {
974 fprintf (stderr, "we can't even fit a single character onto the line\n");
975 if (len == 1) fprintf (stderr, "(the character we couldn't fit was '%c')\n", *p);
976 exit (1);
977 }
978
979 wrapped = 1;
980 break;
981 }
982
983 w += cw;
984 p += len;
985 l -= len;
986 }
987
988 /* if we're wrapping at spaces, and the line is long enough to
989 * wrap, and we've seen a space already, and the space wasn't
990 * the first character on the line, then wrap at the space */
991 if (!wrapped)
992 break;
993
994 /* choose where to break the line */
995 if (opt_wordwrap && break_p && break_p != beg)
996 {
997 prefix_len = break_p - beg;
998 p = break_p;
999 w = width_at_break_p;
1000
1001 /* if breaking at a space, skip all adjacent spaces */
1002 while (*p == ' ')
1003 {
1004 int len = mblen (p, l);
1005 if (len != 1) break;
1006 p++;
1007 }
1008
1009 if (opt_justify)
1010 {
1011 spaces--;
1012 breaks[spaces].len--;
1013 }
1014 }
1015 else
1016 prefix_len = p - beg;
1017
1018 /* make a copy of the tail end of the string */
1019 p = xstrdup (p);
1020
1021 /* and reduce the size of the head of the string */
1022 log->last->line = xrealloc (log->last->line, prefix_len + 1);
1023 log->last->len = prefix_len;
1024 log->last->line[prefix_len] = '\0';
1025
1026 /* note that the head was wrapped on it's right */
1027 log->last->wrapped_right = 1;
1028
1029 /* 'spaces' includes any space we broke on; we can only justify
1030 * if there's at least one other space */
1031 if (opt_justify && spaces &&
1032 width - effect_x_space - width_at_break_p < spaces * log->font_height)
1033 {
1034 int i;
1035 log->last->free_pixels = width - effect_x_space - w;
1036 log->last->num_words = spaces + 1;
1037 log->last->breaks = malloc (log->last->num_words * sizeof (struct breakinfo));
1038 for (i = 0; i < log->last->num_words; i++)
1039 log->last->breaks[i] = breaks[i];
1040 }
1041
1042 line = new_line_node (log);
1043 line->line = p;
1044 l = line->len = strlen (p);
1045
1046 /* note that the tail end of the string is wrapped at its left */
1047 line->wrapped_left = 1;
1048 }
1049 while (l);
1050}
1051
1052static void
1053insert_new_line (char *str, struct logfile_entry *log)
1054{
1055 struct line_node *new;
1056 new = new_line_node (log);
1057 new->line = str;
1058 new->len = strlen (str);
1059
1060 possibly_split_long_line (log);
1061}
1062
1063/*
1064 * append something to an existing physical line. this is done
1065 * by deleting the file on-screen, concatenating the new data to it
1066 * and splitting it again.
1067 */
1068static void
1069append_to_existing_line (char *str, struct logfile_entry *log)
1070{
1071 char *old, *new;
1072
1073 assert(log);
1074 assert(log->last);
1075
1076 old = log->last->line;
1077 assert(old);
1078
1079 new = concat_line (old, str);
1080 free (str);
1081 log->last->line = new;
1082 log->last->len = strlen (new);
1083 possibly_split_long_line (log);
1084}
1085
1086static void
420void main_loop(void) 1087main_loop (void)
421{ 1088{
422 struct linematrix *lines = xmalloc(sizeof(struct linematrix) * listlen); 1089 int lin;
423 int lin, miny, maxy, buflen;
424 char *buf;
425 time_t lastreload; 1090 time_t lastreload;
426 Region region = XCreateRegion(); 1091 Region region = XCreateRegion ();
427 XEvent xev; 1092 XEvent xev;
428 1093 struct logfile_entry *lastprinted = NULL;
429 maxy = 0;
430 miny = win_y + h;
431 buflen = width + 2;
432 buf = xmalloc(buflen);
433 lastreload = time(NULL);
434
435 /* Initialize linematrix */
436 for (lin = 0; lin < listlen; lin++) {
437 lines[lin].line = xmalloc(buflen);
438 strcpy(lines[lin].line, "~");
439 lines[lin].color = GetColor(def_color);
440 }
441
442 if (!opt_noinitial)
443 while (lineinput(buf, buflen, loglist->fp) != 0) {
444 SCROLL_UP(lines, listlen);
445 /* print the next line */
446 strcpy(lines[listlen - 1].line, buf);
447 }
448
449 for (;;) {
450 int need_update = 0;
451 struct logfile_entry *current; 1094 struct logfile_entry *current;
452 static struct logfile_entry *lastprinted = NULL; 1095 int need_update = 1;
453 1096
1097 display = xmalloc (sizeof (struct displaymatrix) * listlen);
1098
1099 lastreload = time (NULL);
1100
1101 /* Initialize line_node */
1102 for (lin = 0; lin < listlen; lin++)
1103 {
1104 struct line_node *e = xmalloc (sizeof (struct line_node));
1105 e->line = xstrdup ("~");
1106 e->len = 1;
1107 e->logfile = loglist; /* this is only needed to get a color for the '~' */
1108 e->wrapped_left = 0;
1109 e->wrapped_right = 0;
1110 e->breaks = 0;
1111 e->next = NULL;
1112 e->prev = linelist_tail;
1113
1114 if (!linelist)
1115 linelist = e;
1116 if (linelist_tail)
1117 linelist_tail->next = e;
1118 linelist_tail = e;
1119
1120 display[lin].line = xstrdup ("");
1121 display[lin].len = 0;
1122 display[lin].offset = 0;
1123 display[lin].buffer_size = 0;
1124 }
1125
1126 for (;;)
1127 {
454 /* read logs */ 1128 /* read logs */
455 for (current = loglist; current; current = current->next) { 1129 for (current = loglist; current; current = current->next)
1130 {
456 if (!current->fp) 1131 if (!current->fp)
457 continue; /* skip missing files */ 1132 continue; /* skip missing files */
458 1133
459 clearerr(current->fp); 1134 clearerr (current->fp);
460 1135
461 while (lineinput(buf, buflen, current->fp) != 0) { 1136 while (lineinput (current))
1137 {
1138 need_update = 1;
1139 /* if we're trying to update old partial lines in
1140 * place, and the last time this file was updated the
1141 * output was partial, and that partial line is not
1142 * too close to the top of the screen, then update
1143 * that partial line */
1144 if (opt_update && current->lastpartial && current->last)
1145 {
1146 append_to_existing_line (current->buf, current);
1147 current->buf = 0;
1148 continue;
1149 }
1150
1151 /* if all we just read was a newline ending a line that we've already displayed, skip it */
1152 if (current->buf[0] == '\0' && current->lastpartial)
1153 {
1154 free(current->buf);
1155 current->buf = 0;
1156 continue;
1157 }
1158
462 /* print filename if any, and if last line was from 1159 /* print filename if any, and if last line was from
463 different file */ 1160 * different file */
464 if (!opt_nofilename && 1161 if (lastprinted != current)
465 !(lastprinted && lastprinted == current) && 1162 {
466 current->desc[0]) { 1163 current->last = 0;
467 SCROLL_UP(lines, listlen); 1164 if (!opt_nofilename && current->desc[0])
468 sprintf(lines[listlen - 1].line, "[%s]", current->desc); 1165 {
469 lines[listlen - 1].color = current->color; 1166 insert_new_line (xstrdup ("["), current);
1167 append_to_existing_line (xstrdup (current->desc), current);
1168 append_to_existing_line (xstrdup ("]"), current);
1169 }
470 } 1170 }
471 1171
472 SCROLL_UP(lines, listlen); 1172 /* if we're dealing with partial lines, and the last
473 strcpy(lines[listlen - 1].line, buf); 1173 * time we showed the line it wasn't finished ... */
474 lines[listlen - 1].color = current->color; 1174 if (!opt_whole && current->lastpartial)
1175 {
1176 /* if this is the same file we showed last then
1177 append to the last line shown */
1178 if (lastprinted == current)
1179 append_to_existing_line (current->buf, current);
1180 else
1181 {
1182 /* but if a different file has been shown in the
1183 * mean time, make a new line, starting with the
1184 * continuation string */
1185 insert_new_line (current->buf, current);
1186 current->last->wrapped_left = 1;
1187 }
1188 }
1189 else
1190 /* otherwise just make a plain and simple new line */
1191 insert_new_line (current->buf, current);
475 1192
476 lastprinted = current; 1193 current->buf = 0;
477 need_update = 1; 1194 lastprinted = current;
478 } 1195 }
479 } 1196 }
480 1197
481 if (need_update) 1198 if (need_update)
482 redraw(); 1199 {
483 else { 1200 redraw (0);
1201 need_update = 0;
1202 }
1203 else
1204 {
484 XFlush(disp); 1205 XFlush (disp);
1206
485 if (!XPending(disp)) { 1207 if (!XPending (disp))
486 fd_set fdr; 1208 {
1209 fd_set fdr;
487 struct timeval to = interval; 1210 struct timeval to = interval;
488 1211
489 FD_ZERO(&fdr); 1212 FD_ZERO (&fdr);
490 FD_SET(ConnectionNumber(disp), &fdr); 1213 FD_SET (ConnectionNumber (disp), &fdr);
491 select(ConnectionNumber(disp) + 1, &fdr, 0, 0, &to); 1214 select (ConnectionNumber (disp) + 1, &fdr, 0, 0, &to);
492 } 1215 }
493 } 1216 }
494 1217
495 check_open_files(); 1218 check_open_files ();
496 1219
497 if (do_reopen) 1220 if (do_reopen)
498 reopen(); 1221 reopen ();
499 1222
500 /* we ignore possible errors due to window resizing &c */ 1223 /* we ignore possible errors due to window resizing &c */
501 while (XPending(disp)) { 1224 while (XPending (disp))
1225 {
502 XNextEvent(disp, &xev); 1226 XNextEvent (disp, &xev);
1227
503 switch (xev.type) { 1228 switch (xev.type)
1229 {
504 case Expose: 1230 case Expose:
505 { 1231 {
506 XRectangle r; 1232 XRectangle r;
507 1233
508 r.x = xev.xexpose.x; 1234 r.x = xev.xexpose.x;
509 r.y = xev.xexpose.y; 1235 r.y = xev.xexpose.y;
510 r.width = xev.xexpose.width; 1236 r.width = xev.xexpose.width;
511 r.height = xev.xexpose.height; 1237 r.height = xev.xexpose.height;
1238
512 XUnionRectWithRegion(&r, region, region); 1239 XUnionRectWithRegion (&r, region, region);
513 if (miny > r.y) 1240 }
514 miny = r.y; 1241 break;
515 if (maxy < r.y + r.height) 1242 default:
516 maxy = r.y + r.height;
517 }
518 break;
519 default:
520#ifdef DEBUGMODE 1243#ifdef DEBUGMODE
521 fprintf(stderr, "PANIC! Unknown event %d\n", xev.type); 1244 fprintf (stderr, "PANIC! Unknown event %d\n", xev.type);
522#endif 1245#endif
523 break; 1246 break;
1247 }
1248 }
1249
1250 /* reload if requested */
1251 if (reload && lastreload + reload < time (NULL))
1252 {
1253 if (command && command[0])
1254 system (command);
1255
1256 reopen ();
1257 lastreload = time (NULL);
1258 }
1259
1260 if (!XEmptyRegion (region))
1261 {
1262 XRectangle r;
1263
1264 XSetRegion (disp, WinGC, region);
1265 XClipBox (region, &r);
1266
1267 refresh (r.y, r.y + r.height, 0, 1);
1268
1269 XDestroyRegion (region);
1270 region = XCreateRegion ();
1271 }
524 } 1272 }
525 }
526
527 /* reload if requested */
528 if (reload && lastreload + reload < time(NULL)) {
529 if (command)
530 system(command);
531
532 reopen();
533 lastreload = time(NULL);
534 }
535
536 if (!XEmptyRegion(region)) {
537 XSetRegion(disp, WinGC, region);
538 refresh(lines, miny, maxy);
539 XDestroyRegion(region);
540 region = XCreateRegion();
541 maxy = 0;
542 miny = win_y + h;
543 }
544 }
545} 1273}
546 1274
547 1275
1276int
548int main(int argc, char *argv[]) 1277main (int argc, char *argv[])
549{ 1278{
550 int i; 1279 int i;
551 int opt_daemonize = 0; 1280 int opt_daemonize = 0;
1281 int opt_partial = 0, file_count = 0;
552#if HAS_REGEX 1282#if HAS_REGEX
553 char *transform = NULL; 1283 char *transform = NULL;
554#endif 1284#endif
555 1285
556 /* window needs to be initialized before colorlookups can be done */ 1286 setlocale (LC_CTYPE, ""); /* try to initialize the locale. */
557 /* just a dummy to get the color lookups right */
558 geom_mask = NoValue;
559 InitWindow();
560 1287
561 for (i = 1; i < argc; i++) { 1288 for (i = 1; i < argc; i++)
1289 {
1290 const char *arg = argv[i];
1291
562 if (argv[i][0] == '-' && argv[i][1] != '\0' && argv[i][1] != ',') { 1292 if (arg[0] == '-' && arg[1] != '\0' && arg[1] != ',')
1293 {
1294 if (arg[1] == '-')
1295 arg++;
1296
563 if (!strcmp(argv[i], "--?") || 1297 if (!strcmp (arg, "-?") ||
564 !strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) 1298 !strcmp (arg, "-help") || !strcmp (arg, "-h"))
565 display_help(argv[0]); 1299 display_help (argv[0]);
566 else if (!strcmp(argv[i], "-V")) 1300 else if (!strcmp (arg, "-V"))
567 display_version(); 1301 display_version ();
568 else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "-geometry")) 1302 else if (!strcmp (arg, "-g") || !strcmp (arg, "-geometry"))
569 geom_mask = XParseGeometry(argv[++i], 1303 geom_mask =
570 &win_x, &win_y, &width, &listlen); 1304 XParseGeometry (argv[++i], &win_x, &win_y, &width, &height);
571 else if (!strcmp(argv[i], "-display")) 1305 else if (!strcmp (arg, "-display"))
572 dispname = argv[++i]; 1306 dispname = argv[++i];
1307 else if (!strcmp (arg, "-cont"))
1308 continuation = argv[++i];
1309 else if (!strcmp (arg, "-cont-color"))
1310 cont_color = argv[++i];
573 else if (!strcmp(argv[i], "-font") || !strcmp(argv[i], "-fn")) 1311 else if (!strcmp (arg, "-font") || !strcmp (arg, "-fn"))
574 fontname = argv[++i]; 1312 fontname = argv[++i];
575#if HAS_REGEX 1313#if HAS_REGEX
576 else if (!strcmp(argv[i], "-t")) 1314 else if (!strcmp (arg, "-t"))
577 transform = argv[++i]; 1315 {
1316 transform = argv[++i];
1317 transform_to = argv[++i];
1318 printf ("transform: '%s' to '%s'\n", transform, transform_to);
1319 }
578#endif 1320#endif
579 else if (!strcmp(argv[i], "-fork") || !strcmp(argv[i], "-f")) 1321 else if (!strcmp (arg, "-fork") || !strcmp (arg, "-f"))
580 opt_daemonize = 1; 1322 opt_daemonize = 1;
581 else if (!strcmp(argv[i], "-reload")) { 1323 else if (!strcmp (arg, "-reload"))
582 reload = atoi(argv[++i]); 1324 {
583 command = argv[++i]; 1325 reload = atoi (argv[++i]);
584 } 1326 command = argv[++i];
1327 }
585 else if (!strcmp(argv[i], "-shade")) 1328 else if (!strcmp (arg, "-shade"))
586 opt_shade = 1; 1329 opt_shade = 1;
1330 else if (!strcmp (arg, "-outline"))
1331 opt_outline = 1;
1332 else if (!strcmp (arg, "-minspace"))
1333 opt_minspace = 1;
1334 else if (!strcmp (arg, "-noflicker"))
1335 opt_noflicker = 1;
587 else if (!strcmp(argv[i], "-frame")) 1336 else if (!strcmp (arg, "-frame"))
588 opt_frame = 1; 1337 opt_frame = 1;
589 else if (!strcmp(argv[i], "-no-filename")) 1338 else if (!strcmp (arg, "-no-filename"))
590 opt_nofilename = 1; 1339 opt_nofilename = 1;
591 else if (!strcmp(argv[i], "-reverse")) 1340 else if (!strcmp (arg, "-reverse"))
592 opt_reverse = 1; 1341 opt_reverse = 1;
1342 else if (!strcmp (arg, "-whole"))
1343 opt_whole = 1;
1344 else if (!strcmp (arg, "-partial"))
1345 opt_partial = 1;
1346 else if (!strcmp (arg, "-update"))
1347 opt_update = opt_partial = 1;
1348 else if (!strcmp (arg, "-wordwrap"))
1349 opt_wordwrap = 1;
1350 else if (!strcmp (arg, "-justify"))
1351 opt_justify = 1;
593 else if (!strcmp(argv[i], "-color")) 1352 else if (!strcmp (arg, "-color"))
594 def_color = argv[++i]; 1353 def_color = argv[++i];
595 else if (!strcmp(argv[i], "-noinitial")) 1354 else if (!strcmp (arg, "-noinitial"))
596 opt_noinitial = 1; 1355 opt_noinitial = 1;
1356 else if (!strcmp (arg, "-id"))
1357 {
1358 unsigned long id;
1359
1360 if (sscanf (argv[++i], "%li", &id) == 1 && id)
1361 root = id;
1362 }
597 else if (!strcmp(argv[i], "-interval") || !strcmp(argv[i], "-i")) { 1363 else if (!strcmp (arg, "-interval") || !strcmp (arg, "-i"))
1364 {
598 double iv = atof(argv[++i]); 1365 double iv = atof (argv[++i]);
599 1366
600 interval.tv_sec = (int) iv; 1367 interval.tv_sec = (int) iv;
601 interval.tv_usec = (iv - interval.tv_sec) * 1e6; 1368 interval.tv_usec = (iv - interval.tv_sec) * 1e6;
602 } else { 1369 }
1370 else
1371 {
603 fprintf(stderr, "Unknown option '%s'.\n" 1372 fprintf (stderr, "Unknown option '%s'.\n"
604 "Try --help for more information.\n", argv[i]); 1373 "Try --help for more information.\n", arg);
605 exit(1); 1374 exit (1);
606 } 1375 }
607 } else { /* it must be a filename */ 1376 }
1377 else
1378 { /* it must be a filename */
608 struct logfile_entry *e; 1379 struct logfile_entry *e;
609 const char *fname, *desc, *fcolor = def_color; 1380 const char *fname, *desc, *fcolor = def_color;
610 char *p; 1381 char *p;
611 1382
1383 file_count++;
1384
612 /* this is not foolproof yet (',' in filenames are not allowed) */ 1385 /* this is not foolproof yet (',' in filenames are not allowed) */
613 fname = desc = argv[i]; 1386 fname = desc = arg;
614 if ((p = strchr(argv[i], ','))) { 1387 if ((p = strchr (arg, ',')))
615 *p = '\0'; 1388 {
616 fcolor = p + 1; 1389 *p = '\0';
1390 fcolor = p + 1;
617 1391
618 if ((p = strchr(fcolor, ','))) { 1392 if ((p = strchr (fcolor, ',')))
619 *p = '\0'; 1393 {
620 desc = p + 1; 1394 *p = '\0';
621 } 1395 desc = p + 1;
622 } 1396 }
1397 }
623 1398
624 e = xmalloc(sizeof(struct logfile_entry)); 1399 e = xmalloc (sizeof (struct logfile_entry));
1400 e->partial = 0;
1401 e->buf = 0;
1402
625 if (argv[i][0] == '-' && argv[i][1] == '\0') { 1403 if (arg[0] == '-' && arg[1] == '\0')
1404 {
626 if ((e->fp = fdopen(0, "r")) == NULL) 1405 if ((e->fp = fdopen (0, "r")) == NULL)
627 perror("fdopen"), exit(1); 1406 perror ("fdopen"), exit (1);
628 if (fcntl(0, F_SETFL, O_NONBLOCK) < 0) 1407 if (fcntl (0, F_SETFL, O_NONBLOCK) < 0)
629 perror("fcntl"), exit(1); 1408 perror ("fcntl"), exit (1);
630 e->fname = NULL; 1409
631 e->inode = 0; 1410 e->fname = NULL;
1411 e->inode = 0;
1412 if (desc == arg)
632 e->desc = xstrdup("stdin"); 1413 e->desc = xstrdup ("stdin");
633 } else { 1414 else
634 int l; 1415 e->desc = xstrdup (desc);
635 1416 }
1417 else
1418 {
636 e->fname = xstrdup(fname); 1419 e->fname = xstrdup (fname);
637 if (openlog(e) == NULL) 1420
1421 if (openlog (e) == NULL)
638 perror(fname), exit(1); 1422 perror (fname), exit (1);
639 1423
640 l = strlen(desc); 1424 e->desc = xstrdup (desc);
641 if (l > width - 2) /* must account for [ ] */ 1425 }
642 l = width - 2; 1426
643 e->desc = xmalloc(l + 1); 1427 e->colorname = fcolor;
644 memcpy(e->desc, desc, l); 1428 e->partial = 0;
645 *(e->desc + l) = '\0'; 1429 e->fontname = fontname;
1430 e->last = NULL;
1431 e->next = NULL;
1432
1433 if (!loglist)
1434 loglist = e;
1435 if (loglist_tail)
1436 loglist_tail->next = e;
1437
1438 loglist_tail = e;
1439 }
646 } 1440 }
647 1441
648 e->color = GetColor(fcolor);
649 e->next = NULL;
650
651 if (!loglist) 1442 if (!loglist)
652 loglist = e;
653 if (loglist_tail)
654 loglist_tail->next = e;
655 loglist_tail = e;
656 }
657 } 1443 {
658
659 if (!loglist) {
660 fprintf(stderr, "You did not specify any files to tail\n" 1444 fprintf (stderr, "You did not specify any files to tail\n"
661 "use %s --help for help\n", argv[0]); 1445 "use %s --help for help\n", argv[0]);
662 exit(1); 1446 exit (1);
1447 }
1448
1449 if (opt_update && opt_whole)
663 } 1450 {
1451 fprintf (stderr, "Specify at most one of -update and -whole\n");
1452 exit (1);
1453 }
1454 else if (opt_partial && opt_whole)
1455 {
1456 fprintf (stderr, "Specify at most one of -partial and -whole\n");
1457 exit (1);
1458 }
1459
1460 /* it doesn't make sense to justify if word wrap isn't on */
1461 if (opt_justify)
1462 opt_wordwrap = 1;
1463
1464 /* HACK-7: do we want to allow both -shade and -outline? */
1465 if (opt_shade && opt_outline)
1466 {
1467 fprintf (stderr, "Specify at most one of -shade and -outline\n");
1468 exit (1);
1469 }
1470
1471 if (opt_partial)
1472 /* if we specifically requested to see partial lines then don't insist on whole lines */
1473 opt_whole = 0;
1474 else if (file_count > 1)
1475 /* otherwise, if we're viewing multiple files, default to showing whole lines */
1476 opt_whole = 1;
664 1477
665#if HAS_REGEX 1478#if HAS_REGEX
666 if (transform) { 1479 if (transform)
667 int i; 1480 {
1481 int i;
668 1482
669 transformre = xmalloc(sizeof(transformre)); 1483 printf ("compiling regexp '%s'\n", transform);
1484 transformre = xmalloc (sizeof (regex_t));
670 i = regcomp(&transformre, transform, REG_EXTENDED); 1485 i = regcomp (transformre, transform, REG_EXTENDED);
671 if (i != 0) { 1486 if (i != 0)
1487 {
672 char buf[512]; 1488 char buf[512];
673 1489
674 regerror(i, &transformre, buf, sizeof(buf)); 1490 regerror (i, transformre, buf, sizeof (buf));
675 fprintf(stderr, "Cannot compile regular expression: %s\n", buf); 1491 fprintf (stderr, "Cannot compile regular expression: %s\n", buf);
676 } 1492 }
1493 else
1494 printf ("compiled '%s' OK to %x\n", transform, (int)transformre);
677 } 1495 }
678#endif 1496#endif
679 1497
1498 if (opt_outline && !opt_minspace)
1499 {
1500 /* adding outline increases the total width and height by 2
1501 pixels each, and offsets the text one pixel right and one
1502 pixel down */
1503 effect_x_space = effect_y_space = 2;
1504 effect_x_offset = effect_y_offset = 1;
1505 }
1506 else if (opt_shade && !opt_minspace)
1507 {
1508 /* adding a shadow increases the space used */
1509 effect_x_space = abs (SHADE_X);
1510 effect_y_space = abs (SHADE_Y);
1511
1512 /* if the shadow is to the right and below then we don't need
1513 * to move the text to make space for it, but shadows to the left
1514 * and above need accomodating */
1515 effect_x_offset = SHADE_X > 0 ? 0 : -SHADE_X;
1516 effect_y_offset = SHADE_Y > 0 ? 0 : -SHADE_Y;
1517 }
1518 else
1519 {
1520 effect_x_space = effect_y_space = 0;
1521 effect_x_offset = effect_y_offset = 0;
1522 }
1523
680 InitWindow(); 1524 InitWindow ();
681 1525
682 install_signal(SIGINT, blank_window); 1526 install_signal (SIGINT, blank_window);
683 install_signal(SIGQUIT, blank_window); 1527 install_signal (SIGQUIT, blank_window);
684 install_signal(SIGTERM, blank_window); 1528 install_signal (SIGTERM, blank_window);
685 install_signal(SIGHUP, force_reopen); 1529 install_signal (SIGHUP, force_reopen);
686 install_signal(SIGUSR1, list_files); 1530 install_signal (SIGUSR1, list_files);
687 install_signal(SIGUSR2, force_refresh); 1531 install_signal (SIGUSR2, force_refresh);
688 1532
689 if (opt_daemonize) 1533 if (opt_daemonize)
690 daemonize(); 1534 daemonize ();
691 1535
692 main_loop(); 1536 main_loop ();
693 1537
694 exit(1); /* to make gcc -Wall stop complaining */ 1538 exit (1); /* to make gcc -Wall stop complaining */
695} 1539}
696 1540
1541void
697void install_signal(int sig, void (*handler)(int)) 1542install_signal (int sig, void (*handler) (int))
698{ 1543{
699 struct sigaction action; 1544 struct sigaction action;
700 1545
701 action.sa_handler = handler; 1546 action.sa_handler = handler;
702 sigemptyset(&action.sa_mask); 1547 sigemptyset (&action.sa_mask);
703 action.sa_flags = SA_RESTART; 1548 action.sa_flags = SA_RESTART;
1549
704 if (sigaction(sig, &action, NULL) < 0) 1550 if (sigaction (sig, &action, NULL) < 0)
705 fprintf(stderr, "sigaction(%d): %s\n", sig, strerror(errno)), exit(1); 1551 fprintf (stderr, "sigaction (%d): %s\n", sig, strerror (errno)), exit (1);
706} 1552}
707 1553
1554void *
708void *xstrdup(const char *string) 1555xstrdup (const char *string)
709{ 1556{
710 void *p; 1557 void *p;
711 1558
712 while ((p = strdup(string)) == NULL) { 1559 while ((p = strdup (string)) == NULL)
1560 {
713 fprintf(stderr, "Memory exausted."); 1561 fprintf (stderr, "Memory exhausted in xstrdup ().\n");
714 sleep(10); 1562 sleep (10);
715 } 1563 }
1564
716 return p; 1565 return p;
717} 1566}
718 1567
1568void *
719void *xmalloc(size_t size) 1569xmalloc (size_t size)
720{ 1570{
721 void *p; 1571 void *p;
722 1572
723 while ((p = malloc(size)) == NULL) { 1573 while ((p = malloc (size)) == NULL)
1574 {
724 fprintf(stderr, "Memory exausted."); 1575 fprintf (stderr, "Memory exhausted in xmalloc ().\n");
725 sleep(10); 1576 sleep (10);
726 } 1577 }
1578
727 return p; 1579 return p;
728} 1580}
729 1581
1582void *
1583xrealloc (void *ptr, size_t size)
1584{
1585 void *p;
1586
1587 while ((p = realloc (ptr, size)) == NULL)
1588 {
1589 fprintf (stderr, "Memory exhausted in xrealloc ().\n");
1590 sleep (10);
1591 }
1592
1593 return p;
1594}
1595
1596void
730void display_help(char *myname) 1597display_help (char *myname)
731{ 1598{
732 printf("Usage: %s [options] file1[,color[,desc]] " 1599 printf ("Usage: %s [options] file1[,color[,desc]]"
733 "[file2[,color[,desc]] ...]\n", myname); 1600 "[options] [file2[,color[,desc]] ...]\n", myname);
734 printf(" -g | -geometry geometry -g WIDTHxHEIGHT+X+Y\n" 1601 printf (" -g | -geometry geometry -g WIDTHxHEIGHT+X+Y\n"
735 " -color color use color $color as default\n" 1602 " -color color use color $color as default\n"
736 " -reload sec command reload after $sec and run command\n" 1603 " -reload sec command reload after $sec and run command\n"
737 " by default -- 3 mins\n" 1604 " -id id window id to use instead of the root window\n"
738 " -font FONTSPEC (-fn) font to use\n" 1605 " -font FONTSPEC (-fn) font to use\n"
739 " -f | -fork fork into background\n" 1606 " -f | -fork fork into background\n"
740 " -reverse print new lines at the top\n" 1607 " -reverse print new lines at the top\n"
1608 " -whole wait for \\n before showing a line\n"
1609 " -partial show lines even if they don't end with a \\n\n"
1610 " -update allow updates to old partial lines\n"
1611 " -cont string to prefix continued partial lines with\n"
1612 " defaults to \"|| \"\n"
1613 " -wordwrap wrap long lines at spaces to avoid breaking words\n"
741 " -shade add shading to font\n" 1614 " -shade add shading to font\n"
1615 " -outline add black outline to font\n"
1616 " -minspace force minimum line spacing\n"
742 " -noinitial don't display the last file lines on\n" 1617 " -noinitial don't display the last file lines on\n"
743 " startup\n" 1618 " startup\n"
744 " -i | -interval seconds interval between checks (fractional\n" 1619 " -i | -interval seconds interval between checks (fractional\n"
745 " values o.k.). Default 3\n" 1620 " values o.k.). Default 2.4 seconds\n"
746 " -V display version information and exit\n" 1621 " -V display version information and exit\n"
747 "\n"); 1622 "\n");
748 printf("Example:\n%s -g 80x25+100+50 -font fixed /var/log/messages,green " 1623 printf ("Example:\n%s -g 800x250+100+50 -font fixed /var/log/messages,green "
749 "/var/log/secure,red,'ALERT'\n", myname); 1624 "/var/log/secure,red,'ALERT'\n", myname);
750 exit(0); 1625 exit (0);
751} 1626}
752 1627
1628void
753void display_version(void) { 1629display_version (void)
1630{
754 printf("root-tail version " VERSION "\n"); 1631 printf ("root-tail version " VERSION "\n");
755 exit(0); 1632 exit (0);
756} 1633}
757 1634
1635int
758int daemonize(void) { 1636daemonize (void)
1637{
1638 pid_t pid;
1639
759 switch (fork()) { 1640 switch (pid = fork ())
1641 {
760 case -1: 1642 case -1:
761 return -1; 1643 return -1;
762 case 0: 1644 case 0:
763 break; 1645 break;
764 default: 1646 default:
765 _exit(0); 1647 /*printf ("%d\n", pid);*/
1648 exit (0);
766 } 1649 }
767 1650
768 if (setsid() == -1) 1651 if (setsid () == -1)
769 return -1; 1652 return -1;
770 1653
771 return 0; 1654 return 0;
772} 1655}
773

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines