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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines