Changeset View
Changeset View
Standalone View
Standalone View
contrib/mg/log.c
- This file was added.
| /* $OpenBSD: log.c,v 1.12 2021/03/02 13:06:50 lum Exp $ */ | |||||
| /* | |||||
| * This file is in the public domain. | |||||
| * | |||||
| * Author: Mark Lumsden <mark@showcomplex.com> | |||||
| * | |||||
| */ | |||||
| /* | |||||
| * Record a history of an mg session for temporal debugging. | |||||
| * Sometimes pressing a key will set the scene for a bug only visible | |||||
| * dozens of keystrokes later. gdb has its limitations in this scenario. | |||||
| * | |||||
| * Note this file is not compiled into mg by default, you will need to | |||||
| * amend the 'Makefile' for that to happen. Because of this, the code | |||||
| * is subject to bit-rot. However, I know myself and others have | |||||
| * written similar functionally often enough, that recording the below | |||||
| * in a code repository could aid the developement efforts of mg, even | |||||
| * if it requires a bit of effort to get working. The current code is | |||||
| * written in the spirit of debugging (quickly and perhaps not ideal, | |||||
| * but it does what is required well enough). Should debugging become | |||||
| * more formalised within mg, then I would expect that to change. | |||||
| * | |||||
| * If you open a file with long lines to run through this debugging | |||||
| * code, you may run into problems with the 1st fprintf statement in | |||||
| * in the mglog_lines() function. mg sometimes segvs at a strlen call | |||||
| * in fprintf - possibly something to do with the format string? | |||||
| * "%s%p b^%p f.%p %d %d\t%c|%s\n" | |||||
| * When I get time I will look into it. But since my debugging | |||||
| * generally revolves around a file like: | |||||
| * | |||||
| * abc | |||||
| * def | |||||
| * ghk | |||||
| * | |||||
| * I don't experience this bug. Just note it for future investigation. | |||||
| */ | |||||
| #include <sys/queue.h> | |||||
| #include <sys/stat.h> | |||||
| #include <ctype.h> | |||||
| #include <fcntl.h> | |||||
| #include <signal.h> | |||||
| #include <stdio.h> | |||||
| #include <stdlib.h> | |||||
| #include <string.h> | |||||
| #include <unistd.h> | |||||
| #include <stdarg.h> | |||||
| #include "def.h" | |||||
| #include "key.h" | |||||
| #include "kbd.h" | |||||
| #include "funmap.h" | |||||
| #include "chrdef.h" | |||||
| #include "log.h" | |||||
| static char *mglogfiles_create(FILE **, char *); | |||||
| static int mglog_lines(PF); | |||||
| static int mglog_undo(void); | |||||
| static int mglog_window(void); | |||||
| static int mglog_key(KEYMAP *map); | |||||
| const char *mglogdir; | |||||
| const char *mglogpath_lines; | |||||
| const char *mglogpath_undo; | |||||
| const char *mglogpath_window; | |||||
| const char *mglogpath_key; | |||||
| const char *mglogpath_interpreter; | |||||
| const char *mglogpath_misc; | |||||
| int mgloglevel; | |||||
| FILE *fd_lines; | |||||
| FILE *fd_undo; | |||||
| FILE *fd_window; | |||||
| FILE *fd_key; | |||||
| FILE *fd_interpreter; | |||||
| FILE *fd_misc; | |||||
| int | |||||
| mglog(PF funct, void *map) | |||||
| { | |||||
| if(!mglog_lines(funct)) | |||||
| ewprintf("Problem logging lines"); | |||||
| if(!mglog_undo()) | |||||
| ewprintf("Problem logging undo"); | |||||
| if(!mglog_window()) | |||||
| ewprintf("Problem logging window"); | |||||
| if(!mglog_key(map)) | |||||
| ewprintf("Problem logging key"); | |||||
| return (TRUE); | |||||
| } | |||||
| static int | |||||
| mglog_key(KEYMAP *map) | |||||
| { | |||||
| PF *pfp; | |||||
| if (ISWORD(*key.k_chars)) { | |||||
| fprintf(fd_key, "k_count:%d k_chars:%hd\tchr:%c\t", key.k_count, | |||||
| *key.k_chars, CHARMASK(*key.k_chars)); | |||||
| } else { | |||||
| fprintf(fd_key, "k_count:%d k_chars:%hd\t\t", key.k_count, | |||||
| *key.k_chars); | |||||
| } | |||||
| fprintf(fd_key, "map:%p %d %d %p %hd %hd\n", | |||||
| map, | |||||
| map->map_num, | |||||
| map->map_max, | |||||
| map->map_default, | |||||
| map->map_element->k_base, | |||||
| map->map_element->k_num | |||||
| ); | |||||
| for (pfp = map->map_element->k_funcp; *pfp != NULL; pfp++) | |||||
| fprintf(fd_key, "%s ", function_name(*pfp)); | |||||
| fprintf(fd_key, "\n\n"); | |||||
| fflush(fd_key); | |||||
| return (TRUE); | |||||
| } | |||||
| static int | |||||
| mglog_window(void) | |||||
| { | |||||
| struct mgwin *wp; | |||||
| int i; | |||||
| for (wp = wheadp, i = 0; wp != NULL; wp = wp->w_wndp, ++i) { | |||||
| fprintf(fd_window, | |||||
| "%d wh%p wlst%p wbfp%p wlp%p wdtp%p wmkp%p wdto%d wmko%d" \ | |||||
| " wtpr%d wntr%d wfrm%d wrfl%c wflg%c wwrl%p wdtl%d" \ | |||||
| " wmkl%d\n", | |||||
| i, | |||||
| wp, | |||||
| &wp->w_list, | |||||
| wp->w_bufp, | |||||
| wp->w_linep, | |||||
| wp->w_dotp, | |||||
| wp->w_markp, | |||||
| wp->w_doto, | |||||
| wp->w_marko, | |||||
| wp->w_toprow, | |||||
| wp->w_ntrows, | |||||
| wp->w_frame, | |||||
| wp->w_rflag, | |||||
| wp->w_flag, | |||||
| wp->w_wrapline, | |||||
| wp->w_dotline, | |||||
| wp->w_markline | |||||
| ); | |||||
| } | |||||
| fflush(fd_window); | |||||
| return (TRUE); | |||||
| } | |||||
| static int | |||||
| mglog_undo(void) | |||||
| { | |||||
| struct undo_rec *rec; | |||||
| char buf[4096], tmp[1024]; | |||||
| int num; | |||||
| char *jptr; | |||||
| jptr = "^J"; /* :) */ | |||||
| /* | |||||
| * From undo_dump() | |||||
| */ | |||||
| num = 0; | |||||
| TAILQ_FOREACH(rec, &curbp->b_undo, next) { | |||||
| num++; | |||||
| fprintf(fd_undo, "%d:\t %s at %d ", num, | |||||
| (rec->type == DELETE) ? "DELETE": | |||||
| (rec->type == DELREG) ? "DELREGION": | |||||
| (rec->type == INSERT) ? "INSERT": | |||||
| (rec->type == BOUNDARY) ? "----" : | |||||
| (rec->type == MODIFIED) ? "MODIFIED": "UNKNOWN", | |||||
| rec->pos | |||||
| ); | |||||
| if (rec->content) { | |||||
| (void)strlcat(buf, "\"", sizeof(buf)); | |||||
| snprintf(tmp, sizeof(tmp), "%.*s", | |||||
| *rec->content == '\n' ? 2 : rec->region.r_size, | |||||
| *rec->content == '\n' ? jptr : rec->content); | |||||
| (void)strlcat(buf, tmp, sizeof(buf)); | |||||
| (void)strlcat(buf, "\"", sizeof(buf)); | |||||
| } | |||||
| snprintf(tmp, sizeof(tmp), " [%d]", rec->region.r_size); | |||||
| if (strlcat(buf, tmp, sizeof(buf)) >= sizeof(buf)) { | |||||
| dobeep(); | |||||
| ewprintf("Undo record too large. Aborted."); | |||||
| return (FALSE); | |||||
| } | |||||
| fprintf(fd_undo, "%s\n", buf); | |||||
| tmp[0] = buf[0] = '\0'; | |||||
| } | |||||
| fprintf(fd_undo, "\t [end-of-undo]\n\n"); | |||||
| fflush(fd_undo); | |||||
| return (TRUE); | |||||
| } | |||||
| static int | |||||
| mglog_lines(PF funct) | |||||
| { | |||||
| struct line *lp; | |||||
| char *curline, *tmp, o; | |||||
| int i; | |||||
| i = 0; | |||||
| fprintf(fd_lines, "%s\n", function_name(funct)); | |||||
| lp = bfirstlp(curbp); | |||||
| for(;;) { | |||||
| i++; | |||||
| curline = " "; | |||||
| o = ' '; | |||||
| if (i == curwp->w_dotline) { | |||||
| curline = ">"; | |||||
| if (lp->l_used > 0 && curwp->w_doto < lp->l_used) | |||||
| o = lp->l_text[curwp->w_doto]; | |||||
| else | |||||
| o = '-'; | |||||
| } | |||||
| if (lp->l_size == 0) | |||||
| tmp = " "; | |||||
| else | |||||
| tmp = lp->l_text; | |||||
| /* segv on fprintf below with long lines */ | |||||
| fprintf(fd_lines, "%s%p b^%p f.%p %d %d\t%c|%s\n", curline, | |||||
| lp, lp->l_bp, lp->l_fp, | |||||
| lp->l_size, lp->l_used, o, tmp); | |||||
| lp = lforw(lp); | |||||
| if (lp == curbp->b_headp) { | |||||
| fprintf(fd_lines, " %p b^%p f.%p [bhead]\n(EOB)\n", | |||||
| lp, lp->l_bp, lp->l_fp); | |||||
| fprintf(fd_lines, "lines:raw:%d buf:%d wdot:%d\n\n", | |||||
| i, curbp->b_lines, curwp->w_dotline); | |||||
| break; | |||||
| } | |||||
| } | |||||
| fflush(fd_lines); | |||||
| return (TRUE); | |||||
| } | |||||
| /* | |||||
| * See what the eval variable code is up to. | |||||
| */ | |||||
| int | |||||
| mglog_isvar( | |||||
| const char* const argbuf, | |||||
| const char* const argp, | |||||
| const int sizof | |||||
| ) | |||||
| { | |||||
| fprintf(fd_interpreter, " argbuf:%s,argp:%s,sizof:%d<\n", | |||||
| argbuf, | |||||
| argp, | |||||
| sizof); | |||||
| fflush(fd_interpreter); | |||||
| return (TRUE); | |||||
| } | |||||
| /* | |||||
| * See what the eval line code is up to. | |||||
| */ | |||||
| int | |||||
| mglog_execbuf( | |||||
| const char* const pre, | |||||
| const char* const excbuf, | |||||
| const char* const argbuf, | |||||
| const char* const argp, | |||||
| const int last, | |||||
| const int inlist, | |||||
| const char* const cmdp, | |||||
| const char* const p, | |||||
| const char* const contbuf | |||||
| ) | |||||
| { | |||||
| fprintf(fd_interpreter, "%sexcbuf:%s,argbuf:%s,argp:%s,last:%d,inlist:%d,"\ | |||||
| "cmdp:%s,p:%s,contbuf:%s<\n", | |||||
| pre, | |||||
| excbuf, | |||||
| argbuf, | |||||
| argp, | |||||
| last, | |||||
| inlist, | |||||
| cmdp, | |||||
| p, | |||||
| contbuf | |||||
| ); | |||||
| fflush(fd_interpreter); | |||||
| return (TRUE); | |||||
| } | |||||
| /* | |||||
| * Misc. logging for various subsystems | |||||
| */ | |||||
| int | |||||
| mglog_misc( | |||||
| const char *fmt, | |||||
| ... | |||||
| ) | |||||
| { | |||||
| va_list ap; | |||||
| int rc; | |||||
| va_start(ap, fmt); | |||||
| rc = vfprintf(fd_misc, fmt, ap); | |||||
| va_end(ap); | |||||
| fflush(fd_misc); | |||||
| if (rc < 0) | |||||
| return (FALSE); | |||||
| return (TRUE); | |||||
| } | |||||
| /* | |||||
| * Make sure logging to log files can happen. | |||||
| */ | |||||
| int | |||||
| mgloginit(void) | |||||
| { | |||||
| struct stat sb; | |||||
| mode_t dir_mode, f_mode, oumask; | |||||
| char *mglogfile_lines, *mglogfile_undo, *mglogfile_window; | |||||
| char *mglogfile_key, *mglogfile_interpreter, *mglogfile_misc; | |||||
| mglogdir = "./log/"; | |||||
| mglogfile_lines = "line.log"; | |||||
| mglogfile_undo = "undo.log"; | |||||
| mglogfile_window = "window.log"; | |||||
| mglogfile_key = "key.log"; | |||||
| mglogfile_interpreter = "interpreter.log"; | |||||
| mglogfile_misc = "misc.log"; | |||||
| /* | |||||
| * Change mgloglevel for desired level of logging. | |||||
| * log.h has relevant level info. | |||||
| */ | |||||
| mgloglevel = 1; | |||||
| oumask = umask(0); | |||||
| f_mode = 0777& ~oumask; | |||||
| dir_mode = f_mode | S_IWUSR | S_IXUSR; | |||||
| if(stat(mglogdir, &sb)) { | |||||
| if (mkdir(mglogdir, dir_mode) != 0) | |||||
| return (FALSE); | |||||
| if (chmod(mglogdir, f_mode) == -1) | |||||
| return (FALSE); | |||||
| } | |||||
| mglogpath_lines = mglogfiles_create(&fd_lines, mglogfile_lines); | |||||
| if (mglogpath_lines == NULL) | |||||
| return (FALSE); | |||||
| mglogpath_undo = mglogfiles_create(&fd_undo, mglogfile_undo); | |||||
| if (mglogpath_undo == NULL) | |||||
| return (FALSE); | |||||
| mglogpath_window = mglogfiles_create(&fd_window, mglogfile_window); | |||||
| if (mglogpath_window == NULL) | |||||
| return (FALSE); | |||||
| mglogpath_key = mglogfiles_create(&fd_key, mglogfile_key); | |||||
| if (mglogpath_key == NULL) | |||||
| return (FALSE); | |||||
| mglogpath_interpreter = mglogfiles_create(&fd_interpreter, | |||||
| mglogfile_interpreter); | |||||
| if (mglogpath_interpreter == NULL) | |||||
| return (FALSE); | |||||
| mglogpath_misc = mglogfiles_create(&fd_misc, mglogfile_misc); | |||||
| if (mglogpath_misc == NULL) | |||||
| return (FALSE); | |||||
| return (TRUE); | |||||
| } | |||||
| static char * | |||||
| mglogfiles_create(FILE ** fd, char *mglogfile) | |||||
| { | |||||
| char tmp[NFILEN], *tmp2; | |||||
| if (strlcpy(tmp, mglogdir, sizeof(tmp)) > | |||||
| sizeof(tmp)) | |||||
| return (NULL); | |||||
| if (strlcat(tmp, mglogfile, sizeof(tmp)) > | |||||
| sizeof(tmp)) | |||||
| return (NULL); | |||||
| if ((tmp2 = strndup(tmp, NFILEN)) == NULL) | |||||
| return (NULL); | |||||
| if ((*fd = fopen(tmp2, "w")) == NULL) | |||||
| return (NULL); | |||||
| return (tmp2); | |||||
| } | |||||