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