… | |
… | |
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 | #include <locale.h> |
34 | #include <ctype.h> |
34 | #include <ctype.h> |
35 | #include <stdarg.h> |
35 | #include <stdarg.h> |
|
|
36 | #include <X11/Xlib.h> |
|
|
37 | #include <X11/Xatom.h> |
|
|
38 | #include <X11/Xutil.h> |
|
|
39 | |
36 | #if HAS_REGEX |
40 | #if HAS_REGEX |
37 | #include <regex.h> |
41 | #include <regex.h> |
38 | #endif |
42 | #endif |
39 | #include <X11/Xlib.h> |
43 | |
40 | #include <X11/Xatom.h> |
44 | #define SHADE_X 2 |
41 | #include <X11/Xutil.h> |
45 | #define SHADE_Y 2 |
42 | |
46 | |
43 | /* data structures */ |
47 | /* data structures */ |
44 | struct logfile_entry |
48 | struct logfile_entry |
45 | { |
49 | { |
46 | struct logfile_entry *next; |
50 | struct logfile_entry *next; |
… | |
… | |
53 | off_t last_size; /* file size at the last check */ |
57 | off_t last_size; /* file size at the last check */ |
54 | unsigned long color; /* color to be used for printing */ |
58 | unsigned long color; /* color to be used for printing */ |
55 | int partial; /* true if the last line isn't complete */ |
59 | int partial; /* true if the last line isn't complete */ |
56 | int lastpartial; /* true if the previous output wasn't complete */ |
60 | int lastpartial; /* true if the previous output wasn't complete */ |
57 | int index; /* index into linematrix of a partial line */ |
61 | int index; /* index into linematrix of a partial line */ |
|
|
62 | int modified; /* true if line is modified & needs displaying */ |
58 | }; |
63 | }; |
59 | |
64 | |
60 | struct linematrix |
65 | struct linematrix |
61 | { |
66 | { |
62 | char *line; |
67 | char *line; |
63 | int len; |
68 | int len; |
64 | unsigned long color; |
69 | unsigned long color; |
65 | }; |
70 | }; |
66 | |
71 | |
|
|
72 | struct displaymatrix |
|
|
73 | { |
|
|
74 | char *line; |
|
|
75 | int len; |
|
|
76 | int buffer_size; |
|
|
77 | }; |
|
|
78 | |
67 | /* global variables */ |
79 | /* global variables */ |
68 | struct linematrix *lines; |
80 | struct linematrix *lines; |
|
|
81 | struct displaymatrix *display; |
69 | int width = STD_WIDTH, height = STD_HEIGHT, listlen; |
82 | int width = STD_WIDTH, height = STD_HEIGHT, listlen; |
70 | int win_x = LOC_X, win_y = LOC_Y; |
83 | int win_x = LOC_X, win_y = LOC_Y; |
71 | int font_descent, font_height; |
84 | int font_ascent, font_height; |
|
|
85 | int effect_x_space, effect_y_space; /* how much space does shading / outlining take up */ |
|
|
86 | int effect_x_offset, effect_y_offset; /* and how does it offset the usable space */ |
72 | int do_reopen; |
87 | int do_reopen; |
73 | struct timeval interval = { 3, 0 }; |
88 | struct timeval interval = { 2, 400000 }; |
74 | XFontSet fontset; |
89 | XFontSet fontset; |
75 | |
90 | |
76 | /* command line options */ |
91 | /* command line options */ |
77 | int opt_noinitial, opt_shade, opt_frame, opt_reverse, opt_nofilename, |
92 | int opt_noinitial, opt_shade, opt_frame, opt_reverse, opt_nofilename, |
78 | opt_whole, opt_update, geom_mask, reload = 0; |
93 | opt_outline, opt_noflicker, opt_whole, opt_update, opt_wordwrap, |
|
|
94 | geom_mask, reload = 0; |
79 | const char *command = NULL, |
95 | const char *command = NULL, |
80 | *fontname = USE_FONT, *dispname = NULL, *def_color = DEF_COLOR, |
96 | *fontname = USE_FONT, *dispname = NULL, *def_color = DEF_COLOR, |
81 | *continuation = "[+]"; |
97 | *continuation = "[+]"; |
82 | |
98 | |
83 | struct logfile_entry *loglist = NULL, *loglist_tail = NULL; |
99 | struct logfile_entry *loglist = NULL, *loglist_tail = NULL; |
… | |
… | |
92 | regex_t from; |
108 | regex_t from; |
93 | const char *to; |
109 | const char *to; |
94 | struct re_list *next; |
110 | struct re_list *next; |
95 | }; |
111 | }; |
96 | struct re_list *re_head, *re_tail; |
112 | struct re_list *re_head, *re_tail; |
|
|
113 | char *transform_to = NULL; |
|
|
114 | regex_t *transformre; |
97 | #endif |
115 | #endif |
98 | |
116 | |
99 | |
117 | |
100 | /* prototypes */ |
118 | /* prototypes */ |
101 | void list_files (int); |
119 | void list_files (int); |
… | |
… | |
103 | void force_refresh (int); |
121 | void force_refresh (int); |
104 | void blank_window (int); |
122 | void blank_window (int); |
105 | |
123 | |
106 | void InitWindow (void); |
124 | void InitWindow (void); |
107 | unsigned long GetColor (const char *); |
125 | unsigned long GetColor (const char *); |
108 | void redraw (void); |
126 | void redraw (int); |
109 | void refresh (int, int); |
127 | void refresh (int, int, int, int); |
110 | |
128 | |
111 | void transform_line (char *s); |
129 | void transform_line (char *s); |
112 | int lineinput (struct logfile_entry *); |
130 | int lineinput (struct logfile_entry *); |
113 | void reopen (void); |
131 | void reopen (void); |
114 | void check_open_files (void); |
132 | void check_open_files (void); |
… | |
… | |
118 | void display_version (void); |
136 | void display_version (void); |
119 | void display_help (char *); |
137 | void display_help (char *); |
120 | void install_signal (int, void (*)(int)); |
138 | void install_signal (int, void (*)(int)); |
121 | void *xstrdup (const char *); |
139 | void *xstrdup (const char *); |
122 | void *xmalloc (size_t); |
140 | void *xmalloc (size_t); |
|
|
141 | void *xrealloc (void *, size_t); |
123 | int daemonize (void); |
142 | int daemonize (void); |
124 | |
143 | |
125 | /* signal handlers */ |
144 | /* signal handlers */ |
126 | void |
145 | void |
127 | list_files (int dummy) |
146 | list_files (int dummy) |
… | |
… | |
140 | } |
159 | } |
141 | |
160 | |
142 | void |
161 | void |
143 | force_refresh (int dummy) |
162 | force_refresh (int dummy) |
144 | { |
163 | { |
145 | redraw (); |
164 | redraw (1); |
146 | } |
165 | } |
147 | |
166 | |
148 | void |
167 | void |
149 | blank_window (int dummy) |
168 | blank_window (int dummy) |
150 | { |
169 | { |
151 | XClearArea (disp, root, win_x, win_y, width, height, False); |
170 | XClearArea (disp, root, win_x - 2, win_y - 2, width + 5, height + 5, False); |
152 | XFlush (disp); |
171 | XFlush (disp); |
153 | exit (0); |
172 | exit (0); |
154 | } |
173 | } |
155 | |
174 | |
156 | /* X related functions */ |
175 | /* X related functions */ |
… | |
… | |
285 | |
304 | |
286 | { |
305 | { |
287 | XFontSetExtents *xfe = XExtentsOfFontSet (fontset); |
306 | XFontSetExtents *xfe = XExtentsOfFontSet (fontset); |
288 | |
307 | |
289 | font_height = xfe->max_logical_extent.height; |
308 | font_height = xfe->max_logical_extent.height; |
290 | font_descent = xfe->max_logical_extent.y; |
309 | font_ascent = -xfe->max_logical_extent.y; |
291 | } |
310 | } |
292 | |
311 | |
293 | if (geom_mask & XNegative) |
312 | if (geom_mask & XNegative) |
294 | win_x = win_x + ScreenWidth - width; |
313 | win_x = win_x + ScreenWidth - width; |
295 | if (geom_mask & YNegative) |
314 | if (geom_mask & YNegative) |
296 | win_y = win_y + ScreenHeight - height; |
315 | win_y = win_y + ScreenHeight - height; |
297 | |
316 | |
|
|
317 | if (opt_outline) |
|
|
318 | { |
|
|
319 | /* adding outline increases the total width and height by 2 |
|
|
320 | pixels each, and offsets the text one pixel right and one |
|
|
321 | pixel down */ |
|
|
322 | effect_x_space = effect_y_space = 2; |
|
|
323 | effect_x_offset = effect_y_offset = 1; |
|
|
324 | } |
|
|
325 | else if (opt_shade) |
|
|
326 | { |
|
|
327 | /* adding a shadow increases the space used */ |
|
|
328 | effect_x_space = abs(SHADE_X); |
|
|
329 | effect_y_space = abs(SHADE_Y); |
|
|
330 | /* if the shadow is to the right and below then we don't need |
|
|
331 | * to move the text to make space for it, but shadows to the left |
|
|
332 | * and above need accomodating */ |
|
|
333 | effect_x_offset = SHADE_X > 0 ? 0 : -SHADE_X; |
|
|
334 | effect_y_offset = SHADE_Y > 0 ? 0 : -SHADE_Y; |
|
|
335 | } |
|
|
336 | else |
|
|
337 | { |
|
|
338 | effect_x_space = effect_y_space = 0; |
|
|
339 | effect_x_offset = effect_y_offset = 0; |
|
|
340 | } |
|
|
341 | |
|
|
342 | /* if we are using -shade or -outline, there will be less usable |
|
|
343 | * space for output */ |
298 | listlen = height / font_height; |
344 | listlen = (height - effect_y_space) / font_height; |
299 | |
345 | |
300 | if (!listlen) |
346 | if (!listlen) |
301 | { |
347 | { |
302 | fprintf (stderr, "height too small for a single line, setting to %d\n", |
348 | fprintf (stderr, "height too small for a single line, setting to %d\n", |
303 | font_height); |
349 | font_height); |
304 | listlen = 1; |
350 | listlen = 1; |
305 | } |
351 | } |
306 | |
352 | |
307 | height = listlen * font_height; |
353 | /* leave the height how the user requested it. it might not all be |
308 | |
354 | * used, but this will allow the geometry to be tuned more accurately |
|
|
355 | * (with the -frame option) |
|
|
356 | * the old code did this: |
|
|
357 | * height = listlen * font_height + effect_y_space; */ |
|
|
358 | |
309 | XSelectInput (disp, root, ExposureMask | FocusChangeMask); |
359 | XSelectInput (disp, root, ExposureMask | FocusChangeMask); |
310 | } |
360 | } |
311 | |
361 | |
312 | /* |
362 | /* |
313 | * redraw does a complete redraw, rather than an update (i.e. the area |
363 | * if redraw() is passwd a non-zero argument, it does a complete |
314 | * gets cleared first) |
364 | * redraw, rather than an update. if the argument is zero (and |
|
|
365 | * -noflicker is in effect) then only the lines which have changed |
|
|
366 | * since the last draw are redrawn. |
|
|
367 | * |
315 | * the rest is handled by regular refresh()'es |
368 | * the rest is handled by regular refresh()'es |
316 | */ |
369 | */ |
317 | void |
370 | void |
318 | redraw (void) |
371 | redraw (int redraw_all) |
319 | { |
372 | { |
320 | XClearArea (disp, root, win_x, win_y, width, height, False); |
373 | XSetClipMask (disp, WinGC, None); |
321 | refresh (0, 32768); |
374 | refresh (0, 32768, 1, redraw_all); |
322 | } |
375 | } |
323 | |
376 | |
324 | /* Just redraw everything without clearing (i.e. after an EXPOSE event) */ |
377 | /* Just redraw everything without clearing (i.e. after an EXPOSE event) */ |
325 | void |
378 | void |
326 | refresh (int miny, int maxy) |
379 | refresh (int miny, int maxy, int clear, int refresh_all) |
327 | { |
380 | { |
328 | int lin; |
381 | int lin; |
329 | int offset = (listlen + 1) * font_height; |
382 | int offset = (listlen + 1) * font_height; |
330 | unsigned long black_color = GetColor ("black"); |
383 | unsigned long black_color = GetColor ("black"); |
331 | |
384 | |
332 | miny -= win_y + font_height; |
385 | miny -= win_y + font_height; |
333 | maxy -= win_y - font_height; |
386 | maxy -= win_y - font_height; |
334 | |
387 | |
|
|
388 | if (clear && !opt_noflicker) |
|
|
389 | XClearArea (disp, root, win_x, win_y, width, height, False); |
335 | |
390 | |
336 | for (lin = listlen; lin--;) |
391 | for (lin = listlen; lin--;) |
337 | { |
392 | { |
338 | struct linematrix *line = lines + (opt_reverse ? listlen - lin - 1 : lin); |
393 | struct linematrix *line = lines + (opt_reverse ? listlen - lin - 1 : lin); |
|
|
394 | struct displaymatrix *display_line = display + lin; |
339 | |
395 | |
340 | offset -= font_height; |
396 | offset -= font_height; |
341 | |
397 | |
342 | if (offset < miny || offset > maxy) |
398 | if (offset < miny || offset > maxy) |
343 | continue; |
399 | continue; |
344 | |
400 | |
345 | if (opt_shade) |
401 | if (opt_noflicker) |
346 | { |
402 | { |
|
|
403 | /* if this line is a different than it was, then it |
|
|
404 | * needs displaying */ |
|
|
405 | if (refresh_all || |
|
|
406 | display_line->len != line->len || |
|
|
407 | strcmp(display_line->line, line->line)) |
|
|
408 | { |
|
|
409 | /* update the record of what has been displayed; |
|
|
410 | * first make sure the buffer is big enough */ |
|
|
411 | if (display_line->buffer_size <= line->len) |
|
|
412 | { |
|
|
413 | display_line->buffer_size = line->len + 1; |
|
|
414 | display_line->line = xrealloc(display_line->line, display_line->buffer_size); |
|
|
415 | } |
|
|
416 | strcpy(display_line->line, line->line); |
|
|
417 | display_line->len = line->len; |
|
|
418 | |
|
|
419 | if (clear) |
|
|
420 | XClearArea (disp, root, win_x, win_y + offset - font_height, |
|
|
421 | width + effect_x_space, font_height + effect_y_space, False); |
|
|
422 | |
|
|
423 | if (opt_outline) |
|
|
424 | { |
|
|
425 | int x, y; |
347 | XSetForeground (disp, WinGC, black_color); |
426 | XSetForeground (disp, WinGC, black_color); |
|
|
427 | |
|
|
428 | for (x = -1; x < 2; x += 2) |
|
|
429 | for (y = -1; y < 2; y += 2) |
348 | XmbDrawString (disp, root, fontset, WinGC, win_x + 2, |
430 | XmbDrawString (disp, root, fontset, WinGC, |
349 | win_y + offset + 2, line->line, line->len); |
431 | win_x + effect_x_offset + x, |
350 | } |
432 | win_y + effect_y_offset + y + offset - font_height + font_ascent, |
|
|
433 | line->line, line->len); |
|
|
434 | } |
|
|
435 | else if (opt_shade) |
|
|
436 | { |
|
|
437 | XSetForeground (disp, WinGC, black_color); |
|
|
438 | XmbDrawString (disp, root, fontset, WinGC, |
|
|
439 | win_x + effect_x_offset + SHADE_X, |
|
|
440 | win_y + effect_y_offset + offset + SHADE_Y - font_height + font_ascent, |
|
|
441 | line->line, line->len); |
|
|
442 | } |
351 | |
443 | |
352 | XSetForeground (disp, WinGC, line->color); |
444 | XSetForeground (disp, WinGC, line->color); |
353 | XmbDrawString (disp, root, fontset, WinGC, win_x, win_y + offset, |
445 | XmbDrawString (disp, root, fontset, WinGC, |
354 | line->line, line->len); |
446 | win_x + effect_x_offset, |
|
|
447 | win_y + effect_y_offset + offset - font_height + font_ascent, |
|
|
448 | line->line, line->len); |
|
|
449 | } |
|
|
450 | } |
|
|
451 | else |
|
|
452 | { |
|
|
453 | XSetForeground (disp, WinGC, line->color); |
|
|
454 | XmbDrawString (disp, root, fontset, WinGC, |
|
|
455 | win_x + effect_x_offset, |
|
|
456 | win_y + effect_y_offset + offset - font_height + font_ascent, |
|
|
457 | line->line, line->len); |
|
|
458 | } |
355 | } |
459 | } |
356 | |
460 | |
357 | if (opt_frame) |
461 | if (opt_frame) |
358 | { |
462 | { |
359 | XSetForeground (disp, WinGC, GetColor (def_color)); |
463 | XSetForeground (disp, WinGC, GetColor (def_color)); |
360 | XDrawRectangle (disp, root, WinGC, win_x - 2, win_y - 2, width + 4, height + 4); |
464 | XDrawRectangle (disp, root, WinGC, win_x - 0, win_y - 0, width - 1, height - 1); |
361 | } |
465 | } |
362 | } |
466 | } |
363 | |
467 | |
364 | #if HAS_REGEX |
468 | #if HAS_REGEX |
365 | void |
469 | void |
… | |
… | |
378 | if (transformre) |
482 | if (transformre) |
379 | { |
483 | { |
380 | int i; |
484 | int i; |
381 | regmatch_t matched[16]; |
485 | regmatch_t matched[16]; |
382 | |
486 | |
383 | i = regexec (&transformre, string, 16, matched, 0); |
487 | i = regexec (transformre, s, 16, matched, 0); |
384 | if (i == 0) |
488 | if (i == 0) |
385 | { /* matched */ |
489 | { /* matched */ |
|
|
490 | int match_start = matched[0].rm_so; |
|
|
491 | int match_end = matched[0].rm_eo; |
|
|
492 | int old_len = match_end - match_start; |
|
|
493 | int new_len = strlen(transform_to); |
|
|
494 | int old_whole_len = strlen(s); |
|
|
495 | printf("regexp was matched by '%s' - replace with '%s'\n", s, transform_to); |
|
|
496 | printf("match is from %d to %d\n", |
|
|
497 | match_start, match_end); |
|
|
498 | if (new_len > old_len) { |
|
|
499 | s = xrealloc(s, old_whole_len + new_len - old_len); |
|
|
500 | } |
|
|
501 | if (new_len != old_len) { |
|
|
502 | memcpy(s + match_end + new_len - old_len, |
|
|
503 | s + match_end, |
|
|
504 | old_whole_len - match_end); |
|
|
505 | s[old_whole_len + new_len - old_len] = '\0'; |
|
|
506 | } |
|
|
507 | memcpy(s + match_start, |
|
|
508 | transform_to, |
|
|
509 | new_len); |
|
|
510 | printf("transformed to '%s'\n", s); |
386 | } |
511 | } |
|
|
512 | else |
|
|
513 | { |
|
|
514 | printf("regexp was not matched by '%s'\n", s); |
|
|
515 | } |
387 | } |
516 | } |
388 | } |
517 | } |
389 | #endif |
518 | #endif |
390 | |
519 | |
391 | char * |
520 | char * |
… | |
… | |
408 | int |
537 | int |
409 | lineinput (struct logfile_entry *logfile) |
538 | lineinput (struct logfile_entry *logfile) |
410 | { |
539 | { |
411 | char buff[1024], *p = buff; |
540 | char buff[1024], *p = buff; |
412 | int ch; |
541 | int ch; |
|
|
542 | /* HACK this - to add on the length of any partial line which we will be appending to */ |
413 | int ofs = logfile->buf ? strlen (logfile->buf) : 0; |
543 | int ofs = logfile->buf ? strlen (logfile->buf) : 0; |
414 | |
544 | |
415 | do |
545 | do |
416 | { |
546 | { |
417 | ch = fgetc (logfile->fp); |
547 | ch = fgetc (logfile->fp); |
418 | |
548 | |
419 | if (ch == '\r') |
549 | if (ch == '\n' || ch == EOF) |
420 | continue; |
|
|
421 | else if (ch == EOF || ch == '\n') |
|
|
422 | break; |
550 | break; |
|
|
551 | else if (ch == '\r') |
|
|
552 | continue; /* skip */ |
423 | else if (ch == '\t') |
553 | else if (ch == '\t') |
424 | { |
554 | { |
425 | do |
555 | do |
426 | { |
556 | { |
427 | *p++ = ' '; |
557 | *p++ = ' '; |
… | |
… | |
435 | ofs++; |
565 | ofs++; |
436 | } |
566 | } |
437 | } |
567 | } |
438 | while (p < buff + (sizeof buff) - 8 - 1); |
568 | while (p < buff + (sizeof buff) - 8 - 1); |
439 | |
569 | |
440 | if (p == buff) |
570 | if (p == buff && ch == EOF) |
441 | return 0; |
571 | return 0; |
442 | |
572 | |
443 | *p = 0; |
573 | *p = 0; |
444 | |
574 | |
445 | p = concat_line (logfile->buf, buff); |
575 | p = concat_line (logfile->buf, buff); |
446 | free (logfile->buf); logfile->buf = p; |
576 | free (logfile->buf); logfile->buf = p; |
447 | |
577 | |
448 | logfile->lastpartial = logfile->partial; |
578 | logfile->lastpartial = logfile->partial; |
|
|
579 | /* there are 3 ways we could have exited the loop: reading '\n', |
|
|
580 | * reaching EOF, or filling the buffer; the 2nd and 3rd of these |
|
|
581 | * both result in a partial line */ |
449 | logfile->partial = ch == EOF; |
582 | logfile->partial = ch != '\n'; |
450 | |
583 | |
451 | if (logfile->partial && opt_whole) |
584 | if (logfile->partial && opt_whole) |
452 | return 0; |
585 | return 0; |
453 | |
586 | |
454 | #if HAS_REGEX |
587 | #if HAS_REGEX |
… | |
… | |
526 | { /* file missing? */ |
659 | { /* file missing? */ |
527 | sleep (1); |
660 | sleep (1); |
528 | if (e->fp) |
661 | if (e->fp) |
529 | fclose (e->fp); |
662 | fclose (e->fp); |
530 | if (openlog (e) == NULL) |
663 | if (openlog (e) == NULL) |
531 | break; |
664 | continue; |
532 | } |
665 | } |
533 | |
666 | |
|
|
667 | /* HACK this - stats can be uninitialised here (if the file |
|
|
668 | * didn't exist when stat() was called, but was recreated during |
|
|
669 | * the sleep(1)) */ |
534 | if (stats.st_ino != e->inode) |
670 | if (stats.st_ino != e->inode) |
535 | { /* file renamed? */ |
671 | { /* file renamed? */ |
536 | if (e->fp) |
672 | if (e->fp) |
537 | fclose (e->fp); |
673 | fclose (e->fp); |
538 | if (openlog (e) == NULL) |
674 | if (openlog (e) == NULL) |
539 | break; |
675 | continue; |
540 | } |
676 | } |
541 | |
677 | |
542 | if (stats.st_size < e->last_size) |
678 | if (stats.st_size < e->last_size) |
543 | { /* file truncated? */ |
679 | { /* file truncated? */ |
544 | fseek (e->fp, 0, SEEK_SET); |
680 | fseek (e->fp, 0, SEEK_SET); |
… | |
… | |
579 | struct logfile_entry *current; |
715 | struct logfile_entry *current; |
580 | |
716 | |
581 | for (cur_line = idx; cur_line > 0; cur_line--) |
717 | for (cur_line = idx; cur_line > 0; cur_line--) |
582 | lines[cur_line] = lines[cur_line - 1]; |
718 | lines[cur_line] = lines[cur_line - 1]; |
583 | |
719 | |
584 | lines[0].line = strdup ("~"); |
720 | lines[0].line = xstrdup ("~"); |
585 | |
721 | |
586 | for (current = loglist; current; current = current->next) |
722 | for (current = loglist; current; current = current->next) |
587 | if (current->index >= 0 && current->index <= idx) |
723 | if (current->index >= 0 && current->index <= idx) |
588 | current->index++; |
724 | current->index++; |
589 | } |
725 | } |
590 | |
726 | |
591 | /* |
727 | /* |
592 | * takes a logical log file line and split it into multiple physical |
728 | * takes a logical log file line and splits it into multiple physical |
593 | * screen lines by splitting it whenever a part becomes too long. |
729 | * screen lines by splitting it whenever a part becomes too long. |
594 | * lal lines will be inserted at position "idx". |
730 | * lal lines will be inserted at position "idx". |
595 | */ |
731 | */ |
596 | static void |
732 | static void |
597 | split_line (int idx, const char *str, unsigned long color) |
733 | split_line (int idx, const char *str, unsigned long color) |
… | |
… | |
600 | const char *p = str; |
736 | const char *p = str; |
601 | |
737 | |
602 | do |
738 | do |
603 | { |
739 | { |
604 | const char *beg = p; |
740 | const char *beg = p; |
605 | int w = 0; |
741 | int w = 0, wrapped = 0; |
|
|
742 | const char *break_p = NULL; |
606 | |
743 | |
607 | while (*p) |
744 | while (*p) |
608 | { |
745 | { |
|
|
746 | /* find the length in bytes of the next multibyte character */ |
609 | int len = mblen (p, l); |
747 | int len = mblen (p, l); |
610 | if (len <= 0) |
748 | if (len <= 0) |
611 | len = 1; /* ignore (don't skip) ilegal character sequences */ |
749 | len = 1; /* ignore (don't skip) illegal character sequences */ |
612 | |
750 | |
|
|
751 | /* find the width in pixels of the next character */ |
613 | int cw = XmbTextEscapement (fontset, p, len); |
752 | int cw = XmbTextEscapement (fontset, p, len); |
614 | if (cw + w >= width) |
753 | if (cw + w > width - effect_x_space) |
|
|
754 | { |
|
|
755 | wrapped = 1; |
615 | break; |
756 | break; |
|
|
757 | } |
|
|
758 | |
|
|
759 | if (opt_wordwrap && len == 1 && p[0] == ' ') |
|
|
760 | break_p = p; |
616 | |
761 | |
617 | w += cw; |
762 | w += cw; |
618 | p += len; |
763 | p += len; |
619 | l -= len; |
764 | l -= len; |
620 | } |
765 | } |
|
|
766 | |
|
|
767 | /* if we're wrapping at spaces, and the line is long enough to |
|
|
768 | * wrap, and we've seen a space already, and the space wasn't |
|
|
769 | * the first character on the line, then wrap at the space */ |
|
|
770 | if (opt_wordwrap && wrapped && break_p && break_p != beg) |
|
|
771 | { |
|
|
772 | l += p - break_p; |
|
|
773 | p = break_p; |
|
|
774 | } |
621 | |
775 | |
622 | { |
776 | { |
623 | char *s = xmalloc (p - beg + 1); |
777 | char *s = xmalloc (p - beg + 1); |
624 | memcpy (s, beg, p - beg); |
778 | memcpy (s, beg, p - beg); |
625 | s[p - beg] = 0; |
779 | s[p - beg] = 0; |
626 | insert_line (idx); |
780 | insert_line (idx); |
627 | lines[idx].line = s; |
781 | lines[idx].line = s; |
628 | lines[idx].len = p - beg; |
782 | lines[idx].len = p - beg; |
629 | lines[idx].color = color; |
783 | lines[idx].color = color; |
630 | } |
784 | } |
|
|
785 | |
|
|
786 | /* if we wrapped at a space, don't display the space */ |
|
|
787 | if (opt_wordwrap && wrapped && break_p && break_p != beg) |
|
|
788 | { |
|
|
789 | l--; |
|
|
790 | p++; |
|
|
791 | } |
631 | } |
792 | } |
632 | while (l); |
793 | while (l); |
633 | } |
794 | } |
634 | |
795 | |
635 | /* |
796 | /* |
… | |
… | |
652 | |
813 | |
653 | static void |
814 | static void |
654 | main_loop (void) |
815 | main_loop (void) |
655 | { |
816 | { |
656 | lines = xmalloc (sizeof (struct linematrix) * listlen); |
817 | lines = xmalloc (sizeof (struct linematrix) * listlen); |
657 | int lin, miny, maxy; |
818 | display = xmalloc (sizeof (struct displaymatrix) * listlen); |
|
|
819 | int lin; |
658 | time_t lastreload; |
820 | time_t lastreload; |
659 | Region region = XCreateRegion (); |
821 | Region region = XCreateRegion (); |
660 | XEvent xev; |
822 | XEvent xev; |
661 | struct logfile_entry *lastprinted = NULL; |
823 | struct logfile_entry *lastprinted = NULL; |
662 | struct logfile_entry *current; |
824 | struct logfile_entry *current; |
|
|
825 | int need_update = 1; |
663 | |
826 | |
664 | maxy = 0; |
|
|
665 | miny = win_y + height; |
|
|
666 | lastreload = time (NULL); |
827 | lastreload = time (NULL); |
667 | |
828 | |
668 | /* Initialize linematrix */ |
829 | /* Initialize linematrix */ |
669 | for (lin = 0; lin < listlen; lin++) |
830 | for (lin = 0; lin < listlen; lin++) |
670 | { |
831 | { |
671 | lines[lin].line = strdup ("~"); |
832 | lines[lin].line = xstrdup ("~"); |
672 | lines[lin].len = 1; |
833 | lines[lin].len = 1; |
|
|
834 | display[lin].line = xstrdup(""); |
|
|
835 | display[lin].len = 0; |
|
|
836 | display[lin].buffer_size = 1; |
673 | lines[lin].color = GetColor (def_color); |
837 | lines[lin].color = GetColor (def_color); |
674 | } |
838 | } |
675 | |
839 | |
676 | /* show the display full of empty lines ("~") in case the first |
|
|
677 | * time around the loop doesn't produce any output, as it won't if |
|
|
678 | * either (a) -noinitial is set or (b) all the files are currently |
|
|
679 | * empty */ |
|
|
680 | redraw (); |
|
|
681 | |
|
|
682 | for (;;) |
840 | for (;;) |
683 | { |
841 | { |
684 | int need_update = 0; |
|
|
685 | |
|
|
686 | /* read logs */ |
842 | /* read logs */ |
687 | for (current = loglist; current; current = current->next) |
843 | for (current = loglist; current; current = current->next) |
688 | { |
844 | { |
689 | if (!current->fp) |
845 | if (!current->fp) |
690 | continue; /* skip missing files */ |
846 | continue; /* skip missing files */ |
… | |
… | |
719 | |
875 | |
720 | /* if this is the same file we showed last, and the |
876 | /* if this is the same file we showed last, and the |
721 | * last time we showed it, it wasn't finished, then |
877 | * last time we showed it, it wasn't finished, then |
722 | * append to the last line shown */ |
878 | * append to the last line shown */ |
723 | if (lastprinted == current && current->lastpartial) |
879 | if (lastprinted == current && current->lastpartial) |
724 | { |
880 | append_line (listlen - 1, current->buf); |
|
|
881 | else if (current->lastpartial) |
|
|
882 | { |
|
|
883 | split_line (listlen - 1, continuation, current->color); |
725 | append_line (listlen - 1, current->buf); |
884 | append_line (listlen - 1, current->buf); |
726 | free (current->buf), current->buf = 0; |
885 | } |
727 | continue; |
|
|
728 | } |
|
|
729 | else |
886 | else |
730 | { |
|
|
731 | split_line (listlen - 1, current->buf, current->color); |
887 | split_line (listlen - 1, current->buf, current->color); |
|
|
888 | |
732 | free (current->buf), current->buf = 0; |
889 | free (current->buf), current->buf = 0; |
733 | } |
|
|
734 | |
|
|
735 | current->index = listlen - 1; |
890 | current->index = listlen - 1; |
736 | lastprinted = current; |
891 | lastprinted = current; |
737 | } |
892 | } |
738 | } |
893 | } |
739 | |
894 | |
740 | if (need_update) |
895 | if (need_update) |
|
|
896 | { |
741 | redraw (); |
897 | redraw (0); |
|
|
898 | need_update = 0; |
|
|
899 | } |
742 | else |
900 | else |
743 | { |
901 | { |
744 | XFlush (disp); |
902 | XFlush (disp); |
745 | |
903 | |
746 | if (!XPending (disp)) |
904 | if (!XPending (disp)) |
… | |
… | |
772 | |
930 | |
773 | r.x = xev.xexpose.x; |
931 | r.x = xev.xexpose.x; |
774 | r.y = xev.xexpose.y; |
932 | r.y = xev.xexpose.y; |
775 | r.width = xev.xexpose.width; |
933 | r.width = xev.xexpose.width; |
776 | r.height = xev.xexpose.height; |
934 | r.height = xev.xexpose.height; |
|
|
935 | |
777 | XUnionRectWithRegion (&r, region, region); |
936 | XUnionRectWithRegion (&r, region, region); |
778 | if (miny > r.y) |
|
|
779 | miny = r.y; |
|
|
780 | if (maxy < r.y + r.height) |
|
|
781 | maxy = r.y + r.height; |
|
|
782 | } |
937 | } |
783 | break; |
938 | break; |
784 | default: |
939 | default: |
785 | #ifdef DEBUGMODE |
940 | #ifdef DEBUGMODE |
786 | fprintf (stderr, "PANIC! Unknown event %d\n", xev.type); |
941 | fprintf (stderr, "PANIC! Unknown event %d\n", xev.type); |
… | |
… | |
799 | lastreload = time (NULL); |
954 | lastreload = time (NULL); |
800 | } |
955 | } |
801 | |
956 | |
802 | if (!XEmptyRegion (region)) |
957 | if (!XEmptyRegion (region)) |
803 | { |
958 | { |
|
|
959 | XRectangle r; |
|
|
960 | |
804 | XSetRegion (disp, WinGC, region); |
961 | XSetRegion (disp, WinGC, region); |
|
|
962 | XClipBox (region, &r); |
805 | |
963 | |
806 | refresh (miny, maxy); |
964 | refresh (r.y, r.y + r.height, 0, 1); |
|
|
965 | |
807 | XDestroyRegion (region); |
966 | XDestroyRegion (region); |
808 | region = XCreateRegion (); |
967 | region = XCreateRegion (); |
809 | maxy = 0; |
|
|
810 | miny = 32768; |
|
|
811 | } |
968 | } |
812 | } |
969 | } |
813 | } |
970 | } |
814 | |
971 | |
815 | |
972 | |
… | |
… | |
853 | continuation = argv[++i]; |
1010 | continuation = argv[++i]; |
854 | else if (!strcmp (arg, "-font") || !strcmp (arg, "-fn")) |
1011 | else if (!strcmp (arg, "-font") || !strcmp (arg, "-fn")) |
855 | fontname = argv[++i]; |
1012 | fontname = argv[++i]; |
856 | #if HAS_REGEX |
1013 | #if HAS_REGEX |
857 | else if (!strcmp (arg, "-t")) |
1014 | else if (!strcmp (arg, "-t")) |
|
|
1015 | { |
858 | transform = argv[++i]; |
1016 | transform = argv[++i]; |
|
|
1017 | transform_to = argv[++i]; |
|
|
1018 | printf("transform: '%s' to '%s'\n", transform, transform_to); |
|
|
1019 | } |
859 | #endif |
1020 | #endif |
860 | else if (!strcmp (arg, "-fork") || !strcmp (arg, "-f")) |
1021 | else if (!strcmp (arg, "-fork") || !strcmp (arg, "-f")) |
861 | opt_daemonize = 1; |
1022 | opt_daemonize = 1; |
862 | else if (!strcmp (arg, "-reload")) |
1023 | else if (!strcmp (arg, "-reload")) |
863 | { |
1024 | { |
864 | reload = atoi (argv[++i]); |
1025 | reload = atoi (argv[++i]); |
865 | command = argv[++i]; |
1026 | command = argv[++i]; |
866 | } |
1027 | } |
867 | else if (!strcmp (arg, "-shade")) |
1028 | else if (!strcmp (arg, "-shade")) |
868 | opt_shade = 1; |
1029 | opt_shade = 1; |
|
|
1030 | else if (!strcmp (arg, "-outline")) |
|
|
1031 | opt_outline = 1; |
|
|
1032 | else if (!strcmp (arg, "-noflicker")) |
|
|
1033 | opt_noflicker = 1; |
869 | else if (!strcmp (arg, "-frame")) |
1034 | else if (!strcmp (arg, "-frame")) |
870 | opt_frame = 1; |
1035 | opt_frame = 1; |
871 | else if (!strcmp (arg, "-no-filename")) |
1036 | else if (!strcmp (arg, "-no-filename")) |
872 | opt_nofilename = 1; |
1037 | opt_nofilename = 1; |
873 | else if (!strcmp (arg, "-reverse")) |
1038 | else if (!strcmp (arg, "-reverse")) |
… | |
… | |
876 | opt_whole = 1; |
1041 | opt_whole = 1; |
877 | else if (!strcmp (arg, "-partial")) |
1042 | else if (!strcmp (arg, "-partial")) |
878 | opt_partial = 1; |
1043 | opt_partial = 1; |
879 | else if (!strcmp (arg, "-update")) |
1044 | else if (!strcmp (arg, "-update")) |
880 | opt_update = opt_partial = 1; |
1045 | opt_update = opt_partial = 1; |
|
|
1046 | else if (!strcmp (arg, "-wordwrap")) |
|
|
1047 | opt_wordwrap = 1; |
881 | else if (!strcmp (arg, "-color")) |
1048 | else if (!strcmp (arg, "-color")) |
882 | def_color = argv[++i]; |
1049 | def_color = argv[++i]; |
883 | else if (!strcmp (arg, "-noinitial")) |
1050 | else if (!strcmp (arg, "-noinitial")) |
884 | opt_noinitial = 1; |
1051 | opt_noinitial = 1; |
885 | else if (!strcmp (arg, "-id")) |
1052 | else if (!strcmp (arg, "-id")) |
… | |
… | |
942 | e->fname = xstrdup (fname); |
1109 | e->fname = xstrdup (fname); |
943 | if (openlog (e) == NULL) |
1110 | if (openlog (e) == NULL) |
944 | perror (fname), exit (1); |
1111 | perror (fname), exit (1); |
945 | |
1112 | |
946 | l = strlen (desc); |
1113 | l = strlen (desc); |
|
|
1114 | /* HACK on this - width is in pixels now */ |
947 | if (l > width - 2) /* must account for [ ] */ |
1115 | if (l > width - 2) /* must account for [ ] */ |
948 | l = width - 2; |
1116 | l = width - 2; |
949 | e->desc = xmalloc (l + 1); |
1117 | e->desc = xmalloc (l + 1); |
950 | memcpy (e->desc, desc, l); |
1118 | memcpy (e->desc, desc, l); |
951 | *(e->desc + l) = '\0'; |
1119 | *(e->desc + l) = '\0'; |
… | |
… | |
974 | { |
1142 | { |
975 | fprintf (stderr, "Specify at most one of -partial and -whole\n"); |
1143 | fprintf (stderr, "Specify at most one of -partial and -whole\n"); |
976 | exit (1); |
1144 | exit (1); |
977 | } |
1145 | } |
978 | |
1146 | |
|
|
1147 | /* HACK this - do we want to allow both -shade and -outline? */ |
|
|
1148 | if (opt_shade && opt_outline) |
|
|
1149 | { |
|
|
1150 | fprintf (stderr, "Specify at most one of -shade and -outline\n"); |
|
|
1151 | exit (1); |
|
|
1152 | } |
|
|
1153 | |
979 | if (opt_partial) |
1154 | if (opt_partial) |
980 | /* if we specifically requested to see partial lines then don't insist on whole lines */ |
1155 | /* if we specifically requested to see partial lines then don't insist on whole lines */ |
981 | opt_whole = 0; |
1156 | opt_whole = 0; |
982 | else if (file_count > 1) |
1157 | else if (file_count > 1) |
983 | /* otherwise, if we've viewing multiple files, default to showing whole lines */ |
1158 | /* otherwise, if we've viewing multiple files, default to showing whole lines */ |
… | |
… | |
986 | #if HAS_REGEX |
1161 | #if HAS_REGEX |
987 | if (transform) |
1162 | if (transform) |
988 | { |
1163 | { |
989 | int i; |
1164 | int i; |
990 | |
1165 | |
|
|
1166 | printf("compiling regexp '%s'\n", transform); |
991 | transformre = xmalloc (sizeof (transformre)); |
1167 | transformre = xmalloc (sizeof (regex_t)); |
992 | i = regcomp (&transformre, transform, REG_EXTENDED); |
1168 | i = regcomp (transformre, transform, REG_EXTENDED); |
993 | if (i != 0) |
1169 | if (i != 0) |
994 | { |
1170 | { |
995 | char buf[512]; |
1171 | char buf[512]; |
996 | |
1172 | |
997 | regerror (i, &transformre, buf, sizeof (buf)); |
1173 | regerror (i, transformre, buf, sizeof (buf)); |
998 | fprintf (stderr, "Cannot compile regular expression: %s\n", buf); |
1174 | fprintf (stderr, "Cannot compile regular expression: %s\n", buf); |
999 | } |
1175 | } |
|
|
1176 | else |
|
|
1177 | { |
|
|
1178 | printf("compiled '%s' OK to %x\n", transform, (int)transformre); |
|
|
1179 | } |
1000 | } |
1180 | } |
1001 | #endif |
1181 | #endif |
1002 | |
1182 | |
1003 | InitWindow (); |
1183 | InitWindow (); |
1004 | |
1184 | |
… | |
… | |
1048 | xmalloc (size_t size) |
1228 | xmalloc (size_t size) |
1049 | { |
1229 | { |
1050 | void *p; |
1230 | void *p; |
1051 | |
1231 | |
1052 | while ((p = malloc (size)) == NULL) |
1232 | while ((p = malloc (size)) == NULL) |
|
|
1233 | { |
|
|
1234 | fprintf (stderr, "Memory exausted."); |
|
|
1235 | sleep (10); |
|
|
1236 | } |
|
|
1237 | |
|
|
1238 | return p; |
|
|
1239 | } |
|
|
1240 | |
|
|
1241 | void * |
|
|
1242 | xrealloc (void *ptr, size_t size) |
|
|
1243 | { |
|
|
1244 | void *p; |
|
|
1245 | |
|
|
1246 | while ((p = realloc (ptr, size)) == NULL) |
1053 | { |
1247 | { |
1054 | fprintf (stderr, "Memory exausted."); |
1248 | fprintf (stderr, "Memory exausted."); |
1055 | sleep (10); |
1249 | sleep (10); |
1056 | } |
1250 | } |
1057 | |
1251 | |
… | |
… | |
1072 | " -reverse print new lines at the top\n" |
1266 | " -reverse print new lines at the top\n" |
1073 | " -whole wait for \\n before showing a line\n" |
1267 | " -whole wait for \\n before showing a line\n" |
1074 | " -partial show lines even if they don't end with a \\n\n" |
1268 | " -partial show lines even if they don't end with a \\n\n" |
1075 | " -update allow updates to old partial lines\n" |
1269 | " -update allow updates to old partial lines\n" |
1076 | " -cont string to prefix continued partial lines with\n" |
1270 | " -cont string to prefix continued partial lines with\n" |
|
|
1271 | " -wordwrap wrap long lines at spaces to avoid breaking words\n" |
1077 | " defaults to \"[+]\"\n" |
1272 | " defaults to \"[+]\"\n" |
1078 | " -shade add shading to font\n" |
1273 | " -shade add shading to font\n" |
1079 | " -noinitial don't display the last file lines on\n" |
1274 | " -noinitial don't display the last file lines on\n" |
1080 | " startup\n" |
1275 | " startup\n" |
1081 | " -i | -interval seconds interval between checks (fractional\n" |
1276 | " -i | -interval seconds interval between checks (fractional\n" |
1082 | " values o.k.). Default 3 seconds\n" |
1277 | " values o.k.). Default 2.4 seconds\n" |
1083 | " -V display version information and exit\n" |
1278 | " -V display version information and exit\n" |
1084 | "\n"); |
1279 | "\n"); |
1085 | printf ("Example:\n%s -g 80x25+100+50 -font fixed /var/log/messages,green " |
1280 | printf ("Example:\n%s -g 80x25+100+50 -font fixed /var/log/messages,green " |
1086 | "/var/log/secure,red,'ALERT'\n", myname); |
1281 | "/var/log/secure,red,'ALERT'\n", myname); |
1087 | exit (0); |
1282 | exit (0); |
… | |
… | |
1095 | } |
1290 | } |
1096 | |
1291 | |
1097 | int |
1292 | int |
1098 | daemonize (void) |
1293 | daemonize (void) |
1099 | { |
1294 | { |
|
|
1295 | pid_t pid; |
|
|
1296 | |
1100 | switch (fork ()) |
1297 | switch (pid = fork ()) |
1101 | { |
1298 | { |
1102 | case -1: |
1299 | case -1: |
1103 | return -1; |
1300 | return -1; |
1104 | case 0: |
1301 | case 0: |
1105 | break; |
1302 | break; |
1106 | default: |
1303 | default: |
|
|
1304 | printf("%d\n", pid); |
1107 | _exit (0); |
1305 | exit (0); |
1108 | } |
1306 | } |
1109 | |
1307 | |
1110 | if (setsid () == -1) |
1308 | if (setsid () == -1) |
1111 | return -1; |
1309 | return -1; |
1112 | |
1310 | |