xref: /openbsd-src/usr.bin/mg/log.c (revision 7350f337b9e3eb4461d99580e625c7ef148d107c)
1 /*	$OpenBSD: log.c,v 1.8 2019/06/22 15:38:15 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 "log.h"
52 #include "funmap.h"
53 
54 char	*mglogfiles_create(char *);
55 int	 mglog_lines(PF);
56 int	 mglog_undo(void);
57 int	 mglog_window(void);
58 
59 char		*mglogdir;
60 extern char	*mglogpath_lines;
61 extern char	*mglogpath_undo;
62 extern char	*mglogpath_window;
63 int		 mgloglevel;
64 
65 int
66 mglog(PF funct)
67 {
68 	if(!mglog_lines(funct))
69 		ewprintf("Problem logging lines");
70 	if(!mglog_undo())
71 		ewprintf("Problem logging undo");
72 	if(!mglog_window())
73 		ewprintf("Problem logging window");
74 
75 	return (TRUE);
76 }
77 
78 
79 int
80 mglog_window(void)
81 {
82 	struct mgwin	*wp;
83 	struct stat	 sb;
84 	FILE		*fd;
85 	int		 i;
86 
87 	if(stat(mglogpath_window, &sb))
88 		return (FALSE);
89 	fd = fopen(mglogpath_window, "a");
90 
91 	for (wp = wheadp, i = 0; wp != NULL; wp = wp->w_wndp, ++i) {
92 		if (fprintf(fd,
93 		    "%d wh%p wlst%p wbfp%p wlp%p wdtp%p wmkp%p wdto%d wmko%d" \
94 		    " wtpr%d wntr%d wfrm%d wrfl%c wflg%c wwrl%p wdtl%d" \
95 		    " wmkl%d\n",
96 		    i,
97 		    wp,
98 		    &wp->w_list,
99 		    wp->w_bufp,
100 		    wp->w_linep,
101 		    wp->w_dotp,
102 		    wp->w_markp,
103 		    wp->w_doto,
104 		    wp->w_marko,
105 		    wp->w_toprow,
106 		    wp->w_ntrows,
107 		    wp->w_frame,
108 		    wp->w_rflag,
109 		    wp->w_flag,
110 		    wp->w_wrapline,
111 		    wp->w_dotline,
112 		    wp->w_markline) == -1) {
113 			fclose(fd);
114 			return (FALSE);
115 		}
116 	}
117 	fclose(fd);
118 	return (TRUE);
119 }
120 
121 int
122 mglog_undo(void)
123 {
124 	struct undo_rec	*rec;
125 	struct stat	 sb;
126 	FILE		*fd;
127 	char		 buf[4096], tmp[1024];
128 	int      	 num;
129 	char		*jptr;
130 
131 	jptr = "^J"; /* :) */
132 
133 	if(stat(mglogpath_undo, &sb))
134 		return (FALSE);
135 	fd = fopen(mglogpath_undo, "a");
136 
137 	/*
138 	 * From undo_dump()
139 	 */
140 	num = 0;
141 	TAILQ_FOREACH(rec, &curbp->b_undo, next) {
142 		num++;
143 		if (fprintf(fd, "%d:\t %s at %d ", num,
144 		    (rec->type == DELETE) ? "DELETE":
145 		    (rec->type == DELREG) ? "DELREGION":
146 		    (rec->type == INSERT) ? "INSERT":
147 		    (rec->type == BOUNDARY) ? "----" :
148 		    (rec->type == MODIFIED) ? "MODIFIED": "UNKNOWN",
149 		    rec->pos) == -1) {
150 			fclose(fd);
151 			return (FALSE);
152 		}
153 		if (rec->content) {
154 			(void)strlcat(buf, "\"", sizeof(buf));
155 			snprintf(tmp, sizeof(tmp), "%.*s",
156 			    *rec->content == '\n' ? 2 : rec->region.r_size,
157 			    *rec->content == '\n' ? jptr : rec->content);
158 			(void)strlcat(buf, tmp, sizeof(buf));
159 			(void)strlcat(buf, "\"", sizeof(buf));
160 		}
161 		snprintf(tmp, sizeof(tmp), " [%d]", rec->region.r_size);
162 		if (strlcat(buf, tmp, sizeof(buf)) >= sizeof(buf)) {
163 			dobeep();
164 			ewprintf("Undo record too large. Aborted.");
165 			return (FALSE);
166 		}
167 		if (fprintf(fd, "%s\n", buf) == -1) {
168 			fclose(fd);
169 			return (FALSE);
170 		}
171 		tmp[0] = buf[0] = '\0';
172 	}
173 	if (fprintf(fd, "\t [end-of-undo]\n\n") == -1) {
174 		fclose(fd);
175 		return (FALSE);
176 	}
177 	fclose(fd);
178 
179 	return (TRUE);
180 }
181 
182 int
183 mglog_lines(PF funct)
184 {
185 	struct line     *lp;
186 	struct stat      sb;
187 	char		*curline, *tmp, o;
188 	FILE            *fd;
189 	int		 i;
190 
191 	i = 0;
192 
193 	if(stat(mglogpath_lines, &sb))
194 		return (FALSE);
195 
196 	fd = fopen(mglogpath_lines, "a");
197 	if (fprintf(fd, "%s\n", function_name(funct)) == -1) {
198 		fclose(fd);
199 		return (FALSE);
200 	}
201 	lp = bfirstlp(curbp);
202 
203 	for(;;) {
204 		i++;
205 		curline = " ";
206 		o = ' ';
207 		if (i == curwp->w_dotline) {
208 			curline = ">";
209 			if (lp->l_used > 0 && curwp->w_doto < lp->l_used)
210 				o = lp->l_text[curwp->w_doto];
211 			else
212 				o = '-';
213 		}
214 		if (lp->l_size == 0)
215 			tmp = " ";
216 		else
217 			tmp = lp->l_text;
218 
219 		/* segv on fprintf below with long lines */
220 		if (fprintf(fd, "%s%p b^%p f.%p %d %d\t%c|%s\n", curline,
221 		    lp, lp->l_bp, lp->l_fp,
222 		    lp->l_size, lp->l_used, o, tmp) == -1) {
223 			fclose(fd);
224 			return (FALSE);
225 		}
226 		lp = lforw(lp);
227 		if (lp == curbp->b_headp) {
228 			if (fprintf(fd, " %p b^%p f.%p [bhead]\n(EOB)\n",
229 			    lp, lp->l_bp, lp->l_fp) == -1) {
230 				fclose(fd);
231         	                return (FALSE);
232 			}
233 			if (fprintf(fd, "lines:raw:%d buf:%d wdot:%d\n\n",
234 			    i, curbp->b_lines, curwp->w_dotline) == -1) {
235 				fclose(fd);
236         	                return (FALSE);
237 			}
238 			break;
239 		}
240 	}
241 	fclose(fd);
242 
243 	return (TRUE);
244 }
245 
246 
247 /*
248  * Make sure logging to log files can happen.
249  */
250 int
251 mgloginit(void)
252 {
253 	struct stat	 sb;
254 	mode_t           dir_mode, f_mode, oumask;
255 	char		*mglogfile_lines, *mglogfile_undo, *mglogfile_window;
256 
257 	mglogdir = "./log/";
258 	mglogfile_lines = "line.log";
259 	mglogfile_undo = "undo.log";
260 	mglogfile_window = "window.log";
261 
262 	/*
263 	 * Change mgloglevel for desired level of logging.
264 	 * log.h has relevant level info.
265 	 */
266 	mgloglevel = 1;
267 
268 	oumask = umask(0);
269 	f_mode = 0777& ~oumask;
270 	dir_mode = f_mode | S_IWUSR | S_IXUSR;
271 
272 	if(stat(mglogdir, &sb)) {
273 		if (mkdir(mglogdir, dir_mode) != 0)
274 			return (FALSE);
275 		if (chmod(mglogdir, f_mode) < 0)
276 			return (FALSE);
277 	}
278 	mglogpath_lines = mglogfiles_create(mglogfile_lines);
279 	if (mglogpath_lines == NULL)
280 		return (FALSE);
281 	mglogpath_undo = mglogfiles_create(mglogfile_undo);
282 	if (mglogpath_undo == NULL)
283 		return (FALSE);
284 	mglogpath_window = mglogfiles_create(mglogfile_window);
285 	if (mglogpath_window == NULL)
286 		return (FALSE);
287 
288 	return (TRUE);
289 }
290 
291 
292 char *
293 mglogfiles_create(char *mglogfile)
294 {
295 	struct stat	 sb;
296 	char		 tmp[20], *tmp2;
297 	int     	 fd;
298 
299 	if (strlcpy(tmp, mglogdir, sizeof(tmp)) >
300 	    sizeof(tmp))
301 		return (NULL);
302 	if (strlcat(tmp, mglogfile, sizeof(tmp)) >
303 	    sizeof(tmp))
304 		return (NULL);
305 	if ((tmp2 = strndup(tmp, 20)) == NULL)
306 		return (NULL);
307 
308 	if(stat(tmp2, &sb))
309 		fd = open(tmp2, O_RDWR | O_CREAT | O_TRUNC, 0644);
310 	else
311 		fd = open(tmp2, O_RDWR | O_TRUNC, 0644);
312 
313 	if (fd == -1)
314 		return (NULL);
315 
316 	close(fd);
317 
318 	return (tmp2);
319 }
320