To: vim_dev@googlegroups.com Subject: Patch 8.1.1195 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.1195 Problem: Vim script debugger functionality needs cleanup. Solution: Move debugger code to a separate file. Add more tests. (Yegappan Lakshmanan, closes #4285) Files: Filelist, src/Make_bc5.mak, src/Make_cyg_ming.mak, src/Make_dice.mak, src/Make_ivc.mak, src/Make_manx.mak, src/Make_morph.mak, src/Make_mvc.mak, src/Make_sas.mak, src/Make_vms.mms, src/Makefile, src/debugger.c, src/ex_cmds2.c, src/proto.h, src/proto/debugger.pro, src/proto/ex_cmds2.pro *** ../vim-8.1.1194/Filelist 2019-04-11 13:09:57.786699880 +0200 --- Filelist 2019-04-21 11:18:57.688977840 +0200 *************** *** 23,28 **** --- 23,29 ---- src/charset.c \ src/crypt.c \ src/crypt_zip.c \ + src/debugger.c \ src/dict.c \ src/diff.c \ src/digraph.c \ *************** *** 156,161 **** --- 157,163 ---- src/proto/charset.pro \ src/proto/crypt.pro \ src/proto/crypt_zip.pro \ + src/proto/debugger.pro \ src/proto/dict.pro \ src/proto/diff.pro \ src/proto/digraph.pro \ *** ../vim-8.1.1194/src/Make_bc5.mak 2019-03-30 13:53:26.166425144 +0100 --- src/Make_bc5.mak 2019-04-21 11:18:57.688977840 +0200 *************** *** 516,521 **** --- 516,522 ---- $(OBJDIR)\charset.obj \ $(OBJDIR)\crypt.obj \ $(OBJDIR)\crypt_zip.obj \ + $(OBJDIR)\debugger.obj \ $(OBJDIR)\dict.obj \ $(OBJDIR)\diff.obj \ $(OBJDIR)\digraph.obj \ *** ../vim-8.1.1194/src/Make_cyg_ming.mak 2019-04-03 20:30:56.081017564 +0200 --- src/Make_cyg_ming.mak 2019-04-21 11:18:57.688977840 +0200 *************** *** 703,708 **** --- 703,709 ---- $(OUTDIR)/charset.o \ $(OUTDIR)/crypt.o \ $(OUTDIR)/crypt_zip.o \ + $(OUTDIR)/debugger.o \ $(OUTDIR)/dict.o \ $(OUTDIR)/diff.o \ $(OUTDIR)/digraph.o \ *** ../vim-8.1.1194/src/Make_dice.mak 2019-03-30 13:53:26.166425144 +0100 --- src/Make_dice.mak 2019-04-21 11:18:57.688977840 +0200 *************** *** 33,38 **** --- 33,39 ---- charset.c \ crypt.c \ crypt_zip.c \ + debugger.c \ dict.c \ diff.c \ digraph.c \ *************** *** 93,98 **** --- 94,100 ---- o/charset.o \ o/crypt.o \ o/crypt_zip.o \ + o/debugger.o \ o/dict.o \ o/diff.o \ o/digraph.o \ *************** *** 179,184 **** --- 181,188 ---- o/crypt_zip.o: crypt_zip.c $(SYMS) + o/debugger.o: debugger.c $(SYMS) + o/dict.o: dict.c $(SYMS) o/diff.o: diff.c $(SYMS) *** ../vim-8.1.1194/src/Make_ivc.mak 2019-03-30 13:53:26.166425144 +0100 --- src/Make_ivc.mak 2019-04-21 11:18:57.688977840 +0200 *************** *** 217,222 **** --- 217,223 ---- "$(INTDIR)/charset.obj" \ "$(INTDIR)/crypt.obj" \ "$(INTDIR)/crypt_zip.obj" \ + "$(INTDIR)/debugger.obj" \ "$(INTDIR)/dict.obj" \ "$(INTDIR)/diff.obj" \ "$(INTDIR)/digraph.obj" \ *************** *** 369,374 **** --- 370,379 ---- # End Source File # Begin Source File + SOURCE=.\debugger.c + # End Source File + # Begin Source File + SOURCE=.\dict.c # End Source File # Begin Source File *** ../vim-8.1.1194/src/Make_manx.mak 2019-03-30 13:53:26.166425144 +0100 --- src/Make_manx.mak 2019-04-21 11:18:57.688977840 +0200 *************** *** 43,48 **** --- 43,49 ---- charset.c \ crypt.c \ crypt_zip.c \ + debugger.c \ dict.c \ diff.c \ digraph.c \ *************** *** 105,110 **** --- 106,112 ---- obj/charset.o \ obj/crypt.o \ obj/crypt_zip.o \ + obj/debugger.o \ obj/dict.o \ obj/diff.o \ obj/digraph.o \ *************** *** 165,170 **** --- 167,173 ---- proto/charset.pro \ proto/crypt.pro \ proto/crypt_zip.pro \ + proto/debugger.pro \ proto/dict.pro \ proto/diff.pro \ proto/digraph.pro \ *************** *** 283,288 **** --- 286,294 ---- obj/crypt_zip.o: crypt_zip.c $(CCSYM) $@ crypt_zip.c + obj/debugger.o: debugger.c + $(CCSYM) $@ debugger.c + obj/dict.o: dict.c $(CCSYM) $@ dict.c *** ../vim-8.1.1194/src/Make_morph.mak 2019-03-30 13:53:26.166425144 +0100 --- src/Make_morph.mak 2019-04-21 11:18:57.688977840 +0200 *************** *** 31,36 **** --- 31,37 ---- charset.c \ crypt.c \ crypt_zip.c \ + debugger.c \ dict.c \ diff.c \ digraph.c \ *** ../vim-8.1.1194/src/Make_mvc.mak 2019-04-04 20:31:59.094873282 +0200 --- src/Make_mvc.mak 2019-04-21 11:18:57.688977840 +0200 *************** *** 711,716 **** --- 711,717 ---- $(OUTDIR)\charset.obj \ $(OUTDIR)\crypt.obj \ $(OUTDIR)\crypt_zip.obj \ + $(OUTDIR)\debugger.obj \ $(OUTDIR)\dict.obj \ $(OUTDIR)\diff.obj \ $(OUTDIR)\digraph.obj \ *************** *** 1367,1372 **** --- 1368,1375 ---- $(OUTDIR)/crypt_zip.obj: $(OUTDIR) crypt_zip.c $(INCL) + $(OUTDIR)/debugger.obj: $(OUTDIR) debugger.c $(INCL) + $(OUTDIR)/dict.obj: $(OUTDIR) dict.c $(INCL) $(OUTDIR)/diff.obj: $(OUTDIR) diff.c $(INCL) *************** *** 1638,1643 **** --- 1641,1647 ---- proto/charset.pro \ proto/crypt.pro \ proto/crypt_zip.pro \ + proto/debugger.pro \ proto/dict.pro \ proto/diff.pro \ proto/digraph.pro \ *** ../vim-8.1.1194/src/Make_sas.mak 2019-03-30 13:53:26.166425144 +0100 --- src/Make_sas.mak 2019-04-21 11:18:57.688977840 +0200 *************** *** 96,101 **** --- 96,102 ---- charset.c \ crypt.c \ crypt_zip.c \ + debugger.c \ dict.c \ diff.c \ digraph.c \ *************** *** 157,162 **** --- 158,164 ---- charset.o \ crypt.o \ crypt_zip.o \ + debugger.o \ dict.o \ diff.o \ digraph.o \ *************** *** 218,223 **** --- 220,226 ---- proto/charset.pro \ proto/crypt.pro \ proto/crypt_zip.pro \ + proto/debugger.pro \ proto/dict.pro \ proto/diff.pro \ proto/digraph.pro \ *************** *** 340,345 **** --- 343,350 ---- proto/crypt.pro: crypt.c crypt_zip.o: crypt_zip.c proto/crypt_zip.pro: crypt_zip.c + debugger.o: debugger.c + proto/debugger.pro: debugger.c dict.o: dict.c proto/dict.pro: dict.c diff.o: diff.c *** ../vim-8.1.1194/src/Make_vms.mms 2019-03-30 13:53:26.166425144 +0100 --- src/Make_vms.mms 2019-04-21 11:18:57.688977840 +0200 *************** *** 308,337 **** $(PERL_LIB) $(PYTHON_LIB) $(TCL_LIB) $(RUBY_LIB) SRC = arabic.c autocmd.c beval.c blob.c blowfish.c buffer.c charset.c \ ! crypt.c crypt_zip.c dict.c diff.c digraph.c edit.c eval.c evalfunc.c \ ! ex_cmds.c ex_cmds2.c ex_docmd.c ex_eval.c ex_getln.c if_cscope.c \ ! if_xcmdsrv.c fileio.c findfile.c fold.c getchar.c hardcopy.c \ ! hashtab.c indent.c insexpand.c json.c list.c main.c mark.c menu.c \ ! mbyte.c memfile.c memline.c message.c misc1.c misc2.c move.c normal.c \ ! ops.c option.c popupmnu.c quickfix.c regexp.c search.c sha256.c sign.c \ ! spell.c spellfile.c syntax.c tag.c term.c termlib.c textprop.c ui.c \ ! undo.c userfunc.c version.c screen.c window.c os_unix.c os_vms.c \ ! pathdef.c $(GUI_SRC) $(PERL_SRC) $(PYTHON_SRC) $(TCL_SRC) \ $(RUBY_SRC) $(HANGULIN_SRC) $(MZSCH_SRC) $(XDIFF_SRC) OBJ = arabic.obj autocmd.obj beval.obj blob.obj blowfish.obj buffer.obj \ ! charset.obj crypt.obj crypt_zip.obj dict.obj diff.obj digraph.obj \ ! edit.obj eval.obj evalfunc.obj ex_cmds.obj ex_cmds2.obj ex_docmd.obj \ ! ex_eval.obj ex_getln.obj if_cscope.obj if_xcmdsrv.obj \ fileio.obj findfile.obj fold.obj getchar.obj hardcopy.obj hashtab.obj \ indent.obj insexpand.obj json.obj list.obj main.obj mark.obj \ menu.obj memfile.obj memline.obj message.obj misc1.obj misc2.obj \ move.obj mbyte.obj normal.obj ops.obj option.obj popupmnu.obj \ quickfix.obj regexp.obj search.obj sha256.obj sign.obj spell.obj \ spellfile.obj syntax.obj tag.obj term.obj termlib.obj textprop.obj \ ! ui.obj undo.obj userfunc.obj screen.obj version.obj window.obj \ ! os_unix.obj os_vms.obj pathdef.obj if_mzsch.obj \ $(GUI_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(TCL_OBJ) \ $(RUBY_OBJ) $(HANGULIN_OBJ) $(MZSCH_OBJ) $(XDIFF_OBJ) --- 308,337 ---- $(PERL_LIB) $(PYTHON_LIB) $(TCL_LIB) $(RUBY_LIB) SRC = arabic.c autocmd.c beval.c blob.c blowfish.c buffer.c charset.c \ ! crypt.c crypt_zip.c debugger.c dict.c diff.c digraph.c edit.c eval.c \ ! evalfunc.c ex_cmds.c ex_cmds2.c ex_docmd.c ex_eval.c ex_getln.c \ ! if_cscope.c if_xcmdsrv.c fileio.c findfile.c fold.c getchar.c \ ! hardcopy.c hashtab.c indent.c insexpand.c json.c list.c main.c mark.c \ ! menu.c mbyte.c memfile.c memline.c message.c misc1.c misc2.c move.c \ ! normal.c ops.c option.c popupmnu.c quickfix.c regexp.c search.c \ ! sha256.c sign.c spell.c spellfile.c syntax.c tag.c term.c termlib.c \ ! textprop.c ui.c undo.c userfunc.c version.c screen.c window.c \ ! os_unix.c os_vms.c pathdef.c \ $(GUI_SRC) $(PERL_SRC) $(PYTHON_SRC) $(TCL_SRC) \ $(RUBY_SRC) $(HANGULIN_SRC) $(MZSCH_SRC) $(XDIFF_SRC) OBJ = arabic.obj autocmd.obj beval.obj blob.obj blowfish.obj buffer.obj \ ! charset.obj crypt.obj crypt_zip.obj debugger.obj dict.obj diff.obj \ ! digraph.obj edit.obj eval.obj evalfunc.obj ex_cmds.obj ex_cmds2.obj \ ! ex_docmd.obj ex_eval.obj ex_getln.obj if_cscope.obj if_xcmdsrv.obj \ fileio.obj findfile.obj fold.obj getchar.obj hardcopy.obj hashtab.obj \ indent.obj insexpand.obj json.obj list.obj main.obj mark.obj \ menu.obj memfile.obj memline.obj message.obj misc1.obj misc2.obj \ move.obj mbyte.obj normal.obj ops.obj option.obj popupmnu.obj \ quickfix.obj regexp.obj search.obj sha256.obj sign.obj spell.obj \ spellfile.obj syntax.obj tag.obj term.obj termlib.obj textprop.obj \ ! ui.obj undo.obj userfunc.obj screen.obj version.obj \ ! window.obj os_unix.obj os_vms.obj pathdef.obj if_mzsch.obj \ $(GUI_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(TCL_OBJ) \ $(RUBY_OBJ) $(HANGULIN_OBJ) $(MZSCH_OBJ) $(XDIFF_OBJ) *************** *** 522,527 **** --- 522,531 ---- ascii.h keymap.h term.h macros.h option.h structs.h \ regexp.h gui.h beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h \ proto.h globals.h + debugger.obj : debugger.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h dict.obj : dict.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h proto.h \ *** ../vim-8.1.1194/src/Makefile 2019-04-17 16:59:12.575688584 +0200 --- src/Makefile 2019-04-21 11:18:57.688977840 +0200 *************** *** 1579,1584 **** --- 1579,1585 ---- charset.c \ crypt.c \ crypt_zip.c \ + debugger.c \ dict.c \ diff.c \ digraph.c \ *************** *** 1693,1698 **** --- 1694,1700 ---- objects/blowfish.o \ objects/crypt.o \ objects/crypt_zip.o \ + objects/debugger.o \ objects/dict.o \ objects/diff.o \ objects/digraph.o \ *************** *** 1820,1825 **** --- 1822,1828 ---- charset.pro \ crypt.pro \ crypt_zip.pro \ + debugger.pro \ dict.pro \ diff.pro \ digraph.pro \ *************** *** 2963,2968 **** --- 2966,2974 ---- objects/crypt_zip.o: crypt_zip.c $(CCC) -o $@ crypt_zip.c + objects/debugger.o: debugger.c + $(CCC) -o $@ debugger.c + objects/dict.o: dict.c $(CCC) -o $@ dict.c *************** *** 3425,3430 **** --- 3431,3439 ---- os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ proto.h globals.h + objects/debugger.o: debugger.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + os_mac.h ascii.h keymap.h term.h macros.h option.h beval.h structs.h \ + regexp.h gui.h alloc.h ex_cmds.h spell.h proto.h globals.h objects/dict.o: dict.c vim.h protodef.h auto/config.h feature.h os_unix.h \ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ *** ../vim-8.1.1194/src/debugger.c 2019-04-21 11:33:08.863781264 +0200 --- src/debugger.c 2019-04-21 11:18:57.688977840 +0200 *************** *** 0 **** --- 1,990 ---- + /* vi:set ts=8 sts=4 sw=4 noet: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + + /* + * debugger.c: Vim script debugger functions + */ + + #include "vim.h" + + #if defined(FEAT_EVAL) || defined(PROTO) + static int debug_greedy = FALSE; /* batch mode debugging: don't save + and restore typeahead. */ + static void do_setdebugtracelevel(char_u *arg); + static void do_checkbacktracelevel(void); + static void do_showbacktrace(char_u *cmd); + + static char_u *debug_oldval = NULL; /* old and newval for debug expressions */ + static char_u *debug_newval = NULL; + static int debug_expr = 0; /* use debug_expr */ + + int + has_watchexpr(void) + { + return debug_expr; + } + + /* + * do_debug(): Debug mode. + * Repeatedly get Ex commands, until told to continue normal execution. + */ + void + do_debug(char_u *cmd) + { + int save_msg_scroll = msg_scroll; + int save_State = State; + int save_did_emsg = did_emsg; + int save_cmd_silent = cmd_silent; + int save_msg_silent = msg_silent; + int save_emsg_silent = emsg_silent; + int save_redir_off = redir_off; + tasave_T typeaheadbuf; + int typeahead_saved = FALSE; + int save_ignore_script = 0; + int save_ex_normal_busy; + int n; + char_u *cmdline = NULL; + char_u *p; + char *tail = NULL; + static int last_cmd = 0; + #define CMD_CONT 1 + #define CMD_NEXT 2 + #define CMD_STEP 3 + #define CMD_FINISH 4 + #define CMD_QUIT 5 + #define CMD_INTERRUPT 6 + #define CMD_BACKTRACE 7 + #define CMD_FRAME 8 + #define CMD_UP 9 + #define CMD_DOWN 10 + + #ifdef ALWAYS_USE_GUI + /* Can't do this when there is no terminal for input/output. */ + if (!gui.in_use) + { + /* Break as soon as possible. */ + debug_break_level = 9999; + return; + } + #endif + + /* Make sure we are in raw mode and start termcap mode. Might have side + * effects... */ + settmode(TMODE_RAW); + starttermcap(); + + ++RedrawingDisabled; /* don't redisplay the window */ + ++no_wait_return; /* don't wait for return */ + did_emsg = FALSE; /* don't use error from debugged stuff */ + cmd_silent = FALSE; /* display commands */ + msg_silent = FALSE; /* display messages */ + emsg_silent = FALSE; /* display error messages */ + redir_off = TRUE; /* don't redirect debug commands */ + + State = NORMAL; + debug_mode = TRUE; + + if (!debug_did_msg) + msg(_("Entering Debug mode. Type \"cont\" to continue.")); + if (debug_oldval != NULL) + { + smsg(_("Oldval = \"%s\""), debug_oldval); + vim_free(debug_oldval); + debug_oldval = NULL; + } + if (debug_newval != NULL) + { + smsg(_("Newval = \"%s\""), debug_newval); + vim_free(debug_newval); + debug_newval = NULL; + } + if (sourcing_name != NULL) + msg((char *)sourcing_name); + if (sourcing_lnum != 0) + smsg(_("line %ld: %s"), (long)sourcing_lnum, cmd); + else + smsg(_("cmd: %s"), cmd); + /* + * Repeat getting a command and executing it. + */ + for (;;) + { + msg_scroll = TRUE; + need_wait_return = FALSE; + + /* Save the current typeahead buffer and replace it with an empty one. + * This makes sure we get input from the user here and don't interfere + * with the commands being executed. Reset "ex_normal_busy" to avoid + * the side effects of using ":normal". Save the stuff buffer and make + * it empty. Set ignore_script to avoid reading from script input. */ + save_ex_normal_busy = ex_normal_busy; + ex_normal_busy = 0; + if (!debug_greedy) + { + save_typeahead(&typeaheadbuf); + typeahead_saved = TRUE; + save_ignore_script = ignore_script; + ignore_script = TRUE; + } + + vim_free(cmdline); + cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL); + + if (typeahead_saved) + { + restore_typeahead(&typeaheadbuf); + ignore_script = save_ignore_script; + } + ex_normal_busy = save_ex_normal_busy; + + cmdline_row = msg_row; + msg_starthere(); + if (cmdline != NULL) + { + /* If this is a debug command, set "last_cmd". + * If not, reset "last_cmd". + * For a blank line use previous command. */ + p = skipwhite(cmdline); + if (*p != NUL) + { + switch (*p) + { + case 'c': last_cmd = CMD_CONT; + tail = "ont"; + break; + case 'n': last_cmd = CMD_NEXT; + tail = "ext"; + break; + case 's': last_cmd = CMD_STEP; + tail = "tep"; + break; + case 'f': + last_cmd = 0; + if (p[1] == 'r') + { + last_cmd = CMD_FRAME; + tail = "rame"; + } + else + { + last_cmd = CMD_FINISH; + tail = "inish"; + } + break; + case 'q': last_cmd = CMD_QUIT; + tail = "uit"; + break; + case 'i': last_cmd = CMD_INTERRUPT; + tail = "nterrupt"; + break; + case 'b': last_cmd = CMD_BACKTRACE; + if (p[1] == 't') + tail = "t"; + else + tail = "acktrace"; + break; + case 'w': last_cmd = CMD_BACKTRACE; + tail = "here"; + break; + case 'u': last_cmd = CMD_UP; + tail = "p"; + break; + case 'd': last_cmd = CMD_DOWN; + tail = "own"; + break; + default: last_cmd = 0; + } + if (last_cmd != 0) + { + /* Check that the tail matches. */ + ++p; + while (*p != NUL && *p == *tail) + { + ++p; + ++tail; + } + if (ASCII_ISALPHA(*p) && last_cmd != CMD_FRAME) + last_cmd = 0; + } + } + + if (last_cmd != 0) + { + /* Execute debug command: decided where to break next and + * return. */ + switch (last_cmd) + { + case CMD_CONT: + debug_break_level = -1; + break; + case CMD_NEXT: + debug_break_level = ex_nesting_level; + break; + case CMD_STEP: + debug_break_level = 9999; + break; + case CMD_FINISH: + debug_break_level = ex_nesting_level - 1; + break; + case CMD_QUIT: + got_int = TRUE; + debug_break_level = -1; + break; + case CMD_INTERRUPT: + got_int = TRUE; + debug_break_level = 9999; + /* Do not repeat ">interrupt" cmd, continue stepping. */ + last_cmd = CMD_STEP; + break; + case CMD_BACKTRACE: + do_showbacktrace(cmd); + continue; + case CMD_FRAME: + if (*p == NUL) + { + do_showbacktrace(cmd); + } + else + { + p = skipwhite(p); + do_setdebugtracelevel(p); + } + continue; + case CMD_UP: + debug_backtrace_level++; + do_checkbacktracelevel(); + continue; + case CMD_DOWN: + debug_backtrace_level--; + do_checkbacktracelevel(); + continue; + } + /* Going out reset backtrace_level */ + debug_backtrace_level = 0; + break; + } + + /* don't debug this command */ + n = debug_break_level; + debug_break_level = -1; + (void)do_cmdline(cmdline, getexline, NULL, + DOCMD_VERBOSE|DOCMD_EXCRESET); + debug_break_level = n; + } + lines_left = Rows - 1; + } + vim_free(cmdline); + + --RedrawingDisabled; + --no_wait_return; + redraw_all_later(NOT_VALID); + need_wait_return = FALSE; + msg_scroll = save_msg_scroll; + lines_left = Rows - 1; + State = save_State; + debug_mode = FALSE; + did_emsg = save_did_emsg; + cmd_silent = save_cmd_silent; + msg_silent = save_msg_silent; + emsg_silent = save_emsg_silent; + redir_off = save_redir_off; + + /* Only print the message again when typing a command before coming back + * here. */ + debug_did_msg = TRUE; + } + + static int + get_maxbacktrace_level(void) + { + char *p, *q; + int maxbacktrace = 0; + + if (sourcing_name != NULL) + { + p = (char *)sourcing_name; + while ((q = strstr(p, "..")) != NULL) + { + p = q + 2; + maxbacktrace++; + } + } + return maxbacktrace; + } + + static void + do_setdebugtracelevel(char_u *arg) + { + int level; + + level = atoi((char *)arg); + if (*arg == '+' || level < 0) + debug_backtrace_level += level; + else + debug_backtrace_level = level; + + do_checkbacktracelevel(); + } + + static void + do_checkbacktracelevel(void) + { + if (debug_backtrace_level < 0) + { + debug_backtrace_level = 0; + msg(_("frame is zero")); + } + else + { + int max = get_maxbacktrace_level(); + + if (debug_backtrace_level > max) + { + debug_backtrace_level = max; + smsg(_("frame at highest level: %d"), max); + } + } + } + + static void + do_showbacktrace(char_u *cmd) + { + char *cur; + char *next; + int i = 0; + int max = get_maxbacktrace_level(); + + if (sourcing_name != NULL) + { + cur = (char *)sourcing_name; + while (!got_int) + { + next = strstr(cur, ".."); + if (next != NULL) + *next = NUL; + if (i == max - debug_backtrace_level) + smsg("->%d %s", max - i, cur); + else + smsg(" %d %s", max - i, cur); + ++i; + if (next == NULL) + break; + *next = '.'; + cur = next + 2; + } + } + if (sourcing_lnum != 0) + smsg(_("line %ld: %s"), (long)sourcing_lnum, cmd); + else + smsg(_("cmd: %s"), cmd); + } + + /* + * ":debug". + */ + void + ex_debug(exarg_T *eap) + { + int debug_break_level_save = debug_break_level; + + debug_break_level = 9999; + do_cmdline_cmd(eap->arg); + debug_break_level = debug_break_level_save; + } + + static char_u *debug_breakpoint_name = NULL; + static linenr_T debug_breakpoint_lnum; + + /* + * When debugging or a breakpoint is set on a skipped command, no debug prompt + * is shown by do_one_cmd(). This situation is indicated by debug_skipped, and + * debug_skipped_name is then set to the source name in the breakpoint case. If + * a skipped command decides itself that a debug prompt should be displayed, it + * can do so by calling dbg_check_skipped(). + */ + static int debug_skipped; + static char_u *debug_skipped_name; + + /* + * Go to debug mode when a breakpoint was encountered or "ex_nesting_level" is + * at or below the break level. But only when the line is actually + * executed. Return TRUE and set breakpoint_name for skipped commands that + * decide to execute something themselves. + * Called from do_one_cmd() before executing a command. + */ + void + dbg_check_breakpoint(exarg_T *eap) + { + char_u *p; + + debug_skipped = FALSE; + if (debug_breakpoint_name != NULL) + { + if (!eap->skip) + { + /* replace K_SNR with "" */ + if (debug_breakpoint_name[0] == K_SPECIAL + && debug_breakpoint_name[1] == KS_EXTRA + && debug_breakpoint_name[2] == (int)KE_SNR) + p = (char_u *)""; + else + p = (char_u *)""; + smsg(_("Breakpoint in \"%s%s\" line %ld"), + p, + debug_breakpoint_name + (*p == NUL ? 0 : 3), + (long)debug_breakpoint_lnum); + debug_breakpoint_name = NULL; + do_debug(eap->cmd); + } + else + { + debug_skipped = TRUE; + debug_skipped_name = debug_breakpoint_name; + debug_breakpoint_name = NULL; + } + } + else if (ex_nesting_level <= debug_break_level) + { + if (!eap->skip) + do_debug(eap->cmd); + else + { + debug_skipped = TRUE; + debug_skipped_name = NULL; + } + } + } + + /* + * Go to debug mode if skipped by dbg_check_breakpoint() because eap->skip was + * set. Return TRUE when the debug mode is entered this time. + */ + int + dbg_check_skipped(exarg_T *eap) + { + int prev_got_int; + + if (debug_skipped) + { + /* + * Save the value of got_int and reset it. We don't want a previous + * interruption cause flushing the input buffer. + */ + prev_got_int = got_int; + got_int = FALSE; + debug_breakpoint_name = debug_skipped_name; + /* eap->skip is TRUE */ + eap->skip = FALSE; + (void)dbg_check_breakpoint(eap); + eap->skip = TRUE; + got_int |= prev_got_int; + return TRUE; + } + return FALSE; + } + + /* + * The list of breakpoints: dbg_breakp. + * This is a grow-array of structs. + */ + struct debuggy + { + int dbg_nr; /* breakpoint number */ + int dbg_type; /* DBG_FUNC, DBG_FILE or DBG_EXPR */ + char_u *dbg_name; /* function, expression or file name */ + regprog_T *dbg_prog; /* regexp program */ + linenr_T dbg_lnum; /* line number in function or file */ + int dbg_forceit; /* ! used */ + #ifdef FEAT_EVAL + typval_T *dbg_val; /* last result of watchexpression */ + #endif + int dbg_level; /* stored nested level for expr */ + }; + + static garray_T dbg_breakp = {0, 0, sizeof(struct debuggy), 4, NULL}; + #define BREAKP(idx) (((struct debuggy *)dbg_breakp.ga_data)[idx]) + #define DEBUGGY(gap, idx) (((struct debuggy *)gap->ga_data)[idx]) + static int last_breakp = 0; /* nr of last defined breakpoint */ + + #ifdef FEAT_PROFILE + /* Profiling uses file and func names similar to breakpoints. */ + static garray_T prof_ga = {0, 0, sizeof(struct debuggy), 4, NULL}; + #endif + #define DBG_FUNC 1 + #define DBG_FILE 2 + #define DBG_EXPR 3 + + static linenr_T debuggy_find(int file,char_u *fname, linenr_T after, garray_T *gap, int *fp); + + /* + * Parse the arguments of ":profile", ":breakadd" or ":breakdel" and put them + * in the entry just after the last one in dbg_breakp. Note that "dbg_name" + * is allocated. + * Returns FAIL for failure. + */ + static int + dbg_parsearg( + char_u *arg, + garray_T *gap) /* either &dbg_breakp or &prof_ga */ + { + char_u *p = arg; + char_u *q; + struct debuggy *bp; + int here = FALSE; + + if (ga_grow(gap, 1) == FAIL) + return FAIL; + bp = &DEBUGGY(gap, gap->ga_len); + + /* Find "func" or "file". */ + if (STRNCMP(p, "func", 4) == 0) + bp->dbg_type = DBG_FUNC; + else if (STRNCMP(p, "file", 4) == 0) + bp->dbg_type = DBG_FILE; + else if ( + #ifdef FEAT_PROFILE + gap != &prof_ga && + #endif + STRNCMP(p, "here", 4) == 0) + { + if (curbuf->b_ffname == NULL) + { + emsg(_(e_noname)); + return FAIL; + } + bp->dbg_type = DBG_FILE; + here = TRUE; + } + else if ( + #ifdef FEAT_PROFILE + gap != &prof_ga && + #endif + STRNCMP(p, "expr", 4) == 0) + bp->dbg_type = DBG_EXPR; + else + { + semsg(_(e_invarg2), p); + return FAIL; + } + p = skipwhite(p + 4); + + /* Find optional line number. */ + if (here) + bp->dbg_lnum = curwin->w_cursor.lnum; + else if ( + #ifdef FEAT_PROFILE + gap != &prof_ga && + #endif + VIM_ISDIGIT(*p)) + { + bp->dbg_lnum = getdigits(&p); + p = skipwhite(p); + } + else + bp->dbg_lnum = 0; + + /* Find the function or file name. Don't accept a function name with (). */ + if ((!here && *p == NUL) + || (here && *p != NUL) + || (bp->dbg_type == DBG_FUNC && strstr((char *)p, "()") != NULL)) + { + semsg(_(e_invarg2), arg); + return FAIL; + } + + if (bp->dbg_type == DBG_FUNC) + bp->dbg_name = vim_strsave(p); + else if (here) + bp->dbg_name = vim_strsave(curbuf->b_ffname); + else if (bp->dbg_type == DBG_EXPR) + { + bp->dbg_name = vim_strsave(p); + if (bp->dbg_name != NULL) + bp->dbg_val = eval_expr(bp->dbg_name, NULL); + } + else + { + /* Expand the file name in the same way as do_source(). This means + * doing it twice, so that $DIR/file gets expanded when $DIR is + * "~/dir". */ + q = expand_env_save(p); + if (q == NULL) + return FAIL; + p = expand_env_save(q); + vim_free(q); + if (p == NULL) + return FAIL; + if (*p != '*') + { + bp->dbg_name = fix_fname(p); + vim_free(p); + } + else + bp->dbg_name = p; + } + + if (bp->dbg_name == NULL) + return FAIL; + return OK; + } + + /* + * ":breakadd". Also used for ":profile". + */ + void + ex_breakadd(exarg_T *eap) + { + struct debuggy *bp; + char_u *pat; + garray_T *gap; + + gap = &dbg_breakp; + #ifdef FEAT_PROFILE + if (eap->cmdidx == CMD_profile) + gap = &prof_ga; + #endif + + if (dbg_parsearg(eap->arg, gap) == OK) + { + bp = &DEBUGGY(gap, gap->ga_len); + bp->dbg_forceit = eap->forceit; + + if (bp->dbg_type != DBG_EXPR) + { + pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, FALSE); + if (pat != NULL) + { + bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + vim_free(pat); + } + if (pat == NULL || bp->dbg_prog == NULL) + vim_free(bp->dbg_name); + else + { + if (bp->dbg_lnum == 0) /* default line number is 1 */ + bp->dbg_lnum = 1; + #ifdef FEAT_PROFILE + if (eap->cmdidx != CMD_profile) + #endif + { + DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp; + ++debug_tick; + } + ++gap->ga_len; + } + } + else + { + /* DBG_EXPR */ + DEBUGGY(gap, gap->ga_len++).dbg_nr = ++last_breakp; + ++debug_tick; + } + } + } + + /* + * ":debuggreedy". + */ + void + ex_debuggreedy(exarg_T *eap) + { + if (eap->addr_count == 0 || eap->line2 != 0) + debug_greedy = TRUE; + else + debug_greedy = FALSE; + } + + /* + * ":breakdel" and ":profdel". + */ + void + ex_breakdel(exarg_T *eap) + { + struct debuggy *bp, *bpi; + int nr; + int todel = -1; + int del_all = FALSE; + int i; + linenr_T best_lnum = 0; + garray_T *gap; + + gap = &dbg_breakp; + if (eap->cmdidx == CMD_profdel) + { + #ifdef FEAT_PROFILE + gap = &prof_ga; + #else + ex_ni(eap); + return; + #endif + } + + if (vim_isdigit(*eap->arg)) + { + /* ":breakdel {nr}" */ + nr = atol((char *)eap->arg); + for (i = 0; i < gap->ga_len; ++i) + if (DEBUGGY(gap, i).dbg_nr == nr) + { + todel = i; + break; + } + } + else if (*eap->arg == '*') + { + todel = 0; + del_all = TRUE; + } + else + { + /* ":breakdel {func|file|expr} [lnum] {name}" */ + if (dbg_parsearg(eap->arg, gap) == FAIL) + return; + bp = &DEBUGGY(gap, gap->ga_len); + for (i = 0; i < gap->ga_len; ++i) + { + bpi = &DEBUGGY(gap, i); + if (bp->dbg_type == bpi->dbg_type + && STRCMP(bp->dbg_name, bpi->dbg_name) == 0 + && (bp->dbg_lnum == bpi->dbg_lnum + || (bp->dbg_lnum == 0 + && (best_lnum == 0 + || bpi->dbg_lnum < best_lnum)))) + { + todel = i; + best_lnum = bpi->dbg_lnum; + } + } + vim_free(bp->dbg_name); + } + + if (todel < 0) + semsg(_("E161: Breakpoint not found: %s"), eap->arg); + else + { + while (gap->ga_len > 0) + { + vim_free(DEBUGGY(gap, todel).dbg_name); + #ifdef FEAT_EVAL + if (DEBUGGY(gap, todel).dbg_type == DBG_EXPR + && DEBUGGY(gap, todel).dbg_val != NULL) + free_tv(DEBUGGY(gap, todel).dbg_val); + #endif + vim_regfree(DEBUGGY(gap, todel).dbg_prog); + --gap->ga_len; + if (todel < gap->ga_len) + mch_memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1), + (gap->ga_len - todel) * sizeof(struct debuggy)); + #ifdef FEAT_PROFILE + if (eap->cmdidx == CMD_breakdel) + #endif + ++debug_tick; + if (!del_all) + break; + } + + /* If all breakpoints were removed clear the array. */ + if (gap->ga_len == 0) + ga_clear(gap); + } + } + + /* + * ":breaklist". + */ + void + ex_breaklist(exarg_T *eap UNUSED) + { + struct debuggy *bp; + int i; + + if (dbg_breakp.ga_len == 0) + msg(_("No breakpoints defined")); + else + for (i = 0; i < dbg_breakp.ga_len; ++i) + { + bp = &BREAKP(i); + if (bp->dbg_type == DBG_FILE) + home_replace(NULL, bp->dbg_name, NameBuff, MAXPATHL, TRUE); + if (bp->dbg_type != DBG_EXPR) + smsg(_("%3d %s %s line %ld"), + bp->dbg_nr, + bp->dbg_type == DBG_FUNC ? "func" : "file", + bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff, + (long)bp->dbg_lnum); + else + smsg(_("%3d expr %s"), + bp->dbg_nr, bp->dbg_name); + } + } + + /* + * Find a breakpoint for a function or sourced file. + * Returns line number at which to break; zero when no matching breakpoint. + */ + linenr_T + dbg_find_breakpoint( + int file, /* TRUE for a file, FALSE for a function */ + char_u *fname, /* file or function name */ + linenr_T after) /* after this line number */ + { + return debuggy_find(file, fname, after, &dbg_breakp, NULL); + } + + #if defined(FEAT_PROFILE) || defined(PROTO) + /* + * Return TRUE if profiling is on for a function or sourced file. + */ + int + has_profiling( + int file, /* TRUE for a file, FALSE for a function */ + char_u *fname, /* file or function name */ + int *fp) /* return: forceit */ + { + return (debuggy_find(file, fname, (linenr_T)0, &prof_ga, fp) + != (linenr_T)0); + } + #endif + + /* + * Common code for dbg_find_breakpoint() and has_profiling(). + */ + static linenr_T + debuggy_find( + int file, /* TRUE for a file, FALSE for a function */ + char_u *fname, /* file or function name */ + linenr_T after, /* after this line number */ + garray_T *gap, /* either &dbg_breakp or &prof_ga */ + int *fp) /* if not NULL: return forceit */ + { + struct debuggy *bp; + int i; + linenr_T lnum = 0; + char_u *name = fname; + int prev_got_int; + + /* Return quickly when there are no breakpoints. */ + if (gap->ga_len == 0) + return (linenr_T)0; + + /* Replace K_SNR in function name with "". */ + if (!file && fname[0] == K_SPECIAL) + { + name = alloc((unsigned)STRLEN(fname) + 3); + if (name == NULL) + name = fname; + else + { + STRCPY(name, ""); + STRCPY(name + 5, fname + 3); + } + } + + for (i = 0; i < gap->ga_len; ++i) + { + /* Skip entries that are not useful or are for a line that is beyond + * an already found breakpoint. */ + bp = &DEBUGGY(gap, i); + if (((bp->dbg_type == DBG_FILE) == file && + bp->dbg_type != DBG_EXPR && ( + #ifdef FEAT_PROFILE + gap == &prof_ga || + #endif + (bp->dbg_lnum > after && (lnum == 0 || bp->dbg_lnum < lnum))))) + { + /* + * Save the value of got_int and reset it. We don't want a + * previous interruption cancel matching, only hitting CTRL-C + * while matching should abort it. + */ + prev_got_int = got_int; + got_int = FALSE; + if (vim_regexec_prog(&bp->dbg_prog, FALSE, name, (colnr_T)0)) + { + lnum = bp->dbg_lnum; + if (fp != NULL) + *fp = bp->dbg_forceit; + } + got_int |= prev_got_int; + } + #ifdef FEAT_EVAL + else if (bp->dbg_type == DBG_EXPR) + { + typval_T *tv; + int line = FALSE; + + prev_got_int = got_int; + got_int = FALSE; + + tv = eval_expr(bp->dbg_name, NULL); + if (tv != NULL) + { + if (bp->dbg_val == NULL) + { + debug_oldval = typval_tostring(NULL); + bp->dbg_val = tv; + debug_newval = typval_tostring(bp->dbg_val); + line = TRUE; + } + else + { + if (typval_compare(tv, bp->dbg_val, TYPE_EQUAL, + TRUE, FALSE) == OK + && tv->vval.v_number == FALSE) + { + typval_T *v; + + line = TRUE; + debug_oldval = typval_tostring(bp->dbg_val); + /* Need to evaluate again, typval_compare() overwrites + * "tv". */ + v = eval_expr(bp->dbg_name, NULL); + debug_newval = typval_tostring(v); + free_tv(bp->dbg_val); + bp->dbg_val = v; + } + free_tv(tv); + } + } + else if (bp->dbg_val != NULL) + { + debug_oldval = typval_tostring(bp->dbg_val); + debug_newval = typval_tostring(NULL); + free_tv(bp->dbg_val); + bp->dbg_val = NULL; + line = TRUE; + } + + if (line) + { + lnum = after > 0 ? after : 1; + break; + } + + got_int |= prev_got_int; + } + #endif + } + if (name != fname) + vim_free(name); + + return lnum; + } + + /* + * Called when a breakpoint was encountered. + */ + void + dbg_breakpoint(char_u *name, linenr_T lnum) + { + /* We need to check if this line is actually executed in do_one_cmd() */ + debug_breakpoint_name = name; + debug_breakpoint_lnum = lnum; + } + #endif *** ../vim-8.1.1194/src/ex_cmds2.c 2019-04-20 14:39:42.796386124 +0200 --- src/ex_cmds2.c 2019-04-21 11:18:57.688977840 +0200 *************** *** 66,1046 **** #endif #if defined(FEAT_EVAL) || defined(PROTO) - static int debug_greedy = FALSE; /* batch mode debugging: don't save - and restore typeahead. */ - static void do_setdebugtracelevel(char_u *arg); - static void do_checkbacktracelevel(void); - static void do_showbacktrace(char_u *cmd); - - static char_u *debug_oldval = NULL; /* old and newval for debug expressions */ - static char_u *debug_newval = NULL; - static int debug_expr = 0; /* use debug_expr */ - - int - has_watchexpr(void) - { - return debug_expr; - } - - /* - * do_debug(): Debug mode. - * Repeatedly get Ex commands, until told to continue normal execution. - */ - void - do_debug(char_u *cmd) - { - int save_msg_scroll = msg_scroll; - int save_State = State; - int save_did_emsg = did_emsg; - int save_cmd_silent = cmd_silent; - int save_msg_silent = msg_silent; - int save_emsg_silent = emsg_silent; - int save_redir_off = redir_off; - tasave_T typeaheadbuf; - int typeahead_saved = FALSE; - int save_ignore_script = 0; - int save_ex_normal_busy; - int n; - char_u *cmdline = NULL; - char_u *p; - char *tail = NULL; - static int last_cmd = 0; - #define CMD_CONT 1 - #define CMD_NEXT 2 - #define CMD_STEP 3 - #define CMD_FINISH 4 - #define CMD_QUIT 5 - #define CMD_INTERRUPT 6 - #define CMD_BACKTRACE 7 - #define CMD_FRAME 8 - #define CMD_UP 9 - #define CMD_DOWN 10 - - #ifdef ALWAYS_USE_GUI - /* Can't do this when there is no terminal for input/output. */ - if (!gui.in_use) - { - /* Break as soon as possible. */ - debug_break_level = 9999; - return; - } - #endif - - /* Make sure we are in raw mode and start termcap mode. Might have side - * effects... */ - settmode(TMODE_RAW); - starttermcap(); - - ++RedrawingDisabled; /* don't redisplay the window */ - ++no_wait_return; /* don't wait for return */ - did_emsg = FALSE; /* don't use error from debugged stuff */ - cmd_silent = FALSE; /* display commands */ - msg_silent = FALSE; /* display messages */ - emsg_silent = FALSE; /* display error messages */ - redir_off = TRUE; /* don't redirect debug commands */ - - State = NORMAL; - debug_mode = TRUE; - - if (!debug_did_msg) - msg(_("Entering Debug mode. Type \"cont\" to continue.")); - if (debug_oldval != NULL) - { - smsg(_("Oldval = \"%s\""), debug_oldval); - vim_free(debug_oldval); - debug_oldval = NULL; - } - if (debug_newval != NULL) - { - smsg(_("Newval = \"%s\""), debug_newval); - vim_free(debug_newval); - debug_newval = NULL; - } - if (sourcing_name != NULL) - msg((char *)sourcing_name); - if (sourcing_lnum != 0) - smsg(_("line %ld: %s"), (long)sourcing_lnum, cmd); - else - smsg(_("cmd: %s"), cmd); - /* - * Repeat getting a command and executing it. - */ - for (;;) - { - msg_scroll = TRUE; - need_wait_return = FALSE; - - /* Save the current typeahead buffer and replace it with an empty one. - * This makes sure we get input from the user here and don't interfere - * with the commands being executed. Reset "ex_normal_busy" to avoid - * the side effects of using ":normal". Save the stuff buffer and make - * it empty. Set ignore_script to avoid reading from script input. */ - save_ex_normal_busy = ex_normal_busy; - ex_normal_busy = 0; - if (!debug_greedy) - { - save_typeahead(&typeaheadbuf); - typeahead_saved = TRUE; - save_ignore_script = ignore_script; - ignore_script = TRUE; - } - - vim_free(cmdline); - cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL); - - if (typeahead_saved) - { - restore_typeahead(&typeaheadbuf); - ignore_script = save_ignore_script; - } - ex_normal_busy = save_ex_normal_busy; - - cmdline_row = msg_row; - msg_starthere(); - if (cmdline != NULL) - { - /* If this is a debug command, set "last_cmd". - * If not, reset "last_cmd". - * For a blank line use previous command. */ - p = skipwhite(cmdline); - if (*p != NUL) - { - switch (*p) - { - case 'c': last_cmd = CMD_CONT; - tail = "ont"; - break; - case 'n': last_cmd = CMD_NEXT; - tail = "ext"; - break; - case 's': last_cmd = CMD_STEP; - tail = "tep"; - break; - case 'f': - last_cmd = 0; - if (p[1] == 'r') - { - last_cmd = CMD_FRAME; - tail = "rame"; - } - else - { - last_cmd = CMD_FINISH; - tail = "inish"; - } - break; - case 'q': last_cmd = CMD_QUIT; - tail = "uit"; - break; - case 'i': last_cmd = CMD_INTERRUPT; - tail = "nterrupt"; - break; - case 'b': last_cmd = CMD_BACKTRACE; - if (p[1] == 't') - tail = "t"; - else - tail = "acktrace"; - break; - case 'w': last_cmd = CMD_BACKTRACE; - tail = "here"; - break; - case 'u': last_cmd = CMD_UP; - tail = "p"; - break; - case 'd': last_cmd = CMD_DOWN; - tail = "own"; - break; - default: last_cmd = 0; - } - if (last_cmd != 0) - { - /* Check that the tail matches. */ - ++p; - while (*p != NUL && *p == *tail) - { - ++p; - ++tail; - } - if (ASCII_ISALPHA(*p) && last_cmd != CMD_FRAME) - last_cmd = 0; - } - } - - if (last_cmd != 0) - { - /* Execute debug command: decided where to break next and - * return. */ - switch (last_cmd) - { - case CMD_CONT: - debug_break_level = -1; - break; - case CMD_NEXT: - debug_break_level = ex_nesting_level; - break; - case CMD_STEP: - debug_break_level = 9999; - break; - case CMD_FINISH: - debug_break_level = ex_nesting_level - 1; - break; - case CMD_QUIT: - got_int = TRUE; - debug_break_level = -1; - break; - case CMD_INTERRUPT: - got_int = TRUE; - debug_break_level = 9999; - /* Do not repeat ">interrupt" cmd, continue stepping. */ - last_cmd = CMD_STEP; - break; - case CMD_BACKTRACE: - do_showbacktrace(cmd); - continue; - case CMD_FRAME: - if (*p == NUL) - { - do_showbacktrace(cmd); - } - else - { - p = skipwhite(p); - do_setdebugtracelevel(p); - } - continue; - case CMD_UP: - debug_backtrace_level++; - do_checkbacktracelevel(); - continue; - case CMD_DOWN: - debug_backtrace_level--; - do_checkbacktracelevel(); - continue; - } - /* Going out reset backtrace_level */ - debug_backtrace_level = 0; - break; - } - - /* don't debug this command */ - n = debug_break_level; - debug_break_level = -1; - (void)do_cmdline(cmdline, getexline, NULL, - DOCMD_VERBOSE|DOCMD_EXCRESET); - debug_break_level = n; - } - lines_left = Rows - 1; - } - vim_free(cmdline); - - --RedrawingDisabled; - --no_wait_return; - redraw_all_later(NOT_VALID); - need_wait_return = FALSE; - msg_scroll = save_msg_scroll; - lines_left = Rows - 1; - State = save_State; - debug_mode = FALSE; - did_emsg = save_did_emsg; - cmd_silent = save_cmd_silent; - msg_silent = save_msg_silent; - emsg_silent = save_emsg_silent; - redir_off = save_redir_off; - - /* Only print the message again when typing a command before coming back - * here. */ - debug_did_msg = TRUE; - } - - static int - get_maxbacktrace_level(void) - { - char *p, *q; - int maxbacktrace = 0; - - if (sourcing_name != NULL) - { - p = (char *)sourcing_name; - while ((q = strstr(p, "..")) != NULL) - { - p = q + 2; - maxbacktrace++; - } - } - return maxbacktrace; - } - - static void - do_setdebugtracelevel(char_u *arg) - { - int level; - - level = atoi((char *)arg); - if (*arg == '+' || level < 0) - debug_backtrace_level += level; - else - debug_backtrace_level = level; - - do_checkbacktracelevel(); - } - - static void - do_checkbacktracelevel(void) - { - if (debug_backtrace_level < 0) - { - debug_backtrace_level = 0; - msg(_("frame is zero")); - } - else - { - int max = get_maxbacktrace_level(); - - if (debug_backtrace_level > max) - { - debug_backtrace_level = max; - smsg(_("frame at highest level: %d"), max); - } - } - } - - static void - do_showbacktrace(char_u *cmd) - { - char *cur; - char *next; - int i = 0; - int max = get_maxbacktrace_level(); - - if (sourcing_name != NULL) - { - cur = (char *)sourcing_name; - while (!got_int) - { - next = strstr(cur, ".."); - if (next != NULL) - *next = NUL; - if (i == max - debug_backtrace_level) - smsg("->%d %s", max - i, cur); - else - smsg(" %d %s", max - i, cur); - ++i; - if (next == NULL) - break; - *next = '.'; - cur = next + 2; - } - } - if (sourcing_lnum != 0) - smsg(_("line %ld: %s"), (long)sourcing_lnum, cmd); - else - smsg(_("cmd: %s"), cmd); - } - - /* - * ":debug". - */ - void - ex_debug(exarg_T *eap) - { - int debug_break_level_save = debug_break_level; - - debug_break_level = 9999; - do_cmdline_cmd(eap->arg); - debug_break_level = debug_break_level_save; - } - - static char_u *debug_breakpoint_name = NULL; - static linenr_T debug_breakpoint_lnum; - - /* - * When debugging or a breakpoint is set on a skipped command, no debug prompt - * is shown by do_one_cmd(). This situation is indicated by debug_skipped, and - * debug_skipped_name is then set to the source name in the breakpoint case. If - * a skipped command decides itself that a debug prompt should be displayed, it - * can do so by calling dbg_check_skipped(). - */ - static int debug_skipped; - static char_u *debug_skipped_name; - - /* - * Go to debug mode when a breakpoint was encountered or "ex_nesting_level" is - * at or below the break level. But only when the line is actually - * executed. Return TRUE and set breakpoint_name for skipped commands that - * decide to execute something themselves. - * Called from do_one_cmd() before executing a command. - */ - void - dbg_check_breakpoint(exarg_T *eap) - { - char_u *p; - - debug_skipped = FALSE; - if (debug_breakpoint_name != NULL) - { - if (!eap->skip) - { - /* replace K_SNR with "" */ - if (debug_breakpoint_name[0] == K_SPECIAL - && debug_breakpoint_name[1] == KS_EXTRA - && debug_breakpoint_name[2] == (int)KE_SNR) - p = (char_u *)""; - else - p = (char_u *)""; - smsg(_("Breakpoint in \"%s%s\" line %ld"), - p, - debug_breakpoint_name + (*p == NUL ? 0 : 3), - (long)debug_breakpoint_lnum); - debug_breakpoint_name = NULL; - do_debug(eap->cmd); - } - else - { - debug_skipped = TRUE; - debug_skipped_name = debug_breakpoint_name; - debug_breakpoint_name = NULL; - } - } - else if (ex_nesting_level <= debug_break_level) - { - if (!eap->skip) - do_debug(eap->cmd); - else - { - debug_skipped = TRUE; - debug_skipped_name = NULL; - } - } - } - - /* - * Go to debug mode if skipped by dbg_check_breakpoint() because eap->skip was - * set. Return TRUE when the debug mode is entered this time. - */ - int - dbg_check_skipped(exarg_T *eap) - { - int prev_got_int; - - if (debug_skipped) - { - /* - * Save the value of got_int and reset it. We don't want a previous - * interruption cause flushing the input buffer. - */ - prev_got_int = got_int; - got_int = FALSE; - debug_breakpoint_name = debug_skipped_name; - /* eap->skip is TRUE */ - eap->skip = FALSE; - (void)dbg_check_breakpoint(eap); - eap->skip = TRUE; - got_int |= prev_got_int; - return TRUE; - } - return FALSE; - } - - /* - * The list of breakpoints: dbg_breakp. - * This is a grow-array of structs. - */ - struct debuggy - { - int dbg_nr; /* breakpoint number */ - int dbg_type; /* DBG_FUNC, DBG_FILE or DBG_EXPR */ - char_u *dbg_name; /* function, expression or file name */ - regprog_T *dbg_prog; /* regexp program */ - linenr_T dbg_lnum; /* line number in function or file */ - int dbg_forceit; /* ! used */ - #ifdef FEAT_EVAL - typval_T *dbg_val; /* last result of watchexpression */ - #endif - int dbg_level; /* stored nested level for expr */ - }; - - static garray_T dbg_breakp = {0, 0, sizeof(struct debuggy), 4, NULL}; - #define BREAKP(idx) (((struct debuggy *)dbg_breakp.ga_data)[idx]) - #define DEBUGGY(gap, idx) (((struct debuggy *)gap->ga_data)[idx]) - static int last_breakp = 0; /* nr of last defined breakpoint */ - - #ifdef FEAT_PROFILE - /* Profiling uses file and func names similar to breakpoints. */ - static garray_T prof_ga = {0, 0, sizeof(struct debuggy), 4, NULL}; - #endif - #define DBG_FUNC 1 - #define DBG_FILE 2 - #define DBG_EXPR 3 - - static linenr_T debuggy_find(int file,char_u *fname, linenr_T after, garray_T *gap, int *fp); - - /* - * Parse the arguments of ":profile", ":breakadd" or ":breakdel" and put them - * in the entry just after the last one in dbg_breakp. Note that "dbg_name" - * is allocated. - * Returns FAIL for failure. - */ - static int - dbg_parsearg( - char_u *arg, - garray_T *gap) /* either &dbg_breakp or &prof_ga */ - { - char_u *p = arg; - char_u *q; - struct debuggy *bp; - int here = FALSE; - - if (ga_grow(gap, 1) == FAIL) - return FAIL; - bp = &DEBUGGY(gap, gap->ga_len); - - /* Find "func" or "file". */ - if (STRNCMP(p, "func", 4) == 0) - bp->dbg_type = DBG_FUNC; - else if (STRNCMP(p, "file", 4) == 0) - bp->dbg_type = DBG_FILE; - else if ( - #ifdef FEAT_PROFILE - gap != &prof_ga && - #endif - STRNCMP(p, "here", 4) == 0) - { - if (curbuf->b_ffname == NULL) - { - emsg(_(e_noname)); - return FAIL; - } - bp->dbg_type = DBG_FILE; - here = TRUE; - } - else if ( - #ifdef FEAT_PROFILE - gap != &prof_ga && - #endif - STRNCMP(p, "expr", 4) == 0) - bp->dbg_type = DBG_EXPR; - else - { - semsg(_(e_invarg2), p); - return FAIL; - } - p = skipwhite(p + 4); - - /* Find optional line number. */ - if (here) - bp->dbg_lnum = curwin->w_cursor.lnum; - else if ( - #ifdef FEAT_PROFILE - gap != &prof_ga && - #endif - VIM_ISDIGIT(*p)) - { - bp->dbg_lnum = getdigits(&p); - p = skipwhite(p); - } - else - bp->dbg_lnum = 0; - - /* Find the function or file name. Don't accept a function name with (). */ - if ((!here && *p == NUL) - || (here && *p != NUL) - || (bp->dbg_type == DBG_FUNC && strstr((char *)p, "()") != NULL)) - { - semsg(_(e_invarg2), arg); - return FAIL; - } - - if (bp->dbg_type == DBG_FUNC) - bp->dbg_name = vim_strsave(p); - else if (here) - bp->dbg_name = vim_strsave(curbuf->b_ffname); - else if (bp->dbg_type == DBG_EXPR) - { - bp->dbg_name = vim_strsave(p); - if (bp->dbg_name != NULL) - bp->dbg_val = eval_expr(bp->dbg_name, NULL); - } - else - { - /* Expand the file name in the same way as do_source(). This means - * doing it twice, so that $DIR/file gets expanded when $DIR is - * "~/dir". */ - q = expand_env_save(p); - if (q == NULL) - return FAIL; - p = expand_env_save(q); - vim_free(q); - if (p == NULL) - return FAIL; - if (*p != '*') - { - bp->dbg_name = fix_fname(p); - vim_free(p); - } - else - bp->dbg_name = p; - } - - if (bp->dbg_name == NULL) - return FAIL; - return OK; - } - - /* - * ":breakadd". Also used for ":profile". - */ - void - ex_breakadd(exarg_T *eap) - { - struct debuggy *bp; - char_u *pat; - garray_T *gap; - - gap = &dbg_breakp; - #ifdef FEAT_PROFILE - if (eap->cmdidx == CMD_profile) - gap = &prof_ga; - #endif - - if (dbg_parsearg(eap->arg, gap) == OK) - { - bp = &DEBUGGY(gap, gap->ga_len); - bp->dbg_forceit = eap->forceit; - - if (bp->dbg_type != DBG_EXPR) - { - pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, FALSE); - if (pat != NULL) - { - bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING); - vim_free(pat); - } - if (pat == NULL || bp->dbg_prog == NULL) - vim_free(bp->dbg_name); - else - { - if (bp->dbg_lnum == 0) /* default line number is 1 */ - bp->dbg_lnum = 1; - #ifdef FEAT_PROFILE - if (eap->cmdidx != CMD_profile) - #endif - { - DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp; - ++debug_tick; - } - ++gap->ga_len; - } - } - else - { - /* DBG_EXPR */ - DEBUGGY(gap, gap->ga_len++).dbg_nr = ++last_breakp; - ++debug_tick; - } - } - } - - /* - * ":debuggreedy". - */ - void - ex_debuggreedy(exarg_T *eap) - { - if (eap->addr_count == 0 || eap->line2 != 0) - debug_greedy = TRUE; - else - debug_greedy = FALSE; - } - - /* - * ":breakdel" and ":profdel". - */ - void - ex_breakdel(exarg_T *eap) - { - struct debuggy *bp, *bpi; - int nr; - int todel = -1; - int del_all = FALSE; - int i; - linenr_T best_lnum = 0; - garray_T *gap; - - gap = &dbg_breakp; - if (eap->cmdidx == CMD_profdel) - { - #ifdef FEAT_PROFILE - gap = &prof_ga; - #else - ex_ni(eap); - return; - #endif - } - - if (vim_isdigit(*eap->arg)) - { - /* ":breakdel {nr}" */ - nr = atol((char *)eap->arg); - for (i = 0; i < gap->ga_len; ++i) - if (DEBUGGY(gap, i).dbg_nr == nr) - { - todel = i; - break; - } - } - else if (*eap->arg == '*') - { - todel = 0; - del_all = TRUE; - } - else - { - /* ":breakdel {func|file|expr} [lnum] {name}" */ - if (dbg_parsearg(eap->arg, gap) == FAIL) - return; - bp = &DEBUGGY(gap, gap->ga_len); - for (i = 0; i < gap->ga_len; ++i) - { - bpi = &DEBUGGY(gap, i); - if (bp->dbg_type == bpi->dbg_type - && STRCMP(bp->dbg_name, bpi->dbg_name) == 0 - && (bp->dbg_lnum == bpi->dbg_lnum - || (bp->dbg_lnum == 0 - && (best_lnum == 0 - || bpi->dbg_lnum < best_lnum)))) - { - todel = i; - best_lnum = bpi->dbg_lnum; - } - } - vim_free(bp->dbg_name); - } - - if (todel < 0) - semsg(_("E161: Breakpoint not found: %s"), eap->arg); - else - { - while (gap->ga_len > 0) - { - vim_free(DEBUGGY(gap, todel).dbg_name); - #ifdef FEAT_EVAL - if (DEBUGGY(gap, todel).dbg_type == DBG_EXPR - && DEBUGGY(gap, todel).dbg_val != NULL) - free_tv(DEBUGGY(gap, todel).dbg_val); - #endif - vim_regfree(DEBUGGY(gap, todel).dbg_prog); - --gap->ga_len; - if (todel < gap->ga_len) - mch_memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1), - (gap->ga_len - todel) * sizeof(struct debuggy)); - #ifdef FEAT_PROFILE - if (eap->cmdidx == CMD_breakdel) - #endif - ++debug_tick; - if (!del_all) - break; - } - - /* If all breakpoints were removed clear the array. */ - if (gap->ga_len == 0) - ga_clear(gap); - } - } - - /* - * ":breaklist". - */ - void - ex_breaklist(exarg_T *eap UNUSED) - { - struct debuggy *bp; - int i; - - if (dbg_breakp.ga_len == 0) - msg(_("No breakpoints defined")); - else - for (i = 0; i < dbg_breakp.ga_len; ++i) - { - bp = &BREAKP(i); - if (bp->dbg_type == DBG_FILE) - home_replace(NULL, bp->dbg_name, NameBuff, MAXPATHL, TRUE); - if (bp->dbg_type != DBG_EXPR) - smsg(_("%3d %s %s line %ld"), - bp->dbg_nr, - bp->dbg_type == DBG_FUNC ? "func" : "file", - bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff, - (long)bp->dbg_lnum); - else - smsg(_("%3d expr %s"), - bp->dbg_nr, bp->dbg_name); - } - } - - /* - * Find a breakpoint for a function or sourced file. - * Returns line number at which to break; zero when no matching breakpoint. - */ - linenr_T - dbg_find_breakpoint( - int file, /* TRUE for a file, FALSE for a function */ - char_u *fname, /* file or function name */ - linenr_T after) /* after this line number */ - { - return debuggy_find(file, fname, after, &dbg_breakp, NULL); - } - - #if defined(FEAT_PROFILE) || defined(PROTO) - /* - * Return TRUE if profiling is on for a function or sourced file. - */ - int - has_profiling( - int file, /* TRUE for a file, FALSE for a function */ - char_u *fname, /* file or function name */ - int *fp) /* return: forceit */ - { - return (debuggy_find(file, fname, (linenr_T)0, &prof_ga, fp) - != (linenr_T)0); - } - #endif - - /* - * Common code for dbg_find_breakpoint() and has_profiling(). - */ - static linenr_T - debuggy_find( - int file, /* TRUE for a file, FALSE for a function */ - char_u *fname, /* file or function name */ - linenr_T after, /* after this line number */ - garray_T *gap, /* either &dbg_breakp or &prof_ga */ - int *fp) /* if not NULL: return forceit */ - { - struct debuggy *bp; - int i; - linenr_T lnum = 0; - char_u *name = fname; - int prev_got_int; - - /* Return quickly when there are no breakpoints. */ - if (gap->ga_len == 0) - return (linenr_T)0; - - /* Replace K_SNR in function name with "". */ - if (!file && fname[0] == K_SPECIAL) - { - name = alloc((unsigned)STRLEN(fname) + 3); - if (name == NULL) - name = fname; - else - { - STRCPY(name, ""); - STRCPY(name + 5, fname + 3); - } - } - - for (i = 0; i < gap->ga_len; ++i) - { - /* Skip entries that are not useful or are for a line that is beyond - * an already found breakpoint. */ - bp = &DEBUGGY(gap, i); - if (((bp->dbg_type == DBG_FILE) == file && - bp->dbg_type != DBG_EXPR && ( - #ifdef FEAT_PROFILE - gap == &prof_ga || - #endif - (bp->dbg_lnum > after && (lnum == 0 || bp->dbg_lnum < lnum))))) - { - /* - * Save the value of got_int and reset it. We don't want a - * previous interruption cancel matching, only hitting CTRL-C - * while matching should abort it. - */ - prev_got_int = got_int; - got_int = FALSE; - if (vim_regexec_prog(&bp->dbg_prog, FALSE, name, (colnr_T)0)) - { - lnum = bp->dbg_lnum; - if (fp != NULL) - *fp = bp->dbg_forceit; - } - got_int |= prev_got_int; - } - #ifdef FEAT_EVAL - else if (bp->dbg_type == DBG_EXPR) - { - typval_T *tv; - int line = FALSE; - - prev_got_int = got_int; - got_int = FALSE; - - tv = eval_expr(bp->dbg_name, NULL); - if (tv != NULL) - { - if (bp->dbg_val == NULL) - { - debug_oldval = typval_tostring(NULL); - bp->dbg_val = tv; - debug_newval = typval_tostring(bp->dbg_val); - line = TRUE; - } - else - { - if (typval_compare(tv, bp->dbg_val, TYPE_EQUAL, - TRUE, FALSE) == OK - && tv->vval.v_number == FALSE) - { - typval_T *v; - - line = TRUE; - debug_oldval = typval_tostring(bp->dbg_val); - /* Need to evaluate again, typval_compare() overwrites - * "tv". */ - v = eval_expr(bp->dbg_name, NULL); - debug_newval = typval_tostring(v); - free_tv(bp->dbg_val); - bp->dbg_val = v; - } - free_tv(tv); - } - } - else if (bp->dbg_val != NULL) - { - debug_oldval = typval_tostring(bp->dbg_val); - debug_newval = typval_tostring(NULL); - free_tv(bp->dbg_val); - bp->dbg_val = NULL; - line = TRUE; - } - - if (line) - { - lnum = after > 0 ? after : 1; - break; - } - - got_int |= prev_got_int; - } - #endif - } - if (name != fname) - vim_free(name); - - return lnum; - } - - /* - * Called when a breakpoint was encountered. - */ - void - dbg_breakpoint(char_u *name, linenr_T lnum) - { - /* We need to check if this line is actually executed in do_one_cmd() */ - debug_breakpoint_name = name; - debug_breakpoint_lnum = lnum; - } - - # if defined(FEAT_PROFILE) || defined(FEAT_RELTIME) || defined(PROTO) /* * Store the current time in "tm". --- 66,71 ---- *** ../vim-8.1.1194/src/proto.h 2019-03-30 13:53:26.174425093 +0100 --- src/proto.h 2019-04-21 11:18:57.688977840 +0200 *************** *** 68,73 **** --- 68,74 ---- # ifdef FEAT_CSCOPE # include "if_cscope.pro" # endif + # include "debugger.pro" # include "dict.pro" # include "diff.pro" # include "digraph.pro" *** ../vim-8.1.1194/src/proto/debugger.pro 2019-04-21 11:33:08.875781192 +0200 --- src/proto/debugger.pro 2019-04-21 11:18:57.688977840 +0200 *************** *** 0 **** --- 1,14 ---- + /* debugger.c */ + int has_watchexpr(void); + void do_debug(char_u *cmd); + void ex_debug(exarg_T *eap); + void dbg_check_breakpoint(exarg_T *eap); + int dbg_check_skipped(exarg_T *eap); + void ex_breakadd(exarg_T *eap); + void ex_debuggreedy(exarg_T *eap); + void ex_breakdel(exarg_T *eap); + void ex_breaklist(exarg_T *eap); + linenr_T dbg_find_breakpoint(int file, char_u *fname, linenr_T after); + int has_profiling(int file, char_u *fname, int *fp); + void dbg_breakpoint(char_u *name, linenr_T lnum); + /* vim: set ft=c : */ *** ../vim-8.1.1194/src/proto/ex_cmds2.pro 2019-04-04 18:15:05.770857065 +0200 --- src/proto/ex_cmds2.pro 2019-04-21 11:18:57.688977840 +0200 *************** *** 1,16 **** /* ex_cmds2.c */ - int has_watchexpr(void); - void do_debug(char_u *cmd); - void ex_debug(exarg_T *eap); - void dbg_check_breakpoint(exarg_T *eap); - int dbg_check_skipped(exarg_T *eap); - void ex_breakadd(exarg_T *eap); - void ex_debuggreedy(exarg_T *eap); - void ex_breakdel(exarg_T *eap); - void ex_breaklist(exarg_T *eap); - linenr_T dbg_find_breakpoint(int file, char_u *fname, linenr_T after); - int has_profiling(int file, char_u *fname, int *fp); - void dbg_breakpoint(char_u *name, linenr_T lnum); void profile_start(proftime_T *tm); void profile_end(proftime_T *tm); void profile_sub(proftime_T *tm, proftime_T *tm2); --- 1,4 ---- *** ../vim-8.1.1194/src/version.c 2019-04-21 00:00:07.946354820 +0200 --- src/version.c 2019-04-21 11:20:15.084493742 +0200 *************** *** 773,774 **** --- 773,776 ---- { /* Add new patch number below this line */ + /**/ + 1195, /**/ -- FATHER: Make sure the Prince doesn't leave this room until I come and get him. FIRST GUARD: Not ... to leave the room ... even if you come and get him. FATHER: No. Until I come and get him. SECOND GUARD: Hic. "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///