xref: /openbsd/usr.bin/mg/log.c (revision 09467b48)
1 /*	$OpenBSD: log.c,v 1.11 2019/07/18 10:50:24 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 
50 #include "def.h"
51 #include "key.h"
52 #include "kbd.h"
53 #include "funmap.h"
54 #include "chrdef.h"
55 
56 #include "log.h"
57 
58 static char	*mglogfiles_create(char *);
59 static int	 mglog_lines(PF);
60 static int	 mglog_undo(void);
61 static int	 mglog_window(void);
62 static int	 mglog_key(KEYMAP *map);
63 
64 char		*mglogdir;
65 extern char	*mglogpath_lines;
66 extern char	*mglogpath_undo;
67 extern char	*mglogpath_window;
68 extern char	*mglogpath_key;
69 extern char     *mglogpath_interpreter;
70 int		 mgloglevel;
71 
72 int
73 mglog(PF funct, KEYMAP *map)
74 {
75 	if(!mglog_lines(funct))
76 		ewprintf("Problem logging lines");
77 	if(!mglog_undo())
78 		ewprintf("Problem logging undo");
79 	if(!mglog_window())
80 		ewprintf("Problem logging window");
81 	if(!mglog_key(map))
82 		ewprintf("Problem logging key");
83 
84 	return (TRUE);
85 }
86 
87 
88 static int
89 mglog_key(KEYMAP *map)
90 {
91 	struct stat      sb;
92 	FILE            *fd;
93 	PF		*pfp;
94 
95 	if(stat(mglogpath_key, &sb))
96 		 return (FALSE);
97 	fd = fopen(mglogpath_key, "a");
98 
99 	if (ISWORD(*key.k_chars)) {
100 		if (fprintf(fd, "k_count:%d k_chars:%hd\tchr:%c\t", key.k_count,
101 		    *key.k_chars, CHARMASK(*key.k_chars)) == -1) {
102 			fclose(fd);
103 			return (FALSE);
104 		}
105 	} else {
106 		if (fprintf(fd, "k_count:%d k_chars:%hd\t\t", key.k_count,
107 		    *key.k_chars) == -1) {
108 			fclose(fd);
109 			return (FALSE);
110 		}
111 	}
112 	if (fprintf(fd, "map:%p %d %d %p %hd %hd\n",
113 	    map,
114 	    map->map_num,
115 	    map->map_max,
116 	    map->map_default,
117 	    map->map_element->k_base,
118 	    map->map_element->k_num
119 	    ) == -1) {
120 		fclose(fd);
121 		return (FALSE);
122 	}
123 	for (pfp = map->map_element->k_funcp; *pfp != '\0'; pfp++)
124 		fprintf(fd, "%s ", function_name(*pfp));
125 
126 	fprintf(fd, "\n\n");
127 	fclose(fd);
128 	return (TRUE);
129 }
130 
131 static int
132 mglog_window(void)
133 {
134 	struct mgwin	*wp;
135 	struct stat	 sb;
136 	FILE		*fd;
137 	int		 i;
138 
139 	if(stat(mglogpath_window, &sb))
140 		return (FALSE);
141 	fd = fopen(mglogpath_window, "a");
142 
143 	for (wp = wheadp, i = 0; wp != NULL; wp = wp->w_wndp, ++i) {
144 		if (fprintf(fd,
145 		    "%d wh%p wlst%p wbfp%p wlp%p wdtp%p wmkp%p wdto%d wmko%d" \
146 		    " wtpr%d wntr%d wfrm%d wrfl%c wflg%c wwrl%p wdtl%d" \
147 		    " wmkl%d\n",
148 		    i,
149 		    wp,
150 		    &wp->w_list,
151 		    wp->w_bufp,
152 		    wp->w_linep,
153 		    wp->w_dotp,
154 		    wp->w_markp,
155 		    wp->w_doto,
156 		    wp->w_marko,
157 		    wp->w_toprow,
158 		    wp->w_ntrows,
159 		    wp->w_frame,
160 		    wp->w_rflag,
161 		    wp->w_flag,
162 		    wp->w_wrapline,
163 		    wp->w_dotline,
164 		    wp->w_markline) == -1) {
165 			fclose(fd);
166 			return (FALSE);
167 		}
168 	}
169 	fclose(fd);
170 	return (TRUE);
171 }
172 
173 static int
174 mglog_undo(void)
175 {
176 	struct undo_rec	*rec;
177 	struct stat	 sb;
178 	FILE		*fd;
179 	char		 buf[4096], tmp[1024];
180 	int      	 num;
181 	char		*jptr;
182 
183 	jptr = "^J"; /* :) */
184 
185 	if(stat(mglogpath_undo, &sb))
186 		return (FALSE);
187 	fd = fopen(mglogpath_undo, "a");
188 
189 	/*
190 	 * From undo_dump()
191 	 */
192 	num = 0;
193 	TAILQ_FOREACH(rec, &curbp->b_undo, next) {
194 		num++;
195 		if (fprintf(fd, "%d:\t %s at %d ", num,
196 		    (rec->type == DELETE) ? "DELETE":
197 		    (rec->type == DELREG) ? "DELREGION":
198 		    (rec->type == INSERT) ? "INSERT":
199 		    (rec->type == BOUNDARY) ? "----" :
200 		    (rec->type == MODIFIED) ? "MODIFIED": "UNKNOWN",
201 		    rec->pos) == -1) {
202 			fclose(fd);
203 			return (FALSE);
204 		}
205 		if (rec->content) {
206 			(void)strlcat(buf, "\"", sizeof(buf));
207 			snprintf(tmp, sizeof(tmp), "%.*s",
208 			    *rec->content == '\n' ? 2 : rec->region.r_size,
209 			    *rec->content == '\n' ? jptr : rec->content);
210 			(void)strlcat(buf, tmp, sizeof(buf));
211 			(void)strlcat(buf, "\"", sizeof(buf));
212 		}
213 		snprintf(tmp, sizeof(tmp), " [%d]", rec->region.r_size);
214 		if (strlcat(buf, tmp, sizeof(buf)) >= sizeof(buf)) {
215 			dobeep();
216 			ewprintf("Undo record too large. Aborted.");
217 			return (FALSE);
218 		}
219 		if (fprintf(fd, "%s\n", buf) == -1) {
220 			fclose(fd);
221 			return (FALSE);
222 		}
223 		tmp[0] = buf[0] = '\0';
224 	}
225 	if (fprintf(fd, "\t [end-of-undo]\n\n") == -1) {
226 		fclose(fd);
227 		return (FALSE);
228 	}
229 	fclose(fd);
230 
231 	return (TRUE);
232 }
233 
234 static int
235 mglog_lines(PF funct)
236 {
237 	struct line     *lp;
238 	struct stat      sb;
239 	char		*curline, *tmp, o;
240 	FILE            *fd;
241 	int		 i;
242 
243 	i = 0;
244 
245 	if(stat(mglogpath_lines, &sb))
246 		return (FALSE);
247 
248 	fd = fopen(mglogpath_lines, "a");
249 	if (fprintf(fd, "%s\n", function_name(funct)) == -1) {
250 		fclose(fd);
251 		return (FALSE);
252 	}
253 	lp = bfirstlp(curbp);
254 
255 	for(;;) {
256 		i++;
257 		curline = " ";
258 		o = ' ';
259 		if (i == curwp->w_dotline) {
260 			curline = ">";
261 			if (lp->l_used > 0 && curwp->w_doto < lp->l_used)
262 				o = lp->l_text[curwp->w_doto];
263 			else
264 				o = '-';
265 		}
266 		if (lp->l_size == 0)
267 			tmp = " ";
268 		else
269 			tmp = lp->l_text;
270 
271 		/* segv on fprintf below with long lines */
272 		if (fprintf(fd, "%s%p b^%p f.%p %d %d\t%c|%s\n", curline,
273 		    lp, lp->l_bp, lp->l_fp,
274 		    lp->l_size, lp->l_used, o, tmp) == -1) {
275 			fclose(fd);
276 			return (FALSE);
277 		}
278 		lp = lforw(lp);
279 		if (lp == curbp->b_headp) {
280 			if (fprintf(fd, " %p b^%p f.%p [bhead]\n(EOB)\n",
281 			    lp, lp->l_bp, lp->l_fp) == -1) {
282 				fclose(fd);
283         	                return (FALSE);
284 			}
285 			if (fprintf(fd, "lines:raw:%d buf:%d wdot:%d\n\n",
286 			    i, curbp->b_lines, curwp->w_dotline) == -1) {
287 				fclose(fd);
288         	                return (FALSE);
289 			}
290 			break;
291 		}
292 	}
293 	fclose(fd);
294 
295 	return (TRUE);
296 }
297 
298 /*
299  * See what the eval variable code is up to.
300  */
301 int
302 mglog_isvar(
303 	const char* const argbuf,
304 	const char* const argp,
305 	const int 	  sizof
306 )
307 {
308 	FILE		*fd;
309 
310 	fd = fopen(mglogpath_interpreter, "a");
311 
312 	if (fprintf(fd, " argbuf:%s,argp:%s,sizof:%d<\n",
313 	    argbuf,
314 	    argp,
315 	    sizof
316 	    ) == -1) {
317 		fclose(fd);
318 		return (FALSE);
319 	}
320 	fclose(fd);
321 	return (TRUE);
322 }
323 
324 /*
325  * See what the eval line code is up to.
326  */
327 int
328 mglog_execbuf(
329 	const char* const pre,
330 	const char* const excbuf,
331 	const char* const argbuf,
332     	const char* const argp,
333 	const int 	  last,
334 	const int	  inlist,
335     	const char* const cmdp,
336 	const char* const p,
337 	const char* const contbuf
338 )
339 {
340 	FILE		*fd;
341 
342 	fd = fopen(mglogpath_interpreter, "a");
343 
344 	if (fprintf(fd, "%sexcbuf:%s,argbuf:%s,argp:%s,last:%d,inlist:%d,"\
345 	    "cmdp:%s,p:%s,contbuf:%s<\n",
346 	    pre,
347 	    excbuf,
348 	    argbuf,
349 	    argp,
350 	    last,
351 	    inlist,
352 	    cmdp,
353 	    p,
354 	    contbuf
355 	    ) == -1) {
356 		fclose(fd);
357 		return (FALSE);
358 	}
359 	fclose(fd);
360 	return (TRUE);
361 }
362 
363 /*
364  * Make sure logging to log files can happen.
365  */
366 int
367 mgloginit(void)
368 {
369 	struct stat	 sb;
370 	mode_t           dir_mode, f_mode, oumask;
371 	char		*mglogfile_lines, *mglogfile_undo, *mglogfile_window;
372 	char		*mglogfile_key, *mglogfile_interpreter;
373 
374 	mglogdir = "./log/";
375 	mglogfile_lines = "line.log";
376 	mglogfile_undo = "undo.log";
377 	mglogfile_window = "window.log";
378 	mglogfile_key = "key.log";
379 	mglogfile_interpreter = "interpreter.log";
380 
381 	/*
382 	 * Change mgloglevel for desired level of logging.
383 	 * log.h has relevant level info.
384 	 */
385 	mgloglevel = 1;
386 
387 	oumask = umask(0);
388 	f_mode = 0777& ~oumask;
389 	dir_mode = f_mode | S_IWUSR | S_IXUSR;
390 
391 	if(stat(mglogdir, &sb)) {
392 		if (mkdir(mglogdir, dir_mode) != 0)
393 			return (FALSE);
394 		if (chmod(mglogdir, f_mode) == -1)
395 			return (FALSE);
396 	}
397 	mglogpath_lines = mglogfiles_create(mglogfile_lines);
398 	if (mglogpath_lines == NULL)
399 		return (FALSE);
400 	mglogpath_undo = mglogfiles_create(mglogfile_undo);
401 	if (mglogpath_undo == NULL)
402 		return (FALSE);
403 	mglogpath_window = mglogfiles_create(mglogfile_window);
404 	if (mglogpath_window == NULL)
405 		return (FALSE);
406 	mglogpath_key = mglogfiles_create(mglogfile_key);
407 	if (mglogpath_key == NULL)
408 		return (FALSE);
409 	mglogpath_interpreter = mglogfiles_create(mglogfile_interpreter);
410 	if (mglogpath_interpreter == NULL)
411 		return (FALSE);
412 
413 	return (TRUE);
414 }
415 
416 
417 static char *
418 mglogfiles_create(char *mglogfile)
419 {
420 	struct stat	 sb;
421 	char		 tmp[NFILEN], *tmp2;
422 	int     	 fd;
423 
424 	if (strlcpy(tmp, mglogdir, sizeof(tmp)) >
425 	    sizeof(tmp))
426 		return (NULL);
427 	if (strlcat(tmp, mglogfile, sizeof(tmp)) >
428 	    sizeof(tmp))
429 		return (NULL);
430 	if ((tmp2 = strndup(tmp, NFILEN)) == NULL)
431 		return (NULL);
432 
433 	if(stat(tmp2, &sb))
434 		fd = open(tmp2, O_RDWR | O_CREAT | O_TRUNC, 0644);
435 	else
436 		fd = open(tmp2, O_RDWR | O_TRUNC, 0644);
437 
438 	if (fd == -1)
439 		return (NULL);
440 
441 	close(fd);
442 
443 	return (tmp2);
444 }
445 
446 /*
447  * Template log function.
448  */
449 /*
450 int
451 mglog_?(void)
452 {
453 	struct stat      sb;
454 	FILE            *fd;
455 
456 	if(stat(mglogpath_?, &sb))
457 	fd = fopen(mglogpath_?, "a");
458 
459 	if (fprintf(fd, "%?", ??) == -1) {
460 		fclose(fd);
461 		return (FALSE);
462 	}
463 	fclose(fd);
464 	return (TRUE);
465 }
466 */
467