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.33 by chris_moore, Wed Mar 31 00:14:13 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 3 * Copyright 2000,2001,2002,2003,2004
4 * Marc Lehmann <pcg@goof.com>, 4 * Marc Lehmann <pcg@goof.com>,
5 * Chris Moore <chris.moore@mail.com>,
5 * and many others, see README 6 * and many others, see README
6 * 7 *
7 * Original version by Mike Baker. 8 * Original version by Mike Baker.
8 * 9 *
9 * 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
10 * 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
11 * the Free Software Foundation; either version 2 of the License, or 12 * the Free Software Foundation; either version 2 of the License, or
20 * along with this program; if not, write to the Free Software 21 * along with this program; if not, write to the Free Software
21 * 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.
22 */ 23 */
23 24
24#include "config.h" 25#include "config.h"
26#include <assert.h>
25#include <stdlib.h> 27#include <stdlib.h>
26#include <stdio.h> 28#include <stdio.h>
27#include <unistd.h> 29#include <unistd.h>
28#include <string.h> 30#include <string.h>
29#include <signal.h> 31#include <signal.h>
45#endif 47#endif
46 48
47#define SHADE_X 2 49#define SHADE_X 2
48#define SHADE_Y 2 50#define SHADE_Y 2
49 51
52/* some italic fonts still go over the margin - this margin of error cleans up the mess */
53#define MARGIN_OF_ERROR 2
54
50/* data structures */ 55/* data structures */
51struct logfile_entry 56struct logfile_entry
52{ 57{
53 struct logfile_entry *next; 58 struct logfile_entry *next;
54 59
55 char *fname; /* name of file */ 60 char *fname; /* name of file */
56 char *desc; /* alternative description */ 61 char *desc; /* alternative description */
57 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;
58 FILE *fp; /* FILE struct associated with file */ 67 FILE *fp; /* FILE struct associated with file */
59 ino_t inode; /* inode of the file opened */ 68 ino_t inode; /* inode of the file opened */
60 off_t last_size; /* file size at the last check */ 69 off_t last_size; /* file size at the last check */
61 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 */
62 int partial; /* true if the last line isn't complete */ 72 int partial; /* true if the last line isn't complete */
63 int lastpartial; /* true if the previous output wasn't complete */ 73 int lastpartial; /* true if the previous output wasn't complete */
64 int index; /* index into linematrix of a partial line */ 74 struct line_node *last; /* last line we output */
65 int modified; /* true if line is modified & needs displaying */ 75 int modified; /* true if line is modified & needs displaying */
66}; 76};
67 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
68struct linematrix 100struct displaymatrix
69{ 101{
70 char *line; 102 char *line;
71 int len; 103 int len;
104 int offset;
105 int buffer_size;
72 unsigned long color; 106 unsigned long color;
73}; 107};
74 108
75struct displaymatrix
76{
77 char *line;
78 int len;
79 int buffer_size;
80};
81
82/* global variables */ 109/* global variables */
83struct linematrix *lines; 110struct line_node *linelist = NULL, *linelist_tail = NULL;
84struct displaymatrix *display; 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 */
85int width = STD_WIDTH, height = STD_HEIGHT, listlen; 118int width = STD_WIDTH, height = STD_HEIGHT, listlen = 50;
86int win_x = LOC_X, win_y = LOC_Y; 119int win_x = LOC_X, win_y = LOC_Y;
87int font_ascent, font_height;
88int effect_x_space, effect_y_space; /* how much space does shading / outlining take up */ 120int effect_x_space, effect_y_space; /* how much space does shading / outlining take up */
89int effect_x_offset, effect_y_offset; /* and how does it offset the usable space */ 121int effect_x_offset, effect_y_offset; /* and how does it offset the usable space */
90int do_reopen; 122int do_reopen;
91struct timeval interval = { 2, 400000 }; 123struct timeval interval = { 2, 400000 };
92XFontSet fontset;
93 124
94/* command line options */ 125/* command line options */
95int opt_noinitial, opt_shade, opt_frame, opt_reverse, opt_nofilename, 126int opt_noinitial, opt_shade, opt_frame, opt_reverse, opt_nofilename,
96 opt_outline, opt_noflicker, opt_whole, opt_update, opt_wordwrap, 127 opt_outline, opt_noflicker, opt_whole, opt_update, opt_wordwrap,
97 geom_mask, reload = 0; 128 opt_justify, geom_mask, opt_minspace, reload;
98const char *command = NULL, 129const char *command = NULL,
99 *fontname = USE_FONT, *dispname = NULL, *def_color = DEF_COLOR, 130 *fontname = USE_FONT, *dispname = NULL, *def_color = DEF_COLOR,
100 *continuation = "[+]"; 131 *continuation = "|| ", *cont_color = DEF_CONT_COLOR;
101 132
102struct logfile_entry *loglist = NULL, *loglist_tail = NULL; 133struct logfile_entry *loglist = NULL, *loglist_tail = NULL;
103 134
104Display *disp; 135Display *disp;
105Window root; 136Window root;
121/* prototypes */ 152/* prototypes */
122void list_files (int); 153void list_files (int);
123void force_reopen (int); 154void force_reopen (int);
124void force_refresh (int); 155void force_refresh (int);
125void 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 */
126 160
127void InitWindow (void); 161void InitWindow (void);
128unsigned long GetColor (const char *); 162unsigned long GetColor (const char *);
129void redraw (int); 163void redraw (int);
130void refresh (int, int, int, int); 164void refresh (int, int, int, int);
168} 202}
169 203
170void 204void
171blank_window (int dummy) 205blank_window (int dummy)
172{ 206{
173 XClearArea (disp, root, win_x - 2, win_y - 2, width + 5, height + 5, False); 207 XClearArea (disp, root, win_x, win_y, width + MARGIN_OF_ERROR, height, False);
174 XFlush (disp); 208 XFlush (disp);
175 exit (0); 209 exit (0);
176} 210}
177 211
178/* X related functions */ 212/* X related functions */
191 fprintf (stderr, "can't allocate %s\n", ColorName); 225 fprintf (stderr, "can't allocate %s\n", ColorName);
192 226
193 return Color.pixel; 227 return Color.pixel;
194} 228}
195 229
230#ifndef USE_TOON_GET_ROOT_WINDOW
196static Window 231static void
197root_window (Display * display, int screen_number) 232find_root_window (Display *display, int screen_number)
198{ 233{
234 if (!root)
235 {
199 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);
200 Window real_root_window = RootWindow (display, screen_number); 238 root = RootWindow (display, screen_number);
201 239
202 if (root) /* root window set via option */
203 return root;
204
205 if (SWM_VROOT != None)
206 {
207 Window unused, *windows; 240 Window unused, *windows = 0;
208 unsigned int count; 241 unsigned int count;
209 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 {
210 if (XQueryTree (display, real_root_window, &unused, &unused, &windows, 254 if (XQueryTree (display, *(Window *)virtual_root_window, &unused, &unused, &windows, &count))
211 &count)) 255 root = windows[count - 1];
256
257 XFree (virtual_root_window);
258 }
259 else if (XQueryTree (display, root, &unused, &unused, &windows, &count))
212 { 260 {
213 int i; 261 int i;
214 262
215 for (i = 0; i < count; i++) 263 for (i = 0; i < count; i++)
216 { 264 {
217 Atom type;
218 int format;
219 unsigned long nitems, bytes_after_return;
220 unsigned char *virtual_root_window;
221
222 if (XGetWindowProperty (display, windows[i], SWM_VROOT, 265 if (XGetWindowProperty (display, windows[i], SWM_VROOT,
223 0, 1, False, XA_WINDOW, &type, &format, 266 0, 1, False, XA_WINDOW, &type, &format,
224 &nitems, &bytes_after_return, 267 &nitems, &bytes_after_return,
225 &virtual_root_window) == Success) 268 &virtual_root_window) == Success
269 && type == XA_WINDOW)
226 { 270 {
227 if (type != None)
228 {
229 if (type == XA_WINDOW)
230 {
231 XFree (windows);
232 return (Window) virtual_root_window; 271 root = *(Window *)virtual_root_window;
233 } 272 XFree (virtual_root_window);
234 else
235 fprintf (stderr,
236 "__SWM_VROOT property type mismatch");
237 } 273 break;
238 } 274 }
239 else 275 }
240 fprintf (stderr,
241 "failed to get __SWM_VROOT property on window 0x%lx",
242 windows[i]);
243 }
244 276
245 if (count)
246 XFree (windows); 277 XFree (windows);
247 } 278 }
248 else 279 else
249 fprintf (stderr, "Can't query tree on root window 0x%lx", 280 fprintf (stderr, "Can't query tree on root window 0x%lx", root);
250 real_root_window);
251 } 281 }
252 else
253 /* This shouldn't happen. The Xlib documentation is wrong BTW. */
254 fprintf (stderr, "Can't intern atom __SWM_VROOT");
255
256 return real_root_window;
257} 282}
283#endif /* USE_TOON_GET_ROOT_WINDOW */
258 284
259void 285void
260InitWindow (void) 286InitWindow (void)
261{ 287{
262 XGCValues gcv; 288 XGCValues gcv;
263 unsigned long gcm; 289 unsigned long gcm;
264 int screen, ScreenWidth, ScreenHeight; 290 int screen, ScreenWidth, ScreenHeight;
291 struct logfile_entry *e;
265 292
266 if (!(disp = XOpenDisplay (dispname))) 293 if (!(disp = XOpenDisplay (dispname)))
267 { 294 {
268 fprintf (stderr, "Can't open display %s.\n", dispname); 295 fprintf (stderr, "Can't open display %s.\n", dispname);
269 exit (1); 296 exit (1);
271 298
272 screen = DefaultScreen (disp); 299 screen = DefaultScreen (disp);
273 ScreenHeight = DisplayHeight (disp, screen); 300 ScreenHeight = DisplayHeight (disp, screen);
274 ScreenWidth = DisplayWidth (disp, screen); 301 ScreenWidth = DisplayWidth (disp, screen);
275 302
276 root = root_window (disp, screen); 303 find_root_window (disp, screen);
277 304
278 gcm = GCBackground; 305 gcm = GCBackground;
279 gcv.graphics_exposures = True; 306 gcv.graphics_exposures = True;
280 WinGC = XCreateGC (disp, root, gcm, &gcv); 307 WinGC = XCreateGC (disp, root, gcm, &gcv);
281 XMapWindow (disp, root); 308 XMapWindow (disp, root);
309
282 XSetForeground (disp, WinGC, GetColor (DEF_COLOR)); 310 XSetForeground (disp, WinGC, GetColor (DEF_COLOR));
283 311
312 for (e = loglist; e; e = e->next)
284 { 313 {
285 char **missing_charset_list; 314 char **missing_charset_list;
286 int missing_charset_count; 315 int missing_charset_count;
287 char *def_string; 316 char *def_string;
288 317
289 fontset = XCreateFontSet (disp, fontname, 318 e->fontset = XCreateFontSet (disp, e->fontname,
290 &missing_charset_list, &missing_charset_count, 319 &missing_charset_list, &missing_charset_count,
291 &def_string); 320 &def_string);
292 321
293 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
294 { 336 {
295 fprintf (stderr, 337 XFontSetExtents *xfe = XExtentsOfFontSet (e->fontset);
296 "Missing charsets in String to FontSet conversion (%s)\n", 338
297 missing_charset_list[0]); 339 e->font_height = xfe->max_logical_extent.height;
298 XFreeStringList (missing_charset_list); 340 e->font_ascent = -xfe->max_logical_extent.y;
299 } 341 }
300 }
301 342
302 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 }
303 { 355 }
304 fprintf (stderr, "unable to create fontset, exiting.\n");
305 exit (1);
306 }
307
308 {
309 XFontSetExtents *xfe = XExtentsOfFontSet (fontset);
310
311 font_height = xfe->max_logical_extent.height;
312 font_ascent = -xfe->max_logical_extent.y;
313 }
314 356
315 if (geom_mask & XNegative) 357 if (geom_mask & XNegative)
316 win_x = win_x + ScreenWidth - width; 358 win_x = win_x + ScreenWidth - width;
317 if (geom_mask & YNegative) 359 if (geom_mask & YNegative)
318 win_y = win_y + ScreenHeight - height; 360 win_y = win_y + ScreenHeight - height;
319 361
320 if (opt_outline)
321 {
322 /* adding outline increases the total width and height by 2
323 pixels each, and offsets the text one pixel right and one
324 pixel down */
325 effect_x_space = effect_y_space = 2;
326 effect_x_offset = effect_y_offset = 1;
327 }
328 else if (opt_shade)
329 {
330 /* adding a shadow increases the space used */
331 effect_x_space = abs(SHADE_X);
332 effect_y_space = abs(SHADE_Y);
333 /* if the shadow is to the right and below then we don't need
334 * to move the text to make space for it, but shadows to the left
335 * and above need accomodating */
336 effect_x_offset = SHADE_X > 0 ? 0 : -SHADE_X;
337 effect_y_offset = SHADE_Y > 0 ? 0 : -SHADE_Y;
338 }
339 else
340 {
341 effect_x_space = effect_y_space = 0;
342 effect_x_offset = effect_y_offset = 0;
343 }
344
345 /* if we are using -shade or -outline, there will be less usable
346 * space for output */
347 listlen = (height - effect_y_space) / font_height;
348
349 if (!listlen)
350 {
351 fprintf (stderr, "height too small for a single line, setting to %d\n",
352 font_height);
353 listlen = 1;
354 }
355
356 /* leave the height how the user requested it. it might not all be
357 * used, but this will allow the geometry to be tuned more accurately
358 * (with the -frame option)
359 * the old code did this:
360 * height = listlen * font_height + effect_y_space; */
361 362 {
363 struct logfile_entry *e;
364
365 for (e = loglist; e; e = e->next)
366 e->color = GetColor (e->colorname);
367 }
368
362 XSelectInput (disp, root, ExposureMask | FocusChangeMask); 369 XSelectInput (disp, root, ExposureMask | FocusChangeMask);
363} 370}
364 371
365/* 372/*
366 * if redraw() is passwd a non-zero argument, it does a complete 373 * if redraw () is passwd a non-zero argument, it does a complete
367 * redraw, rather than an update. if the argument is zero (and 374 * redraw, rather than an update. if the argument is zero (and
368 * -noflicker is in effect) then only the lines which have changed 375 * -noflicker is in effect) then only the lines which have changed
369 * since the last draw are redrawn. 376 * since the last draw are redrawn.
370 * 377 *
371 * the rest is handled by regular refresh()'es 378 * the rest is handled by regular refresh ()'es
372 */ 379 */
373void 380void
374redraw (int redraw_all) 381redraw (int redraw_all)
375{ 382{
376 XSetClipMask (disp, WinGC, None); 383 XSetClipMask (disp, WinGC, None);
377 refresh (0, 32768, 1, redraw_all); 384 refresh (0, 32768, 1, redraw_all);
378} 385}
379 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 }
416}
417
380/* Just redraw everything without clearing (i.e. after an EXPOSE event) */ 418/* Just redraw everything without clearing (i.e. after an EXPOSE event) */
381void 419void
382refresh (int miny, int maxy, int clear, int refresh_all) 420refresh (int miny, int maxy, int clear, int refresh_all)
383{ 421{
384 int lin; 422 int lin = 0;
385 int offset = listlen * font_height + font_ascent + effect_y_offset; 423 int space = height;
424 int offset;
386 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;
387 429
388 miny -= win_y + font_height; 430 if (opt_reverse)
389 maxy -= win_y - font_height; 431 offset = effect_y_offset;
432 else
433 offset = height + effect_y_offset;
434
435 miny -= win_y;
436 maxy -= win_y;
390 437
391 if (clear && !opt_noflicker) 438 if (clear && !opt_noflicker)
392 XClearArea (disp, root, win_x, win_y, width, height, False); 439 XClearArea (disp, root, win_x, win_y, width + MARGIN_OF_ERROR, height, False);
393 440
394 for (lin = listlen; lin--;) 441 for (line = linelist; line; line = line->next, lin++)
395 { 442 {
396 struct linematrix *line = lines + (opt_reverse ? listlen - lin - 1 : lin);
397 struct displaymatrix *display_line = display + lin; 443 struct displaymatrix *display_line;
398 444
399 offset -= font_height; 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 }
457 }
400 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
401 if (offset < miny || offset > maxy) 473 if (offset >= miny && offset <= maxy)
402 continue; 474 {
403
404 /* if this line is a different than it was, then it 475 /* if this line is a different than it was, then it
405 * needs displaying */ 476 * needs displaying */
406 if (!opt_noflicker 477 if (!opt_noflicker
407 || refresh_all 478 || refresh_all
408 || display_line->len != line->len 479 || display_line->len != line->len
480 || display_line->color != line->logfile->color
481 || display_line->offset != offset
409 || memcmp (display_line->line, line->line, line->len)) 482 || memcmp (display_line->line, line->line, line->len))
410 { 483 {
411 /* don't bother updating the record of what has been 484 /* don't bother updating the record of what has been
412 * displayed if -noflicker isn't in effect, since we redraw 485 * displayed if -noflicker isn't in effect, since we redraw
413 * the whole display every time anyway */ 486 * the whole display every time anyway */
414 if (opt_noflicker) 487 if (opt_noflicker)
415 { 488 {
416 /* update the record of what has been displayed; 489 /* update the record of what has been displayed;
417 * first make sure the buffer is big enough */ 490 * first make sure the buffer is big enough */
418 if (display_line->buffer_size <= line->len) 491 if (display_line->buffer_size < line->len)
419 { 492 {
420 display_line->buffer_size = line->len + 1; 493 display_line->buffer_size = line->len;
421 display_line->line = xrealloc (display_line->line, display_line->buffer_size); 494 display_line->line = xrealloc (display_line->line, display_line->buffer_size);
422 } 495 }
423 496
424 display_line->len = line->len; 497 display_line->len = line->len;
498 display_line->color = line->logfile->color;
499 display_line->offset = offset;
425 memcpy (display_line->line, line->line, line->len); 500 memcpy (display_line->line, line->line, line->len);
426 501
427 if (clear) 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 */
428 XClearArea (disp, root, win_x, win_y + offset - font_ascent, 511 XClearArea (disp, root, win_x, win_y + offset - line->logfile->font_ascent,
429 width + effect_x_space, font_height + effect_y_space, False); 512 width + MARGIN_OF_ERROR, step_per_line, False);
430 } 513#endif /* DEBUG */
514 }
515 }
431 516
432 if (opt_outline) 517 if (opt_outline)
433 { 518 {
434 int x, y; 519 int x, y;
435 XSetForeground (disp, WinGC, black_color); 520 XSetForeground (disp, WinGC, black_color);
436 521
437 for (x = -1; x < 2; x += 2) 522 for (x = -1; x <= 1; x += 2)
438 for (y = -1; y < 2; y += 2) 523 for (y = -1; y <= 1; y += 2)
439 XmbDrawString (disp, root, fontset, WinGC, 524 draw_text (disp, root, WinGC,
440 win_x + effect_x_offset + x, 525 win_x + effect_x_offset + x,
441 win_y + y + offset, 526 win_y + y + offset, line, foreground = 0);
442 line->line, line->len);
443 } 527 }
444 else if (opt_shade) 528 else if (opt_shade)
445 { 529 {
446 XSetForeground (disp, WinGC, black_color); 530 XSetForeground (disp, WinGC, black_color);
447 XmbDrawString (disp, root, fontset, WinGC, 531 draw_text (disp, root, WinGC,
448 win_x + effect_x_offset + SHADE_X, 532 win_x + effect_x_offset + SHADE_X,
449 win_y + offset + SHADE_Y, 533 win_y + offset + SHADE_Y, line, foreground = 0);
450 line->line, line->len);
451 } 534 }
452 535
453 XSetForeground (disp, WinGC, line->color); 536 XSetForeground (disp, WinGC, line->logfile->color);
454 XmbDrawString (disp, root, fontset, WinGC, 537 draw_text (disp, root, WinGC,
455 win_x + effect_x_offset, 538 win_x + effect_x_offset,
456 win_y + offset, 539 win_y + offset, line, foreground = 1);
457 line->line, line->len); 540 }
458 } 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);
459 } 584 }
460 585
461 if (opt_frame) 586 if (opt_frame)
462 { 587 {
463 XSetForeground (disp, WinGC, GetColor (def_color)); 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 */
464 XDrawRectangle (disp, root, WinGC, win_x - 0, win_y - 0, width - 1, height - 1); 592 XDrawRectangle (disp, root, WinGC, win_x - 0, win_y - 0, width - 1, height - 1);
465 } 593 }
466} 594}
467 595
468#if HAS_REGEX 596#if HAS_REGEX
485 regmatch_t matched[16]; 613 regmatch_t matched[16];
486 614
487 i = regexec (transformre, s, 16, matched, 0); 615 i = regexec (transformre, s, 16, matched, 0);
488 if (i == 0) 616 if (i == 0)
489 { /* matched */ 617 { /* matched */
490 int match_start = matched[0].rm_so; 618 int match_start = matched[0].rm_so;
491 int match_end = matched[0].rm_eo; 619 int match_end = matched[0].rm_eo;
492 int old_len = match_end - match_start; 620 int old_len = match_end - match_start;
493 int new_len = strlen(transform_to); 621 int new_len = strlen (transform_to);
494 int old_whole_len = strlen(s); 622 int old_whole_len = strlen (s);
623
495 printf("regexp was matched by '%s' - replace with '%s'\n", s, transform_to); 624 printf ("regexp was matched by '%s' - replace with '%s'\n", s, transform_to);
496 printf("match is from %d to %d\n", 625 printf ("match is from %d to %d\n", match_start, match_end);
497 match_start, match_end);
498 if (new_len > old_len) { 626 if (new_len > old_len)
499 s = xrealloc(s, old_whole_len + new_len - old_len); 627 s = xrealloc (s, old_whole_len + new_len - old_len);
500 } 628
501 if (new_len != old_len) { 629 if (new_len != old_len)
630 {
502 memcpy(s + match_end + new_len - old_len, 631 memcpy (s + match_end + new_len - old_len,
503 s + match_end, 632 s + match_end,
504 old_whole_len - match_end); 633 old_whole_len - match_end);
505 s[old_whole_len + new_len - old_len] = '\0'; 634 s[old_whole_len + new_len - old_len] = '\0';
506 } 635 }
636
507 memcpy(s + match_start, 637 memcpy (s + match_start,
508 transform_to, 638 transform_to,
509 new_len); 639 new_len);
510 printf("transformed to '%s'\n", s); 640 printf ("transformed to '%s'\n", s);
511 } 641 }
512 else 642 else
513 {
514 printf("regexp was not matched by '%s'\n", s); 643 printf ("regexp was not matched by '%s'\n", s);
515 }
516 } 644 }
517} 645}
518#endif 646#endif
519 647
648/*
649 * appends p2 to the end of p1, if p1 is not null
650 * otherwise allocates a new string and copies p2 to it
651 */
520char * 652char *
521concat_line (const char *p1, const char *p2) 653concat_line (char *p1, const char *p2)
522{ 654{
523 int l1 = p1 ? strlen (p1) : 0; 655 int l1 = p1 ? strlen (p1) : 0;
524 int l2 = strlen (p2); 656 int l2 = strlen (p2);
525 char *r = xmalloc (l1 + l2 + 1); 657 char *r;
526 658
527 memcpy (r, p1, l1); 659 assert (p2);
660
661 if (p1)
662 r = xrealloc(p1, l1 + l2 + 1);
663 else
664 r = xmalloc (l2 + 1);
665
528 memcpy (r + l1, p2, l2); 666 memcpy (r + l1, p2, l2);
529 r[l1 + l2] = 0; 667 r[l1 + l2] = 0;
530 668
531 return r; 669 return r;
532} 670}
533 671
534/* 672/*
535 * HACK-1: This routine should read a single line, no matter how long. 673 * This routine can read a line of any length if it is called enough times.
536 */ 674 */
537int 675int
538lineinput (struct logfile_entry *logfile) 676lineinput (struct logfile_entry *logfile)
539{ 677{
540 char buff[1024], *p = buff; 678 char buff[1024], *p;
541 int ch; 679 int ch;
542 /* HACK-2: add on the length of any partial line which we will be appending to */ 680 /* HACK-2: add on the length of any partial line which we will be appending to */
543 int ofs = logfile->buf ? strlen (logfile->buf) : 0; 681 int ofs = logfile->buf ? strlen (logfile->buf) : 0;
544 682
683 /* this loop ensures that the whole line is read, even if it's
684 * longer than the buffer. we need to do this because when --whole
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 */
545 do 687 do
546 { 688 {
689 p = buff;
690 do
691 {
547 ch = fgetc (logfile->fp); 692 ch = fgetc (logfile->fp);
548 693
549 if (ch == '\n' || ch == EOF) 694 if (ch == '\n' || ch == EOF)
550 break; 695 break;
551 else if (ch == '\r') 696 else if (ch == '\r')
552 continue; /* skip */ 697 continue; /* skip */
553 else if (ch == '\t') 698 else if (ch == '\t')
554 { 699 {
555 do 700 do
556 { 701 {
557 *p++ = ' '; 702 *p++ = ' ';
703 ofs++;
704 }
705 while (ofs & 7);
706 }
707 else
708 {
709 *p++ = ch;
558 ofs++; 710 ofs++;
559 } 711 }
560 while (ofs & 7);
561 }
562 else
563 { 712 }
564 *p++ = ch;
565 ofs++;
566 }
567 }
568 while (p < buff + (sizeof buff) - 8 - 1); 713 while (p < buff + (sizeof buff) - 8 - 1);
569 714
570 if (p == buff && ch == EOF) 715 if (p == buff && ch == EOF)
571 return 0; 716 return 0;
572 717
573 *p = 0; 718 *p = 0;
574 719
575 p = concat_line (logfile->buf, buff); 720 p = logfile->buf = concat_line (logfile->buf, buff);
576 free (logfile->buf); logfile->buf = p; 721 }
722 while (ch != '\n' && ch != EOF);
577 723
578 logfile->lastpartial = logfile->partial; 724 logfile->lastpartial = logfile->partial;
579 /* there are 3 ways we could have exited the loop: reading '\n', 725 /* 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 726 * reaching EOF, or filling the buffer; the 2nd and 3rd of these
581 * both result in a partial line */ 727 * both result in a partial line */
582 logfile->partial = ch != '\n'; 728 logfile->partial = ch != '\n';
583 729
584 if (logfile->partial && opt_whole) 730 if (logfile->partial && opt_whole)
585 return 0; 731 return 0;
586 732
587#if HAS_REGEX 733#if HAS_REGEX
588 transform_line (logfile->buf); 734 transform_line (logfile->buf);
616 else 762 else
617 file->inode = stats.st_ino; 763 file->inode = stats.st_ino;
618 764
619 if (opt_noinitial) 765 if (opt_noinitial)
620 fseek (file->fp, 0, SEEK_END); 766 fseek (file->fp, 0, SEEK_END);
621 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?
622 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);
623 771
624 file->last_size = stats.st_size; 772 file->last_size = stats.st_size;
625 return file->fp; 773 return file->fp;
626} 774}
627 775
660 sleep (1); 808 sleep (1);
661 if (e->fp) 809 if (e->fp)
662 fclose (e->fp); 810 fclose (e->fp);
663 if (openlog (e) == NULL) 811 if (openlog (e) == NULL)
664 continue; 812 continue;
813 if (fstat (fileno (e->fp), &stats) < 0)
814 continue;
665 } 815 }
666 816
667 /* HACK-3: stats can be uninitialised here (if the file didn't
668 * exist when stat() was called, but was recreated during the
669 * sleep(1)) */
670 if (stats.st_ino != e->inode) 817 if (stats.st_ino != e->inode)
671 { /* file renamed? */ 818 { /* file renamed? */
672 if (e->fp) 819 if (e->fp)
673 fclose (e->fp); 820 fclose (e->fp);
674 if (openlog (e) == NULL) 821 if (openlog (e) == NULL)
675 continue; 822 continue;
823 if (fstat (fileno (e->fp), &stats) < 0)
824 continue;
676 } 825 }
677 826
678 if (stats.st_size < e->last_size) 827 if (stats.st_size < e->last_size)
679 { /* file truncated? */ 828 { /* file truncated? */
680 fseek (e->fp, 0, SEEK_SET); 829 fseek (e->fp, 0, SEEK_SET);
682 } 831 }
683 } 832 }
684} 833}
685 834
686/* 835/*
687 * insert a single physical line (that must be short enough to fit) 836 * insert a single node in the list of screen lines and return a
688 * at position "idx" by pushing up lines above it. the caller 837 * pointer to the new node.
689 * MUST then fill in lines[idx] with valid data. 838 * the caller MUST then fill in ret->line and ret->len with valid
839 * data.
840 */
841static struct line_node *
842new_line_node (struct logfile_entry *log)
843{
844 struct line_node *new = xmalloc (sizeof (struct line_node));
845
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.
690 */ 889 */
691static void 890static void
692insert_line (int idx) 891possibly_split_long_line (struct logfile_entry *log)
693{ 892{
694 int cur_line; 893 char *str = log->last->line;
695 struct logfile_entry *current;
696
697 free (lines[0].line);
698
699 for (cur_line = 0; cur_line < idx; cur_line++)
700 lines[cur_line] = lines[cur_line + 1];
701
702 for (current = loglist; current; current = current->next)
703 if (current->index <= idx)
704 current->index--;
705}
706
707/*
708 * remove a single physical line at position "idx" by moving the lines above it
709 * down and inserting a "~" line at the top.
710 */
711static void
712delete_line (int idx)
713{
714 int cur_line;
715 struct logfile_entry *current;
716
717 for (cur_line = idx; cur_line > 0; cur_line--)
718 lines[cur_line] = lines[cur_line - 1];
719
720 lines[0].line = xstrdup ("~");
721
722 for (current = loglist; current; current = current->next)
723 if (current->index >= 0 && current->index <= idx)
724 current->index++;
725}
726
727/*
728 * takes a logical log file line and splits it into multiple physical
729 * screen lines by splitting it whenever a part becomes too long.
730 * lal lines will be inserted at position "idx".
731 */
732static void
733split_line (int idx, const char *str, unsigned long color)
734{
735 int l = strlen (str); 894 int l = strlen (str);
736 int last_wrapped = 0;
737 const char *p = str; 895 char *p = str;
738 static int continuation_width = -1; 896 struct line_node *line;
739 static int continuation_length; 897 int spaces;
898 static struct breakinfo *breaks;
899 static int break_buffer_size;
740 900
741 /* only calculate the continuation's width once */ 901 /* only calculate the continuation's width once */
742 if (continuation_width == -1) 902 if (continuation_width == -1)
743 { 903 {
744 continuation_length = strlen(continuation); 904 continuation_length = strlen (continuation);
745 continuation_width = XmbTextEscapement (fontset, continuation, continuation_length); 905 continuation_width = XmbTextEscapement (log->fontset, continuation, continuation_length);
746 printf("continuation width is %d and length is %d\n", continuation_width, 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 }
747 } 915 }
748 916
749 do 917 do
750 { 918 {
751 const char *beg = p; 919 const char *beg = p;
752 int w = last_wrapped ? continuation_width : 0; 920 int start_w = log->last->wrapped_left ? continuation_width : 0;
921 int w = start_w;
753 int wrapped = 0; 922 int wrapped = 0;
754 const char *break_p = NULL; 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;
755 931
756 while (*p) 932 while (*p)
757 { 933 {
934 int cw, len;
935
758 /* find the length in bytes of the next multibyte character */ 936 /* find the length in bytes of the next multibyte character */
759 int len = mblen (p, l); 937 len = mblen (p, l);
760 if (len <= 0) 938 if (len <= 0)
761 len = 1; /* ignore (don't skip) illegal character sequences */ 939 len = 1; /* ignore (don't skip) illegal character sequences */
762 940
763 /* find the width in pixels of the next character */ 941 /* find the width in pixels of the next character */
764 int cw = XmbTextEscapement (fontset, p, len); 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
765 if (cw + w > width - effect_x_space) 970 if (cw + w > width - effect_x_space)
766 { 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
767 wrapped = 1; 979 wrapped = 1;
768 break; 980 break;
769 } 981 }
770
771 if (opt_wordwrap && len == 1 && p[0] == ' ')
772 break_p = p;
773 982
774 w += cw; 983 w += cw;
775 p += len; 984 p += len;
776 l -= len; 985 l -= len;
777 } 986 }
778 987
779 /* if we're wrapping at spaces, and the line is long enough to 988 /* if we're wrapping at spaces, and the line is long enough to
780 * wrap, and we've seen a space already, and the space wasn't 989 * wrap, and we've seen a space already, and the space wasn't
781 * the first character on the line, then wrap at the space */ 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 */
782 if (opt_wordwrap && wrapped && break_p && break_p != beg) 995 if (opt_wordwrap && break_p && break_p != beg)
783 {
784 l += p - break_p;
785 p = break_p;
786 }
787
788 { 996 {
789 /* HACK-4 - consider inserting the 'continuation string' 997 prefix_len = break_p - beg;
790 * before the rest of the wrapped line */ 998 p = break_p;
791 int len = p - beg + (last_wrapped ? continuation_length : 0); 999 w = width_at_break_p;
792 char *s = xmalloc (len + 1);
793 if (last_wrapped)
794 {
795 memcpy (s, continuation, continuation_length);
796 memcpy (s + continuation_length, beg, p - beg);
797 }
798 else
799 memcpy (s, beg, len);
800 1000
801 s[len] = 0; 1001 /* if breaking at a space, skip all adjacent spaces */
802 insert_line (idx); 1002 while (*p == ' ')
803 lines[idx].line = s; 1003 {
804 lines[idx].len = len; 1004 int len = mblen (p, l);
805 lines[idx].color = color; 1005 if (len != 1) break;
1006 p++;
1007 }
1008
1009 if (opt_justify)
1010 {
1011 spaces--;
1012 breaks[spaces].len--;
1013 }
806 } 1014 }
1015 else
1016 prefix_len = p - beg;
807 1017
808 /* if we wrapped at a space, don't display the space */ 1018 /* make a copy of the tail end of the string */
809 if (opt_wordwrap && wrapped && break_p && break_p != beg) 1019 p = xstrdup (p);
810 {
811 l--;
812 p++;
813 }
814 1020
815 last_wrapped = wrapped; 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;
816 } 1048 }
817 while (l); 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);
818} 1061}
819 1062
820/* 1063/*
821 * append something to an existing physical line. this is done 1064 * append something to an existing physical line. this is done
822 * by deleting the file on-screen, concatenating the new data to it 1065 * by deleting the file on-screen, concatenating the new data to it
823 * and splitting it again. 1066 * and splitting it again.
824 */ 1067 */
825static void 1068static void
826append_line (int idx, const char *str) 1069append_to_existing_line (char *str, struct logfile_entry *log)
827{ 1070{
828 unsigned long color = lines[idx].color; 1071 char *old, *new;
829 char *old = lines[idx].line; 1072
1073 assert(log);
1074 assert(log->last);
1075
1076 old = log->last->line;
1077 assert(old);
1078
830 char *new = concat_line (old, str); 1079 new = concat_line (old, str);
831
832 free (old); 1080 free (str);
833 1081 log->last->line = new;
834 delete_line (idx); 1082 log->last->len = strlen (new);
835 split_line (idx, new, color); 1083 possibly_split_long_line (log);
836} 1084}
837 1085
838static void 1086static void
839main_loop (void) 1087main_loop (void)
840{ 1088{
841 lines = xmalloc (sizeof (struct linematrix) * listlen);
842 display = xmalloc (sizeof (struct displaymatrix) * listlen);
843 int lin; 1089 int lin;
844 time_t lastreload; 1090 time_t lastreload;
845 Region region = XCreateRegion (); 1091 Region region = XCreateRegion ();
846 XEvent xev; 1092 XEvent xev;
847 struct logfile_entry *lastprinted = NULL; 1093 struct logfile_entry *lastprinted = NULL;
848 struct logfile_entry *current; 1094 struct logfile_entry *current;
849 int need_update = 1; 1095 int need_update = 1;
850 1096
1097 display = xmalloc (sizeof (struct displaymatrix) * listlen);
1098
851 lastreload = time (NULL); 1099 lastreload = time (NULL);
852 1100
853 /* Initialize linematrix */ 1101 /* Initialize line_node */
854 for (lin = 0; lin < listlen; lin++) 1102 for (lin = 0; lin < listlen; lin++)
855 { 1103 {
1104 struct line_node *e = xmalloc (sizeof (struct line_node));
856 lines[lin].line = xstrdup ("~"); 1105 e->line = xstrdup ("~");
857 lines[lin].len = 1; 1106 e->len = 1;
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;
1113
1114 if (!linelist)
1115 linelist = e;
1116 if (linelist_tail)
1117 linelist_tail->next = e;
1118 linelist_tail = e;
1119
858 display[lin].line = xstrdup(""); 1120 display[lin].line = xstrdup ("");
859 display[lin].len = 0; 1121 display[lin].len = 0;
1122 display[lin].offset = 0;
860 display[lin].buffer_size = 1; 1123 display[lin].buffer_size = 0;
861 lines[lin].color = GetColor (def_color);
862 } 1124 }
863 1125
864 for (;;) 1126 for (;;)
865 { 1127 {
866 /* read logs */ 1128 /* read logs */
877 /* if we're trying to update old partial lines in 1139 /* if we're trying to update old partial lines in
878 * place, and the last time this file was updated the 1140 * place, and the last time this file was updated the
879 * output was partial, and that partial line is not 1141 * output was partial, and that partial line is not
880 * too close to the top of the screen, then update 1142 * too close to the top of the screen, then update
881 * that partial line */ 1143 * that partial line */
882 if (opt_update && current->lastpartial && current->index >= 0) 1144 if (opt_update && current->lastpartial && current->last)
883 { 1145 {
884 int idx = current->index;
885 append_line (idx, current->buf); 1146 append_to_existing_line (current->buf, current);
886 current->index = idx; 1147 current->buf = 0;
887 free (current->buf), current->buf = 0;
888 continue; 1148 continue;
889 } 1149 }
890 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
891 /* print filename if any, and if last line was from 1159 /* print filename if any, and if last line was from
892 * different file */ 1160 * different file */
893 if (!opt_nofilename && lastprinted != current && current->desc[0]) 1161 if (lastprinted != current)
894 { 1162 {
895 char buf[1024]; /* HACK-5 */ 1163 current->last = 0;
896 snprintf (buf, sizeof (buf), "[%s]", current->desc); 1164 if (!opt_nofilename && current->desc[0])
897 split_line (listlen - 1, buf, current->color); 1165 {
898 } 1166 insert_new_line (xstrdup ("["), current);
1167 append_to_existing_line (xstrdup (current->desc), current);
1168 append_to_existing_line (xstrdup ("]"), current);
1169 }
1170 }
899 1171
900 /* if we're dealing with partial lines, and the last 1172 /* if we're dealing with partial lines, and the last
901 * time we showed the line it wasn't finished ... */ 1173 * time we showed the line it wasn't finished ... */
902 if (!opt_whole && current->lastpartial) 1174 if (!opt_whole && current->lastpartial)
903 { 1175 {
904 /* if this is the same file we showed last then 1176 /* if this is the same file we showed last then
905 append to the last line shown */ 1177 append to the last line shown */
906 if (lastprinted == current) 1178 if (lastprinted == current)
907 append_line (listlen - 1, current->buf); 1179 append_to_existing_line (current->buf, current);
908 else 1180 else
909 { 1181 {
910 /* but if a different file has been shown in the 1182 /* but if a different file has been shown in the
911 * mean time, make a new line, starting with the 1183 * mean time, make a new line, starting with the
912 * continuation string */ 1184 * continuation string */
913 split_line (listlen - 1, continuation, current->color); 1185 insert_new_line (current->buf, current);
914 append_line (listlen - 1, current->buf); 1186 current->last->wrapped_left = 1;
915 } 1187 }
916 } 1188 }
917 else 1189 else
918 /* otherwise just make a plain and simple new line */ 1190 /* otherwise just make a plain and simple new line */
919 split_line (listlen - 1, current->buf, current->color); 1191 insert_new_line (current->buf, current);
920 1192
921 free (current->buf), current->buf = 0; 1193 current->buf = 0;
922 current->index = listlen - 1;
923 lastprinted = current; 1194 lastprinted = current;
924 } 1195 }
925 } 1196 }
926 1197
927 if (need_update) 1198 if (need_update)
1011#if HAS_REGEX 1282#if HAS_REGEX
1012 char *transform = NULL; 1283 char *transform = NULL;
1013#endif 1284#endif
1014 1285
1015 setlocale (LC_CTYPE, ""); /* try to initialize the locale. */ 1286 setlocale (LC_CTYPE, ""); /* try to initialize the locale. */
1016
1017 /* window needs to be initialized before colorlookups can be done */
1018 /* just a dummy to get the color lookups right */
1019 geom_mask = NoValue;
1020 InitWindow ();
1021 1287
1022 for (i = 1; i < argc; i++) 1288 for (i = 1; i < argc; i++)
1023 { 1289 {
1024 const char *arg = argv[i]; 1290 const char *arg = argv[i];
1025 1291
1038 XParseGeometry (argv[++i], &win_x, &win_y, &width, &height); 1304 XParseGeometry (argv[++i], &win_x, &win_y, &width, &height);
1039 else if (!strcmp (arg, "-display")) 1305 else if (!strcmp (arg, "-display"))
1040 dispname = argv[++i]; 1306 dispname = argv[++i];
1041 else if (!strcmp (arg, "-cont")) 1307 else if (!strcmp (arg, "-cont"))
1042 continuation = argv[++i]; 1308 continuation = argv[++i];
1309 else if (!strcmp (arg, "-cont-color"))
1310 cont_color = argv[++i];
1043 else if (!strcmp (arg, "-font") || !strcmp (arg, "-fn")) 1311 else if (!strcmp (arg, "-font") || !strcmp (arg, "-fn"))
1044 fontname = argv[++i]; 1312 fontname = argv[++i];
1045#if HAS_REGEX 1313#if HAS_REGEX
1046 else if (!strcmp (arg, "-t")) 1314 else if (!strcmp (arg, "-t"))
1047 { 1315 {
1048 transform = argv[++i]; 1316 transform = argv[++i];
1049 transform_to = argv[++i]; 1317 transform_to = argv[++i];
1050 printf("transform: '%s' to '%s'\n", transform, transform_to); 1318 printf ("transform: '%s' to '%s'\n", transform, transform_to);
1051 } 1319 }
1052#endif 1320#endif
1053 else if (!strcmp (arg, "-fork") || !strcmp (arg, "-f")) 1321 else if (!strcmp (arg, "-fork") || !strcmp (arg, "-f"))
1054 opt_daemonize = 1; 1322 opt_daemonize = 1;
1055 else if (!strcmp (arg, "-reload")) 1323 else if (!strcmp (arg, "-reload"))
1056 { 1324 {
1059 } 1327 }
1060 else if (!strcmp (arg, "-shade")) 1328 else if (!strcmp (arg, "-shade"))
1061 opt_shade = 1; 1329 opt_shade = 1;
1062 else if (!strcmp (arg, "-outline")) 1330 else if (!strcmp (arg, "-outline"))
1063 opt_outline = 1; 1331 opt_outline = 1;
1332 else if (!strcmp (arg, "-minspace"))
1333 opt_minspace = 1;
1064 else if (!strcmp (arg, "-noflicker")) 1334 else if (!strcmp (arg, "-noflicker"))
1065 opt_noflicker = 1; 1335 opt_noflicker = 1;
1066 else if (!strcmp (arg, "-frame")) 1336 else if (!strcmp (arg, "-frame"))
1067 opt_frame = 1; 1337 opt_frame = 1;
1068 else if (!strcmp (arg, "-no-filename")) 1338 else if (!strcmp (arg, "-no-filename"))
1075 opt_partial = 1; 1345 opt_partial = 1;
1076 else if (!strcmp (arg, "-update")) 1346 else if (!strcmp (arg, "-update"))
1077 opt_update = opt_partial = 1; 1347 opt_update = opt_partial = 1;
1078 else if (!strcmp (arg, "-wordwrap")) 1348 else if (!strcmp (arg, "-wordwrap"))
1079 opt_wordwrap = 1; 1349 opt_wordwrap = 1;
1350 else if (!strcmp (arg, "-justify"))
1351 opt_justify = 1;
1080 else if (!strcmp (arg, "-color")) 1352 else if (!strcmp (arg, "-color"))
1081 def_color = argv[++i]; 1353 def_color = argv[++i];
1082 else if (!strcmp (arg, "-noinitial")) 1354 else if (!strcmp (arg, "-noinitial"))
1083 opt_noinitial = 1; 1355 opt_noinitial = 1;
1084 else if (!strcmp (arg, "-id")) 1356 else if (!strcmp (arg, "-id"))
1085 root = atoi (argv[++i]); 1357 {
1358 unsigned long id;
1359
1360 if (sscanf (argv[++i], "%li", &id) == 1 && id)
1361 root = id;
1362 }
1086 else if (!strcmp (arg, "-interval") || !strcmp (arg, "-i")) 1363 else if (!strcmp (arg, "-interval") || !strcmp (arg, "-i"))
1087 { 1364 {
1088 double iv = atof (argv[++i]); 1365 double iv = atof (argv[++i]);
1089 1366
1090 interval.tv_sec = (int) iv; 1367 interval.tv_sec = (int) iv;
1120 } 1397 }
1121 1398
1122 e = xmalloc (sizeof (struct logfile_entry)); 1399 e = xmalloc (sizeof (struct logfile_entry));
1123 e->partial = 0; 1400 e->partial = 0;
1124 e->buf = 0; 1401 e->buf = 0;
1125 e->index = -1;
1126 1402
1127 if (arg[0] == '-' && arg[1] == '\0') 1403 if (arg[0] == '-' && arg[1] == '\0')
1128 { 1404 {
1129 if ((e->fp = fdopen (0, "r")) == NULL) 1405 if ((e->fp = fdopen (0, "r")) == NULL)
1130 perror ("fdopen"), exit (1); 1406 perror ("fdopen"), exit (1);
1131 if (fcntl (0, F_SETFL, O_NONBLOCK) < 0) 1407 if (fcntl (0, F_SETFL, O_NONBLOCK) < 0)
1132 perror ("fcntl"), exit (1); 1408 perror ("fcntl"), exit (1);
1409
1133 e->fname = NULL; 1410 e->fname = NULL;
1134 e->inode = 0; 1411 e->inode = 0;
1412 if (desc == arg)
1135 e->desc = xstrdup ("stdin"); 1413 e->desc = xstrdup ("stdin");
1414 else
1415 e->desc = xstrdup (desc);
1136 } 1416 }
1137 else 1417 else
1138 { 1418 {
1139 int l;
1140
1141 e->fname = xstrdup (fname); 1419 e->fname = xstrdup (fname);
1420
1142 if (openlog (e) == NULL) 1421 if (openlog (e) == NULL)
1143 perror (fname), exit (1); 1422 perror (fname), exit (1);
1144 1423
1145 l = strlen (desc); 1424 e->desc = xstrdup (desc);
1146 /* HACK-6: width is in pixels now */
1147 if (l > width - 2) /* must account for [ ] */
1148 l = width - 2;
1149 e->desc = xmalloc (l + 1);
1150 memcpy (e->desc, desc, l);
1151 *(e->desc + l) = '\0';
1152 } 1425 }
1153 1426
1154 e->color = GetColor (fcolor); 1427 e->colorname = fcolor;
1155 e->partial = 0; 1428 e->partial = 0;
1429 e->fontname = fontname;
1430 e->last = NULL;
1156 e->next = NULL; 1431 e->next = NULL;
1157 1432
1158 if (!loglist) 1433 if (!loglist)
1159 loglist = e; 1434 loglist = e;
1160 if (loglist_tail) 1435 if (loglist_tail)
1161 loglist_tail->next = e; 1436 loglist_tail->next = e;
1437
1162 loglist_tail = e; 1438 loglist_tail = e;
1163 } 1439 }
1164 } 1440 }
1165 1441
1166 if (!loglist) 1442 if (!loglist)
1168 fprintf (stderr, "You did not specify any files to tail\n" 1444 fprintf (stderr, "You did not specify any files to tail\n"
1169 "use %s --help for help\n", argv[0]); 1445 "use %s --help for help\n", argv[0]);
1170 exit (1); 1446 exit (1);
1171 } 1447 }
1172 1448
1449 if (opt_update && opt_whole)
1450 {
1451 fprintf (stderr, "Specify at most one of -update and -whole\n");
1452 exit (1);
1453 }
1173 if (opt_partial && opt_whole) 1454 else if (opt_partial && opt_whole)
1174 { 1455 {
1175 fprintf (stderr, "Specify at most one of -partial and -whole\n"); 1456 fprintf (stderr, "Specify at most one of -partial and -whole\n");
1176 exit (1); 1457 exit (1);
1177 } 1458 }
1459
1460 /* it doesn't make sense to justify if word wrap isn't on */
1461 if (opt_justify)
1462 opt_wordwrap = 1;
1178 1463
1179 /* HACK-7: do we want to allow both -shade and -outline? */ 1464 /* HACK-7: do we want to allow both -shade and -outline? */
1180 if (opt_shade && opt_outline) 1465 if (opt_shade && opt_outline)
1181 { 1466 {
1182 fprintf (stderr, "Specify at most one of -shade and -outline\n"); 1467 fprintf (stderr, "Specify at most one of -shade and -outline\n");
1193#if HAS_REGEX 1478#if HAS_REGEX
1194 if (transform) 1479 if (transform)
1195 { 1480 {
1196 int i; 1481 int i;
1197 1482
1198 printf("compiling regexp '%s'\n", transform); 1483 printf ("compiling regexp '%s'\n", transform);
1199 transformre = xmalloc (sizeof (regex_t)); 1484 transformre = xmalloc (sizeof (regex_t));
1200 i = regcomp (transformre, transform, REG_EXTENDED); 1485 i = regcomp (transformre, transform, REG_EXTENDED);
1201 if (i != 0) 1486 if (i != 0)
1202 { 1487 {
1203 char buf[512]; 1488 char buf[512];
1204 1489
1205 regerror (i, transformre, buf, sizeof (buf)); 1490 regerror (i, transformre, buf, sizeof (buf));
1206 fprintf (stderr, "Cannot compile regular expression: %s\n", buf); 1491 fprintf (stderr, "Cannot compile regular expression: %s\n", buf);
1207 } 1492 }
1208 else 1493 else
1209 {
1210 printf("compiled '%s' OK to %x\n", transform, (int)transformre); 1494 printf ("compiled '%s' OK to %x\n", transform, (int)transformre);
1211 }
1212 } 1495 }
1213#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 }
1214 1523
1215 InitWindow (); 1524 InitWindow ();
1216 1525
1217 install_signal (SIGINT, blank_window); 1526 install_signal (SIGINT, blank_window);
1218 install_signal (SIGQUIT, blank_window); 1527 install_signal (SIGQUIT, blank_window);
1237 action.sa_handler = handler; 1546 action.sa_handler = handler;
1238 sigemptyset (&action.sa_mask); 1547 sigemptyset (&action.sa_mask);
1239 action.sa_flags = SA_RESTART; 1548 action.sa_flags = SA_RESTART;
1240 1549
1241 if (sigaction (sig, &action, NULL) < 0) 1550 if (sigaction (sig, &action, NULL) < 0)
1242 fprintf (stderr, "sigaction(%d): %s\n", sig, strerror (errno)), exit (1); 1551 fprintf (stderr, "sigaction (%d): %s\n", sig, strerror (errno)), exit (1);
1243} 1552}
1244 1553
1245void * 1554void *
1246xstrdup (const char *string) 1555xstrdup (const char *string)
1247{ 1556{
1248 void *p; 1557 void *p;
1249 1558
1250 while ((p = strdup (string)) == NULL) 1559 while ((p = strdup (string)) == NULL)
1251 { 1560 {
1252 fprintf (stderr, "Memory exausted."); 1561 fprintf (stderr, "Memory exhausted in xstrdup ().\n");
1253 sleep (10); 1562 sleep (10);
1254 } 1563 }
1255 1564
1256 return p; 1565 return p;
1257} 1566}
1261{ 1570{
1262 void *p; 1571 void *p;
1263 1572
1264 while ((p = malloc (size)) == NULL) 1573 while ((p = malloc (size)) == NULL)
1265 { 1574 {
1266 fprintf (stderr, "Memory exausted."); 1575 fprintf (stderr, "Memory exhausted in xmalloc ().\n");
1267 sleep (10); 1576 sleep (10);
1268 } 1577 }
1269 1578
1270 return p; 1579 return p;
1271} 1580}
1275{ 1584{
1276 void *p; 1585 void *p;
1277 1586
1278 while ((p = realloc (ptr, size)) == NULL) 1587 while ((p = realloc (ptr, size)) == NULL)
1279 { 1588 {
1280 fprintf (stderr, "Memory exausted."); 1589 fprintf (stderr, "Memory exhausted in xrealloc ().\n");
1281 sleep (10); 1590 sleep (10);
1282 } 1591 }
1283 1592
1284 return p; 1593 return p;
1285} 1594}
1286 1595
1287void 1596void
1288display_help (char *myname) 1597display_help (char *myname)
1289{ 1598{
1290 printf ("Usage: %s [options] file1[,color[,desc]] " 1599 printf ("Usage: %s [options] file1[,color[,desc]]"
1291 "[file2[,color[,desc]] ...]\n", myname); 1600 "[options] [file2[,color[,desc]] ...]\n", myname);
1292 printf (" -g | -geometry geometry -g WIDTHxHEIGHT+X+Y\n" 1601 printf (" -g | -geometry geometry -g WIDTHxHEIGHT+X+Y\n"
1293 " -color color use color $color as default\n" 1602 " -color color use color $color as default\n"
1294 " -reload sec command reload after $sec and run command\n" 1603 " -reload sec command reload after $sec and run command\n"
1295 " -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"
1296 " -font FONTSPEC (-fn) font to use\n" 1605 " -font FONTSPEC (-fn) font to use\n"
1298 " -reverse print new lines at the top\n" 1607 " -reverse print new lines at the top\n"
1299 " -whole wait for \\n before showing a line\n" 1608 " -whole wait for \\n before showing a line\n"
1300 " -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"
1301 " -update allow updates to old partial lines\n" 1610 " -update allow updates to old partial lines\n"
1302 " -cont string to prefix continued partial lines with\n" 1611 " -cont string to prefix continued partial lines with\n"
1612 " defaults to \"|| \"\n"
1303 " -wordwrap wrap long lines at spaces to avoid breaking words\n" 1613 " -wordwrap wrap long lines at spaces to avoid breaking words\n"
1304 " defaults to \"[+]\"\n"
1305 " -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"
1306 " -noinitial don't display the last file lines on\n" 1617 " -noinitial don't display the last file lines on\n"
1307 " startup\n" 1618 " startup\n"
1308 " -i | -interval seconds interval between checks (fractional\n" 1619 " -i | -interval seconds interval between checks (fractional\n"
1309 " values o.k.). Default 2.4 seconds\n" 1620 " values o.k.). Default 2.4 seconds\n"
1310 " -V display version information and exit\n" 1621 " -V display version information and exit\n"
1311 "\n"); 1622 "\n");
1312 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 "
1313 "/var/log/secure,red,'ALERT'\n", myname); 1624 "/var/log/secure,red,'ALERT'\n", myname);
1314 exit (0); 1625 exit (0);
1315} 1626}
1316 1627
1317void 1628void
1331 case -1: 1642 case -1:
1332 return -1; 1643 return -1;
1333 case 0: 1644 case 0:
1334 break; 1645 break;
1335 default: 1646 default:
1336 /*printf("%d\n", pid);*/ 1647 /*printf ("%d\n", pid);*/
1337 exit (0); 1648 exit (0);
1338 } 1649 }
1339 1650
1340 if (setsid () == -1) 1651 if (setsid () == -1)
1341 return -1; 1652 return -1;

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines