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