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.18 by pcg, Sat Mar 27 16:12:00 2004 UTC

58}; 58};
59 59
60struct linematrix 60struct linematrix
61{ 61{
62 char *line; 62 char *line;
63 int len;
63 unsigned long color; 64 unsigned long color;
64}; 65};
65 66
66/* global variables */ 67/* global variables */
68struct linematrix *lines;
67int width = STD_WIDTH, height = STD_HEIGHT, listlen; 69int width = STD_WIDTH, height = STD_HEIGHT, listlen;
68int win_x = LOC_X, win_y = LOC_Y; 70int win_x = LOC_X, win_y = LOC_Y;
69int font_descent, font_height; 71int font_descent, font_height;
70int do_reopen; 72int do_reopen;
71struct timeval interval = { 3, 0 }; 73struct timeval interval = { 3, 0 };
102void blank_window (int); 104void blank_window (int);
103 105
104void InitWindow (void); 106void InitWindow (void);
105unsigned long GetColor (const char *); 107unsigned long GetColor (const char *);
106void redraw (void); 108void redraw (void);
107void refresh (struct linematrix *, int, int); 109void refresh (int, int);
108 110
109void transform_line (char *s); 111void transform_line (char *s);
110int lineinput (struct logfile_entry *); 112int lineinput (struct logfile_entry *);
111void reopen (void); 113void reopen (void);
112void check_open_files (void); 114void check_open_files (void);
113FILE *openlog (struct logfile_entry *); 115FILE *openlog (struct logfile_entry *);
114void main_loop (void); 116static void main_loop (void);
115 117
116void display_version (void); 118void display_version (void);
117void display_help (char *); 119void display_help (char *);
118void install_signal (int, void (*)(int)); 120void install_signal (int, void (*)(int));
119void *xstrdup (const char *); 121void *xstrdup (const char *);
138} 140}
139 141
140void 142void
141force_refresh (int dummy) 143force_refresh (int dummy)
142{ 144{
143 XClearArea (disp, root, win_x, win_y, width, height, False);
144 redraw (); 145 redraw ();
145} 146}
146 147
147void 148void
148blank_window (int dummy) 149blank_window (int dummy)
306 height = listlen * font_height; 307 height = listlen * font_height;
307 308
308 XSelectInput (disp, root, ExposureMask | FocusChangeMask); 309 XSelectInput (disp, root, ExposureMask | FocusChangeMask);
309} 310}
310 311
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/* 312/*
334 * redraw does a complete redraw, rather than an update (i.e. the area 313 * redraw does a complete redraw, rather than an update (i.e. the area
335 * gets cleared first) 314 * gets cleared first)
336 * the rest is handled by regular refresh()'es 315 * the rest is handled by regular refresh()'es
337 */ 316 */
338void 317void
339redraw (void) 318redraw (void)
340{ 319{
341 XClearArea (disp, root, win_x, win_y, width, height, True); 320 XClearArea (disp, root, win_x, win_y, width, height, False);
321 refresh (0, 32768);
342} 322}
343 323
344/* Just redraw everything without clearing (i.e. after an EXPOSE event) */ 324/* Just redraw everything without clearing (i.e. after an EXPOSE event) */
345void 325void
346refresh (struct linematrix *lines, int miny, int maxy) 326refresh (int miny, int maxy)
347{ 327{
348 int lin; 328 int lin;
349 int offset = (listlen + 1) * font_height; 329 int offset = (listlen + 1) * font_height;
350 unsigned long black_color = GetColor ("black"); 330 unsigned long black_color = GetColor ("black");
351 331
352 miny -= win_y + font_height; 332 miny -= win_y + font_height;
353 maxy -= win_y - font_height; 333 maxy -= win_y - font_height;
354 334
335
355 for (lin = listlen; lin--;) 336 for (lin = listlen; lin--;)
356 { 337 {
357 struct linematrix *line =
358 lines + (opt_reverse ? listlen - lin - 1 : lin); 338 struct linematrix *line = lines + (opt_reverse ? listlen - lin - 1 : lin);
359 339
360 offset -= font_height; 340 offset -= font_height;
361 341
362 if (offset < miny || offset > maxy) 342 if (offset < miny || offset > maxy)
363 continue; 343 continue;
364 344
365 if (opt_shade) 345 if (opt_shade)
366 { 346 {
367 XSetForeground (disp, WinGC, black_color); 347 XSetForeground (disp, WinGC, black_color);
368 XmbDrawString (disp, root, fontset, WinGC, win_x + 2, 348 XmbDrawString (disp, root, fontset, WinGC, win_x + 2,
369 win_y + offset + 2, line->line, strlen (line->line)); 349 win_y + offset + 2, line->line, line->len);
370 } 350 }
371 351
372 XSetForeground (disp, WinGC, line->color); 352 XSetForeground (disp, WinGC, line->color);
373 XmbDrawString (disp, root, fontset, WinGC, win_x, win_y + offset, 353 XmbDrawString (disp, root, fontset, WinGC, win_x, win_y + offset,
374 line->line, strlen (line->line)); 354 line->line, line->len);
375 } 355 }
376 356
377 if (opt_frame) 357 if (opt_frame)
358 {
359 XSetForeground (disp, WinGC, GetColor (def_color));
378 XDrawRectangle (disp, root, WinGC, win_x, win_y, width, height); 360 XDrawRectangle (disp, root, WinGC, win_x - 2, win_y - 2, width + 4, height + 4);
361 }
379} 362}
380 363
381#if HAS_REGEX 364#if HAS_REGEX
382void 365void
383transform_line (char *s) 366transform_line (char *s)
403 } 386 }
404 } 387 }
405} 388}
406#endif 389#endif
407 390
391char *
392concat_line (const char *p1, const char *p2)
393{
394 int l1 = p1 ? strlen (p1) : 0;
395 int l2 = strlen (p2);
396 char *r = xmalloc (l1 + l2 + 1);
397
398 memcpy (r, p1, l1);
399 memcpy (r + l1, p2, l2);
400 r[l1 + l2] = 0;
401
402 return r;
403}
408 404
409/* 405/*
410 * This routine should read 'width' characters and not more. However, 406 * This routine should read a single line, no matter how long.
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 */ 407 */
416int 408int
417lineinput (struct logfile_entry *logfile) 409lineinput (struct logfile_entry *logfile)
418{ 410{
419 char *string = logfile->buf; 411 char buff[1024], *p = buff;
420 int slen = width + 2; 412 int ch;
421 FILE *f = logfile->fp; 413 int ofs = logfile->buf ? strlen (logfile->buf) : 0;
422
423 int len = strlen (string);
424 int partial = logfile->partial;
425 414
426 do 415 do
427 { 416 {
428 if (fgets (string + len, slen - len, f) == NULL) /* EOF or Error */ 417 ch = fgetc (logfile->fp);
429 return 0;
430 418
431 len = strlen (string); 419 if (ch == '\n' || ch == EOF)
432 } 420 break;
433 while (len == 0); 421 else if (ch == '\r')
434 422 continue; /* skip */
435 logfile->partial = 0; 423 else if (ch == '\t')
436 424 {
437 if (string[len - 1] == '\n') 425 do
438 /* if the string ends in a newline, delete the newline */ 426 {
439 string[len - 1] = '\0'; /* erase newline */ 427 *p++ = ' ';
440 else if (len >= slen - 1) 428 ofs++;
429 }
430 while (ofs & 7);
431 }
432 else
433 {
434 *p++ = ch;
435 ofs++;
436 }
441 { 437 }
442 /* otherwise if we've read one too many characters, un-read the last one and delete it */ 438 while (p < buff + (sizeof buff) - 8 - 1);
443 ungetc (string[len - 1], f); 439
444 string[len - 1] = '\0'; 440 if (p == buff)
445 }
446 else if (opt_whole)
447 return 0; 441 return 0;
448 else 442
443 *p = 0;
444
445 p = concat_line (logfile->buf, buff);
446 free (logfile->buf); logfile->buf = p;
447
448 logfile->lastpartial = logfile->partial;
449 logfile->partial = 1; 449 logfile->partial = ch == EOF;
450
451 if (logfile->partial && opt_whole)
452 return 0;
450 453
451#if HAS_REGEX 454#if HAS_REGEX
452 transform_line (string); 455 transform_line (logfile->buf);
453#endif 456#endif
454 logfile->lastpartial = partial;
455 return len; 457 return 1;
456} 458}
457 459
458/* input: reads file->fname 460/* input: reads file->fname
459 * output: fills file->fp, file->inode 461 * output: fills file->fp, file->inode
460 * returns file->fp 462 * returns file->fp
543 e->last_size = stats.st_size; 545 e->last_size = stats.st_size;
544 } 546 }
545 } 547 }
546} 548}
547 549
548#define SCROLL_UP(lines, listlen) \ 550/*
549{ \ 551 * insert a single physical line (that must be short enough to fit)
550 int cur_line; \ 552 * at position "idx" by pushing up lines above it. the caller
551 struct logfile_entry *current; \ 553 * MUST then fill in lines[idx] with valid data.
554 */
555static void
556insert_line (int idx)
557{
558 int cur_line;
559 struct logfile_entry *current;
560
561 free (lines[0].line);
562
552 for (cur_line = 0; cur_line < (listlen - 1); cur_line++) { \ 563 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; \ 564 lines[cur_line] = lines[cur_line + 1];
555 } \ 565
556 for (current = loglist; current; current = current->next) \ 566 for (current = loglist; current; current = current->next)
557 if (current->partial && current->index) \ 567 if (current->index <= idx)
558 current->index--; \ 568 current->index--;
559} 569}
560 570
561void 571/*
572 * remove a single physical line at position "idx" by moving the lines above it
573 * down and inserting a "~" line at the top.
574 */
575static void
576delete_line (int idx)
577{
578 int cur_line;
579 struct logfile_entry *current;
580
581 for (cur_line = idx; cur_line > 0; cur_line--)
582 lines[cur_line] = lines[cur_line - 1];
583
584 lines[0].line = strdup ("~");
585
586 for (current = loglist; current; current = current->next)
587 if (current->index >= 0 && current->index <= idx)
588 current->index++;
589}
590
591/*
592 * takes a logical log file line and split it into multiple physical
593 * screen lines by splitting it whenever a part becomes too long.
594 * lal lines will be inserted at position "idx".
595 */
596static void
597split_line (int idx, const char *str, unsigned long color)
598{
599 int l = strlen (str);
600 const char *p = str;
601
602 do
603 {
604 const char *beg = p;
605 int w = 0;
606
607 while (*p)
608 {
609 int len = mblen (p, l);
610 if (len <= 0)
611 len = 1; /* ignore (don't skip) ilegal character sequences */
612
613 int cw = XmbTextEscapement (fontset, p, len);
614 if (cw + w >= width)
615 break;
616
617 w += cw;
618 p += len;
619 l -= len;
620 }
621
622 {
623 char *s = xmalloc (p - beg + 1);
624 memcpy (s, beg, p - beg);
625 s[p - beg] = 0;
626 insert_line (idx);
627 lines[idx].line = s;
628 lines[idx].len = p - beg;
629 lines[idx].color = color;
630 }
631 }
632 while (l);
633}
634
635/*
636 * append something to an existing physical line. this is done
637 * by deleting the file on-screen, concatenating the new data to it
638 * and splitting it again.
639 */
640static void
641append_line (int idx, const char *str)
642{
643 unsigned long color = lines[idx].color;
644 char *old = lines[idx].line;
645 char *new = concat_line (old, str);
646
647 free (old);
648
649 delete_line (idx);
650 split_line (idx, new, color);
651}
652
653static void
562main_loop (void) 654main_loop (void)
563{ 655{
564 struct linematrix *lines = xmalloc (sizeof (struct linematrix) * listlen); 656 lines = xmalloc (sizeof (struct linematrix) * listlen);
565 int lin, miny, maxy; 657 int lin, miny, maxy;
566 time_t lastreload; 658 time_t lastreload;
567 Region region = XCreateRegion (); 659 Region region = XCreateRegion ();
568 XEvent xev; 660 XEvent xev;
569 struct logfile_entry *lastprinted = NULL; 661 struct logfile_entry *lastprinted = NULL;
570 struct logfile_entry *current; 662 struct logfile_entry *current;
663 int need_update = 1;
571 664
572 maxy = 0; 665 maxy = 0;
573 miny = win_y + height; 666 miny = win_y + height;
574 lastreload = time (NULL); 667 lastreload = time (NULL);
575 668
576 /* Initialize linematrix */ 669 /* Initialize linematrix */
577 for (lin = 0; lin < listlen; lin++) 670 for (lin = 0; lin < listlen; lin++)
578 { 671 {
579 lines[lin].line = xmalloc (width + 2); 672 lines[lin].line = strdup ("~");
580 strcpy (lines[lin].line, "~"); 673 lines[lin].len = 1;
581 lines[lin].color = GetColor (def_color); 674 lines[lin].color = GetColor (def_color);
582 } 675 }
583 676
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 (;;) 677 for (;;)
591 { 678 {
592 int need_update = 0;
593
594 /* read logs */ 679 /* read logs */
595 for (current = loglist; current; current = current->next) 680 for (current = loglist; current; current = current->next)
596 { 681 {
597 if (!current->fp) 682 if (!current->fp)
598 continue; /* skip missing files */ 683 continue; /* skip missing files */
599 684
600 clearerr (current->fp); 685 clearerr (current->fp);
601 686
602 while (lineinput (current) != 0) 687 while (lineinput (current))
603 { 688 {
689 fprintf (stderr, "got line <%s>\n", current->buf);//D
690 need_update = 1;
604 /* if we're trying to update old partial lines in 691 /* if we're trying to update old partial lines in
605 * place, and the last time this file was updated the 692 * place, and the last time this file was updated the
606 * output was partial, and that partial line is not 693 * output was partial, and that partial line is not
607 * too close to the top of the screen, then update 694 * too close to the top of the screen, then update
608 * that partial line */ 695 * that partial line */
609 if (opt_update && current->lastpartial && current->index >= 3) 696 if (opt_update && current->lastpartial && current->index >= 0)
610 { 697 {
611 int old_len = strlen (lines[current->index].line); 698 int idx = current->index;
612 int new_len = strlen (current->buf); 699 append_line (idx, current->buf);
613 int space_on_old_line = width / font_width - old_len; //D 700 current->index = idx;
614 701 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; 702 continue;
634 }
635 } 703 }
636 704
637 /* print filename if any, and if last line was from 705 /* print filename if any, and if last line was from
638 * different file */ 706 * different file */
639 if (!opt_nofilename &&
640 lastprinted != current && current->desc[0]) 707 if (!opt_nofilename && lastprinted != current && current->desc[0])
641 { 708 {
642 SCROLL_UP (lines, listlen); 709 char buf[1024]; /* HACK */
643 sprintf (lines[listlen - 1].line, "[%s]", current->desc); 710 snprintf (buf, sizeof (buf), "[%s]", current->desc);
644 lines[listlen - 1].color = current->color; 711 split_line (listlen - 1, buf, current->color);
645 } 712 }
646 713
647 /* if this is the same file we showed last, and the 714 /* if this is the same file we showed last, and the
648 * last time we showed it, it wasn't finished, then 715 * last time we showed it, it wasn't finished, then
649 * append to the last line shown */ 716 * append to the last line shown */
650 if (lastprinted == current && current->lastpartial) 717 if (lastprinted == current && current->lastpartial)
651 { 718 {
652 int old_len = strlen (lines[listlen - 1].line);
653 int new_len = strlen (current->buf);
654 strncat (lines[listlen - 1].line, current->buf, 719 append_line (listlen - 1, current->buf);
655 width - old_len); 720 free (current->buf), current->buf = 0;
656 /* if it doesn't all fit, then put the part that
657 * doesn't fit on a new line */
658 if (new_len > width - old_len)
659 { 721 continue;
660 SCROLL_UP (lines, listlen);
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 } 722 }
685 else 723 else
686 { 724 {
687 SCROLL_UP (lines, listlen); 725 split_line (listlen - 1, current->buf, current->color);
688 strcpy (lines[listlen - 1].line, current->buf); 726 free (current->buf), current->buf = 0;
689 } 727 }
690 728
691 /* we've shown the line now. clear the buffer for the next line */
692 strcpy (current->buf, "");
693 current->index = listlen - 1; 729 current->index = listlen - 1;
694 lines[listlen - 1].color = current->color;
695
696 lastprinted = current; 730 lastprinted = current;
697 need_update = 1;
698 } 731 }
699 } 732 }
700 733
701 if (need_update) 734 if (need_update)
735 {
702 redraw (); 736 redraw ();
737 need_update = 0;
738 }
703 else 739 else
704 { 740 {
705 XFlush (disp); 741 XFlush (disp);
706 742
707 if (!XPending (disp)) 743 if (!XPending (disp))
762 798
763 if (!XEmptyRegion (region)) 799 if (!XEmptyRegion (region))
764 { 800 {
765 XSetRegion (disp, WinGC, region); 801 XSetRegion (disp, WinGC, region);
766 802
767 refresh (lines, miny, maxy); 803 refresh (miny, maxy);
768 XDestroyRegion (region); 804 XDestroyRegion (region);
769 region = XCreateRegion (); 805 region = XCreateRegion ();
770 maxy = 0; 806 maxy = 0;
771 miny = win_y + height; 807 miny = 32768;
772 } 808 }
773 } 809 }
774} 810}
775 811
776 812
880 desc = p + 1; 916 desc = p + 1;
881 } 917 }
882 } 918 }
883 919
884 e = xmalloc (sizeof (struct logfile_entry)); 920 e = xmalloc (sizeof (struct logfile_entry));
921 e->partial = 0;
922 e->buf = 0;
923 e->index = -1;
924
885 if (arg[0] == '-' && arg[1] == '\0') 925 if (arg[0] == '-' && arg[1] == '\0')
886 { 926 {
887 if ((e->fp = fdopen (0, "r")) == NULL) 927 if ((e->fp = fdopen (0, "r")) == NULL)
888 perror ("fdopen"), exit (1); 928 perror ("fdopen"), exit (1);
889 if (fcntl (0, F_SETFL, O_NONBLOCK) < 0) 929 if (fcntl (0, F_SETFL, O_NONBLOCK) < 0)
907 memcpy (e->desc, desc, l); 947 memcpy (e->desc, desc, l);
908 *(e->desc + l) = '\0'; 948 *(e->desc + l) = '\0';
909 } 949 }
910 950
911 e->color = GetColor (fcolor); 951 e->color = GetColor (fcolor);
912 e->buf = xmalloc (width + 2);
913 e->partial = 0; 952 e->partial = 0;
914 e->buf[0] = '\0';
915 e->next = NULL; 953 e->next = NULL;
916 954
917 if (!loglist) 955 if (!loglist)
918 loglist = e; 956 loglist = e;
919 if (loglist_tail) 957 if (loglist_tail)
1011 while ((p = malloc (size)) == NULL) 1049 while ((p = malloc (size)) == NULL)
1012 { 1050 {
1013 fprintf (stderr, "Memory exausted."); 1051 fprintf (stderr, "Memory exausted.");
1014 sleep (10); 1052 sleep (10);
1015 } 1053 }
1054
1016 return p; 1055 return p;
1017} 1056}
1018 1057
1019void 1058void
1020display_help (char *myname) 1059display_help (char *myname)

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines