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.63 by chris_moore, Thu Apr 15 00:38:13 2004 UTC

1/* 1/*
2 * Copyright 2001 by Marco d'Itri <md@linux.it> 2 * Copyright 2001 by Marco d'Itri <md@linux.it>
3 * Copyright 2000,2001,2002,2003,2004
4 * Marc Lehmann <pcg@goof.com>,
5 * Chris Moore <chris.moore@mail.com>,
6 * and many others, see README
3 * 7 *
4 * Original version by Mike Baker, then maintained by pcg@goof.com. 8 * Original version by Mike Baker.
5 * 9 *
6 * This program is free software; you can redistribute it and/or modify 10 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by 11 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or 12 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version. 13 * (at your option) any later version.
17 * along with this program; if not, write to the Free Software 21 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
19 */ 23 */
20 24
21#include "config.h" 25#include "config.h"
26#include <assert.h>
22#include <stdlib.h> 27#include <stdlib.h>
23#include <stdio.h> 28#include <stdio.h>
24#include <unistd.h> 29#include <unistd.h>
25#include <string.h> 30#include <string.h>
26#include <signal.h> 31#include <signal.h>
31#include <sys/stat.h> 36#include <sys/stat.h>
32#include <sys/types.h> 37#include <sys/types.h>
33#include <locale.h> 38#include <locale.h>
34#include <ctype.h> 39#include <ctype.h>
35#include <stdarg.h> 40#include <stdarg.h>
41#include <X11/Xlib.h>
42#include <X11/Xatom.h>
43#include <X11/Xutil.h>
44
36#if HAS_REGEX 45#if HAS_REGEX
37#include <regex.h> 46#include <regex.h>
38#endif 47#endif
39#include <X11/Xlib.h> 48
40#include <X11/Xatom.h> 49#define SHADE_X 2
41#include <X11/Xutil.h> 50#define SHADE_Y 2
51
52/* some italic fonts still go over the margin - this margin of error cleans up the mess */
53#define MARGIN_OF_ERROR 2
42 54
43/* data structures */ 55/* data structures */
44struct logfile_entry 56struct logfile_entry
45{ 57{
46 struct logfile_entry *next; 58 struct logfile_entry *next;
47 59
48 char *fname; /* name of file */ 60 char *fname; /* name of file */
49 char *desc; /* alternative description */ 61 char *desc; /* alternative description */
50 char *buf; /* text read but not yet displayed */ 62 char *buf; /* text read but not yet displayed */
63 const char *fontname;
64 XFontSet fontset;
65 int font_height;
66 int font_ascent;
51 FILE *fp; /* FILE struct associated with file */ 67 FILE *fp; /* FILE struct associated with file */
52 ino_t inode; /* inode of the file opened */ 68 ino_t inode; /* inode of the file opened */
53 off_t last_size; /* file size at the last check */ 69 off_t last_size; /* file size at the last check */
54 unsigned long color; /* color to be used for printing */ 70 unsigned long color; /* color to be used for printing */
71 const char *colorname; /* color name/string */
55 int partial; /* true if the last line isn't complete */ 72 int partial; /* true if the last line isn't complete */
56 int lastpartial; /* true if the previous output wasn't complete */ 73 int lastpartial; /* true if the previous output wasn't complete */
57 int index; /* index into linematrix of a partial line */ 74 struct line_node *last; /* last line we output */
75 int modified; /* true if line is modified & needs displaying */
58}; 76};
59 77
78struct line_node
79{
80 struct line_node *next;
81 struct line_node *prev;
82 struct logfile_entry *logfile;
83
84 char *line; /* the text of the line (so far) */
85 int len; /* the length of the line (in bytes) so far */
86 int wrapped_left; /* true if wrapped from the previous line */
87 int wrapped_right; /* true if wrapped to the next line */
88 struct breakinfo *breaks; /* array of indicies to spaces if wrapped_right */
89 int num_words; /* the number of words in the line */
90 int free_pixels; /* the number of free pixels to spread out */
91};
92
93struct breakinfo
94{
95 int index; /* index into string of start of substring */
96 int width; /* width in pixels of start of substring */
97 int len; /* length of substring */
98};
99
60struct linematrix 100struct displaymatrix
61{ 101{
62 char *line; 102 char *line;
103 int len;
104 int offset;
105 int buffer_size;
63 unsigned long color; 106 unsigned long color;
64}; 107};
65 108
66/* global variables */ 109/* global variables */
110struct line_node *linelist = NULL, *linelist_tail = NULL;
111struct displaymatrix *display;
112int continuation_width = -1;
113int continuation_color;
114int continuation_length;
115
116/* HACK - ideally listlen will start at however many '~'s will fit on
117 * the screen */
67int width = STD_WIDTH, height = STD_HEIGHT, listlen; 118int width = STD_WIDTH, height = STD_HEIGHT, listlen = 50;
68int win_x = LOC_X, win_y = LOC_Y; 119int win_x = LOC_X, win_y = LOC_Y;
69int font_descent, font_height; 120int effect_x_space, effect_y_space; /* how much space does shading / outlining take up */
121int effect_x_offset, effect_y_offset; /* and how does it offset the usable space */
70int do_reopen; 122int do_reopen;
71struct timeval interval = { 3, 0 }; 123struct timeval interval = { 2, 400000 };
72XFontSet fontset;
73 124
74/* command line options */ 125/* command line options */
75int opt_noinitial, opt_shade, opt_frame, opt_reverse, opt_nofilename, 126int opt_noinitial, opt_shade, opt_frame, opt_reverse, opt_nofilename,
76 opt_whole, opt_update, geom_mask, reload = 0; 127 opt_outline, opt_noflicker, opt_whole, opt_update, opt_wordwrap,
128 opt_justify, geom_mask, opt_minspace, reload;
77const char *command = NULL, 129const char *command = NULL,
78 *fontname = USE_FONT, *dispname = NULL, *def_color = DEF_COLOR, 130 *fontname = USE_FONT, *dispname = NULL, *def_color = DEF_COLOR,
79 *continuation = "[+]"; 131 *continuation = "|| ", *cont_color = DEF_CONT_COLOR;
80 132
81struct logfile_entry *loglist = NULL, *loglist_tail = NULL; 133struct logfile_entry *loglist = NULL, *loglist_tail = NULL;
82 134
83Display *disp; 135Display *disp;
84Window root; 136Window root;
90 regex_t from; 142 regex_t from;
91 const char *to; 143 const char *to;
92 struct re_list *next; 144 struct re_list *next;
93}; 145};
94struct re_list *re_head, *re_tail; 146struct re_list *re_head, *re_tail;
147char *transform_to = NULL;
148regex_t *transformre;
95#endif 149#endif
96 150
97 151
98/* prototypes */ 152/* prototypes */
99void list_files (int); 153void list_files (int);
100void force_reopen (int); 154void force_reopen (int);
101void force_refresh (int); 155void force_refresh (int);
102void blank_window (int); 156void blank_window (int);
157#ifdef USE_TOON_GET_ROOT_WINDOW
158Window ToonGetRootWindow(Display *, int, Window *);
159#endif /* USE_TOON_GET_ROOT_WINDOW */
103 160
104void InitWindow (void); 161void InitWindow (void);
105unsigned long GetColor (const char *); 162unsigned long GetColor (const char *);
106void redraw (void); 163void redraw (int);
107void refresh (struct linematrix *, int, int); 164void refresh (int, int, int, int);
108 165
109void transform_line (char *s); 166void transform_line (char *s);
110int lineinput (struct logfile_entry *); 167int lineinput (struct logfile_entry *);
111void reopen (void); 168void reopen (void);
112void check_open_files (void); 169void check_open_files (void);
113FILE *openlog (struct logfile_entry *); 170FILE *openlog (struct logfile_entry *);
114void main_loop (void); 171static void main_loop (void);
115 172
116void display_version (void); 173void display_version (void);
117void display_help (char *); 174void display_help (char *);
118void install_signal (int, void (*)(int)); 175void install_signal (int, void (*)(int));
119void *xstrdup (const char *); 176void *xstrdup (const char *);
120void *xmalloc (size_t); 177void *xmalloc (size_t);
178void *xrealloc (void *, size_t);
121int daemonize (void); 179int daemonize (void);
122 180
123/* signal handlers */ 181/* signal handlers */
124void 182void
125list_files (int dummy) 183list_files (int dummy)
138} 196}
139 197
140void 198void
141force_refresh (int dummy) 199force_refresh (int dummy)
142{ 200{
143 XClearArea (disp, root, win_x, win_y, width, height, False);
144 redraw (); 201 redraw (1);
145} 202}
146 203
147void 204void
148blank_window (int dummy) 205blank_window (int dummy)
149{ 206{
150 XClearArea (disp, root, win_x, win_y, width, height, False); 207 XClearArea (disp, root, win_x, win_y, width + MARGIN_OF_ERROR, height, False);
151 XFlush (disp); 208 XFlush (disp);
152 exit (0); 209 exit (0);
153} 210}
154 211
155/* X related functions */ 212/* X related functions */
168 fprintf (stderr, "can't allocate %s\n", ColorName); 225 fprintf (stderr, "can't allocate %s\n", ColorName);
169 226
170 return Color.pixel; 227 return Color.pixel;
171} 228}
172 229
230#ifndef USE_TOON_GET_ROOT_WINDOW
173static Window 231static void
174root_window (Display * display, int screen_number) 232find_root_window (Display *display, int screen_number)
175{ 233{
234 if (!root)
235 {
176 Atom SWM_VROOT = XInternAtom (display, "__SWM_VROOT", False); 236 Atom SWM_VROOT = XInternAtom (display, "__SWM_VROOT", False);
237 Atom NAUTILUS_DESKTOP_WINDOW_ID = XInternAtom (display, "NAUTILUS_DESKTOP_WINDOW_ID", False);
177 Window real_root_window = RootWindow (display, screen_number); 238 root = RootWindow (display, screen_number);
178 239
179 if (root) /* root window set via option */
180 return root;
181
182 if (SWM_VROOT != None)
183 {
184 Window unused, *windows; 240 Window unused, *windows = 0;
185 unsigned int count; 241 unsigned int count;
186 242
243 Atom type;
244 int format;
245 unsigned long nitems, bytes_after_return;
246 unsigned char *virtual_root_window;
247
248 if (XGetWindowProperty (display, root, NAUTILUS_DESKTOP_WINDOW_ID,
249 0, 1, False, XA_WINDOW, &type, &format,
250 &nitems, &bytes_after_return,
251 &virtual_root_window) == Success
252 && type == XA_WINDOW)
253 {
187 if (XQueryTree (display, real_root_window, &unused, &unused, &windows, 254 if (XQueryTree (display, *(Window *)virtual_root_window, &unused, &unused, &windows, &count))
188 &count)) 255 root = windows[count - 1];
256
257 XFree (virtual_root_window);
258 }
259 else if (XQueryTree (display, root, &unused, &unused, &windows, &count))
189 { 260 {
190 int i; 261 int i;
191 262
192 for (i = 0; i < count; i++) 263 for (i = 0; i < count; i++)
193 { 264 {
194 Atom type;
195 int format;
196 unsigned long nitems, bytes_after_return;
197 unsigned char *virtual_root_window;
198
199 if (XGetWindowProperty (display, windows[i], SWM_VROOT, 265 if (XGetWindowProperty (display, windows[i], SWM_VROOT,
200 0, 1, False, XA_WINDOW, &type, &format, 266 0, 1, False, XA_WINDOW, &type, &format,
201 &nitems, &bytes_after_return, 267 &nitems, &bytes_after_return,
202 &virtual_root_window) == Success) 268 &virtual_root_window) == Success
269 && type == XA_WINDOW)
203 { 270 {
204 if (type != None)
205 {
206 if (type == XA_WINDOW)
207 {
208 XFree (windows);
209 return (Window) virtual_root_window; 271 root = *(Window *)virtual_root_window;
210 } 272 XFree (virtual_root_window);
211 else
212 fprintf (stderr,
213 "__SWM_VROOT property type mismatch");
214 } 273 break;
215 } 274 }
216 else 275 }
217 fprintf (stderr,
218 "failed to get __SWM_VROOT property on window 0x%lx",
219 windows[i]);
220 }
221 276
222 if (count)
223 XFree (windows); 277 XFree (windows);
224 } 278 }
225 else 279 else
226 fprintf (stderr, "Can't query tree on root window 0x%lx", 280 fprintf (stderr, "Can't query tree on root window 0x%lx", root);
227 real_root_window);
228 } 281 }
229 else
230 /* This shouldn't happen. The Xlib documentation is wrong BTW. */
231 fprintf (stderr, "Can't intern atom __SWM_VROOT");
232
233 return real_root_window;
234} 282}
283#endif /* USE_TOON_GET_ROOT_WINDOW */
235 284
236void 285void
237InitWindow (void) 286InitWindow (void)
238{ 287{
239 XGCValues gcv; 288 XGCValues gcv;
240 unsigned long gcm; 289 unsigned long gcm;
241 int screen, ScreenWidth, ScreenHeight; 290 int screen, ScreenWidth, ScreenHeight;
291 struct logfile_entry *e;
242 292
243 if (!(disp = XOpenDisplay (dispname))) 293 if (!(disp = XOpenDisplay (dispname)))
244 { 294 {
245 fprintf (stderr, "Can't open display %s.\n", dispname); 295 fprintf (stderr, "Can't open display %s.\n", dispname);
246 exit (1); 296 exit (1);
248 298
249 screen = DefaultScreen (disp); 299 screen = DefaultScreen (disp);
250 ScreenHeight = DisplayHeight (disp, screen); 300 ScreenHeight = DisplayHeight (disp, screen);
251 ScreenWidth = DisplayWidth (disp, screen); 301 ScreenWidth = DisplayWidth (disp, screen);
252 302
253 root = root_window (disp, screen); 303 find_root_window (disp, screen);
254 304
255 gcm = GCBackground; 305 gcm = GCBackground;
256 gcv.graphics_exposures = True; 306 gcv.graphics_exposures = True;
257 WinGC = XCreateGC (disp, root, gcm, &gcv); 307 WinGC = XCreateGC (disp, root, gcm, &gcv);
258 XMapWindow (disp, root); 308 XMapWindow (disp, root);
309
259 XSetForeground (disp, WinGC, GetColor (DEF_COLOR)); 310 XSetForeground (disp, WinGC, GetColor (DEF_COLOR));
260 311
312 for (e = loglist; e; e = e->next)
261 { 313 {
262 char **missing_charset_list; 314 char **missing_charset_list;
263 int missing_charset_count; 315 int missing_charset_count;
264 char *def_string; 316 char *def_string;
265 317
266 fontset = XCreateFontSet (disp, fontname, 318 e->fontset = XCreateFontSet (disp, e->fontname,
267 &missing_charset_list, &missing_charset_count, 319 &missing_charset_list, &missing_charset_count,
268 &def_string); 320 &def_string);
269 321
270 if (missing_charset_count) 322 if (missing_charset_count)
323 {
324 fprintf (stderr,
325 "Missing charsets in String to FontSet conversion (%s)\n",
326 missing_charset_list[0]);
327 XFreeStringList (missing_charset_list);
328 }
329
330 if (!e->fontset)
331 {
332 fprintf (stderr, "unable to create fontset for font '%s', exiting.\n", e->fontname);
333 exit (1);
334 }
335
271 { 336 {
272 fprintf (stderr, 337 XFontSetExtents *xfe = XExtentsOfFontSet (e->fontset);
273 "Missing charsets in String to FontSet conversion (%s)\n", 338
274 missing_charset_list[0]); 339 e->font_height = xfe->max_logical_extent.height;
275 XFreeStringList (missing_charset_list); 340 e->font_ascent = -xfe->max_logical_extent.y;
276 } 341 }
277 }
278 342
279 if (!fontset) 343 if (e->font_height > height - effect_y_space)
344 {
345 fprintf(stderr, "\n the display isn't tall enough to display a single line in font '%s'\n",
346 e->fontname);
347 fprintf(stderr, "\n the geometry in use is %d pixels tall\n", height);
348 fprintf(stderr, "\n font '%s' is %d pixels tall\n", e->fontname, e->font_height);
349 if (effect_y_space)
350 fprintf(stderr, "\n the shade or outline options need an extra %d pixel%s of vertical space\n",
351 effect_y_space, effect_y_space == 1 ? "" : "s");
352 fprintf(stderr, "\n");
353 exit(1);
354 }
280 { 355 }
281 fprintf (stderr, "unable to create fontset, exiting.\n");
282 exit (1);
283 }
284
285 {
286 XFontSetExtents *xfe = XExtentsOfFontSet (fontset);
287
288 font_height = xfe->max_logical_extent.height;
289 font_descent = xfe->max_logical_extent.y;
290 }
291 356
292 if (geom_mask & XNegative) 357 if (geom_mask & XNegative)
293 win_x = win_x + ScreenWidth - width; 358 win_x = win_x + ScreenWidth - width;
294 if (geom_mask & YNegative) 359 if (geom_mask & YNegative)
295 win_y = win_y + ScreenHeight - height; 360 win_y = win_y + ScreenHeight - height;
296 361
297 listlen = height / font_height;
298
299 if (!listlen)
300 { 362 {
301 fprintf (stderr, "height too small for a single line, setting to %d\n", 363 struct logfile_entry *e;
302 font_height); 364
303 listlen = 1; 365 for (e = loglist; e; e = e->next)
366 e->color = GetColor (e->colorname);
304 } 367 }
305
306 height = listlen * font_height;
307 368
308 XSelectInput (disp, root, ExposureMask | FocusChangeMask); 369 XSelectInput (disp, root, ExposureMask | FocusChangeMask);
309} 370}
310 371
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/* 372/*
334 * redraw does a complete redraw, rather than an update (i.e. the area 373 * if redraw () is passwd a non-zero argument, it does a complete
335 * gets cleared first) 374 * redraw, rather than an update. if the argument is zero (and
375 * -noflicker is in effect) then only the lines which have changed
376 * since the last draw are redrawn.
377 *
336 * the rest is handled by regular refresh()'es 378 * the rest is handled by regular refresh ()'es
337 */ 379 */
338void 380void
339redraw (void) 381redraw (int redraw_all)
340{ 382{
341 XClearArea (disp, root, win_x, win_y, width, height, True); 383 XSetClipMask (disp, WinGC, None);
384 refresh (0, 32768, 1, redraw_all);
385}
386
387void draw_text (Display *disp, Window root, GC WinGC, int x, int y, struct line_node *line, int foreground)
388{
389 if (line->wrapped_right && opt_justify && line->breaks)
390 {
391 int i;
392 for (i = 0; i < line->num_words; i++)
393 XmbDrawString (disp, root, line->logfile->fontset, WinGC,
394 x + line->breaks[i].width + ((i * line->free_pixels) / (line->num_words - 1))
395 + continuation_width * line->wrapped_left, y,
396 line->line + line->breaks[i].index,
397 line->breaks[i].len);
398
399 if (line->wrapped_left)
400 {
401 if (foreground) XSetForeground (disp, WinGC, continuation_color);
402 XmbDrawString (disp, root, line->logfile->fontset, WinGC, x, y, continuation, continuation_length);
403 }
404 }
405 else
406 {
407 XmbDrawString (disp, root, line->logfile->fontset, WinGC, x + continuation_width * line->wrapped_left,
408 y, line->line, line->len);
409
410 if (line->wrapped_left)
411 {
412 if (foreground) XSetForeground (disp, WinGC, continuation_color);
413 XmbDrawString (disp, root, line->logfile->fontset, WinGC, x, y, continuation, continuation_length);
414 }
415 }
342} 416}
343 417
344/* Just redraw everything without clearing (i.e. after an EXPOSE event) */ 418/* Just redraw everything without clearing (i.e. after an EXPOSE event) */
345void 419void
346refresh (struct linematrix *lines, int miny, int maxy) 420refresh (int miny, int maxy, int clear, int refresh_all)
347{ 421{
348 int lin; 422 int lin = 0;
349 int offset = (listlen + 1) * font_height; 423 int space = height;
424 int offset;
350 unsigned long black_color = GetColor ("black"); 425 unsigned long black_color = GetColor ("black");
426 struct line_node *line;
427 int step_per_line;
428 int foreground = 0;
351 429
352 miny -= win_y + font_height; 430 if (opt_reverse)
353 maxy -= win_y - font_height; 431 offset = effect_y_offset;
432 else
433 offset = height + effect_y_offset;
354 434
355 for (lin = listlen; lin--;) 435 miny -= win_y;
356 { 436 maxy -= win_y;
357 struct linematrix *line =
358 lines + (opt_reverse ? listlen - lin - 1 : lin);
359 437
360 offset -= font_height; 438 if (clear && !opt_noflicker)
439 XClearArea (disp, root, win_x, win_y, width + MARGIN_OF_ERROR, height, False);
361 440
362 if (offset < miny || offset > maxy) 441 for (line = linelist; line; line = line->next, lin++)
363 continue; 442 {
443 struct displaymatrix *display_line;
364 444
365 if (opt_shade) 445 if (opt_noflicker && lin >= listlen)
446 {
447 int i = listlen;
448 listlen *= 1.5;
449 display = xrealloc(display, listlen * sizeof(struct displaymatrix));
450 for (; i < listlen; i++)
451 {
452 display[i].line = xstrdup ("");
453 display[i].len = 0;
454 display[i].offset = 0;
455 display[i].buffer_size = 0;
456 }
366 { 457 }
458
459 display_line = display + lin;
460
461 step_per_line = line->logfile->font_height + effect_y_space;
462 if (step_per_line > space)
463 break;
464
465 if (!opt_reverse)
466 offset -= step_per_line;
467
468 offset += line->logfile->font_ascent;
469
470 miny -= line->logfile->font_height;
471 maxy += line->logfile->font_height;
472
473 if (offset >= miny && offset <= maxy)
474 {
475 /* if this line is a different than it was, then it
476 * needs displaying */
477 if (!opt_noflicker
478 || refresh_all
479 || display_line->len != line->len
480 || display_line->color != line->logfile->color
481 || display_line->offset != offset
482 || memcmp (display_line->line, line->line, line->len))
483 {
484 /* don't bother updating the record of what has been
485 * displayed if -noflicker isn't in effect, since we redraw
486 * the whole display every time anyway */
487 if (opt_noflicker)
488 {
489 /* update the record of what has been displayed;
490 * first make sure the buffer is big enough */
491 if (display_line->buffer_size < line->len)
492 {
493 display_line->buffer_size = line->len;
494 display_line->line = xrealloc (display_line->line, display_line->buffer_size);
495 }
496
497 display_line->len = line->len;
498 display_line->color = line->logfile->color;
499 display_line->offset = offset;
500 memcpy (display_line->line, line->line, line->len);
501
502 if (clear)
503 {
504#ifdef DEBUG
505 static int toggle;
506 toggle = 1 - toggle;
507 XSetForeground (disp, WinGC, toggle ? GetColor ("cyan") : GetColor ("yellow"));
508 XFillRectangle (disp, root, WinGC, win_x, win_y + offset - line->logfile->font_ascent,
509 width, step_per_line);
510#else /* DEBUG */
511 XClearArea (disp, root, win_x, win_y + offset - line->logfile->font_ascent,
512 width + MARGIN_OF_ERROR, step_per_line, False);
513#endif /* DEBUG */
514 }
515 }
516
517 if (opt_outline)
518 {
519 int x, y;
367 XSetForeground (disp, WinGC, black_color); 520 XSetForeground (disp, WinGC, black_color);
368 XmbDrawString (disp, root, fontset, WinGC, win_x + 2,
369 win_y + offset + 2, line->line, strlen (line->line));
370 }
371 521
522 for (x = -1; x <= 1; x += 2)
523 for (y = -1; y <= 1; y += 2)
524 draw_text (disp, root, WinGC,
525 win_x + effect_x_offset + x,
526 win_y + y + offset, line, foreground = 0);
527 }
528 else if (opt_shade)
529 {
530 XSetForeground (disp, WinGC, black_color);
531 draw_text (disp, root, WinGC,
532 win_x + effect_x_offset + SHADE_X,
533 win_y + offset + SHADE_Y, line, foreground = 0);
534 }
535
372 XSetForeground (disp, WinGC, line->color); 536 XSetForeground (disp, WinGC, line->logfile->color);
373 XmbDrawString (disp, root, fontset, WinGC, win_x, win_y + offset, 537 draw_text (disp, root, WinGC,
374 line->line, strlen (line->line)); 538 win_x + effect_x_offset,
539 win_y + offset, line, foreground = 1);
540 }
541 }
542
543 if (opt_reverse)
544 offset += step_per_line;
545 offset -= line->logfile->font_ascent;
546
547 miny += line->logfile->font_height;
548 maxy -= line->logfile->font_height;
549
550 space -= step_per_line;
551 }
552
553 if (space > 0 && clear)
554 {
555#ifdef DEBUG
556 XSetForeground (disp, WinGC, GetColor ("orange"));
557 XFillRectangle (disp, root, WinGC, win_x, win_y + offset - (opt_reverse ? 0 : space),
558 width, space);
559#else /* DEBUG */
560 XClearArea (disp, root, win_x, win_y + offset - (opt_reverse ? 0 : space),
561 width + MARGIN_OF_ERROR, space, False);
562#endif
563 }
564
565 /* at least one of the lines must fit in the allocated area. we've
566 * already checked at initialisation time that all the fonts are small
567 * enough to fit at least one line in the display area, but assert it
568 * again here to be sure */
569 assert(line != linelist);
570
571 /* any lines that didn't just get looked at are never going to be, so break the chain */
572 if (line) line->prev->next = 0;
573
574 /* and throw them all away */
575 while (line)
576 {
577 struct line_node *this = line;
578 line = line->next;
579 if (this->logfile && this->logfile->last == this)
580 this->logfile->last = NULL;
581 free (this->line);
582 free (this->breaks);
583 free (this);
375 } 584 }
376 585
377 if (opt_frame) 586 if (opt_frame)
587 {
588 XSetForeground (disp, WinGC, GetColor (def_color));
589 /* note that XDrawRectangle() draws a rectangle one pixel bigger
590 * in both dimensions than you ask for, hence the subtractions.
591 * XFillRectangle() doesn't suffer from this problem */
378 XDrawRectangle (disp, root, WinGC, win_x, win_y, width, height); 592 XDrawRectangle (disp, root, WinGC, win_x - 0, win_y - 0, width - 1, height - 1);
593 }
379} 594}
380 595
381#if HAS_REGEX 596#if HAS_REGEX
382void 597void
383transform_line (char *s) 598transform_line (char *s)
395 if (transformre) 610 if (transformre)
396 { 611 {
397 int i; 612 int i;
398 regmatch_t matched[16]; 613 regmatch_t matched[16];
399 614
400 i = regexec (&transformre, string, 16, matched, 0); 615 i = regexec (transformre, s, 16, matched, 0);
401 if (i == 0) 616 if (i == 0)
402 { /* matched */ 617 { /* matched */
618 int match_start = matched[0].rm_so;
619 int match_end = matched[0].rm_eo;
620 int old_len = match_end - match_start;
621 int new_len = strlen (transform_to);
622 int old_whole_len = strlen (s);
623
624 printf ("regexp was matched by '%s' - replace with '%s'\n", s, transform_to);
625 printf ("match is from %d to %d\n", match_start, match_end);
626 if (new_len > old_len)
627 s = xrealloc (s, old_whole_len + new_len - old_len);
628
629 if (new_len != old_len)
630 {
631 memcpy (s + match_end + new_len - old_len,
632 s + match_end,
633 old_whole_len - match_end);
634 s[old_whole_len + new_len - old_len] = '\0';
403 } 635 }
636
637 memcpy (s + match_start,
638 transform_to,
639 new_len);
640 printf ("transformed to '%s'\n", s);
641 }
642 else
643 printf ("regexp was not matched by '%s'\n", s);
404 } 644 }
405} 645}
406#endif 646#endif
407 647
408
409/* 648/*
410 * This routine should read 'width' characters and not more. However, 649 * appends p2 to the end of p1, if p1 is not null
411 * we really want to read width + 1 characters if the last char is a '\n', 650 * otherwise allocates a new string and copies p2 to it
412 * which we should remove afterwards. So, read width+1 chars and ungetc 651 */
413 * the last character if it's not a newline. This means 'string' must be 652char *
414 * width + 2 wide! 653concat_line (char *p1, const char *p2)
654{
655 int l1 = p1 ? strlen (p1) : 0;
656 int l2 = strlen (p2);
657 char *r;
658
659 assert (p2);
660
661 if (p1)
662 r = xrealloc(p1, l1 + l2 + 1);
663 else
664 r = xmalloc (l2 + 1);
665
666 memcpy (r + l1, p2, l2);
667 r[l1 + l2] = 0;
668
669 return r;
670}
671
672/*
673 * This routine can read a line of any length if it is called enough times.
415 */ 674 */
416int 675int
417lineinput (struct logfile_entry *logfile) 676lineinput (struct logfile_entry *logfile)
418{ 677{
419 char *string = logfile->buf; 678 char buff[1024], *p;
420 int slen = width + 2; 679 int ch;
421 FILE *f = logfile->fp; 680 /* HACK-2: add on the length of any partial line which we will be appending to */
681 int ofs = logfile->buf ? strlen (logfile->buf) : 0;
422 682
423 int len = strlen (string); 683 /* this loop ensures that the whole line is read, even if it's
424 int partial = logfile->partial; 684 * longer than the buffer. we need to do this because when --whole
425 685 * is in effect we don't know whether to display the line or not
686 * until we've seen how (ie. whether) it ends */
426 do 687 do
427 { 688 {
428 if (fgets (string + len, slen - len, f) == NULL) /* EOF or Error */ 689 p = buff;
690 do
691 {
692 ch = fgetc (logfile->fp);
693
694 if (ch == '\n' || ch == EOF)
695 break;
696 else if (ch == '\r')
697 continue; /* skip */
698 else if (ch == '\t')
699 {
700 do
701 {
702 *p++ = ' ';
703 ofs++;
704 }
705 while (ofs & 7);
706 }
707 else
708 {
709 *p++ = ch;
710 ofs++;
711 }
712 }
713 while (p < buff + (sizeof buff) - 8 - 1);
714
715 if (p == buff && ch == EOF)
429 return 0; 716 return 0;
430 717
431 len = strlen (string); 718 *p = 0;
432 }
433 while (len == 0);
434 719
435 logfile->partial = 0; 720 p = logfile->buf = concat_line (logfile->buf, buff);
436
437 if (string[len - 1] == '\n')
438 /* if the string ends in a newline, delete the newline */
439 string[len - 1] = '\0'; /* erase newline */
440 else if (len >= slen - 1)
441 { 721 }
442 /* otherwise if we've read one too many characters, un-read the last one and delete it */ 722 while (ch != '\n' && ch != EOF);
443 ungetc (string[len - 1], f); 723
444 string[len - 1] = '\0'; 724 logfile->lastpartial = logfile->partial;
445 } 725 /* there are 3 ways we could have exited the loop: reading '\n',
446 else if (opt_whole) 726 * reaching EOF, or filling the buffer; the 2nd and 3rd of these
727 * both result in a partial line */
728 logfile->partial = ch != '\n';
729
730 if (logfile->partial && opt_whole)
447 return 0; 731 return 0;
448 else
449 logfile->partial = 1;
450 732
451#if HAS_REGEX 733#if HAS_REGEX
452 transform_line (string); 734 transform_line (logfile->buf);
453#endif 735#endif
454 logfile->lastpartial = partial;
455 return len; 736 return 1;
456} 737}
457 738
458/* input: reads file->fname 739/* input: reads file->fname
459 * output: fills file->fp, file->inode 740 * output: fills file->fp, file->inode
460 * returns file->fp 741 * returns file->fp
481 else 762 else
482 file->inode = stats.st_ino; 763 file->inode = stats.st_ino;
483 764
484 if (opt_noinitial) 765 if (opt_noinitial)
485 fseek (file->fp, 0, SEEK_END); 766 fseek (file->fp, 0, SEEK_END);
486 else if (stats.st_size > (listlen + 1) * width) 767 else /* if (stats.st_size > (listlen + 1) * width)
768 * HACK - 'width' is in pixels - how are we to know how much text will fit?
487 fseek (file->fp, -((listlen + 2) * width), SEEK_END); 769 * fseek (file->fp, -((listlen + 2) * width/10), SEEK_END); */
770 fseek (file->fp, -5000, SEEK_END);
488 771
489 file->last_size = stats.st_size; 772 file->last_size = stats.st_size;
490 return file->fp; 773 return file->fp;
491} 774}
492 775
524 { /* file missing? */ 807 { /* file missing? */
525 sleep (1); 808 sleep (1);
526 if (e->fp) 809 if (e->fp)
527 fclose (e->fp); 810 fclose (e->fp);
528 if (openlog (e) == NULL) 811 if (openlog (e) == NULL)
529 break; 812 continue;
813 if (fstat (fileno (e->fp), &stats) < 0)
814 continue;
530 } 815 }
531 816
532 if (stats.st_ino != e->inode) 817 if (stats.st_ino != e->inode)
533 { /* file renamed? */ 818 { /* file renamed? */
534 if (e->fp) 819 if (e->fp)
535 fclose (e->fp); 820 fclose (e->fp);
536 if (openlog (e) == NULL) 821 if (openlog (e) == NULL)
537 break; 822 continue;
823 if (fstat (fileno (e->fp), &stats) < 0)
824 continue;
538 } 825 }
539 826
540 if (stats.st_size < e->last_size) 827 if (stats.st_size < e->last_size)
541 { /* file truncated? */ 828 { /* file truncated? */
542 fseek (e->fp, 0, SEEK_SET); 829 fseek (e->fp, 0, SEEK_SET);
543 e->last_size = stats.st_size; 830 e->last_size = stats.st_size;
544 } 831 }
545 } 832 }
546} 833}
547 834
548#define SCROLL_UP(lines, listlen) \ 835/*
549{ \ 836 * insert a single node in the list of screen lines and return a
550 int cur_line; \ 837 * pointer to the new node.
551 struct logfile_entry *current; \ 838 * the caller MUST then fill in ret->line and ret->len with valid
552 for (cur_line = 0; cur_line < (listlen - 1); cur_line++) { \ 839 * data.
553 strcpy(lines[cur_line].line, lines[cur_line + 1].line); \ 840 */
554 lines[cur_line].color = lines[cur_line + 1].color; \ 841static struct line_node *
555 } \ 842new_line_node (struct logfile_entry *log)
556 for (current = loglist; current; current = current->next) \ 843{
557 if (current->partial && current->index) \ 844 struct line_node *new = xmalloc (sizeof (struct line_node));
558 current->index--; \
559}
560 845
561void 846 new->logfile = log;
847 new->wrapped_left = 0;
848 new->wrapped_right = 0;
849 new->breaks = 0;
850
851 assert(log);
852
853 if (!log || !log->last)
854 {
855 new->next = linelist;
856 new->next->prev = new;
857
858 new->prev = NULL;
859 linelist = new;
860 }
861 else
862 {
863 /* 2 pointers from the new node */
864 new->next = log->last;
865 new->prev = log->last->prev;
866
867 /* 2 pointers back to the new node */
868 if (new->next) new->next->prev = new;
869 if (new->prev) new->prev->next = new;
870
871 /* if this is a new first entry in the list then update
872 * 'linelist' */
873 if (log->last == linelist)
874 linelist = new;
875 }
876
877 /* update the logfile record */
878 if (log)
879 log->last = new;
880
881 return new;
882}
883
884/*
885 * this is called after either adding a new line or appending to an
886 * old one. in both cases it's possible that the line no longer fits,
887 * and needs wrapping. this function checks the last line associated
888 * with the supplied logfile.
889 */
890static void
891possibly_split_long_line (struct logfile_entry *log)
892{
893 char *str = log->last->line;
894 int l = strlen (str);
895 char *p = str;
896 struct line_node *line;
897 int spaces;
898 static struct breakinfo *breaks;
899 static int break_buffer_size;
900
901 /* only calculate the continuation's width once */
902 if (continuation_width == -1)
903 {
904 continuation_length = strlen (continuation);
905 continuation_width = XmbTextEscapement (log->fontset, continuation, continuation_length);
906 continuation_color = GetColor (cont_color);
907
908 /* make an array to store information about the location of
909 * spaces in the line */
910 if (opt_justify)
911 {
912 break_buffer_size = 32;
913 breaks = xmalloc (break_buffer_size * sizeof (struct breakinfo));
914 }
915 }
916
917 do
918 {
919 const char *beg = p;
920 int start_w = log->last->wrapped_left ? continuation_width : 0;
921 int w = start_w;
922 int wrapped = 0;
923 char *break_p = NULL;
924 int width_at_break_p = 0;
925 int prefix_len;
926
927 spaces = 0;
928
929 if (opt_justify)
930 breaks[spaces].index = breaks[spaces].width = 0;
931
932 while (*p)
933 {
934 int cw, len;
935
936 /* find the length in bytes of the next multibyte character */
937 len = mblen (p, l);
938 if (len <= 0)
939 len = 1; /* ignore (don't skip) illegal character sequences */
940
941 /* find the width in pixels of the next character */
942 cw = XmbTextEscapement (log->fontset, p, len);
943 if (opt_wordwrap && len == 1 && p[0] == ' ' && p != break_p + 1)
944 {
945 break_p = p;
946 width_at_break_p = w;
947 spaces++;
948
949 if (opt_justify)
950 {
951 /* increase the size of the 'breaks' array when
952 * necessary */
953 if (spaces >= break_buffer_size)
954 {
955 break_buffer_size *= 1.5;
956 breaks = xrealloc (breaks, break_buffer_size * sizeof (struct breakinfo));
957 }
958
959 /* store information about (a) the location of each
960 * space */
961 breaks[spaces].index = p + 1 - beg;
962 /* (b) the width (in pixels) of the string up to
963 * this space */
964 breaks[spaces].width = cw + w - start_w;
965 /* (c) the length of each 'word' */
966 breaks[spaces-1].len = breaks[spaces].index - breaks[spaces-1].index;
967 }
968 }
969
970 if (cw + w > width - effect_x_space)
971 {
972 if (p == beg)
973 {
974 fprintf (stderr, "we can't even fit a single character onto the line\n");
975 if (len == 1) fprintf (stderr, "(the character we couldn't fit was '%c')\n", *p);
976 exit (1);
977 }
978
979 wrapped = 1;
980 break;
981 }
982
983 w += cw;
984 p += len;
985 l -= len;
986 }
987
988 /* if we're wrapping at spaces, and the line is long enough to
989 * wrap, and we've seen a space already, and the space wasn't
990 * the first character on the line, then wrap at the space */
991 if (!wrapped)
992 break;
993
994 /* choose where to break the line */
995 if (opt_wordwrap && break_p && break_p != beg)
996 {
997 prefix_len = break_p - beg;
998 p = break_p;
999 w = width_at_break_p;
1000
1001 /* if breaking at a space, skip all adjacent spaces */
1002 while (*p == ' ')
1003 {
1004 int len = mblen (p, l);
1005 if (len != 1) break;
1006 p++;
1007 }
1008
1009 if (opt_justify)
1010 {
1011 spaces--;
1012 breaks[spaces].len--;
1013 }
1014 }
1015 else
1016 prefix_len = p - beg;
1017
1018 /* make a copy of the tail end of the string */
1019 p = xstrdup (p);
1020
1021 /* and reduce the size of the head of the string */
1022 log->last->line = xrealloc (log->last->line, prefix_len + 1);
1023 log->last->len = prefix_len;
1024 log->last->line[prefix_len] = '\0';
1025
1026 /* note that the head was wrapped on it's right */
1027 log->last->wrapped_right = 1;
1028
1029 /* 'spaces' includes any space we broke on; we can only justify
1030 * if there's at least one other space */
1031 if (opt_justify && spaces &&
1032 width - effect_x_space - width_at_break_p < spaces * log->font_height)
1033 {
1034 int i;
1035 log->last->free_pixels = width - effect_x_space - w;
1036 log->last->num_words = spaces + 1;
1037 log->last->breaks = malloc (log->last->num_words * sizeof (struct breakinfo));
1038 for (i = 0; i < log->last->num_words; i++)
1039 log->last->breaks[i] = breaks[i];
1040 }
1041
1042 line = new_line_node (log);
1043 line->line = p;
1044 l = line->len = strlen (p);
1045
1046 /* note that the tail end of the string is wrapped at its left */
1047 line->wrapped_left = 1;
1048 }
1049 while (l);
1050}
1051
1052static void
1053insert_new_line (char *str, struct logfile_entry *log)
1054{
1055 struct line_node *new;
1056 new = new_line_node (log);
1057 new->line = str;
1058 new->len = strlen (str);
1059
1060 possibly_split_long_line (log);
1061}
1062
1063/*
1064 * append something to an existing physical line. this is done
1065 * by deleting the file on-screen, concatenating the new data to it
1066 * and splitting it again.
1067 */
1068static void
1069append_to_existing_line (char *str, struct logfile_entry *log)
1070{
1071 char *old, *new;
1072
1073 assert(log);
1074 assert(log->last);
1075
1076 old = log->last->line;
1077 assert(old);
1078
1079 new = concat_line (old, str);
1080 free (str);
1081 log->last->line = new;
1082 log->last->len = strlen (new);
1083 possibly_split_long_line (log);
1084}
1085
1086static void
562main_loop (void) 1087main_loop (void)
563{ 1088{
564 struct linematrix *lines = xmalloc (sizeof (struct linematrix) * listlen); 1089 int lin;
565 int lin, miny, maxy;
566 time_t lastreload; 1090 time_t lastreload;
567 Region region = XCreateRegion (); 1091 Region region = XCreateRegion ();
568 XEvent xev; 1092 XEvent xev;
569 struct logfile_entry *lastprinted = NULL; 1093 struct logfile_entry *lastprinted = NULL;
570 struct logfile_entry *current; 1094 struct logfile_entry *current;
1095 int need_update = 1;
571 1096
572 maxy = 0; 1097 display = xmalloc (sizeof (struct displaymatrix) * listlen);
573 miny = win_y + height; 1098
574 lastreload = time (NULL); 1099 lastreload = time (NULL);
575 1100
576 /* Initialize linematrix */ 1101 /* Initialize line_node */
577 for (lin = 0; lin < listlen; lin++) 1102 for (lin = 0; lin < listlen; lin++)
578 { 1103 {
579 lines[lin].line = xmalloc (width + 2); 1104 struct line_node *e = xmalloc (sizeof (struct line_node));
580 strcpy (lines[lin].line, "~"); 1105 e->line = xstrdup ("~");
581 lines[lin].color = GetColor (def_color); 1106 e->len = 1;
582 } 1107 e->logfile = loglist; /* this is only needed to get a color for the '~' */
1108 e->wrapped_left = 0;
1109 e->wrapped_right = 0;
1110 e->breaks = 0;
1111 e->next = NULL;
1112 e->prev = linelist_tail;
583 1113
584 /* show the display full of empty lines ("~") in case the first 1114 if (!linelist)
585 * time around the loop doesn't produce any output, as it won't if 1115 linelist = e;
586 * either (a) -noinitial is set or (b) all the files are currently 1116 if (linelist_tail)
587 * empty */ 1117 linelist_tail->next = e;
588 redraw (); 1118 linelist_tail = e;
1119
1120 display[lin].line = xstrdup ("");
1121 display[lin].len = 0;
1122 display[lin].offset = 0;
1123 display[lin].buffer_size = 0;
1124 }
589 1125
590 for (;;) 1126 for (;;)
591 { 1127 {
592 int need_update = 0;
593
594 /* read logs */ 1128 /* read logs */
595 for (current = loglist; current; current = current->next) 1129 for (current = loglist; current; current = current->next)
596 { 1130 {
597 if (!current->fp) 1131 if (!current->fp)
598 continue; /* skip missing files */ 1132 continue; /* skip missing files */
599 1133
600 clearerr (current->fp); 1134 clearerr (current->fp);
601 1135
602 while (lineinput (current) != 0) 1136 while (lineinput (current))
603 { 1137 {
1138 need_update = 1;
604 /* if we're trying to update old partial lines in 1139 /* if we're trying to update old partial lines in
605 * place, and the last time this file was updated the 1140 * place, and the last time this file was updated the
606 * output was partial, and that partial line is not 1141 * output was partial, and that partial line is not
607 * too close to the top of the screen, then update 1142 * too close to the top of the screen, then update
608 * that partial line */ 1143 * that partial line */
609 if (opt_update && current->lastpartial && current->index >= 3) 1144 if (opt_update && current->lastpartial && current->last)
610 { 1145 {
611 int old_len = strlen (lines[current->index].line); 1146 append_to_existing_line (current->buf, current);
612 int new_len = strlen (current->buf);
613 int space_on_old_line = width / font_width - old_len; //D
614
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, 1147 current->buf = 0;
626 current->buf + space_on_old_line, 1148 continue;
627 new_len - space_on_old_line + 1);
628 } 1149 }
1150
1151 /* if all we just read was a newline ending a line that we've already displayed, skip it */
1152 if (current->buf[0] == '\0' && current->lastpartial)
1153 {
1154 free(current->buf);
1155 current->buf = 0;
1156 continue;
1157 }
1158
1159 /* print filename if any, and if last line was from
1160 * different file */
1161 if (lastprinted != current)
1162 {
1163 current->last = 0;
1164 if (!opt_nofilename && current->desc[0])
1165 {
1166 insert_new_line (xstrdup ("["), current);
1167 append_to_existing_line (xstrdup (current->desc), current);
1168 append_to_existing_line (xstrdup ("]"), current);
1169 }
1170 }
1171
1172 /* if we're dealing with partial lines, and the last
1173 * time we showed the line it wasn't finished ... */
1174 if (!opt_whole && current->lastpartial)
1175 {
1176 /* if this is the same file we showed last then
1177 append to the last line shown */
1178 if (lastprinted == current)
1179 append_to_existing_line (current->buf, current);
629 else 1180 else
630 { 1181 {
631 need_update = 1; 1182 /* but if a different file has been shown in the
1183 * mean time, make a new line, starting with the
1184 * continuation string */
632 strcpy (current->buf, ""); 1185 insert_new_line (current->buf, current);
633 continue; 1186 current->last->wrapped_left = 1;
634 }
635 }
636
637 /* print filename if any, and if last line was from
638 * different file */
639 if (!opt_nofilename &&
640 lastprinted != current && current->desc[0])
641 {
642 SCROLL_UP (lines, listlen);
643 sprintf (lines[listlen - 1].line, "[%s]", current->desc);
644 lines[listlen - 1].color = current->color;
645 }
646
647 /* if this is the same file we showed last, and the
648 * last time we showed it, it wasn't finished, then
649 * append to the last line shown */
650 if (lastprinted == current && current->lastpartial)
651 {
652 int old_len = strlen (lines[listlen - 1].line);
653 int new_len = strlen (current->buf);
654 strncat (lines[listlen - 1].line, current->buf,
655 width - old_len);
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 {
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 } 1187 }
684 } 1188 }
685 else 1189 else
686 { 1190 /* otherwise just make a plain and simple new line */
687 SCROLL_UP (lines, listlen); 1191 insert_new_line (current->buf, current);
688 strcpy (lines[listlen - 1].line, current->buf);
689 }
690 1192
691 /* we've shown the line now. clear the buffer for the next line */
692 strcpy (current->buf, ""); 1193 current->buf = 0;
693 current->index = listlen - 1;
694 lines[listlen - 1].color = current->color;
695
696 lastprinted = current; 1194 lastprinted = current;
697 need_update = 1;
698 } 1195 }
699 } 1196 }
700 1197
701 if (need_update) 1198 if (need_update)
1199 {
702 redraw (); 1200 redraw (0);
1201 need_update = 0;
1202 }
703 else 1203 else
704 { 1204 {
705 XFlush (disp); 1205 XFlush (disp);
706 1206
707 if (!XPending (disp)) 1207 if (!XPending (disp))
733 1233
734 r.x = xev.xexpose.x; 1234 r.x = xev.xexpose.x;
735 r.y = xev.xexpose.y; 1235 r.y = xev.xexpose.y;
736 r.width = xev.xexpose.width; 1236 r.width = xev.xexpose.width;
737 r.height = xev.xexpose.height; 1237 r.height = xev.xexpose.height;
1238
738 XUnionRectWithRegion (&r, region, region); 1239 XUnionRectWithRegion (&r, region, region);
739 if (miny > r.y)
740 miny = r.y;
741 if (maxy < r.y + r.height)
742 maxy = r.y + r.height;
743 } 1240 }
744 break; 1241 break;
745 default: 1242 default:
746#ifdef DEBUGMODE 1243#ifdef DEBUGMODE
747 fprintf (stderr, "PANIC! Unknown event %d\n", xev.type); 1244 fprintf (stderr, "PANIC! Unknown event %d\n", xev.type);
760 lastreload = time (NULL); 1257 lastreload = time (NULL);
761 } 1258 }
762 1259
763 if (!XEmptyRegion (region)) 1260 if (!XEmptyRegion (region))
764 { 1261 {
1262 XRectangle r;
1263
765 XSetRegion (disp, WinGC, region); 1264 XSetRegion (disp, WinGC, region);
1265 XClipBox (region, &r);
766 1266
767 refresh (lines, miny, maxy); 1267 refresh (r.y, r.y + r.height, 0, 1);
1268
768 XDestroyRegion (region); 1269 XDestroyRegion (region);
769 region = XCreateRegion (); 1270 region = XCreateRegion ();
770 maxy = 0;
771 miny = win_y + height;
772 } 1271 }
773 } 1272 }
774} 1273}
775 1274
776 1275
783#if HAS_REGEX 1282#if HAS_REGEX
784 char *transform = NULL; 1283 char *transform = NULL;
785#endif 1284#endif
786 1285
787 setlocale (LC_CTYPE, ""); /* try to initialize the locale. */ 1286 setlocale (LC_CTYPE, ""); /* try to initialize the locale. */
788
789 /* window needs to be initialized before colorlookups can be done */
790 /* just a dummy to get the color lookups right */
791 geom_mask = NoValue;
792 InitWindow ();
793 1287
794 for (i = 1; i < argc; i++) 1288 for (i = 1; i < argc; i++)
795 { 1289 {
796 const char *arg = argv[i]; 1290 const char *arg = argv[i];
797 1291
810 XParseGeometry (argv[++i], &win_x, &win_y, &width, &height); 1304 XParseGeometry (argv[++i], &win_x, &win_y, &width, &height);
811 else if (!strcmp (arg, "-display")) 1305 else if (!strcmp (arg, "-display"))
812 dispname = argv[++i]; 1306 dispname = argv[++i];
813 else if (!strcmp (arg, "-cont")) 1307 else if (!strcmp (arg, "-cont"))
814 continuation = argv[++i]; 1308 continuation = argv[++i];
1309 else if (!strcmp (arg, "-cont-color"))
1310 cont_color = argv[++i];
815 else if (!strcmp (arg, "-font") || !strcmp (arg, "-fn")) 1311 else if (!strcmp (arg, "-font") || !strcmp (arg, "-fn"))
816 fontname = argv[++i]; 1312 fontname = argv[++i];
817#if HAS_REGEX 1313#if HAS_REGEX
818 else if (!strcmp (arg, "-t")) 1314 else if (!strcmp (arg, "-t"))
1315 {
819 transform = argv[++i]; 1316 transform = argv[++i];
1317 transform_to = argv[++i];
1318 printf ("transform: '%s' to '%s'\n", transform, transform_to);
1319 }
820#endif 1320#endif
821 else if (!strcmp (arg, "-fork") || !strcmp (arg, "-f")) 1321 else if (!strcmp (arg, "-fork") || !strcmp (arg, "-f"))
822 opt_daemonize = 1; 1322 opt_daemonize = 1;
823 else if (!strcmp (arg, "-reload")) 1323 else if (!strcmp (arg, "-reload"))
824 { 1324 {
825 reload = atoi (argv[++i]); 1325 reload = atoi (argv[++i]);
826 command = argv[++i]; 1326 command = argv[++i];
827 } 1327 }
828 else if (!strcmp (arg, "-shade")) 1328 else if (!strcmp (arg, "-shade"))
829 opt_shade = 1; 1329 opt_shade = 1;
1330 else if (!strcmp (arg, "-outline"))
1331 opt_outline = 1;
1332 else if (!strcmp (arg, "-minspace"))
1333 opt_minspace = 1;
1334 else if (!strcmp (arg, "-noflicker"))
1335 opt_noflicker = 1;
830 else if (!strcmp (arg, "-frame")) 1336 else if (!strcmp (arg, "-frame"))
831 opt_frame = 1; 1337 opt_frame = 1;
832 else if (!strcmp (arg, "-no-filename")) 1338 else if (!strcmp (arg, "-no-filename"))
833 opt_nofilename = 1; 1339 opt_nofilename = 1;
834 else if (!strcmp (arg, "-reverse")) 1340 else if (!strcmp (arg, "-reverse"))
837 opt_whole = 1; 1343 opt_whole = 1;
838 else if (!strcmp (arg, "-partial")) 1344 else if (!strcmp (arg, "-partial"))
839 opt_partial = 1; 1345 opt_partial = 1;
840 else if (!strcmp (arg, "-update")) 1346 else if (!strcmp (arg, "-update"))
841 opt_update = opt_partial = 1; 1347 opt_update = opt_partial = 1;
1348 else if (!strcmp (arg, "-wordwrap"))
1349 opt_wordwrap = 1;
1350 else if (!strcmp (arg, "-justify"))
1351 opt_justify = 1;
842 else if (!strcmp (arg, "-color")) 1352 else if (!strcmp (arg, "-color"))
843 def_color = argv[++i]; 1353 def_color = argv[++i];
844 else if (!strcmp (arg, "-noinitial")) 1354 else if (!strcmp (arg, "-noinitial"))
845 opt_noinitial = 1; 1355 opt_noinitial = 1;
846 else if (!strcmp (arg, "-id")) 1356 else if (!strcmp (arg, "-id"))
847 root = atoi (argv[++i]); 1357 {
1358 unsigned long id;
1359
1360 if (sscanf (argv[++i], "%li", &id) == 1 && id)
1361 root = id;
1362 }
848 else if (!strcmp (arg, "-interval") || !strcmp (arg, "-i")) 1363 else if (!strcmp (arg, "-interval") || !strcmp (arg, "-i"))
849 { 1364 {
850 double iv = atof (argv[++i]); 1365 double iv = atof (argv[++i]);
851 1366
852 interval.tv_sec = (int) iv; 1367 interval.tv_sec = (int) iv;
880 desc = p + 1; 1395 desc = p + 1;
881 } 1396 }
882 } 1397 }
883 1398
884 e = xmalloc (sizeof (struct logfile_entry)); 1399 e = xmalloc (sizeof (struct logfile_entry));
1400 e->partial = 0;
1401 e->buf = 0;
1402
885 if (arg[0] == '-' && arg[1] == '\0') 1403 if (arg[0] == '-' && arg[1] == '\0')
886 { 1404 {
887 if ((e->fp = fdopen (0, "r")) == NULL) 1405 if ((e->fp = fdopen (0, "r")) == NULL)
888 perror ("fdopen"), exit (1); 1406 perror ("fdopen"), exit (1);
889 if (fcntl (0, F_SETFL, O_NONBLOCK) < 0) 1407 if (fcntl (0, F_SETFL, O_NONBLOCK) < 0)
890 perror ("fcntl"), exit (1); 1408 perror ("fcntl"), exit (1);
1409
891 e->fname = NULL; 1410 e->fname = NULL;
892 e->inode = 0; 1411 e->inode = 0;
1412 if (desc == arg)
893 e->desc = xstrdup ("stdin"); 1413 e->desc = xstrdup ("stdin");
1414 else
1415 e->desc = xstrdup (desc);
894 } 1416 }
895 else 1417 else
896 { 1418 {
897 int l;
898
899 e->fname = xstrdup (fname); 1419 e->fname = xstrdup (fname);
1420
900 if (openlog (e) == NULL) 1421 if (openlog (e) == NULL)
901 perror (fname), exit (1); 1422 perror (fname), exit (1);
902 1423
903 l = strlen (desc); 1424 e->desc = xstrdup (desc);
904 if (l > width - 2) /* must account for [ ] */
905 l = width - 2;
906 e->desc = xmalloc (l + 1);
907 memcpy (e->desc, desc, l);
908 *(e->desc + l) = '\0';
909 } 1425 }
910 1426
911 e->color = GetColor (fcolor); 1427 e->colorname = fcolor;
912 e->buf = xmalloc (width + 2);
913 e->partial = 0; 1428 e->partial = 0;
914 e->buf[0] = '\0'; 1429 e->fontname = fontname;
1430 e->last = NULL;
915 e->next = NULL; 1431 e->next = NULL;
916 1432
917 if (!loglist) 1433 if (!loglist)
918 loglist = e; 1434 loglist = e;
919 if (loglist_tail) 1435 if (loglist_tail)
920 loglist_tail->next = e; 1436 loglist_tail->next = e;
1437
921 loglist_tail = e; 1438 loglist_tail = e;
922 } 1439 }
923 } 1440 }
924 1441
925 if (!loglist) 1442 if (!loglist)
927 fprintf (stderr, "You did not specify any files to tail\n" 1444 fprintf (stderr, "You did not specify any files to tail\n"
928 "use %s --help for help\n", argv[0]); 1445 "use %s --help for help\n", argv[0]);
929 exit (1); 1446 exit (1);
930 } 1447 }
931 1448
1449 if (opt_update && opt_whole)
1450 {
1451 fprintf (stderr, "Specify at most one of -update and -whole\n");
1452 exit (1);
1453 }
932 if (opt_partial && opt_whole) 1454 else if (opt_partial && opt_whole)
933 { 1455 {
934 fprintf (stderr, "Specify at most one of -partial and -whole\n"); 1456 fprintf (stderr, "Specify at most one of -partial and -whole\n");
1457 exit (1);
1458 }
1459
1460 /* it doesn't make sense to justify if word wrap isn't on */
1461 if (opt_justify)
1462 opt_wordwrap = 1;
1463
1464 /* HACK-7: do we want to allow both -shade and -outline? */
1465 if (opt_shade && opt_outline)
1466 {
1467 fprintf (stderr, "Specify at most one of -shade and -outline\n");
935 exit (1); 1468 exit (1);
936 } 1469 }
937 1470
938 if (opt_partial) 1471 if (opt_partial)
939 /* if we specifically requested to see partial lines then don't insist on whole lines */ 1472 /* if we specifically requested to see partial lines then don't insist on whole lines */
940 opt_whole = 0; 1473 opt_whole = 0;
941 else if (file_count > 1) 1474 else if (file_count > 1)
942 /* otherwise, if we've viewing multiple files, default to showing whole lines */ 1475 /* otherwise, if we're viewing multiple files, default to showing whole lines */
943 opt_whole = 1; 1476 opt_whole = 1;
944 1477
945#if HAS_REGEX 1478#if HAS_REGEX
946 if (transform) 1479 if (transform)
947 { 1480 {
948 int i; 1481 int i;
949 1482
1483 printf ("compiling regexp '%s'\n", transform);
950 transformre = xmalloc (sizeof (transformre)); 1484 transformre = xmalloc (sizeof (regex_t));
951 i = regcomp (&transformre, transform, REG_EXTENDED); 1485 i = regcomp (transformre, transform, REG_EXTENDED);
952 if (i != 0) 1486 if (i != 0)
953 { 1487 {
954 char buf[512]; 1488 char buf[512];
955 1489
956 regerror (i, &transformre, buf, sizeof (buf)); 1490 regerror (i, transformre, buf, sizeof (buf));
957 fprintf (stderr, "Cannot compile regular expression: %s\n", buf); 1491 fprintf (stderr, "Cannot compile regular expression: %s\n", buf);
958 } 1492 }
1493 else
1494 printf ("compiled '%s' OK to %x\n", transform, (int)transformre);
959 } 1495 }
960#endif 1496#endif
1497
1498 if (opt_outline && !opt_minspace)
1499 {
1500 /* adding outline increases the total width and height by 2
1501 pixels each, and offsets the text one pixel right and one
1502 pixel down */
1503 effect_x_space = effect_y_space = 2;
1504 effect_x_offset = effect_y_offset = 1;
1505 }
1506 else if (opt_shade && !opt_minspace)
1507 {
1508 /* adding a shadow increases the space used */
1509 effect_x_space = abs (SHADE_X);
1510 effect_y_space = abs (SHADE_Y);
1511
1512 /* if the shadow is to the right and below then we don't need
1513 * to move the text to make space for it, but shadows to the left
1514 * and above need accomodating */
1515 effect_x_offset = SHADE_X > 0 ? 0 : -SHADE_X;
1516 effect_y_offset = SHADE_Y > 0 ? 0 : -SHADE_Y;
1517 }
1518 else
1519 {
1520 effect_x_space = effect_y_space = 0;
1521 effect_x_offset = effect_y_offset = 0;
1522 }
961 1523
962 InitWindow (); 1524 InitWindow ();
963 1525
964 install_signal (SIGINT, blank_window); 1526 install_signal (SIGINT, blank_window);
965 install_signal (SIGQUIT, blank_window); 1527 install_signal (SIGQUIT, blank_window);
984 action.sa_handler = handler; 1546 action.sa_handler = handler;
985 sigemptyset (&action.sa_mask); 1547 sigemptyset (&action.sa_mask);
986 action.sa_flags = SA_RESTART; 1548 action.sa_flags = SA_RESTART;
987 1549
988 if (sigaction (sig, &action, NULL) < 0) 1550 if (sigaction (sig, &action, NULL) < 0)
989 fprintf (stderr, "sigaction(%d): %s\n", sig, strerror (errno)), exit (1); 1551 fprintf (stderr, "sigaction (%d): %s\n", sig, strerror (errno)), exit (1);
990} 1552}
991 1553
992void * 1554void *
993xstrdup (const char *string) 1555xstrdup (const char *string)
994{ 1556{
995 void *p; 1557 void *p;
996 1558
997 while ((p = strdup (string)) == NULL) 1559 while ((p = strdup (string)) == NULL)
998 { 1560 {
999 fprintf (stderr, "Memory exausted."); 1561 fprintf (stderr, "Memory exhausted in xstrdup ().\n");
1000 sleep (10); 1562 sleep (10);
1001 } 1563 }
1002 1564
1003 return p; 1565 return p;
1004} 1566}
1008{ 1570{
1009 void *p; 1571 void *p;
1010 1572
1011 while ((p = malloc (size)) == NULL) 1573 while ((p = malloc (size)) == NULL)
1012 { 1574 {
1013 fprintf (stderr, "Memory exausted."); 1575 fprintf (stderr, "Memory exhausted in xmalloc ().\n");
1014 sleep (10); 1576 sleep (10);
1015 } 1577 }
1578
1579 return p;
1580}
1581
1582void *
1583xrealloc (void *ptr, size_t size)
1584{
1585 void *p;
1586
1587 while ((p = realloc (ptr, size)) == NULL)
1588 {
1589 fprintf (stderr, "Memory exhausted in xrealloc ().\n");
1590 sleep (10);
1591 }
1592
1016 return p; 1593 return p;
1017} 1594}
1018 1595
1019void 1596void
1020display_help (char *myname) 1597display_help (char *myname)
1021{ 1598{
1022 printf ("Usage: %s [options] file1[,color[,desc]] " 1599 printf ("Usage: %s [options] file1[,color[,desc]]"
1023 "[file2[,color[,desc]] ...]\n", myname); 1600 "[options] [file2[,color[,desc]] ...]\n", myname);
1024 printf (" -g | -geometry geometry -g WIDTHxHEIGHT+X+Y\n" 1601 printf (" -g | -geometry geometry -g WIDTHxHEIGHT+X+Y\n"
1025 " -color color use color $color as default\n" 1602 " -color color use color $color as default\n"
1026 " -reload sec command reload after $sec and run command\n" 1603 " -reload sec command reload after $sec and run command\n"
1027 " -id id window id to use instead of the root window\n" 1604 " -id id window id to use instead of the root window\n"
1028 " -font FONTSPEC (-fn) font to use\n" 1605 " -font FONTSPEC (-fn) font to use\n"
1030 " -reverse print new lines at the top\n" 1607 " -reverse print new lines at the top\n"
1031 " -whole wait for \\n before showing a line\n" 1608 " -whole wait for \\n before showing a line\n"
1032 " -partial show lines even if they don't end with a \\n\n" 1609 " -partial show lines even if they don't end with a \\n\n"
1033 " -update allow updates to old partial lines\n" 1610 " -update allow updates to old partial lines\n"
1034 " -cont string to prefix continued partial lines with\n" 1611 " -cont string to prefix continued partial lines with\n"
1035 " defaults to \"[+]\"\n" 1612 " defaults to \"|| \"\n"
1613 " -wordwrap wrap long lines at spaces to avoid breaking words\n"
1036 " -shade add shading to font\n" 1614 " -shade add shading to font\n"
1615 " -outline add black outline to font\n"
1616 " -minspace force minimum line spacing\n"
1037 " -noinitial don't display the last file lines on\n" 1617 " -noinitial don't display the last file lines on\n"
1038 " startup\n" 1618 " startup\n"
1039 " -i | -interval seconds interval between checks (fractional\n" 1619 " -i | -interval seconds interval between checks (fractional\n"
1040 " values o.k.). Default 3 seconds\n" 1620 " values o.k.). Default 2.4 seconds\n"
1041 " -V display version information and exit\n" 1621 " -V display version information and exit\n"
1042 "\n"); 1622 "\n");
1043 printf ("Example:\n%s -g 80x25+100+50 -font fixed /var/log/messages,green " 1623 printf ("Example:\n%s -g 800x250+100+50 -font fixed /var/log/messages,green "
1044 "/var/log/secure,red,'ALERT'\n", myname); 1624 "/var/log/secure,red,'ALERT'\n", myname);
1045 exit (0); 1625 exit (0);
1046} 1626}
1047 1627
1048void 1628void
1053} 1633}
1054 1634
1055int 1635int
1056daemonize (void) 1636daemonize (void)
1057{ 1637{
1638 pid_t pid;
1639
1058 switch (fork ()) 1640 switch (pid = fork ())
1059 { 1641 {
1060 case -1: 1642 case -1:
1061 return -1; 1643 return -1;
1062 case 0: 1644 case 0:
1063 break; 1645 break;
1064 default: 1646 default:
1647 /*printf ("%d\n", pid);*/
1065 _exit (0); 1648 exit (0);
1066 } 1649 }
1067 1650
1068 if (setsid () == -1) 1651 if (setsid () == -1)
1069 return -1; 1652 return -1;
1070 1653

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines