1 /*
2  *  log.c --  code for #movie / #capture backbuffering
3  *            and code for reprint-on-prompt
4  *
5  *  Copyright (C) 1998 by Massimiliano Ghilardi
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  */
13 
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18 #include <time.h>
19 
20 #include "defines.h"
21 #include "main.h"
22 #include "log.h"
23 #include "tty.h"
24 #include "list.h"
25 #include "utils.h"
26 
27 vtime movie_last;		     /* time movie_file was last written */
28 FILE *capturefile = (FILE *)NULL;    /* capture file or NULL */
29 FILE *moviefile = (FILE *)NULL;	     /* movie file or NULL */
30 FILE *recordfile = (FILE *)NULL;     /* record file or NULL */
31 
32 
33 static char *datalist;		/* circular string list */
34 static int datastart = 0;	/* index to first string start */
35 static int dataend   = 0;	/* index one past last string end */
36 static int datasize  = 0;	/* size of circular string list */
37 
38 #define DATALEFT (datastart > dataend ? datastart - dataend - 2 : datastart ? \
39 		MAX2(datastart - 1, datasize - dataend - 1) : datasize - dataend - 1)
40 
41 typedef struct logentry {
42     enum linetype kind;
43     long msecs;			/* millisecs to sleep if kind == SLEEP */
44     char *line;			/* pointer to string in "datalist" circular buffer */
45 } logentry;
46 
47 logentry *loglist;		/* circular (pointer to string) list */
48 
49 static int logstart = 0;	/* index to first loglist used */
50 static int logend   = 0;	/* index one past last loglist used */
51 static int logsize  = 0;	/* size of circular (pointer to string) list */
52 
53 #define LOGFULL (logend == (logstart ? logstart - 1 : logsize - 1))
54 
55 static char *names[] = { NULL, "line", "prompt", "sleep" };
56 
57 /*
58  * flush a single buffer line
59  */
__P1(int,i)60 static void log_flushline __P1 (int,i)
61 {
62     if (capturefile)
63 	fprintf(capturefile, "%s%s",
64 		loglist[i].line, loglist[i].kind == LINE ? "\n" : "");
65     if (moviefile) {
66 	if (loglist[i].msecs)
67 	    fprintf(moviefile, "%s %ld\n",
68 		    names[SLEEP], loglist[i].msecs);
69 	fprintf(moviefile, "%s %s\n",
70 		names[loglist[i].kind], loglist[i].line);
71     }
72 }
73 
74 /*
75  * remove the oldest (first) line from the buffer
76  */
__P0(void)77 static void log_clearline __P0 (void)
78 {
79     int next;
80 
81     if (logstart == logend)
82 	return;
83     log_flushline(logstart);
84 
85     next = (logstart + 1) % logsize;
86     if (next == logend)
87 	datastart = dataend = logstart = logend = 0;
88     else
89 	datastart = loglist[next].line - datalist, logstart = next;
90 }
91 
92 /*
93  * remove an initial SLEEP from the buffer
94  */
__P0(void)95 void log_clearsleep __P0 (void)
96 {
97     if (logstart != logend)
98 	loglist[logstart].msecs = 0;
99 }
100 
101 /*
102  * flush the buffer
103  */
__P0(void)104 void log_flush __P0 (void)
105 {
106     int i = logstart;
107     while (i != logend) {
108 	log_flushline(i);
109 	if (++i == logsize)
110 	    i = 0;
111     }
112     datastart = dataend = logstart = logend = 0;
113 }
114 
__P0(void)115 int log_getsize __P0 (void)
116 {
117     return datasize;
118 }
119 
__P0(void)120 static void log_reset __P0 (void)
121 {
122     if (datasize) {
123 	if (datalist) free(datalist);
124 	if (loglist)  free(loglist);
125 	loglist = NULL;
126 	datalist = NULL;
127 	logsize = datasize = 0;
128     }
129 }
130 
131 
__P1(int,newsize)132 void log_resize __P1 (int,newsize)
133 {
134     if (newsize && newsize < 1000) {
135 	PRINTF("#buffer size must be 0 (zero) or >= 1000\n");
136 	return;
137     }
138 
139     if (newsize == datasize)
140 	return;
141 
142     log_flush();
143     log_reset();
144     if (newsize) {
145 	datalist = (char *)malloc(newsize);
146 	if (!datalist) { log_reset(); errmsg("malloc"); return; }
147 
148 	loglist = (logentry *)malloc(newsize/16*sizeof(logentry));
149 	if (!loglist) { log_reset(); errmsg("malloc"); return; }
150 
151 	datasize = newsize;
152 	logsize = newsize / 16;
153     }
154     if (opt_info) {
155 	PRINTF("#buffer resized to %d bytes%s\n", newsize, newsize ? "" : " (disabled)");
156     }
157 }
158 
159 /*
160  * add a single line to the buffer
161  */
__P4(char *,line,int,len,int,kind,long,msecs)162 static void log_writeline __P4 (char *,line, int,len, int,kind, long,msecs)
163 {
164     int dst;
165 
166     if (++len >= datasize) {
167 	PRINTF("#line too long, discarded from movie/capture buffer\n");
168 	return;
169     }
170     while (LOGFULL || DATALEFT < len)
171 	log_clearline();
172     /* ok, now we know there IS enough space */
173 
174     if (datastart >= dataend /* is == iff loglist is empty */
175 	|| datasize - dataend > len)
176 	dst = dataend;
177     else
178 	dst = 0;
179 
180     memcpy(loglist[logend].line = datalist + dst, line, len - 1);
181     datalist[dst + len - 1] = '\0';
182 
183     loglist[logend].kind = kind;
184     loglist[logend].msecs = msecs;
185 
186     if ((dataend = dst + len) == datasize)
187 	dataend = 0;
188 
189     if (++logend == logsize)
190 	logend = 0;
191 }
192 
193 /*
194  * write to #capture / #movie buffer
195  */
__P3(char *,str,int,len,int,newline)196 void log_write __P3 (char *,str, int,len, int,newline)
197 {
198     char *next;
199     long diff;
200     int i, last = 0;
201 
202     if (!datasize && !moviefile && !capturefile)
203 	return;
204 
205     update_now();
206     diff = diff_vtime(&now, &movie_last);
207     movie_last = now;
208 
209     do {
210 	if ((next = memchr(str, '\n', len))) {
211 	    i = next - str;
212             newline = 1;
213         } else {
214 	    i = len;
215             last = 1;
216 	}
217 
218 	if (datasize)
219 	    log_writeline(str, i, last && !newline ? PROMPT : LINE, diff);
220 	else {
221 	    if (moviefile) {
222 		if (diff)
223 		    fprintf(moviefile, "%s %ld\n",
224 			names[SLEEP], diff);
225 		fprintf(moviefile, "%s %.*s\n",
226 			names[last && !newline ? PROMPT : LINE], i, str);
227 	    }
228 	    if (capturefile) {
229                 fwrite(str, 1, i, capturefile);
230                 if (newline) {
231                     const char nl[1] = "\n";
232                     fwrite(nl, 1, 1, capturefile);
233                 }
234             }
235 	}
236 	diff = 0;
237 	if (next) {
238 	    len -= next + 1 - str;
239 	    str = next + 1;
240 	}
241     } while (next && len > 0);
242 }
243 
244 static char reprintlist[BUFSIZE];	/* circular string list */
245 static int  reprintstart = 0;		/* index to first string start */
246 static int  reprintend   = 0;		/* index one past last string end */
247 static int  reprintsize  = BUFSIZE;	/* size of circular string list */
248 
249 #define REPRINTLEFT (reprintstart > reprintend ? reprintstart - reprintend - 1 : reprintstart ? \
250 			MAX2(reprintstart - 1, reprintsize - reprintend - 1) : reprintsize - reprintend - 1)
251 
252 static char *replist[BUFSIZE/8];	/* circular (pointer to string) list */
253 
254 static int repstart = 0;		/* index to first replist used */
255 static int repend   = 0;		/* index one past last replist used */
256 static int repsize  = BUFSIZE/8;	/* size of circular (pointer to string) list */
257 
258 #define REPFULL (repend == (repstart ? repstart - 1 : repsize - 1))
259 
260 /*
261  * remove the oldest (first) line from reprintlist
262  */
__P0(void)263 static void reprint_clearline __P0 (void)
264 {
265     int next;
266 
267     if (repstart == repend)
268 	return;
269 
270     next = (repstart + 1) % repsize;
271     if (next == repend)
272 	reprintstart = reprintend = repstart = repend = 0;
273     else
274 	reprintstart = replist[next] - reprintlist, repstart = next;
275 }
276 
__P0(void)277 void reprint_clear __P0 (void)
278 {
279     reprintstart = reprintend = repstart = repend = 0;
280 }
281 
282 /*
283  * add a single line to the buffer
284  */
__P1(char *,line)285 void reprint_writeline __P1 (char *,line)
286 {
287     int len = strlen(line) + 1;
288     int dst;
289 
290     if (!opt_reprint || (promptlen && prompt_status != -1))
291 	/*
292 	 * if prompt is valid, we'll never have to reprint, as we
293 	 * _already_ printed the command at the right moment
294 	 */
295 	return;
296 
297     if (len >= reprintsize) {
298 	PRINTF("#line too long, discarded from prompt reprint buffer\n");
299 	return;
300     }
301     while (REPFULL || REPRINTLEFT < len)
302 	reprint_clearline();
303     /* ok, now we know there IS enough space */
304 
305     if (reprintstart >= reprintend /* is == iff replist is empty */
306 	|| reprintsize - reprintend > len)
307 	dst = reprintend;
308     else
309 	dst = 0;
310 
311     memcpy(replist[repend] = reprintlist + dst, line, len - 1);
312     reprintlist[dst + len - 1] = '\0';
313 
314     if ((reprintend = dst + len) == reprintsize)
315 	reprintend = 0;
316 
317     if (++repend == repsize)
318 	repend = 0;
319 }
320 
__P0(void)321 char *reprint_getline __P0 (void)
322 {
323     char *line = NULL;
324     if (opt_reprint && repend != repstart)
325 	line = replist[repstart];
326     reprint_clearline();
327     return line;
328 }
329 
330