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