xref: /openbsd/usr.bin/mg/log.c (revision 76d0caae)
1 /*	$OpenBSD: log.c,v 1.12 2021/03/02 13:06:50 lum 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 developement 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
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
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
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
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
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
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
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
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
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 *
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