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