1 /* $OpenBSD: log.c,v 1.13 2022/12/26 19:16:02 jmc Exp $ */
2
3 /*
4 * This file is in the public domain.
5 *
6 * Author: Mark Lumsden <mark@showcomplex.com>
7 *
8 */
9
10 /*
11 * Record a history of an mg session for temporal debugging.
12 * Sometimes pressing a key will set the scene for a bug only visible
13 * dozens of keystrokes later. gdb has its limitations in this scenario.
14 *
15 * Note this file is not compiled into mg by default, you will need to
16 * amend the 'Makefile' for that to happen. Because of this, the code
17 * is subject to bit-rot. However, I know myself and others have
18 * written similar functionally often enough, that recording the below
19 * in a code repository could aid the development efforts of mg, even
20 * if it requires a bit of effort to get working. The current code is
21 * written in the spirit of debugging (quickly and perhaps not ideal,
22 * but it does what is required well enough). Should debugging become
23 * more formalised within mg, then I would expect that to change.
24 *
25 * If you open a file with long lines to run through this debugging
26 * code, you may run into problems with the 1st fprintf statement in
27 * in the mglog_lines() function. mg sometimes segvs at a strlen call
28 * in fprintf - possibly something to do with the format string?
29 * "%s%p b^%p f.%p %d %d\t%c|%s\n"
30 * When I get time I will look into it. But since my debugging
31 * generally revolves around a file like:
32 *
33 * abc
34 * def
35 * ghk
36 *
37 * I don't experience this bug. Just note it for future investigation.
38 */
39
40 #include <sys/queue.h>
41 #include <sys/stat.h>
42 #include <ctype.h>
43 #include <fcntl.h>
44 #include <signal.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <stdarg.h>
50
51 #include "def.h"
52 #include "key.h"
53 #include "kbd.h"
54 #include "funmap.h"
55 #include "chrdef.h"
56
57 #include "log.h"
58
59 static char *mglogfiles_create(FILE **, char *);
60 static int mglog_lines(PF);
61 static int mglog_undo(void);
62 static int mglog_window(void);
63 static int mglog_key(KEYMAP *map);
64
65 const char *mglogdir;
66 const char *mglogpath_lines;
67 const char *mglogpath_undo;
68 const char *mglogpath_window;
69 const char *mglogpath_key;
70 const char *mglogpath_interpreter;
71 const char *mglogpath_misc;
72 int mgloglevel;
73
74 FILE *fd_lines;
75 FILE *fd_undo;
76 FILE *fd_window;
77 FILE *fd_key;
78 FILE *fd_interpreter;
79 FILE *fd_misc;
80
81 int
mglog(PF funct,void * map)82 mglog(PF funct, void *map)
83 {
84 if(!mglog_lines(funct))
85 ewprintf("Problem logging lines");
86 if(!mglog_undo())
87 ewprintf("Problem logging undo");
88 if(!mglog_window())
89 ewprintf("Problem logging window");
90 if(!mglog_key(map))
91 ewprintf("Problem logging key");
92
93 return (TRUE);
94 }
95
96
97 static int
mglog_key(KEYMAP * map)98 mglog_key(KEYMAP *map)
99 {
100 PF *pfp;
101
102 if (ISWORD(*key.k_chars)) {
103 fprintf(fd_key, "k_count:%d k_chars:%hd\tchr:%c\t", key.k_count,
104 *key.k_chars, CHARMASK(*key.k_chars));
105 } else {
106 fprintf(fd_key, "k_count:%d k_chars:%hd\t\t", key.k_count,
107 *key.k_chars);
108 }
109 fprintf(fd_key, "map:%p %d %d %p %hd %hd\n",
110 map,
111 map->map_num,
112 map->map_max,
113 map->map_default,
114 map->map_element->k_base,
115 map->map_element->k_num
116 );
117 for (pfp = map->map_element->k_funcp; *pfp != NULL; pfp++)
118 fprintf(fd_key, "%s ", function_name(*pfp));
119
120 fprintf(fd_key, "\n\n");
121 fflush(fd_key);
122 return (TRUE);
123 }
124
125 static int
mglog_window(void)126 mglog_window(void)
127 {
128 struct mgwin *wp;
129 int i;
130
131 for (wp = wheadp, i = 0; wp != NULL; wp = wp->w_wndp, ++i) {
132 fprintf(fd_window,
133 "%d wh%p wlst%p wbfp%p wlp%p wdtp%p wmkp%p wdto%d wmko%d" \
134 " wtpr%d wntr%d wfrm%d wrfl%c wflg%c wwrl%p wdtl%d" \
135 " wmkl%d\n",
136 i,
137 wp,
138 &wp->w_list,
139 wp->w_bufp,
140 wp->w_linep,
141 wp->w_dotp,
142 wp->w_markp,
143 wp->w_doto,
144 wp->w_marko,
145 wp->w_toprow,
146 wp->w_ntrows,
147 wp->w_frame,
148 wp->w_rflag,
149 wp->w_flag,
150 wp->w_wrapline,
151 wp->w_dotline,
152 wp->w_markline
153 );
154 }
155 fflush(fd_window);
156 return (TRUE);
157 }
158
159 static int
mglog_undo(void)160 mglog_undo(void)
161 {
162 struct undo_rec *rec;
163 char buf[4096], tmp[1024];
164 int num;
165 char *jptr;
166
167 jptr = "^J"; /* :) */
168 /*
169 * From undo_dump()
170 */
171 num = 0;
172 TAILQ_FOREACH(rec, &curbp->b_undo, next) {
173 num++;
174 fprintf(fd_undo, "%d:\t %s at %d ", num,
175 (rec->type == DELETE) ? "DELETE":
176 (rec->type == DELREG) ? "DELREGION":
177 (rec->type == INSERT) ? "INSERT":
178 (rec->type == BOUNDARY) ? "----" :
179 (rec->type == MODIFIED) ? "MODIFIED": "UNKNOWN",
180 rec->pos
181 );
182 if (rec->content) {
183 (void)strlcat(buf, "\"", sizeof(buf));
184 snprintf(tmp, sizeof(tmp), "%.*s",
185 *rec->content == '\n' ? 2 : rec->region.r_size,
186 *rec->content == '\n' ? jptr : rec->content);
187 (void)strlcat(buf, tmp, sizeof(buf));
188 (void)strlcat(buf, "\"", sizeof(buf));
189 }
190 snprintf(tmp, sizeof(tmp), " [%d]", rec->region.r_size);
191 if (strlcat(buf, tmp, sizeof(buf)) >= sizeof(buf)) {
192 dobeep();
193 ewprintf("Undo record too large. Aborted.");
194 return (FALSE);
195 }
196 fprintf(fd_undo, "%s\n", buf);
197 tmp[0] = buf[0] = '\0';
198 }
199 fprintf(fd_undo, "\t [end-of-undo]\n\n");
200 fflush(fd_undo);
201
202 return (TRUE);
203 }
204
205 static int
mglog_lines(PF funct)206 mglog_lines(PF funct)
207 {
208 struct line *lp;
209 char *curline, *tmp, o;
210 int i;
211
212 i = 0;
213
214 fprintf(fd_lines, "%s\n", function_name(funct));
215 lp = bfirstlp(curbp);
216
217 for(;;) {
218 i++;
219 curline = " ";
220 o = ' ';
221 if (i == curwp->w_dotline) {
222 curline = ">";
223 if (lp->l_used > 0 && curwp->w_doto < lp->l_used)
224 o = lp->l_text[curwp->w_doto];
225 else
226 o = '-';
227 }
228 if (lp->l_size == 0)
229 tmp = " ";
230 else
231 tmp = lp->l_text;
232
233 /* segv on fprintf below with long lines */
234 fprintf(fd_lines, "%s%p b^%p f.%p %d %d\t%c|%s\n", curline,
235 lp, lp->l_bp, lp->l_fp,
236 lp->l_size, lp->l_used, o, tmp);
237
238 lp = lforw(lp);
239 if (lp == curbp->b_headp) {
240 fprintf(fd_lines, " %p b^%p f.%p [bhead]\n(EOB)\n",
241 lp, lp->l_bp, lp->l_fp);
242
243 fprintf(fd_lines, "lines:raw:%d buf:%d wdot:%d\n\n",
244 i, curbp->b_lines, curwp->w_dotline);
245
246 break;
247 }
248 }
249 fflush(fd_lines);
250
251 return (TRUE);
252 }
253
254 /*
255 * See what the eval variable code is up to.
256 */
257 int
mglog_isvar(const char * const argbuf,const char * const argp,const int sizof)258 mglog_isvar(
259 const char* const argbuf,
260 const char* const argp,
261 const int sizof
262 )
263 {
264
265 fprintf(fd_interpreter, " argbuf:%s,argp:%s,sizof:%d<\n",
266 argbuf,
267 argp,
268 sizof);
269
270 fflush(fd_interpreter);
271 return (TRUE);
272 }
273
274 /*
275 * See what the eval line code is up to.
276 */
277 int
mglog_execbuf(const char * const pre,const char * const excbuf,const char * const argbuf,const char * const argp,const int last,const int inlist,const char * const cmdp,const char * const p,const char * const contbuf)278 mglog_execbuf(
279 const char* const pre,
280 const char* const excbuf,
281 const char* const argbuf,
282 const char* const argp,
283 const int last,
284 const int inlist,
285 const char* const cmdp,
286 const char* const p,
287 const char* const contbuf
288 )
289 {
290 fprintf(fd_interpreter, "%sexcbuf:%s,argbuf:%s,argp:%s,last:%d,inlist:%d,"\
291 "cmdp:%s,p:%s,contbuf:%s<\n",
292 pre,
293 excbuf,
294 argbuf,
295 argp,
296 last,
297 inlist,
298 cmdp,
299 p,
300 contbuf
301 );
302 fflush(fd_interpreter);
303 return (TRUE);
304 }
305
306 /*
307 * Misc. logging for various subsystems
308 */
309 int
mglog_misc(const char * fmt,...)310 mglog_misc(
311 const char *fmt,
312 ...
313 )
314 {
315 va_list ap;
316 int rc;
317
318 va_start(ap, fmt);
319 rc = vfprintf(fd_misc, fmt, ap);
320 va_end(ap);
321 fflush(fd_misc);
322
323 if (rc < 0)
324 return (FALSE);
325
326 return (TRUE);
327 }
328
329
330
331 /*
332 * Make sure logging to log files can happen.
333 */
334 int
mgloginit(void)335 mgloginit(void)
336 {
337 struct stat sb;
338 mode_t dir_mode, f_mode, oumask;
339 char *mglogfile_lines, *mglogfile_undo, *mglogfile_window;
340 char *mglogfile_key, *mglogfile_interpreter, *mglogfile_misc;
341
342 mglogdir = "./log/";
343 mglogfile_lines = "line.log";
344 mglogfile_undo = "undo.log";
345 mglogfile_window = "window.log";
346 mglogfile_key = "key.log";
347 mglogfile_interpreter = "interpreter.log";
348 mglogfile_misc = "misc.log";
349
350 /*
351 * Change mgloglevel for desired level of logging.
352 * log.h has relevant level info.
353 */
354 mgloglevel = 1;
355
356 oumask = umask(0);
357 f_mode = 0777& ~oumask;
358 dir_mode = f_mode | S_IWUSR | S_IXUSR;
359
360 if(stat(mglogdir, &sb)) {
361 if (mkdir(mglogdir, dir_mode) != 0)
362 return (FALSE);
363 if (chmod(mglogdir, f_mode) == -1)
364 return (FALSE);
365 }
366 mglogpath_lines = mglogfiles_create(&fd_lines, mglogfile_lines);
367 if (mglogpath_lines == NULL)
368 return (FALSE);
369 mglogpath_undo = mglogfiles_create(&fd_undo, mglogfile_undo);
370 if (mglogpath_undo == NULL)
371 return (FALSE);
372 mglogpath_window = mglogfiles_create(&fd_window, mglogfile_window);
373 if (mglogpath_window == NULL)
374 return (FALSE);
375 mglogpath_key = mglogfiles_create(&fd_key, mglogfile_key);
376 if (mglogpath_key == NULL)
377 return (FALSE);
378 mglogpath_interpreter = mglogfiles_create(&fd_interpreter,
379 mglogfile_interpreter);
380 if (mglogpath_interpreter == NULL)
381 return (FALSE);
382 mglogpath_misc = mglogfiles_create(&fd_misc, mglogfile_misc);
383 if (mglogpath_misc == NULL)
384 return (FALSE);
385
386 return (TRUE);
387 }
388
389
390 static char *
mglogfiles_create(FILE ** fd,char * mglogfile)391 mglogfiles_create(FILE ** fd, char *mglogfile)
392 {
393 char tmp[NFILEN], *tmp2;
394
395 if (strlcpy(tmp, mglogdir, sizeof(tmp)) >
396 sizeof(tmp))
397 return (NULL);
398 if (strlcat(tmp, mglogfile, sizeof(tmp)) >
399 sizeof(tmp))
400 return (NULL);
401 if ((tmp2 = strndup(tmp, NFILEN)) == NULL)
402 return (NULL);
403
404 if ((*fd = fopen(tmp2, "w")) == NULL)
405 return (NULL);
406
407 return (tmp2);
408 }
409