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.11 by pcg, Fri Mar 26 13:14:54 2004 UTC vs.
Revision 1.47 by pcg, Thu Apr 1 13:37:30 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 * 3 * Copyright 2000,2001,2002,2003,2004
4 * Original version by Mike Baker, then maintained by pcg@goof.com. 4 * Marc Lehmann <pcg@goof.com>,
5 * and many others, see README
6 *
7 * Original version by Mike Baker.
5 * 8 *
6 * This program is free software; you can redistribute it and/or modify 9 * 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 10 * 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 11 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version. 12 * (at your option) any later version.
31#include <sys/stat.h> 34#include <sys/stat.h>
32#include <sys/types.h> 35#include <sys/types.h>
33#include <locale.h> 36#include <locale.h>
34#include <ctype.h> 37#include <ctype.h>
35#include <stdarg.h> 38#include <stdarg.h>
39#include <X11/Xlib.h>
40#include <X11/Xatom.h>
41#include <X11/Xutil.h>
42
36#if HAS_REGEX 43#if HAS_REGEX
37#include <regex.h> 44#include <regex.h>
38#endif 45#endif
39#include <X11/Xlib.h> 46
40#include <X11/Xatom.h> 47#define SHADE_X 2
41#include <X11/Xutil.h> 48#define SHADE_Y 2
42 49
43/* data structures */ 50/* data structures */
44struct logfile_entry 51struct logfile_entry
45{ 52{
46 struct logfile_entry *next; 53 struct logfile_entry *next;
53 off_t last_size; /* file size at the last check */ 60 off_t last_size; /* file size at the last check */
54 unsigned long color; /* color to be used for printing */ 61 unsigned long color; /* color to be used for printing */
55 int partial; /* true if the last line isn't complete */ 62 int partial; /* true if the last line isn't complete */
56 int lastpartial; /* true if the previous output wasn't complete */ 63 int lastpartial; /* true if the previous output wasn't complete */
57 int index; /* index into linematrix of a partial line */ 64 int index; /* index into linematrix of a partial line */
65 int modified; /* true if line is modified & needs displaying */
58}; 66};
59 67
60struct linematrix 68struct linematrix
61{ 69{
62 char *line; 70 char *line;
71 int len;
63 unsigned long color; 72 unsigned long color;
64}; 73};
65 74
75struct displaymatrix
76{
77 char *line;
78 int len;
79 int buffer_size;
80 unsigned long color;
81};
82
66/* global variables */ 83/* global variables */
84struct linematrix *lines;
85struct displaymatrix *display;
67int width = STD_WIDTH, height = STD_HEIGHT, listlen; 86int width = STD_WIDTH, height = STD_HEIGHT, listlen;
68int win_x = LOC_X, win_y = LOC_Y; 87int win_x = LOC_X, win_y = LOC_Y;
69int font_descent, font_height; 88int font_ascent, font_height;
89int effect_x_space, effect_y_space; /* how much space does shading / outlining take up */
90int effect_x_offset, effect_y_offset; /* and how does it offset the usable space */
70int do_reopen; 91int do_reopen;
71struct timeval interval = { 3, 0 }; 92struct timeval interval = { 2, 400000 };
72XFontSet fontset; 93XFontSet fontset;
73 94
74/* command line options */ 95/* command line options */
75int opt_noinitial, opt_shade, opt_frame, opt_reverse, opt_nofilename, 96int opt_noinitial, opt_shade, opt_frame, opt_reverse, opt_nofilename,
76 opt_whole, opt_update, geom_mask, reload = 0; 97 opt_outline, opt_noflicker, opt_whole, opt_update, opt_wordwrap,
98 geom_mask, reload = 0;
77const char *command = NULL, 99const char *command = NULL,
78 *fontname = USE_FONT, *dispname = NULL, *def_color = DEF_COLOR, 100 *fontname = USE_FONT, *dispname = NULL, *def_color = DEF_COLOR,
79 *continuation = "[+]"; 101 *continuation = "[+]";
80 102
81struct logfile_entry *loglist = NULL, *loglist_tail = NULL; 103struct logfile_entry *loglist = NULL, *loglist_tail = NULL;
90 regex_t from; 112 regex_t from;
91 const char *to; 113 const char *to;
92 struct re_list *next; 114 struct re_list *next;
93}; 115};
94struct re_list *re_head, *re_tail; 116struct re_list *re_head, *re_tail;
117char *transform_to = NULL;
118regex_t *transformre;
95#endif 119#endif
96 120
97 121
98/* prototypes */ 122/* prototypes */
99void list_files (int); 123void list_files (int);
101void force_refresh (int); 125void force_refresh (int);
102void blank_window (int); 126void blank_window (int);
103 127
104void InitWindow (void); 128void InitWindow (void);
105unsigned long GetColor (const char *); 129unsigned long GetColor (const char *);
106void redraw (void); 130void redraw (int);
107void refresh (struct linematrix *, int, int); 131void refresh (int, int, int, int);
108 132
109void transform_line (char *s); 133void transform_line (char *s);
110int lineinput (struct logfile_entry *); 134int lineinput (struct logfile_entry *);
111void reopen (void); 135void reopen (void);
112void check_open_files (void); 136void check_open_files (void);
113FILE *openlog (struct logfile_entry *); 137FILE *openlog (struct logfile_entry *);
114void main_loop (void); 138static void main_loop (void);
115 139
116void display_version (void); 140void display_version (void);
117void display_help (char *); 141void display_help (char *);
118void install_signal (int, void (*)(int)); 142void install_signal (int, void (*)(int));
119void *xstrdup (const char *); 143void *xstrdup (const char *);
120void *xmalloc (size_t); 144void *xmalloc (size_t);
145void *xrealloc (void *, size_t);
121int daemonize (void); 146int daemonize (void);
122 147
123/* signal handlers */ 148/* signal handlers */
124void 149void
125list_files (int dummy) 150list_files (int dummy)
138} 163}
139 164
140void 165void
141force_refresh (int dummy) 166force_refresh (int dummy)
142{ 167{
143 XClearArea (disp, root, win_x, win_y, width, height, False);
144 redraw (); 168 redraw (1);
145} 169}
146 170
147void 171void
148blank_window (int dummy) 172blank_window (int dummy)
149{ 173{
150 XClearArea (disp, root, win_x, win_y, width, height, False); 174 XClearArea (disp, root, win_x - 2, win_y - 2, width + 5, height + 5, False);
151 XFlush (disp); 175 XFlush (disp);
152 exit (0); 176 exit (0);
153} 177}
154 178
155/* X related functions */ 179/* X related functions */
284 308
285 { 309 {
286 XFontSetExtents *xfe = XExtentsOfFontSet (fontset); 310 XFontSetExtents *xfe = XExtentsOfFontSet (fontset);
287 311
288 font_height = xfe->max_logical_extent.height; 312 font_height = xfe->max_logical_extent.height;
289 font_descent = xfe->max_logical_extent.y; 313 font_ascent = -xfe->max_logical_extent.y;
290 } 314 }
291 315
292 if (geom_mask & XNegative) 316 if (geom_mask & XNegative)
293 win_x = win_x + ScreenWidth - width; 317 win_x = win_x + ScreenWidth - width;
294 if (geom_mask & YNegative) 318 if (geom_mask & YNegative)
295 win_y = win_y + ScreenHeight - height; 319 win_y = win_y + ScreenHeight - height;
296 320
321 if (opt_outline)
322 {
323 /* adding outline increases the total width and height by 2
324 pixels each, and offsets the text one pixel right and one
325 pixel down */
326 effect_x_space = effect_y_space = 2;
327 effect_x_offset = effect_y_offset = 1;
328 }
329 else if (opt_shade)
330 {
331 /* adding a shadow increases the space used */
332 effect_x_space = abs(SHADE_X);
333 effect_y_space = abs(SHADE_Y);
334 /* if the shadow is to the right and below then we don't need
335 * to move the text to make space for it, but shadows to the left
336 * and above need accomodating */
337 effect_x_offset = SHADE_X > 0 ? 0 : -SHADE_X;
338 effect_y_offset = SHADE_Y > 0 ? 0 : -SHADE_Y;
339 }
340 else
341 {
342 effect_x_space = effect_y_space = 0;
343 effect_x_offset = effect_y_offset = 0;
344 }
345
346 /* if we are using -shade or -outline, there will be less usable
347 * space for output */
297 listlen = height / font_height; 348 listlen = (height - effect_y_space) / font_height;
298 349
299 if (!listlen) 350 if (!listlen)
300 { 351 {
301 fprintf (stderr, "height too small for a single line, setting to %d\n", 352 fprintf (stderr, "height too small for a single line, setting to %d\n",
302 font_height); 353 font_height);
303 listlen = 1; 354 listlen = 1;
304 } 355 }
305 356
306 height = listlen * font_height; 357 /* leave the height how the user requested it. it might not all be
307 358 * used, but this will allow the geometry to be tuned more accurately
359 * (with the -frame option)
360 * the old code did this:
361 * height = listlen * font_height + effect_y_space; */
362
308 XSelectInput (disp, root, ExposureMask | FocusChangeMask); 363 XSelectInput (disp, root, ExposureMask | FocusChangeMask);
309} 364}
310 365
311char *
312detabificate (char *s)
313{
314 char *out;
315 int i, j;
316
317 out = malloc (8 * strlen (s) + 1);
318
319 for (i = 0, j = 0; s[i]; i++)
320 {
321 if (s[i] == '\t')
322 do
323 out[j++] = ' ';
324 while (j % 8);
325 else
326 out[j++] = s[i];
327 }
328
329 out[j] = '\0';
330 return out;
331}
332
333/* 366/*
334 * redraw does a complete redraw, rather than an update (i.e. the area 367 * if redraw() is passwd a non-zero argument, it does a complete
335 * gets cleared first) 368 * redraw, rather than an update. if the argument is zero (and
369 * -noflicker is in effect) then only the lines which have changed
370 * since the last draw are redrawn.
371 *
336 * the rest is handled by regular refresh()'es 372 * the rest is handled by regular refresh()'es
337 */ 373 */
338void 374void
339redraw (void) 375redraw (int redraw_all)
340{ 376{
341 XClearArea (disp, root, win_x, win_y, width, height, True); 377 XSetClipMask (disp, WinGC, None);
378 refresh (0, 32768, 1, redraw_all);
342} 379}
343 380
344/* Just redraw everything without clearing (i.e. after an EXPOSE event) */ 381/* Just redraw everything without clearing (i.e. after an EXPOSE event) */
345void 382void
346refresh (struct linematrix *lines, int miny, int maxy) 383refresh (int miny, int maxy, int clear, int refresh_all)
347{ 384{
348 int lin; 385 int lin;
349 int offset = (listlen + 1) * font_height; 386 int offset = listlen * font_height + font_ascent + effect_y_offset;
350 unsigned long black_color = GetColor ("black"); 387 unsigned long black_color = GetColor ("black");
351 388
352 miny -= win_y + font_height; 389 miny -= win_y + font_height;
353 maxy -= win_y - font_height; 390 maxy -= win_y - font_height;
354 391
392 if (clear && !opt_noflicker)
393 XClearArea (disp, root, win_x, win_y, width, height, False);
394
355 for (lin = listlen; lin--;) 395 for (lin = listlen; lin--;)
356 { 396 {
357 struct linematrix *line =
358 lines + (opt_reverse ? listlen - lin - 1 : lin); 397 struct linematrix *line = lines + (opt_reverse ? listlen - lin - 1 : lin);
398 struct displaymatrix *display_line = display + lin;
359 399
360 offset -= font_height; 400 offset -= font_height;
361 401
362 if (offset < miny || offset > maxy) 402 if (offset < miny || offset > maxy)
363 continue; 403 continue;
364 404
365 if (opt_shade) 405 /* if this line is a different than it was, then it
406 * needs displaying */
407 if (!opt_noflicker
408 || refresh_all
409 || display_line->len != line->len
410 || display_line->color != line->color
411 || memcmp (display_line->line, line->line, line->len))
366 { 412 {
413 /* don't bother updating the record of what has been
414 * displayed if -noflicker isn't in effect, since we redraw
415 * the whole display every time anyway */
416 if (opt_noflicker)
417 {
418 /* update the record of what has been displayed;
419 * first make sure the buffer is big enough */
420 if (display_line->buffer_size < line->len)
421 {
422 display_line->buffer_size = line->len;
423 display_line->line = xrealloc (display_line->line, display_line->buffer_size);
424 }
425
426 display_line->len = line->len;
427 display_line->color = line->color;
428 memcpy (display_line->line, line->line, line->len);
429
430 if (clear)
431 XClearArea (disp, root, win_x, win_y + offset - font_ascent,
432 width + effect_x_space, font_height + effect_y_space, False);
433 }
434
435 if (opt_outline)
436 {
437 int x, y;
367 XSetForeground (disp, WinGC, black_color); 438 XSetForeground (disp, WinGC, black_color);
439
440 for (x = -1; x <= 1; x += 2)
441 for (y = -1; y <= 1; y += 2)
442 XmbDrawString (disp, root, fontset, WinGC,
443 win_x + effect_x_offset + x,
444 win_y + y + offset,
445 line->line, line->len);
446 }
447 else if (opt_shade)
448 {
449 XSetForeground (disp, WinGC, black_color);
368 XmbDrawString (disp, root, fontset, WinGC, win_x + 2, 450 XmbDrawString (disp, root, fontset, WinGC,
369 win_y + offset + 2, line->line, strlen (line->line)); 451 win_x + effect_x_offset + SHADE_X,
452 win_y + offset + SHADE_Y,
453 line->line, line->len);
370 } 454 }
371 455
372 XSetForeground (disp, WinGC, line->color); 456 XSetForeground (disp, WinGC, line->color);
373 XmbDrawString (disp, root, fontset, WinGC, win_x, win_y + offset, 457 XmbDrawString (disp, root, fontset, WinGC,
458 win_x + effect_x_offset,
459 win_y + offset,
374 line->line, strlen (line->line)); 460 line->line, line->len);
461 }
375 } 462 }
376 463
377 if (opt_frame) 464 if (opt_frame)
465 {
466 XSetForeground (disp, WinGC, GetColor (def_color));
378 XDrawRectangle (disp, root, WinGC, win_x, win_y, width, height); 467 XDrawRectangle (disp, root, WinGC, win_x - 0, win_y - 0, width - 1, height - 1);
468 }
379} 469}
380 470
381#if HAS_REGEX 471#if HAS_REGEX
382void 472void
383transform_line (char *s) 473transform_line (char *s)
395 if (transformre) 485 if (transformre)
396 { 486 {
397 int i; 487 int i;
398 regmatch_t matched[16]; 488 regmatch_t matched[16];
399 489
400 i = regexec (&transformre, string, 16, matched, 0); 490 i = regexec (transformre, s, 16, matched, 0);
401 if (i == 0) 491 if (i == 0)
402 { /* matched */ 492 { /* matched */
493 int match_start = matched[0].rm_so;
494 int match_end = matched[0].rm_eo;
495 int old_len = match_end - match_start;
496 int new_len = strlen (transform_to);
497 int old_whole_len = strlen (s);
498
499 printf ("regexp was matched by '%s' - replace with '%s'\n", s, transform_to);
500 printf ("match is from %d to %d\n", match_start, match_end);
501 if (new_len > old_len)
502 s = xrealloc(s, old_whole_len + new_len - old_len);
503
504 if (new_len != old_len)
505 {
506 memcpy(s + match_end + new_len - old_len,
507 s + match_end,
508 old_whole_len - match_end);
509 s[old_whole_len + new_len - old_len] = '\0';
403 } 510 }
511
512 memcpy (s + match_start,
513 transform_to,
514 new_len);
515 printf ("transformed to '%s'\n", s);
516 }
517 else
518 {
519 printf ("regexp was not matched by '%s'\n", s);
520 }
404 } 521 }
405} 522}
406#endif 523#endif
407 524
525char *
526concat_line (const char *p1, const char *p2)
527{
528 int l1 = p1 ? strlen (p1) : 0;
529 int l2 = strlen (p2);
530 char *r = xmalloc (l1 + l2 + 1);
531
532 memcpy (r, p1, l1);
533 memcpy (r + l1, p2, l2);
534 r[l1 + l2] = 0;
535
536 return r;
537}
408 538
409/* 539/*
410 * This routine should read 'width' characters and not more. However, 540 * This routine can read a line of any length if it is called enough times.
411 * we really want to read width + 1 characters if the last char is a '\n',
412 * which we should remove afterwards. So, read width+1 chars and ungetc
413 * the last character if it's not a newline. This means 'string' must be
414 * width + 2 wide!
415 */ 541 */
416int 542int
417lineinput (struct logfile_entry *logfile) 543lineinput (struct logfile_entry *logfile)
418{ 544{
419 char *string = logfile->buf; 545 char buff[1024], *p;
420 int slen = width + 2; 546 int ch;
421 FILE *f = logfile->fp; 547 /* HACK-2: add on the length of any partial line which we will be appending to */
548 int ofs = logfile->buf ? strlen (logfile->buf) : 0;
422 549
423 int len = strlen (string); 550 /* this loop ensures that the whole line is read, even if it's
424 int partial = logfile->partial; 551 * longer than the buffer. we need to do this because when --whole
425 552 * is in effect we don't know whether to display the line or not
553 * until we've seen how (ie. whether) it ends */
426 do 554 do
427 { 555 {
428 if (fgets (string + len, slen - len, f) == NULL) /* EOF or Error */ 556 p = buff;
429 return 0; 557 do
558 {
559 ch = fgetc (logfile->fp);
430 560
431 len = strlen (string); 561 if (ch == '\n' || ch == EOF)
432 } 562 break;
433 while (len == 0); 563 else if (ch == '\r')
434 564 continue; /* skip */
435 logfile->partial = 0; 565 else if (ch == '\t')
436
437 if (string[len - 1] == '\n')
438 /* if the string ends in a newline, delete the newline */
439 string[len - 1] = '\0'; /* erase newline */
440 else if (len >= slen - 1)
441 { 566 {
442 /* otherwise if we've read one too many characters, un-read the last one and delete it */ 567 do
443 ungetc (string[len - 1], f); 568 {
444 string[len - 1] = '\0'; 569 *p++ = ' ';
570 ofs++;
571 }
572 while (ofs & 7);
445 } 573 }
446 else if (opt_whole) 574 else
575 {
576 *p++ = ch;
577 ofs++;
578 }
579 }
580 while (p < buff + (sizeof buff) - 8 - 1);
581
582 if (p == buff && ch == EOF)
583 return 0;
584
585 *p = 0;
586
587 p = concat_line (logfile->buf, buff);
588 free (logfile->buf); logfile->buf = p;
589 }
590 while (ch != '\n' && ch != EOF);
591
592 logfile->lastpartial = logfile->partial;
593 /* there are 3 ways we could have exited the loop: reading '\n',
594 * reaching EOF, or filling the buffer; the 2nd and 3rd of these
595 * both result in a partial line */
596 logfile->partial = ch != '\n';
597
598 if (logfile->partial && opt_whole)
447 return 0; 599 return 0;
448 else
449 logfile->partial = 1;
450 600
451#if HAS_REGEX 601#if HAS_REGEX
452 transform_line (string); 602 transform_line (logfile->buf);
453#endif 603#endif
454 logfile->lastpartial = partial;
455 return len; 604 return 1;
456} 605}
457 606
458/* input: reads file->fname 607/* input: reads file->fname
459 * output: fills file->fp, file->inode 608 * output: fills file->fp, file->inode
460 * returns file->fp 609 * returns file->fp
524 { /* file missing? */ 673 { /* file missing? */
525 sleep (1); 674 sleep (1);
526 if (e->fp) 675 if (e->fp)
527 fclose (e->fp); 676 fclose (e->fp);
528 if (openlog (e) == NULL) 677 if (openlog (e) == NULL)
529 break; 678 continue;
679 if (fstat (fileno (e->fp), &stats) < 0)
680 continue;
530 } 681 }
531 682
532 if (stats.st_ino != e->inode) 683 if (stats.st_ino != e->inode)
533 { /* file renamed? */ 684 { /* file renamed? */
534 if (e->fp) 685 if (e->fp)
535 fclose (e->fp); 686 fclose (e->fp);
536 if (openlog (e) == NULL) 687 if (openlog (e) == NULL)
537 break; 688 continue;
689 if (fstat (fileno (e->fp), &stats) < 0)
690 continue;
538 } 691 }
539 692
540 if (stats.st_size < e->last_size) 693 if (stats.st_size < e->last_size)
541 { /* file truncated? */ 694 { /* file truncated? */
542 fseek (e->fp, 0, SEEK_SET); 695 fseek (e->fp, 0, SEEK_SET);
543 e->last_size = stats.st_size; 696 e->last_size = stats.st_size;
544 } 697 }
545 } 698 }
546} 699}
547 700
548#define SCROLL_UP(lines, listlen) \ 701/*
549{ \ 702 * insert a single physical line (that must be short enough to fit)
550 int cur_line; \ 703 * at position "idx" by pushing up lines above it. the caller
551 struct logfile_entry *current; \ 704 * MUST then fill in lines[idx] with valid data.
705 */
706static void
707insert_line (int idx)
708{
709 int cur_line;
710 struct logfile_entry *current;
711
712 free (lines[0].line);
713
552 for (cur_line = 0; cur_line < (listlen - 1); cur_line++) { \ 714 for (cur_line = 0; cur_line < idx; cur_line++)
553 strcpy(lines[cur_line].line, lines[cur_line + 1].line); \
554 lines[cur_line].color = lines[cur_line + 1].color; \ 715 lines[cur_line] = lines[cur_line + 1];
555 } \ 716
556 for (current = loglist; current; current = current->next) \ 717 for (current = loglist; current; current = current->next)
557 if (current->partial && current->index) \ 718 if (current->index <= idx)
558 current->index--; \ 719 current->index--;
559} 720}
560 721
561void 722/*
723 * remove a single physical line at position "idx" by moving the lines above it
724 * down and inserting a "~" line at the top.
725 */
726static void
727delete_line (int idx)
728{
729 int cur_line;
730 struct logfile_entry *current;
731
732 for (cur_line = idx; cur_line > 0; cur_line--)
733 lines[cur_line] = lines[cur_line - 1];
734
735 lines[0].line = xstrdup ("~");
736
737 for (current = loglist; current; current = current->next)
738 if (current->index >= 0 && current->index <= idx)
739 current->index++;
740}
741
742/*
743 * takes a logical log file line and splits it into multiple physical
744 * screen lines by splitting it whenever a part becomes too long.
745 * lal lines will be inserted at position "idx".
746 */
747static void
748split_line (int idx, const char *str, unsigned long color)
749{
750 int l = strlen (str);
751 int last_wrapped = 0;
752 const char *p = str;
753 static int continuation_width = -1;
754 static int continuation_length;
755
756 /* only calculate the continuation's width once */
757 if (continuation_width == -1)
758 {
759 continuation_length = strlen (continuation);
760 continuation_width = XmbTextEscapement (fontset, continuation, continuation_length);
761 }
762
763 do
764 {
765 const char *beg = p;
766 int w = last_wrapped ? continuation_width : 0;
767 int wrapped = 0;
768 const char *break_p = NULL;
769
770 while (*p)
771 {
772 int cw, len;
773
774 /* find the length in bytes of the next multibyte character */
775 len = mblen (p, l);
776 if (len <= 0)
777 len = 1; /* ignore (don't skip) illegal character sequences */
778
779 /* find the width in pixels of the next character */
780 cw = XmbTextEscapement (fontset, p, len);
781 if (cw + w > width - effect_x_space)
782 {
783 if (p == beg)
784 {
785 fprintf (stderr, "we can't even fit a single character onto the line\n");
786 if (len == 1) fprintf (stderr, "(the character we couldn't fit was '%c')\n", *p);
787 exit (1);
788 }
789
790 wrapped = 1;
791 break;
792 }
793
794 if (opt_wordwrap && len == 1 && p[0] == ' ')
795 break_p = p;
796
797 w += cw;
798 p += len;
799 l -= len;
800 }
801
802 /* if we're wrapping at spaces, and the line is long enough to
803 * wrap, and we've seen a space already, and the space wasn't
804 * the first character on the line, then wrap at the space */
805 if (opt_wordwrap && wrapped && break_p && break_p != beg)
806 {
807 l += p - break_p;
808 p = break_p;
809 }
810
811 {
812 int len = p - beg + (last_wrapped ? continuation_length : 0);
813 char *s = xmalloc (len + 1);
814 if (last_wrapped)
815 {
816 memcpy (s, continuation, continuation_length);
817 memcpy (s + continuation_length, beg, p - beg);
818 }
819 else
820 memcpy (s, beg, len);
821
822 s[len] = 0;
823 insert_line (idx);
824 lines[idx].line = s;
825 lines[idx].len = len;
826 lines[idx].color = color;
827 }
828
829 /* if we wrapped at a space, don't display the space */
830 if (opt_wordwrap && wrapped && break_p && break_p != beg)
831 {
832 l--;
833 p++;
834 }
835
836 last_wrapped = wrapped;
837 }
838 while (l);
839}
840
841/*
842 * append something to an existing physical line. this is done
843 * by deleting the file on-screen, concatenating the new data to it
844 * and splitting it again.
845 */
846static void
847append_line (int idx, const char *str)
848{
849 unsigned long color = lines[idx].color;
850 char *old = lines[idx].line;
851 char *new = concat_line (old, str);
852
853 free (old);
854
855 delete_line (idx);
856 split_line (idx, new, color);
857}
858
859static void
562main_loop (void) 860main_loop (void)
563{ 861{
564 struct linematrix *lines = xmalloc (sizeof (struct linematrix) * listlen); 862 int lin;
565 int lin, miny, maxy;
566 time_t lastreload; 863 time_t lastreload;
567 Region region = XCreateRegion (); 864 Region region = XCreateRegion ();
568 XEvent xev; 865 XEvent xev;
569 struct logfile_entry *lastprinted = NULL; 866 struct logfile_entry *lastprinted = NULL;
570 struct logfile_entry *current; 867 struct logfile_entry *current;
868 int need_update = 1;
571 869
572 maxy = 0; 870 lines = xmalloc (sizeof (struct linematrix) * listlen);
573 miny = win_y + height; 871 display = xmalloc (sizeof (struct displaymatrix) * listlen);
872
574 lastreload = time (NULL); 873 lastreload = time (NULL);
575 874
576 /* Initialize linematrix */ 875 /* Initialize linematrix */
577 for (lin = 0; lin < listlen; lin++) 876 for (lin = 0; lin < listlen; lin++)
578 { 877 {
579 lines[lin].line = xmalloc (width + 2); 878 lines[lin].line = xstrdup ("~");
580 strcpy (lines[lin].line, "~"); 879 lines[lin].len = 1;
880 display[lin].line = xstrdup("");
881 display[lin].len = 0;
882 display[lin].buffer_size = 0;
581 lines[lin].color = GetColor (def_color); 883 lines[lin].color = GetColor (def_color);
582 } 884 }
583 885
584 /* show the display full of empty lines ("~") in case the first
585 * time around the loop doesn't produce any output, as it won't if
586 * either (a) -noinitial is set or (b) all the files are currently
587 * empty */
588 redraw ();
589
590 for (;;) 886 for (;;)
591 { 887 {
592 int need_update = 0;
593
594 /* read logs */ 888 /* read logs */
595 for (current = loglist; current; current = current->next) 889 for (current = loglist; current; current = current->next)
596 { 890 {
597 if (!current->fp) 891 if (!current->fp)
598 continue; /* skip missing files */ 892 continue; /* skip missing files */
599 893
600 clearerr (current->fp); 894 clearerr (current->fp);
601 895
602 while (lineinput (current) != 0) 896 while (lineinput (current))
603 { 897 {
898 need_update = 1;
604 /* if we're trying to update old partial lines in 899 /* if we're trying to update old partial lines in
605 * place, and the last time this file was updated the 900 * place, and the last time this file was updated the
606 * output was partial, and that partial line is not 901 * output was partial, and that partial line is not
607 * too close to the top of the screen, then update 902 * too close to the top of the screen, then update
608 * that partial line */ 903 * that partial line */
609 if (opt_update && current->lastpartial && current->index >= 3) 904 if (opt_update && current->lastpartial && current->index >= 0)
610 { 905 {
611 int old_len = strlen (lines[current->index].line); 906 int idx = current->index;
612 int new_len = strlen (current->buf); 907 append_line (idx, current->buf);
613 int space_on_old_line = width / font_width - old_len; //D 908 current->index = idx;
614 909 free (current->buf), current->buf = 0;
615 strncat (lines[current->index].line, current->buf,
616 width - old_len);
617 /* if we can't fit the whole update into the old
618 * partial line then we're going to have to print
619 * the rest of it at the bottom on the screen */
620 if (new_len > space_on_old_line)
621 {
622 /* strcpy() doesn't like the strings to
623 * overlap in memory, but memmove() doesn't
624 * care */
625 memmove (current->buf,
626 current->buf + space_on_old_line,
627 new_len - space_on_old_line + 1);
628 }
629 else
630 {
631 need_update = 1;
632 strcpy (current->buf, "");
633 continue; 910 continue;
634 }
635 } 911 }
636 912
637 /* print filename if any, and if last line was from 913 /* print filename if any, and if last line was from
638 * different file */ 914 * different file */
639 if (!opt_nofilename &&
640 lastprinted != current && current->desc[0]) 915 if (!opt_nofilename && lastprinted != current && current->desc[0])
641 { 916 {
642 SCROLL_UP (lines, listlen);
643 sprintf (lines[listlen - 1].line, "[%s]", current->desc); 917 split_line (listlen - 1, "[", current->color);
644 lines[listlen - 1].color = current->color; 918 append_line (listlen - 1, current->desc);
919 append_line (listlen - 1, "]");
645 } 920 }
646 921
922 /* if we're dealing with partial lines, and the last
923 * time we showed the line it wasn't finished ... */
924 if (!opt_whole && current->lastpartial)
925 {
647 /* if this is the same file we showed last, and the 926 /* if this is the same file we showed last then
648 * last time we showed it, it wasn't finished, then
649 * append to the last line shown */ 927 append to the last line shown */
650 if (lastprinted == current && current->lastpartial) 928 if (lastprinted == current)
651 { 929 append_line (listlen - 1, current->buf);
652 int old_len = strlen (lines[listlen - 1].line); 930 else
653 int new_len = strlen (current->buf); 931 {
654 strncat (lines[listlen - 1].line, current->buf, 932 /* but if a different file has been shown in the
655 width - old_len); 933 * mean time, make a new line, starting with the
656 /* if it doesn't all fit, then put the part that 934 * continuation string */
657 * doesn't fit on a new line */ 935 split_line (listlen - 1, continuation, current->color);
658 if (new_len > width - old_len) 936 append_line (listlen - 1, current->buf);
659 { 937 }
660 SCROLL_UP (lines, listlen); 938 }
661 strcpy (lines[listlen - 1].line,
662 current->buf + width - old_len);
663 }
664 /* show the 'continuation' string because we've got a
665 * continued partial line, but we weren't able to
666 * append it to the old displayed partial line */
667 }
668 else if (current->lastpartial)
669 {
670 int old_len = strlen (continuation);
671 int new_len = strlen (current->buf);
672 SCROLL_UP (lines, listlen);
673 strcpy (lines[listlen - 1].line, continuation);
674 strncat (lines[listlen - 1].line, current->buf,
675 width - old_len);
676 /* it might not fit, now that we've displayed the
677 * continuation string, so we may need to 'wrap' it */
678 if (new_len > width - old_len)
679 {
680 SCROLL_UP (lines, listlen);
681 strcpy (lines[listlen - 1].line,
682 current->buf + width - old_len);
683 }
684 }
685 else 939 else
686 { 940 /* otherwise just make a plain and simple new line */
687 SCROLL_UP (lines, listlen); 941 split_line (listlen - 1, current->buf, current->color);
688 strcpy (lines[listlen - 1].line, current->buf);
689 }
690 942
691 /* we've shown the line now. clear the buffer for the next line */ 943 free (current->buf), current->buf = 0;
692 strcpy (current->buf, "");
693 current->index = listlen - 1; 944 current->index = listlen - 1;
694 lines[listlen - 1].color = current->color;
695
696 lastprinted = current; 945 lastprinted = current;
697 need_update = 1;
698 } 946 }
699 } 947 }
700 948
701 if (need_update) 949 if (need_update)
950 {
702 redraw (); 951 redraw (0);
952 need_update = 0;
953 }
703 else 954 else
704 { 955 {
705 XFlush (disp); 956 XFlush (disp);
706 957
707 if (!XPending (disp)) 958 if (!XPending (disp))
733 984
734 r.x = xev.xexpose.x; 985 r.x = xev.xexpose.x;
735 r.y = xev.xexpose.y; 986 r.y = xev.xexpose.y;
736 r.width = xev.xexpose.width; 987 r.width = xev.xexpose.width;
737 r.height = xev.xexpose.height; 988 r.height = xev.xexpose.height;
989
738 XUnionRectWithRegion (&r, region, region); 990 XUnionRectWithRegion (&r, region, region);
739 if (miny > r.y)
740 miny = r.y;
741 if (maxy < r.y + r.height)
742 maxy = r.y + r.height;
743 } 991 }
744 break; 992 break;
745 default: 993 default:
746#ifdef DEBUGMODE 994#ifdef DEBUGMODE
747 fprintf (stderr, "PANIC! Unknown event %d\n", xev.type); 995 fprintf (stderr, "PANIC! Unknown event %d\n", xev.type);
760 lastreload = time (NULL); 1008 lastreload = time (NULL);
761 } 1009 }
762 1010
763 if (!XEmptyRegion (region)) 1011 if (!XEmptyRegion (region))
764 { 1012 {
1013 XRectangle r;
1014
765 XSetRegion (disp, WinGC, region); 1015 XSetRegion (disp, WinGC, region);
1016 XClipBox (region, &r);
766 1017
767 refresh (lines, miny, maxy); 1018 refresh (r.y, r.y + r.height, 0, 1);
1019
768 XDestroyRegion (region); 1020 XDestroyRegion (region);
769 region = XCreateRegion (); 1021 region = XCreateRegion ();
770 maxy = 0;
771 miny = win_y + height;
772 } 1022 }
773 } 1023 }
774} 1024}
775 1025
776 1026
814 continuation = argv[++i]; 1064 continuation = argv[++i];
815 else if (!strcmp (arg, "-font") || !strcmp (arg, "-fn")) 1065 else if (!strcmp (arg, "-font") || !strcmp (arg, "-fn"))
816 fontname = argv[++i]; 1066 fontname = argv[++i];
817#if HAS_REGEX 1067#if HAS_REGEX
818 else if (!strcmp (arg, "-t")) 1068 else if (!strcmp (arg, "-t"))
1069 {
819 transform = argv[++i]; 1070 transform = argv[++i];
1071 transform_to = argv[++i];
1072 printf("transform: '%s' to '%s'\n", transform, transform_to);
1073 }
820#endif 1074#endif
821 else if (!strcmp (arg, "-fork") || !strcmp (arg, "-f")) 1075 else if (!strcmp (arg, "-fork") || !strcmp (arg, "-f"))
822 opt_daemonize = 1; 1076 opt_daemonize = 1;
823 else if (!strcmp (arg, "-reload")) 1077 else if (!strcmp (arg, "-reload"))
824 { 1078 {
825 reload = atoi (argv[++i]); 1079 reload = atoi (argv[++i]);
826 command = argv[++i]; 1080 command = argv[++i];
827 } 1081 }
828 else if (!strcmp (arg, "-shade")) 1082 else if (!strcmp (arg, "-shade"))
829 opt_shade = 1; 1083 opt_shade = 1;
1084 else if (!strcmp (arg, "-outline"))
1085 opt_outline = 1;
1086 else if (!strcmp (arg, "-noflicker"))
1087 opt_noflicker = 1;
830 else if (!strcmp (arg, "-frame")) 1088 else if (!strcmp (arg, "-frame"))
831 opt_frame = 1; 1089 opt_frame = 1;
832 else if (!strcmp (arg, "-no-filename")) 1090 else if (!strcmp (arg, "-no-filename"))
833 opt_nofilename = 1; 1091 opt_nofilename = 1;
834 else if (!strcmp (arg, "-reverse")) 1092 else if (!strcmp (arg, "-reverse"))
837 opt_whole = 1; 1095 opt_whole = 1;
838 else if (!strcmp (arg, "-partial")) 1096 else if (!strcmp (arg, "-partial"))
839 opt_partial = 1; 1097 opt_partial = 1;
840 else if (!strcmp (arg, "-update")) 1098 else if (!strcmp (arg, "-update"))
841 opt_update = opt_partial = 1; 1099 opt_update = opt_partial = 1;
1100 else if (!strcmp (arg, "-wordwrap"))
1101 opt_wordwrap = 1;
842 else if (!strcmp (arg, "-color")) 1102 else if (!strcmp (arg, "-color"))
843 def_color = argv[++i]; 1103 def_color = argv[++i];
844 else if (!strcmp (arg, "-noinitial")) 1104 else if (!strcmp (arg, "-noinitial"))
845 opt_noinitial = 1; 1105 opt_noinitial = 1;
846 else if (!strcmp (arg, "-id")) 1106 else if (!strcmp (arg, "-id"))
880 desc = p + 1; 1140 desc = p + 1;
881 } 1141 }
882 } 1142 }
883 1143
884 e = xmalloc (sizeof (struct logfile_entry)); 1144 e = xmalloc (sizeof (struct logfile_entry));
1145 e->partial = 0;
1146 e->buf = 0;
1147 e->index = -1;
1148
885 if (arg[0] == '-' && arg[1] == '\0') 1149 if (arg[0] == '-' && arg[1] == '\0')
886 { 1150 {
887 if ((e->fp = fdopen (0, "r")) == NULL) 1151 if ((e->fp = fdopen (0, "r")) == NULL)
888 perror ("fdopen"), exit (1); 1152 perror ("fdopen"), exit (1);
889 if (fcntl (0, F_SETFL, O_NONBLOCK) < 0) 1153 if (fcntl (0, F_SETFL, O_NONBLOCK) < 0)
890 perror ("fcntl"), exit (1); 1154 perror ("fcntl"), exit (1);
1155
891 e->fname = NULL; 1156 e->fname = NULL;
892 e->inode = 0; 1157 e->inode = 0;
893 e->desc = xstrdup ("stdin"); 1158 e->desc = xstrdup ("stdin");
894 } 1159 }
895 else 1160 else
896 { 1161 {
897 int l;
898
899 e->fname = xstrdup (fname); 1162 e->fname = xstrdup (fname);
1163
900 if (openlog (e) == NULL) 1164 if (openlog (e) == NULL)
901 perror (fname), exit (1); 1165 perror (fname), exit (1);
902 1166
903 l = strlen (desc); 1167 e->desc = xstrdup (desc);
904 if (l > width - 2) /* must account for [ ] */
905 l = width - 2;
906 e->desc = xmalloc (l + 1);
907 memcpy (e->desc, desc, l);
908 *(e->desc + l) = '\0';
909 } 1168 }
910 1169
911 e->color = GetColor (fcolor); 1170 e->color = GetColor (fcolor);
912 e->buf = xmalloc (width + 2);
913 e->partial = 0; 1171 e->partial = 0;
914 e->buf[0] = '\0';
915 e->next = NULL; 1172 e->next = NULL;
916 1173
917 if (!loglist) 1174 if (!loglist)
918 loglist = e; 1175 loglist = e;
919 if (loglist_tail) 1176 if (loglist_tail)
920 loglist_tail->next = e; 1177 loglist_tail->next = e;
1178
921 loglist_tail = e; 1179 loglist_tail = e;
922 } 1180 }
923 } 1181 }
924 1182
925 if (!loglist) 1183 if (!loglist)
927 fprintf (stderr, "You did not specify any files to tail\n" 1185 fprintf (stderr, "You did not specify any files to tail\n"
928 "use %s --help for help\n", argv[0]); 1186 "use %s --help for help\n", argv[0]);
929 exit (1); 1187 exit (1);
930 } 1188 }
931 1189
1190 if (opt_update && opt_whole)
1191 {
1192 fprintf (stderr, "Specify at most one of -update and -whole\n");
1193 exit (1);
1194 }
932 if (opt_partial && opt_whole) 1195 else if (opt_partial && opt_whole)
933 { 1196 {
934 fprintf (stderr, "Specify at most one of -partial and -whole\n"); 1197 fprintf (stderr, "Specify at most one of -partial and -whole\n");
1198 exit (1);
1199 }
1200
1201 /* HACK-7: do we want to allow both -shade and -outline? */
1202 if (opt_shade && opt_outline)
1203 {
1204 fprintf (stderr, "Specify at most one of -shade and -outline\n");
935 exit (1); 1205 exit (1);
936 } 1206 }
937 1207
938 if (opt_partial) 1208 if (opt_partial)
939 /* if we specifically requested to see partial lines then don't insist on whole lines */ 1209 /* if we specifically requested to see partial lines then don't insist on whole lines */
940 opt_whole = 0; 1210 opt_whole = 0;
941 else if (file_count > 1) 1211 else if (file_count > 1)
942 /* otherwise, if we've viewing multiple files, default to showing whole lines */ 1212 /* otherwise, if we're viewing multiple files, default to showing whole lines */
943 opt_whole = 1; 1213 opt_whole = 1;
944 1214
945#if HAS_REGEX 1215#if HAS_REGEX
946 if (transform) 1216 if (transform)
947 { 1217 {
948 int i; 1218 int i;
949 1219
1220 printf("compiling regexp '%s'\n", transform);
950 transformre = xmalloc (sizeof (transformre)); 1221 transformre = xmalloc (sizeof (regex_t));
951 i = regcomp (&transformre, transform, REG_EXTENDED); 1222 i = regcomp (transformre, transform, REG_EXTENDED);
952 if (i != 0) 1223 if (i != 0)
953 { 1224 {
954 char buf[512]; 1225 char buf[512];
955 1226
956 regerror (i, &transformre, buf, sizeof (buf)); 1227 regerror (i, transformre, buf, sizeof (buf));
957 fprintf (stderr, "Cannot compile regular expression: %s\n", buf); 1228 fprintf (stderr, "Cannot compile regular expression: %s\n", buf);
958 } 1229 }
1230 else
1231 {
1232 printf("compiled '%s' OK to %x\n", transform, (int)transformre);
1233 }
959 } 1234 }
960#endif 1235#endif
961 1236
962 InitWindow (); 1237 InitWindow ();
963 1238
994{ 1269{
995 void *p; 1270 void *p;
996 1271
997 while ((p = strdup (string)) == NULL) 1272 while ((p = strdup (string)) == NULL)
998 { 1273 {
999 fprintf (stderr, "Memory exausted."); 1274 fprintf (stderr, "Memory exhausted in xstrdup().\n");
1000 sleep (10); 1275 sleep (10);
1001 } 1276 }
1002 1277
1003 return p; 1278 return p;
1004} 1279}
1008{ 1283{
1009 void *p; 1284 void *p;
1010 1285
1011 while ((p = malloc (size)) == NULL) 1286 while ((p = malloc (size)) == NULL)
1012 { 1287 {
1013 fprintf (stderr, "Memory exausted."); 1288 fprintf (stderr, "Memory exhausted in xmalloc().\n");
1014 sleep (10); 1289 sleep (10);
1015 } 1290 }
1291
1292 return p;
1293}
1294
1295void *
1296xrealloc (void *ptr, size_t size)
1297{
1298 void *p;
1299
1300 while ((p = realloc (ptr, size)) == NULL)
1301 {
1302 fprintf (stderr, "Memory exhausted in xrealloc().\n");
1303 sleep (10);
1304 }
1305
1016 return p; 1306 return p;
1017} 1307}
1018 1308
1019void 1309void
1020display_help (char *myname) 1310display_help (char *myname)
1030 " -reverse print new lines at the top\n" 1320 " -reverse print new lines at the top\n"
1031 " -whole wait for \\n before showing a line\n" 1321 " -whole wait for \\n before showing a line\n"
1032 " -partial show lines even if they don't end with a \\n\n" 1322 " -partial show lines even if they don't end with a \\n\n"
1033 " -update allow updates to old partial lines\n" 1323 " -update allow updates to old partial lines\n"
1034 " -cont string to prefix continued partial lines with\n" 1324 " -cont string to prefix continued partial lines with\n"
1325 " -wordwrap wrap long lines at spaces to avoid breaking words\n"
1035 " defaults to \"[+]\"\n" 1326 " defaults to \"[+]\"\n"
1036 " -shade add shading to font\n" 1327 " -shade add shading to font\n"
1037 " -noinitial don't display the last file lines on\n" 1328 " -noinitial don't display the last file lines on\n"
1038 " startup\n" 1329 " startup\n"
1039 " -i | -interval seconds interval between checks (fractional\n" 1330 " -i | -interval seconds interval between checks (fractional\n"
1040 " values o.k.). Default 3 seconds\n" 1331 " values o.k.). Default 2.4 seconds\n"
1041 " -V display version information and exit\n" 1332 " -V display version information and exit\n"
1042 "\n"); 1333 "\n");
1043 printf ("Example:\n%s -g 80x25+100+50 -font fixed /var/log/messages,green " 1334 printf ("Example:\n%s -g 800x250+100+50 -font fixed /var/log/messages,green "
1044 "/var/log/secure,red,'ALERT'\n", myname); 1335 "/var/log/secure,red,'ALERT'\n", myname);
1045 exit (0); 1336 exit (0);
1046} 1337}
1047 1338
1048void 1339void
1053} 1344}
1054 1345
1055int 1346int
1056daemonize (void) 1347daemonize (void)
1057{ 1348{
1349 pid_t pid;
1350
1058 switch (fork ()) 1351 switch (pid = fork ())
1059 { 1352 {
1060 case -1: 1353 case -1:
1061 return -1; 1354 return -1;
1062 case 0: 1355 case 0:
1063 break; 1356 break;
1064 default: 1357 default:
1358 /*printf("%d\n", pid);*/
1065 _exit (0); 1359 exit (0);
1066 } 1360 }
1067 1361
1068 if (setsid () == -1) 1362 if (setsid () == -1)
1069 return -1; 1363 return -1;
1070 1364

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines