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.8 by pcg, Thu Mar 25 21:46:56 2004 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>
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>
33#include <locale.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
34#if HAS_REGEX 45#if HAS_REGEX
35#include <regex.h> 46#include <regex.h>
36#endif 47#endif
37#include <X11/Xlib.h> 48
38#include <X11/Xatom.h> 49#define SHADE_X 2
39#include <X11/Xutil.h> 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
40 54
41/* data structures */ 55/* data structures */
42struct logfile_entry { 56struct logfile_entry
43 char *fname; /* name of file */ 57{
44 char *desc; /* alternative description */
45 FILE *fp; /* FILE struct associated with file */
46 ino_t inode; /* inode of the file opened */
47 off_t last_size; /* file size at the last check */
48 unsigned long color; /* color to be used for printing */
49 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 */
50}; 76};
51 77
52struct linematrix { 78struct line_node
53 char *line; 79{
54 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 */
55}; 91};
56 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};
57 108
58/* global variables */ 109/* global variables */
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 */
59int width = STD_WIDTH, listlen = STD_HEIGHT; 118int width = STD_WIDTH, height = STD_HEIGHT, listlen = 50;
60int win_x = LOC_X, win_y = LOC_Y; 119int win_x = LOC_X, win_y = LOC_Y;
61int 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 */
62int do_reopen; 122int do_reopen;
63struct timeval interval = { 2, 400000 }; /* see Knuth */ 123struct timeval interval = { 2, 400000 };
64XFontSet fontset;
65 124
66/* command line options */ 125/* command line options */
67int opt_noinitial, opt_shade, opt_frame, opt_reverse, opt_nofilename, 126int opt_noinitial, opt_shade, opt_frame, opt_reverse, opt_nofilename,
68 geom_mask, reload = 3600; 127 opt_outline, opt_noflicker, opt_whole, opt_update, opt_wordwrap,
128 opt_justify, geom_mask, opt_minspace, reload;
69const char *command = NULL, 129const char *command = NULL,
70 *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;
71 132
72struct logfile_entry *loglist = NULL, *loglist_tail = NULL; 133struct logfile_entry *loglist = NULL, *loglist_tail = NULL;
73 134
74Display *disp; 135Display *disp;
75Window root; 136Window root;
76GC WinGC; 137GC WinGC;
77 138
78#if HAS_REGEX 139#if HAS_REGEX
79struct re_list { 140struct re_list
141{
80 regex_t from; 142 regex_t from;
81 const char *to; 143 const char *to;
82 struct re_list *next; 144 struct re_list *next;
83}; 145};
84struct re_list *re_head, *re_tail; 146struct re_list *re_head, *re_tail;
147char *transform_to = NULL;
148regex_t *transformre;
85#endif 149#endif
86 150
87 151
88/* prototypes */ 152/* prototypes */
89void list_files(int); 153void list_files (int);
90void force_reopen(int); 154void force_reopen (int);
91void force_refresh(int); 155void force_refresh (int);
92void 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 */
93 160
94void InitWindow(void); 161void InitWindow (void);
95unsigned long GetColor(const char *); 162unsigned long GetColor (const char *);
96void redraw(void); 163void redraw (int);
97void refresh(struct linematrix *, int, int); 164void refresh (int, int, int, int);
98 165
99void transform_line(char *s); 166void transform_line (char *s);
100int lineinput(char *, int, FILE *); 167int lineinput (struct logfile_entry *);
101void reopen(void); 168void reopen (void);
102void check_open_files(void); 169void check_open_files (void);
103FILE *openlog(struct logfile_entry *); 170FILE *openlog (struct logfile_entry *);
104void main_loop(void); 171static void main_loop (void);
105 172
106void display_version(void); 173void display_version (void);
107void display_help(char *); 174void display_help (char *);
108void install_signal(int, void (*)(int)); 175void install_signal (int, void (*)(int));
109void *xstrdup(const char *); 176void *xstrdup (const char *);
110void *xmalloc(size_t); 177void *xmalloc (size_t);
178void *xrealloc (void *, size_t);
111int daemonize(void); 179int daemonize (void);
112 180
113/* signal handlers */ 181/* signal handlers */
182void
114void list_files(int dummy) 183list_files (int dummy)
115{ 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 {
116 struct logfile_entry *e; 363 struct logfile_entry *e;
117 364
118 fprintf(stderr, "Files opened:\n");
119 for (e = loglist; e; e = e->next) 365 for (e = loglist; e; e = e->next)
120 fprintf(stderr, "\t%s (%s)\n", e->fname, e->desc); 366 e->color = GetColor (e->colorname);
121} 367 }
122 368
123void force_reopen(int dummy) 369 XSelectInput (disp, root, ExposureMask | FocusChangeMask);
124{
125 do_reopen = 1;
126} 370}
127 371
128void force_refresh(int dummy) 372/*
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)
129{ 382{
130 XClearArea(disp, root, win_x, win_y, w, h + font_descent + 2, False); 383 XSetClipMask (disp, WinGC, None);
131 redraw(); 384 refresh (0, 32768, 1, redraw_all);
132} 385}
133 386
134void blank_window(int dummy) 387void draw_text (Display *disp, Window root, GC WinGC, int x, int y, struct line_node *line, int foreground)
135{ 388{
136 XClearArea(disp, root, win_x, win_y, w, h + font_descent + 2, False); 389 if (line->wrapped_right && opt_justify && line->breaks)
137 XFlush(disp); 390 {
138 exit(0); 391 int i;
139} 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);
140 398
141/* X related functions */ 399 if (line->wrapped_left)
142unsigned long GetColor(const char *ColorName) 400 {
143{ 401 if (foreground) XSetForeground (disp, WinGC, continuation_color);
144 XColor Color; 402 XmbDrawString (disp, root, line->logfile->fontset, WinGC, x, y, continuation, continuation_length);
145 XWindowAttributes Attributes; 403 }
146
147 XGetWindowAttributes(disp, root, &Attributes);
148 Color.pixel = 0;
149
150 if (!XParseColor(disp, Attributes.colormap, ColorName, &Color))
151 fprintf(stderr, "can't parse %s\n", ColorName);
152 else if (!XAllocColor(disp, Attributes.colormap, &Color))
153 fprintf(stderr, "can't allocate %s\n", ColorName);
154
155 return Color.pixel;
156}
157
158static Window root_window (Display *display, int screen_number)
159{
160 Atom __SWM_VROOT = XInternAtom (display, "__SWM_VROOT", False);
161 Window real_root_window = RootWindow (display, screen_number);
162
163 if (root) /* root window set via option */
164 return root;
165
166 if (__SWM_VROOT != None)
167 { 404 }
168 Window unused, *windows; 405 else
169 unsigned int count; 406 {
407 XmbDrawString (disp, root, line->logfile->fontset, WinGC, x + continuation_width * line->wrapped_left,
408 y, line->line, line->len);
170 409
171 if (XQueryTree (display, real_root_window, &unused, &unused, &windows, 410 if (line->wrapped_left)
172 &count)) 411 {
173 { 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
174 int i; 601 int i;
175 602 if (1)
176 for (i = 0; i < count; i++)
177 { 603 {
178 Atom type; 604 for (i = 16; s[i]; i++)
179 int format; 605 s[i] = s[i + 11];
180 unsigned long nitems, bytes_after_return;
181 unsigned char *virtual_root_window;
182
183 if (XGetWindowProperty (display, windows[i], __SWM_VROOT,
184 0, 1, False, XA_WINDOW, &type, &format,
185 &nitems, &bytes_after_return,
186 &virtual_root_window)
187 == Success)
188 {
189 if (type != None)
190 {
191 if (type == XA_WINDOW)
192 {
193 XFree (windows);
194 return (Window)virtual_root_window;
195 }
196 else
197 fprintf (stderr, "__SWM_VROOT property type mismatch");
198 }
199 }
200 else
201 fprintf (stderr,
202 "failed to get __SWM_VROOT property on window 0x%lx",
203 windows[i]);
204 } 606 }
607 s[i + 1] = '\0';
608#endif
205 609
206 if (count) 610 if (transformre)
207 XFree (windows); 611 {
208 } 612 int i;
613 regmatch_t matched[16];
614
615 i = regexec (transformre, s, 16, matched, 0);
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);
641 }
209 else 642 else
210 fprintf (stderr, "Can't query tree on root window 0x%lx", 643 printf ("regexp was not matched by '%s'\n", s);
211 real_root_window);
212 } 644 }
645}
646#endif
647
648/*
649 * appends p2 to the end of p1, if p1 is not null
650 * otherwise allocates a new string and copies p2 to it
651 */
652char *
653concat_line (char *p1, const char *p2)
654{
655 int l1 = p1 ? strlen (p1) : 0;
656 int l2 = strlen (p2);
657 char *r;
658
659 assert (p2);
660
661 if (p1)
662 r = xrealloc(p1, l1 + l2 + 1);
213 else 663 else
214 /* This shouldn't happen. The Xlib documentation is wrong BTW. */ 664 r = xmalloc (l2 + 1);
215 fprintf (stderr, "Can't intern atom __SWM_VROOT");
216 665
217 return real_root_window; 666 memcpy (r + l1, p2, l2);
218} 667 r[l1 + l2] = 0;
219 668
220void InitWindow(void)
221{
222 XGCValues gcv;
223 Font font;
224 unsigned long gcm;
225 XFontStruct *info;
226 int screen, ScreenWidth, ScreenHeight;
227
228 if (!(disp = XOpenDisplay(dispname))) {
229 fprintf(stderr, "Can't open display %s.\n", dispname);
230 exit(1);
231 }
232 screen = DefaultScreen(disp);
233 ScreenHeight = DisplayHeight(disp, screen);
234 ScreenWidth = DisplayWidth(disp, screen);
235
236 root = root_window (disp, screen);
237
238 gcm = GCBackground;
239 gcv.graphics_exposures = True;
240 WinGC = XCreateGC(disp, root, gcm, &gcv);
241 XMapWindow(disp, root);
242 XSetForeground(disp, WinGC, GetColor(DEF_COLOR));
243
244 font = XLoadFont(disp, fontname);
245 XSetFont(disp, WinGC, font);
246 info = XQueryFont(disp, font);
247 font_width = info->max_bounds.width;
248 font_descent = info->max_bounds.descent;
249 font_height = info->max_bounds.ascent + font_descent;
250
251 w = width * font_width;
252 h = listlen * font_height;
253
254 if (geom_mask & XNegative)
255 win_x = win_x + ScreenWidth - w;
256 if (geom_mask & YNegative)
257 win_y = win_y + ScreenHeight - h;
258
259 XSelectInput(disp, root, ExposureMask|FocusChangeMask);
260}
261
262char *
263detabificate (char *s)
264{
265 char * out;
266 int i, j;
267
268 out = malloc (8 * strlen (s) + 1);
269
270 for(i = 0, j = 0; s[i]; i++)
271 {
272 if (s[i] == '\t')
273 do
274 out[j++] = ' ';
275 while (j % 8);
276 else
277 out[j++] = s[i];
278 }
279
280 out[j] = '\0';
281 return out; 669 return r;
282} 670}
283 671
284/* 672/*
285 * redraw does a complete redraw, rather than an update (i.e. the area 673 * This routine can read a line of any length if it is called enough times.
286 * gets cleared first)
287 * the rest is handled by regular refresh()'es
288 */ 674 */
289void redraw(void) 675int
676lineinput (struct logfile_entry *logfile)
290{ 677{
291 XClearArea(disp, root, win_x, win_y, w, h + font_descent + 2, True); 678 char buff[1024], *p;
292} 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;
293 682
294/* Just redraw everything without clearing (i.e. after an EXPOSE event) */ 683 /* this loop ensures that the whole line is read, even if it's
295void refresh(struct linematrix *lines, int miny, int maxy) 684 * longer than the buffer. we need to do this because when --whole
296{ 685 * is in effect we don't know whether to display the line or not
297 int lin; 686 * until we've seen how (ie. whether) it ends */
298 int offset = (listlen + 1) * font_height; 687 do
299 unsigned long black_color = GetColor("black"); 688 {
300 689 p = buff;
301 miny -= win_y + font_height; 690 do
302 maxy -= win_y - font_height;
303
304 for (lin = listlen; lin--;)
305 { 691 {
306 char *temp; 692 ch = fgetc (logfile->fp);
307 693
308 offset -= font_height; 694 if (ch == '\n' || ch == EOF)
309 695 break;
310 if (offset < miny || offset > maxy) 696 else if (ch == '\r')
311 continue; 697 continue; /* skip */
312 698 else if (ch == '\t')
313#define LIN ((opt_reverse)?(listlen-lin-1):(lin))
314 temp = detabificate (lines[LIN].line);
315
316 if (opt_shade)
317 { 699 {
318 XSetForeground (disp, WinGC, black_color); 700 do
319#if 0 701 {
320 XmbDrawString (disp, root, fontset, WinGC, win_x + 2, win_y + offset + 2, 702 *p++ = ' ';
321 temp, strlen (temp)); 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);
714
715 if (p == buff && ch == EOF)
716 return 0;
717
718 *p = 0;
719
720 p = logfile->buf = concat_line (logfile->buf, buff);
721 }
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;
732
733#if HAS_REGEX
734 transform_line (logfile->buf);
322#endif 735#endif
323 XDrawString (disp, root, WinGC, win_x + 2, win_y + offset + 2,
324 temp, strlen (temp));
325 }
326
327 XSetForeground (disp, WinGC, lines[LIN].color);
328#if 0
329 XmbDrawString (disp, root, fontset, WinGC, win_x, win_y + offset,
330 temp, strlen (temp));
331#endif
332 XDrawString (disp, root, WinGC, win_x, win_y + offset,
333 temp, strlen (temp));
334 free (temp);
335 }
336
337 if (opt_frame) {
338 int bot_y = win_y + h + font_descent + 2;
339
340 XDrawLine(disp, root, WinGC, win_x, win_y, win_x + w, win_y);
341 XDrawLine(disp, root, WinGC, win_x + w, win_y, win_x + w, bot_y);
342 XDrawLine(disp, root, WinGC, win_x + w, bot_y, win_x, bot_y);
343 XDrawLine(disp, root, WinGC, win_x, bot_y, win_x, win_y);
344 }
345}
346
347#if HAS_REGEX
348void transform_line(char *s)
349{
350#ifdef I_AM_Md
351 int i;
352 if (1) {
353 for (i = 16; s[i]; i++)
354 s[i] = s[i + 11];
355 }
356 s[i + 1] = '\0';
357#endif
358
359 if (transformre) {
360 int i;
361 regmatch_t matched[16];
362
363 i = regexec(&transformre, string, 16, matched, 0);
364 if (i == 0) { /* matched */
365 }
366 }
367}
368#endif
369
370
371/*
372 * This routine should read 'width' characters and not more. However,
373 * we really want to read width + 1 characters if the last char is a '\n',
374 * which we should remove afterwards. So, read width+1 chars and ungetc
375 * the last character if it's not a newline. This means 'string' must be
376 * width + 2 wide!
377 */
378int lineinput(char *string, int slen, FILE *f)
379{
380 int len;
381
382 do {
383 if (fgets(string, slen, f) == NULL) /* EOF or Error */
384 return 0; 736 return 1;
385
386 len = strlen(string);
387 } while (len == 0);
388
389 if (string[len - 1] == '\n')
390 string[len - 1] = '\0'; /* erase newline */
391 else if (len >= slen - 1) {
392 ungetc(string[len - 1], f);
393 string[len - 1] = '\0';
394 }
395
396#if HAS_REGEX
397 transform_line(string);
398#endif
399 return len;
400} 737}
401 738
402/* input: reads file->fname 739/* input: reads file->fname
403 * output: fills file->fp, file->inode 740 * output: fills file->fp, file->inode
404 * returns file->fp 741 * returns file->fp
405 * in case of error, file->fp is NULL 742 * in case of error, file->fp is NULL
406 */ 743 */
744FILE *
407FILE *openlog(struct logfile_entry *file) 745openlog (struct logfile_entry * file)
408{ 746{
409 struct stat stats; 747 struct stat stats;
410 748
411 if ((file->fp = fopen(file->fname, "r")) == NULL) { 749 if ((file->fp = fopen (file->fname, "r")) == NULL)
750 {
412 file->fp = NULL; 751 file->fp = NULL;
413 return NULL; 752 return NULL;
414 } 753 }
415 754
416 fstat(fileno(file->fp), &stats); 755 fstat (fileno (file->fp), &stats);
417 if (S_ISFIFO(stats.st_mode)) { 756 if (S_ISFIFO (stats.st_mode))
757 {
418 if (fcntl(fileno(file->fp), F_SETFL, O_NONBLOCK) < 0) 758 if (fcntl (fileno (file->fp), F_SETFL, O_NONBLOCK) < 0)
419 perror("fcntl"), exit(1); 759 perror ("fcntl"), exit (1);
420 file->inode = 0; 760 file->inode = 0;
761 }
421 } else 762 else
422 file->inode = stats.st_ino; 763 file->inode = stats.st_ino;
423 764
424 if (opt_noinitial) 765 if (opt_noinitial)
425 fseek (file->fp, 0, SEEK_END); 766 fseek (file->fp, 0, SEEK_END);
426 else if (stats.st_size > (listlen + 1) * width) 767 else /* if (stats.st_size > (listlen + 1) * width)
427 { 768 * HACK - 'width' is in pixels - how are we to know how much text will fit?
428 char dummy[255];
429
430 fseek(file->fp, -((listlen + 2) * width), SEEK_END); 769 * fseek (file->fp, -((listlen + 2) * width/10), SEEK_END); */
431 /* the pointer might point halfway some line. Let's 770 fseek (file->fp, -5000, SEEK_END);
432 be nice and skip this damaged line */
433 lineinput(dummy, sizeof(dummy), file->fp);
434 }
435 771
436 file->last_size = stats.st_size; 772 file->last_size = stats.st_size;
437 return file->fp; 773 return file->fp;
438} 774}
439 775
776void
440void reopen(void) 777reopen (void)
441{ 778{
442 struct logfile_entry *e; 779 struct logfile_entry *e;
443 780
444 for (e = loglist; e; e = e->next) { 781 for (e = loglist; e; e = e->next)
782 {
445 if (!e->inode) 783 if (!e->inode)
446 continue; /* skip stdin */ 784 continue; /* skip stdin */
447 785
448 if (e->fp) 786 if (e->fp)
449 fclose(e->fp); 787 fclose (e->fp);
450 /* if fp is NULL we will try again later */ 788 /* if fp is NULL we will try again later */
451 openlog(e); 789 openlog (e);
452 } 790 }
453 791
454 do_reopen = 0; 792 do_reopen = 0;
455} 793}
456 794
795void
457void check_open_files(void) 796check_open_files (void)
458{ 797{
459 struct logfile_entry *e; 798 struct logfile_entry *e;
460 struct stat stats; 799 struct stat stats;
461 800
462 for (e = loglist; e; e = e->next) { 801 for (e = loglist; e; e = e->next)
802 {
463 if (!e->inode) 803 if (!e->inode)
464 continue; /* skip stdin */ 804 continue; /* skip stdin */
465 805
466 if (stat(e->fname, &stats) < 0) { /* file missing? */ 806 if (stat (e->fname, &stats) < 0)
807 { /* file missing? */
467 sleep(1); 808 sleep (1);
468 if (e->fp) 809 if (e->fp)
469 fclose(e->fp); 810 fclose (e->fp);
470 if (openlog(e) == NULL) 811 if (openlog (e) == NULL)
471 break; 812 continue;
472 } 813 if (fstat (fileno (e->fp), &stats) < 0)
814 continue;
815 }
473 816
474 if (stats.st_ino != e->inode) { /* file renamed? */ 817 if (stats.st_ino != e->inode)
818 { /* file renamed? */
475 if (e->fp) 819 if (e->fp)
476 fclose(e->fp); 820 fclose (e->fp);
477 if (openlog(e) == NULL) 821 if (openlog (e) == NULL)
478 break; 822 continue;
479 } 823 if (fstat (fileno (e->fp), &stats) < 0)
824 continue;
825 }
480 826
481 if (stats.st_size < e->last_size) { /* file truncated? */ 827 if (stats.st_size < e->last_size)
828 { /* file truncated? */
482 fseek(e->fp, 0, SEEK_SET); 829 fseek (e->fp, 0, SEEK_SET);
483 e->last_size = stats.st_size; 830 e->last_size = stats.st_size;
484 } 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)
485 } 854 {
486} 855 new->next = linelist;
856 new->next->prev = new;
487 857
488#define SCROLL_UP(lines, listlen) \ 858 new->prev = NULL;
489{ \ 859 linelist = new;
490 int cur_line; \ 860 }
491 for (cur_line = 0; cur_line < (listlen - 1); cur_line++) { \ 861 else
492 strcpy(lines[cur_line].line, lines[cur_line + 1].line); \ 862 {
493 lines[cur_line].color = lines[cur_line + 1].color; \ 863 /* 2 pointers from the new node */
494 } \ 864 new->next = log->last;
495} 865 new->prev = log->last->prev;
496 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
497void main_loop(void) 1087main_loop (void)
498{ 1088{
499 struct linematrix *lines = xmalloc(sizeof(struct linematrix) * listlen); 1089 int lin;
500 int lin, miny, maxy, buflen;
501 char *buf;
502 time_t lastreload; 1090 time_t lastreload;
503 Region region = XCreateRegion(); 1091 Region region = XCreateRegion ();
504 XEvent xev; 1092 XEvent xev;
505 1093 struct logfile_entry *lastprinted = NULL;
506 maxy = 0;
507 miny = win_y + h;
508 buflen = width + 2;
509 buf = xmalloc(buflen);
510 lastreload = time(NULL);
511
512 /* Initialize linematrix */
513 for (lin = 0; lin < listlen; lin++) {
514 lines[lin].line = xmalloc(buflen);
515 strcpy(lines[lin].line, "~");
516 lines[lin].color = GetColor(def_color);
517 }
518
519 if (!opt_noinitial)
520 {
521 while (lineinput(buf, buflen, loglist->fp) != 0) {
522 SCROLL_UP(lines, listlen);
523 /* print the next line */
524 strcpy(lines[listlen - 1].line, buf);
525 }
526
527 redraw ();
528 }
529
530 for (;;) {
531 int need_update = 0;
532 struct logfile_entry *current; 1094 struct logfile_entry *current;
533 static struct logfile_entry *lastprinted = NULL; 1095 int need_update = 1;
534 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 {
535 /* read logs */ 1128 /* read logs */
536 for (current = loglist; current; current = current->next) { 1129 for (current = loglist; current; current = current->next)
1130 {
537 if (!current->fp) 1131 if (!current->fp)
538 continue; /* skip missing files */ 1132 continue; /* skip missing files */
539 1133
540 clearerr(current->fp); 1134 clearerr (current->fp);
541 1135
542 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
543 /* print filename if any, and if last line was from 1159 /* print filename if any, and if last line was from
544 different file */ 1160 * different file */
545 if (!opt_nofilename && 1161 if (lastprinted != current)
546 !(lastprinted && lastprinted == current) && 1162 {
547 current->desc[0]) { 1163 current->last = 0;
548 SCROLL_UP(lines, listlen); 1164 if (!opt_nofilename && current->desc[0])
549 sprintf(lines[listlen - 1].line, "[%s]", current->desc); 1165 {
550 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 }
551 } 1170 }
552 1171
553 SCROLL_UP(lines, listlen); 1172 /* if we're dealing with partial lines, and the last
554 strcpy(lines[listlen - 1].line, buf); 1173 * time we showed the line it wasn't finished ... */
555 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);
556 1192
557 lastprinted = current; 1193 current->buf = 0;
558 need_update = 1; 1194 lastprinted = current;
559 } 1195 }
560 } 1196 }
561 1197
562 if (need_update) 1198 if (need_update)
563 redraw(); 1199 {
564 else { 1200 redraw (0);
1201 need_update = 0;
1202 }
1203 else
1204 {
565 XFlush(disp); 1205 XFlush (disp);
1206
566 if (!XPending(disp)) { 1207 if (!XPending (disp))
567 fd_set fdr; 1208 {
1209 fd_set fdr;
568 struct timeval to = interval; 1210 struct timeval to = interval;
569 1211
570 FD_ZERO(&fdr); 1212 FD_ZERO (&fdr);
571 FD_SET(ConnectionNumber(disp), &fdr); 1213 FD_SET (ConnectionNumber (disp), &fdr);
572 select(ConnectionNumber(disp) + 1, &fdr, 0, 0, &to); 1214 select (ConnectionNumber (disp) + 1, &fdr, 0, 0, &to);
573 } 1215 }
574 } 1216 }
575 1217
576 check_open_files(); 1218 check_open_files ();
577 1219
578 if (do_reopen) 1220 if (do_reopen)
579 reopen(); 1221 reopen ();
580 1222
581 /* we ignore possible errors due to window resizing &c */ 1223 /* we ignore possible errors due to window resizing &c */
582 while (XPending(disp)) { 1224 while (XPending (disp))
1225 {
583 XNextEvent(disp, &xev); 1226 XNextEvent (disp, &xev);
1227
584 switch (xev.type) { 1228 switch (xev.type)
1229 {
585 case Expose: 1230 case Expose:
586 { 1231 {
587 XRectangle r; 1232 XRectangle r;
588 1233
589 r.x = xev.xexpose.x; 1234 r.x = xev.xexpose.x;
590 r.y = xev.xexpose.y; 1235 r.y = xev.xexpose.y;
591 r.width = xev.xexpose.width; 1236 r.width = xev.xexpose.width;
592 r.height = xev.xexpose.height; 1237 r.height = xev.xexpose.height;
1238
593 XUnionRectWithRegion(&r, region, region); 1239 XUnionRectWithRegion (&r, region, region);
594 if (miny > r.y) 1240 }
595 miny = r.y; 1241 break;
596 if (maxy < r.y + r.height) 1242 default:
597 maxy = r.y + r.height;
598 }
599 break;
600 default:
601#ifdef DEBUGMODE 1243#ifdef DEBUGMODE
602 fprintf(stderr, "PANIC! Unknown event %d\n", xev.type); 1244 fprintf (stderr, "PANIC! Unknown event %d\n", xev.type);
603#endif 1245#endif
604 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 }
605 } 1272 }
606 }
607
608 /* reload if requested */
609 if (reload && lastreload + reload < time(NULL)) {
610 if (command)
611 system(command);
612
613 reopen();
614 lastreload = time(NULL);
615 }
616
617 if (!XEmptyRegion(region)) {
618 XSetRegion(disp, WinGC, region);
619 refresh(lines, miny, maxy);
620 XDestroyRegion(region);
621 region = XCreateRegion();
622 maxy = 0;
623 miny = win_y + h;
624 }
625 }
626} 1273}
627 1274
628 1275
1276int
629int main(int argc, char *argv[]) 1277main (int argc, char *argv[])
630{ 1278{
631 int i; 1279 int i;
632 int opt_daemonize = 0; 1280 int opt_daemonize = 0;
1281 int opt_partial = 0, file_count = 0;
633#if HAS_REGEX 1282#if HAS_REGEX
634 char *transform = NULL; 1283 char *transform = NULL;
635#endif 1284#endif
636 1285
637 setlocale (LC_CTYPE, ""); /* try to initialize the locale. */ 1286 setlocale (LC_CTYPE, ""); /* try to initialize the locale. */
638 1287
639 /* window needs to be initialized before colorlookups can be done */
640 /* just a dummy to get the color lookups right */
641 geom_mask = NoValue;
642 InitWindow();
643
644 for (i = 1; i < argc; i++) { 1288 for (i = 1; i < argc; i++)
1289 {
645 const char *arg = argv[i]; 1290 const char *arg = argv[i];
646 1291
647 if (arg[0] == '-' && arg[1] != '\0' && arg[1] != ',') { 1292 if (arg[0] == '-' && arg[1] != '\0' && arg[1] != ',')
1293 {
648 if (arg[1] == '-') 1294 if (arg[1] == '-')
649 arg++; 1295 arg++;
650 1296
651 if (!strcmp(arg, "-?") || 1297 if (!strcmp (arg, "-?") ||
652 !strcmp(arg, "-help") || !strcmp(arg, "-h")) 1298 !strcmp (arg, "-help") || !strcmp (arg, "-h"))
653 display_help(argv[0]); 1299 display_help (argv[0]);
654 else if (!strcmp(arg, "-V")) 1300 else if (!strcmp (arg, "-V"))
655 display_version(); 1301 display_version ();
656 else if (!strcmp(arg, "-g") || !strcmp(arg, "-geometry")) 1302 else if (!strcmp (arg, "-g") || !strcmp (arg, "-geometry"))
657 geom_mask = XParseGeometry(argv[++i], 1303 geom_mask =
658 &win_x, &win_y, &width, &listlen); 1304 XParseGeometry (argv[++i], &win_x, &win_y, &width, &height);
659 else if (!strcmp(arg, "-display")) 1305 else if (!strcmp (arg, "-display"))
660 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];
661 else if (!strcmp(arg, "-font") || !strcmp(arg, "-fn")) 1311 else if (!strcmp (arg, "-font") || !strcmp (arg, "-fn"))
662 fontname = argv[++i]; 1312 fontname = argv[++i];
663#if HAS_REGEX 1313#if HAS_REGEX
664 else if (!strcmp(arg, "-t")) 1314 else if (!strcmp (arg, "-t"))
665 transform = argv[++i]; 1315 {
1316 transform = argv[++i];
1317 transform_to = argv[++i];
1318 printf ("transform: '%s' to '%s'\n", transform, transform_to);
1319 }
666#endif 1320#endif
667 else if (!strcmp(arg, "-fork") || !strcmp(arg, "-f")) 1321 else if (!strcmp (arg, "-fork") || !strcmp (arg, "-f"))
668 opt_daemonize = 1; 1322 opt_daemonize = 1;
669 else if (!strcmp(arg, "-reload")) { 1323 else if (!strcmp (arg, "-reload"))
670 reload = atoi(argv[++i]); 1324 {
671 command = argv[++i]; 1325 reload = atoi (argv[++i]);
672 } 1326 command = argv[++i];
1327 }
673 else if (!strcmp(arg, "-shade")) 1328 else if (!strcmp (arg, "-shade"))
674 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;
675 else if (!strcmp(arg, "-frame")) 1336 else if (!strcmp (arg, "-frame"))
676 opt_frame = 1; 1337 opt_frame = 1;
677 else if (!strcmp(arg, "-no-filename")) 1338 else if (!strcmp (arg, "-no-filename"))
678 opt_nofilename = 1; 1339 opt_nofilename = 1;
679 else if (!strcmp(arg, "-reverse")) 1340 else if (!strcmp (arg, "-reverse"))
680 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;
681 else if (!strcmp(arg, "-color")) 1352 else if (!strcmp (arg, "-color"))
682 def_color = argv[++i]; 1353 def_color = argv[++i];
683 else if (!strcmp(arg, "-noinitial")) 1354 else if (!strcmp (arg, "-noinitial"))
684 opt_noinitial = 1; 1355 opt_noinitial = 1;
685 else if (!strcmp(arg, "-id")) 1356 else if (!strcmp (arg, "-id"))
686 root = atoi (argv[++i]); 1357 {
1358 unsigned long id;
1359
1360 if (sscanf (argv[++i], "%li", &id) == 1 && id)
1361 root = id;
1362 }
687 else if (!strcmp(arg, "-interval") || !strcmp(arg, "-i")) { 1363 else if (!strcmp (arg, "-interval") || !strcmp (arg, "-i"))
1364 {
688 double iv = atof(argv[++i]); 1365 double iv = atof (argv[++i]);
689 1366
690 interval.tv_sec = (int) iv; 1367 interval.tv_sec = (int) iv;
691 interval.tv_usec = (iv - interval.tv_sec) * 1e6; 1368 interval.tv_usec = (iv - interval.tv_sec) * 1e6;
692 } else { 1369 }
1370 else
1371 {
693 fprintf(stderr, "Unknown option '%s'.\n" 1372 fprintf (stderr, "Unknown option '%s'.\n"
694 "Try --help for more information.\n", arg); 1373 "Try --help for more information.\n", arg);
695 exit(1); 1374 exit (1);
696 } 1375 }
697 } else { /* it must be a filename */ 1376 }
1377 else
1378 { /* it must be a filename */
698 struct logfile_entry *e; 1379 struct logfile_entry *e;
699 const char *fname, *desc, *fcolor = def_color; 1380 const char *fname, *desc, *fcolor = def_color;
700 char *p; 1381 char *p;
701 1382
1383 file_count++;
1384
702 /* this is not foolproof yet (',' in filenames are not allowed) */ 1385 /* this is not foolproof yet (',' in filenames are not allowed) */
703 fname = desc = arg; 1386 fname = desc = arg;
704 if ((p = strchr(arg, ','))) { 1387 if ((p = strchr (arg, ',')))
705 *p = '\0'; 1388 {
706 fcolor = p + 1; 1389 *p = '\0';
1390 fcolor = p + 1;
707 1391
708 if ((p = strchr(fcolor, ','))) { 1392 if ((p = strchr (fcolor, ',')))
709 *p = '\0'; 1393 {
710 desc = p + 1; 1394 *p = '\0';
711 } 1395 desc = p + 1;
712 } 1396 }
1397 }
713 1398
714 e = xmalloc(sizeof(struct logfile_entry)); 1399 e = xmalloc (sizeof (struct logfile_entry));
1400 e->partial = 0;
1401 e->buf = 0;
1402
715 if (arg[0] == '-' && arg[1] == '\0') { 1403 if (arg[0] == '-' && arg[1] == '\0')
1404 {
716 if ((e->fp = fdopen(0, "r")) == NULL) 1405 if ((e->fp = fdopen (0, "r")) == NULL)
717 perror("fdopen"), exit(1); 1406 perror ("fdopen"), exit (1);
718 if (fcntl(0, F_SETFL, O_NONBLOCK) < 0) 1407 if (fcntl (0, F_SETFL, O_NONBLOCK) < 0)
719 perror("fcntl"), exit(1); 1408 perror ("fcntl"), exit (1);
720 e->fname = NULL; 1409
721 e->inode = 0; 1410 e->fname = NULL;
1411 e->inode = 0;
1412 if (desc == arg)
722 e->desc = xstrdup("stdin"); 1413 e->desc = xstrdup ("stdin");
723 } else { 1414 else
724 int l; 1415 e->desc = xstrdup (desc);
725 1416 }
1417 else
1418 {
726 e->fname = xstrdup(fname); 1419 e->fname = xstrdup (fname);
727 if (openlog(e) == NULL) 1420
1421 if (openlog (e) == NULL)
728 perror(fname), exit(1); 1422 perror (fname), exit (1);
729 1423
730 l = strlen(desc); 1424 e->desc = xstrdup (desc);
731 if (l > width - 2) /* must account for [ ] */ 1425 }
732 l = width - 2; 1426
733 e->desc = xmalloc(l + 1); 1427 e->colorname = fcolor;
734 memcpy(e->desc, desc, l); 1428 e->partial = 0;
735 *(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 }
736 } 1440 }
737 1441
738 e->color = GetColor(fcolor);
739 e->next = NULL;
740
741 if (!loglist) 1442 if (!loglist)
742 loglist = e;
743 if (loglist_tail)
744 loglist_tail->next = e;
745 loglist_tail = e;
746 }
747 } 1443 {
748
749 if (!loglist) {
750 fprintf(stderr, "You did not specify any files to tail\n" 1444 fprintf (stderr, "You did not specify any files to tail\n"
751 "use %s --help for help\n", argv[0]); 1445 "use %s --help for help\n", argv[0]);
752 exit(1); 1446 exit (1);
1447 }
1448
1449 if (opt_update && opt_whole)
753 } 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;
754 1477
755#if HAS_REGEX 1478#if HAS_REGEX
756 if (transform) { 1479 if (transform)
757 int i; 1480 {
1481 int i;
758 1482
759 transformre = xmalloc(sizeof(transformre)); 1483 printf ("compiling regexp '%s'\n", transform);
1484 transformre = xmalloc (sizeof (regex_t));
760 i = regcomp(&transformre, transform, REG_EXTENDED); 1485 i = regcomp (transformre, transform, REG_EXTENDED);
761 if (i != 0) { 1486 if (i != 0)
1487 {
762 char buf[512]; 1488 char buf[512];
763 1489
764 regerror(i, &transformre, buf, sizeof(buf)); 1490 regerror (i, transformre, buf, sizeof (buf));
765 fprintf(stderr, "Cannot compile regular expression: %s\n", buf); 1491 fprintf (stderr, "Cannot compile regular expression: %s\n", buf);
766 } 1492 }
1493 else
1494 printf ("compiled '%s' OK to %x\n", transform, (int)transformre);
767 } 1495 }
768#endif 1496#endif
769 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
770 InitWindow(); 1524 InitWindow ();
771 1525
772 install_signal(SIGINT, blank_window); 1526 install_signal (SIGINT, blank_window);
773 install_signal(SIGQUIT, blank_window); 1527 install_signal (SIGQUIT, blank_window);
774 install_signal(SIGTERM, blank_window); 1528 install_signal (SIGTERM, blank_window);
775 install_signal(SIGHUP, force_reopen); 1529 install_signal (SIGHUP, force_reopen);
776 install_signal(SIGUSR1, list_files); 1530 install_signal (SIGUSR1, list_files);
777 install_signal(SIGUSR2, force_refresh); 1531 install_signal (SIGUSR2, force_refresh);
778 1532
779 if (opt_daemonize) 1533 if (opt_daemonize)
780 daemonize(); 1534 daemonize ();
781 1535
782 main_loop(); 1536 main_loop ();
783 1537
784 exit(1); /* to make gcc -Wall stop complaining */ 1538 exit (1); /* to make gcc -Wall stop complaining */
785} 1539}
786 1540
1541void
787void install_signal(int sig, void (*handler)(int)) 1542install_signal (int sig, void (*handler) (int))
788{ 1543{
789 struct sigaction action; 1544 struct sigaction action;
790 1545
791 action.sa_handler = handler; 1546 action.sa_handler = handler;
792 sigemptyset(&action.sa_mask); 1547 sigemptyset (&action.sa_mask);
793 action.sa_flags = SA_RESTART; 1548 action.sa_flags = SA_RESTART;
1549
794 if (sigaction(sig, &action, NULL) < 0) 1550 if (sigaction (sig, &action, NULL) < 0)
795 fprintf(stderr, "sigaction(%d): %s\n", sig, strerror(errno)), exit(1); 1551 fprintf (stderr, "sigaction (%d): %s\n", sig, strerror (errno)), exit (1);
796} 1552}
797 1553
1554void *
798void *xstrdup(const char *string) 1555xstrdup (const char *string)
799{ 1556{
800 void *p; 1557 void *p;
801 1558
802 while ((p = strdup(string)) == NULL) { 1559 while ((p = strdup (string)) == NULL)
1560 {
803 fprintf(stderr, "Memory exausted."); 1561 fprintf (stderr, "Memory exhausted in xstrdup ().\n");
804 sleep(10); 1562 sleep (10);
805 } 1563 }
1564
806 return p; 1565 return p;
807} 1566}
808 1567
1568void *
809void *xmalloc(size_t size) 1569xmalloc (size_t size)
810{ 1570{
811 void *p; 1571 void *p;
812 1572
813 while ((p = malloc(size)) == NULL) { 1573 while ((p = malloc (size)) == NULL)
1574 {
814 fprintf(stderr, "Memory exausted."); 1575 fprintf (stderr, "Memory exhausted in xmalloc ().\n");
815 sleep(10); 1576 sleep (10);
816 } 1577 }
1578
817 return p; 1579 return p;
818} 1580}
819 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
820void display_help(char *myname) 1597display_help (char *myname)
821{ 1598{
822 printf("Usage: %s [options] file1[,color[,desc]] " 1599 printf ("Usage: %s [options] file1[,color[,desc]]"
823 "[file2[,color[,desc]] ...]\n", myname); 1600 "[options] [file2[,color[,desc]] ...]\n", myname);
824 printf(" -g | -geometry geometry -g WIDTHxHEIGHT+X+Y\n" 1601 printf (" -g | -geometry geometry -g WIDTHxHEIGHT+X+Y\n"
825 " -color color use color $color as default\n" 1602 " -color color use color $color as default\n"
826 " -reload sec command reload after $sec and run command\n" 1603 " -reload sec command reload after $sec and run command\n"
827 " by default -- 3 mins\n"
828 " -id id window id to use instead of the root window\n" 1604 " -id id window id to use instead of the root window\n"
829 " -font FONTSPEC (-fn) font to use\n" 1605 " -font FONTSPEC (-fn) font to use\n"
830 " -f | -fork fork into background\n" 1606 " -f | -fork fork into background\n"
831 " -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"
832 " -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"
833 " -noinitial don't display the last file lines on\n" 1617 " -noinitial don't display the last file lines on\n"
834 " startup\n" 1618 " startup\n"
835 " -i | -interval seconds interval between checks (fractional\n" 1619 " -i | -interval seconds interval between checks (fractional\n"
836 " values o.k.). Default 3\n" 1620 " values o.k.). Default 2.4 seconds\n"
837 " -V display version information and exit\n" 1621 " -V display version information and exit\n"
838 "\n"); 1622 "\n");
839 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 "
840 "/var/log/secure,red,'ALERT'\n", myname); 1624 "/var/log/secure,red,'ALERT'\n", myname);
841 exit(0); 1625 exit (0);
842} 1626}
843 1627
1628void
844void display_version(void) { 1629display_version (void)
1630{
845 printf("root-tail version " VERSION "\n"); 1631 printf ("root-tail version " VERSION "\n");
846 exit(0); 1632 exit (0);
847} 1633}
848 1634
1635int
849int daemonize(void) { 1636daemonize (void)
1637{
1638 pid_t pid;
1639
850 switch (fork()) { 1640 switch (pid = fork ())
1641 {
851 case -1: 1642 case -1:
852 return -1; 1643 return -1;
853 case 0: 1644 case 0:
854 break; 1645 break;
855 default: 1646 default:
856 _exit(0); 1647 /*printf ("%d\n", pid);*/
1648 exit (0);
857 } 1649 }
858 1650
859 if (setsid() == -1) 1651 if (setsid () == -1)
860 return -1; 1652 return -1;
861 1653
862 return 0; 1654 return 0;
863} 1655}

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines