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.3 by pcg, Sun May 5 19:10:39 2002 UTC vs.
Revision 1.10 by chris_moore, Fri Mar 26 12:11:05 2004 UTC

28#include <fcntl.h> 28#include <fcntl.h>
29#include <errno.h> 29#include <errno.h>
30#include <sys/time.h> 30#include <sys/time.h>
31#include <sys/stat.h> 31#include <sys/stat.h>
32#include <sys/types.h> 32#include <sys/types.h>
33#include <locale.h>
33#if HAS_REGEX 34#if HAS_REGEX
34#include <regex.h> 35#include <regex.h>
35#endif 36#endif
36#include <X11/Xlib.h> 37#include <X11/Xlib.h>
38#include <X11/Xatom.h>
37#include <X11/Xutil.h> 39#include <X11/Xutil.h>
38 40
39/* data structures */ 41/* data structures */
40struct logfile_entry { 42struct logfile_entry {
41 char *fname; /* name of file */ 43 char *fname; /* name of file */
42 char *desc; /* alternative description */ 44 char *desc; /* alternative description */
45 char *buf; /* text read but not yet displayed */
43 FILE *fp; /* FILE struct associated with file */ 46 FILE *fp; /* FILE struct associated with file */
44 ino_t inode; /* inode of the file opened */ 47 ino_t inode; /* inode of the file opened */
45 off_t last_size; /* file size at the last check */ 48 off_t last_size; /* file size at the last check */
46 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 */
47 struct logfile_entry *next; 53 struct logfile_entry *next;
48}; 54};
49 55
50struct linematrix { 56struct linematrix {
51 char *line; 57 char *line;
52 unsigned long color; 58 unsigned long color;
53}; 59};
54 60
55 61
56/* global variables */ 62/* global variables */
57unsigned int width = STD_WIDTH, listlen = STD_HEIGHT; 63int width = STD_WIDTH, listlen = STD_HEIGHT;
58int win_x = LOC_X, win_y = LOC_Y; 64int win_x = LOC_X, win_y = LOC_Y;
59int w = -1, h = -1, font_width, font_height, font_descent; 65int w = -1, h = -1, font_width, font_height, font_descent;
60int do_reopen; 66int do_reopen;
61struct timeval interval = { 2, 400000 }; /* see Knuth */ 67struct timeval interval = { 3, 0 }; /* see Knuth */
68XFontSet fontset;
62 69
63/* command line options */ 70/* command line options */
64int opt_noinitial, opt_shade, opt_frame, opt_reverse=0, opt_nofilename, 71int opt_noinitial, opt_shade, opt_frame, opt_reverse, opt_nofilename,
65 geom_mask, reload = 3600; 72 opt_whole, opt_update, geom_mask, reload = 0;
66const char *command = NULL, 73const char *command = NULL,
67 *fontname = USE_FONT, *dispname = NULL, *def_color = DEF_COLOR; 74 *fontname = USE_FONT, *dispname = NULL, *def_color = DEF_COLOR,
75 *continuation = "[+]";
68 76
69struct logfile_entry *loglist = NULL, *loglist_tail = NULL; 77struct logfile_entry *loglist = NULL, *loglist_tail = NULL;
70 78
71Display *disp; 79Display *disp;
72Window root; 80Window root;
92unsigned long GetColor(const char *); 100unsigned long GetColor(const char *);
93void redraw(void); 101void redraw(void);
94void refresh(struct linematrix *, int, int); 102void refresh(struct linematrix *, int, int);
95 103
96void transform_line(char *s); 104void transform_line(char *s);
97int lineinput(char *, int, FILE *); 105int lineinput(struct logfile_entry *);
98void reopen(void); 106void reopen(void);
99void check_open_files(void); 107void check_open_files(void);
100FILE *openlog(struct logfile_entry *); 108FILE *openlog(struct logfile_entry *);
101void main_loop(void); 109void main_loop(void);
102 110
122 do_reopen = 1; 130 do_reopen = 1;
123} 131}
124 132
125void force_refresh(int dummy) 133void force_refresh(int dummy)
126{ 134{
127 XClearWindow(disp, root); 135 XClearArea(disp, root, win_x, win_y, w, h + font_descent + 2, False);
128 redraw(); 136 redraw();
129} 137}
130 138
131void blank_window(int dummy) 139void blank_window(int dummy)
132{ 140{
133 XClearWindow(disp, root); 141 XClearArea(disp, root, win_x, win_y, w, h + font_descent + 2, False);
134 XFlush(disp); 142 XFlush(disp);
135 exit(0); 143 exit(0);
136} 144}
137 145
138/* X related functions */ 146/* X related functions */
141 XColor Color; 149 XColor Color;
142 XWindowAttributes Attributes; 150 XWindowAttributes Attributes;
143 151
144 XGetWindowAttributes(disp, root, &Attributes); 152 XGetWindowAttributes(disp, root, &Attributes);
145 Color.pixel = 0; 153 Color.pixel = 0;
154
146 if (!XParseColor(disp, Attributes.colormap, ColorName, &Color)) 155 if (!XParseColor(disp, Attributes.colormap, ColorName, &Color))
147 fprintf(stderr, "can't parse %s\n", ColorName); 156 fprintf(stderr, "can't parse %s\n", ColorName);
148 else if (!XAllocColor(disp, Attributes.colormap, &Color)) 157 else if (!XAllocColor(disp, Attributes.colormap, &Color))
149 fprintf(stderr, "can't allocate %s\n", ColorName); 158 fprintf(stderr, "can't allocate %s\n", ColorName);
159
150 return Color.pixel; 160 return Color.pixel;
161}
162
163static Window root_window (Display *display, int screen_number)
164{
165 Atom __SWM_VROOT = XInternAtom (display, "__SWM_VROOT", False);
166 Window real_root_window = RootWindow (display, screen_number);
167
168 if (root) /* root window set via option */
169 return root;
170
171 if (__SWM_VROOT != None)
172 {
173 Window unused, *windows;
174 unsigned int count;
175
176 if (XQueryTree (display, real_root_window, &unused, &unused, &windows,
177 &count))
178 {
179 int i;
180
181 for (i = 0; i < count; i++)
182 {
183 Atom type;
184 int format;
185 unsigned long nitems, bytes_after_return;
186 unsigned char *virtual_root_window;
187
188 if (XGetWindowProperty (display, windows[i], __SWM_VROOT,
189 0, 1, False, XA_WINDOW, &type, &format,
190 &nitems, &bytes_after_return,
191 &virtual_root_window)
192 == Success)
193 {
194 if (type != None)
195 {
196 if (type == XA_WINDOW)
197 {
198 XFree (windows);
199 return (Window)virtual_root_window;
200 }
201 else
202 fprintf (stderr, "__SWM_VROOT property type mismatch");
203 }
204 }
205 else
206 fprintf (stderr,
207 "failed to get __SWM_VROOT property on window 0x%lx",
208 windows[i]);
209 }
210
211 if (count)
212 XFree (windows);
213 }
214 else
215 fprintf (stderr, "Can't query tree on root window 0x%lx",
216 real_root_window);
217 }
218 else
219 /* This shouldn't happen. The Xlib documentation is wrong BTW. */
220 fprintf (stderr, "Can't intern atom __SWM_VROOT");
221
222 return real_root_window;
151} 223}
152 224
153void InitWindow(void) 225void InitWindow(void)
154{ 226{
155 XGCValues gcv; 227 XGCValues gcv;
163 exit(1); 235 exit(1);
164 } 236 }
165 screen = DefaultScreen(disp); 237 screen = DefaultScreen(disp);
166 ScreenHeight = DisplayHeight(disp, screen); 238 ScreenHeight = DisplayHeight(disp, screen);
167 ScreenWidth = DisplayWidth(disp, screen); 239 ScreenWidth = DisplayWidth(disp, screen);
240
168 root = RootWindow(disp, screen); 241 root = root_window (disp, screen);
242
169 gcm = GCBackground; 243 gcm = GCBackground;
170 gcv.graphics_exposures = True; 244 gcv.graphics_exposures = True;
171 WinGC = XCreateGC(disp, root, gcm, &gcv); 245 WinGC = XCreateGC(disp, root, gcm, &gcv);
172 XMapWindow(disp, root); 246 XMapWindow(disp, root);
173 XSetForeground(disp, WinGC, GetColor(DEF_COLOR)); 247 XSetForeground(disp, WinGC, GetColor(DEF_COLOR));
179 font_descent = info->max_bounds.descent; 253 font_descent = info->max_bounds.descent;
180 font_height = info->max_bounds.ascent + font_descent; 254 font_height = info->max_bounds.ascent + font_descent;
181 255
182 w = width * font_width; 256 w = width * font_width;
183 h = listlen * font_height; 257 h = listlen * font_height;
258
184 if (geom_mask & XNegative) 259 if (geom_mask & XNegative)
185 win_x = win_x + ScreenWidth - w; 260 win_x = win_x + ScreenWidth - w;
186 if (geom_mask & YNegative) 261 if (geom_mask & YNegative)
187 win_y = win_y + ScreenHeight - h; 262 win_y = win_y + ScreenHeight - h;
188 263
189 XSelectInput(disp, root, ExposureMask|FocusChangeMask); 264 XSelectInput(disp, root, ExposureMask|FocusChangeMask);
190} 265}
191 266
192char * 267char *
193detabificate (char *s) 268detabificate (char *s)
194{ 269{
195 char * out; 270 char * out;
196 int i, j; 271 int i, j;
197 272
198 out = malloc (8 * strlen (s) + 1); 273 out = malloc (8 * strlen (s) + 1);
199 274
200 for(i = 0, j = 0; s[i]; i++) 275 for(i = 0, j = 0; s[i]; i++)
201 { 276 {
202 if (s[i] == '\t') 277 if (s[i] == '\t')
203 do 278 do
204 out[j++] = ' '; 279 out[j++] = ' ';
205 while (j % 8); 280 while (j % 8);
206 else 281 else
207 out[j++] = s[i]; 282 out[j++] = s[i];
208 } 283 }
209 284
210 out[j] = '\0'; 285 out[j] = '\0';
211 return out; 286 return out;
212} 287}
213 288
214/* 289/*
232 maxy -= win_y - font_height; 307 maxy -= win_y - font_height;
233 308
234 for (lin = listlen; lin--;) 309 for (lin = listlen; lin--;)
235 { 310 {
236 char *temp; 311 char *temp;
237 312
238 offset -= font_height; 313 offset -= font_height;
239 314
240 if (offset < miny || offset > maxy) 315 if (offset < miny || offset > maxy)
241 continue; 316 continue;
242 317
243 if (opt_reverse) 318#define LIN ((opt_reverse)?(listlen-lin-1):(lin))
244 temp = detabificate (lines[listlen-lin-1].line);
245 else
246 temp = detabificate (lines[lin].line); 319 temp = detabificate (lines[LIN].line);
247 320
248 if (opt_shade) 321 if (opt_shade)
249 { 322 {
250 XSetForeground (disp, WinGC, black_color); 323 XSetForeground (disp, WinGC, black_color);
324#if 0
325 XmbDrawString (disp, root, fontset, WinGC, win_x + 2, win_y + offset + 2,
326 temp, strlen (temp));
327#endif
251 XDrawString (disp, root, WinGC, win_x + 2, win_y + offset + 2, 328 XDrawString (disp, root, WinGC, win_x + 2, win_y + offset + 2,
252 temp, strlen (temp)); 329 temp, strlen (temp));
253 } 330 }
254 331
255 XSetForeground (disp, WinGC, lines[lin].color); 332 XSetForeground (disp, WinGC, lines[LIN].color);
333#if 0
334 XmbDrawString (disp, root, fontset, WinGC, win_x, win_y + offset,
335 temp, strlen (temp));
336#endif
256 XDrawString (disp, root, WinGC, win_x, win_y + offset, 337 XDrawString (disp, root, WinGC, win_x, win_y + offset,
257 temp, strlen (temp)); 338 temp, strlen (temp));
258
259 free (temp); 339 free (temp);
260 } 340 }
261 341
262 if (opt_frame) { 342 if (opt_frame) {
263 int bot_y = win_y + h + font_descent + 2; 343 int bot_y = win_y + h + font_descent + 2;
293#endif 373#endif
294 374
295 375
296/* 376/*
297 * This routine should read 'width' characters and not more. However, 377 * This routine should read 'width' characters and not more. However,
298 * we really want to read width + 1 charachters if the last char is a '\n', 378 * we really want to read width + 1 characters if the last char is a '\n',
299 * 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
300 * 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
301 * width + 2 wide! 381 * width + 2 wide!
302 */ 382 */
303int lineinput(char *string, int slen, FILE *f) 383int lineinput(struct logfile_entry *logfile)
304{ 384{
305 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;
306 391
307 do { 392 do {
308 if (fgets(string, slen, f) == NULL) /* EOF or Error */ 393 if (fgets(string + len, slen - len, f) == NULL) /* EOF or Error */
309 return 0; 394 return 0;
310 395
311 len = strlen(string); 396 len = strlen(string);
312 } while (len == 0); 397 } while (len == 0);
313 398
399 logfile->partial = 0;
400
401 /* if the string ends in a newline, delete the newline */
314 if (string[len - 1] == '\n') 402 if (string[len - 1] == '\n')
315 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 */
316 else if (len >= slen - 1) { 405 else if (len >= slen - 1) {
317 ungetc(string[len - 1], f); 406 ungetc(string[len - 1], f);
318 string[len - 1] = '\0'; 407 string[len - 1] = '\0';
319 } 408 } else if (opt_whole)
409 return 0;
410 else
411 logfile->partial = 1;
320 412
321#if HAS_REGEX 413#if HAS_REGEX
322 transform_line(string); 414 transform_line(string);
323#endif 415#endif
416 logfile->lastpartial = partial;
324 return len; 417 return len;
325} 418}
326 419
327/* input: reads file->fname 420/* input: reads file->fname
328 * output: fills file->fp, file->inode 421 * output: fills file->fp, file->inode
347 file->inode = stats.st_ino; 440 file->inode = stats.st_ino;
348 441
349 if (opt_noinitial) 442 if (opt_noinitial)
350 fseek (file->fp, 0, SEEK_END); 443 fseek (file->fp, 0, SEEK_END);
351 else if (stats.st_size > (listlen + 1) * width) 444 else if (stats.st_size > (listlen + 1) * width)
352 {
353 char dummy[255];
354
355 fseek(file->fp, -((listlen + 2) * width), SEEK_END); 445 fseek(file->fp, -((listlen + 2) * width), SEEK_END);
356 /* the pointer might point halfway some line. Let's
357 be nice and skip this damaged line */
358 lineinput(dummy, sizeof(dummy), file->fp);
359 }
360 446
361 file->last_size = stats.st_size; 447 file->last_size = stats.st_size;
362 return file->fp; 448 return file->fp;
363} 449}
364 450
411} 497}
412 498
413#define SCROLL_UP(lines, listlen) \ 499#define SCROLL_UP(lines, listlen) \
414{ \ 500{ \
415 int cur_line; \ 501 int cur_line; \
502 struct logfile_entry *current; \
416 for (cur_line = 0; cur_line < (listlen - 1); cur_line++) { \ 503 for (cur_line = 0; cur_line < (listlen - 1); cur_line++) { \
417 strcpy(lines[cur_line].line, lines[cur_line + 1].line); \ 504 strcpy(lines[cur_line].line, lines[cur_line + 1].line); \
418 lines[cur_line].color = lines[cur_line + 1].color; \ 505 lines[cur_line].color = lines[cur_line + 1].color; \
419 } \ 506 } \
507 for (current = loglist; current; current = current->next) \
508 if (current->partial && current->index) \
509 current->index--; \
420} 510}
421 511
422void main_loop(void) 512void main_loop(void)
423{ 513{
424 struct linematrix *lines = xmalloc(sizeof(struct linematrix) * listlen); 514 struct linematrix *lines = xmalloc(sizeof(struct linematrix) * listlen);
425 int lin, miny, maxy, buflen; 515 int lin, miny, maxy;
426 char *buf;
427 time_t lastreload; 516 time_t lastreload;
428 Region region = XCreateRegion(); 517 Region region = XCreateRegion();
429 XEvent xev; 518 XEvent xev;
519 struct logfile_entry *lastprinted = NULL;
520 struct logfile_entry *current;
430 521
431 maxy = 0; 522 maxy = 0;
432 miny = win_y + h; 523 miny = win_y + h;
433 buflen = width + 2;
434 buf = xmalloc(buflen);
435 lastreload = time(NULL); 524 lastreload = time(NULL);
436 525
437 /* Initialize linematrix */ 526 /* Initialize linematrix */
438 for (lin = 0; lin < listlen; lin++) { 527 for (lin = 0; lin < listlen; lin++) {
439 lines[lin].line = xmalloc(buflen); 528 lines[lin].line = xmalloc(width + 2);
440 strcpy(lines[lin].line, "~"); 529 strcpy(lines[lin].line, "~");
441 lines[lin].color = GetColor(def_color); 530 lines[lin].color = GetColor(def_color);
442 } 531 }
443 532
444 if (!opt_noinitial) 533 /* show the display full of empty lines ("~") in case the first
445 while (lineinput(buf, buflen, loglist->fp) != 0) { 534 * time around the loop doesn't produce any output, as it won't if
446 SCROLL_UP(lines, listlen); 535 * either (a) -noinitial is set or (b) all the files are currently
447 /* print the next line */ 536 * empty */
448 strcpy(lines[listlen - 1].line, buf); 537 redraw();
449 }
450 538
451 for (;;) { 539 for (;;) {
452 int need_update = 0; 540 int need_update = 0;
453 struct logfile_entry *current;
454 static struct logfile_entry *lastprinted = NULL;
455 541
456 /* read logs */ 542 /* read logs */
457 for (current = loglist; current; current = current->next) { 543 for (current = loglist; current; current = current->next) {
458 if (!current->fp) 544 if (!current->fp)
459 continue; /* skip missing files */ 545 continue; /* skip missing files */
460 546
461 clearerr(current->fp); 547 clearerr(current->fp);
462 548
463 while (lineinput(buf, buflen, current->fp) != 0) { 549 while (lineinput(current) != 0) {
550
551 /* if we're trying to update old partial lines in
552 * place, and the last time this file was updated the
553 * output was partial, and that partial line is not
554 * too close to the top of the screen, then update
555 * that partial line */
556 if (opt_update && current->lastpartial && current->index >= 3) {
557 int old_len = strlen(lines[current->index].line);
558 int new_len = strlen(current->buf);
559 int space_on_old_line = width - old_len;
560 strncat(lines[current->index].line, current->buf, width - old_len);
561 /* if we can't fit the whole update into the old
562 * partial line then we're going to have to print
563 * the rest of it at the bottom on the screen */
564 if (new_len > space_on_old_line) {
565 /* strcpy() doesn't like the strings to
566 * overlap in memory, but memmove() doesn't
567 * care */
568 memmove(current->buf,
569 current->buf + space_on_old_line,
570 new_len - space_on_old_line + 1);
571 } else {
572 need_update = 1;
573 strcpy(current->buf, "");
574 continue;
575 }
576 }
577
464 /* print filename if any, and if last line was from 578 /* print filename if any, and if last line was from
465 different file */ 579 * different file */
466 if (!opt_nofilename && 580 if (!opt_nofilename &&
467 !(lastprinted && lastprinted == current) && 581 lastprinted != current &&
468 current->desc[0]) { 582 current->desc[0]) {
469 SCROLL_UP(lines, listlen); 583 SCROLL_UP(lines, listlen);
470 sprintf(lines[listlen - 1].line, "[%s]", current->desc); 584 sprintf(lines[listlen - 1].line, "[%s]", current->desc);
471 lines[listlen - 1].color = current->color; 585 lines[listlen - 1].color = current->color;
472 } 586 }
473 587
588 /* if this is the same file we showed last, and the
589 * last time we showed it, it wasn't finished, then
590 * append to the last line shown */
591 if (lastprinted == current && current->lastpartial) {
592 int old_len = strlen(lines[listlen - 1].line);
593 int new_len = strlen(current->buf);
594 strncat(lines[listlen - 1].line, current->buf, width - old_len);
595 /* if it doesn't all fit, then put the part that
596 * doesn't fit on a new line */
597 if (new_len > width - old_len) {
474 SCROLL_UP(lines, listlen); 598 SCROLL_UP(lines, listlen);
599 strcpy(lines[listlen - 1].line, current->buf + width - old_len);
600 }
601 /* show the 'continuation' string because we've got a
602 * continued partial line, but we weren't able to
603 * append it to the old displayed partial line */
604 } else if (current->lastpartial) {
605 int old_len = strlen(continuation);
606 int new_len = strlen(current->buf);
607 SCROLL_UP(lines, listlen);
608 strcpy(lines[listlen - 1].line, continuation);
609 strncat(lines[listlen - 1].line, current->buf, width - old_len);
610 /* it might not fit, now that we've displayed the
611 * continuation string, so we may need to 'wrap' it */
612 if (new_len > width - old_len) {
613 SCROLL_UP(lines, listlen);
614 strcpy(lines[listlen - 1].line, current->buf + width - old_len);
615 }
616 } else {
617 SCROLL_UP(lines, listlen);
475 strcpy(lines[listlen - 1].line, buf); 618 strcpy(lines[listlen - 1].line, current->buf);
619 }
620
621 /* we've shown the line now. clear the buffer for the next line */
622 strcpy(current->buf, "");
623 current->index = listlen - 1;
476 lines[listlen - 1].color = current->color; 624 lines[listlen - 1].color = current->color;
477 625
478 lastprinted = current; 626 lastprinted = current;
479 need_update = 1; 627 need_update = 1;
480 } 628 }
526 } 674 }
527 } 675 }
528 676
529 /* reload if requested */ 677 /* reload if requested */
530 if (reload && lastreload + reload < time(NULL)) { 678 if (reload && lastreload + reload < time(NULL)) {
531 if (command) 679 if (command && command[0])
532 system(command); 680 system(command);
533 681
534 reopen(); 682 reopen();
535 lastreload = time(NULL); 683 lastreload = time(NULL);
536 } 684 }
549 697
550int main(int argc, char *argv[]) 698int main(int argc, char *argv[])
551{ 699{
552 int i; 700 int i;
553 int opt_daemonize = 0; 701 int opt_daemonize = 0;
702 int opt_partial = 0, file_count = 0;
554#if HAS_REGEX 703#if HAS_REGEX
555 char *transform = NULL; 704 char *transform = NULL;
556#endif 705#endif
706
707 setlocale (LC_CTYPE, ""); /* try to initialize the locale. */
557 708
558 /* window needs to be initialized before colorlookups can be done */ 709 /* window needs to be initialized before colorlookups can be done */
559 /* just a dummy to get the color lookups right */ 710 /* just a dummy to get the color lookups right */
560 geom_mask = NoValue; 711 geom_mask = NoValue;
561 InitWindow(); 712 InitWindow();
562 713
563 for (i = 1; i < argc; i++) { 714 for (i = 1; i < argc; i++) {
715 const char *arg = argv[i];
716
564 if (argv[i][0] == '-' && argv[i][1] != '\0' && argv[i][1] != ',') { 717 if (arg[0] == '-' && arg[1] != '\0' && arg[1] != ',') {
718 if (arg[1] == '-')
719 arg++;
720
565 if (!strcmp(argv[i], "--?") || 721 if (!strcmp(arg, "-?") ||
566 !strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) 722 !strcmp(arg, "-help") || !strcmp(arg, "-h"))
567 display_help(argv[0]); 723 display_help(argv[0]);
568 else if (!strcmp(argv[i], "-V")) 724 else if (!strcmp(arg, "-V"))
569 display_version(); 725 display_version();
570 else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "-geometry")) 726 else if (!strcmp(arg, "-g") || !strcmp(arg, "-geometry"))
571 geom_mask = XParseGeometry(argv[++i], 727 geom_mask = XParseGeometry(argv[++i],
572 &win_x, &win_y, &width, &listlen); 728 &win_x, &win_y, &width, &listlen);
573 else if (!strcmp(argv[i], "-display")) 729 else if (!strcmp(arg, "-display"))
574 dispname = argv[++i]; 730 dispname = argv[++i];
731 else if (!strcmp(arg, "-cont"))
732 continuation = argv[++i];
575 else if (!strcmp(argv[i], "-font") || !strcmp(argv[i], "-fn")) 733 else if (!strcmp(arg, "-font") || !strcmp(arg, "-fn"))
576 fontname = argv[++i]; 734 fontname = argv[++i];
577#if HAS_REGEX 735#if HAS_REGEX
578 else if (!strcmp(argv[i], "-t")) 736 else if (!strcmp(arg, "-t"))
579 transform = argv[++i]; 737 transform = argv[++i];
580#endif 738#endif
581 else if (!strcmp(argv[i], "-fork") || !strcmp(argv[i], "-f")) 739 else if (!strcmp(arg, "-fork") || !strcmp(arg, "-f"))
582 opt_daemonize = 1; 740 opt_daemonize = 1;
583 else if (!strcmp(argv[i], "-reload")) { 741 else if (!strcmp(arg, "-reload")) {
584 reload = atoi(argv[++i]); 742 reload = atoi(argv[++i]);
585 command = argv[++i]; 743 command = argv[++i];
586 } 744 }
587 else if (!strcmp(argv[i], "-shade")) 745 else if (!strcmp(arg, "-shade"))
588 opt_shade = 1; 746 opt_shade = 1;
589 else if (!strcmp(argv[i], "-frame")) 747 else if (!strcmp(arg, "-frame"))
590 opt_frame = 1; 748 opt_frame = 1;
591 else if (!strcmp(argv[i], "-no-filename")) 749 else if (!strcmp(arg, "-no-filename"))
592 opt_nofilename = 1; 750 opt_nofilename = 1;
593 else if (!strcmp(argv[i], "-reverse")) 751 else if (!strcmp(arg, "-reverse"))
594 opt_reverse = 1; 752 opt_reverse = 1;
753 else if (!strcmp(arg, "-whole"))
754 opt_whole = 1;
755 else if (!strcmp(arg, "-partial"))
756 opt_partial = 1;
757 else if (!strcmp(arg, "-update"))
758 opt_update = opt_partial = 1;
595 else if (!strcmp(argv[i], "-color")) 759 else if (!strcmp(arg, "-color"))
596 def_color = argv[++i]; 760 def_color = argv[++i];
597 else if (!strcmp(argv[i], "-noinitial")) 761 else if (!strcmp(arg, "-noinitial"))
598 opt_noinitial = 1; 762 opt_noinitial = 1;
763 else if (!strcmp(arg, "-id"))
764 root = atoi (argv[++i]);
599 else if (!strcmp(argv[i], "-interval") || !strcmp(argv[i], "-i")) { 765 else if (!strcmp(arg, "-interval") || !strcmp(arg, "-i")) {
600 double iv = atof(argv[++i]); 766 double iv = atof(argv[++i]);
601 767
602 interval.tv_sec = (int) iv; 768 interval.tv_sec = (int) iv;
603 interval.tv_usec = (iv - interval.tv_sec) * 1e6; 769 interval.tv_usec = (iv - interval.tv_sec) * 1e6;
604 } else { 770 } else {
605 fprintf(stderr, "Unknown option '%s'.\n" 771 fprintf(stderr, "Unknown option '%s'.\n"
606 "Try --help for more information.\n", argv[i]); 772 "Try --help for more information.\n", arg);
607 exit(1); 773 exit(1);
608 } 774 }
609 } else { /* it must be a filename */ 775 } else { /* it must be a filename */
610 struct logfile_entry *e; 776 struct logfile_entry *e;
611 const char *fname, *desc, *fcolor = def_color; 777 const char *fname, *desc, *fcolor = def_color;
612 char *p; 778 char *p;
613 779
780 file_count++;
781
614 /* this is not foolproof yet (',' in filenames are not allowed) */ 782 /* this is not foolproof yet (',' in filenames are not allowed) */
615 fname = desc = argv[i]; 783 fname = desc = arg;
616 if ((p = strchr(argv[i], ','))) { 784 if ((p = strchr(arg, ','))) {
617 *p = '\0'; 785 *p = '\0';
618 fcolor = p + 1; 786 fcolor = p + 1;
619 787
620 if ((p = strchr(fcolor, ','))) { 788 if ((p = strchr(fcolor, ','))) {
621 *p = '\0'; 789 *p = '\0';
622 desc = p + 1; 790 desc = p + 1;
623 } 791 }
624 } 792 }
625 793
626 e = xmalloc(sizeof(struct logfile_entry)); 794 e = xmalloc(sizeof(struct logfile_entry));
627 if (argv[i][0] == '-' && argv[i][1] == '\0') { 795 if (arg[0] == '-' && arg[1] == '\0') {
628 if ((e->fp = fdopen(0, "r")) == NULL) 796 if ((e->fp = fdopen(0, "r")) == NULL)
629 perror("fdopen"), exit(1); 797 perror("fdopen"), exit(1);
630 if (fcntl(0, F_SETFL, O_NONBLOCK) < 0) 798 if (fcntl(0, F_SETFL, O_NONBLOCK) < 0)
631 perror("fcntl"), exit(1); 799 perror("fcntl"), exit(1);
632 e->fname = NULL; 800 e->fname = NULL;
646 memcpy(e->desc, desc, l); 814 memcpy(e->desc, desc, l);
647 *(e->desc + l) = '\0'; 815 *(e->desc + l) = '\0';
648 } 816 }
649 817
650 e->color = GetColor(fcolor); 818 e->color = GetColor(fcolor);
819 e->buf = xmalloc(width + 2);
820 e->partial = 0;
821 e->buf[0] = '\0';
651 e->next = NULL; 822 e->next = NULL;
652 823
653 if (!loglist) 824 if (!loglist)
654 loglist = e; 825 loglist = e;
655 if (loglist_tail) 826 if (loglist_tail)
660 831
661 if (!loglist) { 832 if (!loglist) {
662 fprintf(stderr, "You did not specify any files to tail\n" 833 fprintf(stderr, "You did not specify any files to tail\n"
663 "use %s --help for help\n", argv[0]); 834 "use %s --help for help\n", argv[0]);
664 exit(1); 835 exit(1);
836 }
837
838 if (opt_partial && opt_whole) {
839 fprintf(stderr, "Specify at most one of -partial and -whole\n");
840 exit(1);
841 }
842
843 /* if we specifically requested to see partial lines then don't insist on whole lines */
844 if (opt_partial) {
845 opt_whole = 0;
846 /* otherwise, if we've viewing multiple files, default to showing whole lines */
847 } else if (file_count > 1) {
848 opt_whole = 1;
665 } 849 }
666 850
667#if HAS_REGEX 851#if HAS_REGEX
668 if (transform) { 852 if (transform) {
669 int i; 853 int i;
734 printf("Usage: %s [options] file1[,color[,desc]] " 918 printf("Usage: %s [options] file1[,color[,desc]] "
735 "[file2[,color[,desc]] ...]\n", myname); 919 "[file2[,color[,desc]] ...]\n", myname);
736 printf(" -g | -geometry geometry -g WIDTHxHEIGHT+X+Y\n" 920 printf(" -g | -geometry geometry -g WIDTHxHEIGHT+X+Y\n"
737 " -color color use color $color as default\n" 921 " -color color use color $color as default\n"
738 " -reload sec command reload after $sec and run command\n" 922 " -reload sec command reload after $sec and run command\n"
739 " by default -- 3 mins\n" 923 " -id id window id to use instead of the root window\n"
740 " -font FONTSPEC (-fn) font to use\n" 924 " -font FONTSPEC (-fn) font to use\n"
741 " -f | -fork fork into background\n" 925 " -f | -fork fork into background\n"
742 " -reverse print new lines at the top\n" 926 " -reverse print new lines at the top\n"
927 " -whole wait for \\n before showing a line\n"
928 " -partial show lines even if they don't end with a \\n\n"
929 " -update allow updates to old partial lines\n"
930 " -cont string to prefix continued partial lines with\n"
931 " defaults to \"[+]\"\n"
743 " -shade add shading to font\n" 932 " -shade add shading to font\n"
744 " -noinitial don't display the last file lines on\n" 933 " -noinitial don't display the last file lines on\n"
745 " startup\n" 934 " startup\n"
746 " -i | -interval seconds interval between checks (fractional\n" 935 " -i | -interval seconds interval between checks (fractional\n"
747 " values o.k.). Default 3\n" 936 " values o.k.). Default 3 seconds\n"
748 " -V display version information and exit\n" 937 " -V display version information and exit\n"
749 "\n"); 938 "\n");
750 printf("Example:\n%s -g 80x25+100+50 -font fixed /var/log/messages,green " 939 printf("Example:\n%s -g 80x25+100+50 -font fixed /var/log/messages,green "
751 "/var/log/secure,red,'ALERT'\n", myname); 940 "/var/log/secure,red,'ALERT'\n", myname);
752 exit(0); 941 exit(0);
770 if (setsid() == -1) 959 if (setsid() == -1)
771 return -1; 960 return -1;
772 961
773 return 0; 962 return 0;
774} 963}
775

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines