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.9 by chris_moore, Fri Mar 26 03:00:52 2004 UTC

38#include <X11/Xatom.h> 38#include <X11/Xatom.h>
39#include <X11/Xutil.h> 39#include <X11/Xutil.h>
40 40
41/* data structures */ 41/* data structures */
42struct logfile_entry { 42struct logfile_entry {
43 char *fname; /* name of file */ 43 char *fname; /* name of file */
44 char *desc; /* alternative description */ 44 char *desc; /* alternative description */
45 char *buf; /* text read but not yet displayed */
45 FILE *fp; /* FILE struct associated with file */ 46 FILE *fp; /* FILE struct associated with file */
46 ino_t inode; /* inode of the file opened */ 47 ino_t inode; /* inode of the file opened */
47 off_t last_size; /* file size at the last check */ 48 off_t last_size; /* file size at the last check */
48 unsigned long color; /* color to be used for printing */ 49 unsigned long color; /* color to be used for printing */
50 int partial; /* true if the last line isn't complete */
51 int lastpartial; /* true if the previous output wasn't complete */
52 int index; /* index into linematrix of a partial line */
49 struct logfile_entry *next; 53 struct logfile_entry *next;
50}; 54};
51 55
52struct linematrix { 56struct linematrix {
53 char *line; 57 char *line;
58/* global variables */ 62/* global variables */
59int width = STD_WIDTH, listlen = STD_HEIGHT; 63int width = STD_WIDTH, listlen = STD_HEIGHT;
60int win_x = LOC_X, win_y = LOC_Y; 64int win_x = LOC_X, win_y = LOC_Y;
61int w = -1, h = -1, font_width, font_height, font_descent; 65int w = -1, h = -1, font_width, font_height, font_descent;
62int do_reopen; 66int do_reopen;
63struct timeval interval = { 2, 400000 }; /* see Knuth */ 67struct timeval interval = { 3, 0 }; /* see Knuth */
64XFontSet fontset; 68XFontSet fontset;
65 69
66/* command line options */ 70/* command line options */
67int opt_noinitial, opt_shade, opt_frame, opt_reverse, opt_nofilename, 71int opt_noinitial, opt_shade, opt_frame, opt_reverse, opt_nofilename,
68 geom_mask, reload = 3600; 72 opt_whole, opt_update, geom_mask, reload = 0;
69const char *command = NULL, 73const char *command = NULL,
70 *fontname = USE_FONT, *dispname = NULL, *def_color = DEF_COLOR; 74 *fontname = USE_FONT, *dispname = NULL, *def_color = DEF_COLOR,
75 *continuation = "[+]";
71 76
72struct logfile_entry *loglist = NULL, *loglist_tail = NULL; 77struct logfile_entry *loglist = NULL, *loglist_tail = NULL;
73 78
74Display *disp; 79Display *disp;
75Window root; 80Window root;
95unsigned long GetColor(const char *); 100unsigned long GetColor(const char *);
96void redraw(void); 101void redraw(void);
97void refresh(struct linematrix *, int, int); 102void refresh(struct linematrix *, int, int);
98 103
99void transform_line(char *s); 104void transform_line(char *s);
100int lineinput(char *, int, FILE *); 105int lineinput(struct logfile_entry *);
101void reopen(void); 106void reopen(void);
102void check_open_files(void); 107void check_open_files(void);
103FILE *openlog(struct logfile_entry *); 108FILE *openlog(struct logfile_entry *);
104void main_loop(void); 109void main_loop(void);
105 110
373 * we really want to read width + 1 characters if the last char is a '\n', 378 * 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 379 * 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 380 * the last character if it's not a newline. This means 'string' must be
376 * width + 2 wide! 381 * width + 2 wide!
377 */ 382 */
378int lineinput(char *string, int slen, FILE *f) 383int lineinput(struct logfile_entry *logfile)
379{ 384{
380 int len; 385 char *string = logfile->buf;
386 int slen = width + 2;
387 FILE *f = logfile->fp;
388
389 int len = strlen(string);
390 int partial = logfile->partial;
381 391
382 do { 392 do {
383 if (fgets(string, slen, f) == NULL) /* EOF or Error */ 393 if (fgets(string + len, slen - len, f) == NULL) /* EOF or Error */
384 return 0; 394 return 0;
385 395
386 len = strlen(string); 396 len = strlen(string);
387 } while (len == 0); 397 } while (len == 0);
388 398
399 logfile->partial = 0;
400
401 /* if the string ends in a newline, delete the newline */
389 if (string[len - 1] == '\n') 402 if (string[len - 1] == '\n')
390 string[len - 1] = '\0'; /* erase newline */ 403 string[len - 1] = '\0'; /* erase newline */
404 /* otherwise if we've read one too many characters, un-read the last one and delete it */
391 else if (len >= slen - 1) { 405 else if (len >= slen - 1) {
392 ungetc(string[len - 1], f); 406 ungetc(string[len - 1], f);
393 string[len - 1] = '\0'; 407 string[len - 1] = '\0';
394 } 408 } else if (opt_whole)
409 return 0;
410 else
411 logfile->partial = 1;
395 412
396#if HAS_REGEX 413#if HAS_REGEX
397 transform_line(string); 414 transform_line(string);
398#endif 415#endif
416 logfile->lastpartial = partial;
399 return len; 417 return len;
400} 418}
401 419
402/* input: reads file->fname 420/* input: reads file->fname
403 * output: fills file->fp, file->inode 421 * output: fills file->fp, file->inode
423 441
424 if (opt_noinitial) 442 if (opt_noinitial)
425 fseek (file->fp, 0, SEEK_END); 443 fseek (file->fp, 0, SEEK_END);
426 else if (stats.st_size > (listlen + 1) * width) 444 else if (stats.st_size > (listlen + 1) * width)
427 { 445 {
428 char dummy[255];
429
430 fseek(file->fp, -((listlen + 2) * width), SEEK_END); 446 fseek(file->fp, -((listlen + 2) * width), SEEK_END);
431 /* the pointer might point halfway some line. Let's
432 be nice and skip this damaged line */
433 lineinput(dummy, sizeof(dummy), file->fp);
434 } 447 }
435 448
436 file->last_size = stats.st_size; 449 file->last_size = stats.st_size;
437 return file->fp; 450 return file->fp;
438} 451}
486} 499}
487 500
488#define SCROLL_UP(lines, listlen) \ 501#define SCROLL_UP(lines, listlen) \
489{ \ 502{ \
490 int cur_line; \ 503 int cur_line; \
504 struct logfile_entry *current; \
491 for (cur_line = 0; cur_line < (listlen - 1); cur_line++) { \ 505 for (cur_line = 0; cur_line < (listlen - 1); cur_line++) { \
492 strcpy(lines[cur_line].line, lines[cur_line + 1].line); \ 506 strcpy(lines[cur_line].line, lines[cur_line + 1].line); \
493 lines[cur_line].color = lines[cur_line + 1].color; \ 507 lines[cur_line].color = lines[cur_line + 1].color; \
494 } \ 508 } \
509 for (current = loglist; current; current = current->next) \
510 if (current->partial && current->index) \
511 current->index--; \
495} 512}
496 513
497void main_loop(void) 514void main_loop(void)
498{ 515{
499 struct linematrix *lines = xmalloc(sizeof(struct linematrix) * listlen); 516 struct linematrix *lines = xmalloc(sizeof(struct linematrix) * listlen);
500 int lin, miny, maxy, buflen; 517 int lin, miny, maxy;
501 char *buf;
502 time_t lastreload; 518 time_t lastreload;
503 Region region = XCreateRegion(); 519 Region region = XCreateRegion();
504 XEvent xev; 520 XEvent xev;
521 struct logfile_entry *lastprinted = NULL;
522 struct logfile_entry *current;
505 523
506 maxy = 0; 524 maxy = 0;
507 miny = win_y + h; 525 miny = win_y + h;
508 buflen = width + 2;
509 buf = xmalloc(buflen);
510 lastreload = time(NULL); 526 lastreload = time(NULL);
511 527
512 /* Initialize linematrix */ 528 /* Initialize linematrix */
513 for (lin = 0; lin < listlen; lin++) { 529 for (lin = 0; lin < listlen; lin++) {
514 lines[lin].line = xmalloc(buflen); 530 lines[lin].line = xmalloc(width + 2);
515 strcpy(lines[lin].line, "~"); 531 strcpy(lines[lin].line, "~");
516 lines[lin].color = GetColor(def_color); 532 lines[lin].color = GetColor(def_color);
517 } 533 }
518 534
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 (;;) { 535 for (;;) {
531 int need_update = 0; 536 int need_update = 0;
532 struct logfile_entry *current;
533 static struct logfile_entry *lastprinted = NULL;
534 537
535 /* read logs */ 538 /* read logs */
536 for (current = loglist; current; current = current->next) { 539 for (current = loglist; current; current = current->next) {
537 if (!current->fp) 540 if (!current->fp)
538 continue; /* skip missing files */ 541 continue; /* skip missing files */
539 542
540 clearerr(current->fp); 543 clearerr(current->fp);
541 544
542 while (lineinput(buf, buflen, current->fp) != 0) { 545 while (lineinput(current) != 0) {
546
547 /* if we're trying to update old partial lines in
548 * place, and the last time this file was updated the
549 * output was partial, and that partial line is not
550 * too close to the top of the screen, then update
551 * that partial line */
552 if (opt_update && current->lastpartial && current->index >= 3) {
553 int old_len = strlen(lines[current->index].line);
554 int new_len = strlen(current->buf);
555 int space_on_old_line = width - old_len;
556 strncat(lines[current->index].line, current->buf, width - old_len);
557 /* if we can't fit the whole update into the old
558 * partial line then we're going to have to print
559 * the rest of it at the bottom on the screen */
560 if (new_len > space_on_old_line) {
561 /* strcpy() doesn't like the strings to
562 * overlap in memory, but memmove() doesn't
563 * care */
564 memmove(current->buf,
565 current->buf + space_on_old_line,
566 new_len - space_on_old_line + 1);
567 } else {
568 need_update = 1;
569 strcpy(current->buf, "");
570 continue;
571 }
572 }
573
543 /* print filename if any, and if last line was from 574 /* print filename if any, and if last line was from
544 different file */ 575 * different file */
545 if (!opt_nofilename && 576 if (!opt_nofilename &&
546 !(lastprinted && lastprinted == current) && 577 lastprinted != current &&
547 current->desc[0]) { 578 current->desc[0]) {
548 SCROLL_UP(lines, listlen); 579 SCROLL_UP(lines, listlen);
549 sprintf(lines[listlen - 1].line, "[%s]", current->desc); 580 sprintf(lines[listlen - 1].line, "[%s]", current->desc);
550 lines[listlen - 1].color = current->color; 581 lines[listlen - 1].color = current->color;
551 } 582 }
552 583
584 /* if this is the same file we showed last, and the
585 * last time we showed it, it wasn't finished, then
586 * append to the last line shown */
587 if (lastprinted == current && current->lastpartial) {
588 int old_len = strlen(lines[listlen - 1].line);
589 int new_len = strlen(current->buf);
590 strncat(lines[listlen - 1].line, current->buf, width - old_len);
591 /* if it doesn't all fit, then put the part that
592 * doesn't fit on a new line */
593 if (new_len > width - old_len) {
553 SCROLL_UP(lines, listlen); 594 SCROLL_UP(lines, listlen);
595 strcpy(lines[listlen - 1].line, current->buf + width - old_len);
596 }
597 /* show the 'continuation' string because we've got a
598 * continued partial line, but we weren't able to
599 * append it to the old displayed partial line */
600 } else if (current->lastpartial) {
601 int old_len = strlen(continuation);
602 int new_len = strlen(current->buf);
603 SCROLL_UP(lines, listlen);
604 strcpy(lines[listlen - 1].line, continuation);
605 strncat(lines[listlen - 1].line, current->buf, width - old_len);
606 /* it might not fit, now that we've displayed the
607 * continuation string, so we may need to 'wrap' it */
608 if (new_len > width - old_len) {
609 SCROLL_UP(lines, listlen);
610 strcpy(lines[listlen - 1].line, current->buf + width - old_len);
611 }
612 } else {
613 SCROLL_UP(lines, listlen);
554 strcpy(lines[listlen - 1].line, buf); 614 strcpy(lines[listlen - 1].line, current->buf);
615 }
616
617 /* we've shown the line now. clear the buffer for the next line */
618 strcpy(current->buf, "");
619 current->index = listlen - 1;
555 lines[listlen - 1].color = current->color; 620 lines[listlen - 1].color = current->color;
556 621
557 lastprinted = current; 622 lastprinted = current;
558 need_update = 1; 623 need_update = 1;
559 } 624 }
605 } 670 }
606 } 671 }
607 672
608 /* reload if requested */ 673 /* reload if requested */
609 if (reload && lastreload + reload < time(NULL)) { 674 if (reload && lastreload + reload < time(NULL)) {
610 if (command) 675 if (command && command[0])
611 system(command); 676 system(command);
612 677
613 reopen(); 678 reopen();
614 lastreload = time(NULL); 679 lastreload = time(NULL);
615 } 680 }
628 693
629int main(int argc, char *argv[]) 694int main(int argc, char *argv[])
630{ 695{
631 int i; 696 int i;
632 int opt_daemonize = 0; 697 int opt_daemonize = 0;
698 int opt_partial = 0, file_count = 0;
633#if HAS_REGEX 699#if HAS_REGEX
634 char *transform = NULL; 700 char *transform = NULL;
635#endif 701#endif
636 702
637 setlocale (LC_CTYPE, ""); /* try to initialize the locale. */ 703 setlocale (LC_CTYPE, ""); /* try to initialize the locale. */
656 else if (!strcmp(arg, "-g") || !strcmp(arg, "-geometry")) 722 else if (!strcmp(arg, "-g") || !strcmp(arg, "-geometry"))
657 geom_mask = XParseGeometry(argv[++i], 723 geom_mask = XParseGeometry(argv[++i],
658 &win_x, &win_y, &width, &listlen); 724 &win_x, &win_y, &width, &listlen);
659 else if (!strcmp(arg, "-display")) 725 else if (!strcmp(arg, "-display"))
660 dispname = argv[++i]; 726 dispname = argv[++i];
727 else if (!strcmp(arg, "-cont"))
728 continuation = argv[++i];
661 else if (!strcmp(arg, "-font") || !strcmp(arg, "-fn")) 729 else if (!strcmp(arg, "-font") || !strcmp(arg, "-fn"))
662 fontname = argv[++i]; 730 fontname = argv[++i];
663#if HAS_REGEX 731#if HAS_REGEX
664 else if (!strcmp(arg, "-t")) 732 else if (!strcmp(arg, "-t"))
665 transform = argv[++i]; 733 transform = argv[++i];
676 opt_frame = 1; 744 opt_frame = 1;
677 else if (!strcmp(arg, "-no-filename")) 745 else if (!strcmp(arg, "-no-filename"))
678 opt_nofilename = 1; 746 opt_nofilename = 1;
679 else if (!strcmp(arg, "-reverse")) 747 else if (!strcmp(arg, "-reverse"))
680 opt_reverse = 1; 748 opt_reverse = 1;
749 else if (!strcmp(arg, "-whole"))
750 opt_whole = 1;
751 else if (!strcmp(arg, "-partial"))
752 opt_partial = 1;
753 else if (!strcmp(arg, "-update"))
754 opt_update = opt_partial = 1;
681 else if (!strcmp(arg, "-color")) 755 else if (!strcmp(arg, "-color"))
682 def_color = argv[++i]; 756 def_color = argv[++i];
683 else if (!strcmp(arg, "-noinitial")) 757 else if (!strcmp(arg, "-noinitial"))
684 opt_noinitial = 1; 758 opt_noinitial = 1;
685 else if (!strcmp(arg, "-id")) 759 else if (!strcmp(arg, "-id"))
697 } else { /* it must be a filename */ 771 } else { /* it must be a filename */
698 struct logfile_entry *e; 772 struct logfile_entry *e;
699 const char *fname, *desc, *fcolor = def_color; 773 const char *fname, *desc, *fcolor = def_color;
700 char *p; 774 char *p;
701 775
776 file_count++;
777
702 /* this is not foolproof yet (',' in filenames are not allowed) */ 778 /* this is not foolproof yet (',' in filenames are not allowed) */
703 fname = desc = arg; 779 fname = desc = arg;
704 if ((p = strchr(arg, ','))) { 780 if ((p = strchr(arg, ','))) {
705 *p = '\0'; 781 *p = '\0';
706 fcolor = p + 1; 782 fcolor = p + 1;
734 memcpy(e->desc, desc, l); 810 memcpy(e->desc, desc, l);
735 *(e->desc + l) = '\0'; 811 *(e->desc + l) = '\0';
736 } 812 }
737 813
738 e->color = GetColor(fcolor); 814 e->color = GetColor(fcolor);
815 e->buf = xmalloc(width + 2);
816 e->partial = 0;
817 e->buf[0] = '\0';
739 e->next = NULL; 818 e->next = NULL;
740 819
741 if (!loglist) 820 if (!loglist)
742 loglist = e; 821 loglist = e;
743 if (loglist_tail) 822 if (loglist_tail)
748 827
749 if (!loglist) { 828 if (!loglist) {
750 fprintf(stderr, "You did not specify any files to tail\n" 829 fprintf(stderr, "You did not specify any files to tail\n"
751 "use %s --help for help\n", argv[0]); 830 "use %s --help for help\n", argv[0]);
752 exit(1); 831 exit(1);
832 }
833
834 if (opt_partial && opt_whole) {
835 fprintf(stderr, "Specify at most one of -partial and -whole\n");
836 exit(1);
837 }
838
839 /* if we specifically requested to see partial lines then don't insist on whole lines */
840 if (opt_partial) {
841 opt_whole = 0;
842 /* otherwise, if we've viewing multiple files, default to showing whole lines */
843 } else if (file_count > 1) {
844 opt_whole = 1;
753 } 845 }
754 846
755#if HAS_REGEX 847#if HAS_REGEX
756 if (transform) { 848 if (transform) {
757 int i; 849 int i;
822 printf("Usage: %s [options] file1[,color[,desc]] " 914 printf("Usage: %s [options] file1[,color[,desc]] "
823 "[file2[,color[,desc]] ...]\n", myname); 915 "[file2[,color[,desc]] ...]\n", myname);
824 printf(" -g | -geometry geometry -g WIDTHxHEIGHT+X+Y\n" 916 printf(" -g | -geometry geometry -g WIDTHxHEIGHT+X+Y\n"
825 " -color color use color $color as default\n" 917 " -color color use color $color as default\n"
826 " -reload sec command reload after $sec and run command\n" 918 " -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" 919 " -id id window id to use instead of the root window\n"
829 " -font FONTSPEC (-fn) font to use\n" 920 " -font FONTSPEC (-fn) font to use\n"
830 " -f | -fork fork into background\n" 921 " -f | -fork fork into background\n"
831 " -reverse print new lines at the top\n" 922 " -reverse print new lines at the top\n"
923 " -whole wait for \\n before showing a line\n"
924 " -partial show lines even if they don't end with a \\n\n"
925 " -update allow updates to old partial lines\n"
926 " -cont string to prefix continued partial lines with\n"
927 " defaults to \"[+]\"\n"
832 " -shade add shading to font\n" 928 " -shade add shading to font\n"
833 " -noinitial don't display the last file lines on\n" 929 " -noinitial don't display the last file lines on\n"
834 " startup\n" 930 " startup\n"
835 " -i | -interval seconds interval between checks (fractional\n" 931 " -i | -interval seconds interval between checks (fractional\n"
836 " values o.k.). Default 3\n" 932 " values o.k.). Default 3 seconds\n"
837 " -V display version information and exit\n" 933 " -V display version information and exit\n"
838 "\n"); 934 "\n");
839 printf("Example:\n%s -g 80x25+100+50 -font fixed /var/log/messages,green " 935 printf("Example:\n%s -g 80x25+100+50 -font fixed /var/log/messages,green "
840 "/var/log/secure,red,'ALERT'\n", myname); 936 "/var/log/secure,red,'ALERT'\n", myname);
841 exit(0); 937 exit(0);

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines