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.12 by pcg, Sat Mar 27 00:52:52 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;
63 int len; 71 int len;
64 unsigned long color; 72 unsigned long color;
65}; 73};
66 74
75struct displaymatrix
76{
77 char *line;
78 int len;
79 int buffer_size;
80 unsigned long color;
81};
82
67/* global variables */ 83/* global variables */
68struct linematrix *lines; 84struct linematrix *lines;
85struct displaymatrix *display;
69int width = STD_WIDTH, height = STD_HEIGHT, listlen; 86int width = STD_WIDTH, height = STD_HEIGHT, listlen;
70int win_x = LOC_X, win_y = LOC_Y; 87int win_x = LOC_X, win_y = LOC_Y;
71int 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 */
72int do_reopen; 91int do_reopen;
73struct timeval interval = { 3, 0 }; 92struct timeval interval = { 2, 400000 };
74XFontSet fontset; 93XFontSet fontset;
75 94
76/* command line options */ 95/* command line options */
77int opt_noinitial, opt_shade, opt_frame, opt_reverse, opt_nofilename, 96int opt_noinitial, opt_shade, opt_frame, opt_reverse, opt_nofilename,
78 opt_whole, opt_update, geom_mask, reload = 0; 97 opt_outline, opt_noflicker, opt_whole, opt_update, opt_wordwrap,
98 geom_mask, reload = 0;
79const char *command = NULL, 99const char *command = NULL,
80 *fontname = USE_FONT, *dispname = NULL, *def_color = DEF_COLOR, 100 *fontname = USE_FONT, *dispname = NULL, *def_color = DEF_COLOR,
81 *continuation = "[+]"; 101 *continuation = "[+]";
82 102
83struct logfile_entry *loglist = NULL, *loglist_tail = NULL; 103struct logfile_entry *loglist = NULL, *loglist_tail = NULL;
92 regex_t from; 112 regex_t from;
93 const char *to; 113 const char *to;
94 struct re_list *next; 114 struct re_list *next;
95}; 115};
96struct re_list *re_head, *re_tail; 116struct re_list *re_head, *re_tail;
117char *transform_to = NULL;
118regex_t *transformre;
97#endif 119#endif
98 120
99 121
100/* prototypes */ 122/* prototypes */
101void list_files (int); 123void list_files (int);
103void force_refresh (int); 125void force_refresh (int);
104void blank_window (int); 126void blank_window (int);
105 127
106void InitWindow (void); 128void InitWindow (void);
107unsigned long GetColor (const char *); 129unsigned long GetColor (const char *);
108void redraw (void); 130void redraw (int);
109void refresh (int, int); 131void refresh (int, int, int, int);
110 132
111void transform_line (char *s); 133void transform_line (char *s);
112int lineinput (struct logfile_entry *); 134int lineinput (struct logfile_entry *);
113void reopen (void); 135void reopen (void);
114void check_open_files (void); 136void check_open_files (void);
118void display_version (void); 140void display_version (void);
119void display_help (char *); 141void display_help (char *);
120void install_signal (int, void (*)(int)); 142void install_signal (int, void (*)(int));
121void *xstrdup (const char *); 143void *xstrdup (const char *);
122void *xmalloc (size_t); 144void *xmalloc (size_t);
145void *xrealloc (void *, size_t);
123int daemonize (void); 146int daemonize (void);
124 147
125/* signal handlers */ 148/* signal handlers */
126void 149void
127list_files (int dummy) 150list_files (int dummy)
140} 163}
141 164
142void 165void
143force_refresh (int dummy) 166force_refresh (int dummy)
144{ 167{
145 redraw (); 168 redraw (1);
146} 169}
147 170
148void 171void
149blank_window (int dummy) 172blank_window (int dummy)
150{ 173{
151 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);
152 XFlush (disp); 175 XFlush (disp);
153 exit (0); 176 exit (0);
154} 177}
155 178
156/* X related functions */ 179/* X related functions */
285 308
286 { 309 {
287 XFontSetExtents *xfe = XExtentsOfFontSet (fontset); 310 XFontSetExtents *xfe = XExtentsOfFontSet (fontset);
288 311
289 font_height = xfe->max_logical_extent.height; 312 font_height = xfe->max_logical_extent.height;
290 font_descent = xfe->max_logical_extent.y; 313 font_ascent = -xfe->max_logical_extent.y;
291 } 314 }
292 315
293 if (geom_mask & XNegative) 316 if (geom_mask & XNegative)
294 win_x = win_x + ScreenWidth - width; 317 win_x = win_x + ScreenWidth - width;
295 if (geom_mask & YNegative) 318 if (geom_mask & YNegative)
296 win_y = win_y + ScreenHeight - height; 319 win_y = win_y + ScreenHeight - height;
297 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 */
298 listlen = height / font_height; 348 listlen = (height - effect_y_space) / font_height;
299 349
300 if (!listlen) 350 if (!listlen)
301 { 351 {
302 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",
303 font_height); 353 font_height);
304 listlen = 1; 354 listlen = 1;
305 } 355 }
306 356
307 height = listlen * font_height; 357 /* leave the height how the user requested it. it might not all be
308 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
309 XSelectInput (disp, root, ExposureMask | FocusChangeMask); 363 XSelectInput (disp, root, ExposureMask | FocusChangeMask);
310} 364}
311 365
312/* 366/*
313 * 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
314 * 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 *
315 * the rest is handled by regular refresh()'es 372 * the rest is handled by regular refresh()'es
316 */ 373 */
317void 374void
318redraw (void) 375redraw (int redraw_all)
319{ 376{
320 XClearArea (disp, root, win_x, win_y, width, height, False); 377 XSetClipMask (disp, WinGC, None);
321 refresh (0, 32768); 378 refresh (0, 32768, 1, redraw_all);
322} 379}
323 380
324/* Just redraw everything without clearing (i.e. after an EXPOSE event) */ 381/* Just redraw everything without clearing (i.e. after an EXPOSE event) */
325void 382void
326refresh (int miny, int maxy) 383refresh (int miny, int maxy, int clear, int refresh_all)
327{ 384{
328 int lin; 385 int lin;
329 int offset = (listlen + 1) * font_height; 386 int offset = listlen * font_height + font_ascent + effect_y_offset;
330 unsigned long black_color = GetColor ("black"); 387 unsigned long black_color = GetColor ("black");
331 388
332 miny -= win_y + font_height; 389 miny -= win_y + font_height;
333 maxy -= win_y - font_height; 390 maxy -= win_y - font_height;
334 391
392 if (clear && !opt_noflicker)
393 XClearArea (disp, root, win_x, win_y, width, height, False);
335 394
336 for (lin = listlen; lin--;) 395 for (lin = listlen; lin--;)
337 { 396 {
338 struct linematrix *line = 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;
339 399
340 offset -= font_height; 400 offset -= font_height;
341 401
342 if (offset < miny || offset > maxy) 402 if (offset < miny || offset > maxy)
343 continue; 403 continue;
344 404
345 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))
346 { 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;
347 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);
348 XmbDrawString (disp, root, fontset, WinGC, win_x + 2, 450 XmbDrawString (disp, root, fontset, WinGC,
451 win_x + effect_x_offset + SHADE_X,
452 win_y + offset + SHADE_Y,
349 win_y + offset + 2, line->line, line->len); 453 line->line, line->len);
350 } 454 }
351 455
352 XSetForeground (disp, WinGC, line->color); 456 XSetForeground (disp, WinGC, line->color);
353 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,
354 line->line, line->len); 460 line->line, line->len);
461 }
355 } 462 }
356 463
357 if (opt_frame) 464 if (opt_frame)
465 {
466 XSetForeground (disp, WinGC, GetColor (def_color));
358 XDrawRectangle (disp, root, WinGC, win_x - 2, win_y - 2, width + 4, height + 4); 467 XDrawRectangle (disp, root, WinGC, win_x - 0, win_y - 0, width - 1, height - 1);
468 }
359} 469}
360 470
361#if HAS_REGEX 471#if HAS_REGEX
362void 472void
363transform_line (char *s) 473transform_line (char *s)
375 if (transformre) 485 if (transformre)
376 { 486 {
377 int i; 487 int i;
378 regmatch_t matched[16]; 488 regmatch_t matched[16];
379 489
380 i = regexec (&transformre, string, 16, matched, 0); 490 i = regexec (transformre, s, 16, matched, 0);
381 if (i == 0) 491 if (i == 0)
382 { /* 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';
383 } 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 }
384 } 521 }
385} 522}
386#endif 523#endif
387 524
388char * 525char *
398 535
399 return r; 536 return r;
400} 537}
401 538
402/* 539/*
403 * This routine should read a single line, no matter how long. 540 * This routine can read a line of any length if it is called enough times.
404 */ 541 */
405int 542int
406lineinput (struct logfile_entry *logfile) 543lineinput (struct logfile_entry *logfile)
407{ 544{
408 char buff[1024], *p = buff; 545 char buff[1024], *p;
409 int ch; 546 int ch;
547 /* HACK-2: add on the length of any partial line which we will be appending to */
410 int ofs = logfile->buf ? strlen (logfile->buf) : 0; 548 int ofs = logfile->buf ? strlen (logfile->buf) : 0;
411 549
550 /* this loop ensures that the whole line is read, even if it's
551 * longer than the buffer. we need to do this because when --whole
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 */
412 do 554 do
413 { 555 {
556 p = buff;
557 do
558 {
414 ch = fgetc (logfile->fp); 559 ch = fgetc (logfile->fp);
415 560
416 if (ch == '\r') 561 if (ch == '\n' || ch == EOF)
417 continue;
418 else if (ch == EOF || ch == '\n')
419 break; 562 break;
563 else if (ch == '\r')
564 continue; /* skip */
420 else if (ch == '\t') 565 else if (ch == '\t')
421 { 566 {
422 do 567 do
423 { 568 {
424 *p++ = ' '; 569 *p++ = ' ';
425 ofs++; 570 ofs++;
426 } 571 }
427 while (ofs & 7); 572 while (ofs & 7);
428 }
429 else
430 {
431 *p++ = ch;
432 ofs++;
433 }
434 } 573 }
574 else
575 {
576 *p++ = ch;
577 ofs++;
578 }
579 }
435 while (p < buff + (sizeof buff) - 8 - 1); 580 while (p < buff + (sizeof buff) - 8 - 1);
436 581
437 if (p == buff) 582 if (p == buff && ch == EOF)
438 return 0; 583 return 0;
439 584
440 *p = 0; 585 *p = 0;
441 586
442 p = concat_line (logfile->buf, buff); 587 p = concat_line (logfile->buf, buff);
443 free (logfile->buf); logfile->buf = p; 588 free (logfile->buf); logfile->buf = p;
589 }
590 while (ch != '\n' && ch != EOF);
444 591
445 logfile->lastpartial = logfile->partial; 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 */
446 logfile->partial = ch == EOF; 596 logfile->partial = ch != '\n';
447 597
448 if (logfile->partial && opt_whole) 598 if (logfile->partial && opt_whole)
449 return 0; 599 return 0;
450 600
451#if HAS_REGEX 601#if HAS_REGEX
452 transform_line (logfile->buf); 602 transform_line (logfile->buf);
453#endif 603#endif
454 printf ("got buf <%s>\n", logfile->buf);
455 return 1; 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
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
701/*
702 * insert a single physical line (that must be short enough to fit)
703 * at position "idx" by pushing up lines above it. the caller
704 * MUST then fill in lines[idx] with valid data.
705 */
548static void 706static void
549insert_line (int idx) 707insert_line (int idx)
550{ 708{
551 int cur_line; 709 int cur_line;
552 struct logfile_entry *current; 710 struct logfile_entry *current;
555 713
556 for (cur_line = 0; cur_line < idx; cur_line++) 714 for (cur_line = 0; cur_line < idx; cur_line++)
557 lines[cur_line] = lines[cur_line + 1]; 715 lines[cur_line] = lines[cur_line + 1];
558 716
559 for (current = loglist; current; current = current->next) 717 for (current = loglist; current; current = current->next)
560 if (current->partial && current->index && current->index <= idx) 718 if (current->index <= idx)
561 current->index--; 719 current->index--;
562} 720}
563 721
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 */
564static void 726static void
565delete_line (int idx) 727delete_line (int idx)
566{ 728{
567 int cur_line; 729 int cur_line;
568 struct logfile_entry *current; 730 struct logfile_entry *current;
569 731
570 for (cur_line = idx; cur_line > 0; cur_line--) 732 for (cur_line = idx; cur_line > 0; cur_line--)
571 lines[cur_line] = lines[cur_line - 1]; 733 lines[cur_line] = lines[cur_line - 1];
572 734
573 lines[0].line = strdup ("~"); 735 lines[0].line = xstrdup ("~");
574 736
575 for (current = loglist; current; current = current->next) 737 for (current = loglist; current; current = current->next)
576 if (current->partial && current->index && current->index <= idx) 738 if (current->index >= 0 && current->index <= idx)
577 current->index++; 739 current->index++;
578} 740}
579 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 */
580static void 747static void
581split_line (int idx, const char *str, unsigned long color) 748split_line (int idx, const char *str, unsigned long color)
582{ 749{
583 int l = strlen (str); 750 int l = strlen (str);
751 int last_wrapped = 0;
584 const char *p = str; 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 }
585 762
586 do 763 do
587 { 764 {
588 const char *beg = p; 765 const char *beg = p;
766 int w = last_wrapped ? continuation_width : 0;
589 int w = 0; 767 int wrapped = 0;
768 const char *break_p = NULL;
590 769
591 while (*p) 770 while (*p)
592 { 771 {
772 int cw, len;
773
774 /* find the length in bytes of the next multibyte character */
593 int len = mblen (p, l); 775 len = mblen (p, l);
594 if (len <= 0) 776 if (len <= 0)
595 len = 1; /* ignore (don't skip) ilegal character sequences */ 777 len = 1; /* ignore (don't skip) illegal character sequences */
596 778
779 /* find the width in pixels of the next character */
597 int cw = XmbTextEscapement (fontset, p, len); 780 cw = XmbTextEscapement (fontset, p, len);
598 if (cw + w >= width) 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;
599 break; 791 break;
792 }
793
794 if (opt_wordwrap && len == 1 && p[0] == ' ')
795 break_p = p;
600 796
601 w += cw; 797 w += cw;
602 p += len; 798 p += len;
603 l -= len; 799 l -= len;
604 } 800 }
605 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
606 { 811 {
812 int len = p - beg + (last_wrapped ? continuation_length : 0);
607 char *s = xmalloc (p - beg + 1); 813 char *s = xmalloc (len + 1);
608 memcpy (s, beg, p - beg); 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
609 s[p - beg] = 0; 822 s[len] = 0;
610 insert_line (idx); 823 insert_line (idx);
611 lines[idx].line = s; 824 lines[idx].line = s;
612 lines[idx].len = p - beg; 825 lines[idx].len = len;
613 lines[idx].color = color; 826 lines[idx].color = color;
614 } 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;
615 } 837 }
616 while (l); 838 while (l);
617} 839}
618 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 */
619static void 846static void
620append_line (int idx, const char *str) 847append_line (int idx, const char *str)
621{ 848{
622 unsigned long color = lines[idx].color; 849 unsigned long color = lines[idx].color;
623 char *old = lines[idx].line; 850 char *old = lines[idx].line;
624 char *new = concat_line (old, str); 851 char *new = concat_line (old, str);
625 852
626 free (old); 853 free (old);
627 free (str);
628 854
629 delete_line (idx); 855 delete_line (idx);
630 split_line (idx, new, color); 856 split_line (idx, new, color);
631} 857}
632 858
633static void 859static void
634main_loop (void) 860main_loop (void)
635{ 861{
636 lines = xmalloc (sizeof (struct linematrix) * listlen); 862 int lin;
637 int lin, miny, maxy;
638 time_t lastreload; 863 time_t lastreload;
639 Region region = XCreateRegion (); 864 Region region = XCreateRegion ();
640 XEvent xev; 865 XEvent xev;
641 struct logfile_entry *lastprinted = NULL; 866 struct logfile_entry *lastprinted = NULL;
642 struct logfile_entry *current; 867 struct logfile_entry *current;
868 int need_update = 1;
643 869
644 maxy = 0; 870 lines = xmalloc (sizeof (struct linematrix) * listlen);
645 miny = win_y + height; 871 display = xmalloc (sizeof (struct displaymatrix) * listlen);
872
646 lastreload = time (NULL); 873 lastreload = time (NULL);
647 874
648 /* Initialize linematrix */ 875 /* Initialize linematrix */
649 for (lin = 0; lin < listlen; lin++) 876 for (lin = 0; lin < listlen; lin++)
650 { 877 {
651 lines[lin].line = strdup ("~"); 878 lines[lin].line = xstrdup ("~");
652 lines[lin].len = 1; 879 lines[lin].len = 1;
880 display[lin].line = xstrdup("");
881 display[lin].len = 0;
882 display[lin].buffer_size = 0;
653 lines[lin].color = GetColor (def_color); 883 lines[lin].color = GetColor (def_color);
654 } 884 }
655 885
656 /* show the display full of empty lines ("~") in case the first
657 * time around the loop doesn't produce any output, as it won't if
658 * either (a) -noinitial is set or (b) all the files are currently
659 * empty */
660 redraw ();
661
662 for (;;) 886 for (;;)
663 { 887 {
664 int need_update = 0;
665
666 /* read logs */ 888 /* read logs */
667 for (current = loglist; current; current = current->next) 889 for (current = loglist; current; current = current->next)
668 { 890 {
669 if (!current->fp) 891 if (!current->fp)
670 continue; /* skip missing files */ 892 continue; /* skip missing files */
671 893
672 clearerr (current->fp); 894 clearerr (current->fp);
673 895
674 while (lineinput (current)) 896 while (lineinput (current))
675 { 897 {
676 printf ("lineinput %s\n", current->buf);//D
677 need_update = 1; 898 need_update = 1;
678 /* if we're trying to update old partial lines in 899 /* if we're trying to update old partial lines in
679 * place, and the last time this file was updated the 900 * place, and the last time this file was updated the
680 * output was partial, and that partial line is not 901 * output was partial, and that partial line is not
681 * too close to the top of the screen, then update 902 * too close to the top of the screen, then update
682 * that partial line */ 903 * that partial line */
683 printf ("BPa\n");//D
684 if (opt_update && current->lastpartial && current->index >= 3) 904 if (opt_update && current->lastpartial && current->index >= 0)
685 { 905 {
906 int idx = current->index;
686 append_line (current->index, current->buf); 907 append_line (idx, current->buf);
687
688 if (!current->partial)
689 {
690 free (current->buf);
691 current->buf = 0; 908 current->index = idx;
692 } 909 free (current->buf), current->buf = 0;
693
694 continue; 910 continue;
695 } 911 }
696 912
697 printf ("BPb\n");//D
698 /* print filename if any, and if last line was from 913 /* print filename if any, and if last line was from
699 * different file */ 914 * different file */
700 if (!opt_nofilename &&
701 lastprinted != current && current->desc[0]) 915 if (!opt_nofilename && lastprinted != current && current->desc[0])
702 { 916 {
703 char buf[1024]; /* HACK */
704 snprintf (buf, sizeof (buf), "[%s]", current->desc);
705 printf ("BPc<%s>\n", buf);//D
706 split_line (listlen - 1, buf, current->color); 917 split_line (listlen - 1, "[", current->color);
918 append_line (listlen - 1, current->desc);
919 append_line (listlen - 1, "]");
707 } 920 }
708 921
709 printf ("BP1\n");//D 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 {
710 /* if this is the same file we showed last, and the 926 /* if this is the same file we showed last then
711 * last time we showed it, it wasn't finished, then
712 * append to the last line shown */ 927 append to the last line shown */
713 if (lastprinted == current && current->lastpartial) 928 if (lastprinted == current)
714 { 929 append_line (listlen - 1, current->buf);
930 else
931 {
932 /* but if a different file has been shown in the
933 * mean time, make a new line, starting with the
934 * continuation string */
935 split_line (listlen - 1, continuation, current->color);
715 append_line (listlen - 1, current->buf); 936 append_line (listlen - 1, current->buf);
716 free (current->buf); 937 }
717 current->buf = 0; 938 }
718 continue;
719 }
720 else 939 else
721 { 940 /* otherwise just make a plain and simple new line */
722 split_line (listlen - 1, current->buf, current->color); 941 split_line (listlen - 1, current->buf, current->color);
942
723 free (current->buf); current->buf = 0; 943 free (current->buf), current->buf = 0;
724 }
725 printf ("BP3\n");//D
726
727 current->index = listlen - 1; 944 current->index = listlen - 1;
728 lastprinted = current; 945 lastprinted = current;
729 } 946 }
730 } 947 }
731 948
732 if (need_update) 949 if (need_update)
950 {
733 redraw (); 951 redraw (0);
952 need_update = 0;
953 }
734 else 954 else
735 { 955 {
736 XFlush (disp); 956 XFlush (disp);
737 957
738 if (!XPending (disp)) 958 if (!XPending (disp))
764 984
765 r.x = xev.xexpose.x; 985 r.x = xev.xexpose.x;
766 r.y = xev.xexpose.y; 986 r.y = xev.xexpose.y;
767 r.width = xev.xexpose.width; 987 r.width = xev.xexpose.width;
768 r.height = xev.xexpose.height; 988 r.height = xev.xexpose.height;
989
769 XUnionRectWithRegion (&r, region, region); 990 XUnionRectWithRegion (&r, region, region);
770 if (miny > r.y)
771 miny = r.y;
772 if (maxy < r.y + r.height)
773 maxy = r.y + r.height;
774 } 991 }
775 break; 992 break;
776 default: 993 default:
777#ifdef DEBUGMODE 994#ifdef DEBUGMODE
778 fprintf (stderr, "PANIC! Unknown event %d\n", xev.type); 995 fprintf (stderr, "PANIC! Unknown event %d\n", xev.type);
791 lastreload = time (NULL); 1008 lastreload = time (NULL);
792 } 1009 }
793 1010
794 if (!XEmptyRegion (region)) 1011 if (!XEmptyRegion (region))
795 { 1012 {
1013 XRectangle r;
1014
796 XSetRegion (disp, WinGC, region); 1015 XSetRegion (disp, WinGC, region);
1016 XClipBox (region, &r);
797 1017
798 refresh (miny, maxy); 1018 refresh (r.y, r.y + r.height, 0, 1);
1019
799 XDestroyRegion (region); 1020 XDestroyRegion (region);
800 region = XCreateRegion (); 1021 region = XCreateRegion ();
801 maxy = 0;
802 miny = win_y + height;
803 } 1022 }
804 } 1023 }
805} 1024}
806 1025
807 1026
845 continuation = argv[++i]; 1064 continuation = argv[++i];
846 else if (!strcmp (arg, "-font") || !strcmp (arg, "-fn")) 1065 else if (!strcmp (arg, "-font") || !strcmp (arg, "-fn"))
847 fontname = argv[++i]; 1066 fontname = argv[++i];
848#if HAS_REGEX 1067#if HAS_REGEX
849 else if (!strcmp (arg, "-t")) 1068 else if (!strcmp (arg, "-t"))
1069 {
850 transform = argv[++i]; 1070 transform = argv[++i];
1071 transform_to = argv[++i];
1072 printf("transform: '%s' to '%s'\n", transform, transform_to);
1073 }
851#endif 1074#endif
852 else if (!strcmp (arg, "-fork") || !strcmp (arg, "-f")) 1075 else if (!strcmp (arg, "-fork") || !strcmp (arg, "-f"))
853 opt_daemonize = 1; 1076 opt_daemonize = 1;
854 else if (!strcmp (arg, "-reload")) 1077 else if (!strcmp (arg, "-reload"))
855 { 1078 {
856 reload = atoi (argv[++i]); 1079 reload = atoi (argv[++i]);
857 command = argv[++i]; 1080 command = argv[++i];
858 } 1081 }
859 else if (!strcmp (arg, "-shade")) 1082 else if (!strcmp (arg, "-shade"))
860 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;
861 else if (!strcmp (arg, "-frame")) 1088 else if (!strcmp (arg, "-frame"))
862 opt_frame = 1; 1089 opt_frame = 1;
863 else if (!strcmp (arg, "-no-filename")) 1090 else if (!strcmp (arg, "-no-filename"))
864 opt_nofilename = 1; 1091 opt_nofilename = 1;
865 else if (!strcmp (arg, "-reverse")) 1092 else if (!strcmp (arg, "-reverse"))
868 opt_whole = 1; 1095 opt_whole = 1;
869 else if (!strcmp (arg, "-partial")) 1096 else if (!strcmp (arg, "-partial"))
870 opt_partial = 1; 1097 opt_partial = 1;
871 else if (!strcmp (arg, "-update")) 1098 else if (!strcmp (arg, "-update"))
872 opt_update = opt_partial = 1; 1099 opt_update = opt_partial = 1;
1100 else if (!strcmp (arg, "-wordwrap"))
1101 opt_wordwrap = 1;
873 else if (!strcmp (arg, "-color")) 1102 else if (!strcmp (arg, "-color"))
874 def_color = argv[++i]; 1103 def_color = argv[++i];
875 else if (!strcmp (arg, "-noinitial")) 1104 else if (!strcmp (arg, "-noinitial"))
876 opt_noinitial = 1; 1105 opt_noinitial = 1;
877 else if (!strcmp (arg, "-id")) 1106 else if (!strcmp (arg, "-id"))
913 } 1142 }
914 1143
915 e = xmalloc (sizeof (struct logfile_entry)); 1144 e = xmalloc (sizeof (struct logfile_entry));
916 e->partial = 0; 1145 e->partial = 0;
917 e->buf = 0; 1146 e->buf = 0;
1147 e->index = -1;
918 1148
919 if (arg[0] == '-' && arg[1] == '\0') 1149 if (arg[0] == '-' && arg[1] == '\0')
920 { 1150 {
921 if ((e->fp = fdopen (0, "r")) == NULL) 1151 if ((e->fp = fdopen (0, "r")) == NULL)
922 perror ("fdopen"), exit (1); 1152 perror ("fdopen"), exit (1);
923 if (fcntl (0, F_SETFL, O_NONBLOCK) < 0) 1153 if (fcntl (0, F_SETFL, O_NONBLOCK) < 0)
924 perror ("fcntl"), exit (1); 1154 perror ("fcntl"), exit (1);
1155
925 e->fname = NULL; 1156 e->fname = NULL;
926 e->inode = 0; 1157 e->inode = 0;
927 e->desc = xstrdup ("stdin"); 1158 e->desc = xstrdup ("stdin");
928 } 1159 }
929 else 1160 else
930 { 1161 {
931 int l;
932
933 e->fname = xstrdup (fname); 1162 e->fname = xstrdup (fname);
1163
934 if (openlog (e) == NULL) 1164 if (openlog (e) == NULL)
935 perror (fname), exit (1); 1165 perror (fname), exit (1);
936 1166
937 l = strlen (desc); 1167 e->desc = xstrdup (desc);
938 if (l > width - 2) /* must account for [ ] */
939 l = width - 2;
940 e->desc = xmalloc (l + 1);
941 memcpy (e->desc, desc, l);
942 *(e->desc + l) = '\0';
943 } 1168 }
944 1169
945 e->color = GetColor (fcolor); 1170 e->color = GetColor (fcolor);
946 e->partial = 0; 1171 e->partial = 0;
947 e->next = NULL; 1172 e->next = NULL;
948 1173
949 if (!loglist) 1174 if (!loglist)
950 loglist = e; 1175 loglist = e;
951 if (loglist_tail) 1176 if (loglist_tail)
952 loglist_tail->next = e; 1177 loglist_tail->next = e;
1178
953 loglist_tail = e; 1179 loglist_tail = e;
954 } 1180 }
955 } 1181 }
956 1182
957 if (!loglist) 1183 if (!loglist)
959 fprintf (stderr, "You did not specify any files to tail\n" 1185 fprintf (stderr, "You did not specify any files to tail\n"
960 "use %s --help for help\n", argv[0]); 1186 "use %s --help for help\n", argv[0]);
961 exit (1); 1187 exit (1);
962 } 1188 }
963 1189
1190 if (opt_update && opt_whole)
1191 {
1192 fprintf (stderr, "Specify at most one of -update and -whole\n");
1193 exit (1);
1194 }
964 if (opt_partial && opt_whole) 1195 else if (opt_partial && opt_whole)
965 { 1196 {
966 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");
967 exit (1); 1205 exit (1);
968 } 1206 }
969 1207
970 if (opt_partial) 1208 if (opt_partial)
971 /* 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 */
972 opt_whole = 0; 1210 opt_whole = 0;
973 else if (file_count > 1) 1211 else if (file_count > 1)
974 /* 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 */
975 opt_whole = 1; 1213 opt_whole = 1;
976 1214
977#if HAS_REGEX 1215#if HAS_REGEX
978 if (transform) 1216 if (transform)
979 { 1217 {
980 int i; 1218 int i;
981 1219
1220 printf("compiling regexp '%s'\n", transform);
982 transformre = xmalloc (sizeof (transformre)); 1221 transformre = xmalloc (sizeof (regex_t));
983 i = regcomp (&transformre, transform, REG_EXTENDED); 1222 i = regcomp (transformre, transform, REG_EXTENDED);
984 if (i != 0) 1223 if (i != 0)
985 { 1224 {
986 char buf[512]; 1225 char buf[512];
987 1226
988 regerror (i, &transformre, buf, sizeof (buf)); 1227 regerror (i, transformre, buf, sizeof (buf));
989 fprintf (stderr, "Cannot compile regular expression: %s\n", buf); 1228 fprintf (stderr, "Cannot compile regular expression: %s\n", buf);
990 } 1229 }
1230 else
1231 {
1232 printf("compiled '%s' OK to %x\n", transform, (int)transformre);
1233 }
991 } 1234 }
992#endif 1235#endif
993 1236
994 InitWindow (); 1237 InitWindow ();
995 1238
1026{ 1269{
1027 void *p; 1270 void *p;
1028 1271
1029 while ((p = strdup (string)) == NULL) 1272 while ((p = strdup (string)) == NULL)
1030 { 1273 {
1031 fprintf (stderr, "Memory exausted."); 1274 fprintf (stderr, "Memory exhausted in xstrdup().\n");
1032 sleep (10); 1275 sleep (10);
1033 } 1276 }
1034 1277
1035 return p; 1278 return p;
1036} 1279}
1040{ 1283{
1041 void *p; 1284 void *p;
1042 1285
1043 while ((p = malloc (size)) == NULL) 1286 while ((p = malloc (size)) == NULL)
1044 { 1287 {
1045 fprintf (stderr, "Memory exausted."); 1288 fprintf (stderr, "Memory exhausted in xmalloc().\n");
1289 sleep (10);
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");
1046 sleep (10); 1303 sleep (10);
1047 } 1304 }
1048 1305
1049 return p; 1306 return p;
1050} 1307}
1063 " -reverse print new lines at the top\n" 1320 " -reverse print new lines at the top\n"
1064 " -whole wait for \\n before showing a line\n" 1321 " -whole wait for \\n before showing a line\n"
1065 " -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"
1066 " -update allow updates to old partial lines\n" 1323 " -update allow updates to old partial lines\n"
1067 " -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"
1068 " defaults to \"[+]\"\n" 1326 " defaults to \"[+]\"\n"
1069 " -shade add shading to font\n" 1327 " -shade add shading to font\n"
1070 " -noinitial don't display the last file lines on\n" 1328 " -noinitial don't display the last file lines on\n"
1071 " startup\n" 1329 " startup\n"
1072 " -i | -interval seconds interval between checks (fractional\n" 1330 " -i | -interval seconds interval between checks (fractional\n"
1073 " values o.k.). Default 3 seconds\n" 1331 " values o.k.). Default 2.4 seconds\n"
1074 " -V display version information and exit\n" 1332 " -V display version information and exit\n"
1075 "\n"); 1333 "\n");
1076 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 "
1077 "/var/log/secure,red,'ALERT'\n", myname); 1335 "/var/log/secure,red,'ALERT'\n", myname);
1078 exit (0); 1336 exit (0);
1079} 1337}
1080 1338
1081void 1339void
1086} 1344}
1087 1345
1088int 1346int
1089daemonize (void) 1347daemonize (void)
1090{ 1348{
1349 pid_t pid;
1350
1091 switch (fork ()) 1351 switch (pid = fork ())
1092 { 1352 {
1093 case -1: 1353 case -1:
1094 return -1; 1354 return -1;
1095 case 0: 1355 case 0:
1096 break; 1356 break;
1097 default: 1357 default:
1358 /*printf("%d\n", pid);*/
1098 _exit (0); 1359 exit (0);
1099 } 1360 }
1100 1361
1101 if (setsid () == -1) 1362 if (setsid () == -1)
1102 return -1; 1363 return -1;
1103 1364

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines