diff options
| author | Eliseo Martínez <eliseomarmol@gmail.com> | 2014-05-12 02:25:17 +0200 |
|---|---|---|
| committer | Eliseo Martínez <eliseomarmol@gmail.com> | 2014-05-15 20:46:01 +0200 |
| commit | da51dc9cf202772f60bd2da975dbef257bd9237c (patch) | |
| tree | 5c16b93238a153f55634e9323077f30c8133970c /src/nvim/ex_docmd.c | |
| parent | ffe61e5ba1721340ca51d56bae3ddaca415fb5bc (diff) | |
| download | rneovim-da51dc9cf202772f60bd2da975dbef257bd9237c.tar.gz rneovim-da51dc9cf202772f60bd2da975dbef257bd9237c.tar.bz2 rneovim-da51dc9cf202772f60bd2da975dbef257bd9237c.zip | |
Introduce nvim namespace: Move files.
Move files from src/ to src/nvim/.
- src/nvim/ becomes the new root dir for nvim executable sources.
- src/libnvim/ is planned to become root dir of the neovim library.
Diffstat (limited to 'src/nvim/ex_docmd.c')
| -rw-r--r-- | src/nvim/ex_docmd.c | 9109 |
1 files changed, 9109 insertions, 0 deletions
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c new file mode 100644 index 0000000000..87ce252434 --- /dev/null +++ b/src/nvim/ex_docmd.c @@ -0,0 +1,9109 @@ +/* + * 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. + */ + +/* + * ex_docmd.c: functions for executing an Ex command line. + */ + +#include <string.h> + +#include "vim.h" +#include "ex_docmd.h" +#include "blowfish.h" +#include "buffer.h" +#include "charset.h" +#include "diff.h" +#include "digraph.h" +#include "edit.h" +#include "eval.h" +#include "ex_cmds.h" +#include "ex_cmds2.h" +#include "ex_eval.h" +#include "ex_getln.h" +#include "fileio.h" +#include "fold.h" +#include "getchar.h" +#include "hardcopy.h" +#include "if_cscope.h" +#include "main.h" +#include "mark.h" +#include "mbyte.h" +#include "memline.h" +#include "memory.h" +#include "menu.h" +#include "message.h" +#include "misc1.h" +#include "misc2.h" +#include "keymap.h" +#include "crypt.h" +#include "file_search.h" +#include "garray.h" +#include "move.h" +#include "normal.h" +#include "ops.h" +#include "option.h" +#include "os_unix.h" +#include "path.h" +#include "quickfix.h" +#include "regexp.h" +#include "screen.h" +#include "search.h" +#include "spell.h" +#include "syntax.h" +#include "tag.h" +#include "term.h" +#include "ui.h" +#include "undo.h" +#include "version.h" +#include "window.h" +#include "os/os.h" + +static int quitmore = 0; +static int ex_pressedreturn = FALSE; + +typedef struct ucmd { + char_u *uc_name; /* The command name */ + long_u uc_argt; /* The argument type */ + char_u *uc_rep; /* The command's replacement string */ + long uc_def; /* The default value for a range/count */ + int uc_compl; /* completion type */ + scid_T uc_scriptID; /* SID where the command was defined */ + char_u *uc_compl_arg; /* completion argument if any */ +} ucmd_T; + +#define UC_BUFFER 1 /* -buffer: local to current buffer */ + +static garray_T ucmds = {0, 0, sizeof(ucmd_T), 4, NULL}; + +#define USER_CMD(i) (&((ucmd_T *)(ucmds.ga_data))[i]) +#define USER_CMD_GA(gap, i) (&((ucmd_T *)((gap)->ga_data))[i]) + +static void do_ucmd(exarg_T *eap); +static void ex_command(exarg_T *eap); +static void ex_delcommand(exarg_T *eap); +static char_u *get_user_command_name(int idx); + + +static char_u *do_one_cmd(char_u **, int, struct condstack *, + char_u *(*fgetline)(int, void *, int), + void *cookie); +static void append_command(char_u *cmd); +static char_u *find_command(exarg_T *eap, int *full); + +static void ex_abbreviate(exarg_T *eap); +static void ex_map(exarg_T *eap); +static void ex_unmap(exarg_T *eap); +static void ex_mapclear(exarg_T *eap); +static void ex_abclear(exarg_T *eap); +static void ex_autocmd(exarg_T *eap); +static void ex_doautocmd(exarg_T *eap); +static void ex_bunload(exarg_T *eap); +static void ex_buffer(exarg_T *eap); +static void ex_bmodified(exarg_T *eap); +static void ex_bnext(exarg_T *eap); +static void ex_bprevious(exarg_T *eap); +static void ex_brewind(exarg_T *eap); +static void ex_blast(exarg_T *eap); +static char_u *getargcmd(char_u **); +static char_u *skip_cmd_arg(char_u *p, int rembs); +static int getargopt(exarg_T *eap); + +static int check_more(int, int); +static linenr_T get_address(char_u **, int skip, int to_other_file); +static void get_flags(exarg_T *eap); +# define HAVE_EX_SCRIPT_NI +static void ex_script_ni(exarg_T *eap); +static char_u *invalid_range(exarg_T *eap); +static void correct_range(exarg_T *eap); +static char_u *replace_makeprg(exarg_T *eap, char_u *p, + char_u **cmdlinep); +static char_u *repl_cmdline(exarg_T *eap, char_u *src, int srclen, + char_u *repl, + char_u **cmdlinep); +static void ex_highlight(exarg_T *eap); +static void ex_colorscheme(exarg_T *eap); +static void ex_quit(exarg_T *eap); +static void ex_cquit(exarg_T *eap); +static void ex_quit_all(exarg_T *eap); +static void ex_close(exarg_T *eap); +static void ex_win_close(int forceit, win_T *win, tabpage_T *tp); +static void ex_only(exarg_T *eap); +static void ex_resize(exarg_T *eap); +static void ex_stag(exarg_T *eap); +static void ex_tabclose(exarg_T *eap); +static void ex_tabonly(exarg_T *eap); +static void ex_tabnext(exarg_T *eap); +static void ex_tabmove(exarg_T *eap); +static void ex_tabs(exarg_T *eap); +static void ex_pclose(exarg_T *eap); +static void ex_ptag(exarg_T *eap); +static void ex_pedit(exarg_T *eap); +static void ex_hide(exarg_T *eap); +static void ex_stop(exarg_T *eap); +static void ex_exit(exarg_T *eap); +static void ex_print(exarg_T *eap); +static void ex_goto(exarg_T *eap); +static void ex_preserve(exarg_T *eap); +static void ex_recover(exarg_T *eap); +static void ex_mode(exarg_T *eap); +static void ex_wrongmodifier(exarg_T *eap); +static void ex_find(exarg_T *eap); +static void ex_open(exarg_T *eap); +static void ex_edit(exarg_T *eap); +# define ex_drop ex_ni +# define ex_gui ex_nogui +static void ex_nogui(exarg_T *eap); +# define ex_tearoff ex_ni +# define ex_popup ex_ni +# define ex_simalt ex_ni +# define gui_mch_find_dialog ex_ni +# define gui_mch_replace_dialog ex_ni +# define ex_helpfind ex_ni +static void ex_swapname(exarg_T *eap); +static void ex_syncbind(exarg_T *eap); +static void ex_read(exarg_T *eap); +static void ex_pwd(exarg_T *eap); +static void ex_equal(exarg_T *eap); +static void ex_sleep(exarg_T *eap); +static void do_exmap(exarg_T *eap, int isabbrev); +static void ex_winsize(exarg_T *eap); +static void ex_wincmd(exarg_T *eap); +#if defined(FEAT_GUI) || defined(UNIX) || defined(MSWIN) +static void ex_winpos(exarg_T *eap); +#else +# define ex_winpos ex_ni +#endif +static void ex_operators(exarg_T *eap); +static void ex_put(exarg_T *eap); +static void ex_copymove(exarg_T *eap); +static void ex_submagic(exarg_T *eap); +static void ex_join(exarg_T *eap); +static void ex_at(exarg_T *eap); +static void ex_bang(exarg_T *eap); +static void ex_undo(exarg_T *eap); +static void ex_wundo(exarg_T *eap); +static void ex_rundo(exarg_T *eap); +static void ex_redo(exarg_T *eap); +static void ex_later(exarg_T *eap); +static void ex_redir(exarg_T *eap); +static void ex_redraw(exarg_T *eap); +static void ex_redrawstatus(exarg_T *eap); +static void close_redir(void); +static void ex_mkrc(exarg_T *eap); +static void ex_mark(exarg_T *eap); +static char_u *uc_fun_cmd(void); +static char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, + expand_T *xp, + int *compl); +static void ex_normal(exarg_T *eap); +static void ex_startinsert(exarg_T *eap); +static void ex_stopinsert(exarg_T *eap); +static void ex_checkpath(exarg_T *eap); +static void ex_findpat(exarg_T *eap); +static void ex_psearch(exarg_T *eap); +static void ex_tag(exarg_T *eap); +static void ex_tag_cmd(exarg_T *eap, char_u *name); +static char_u *arg_all(void); +static int makeopens(FILE *fd, char_u *dirnow); +static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, + int current_arg_idx); +static void ex_loadview(exarg_T *eap); +static char_u *get_view_file(int c); +static int did_lcd; /* whether ":lcd" was produced for a session */ +static void ex_viminfo(exarg_T *eap); +static void ex_behave(exarg_T *eap); +static void ex_filetype(exarg_T *eap); +static void ex_setfiletype(exarg_T *eap); +static void ex_digraphs(exarg_T *eap); +static void ex_set(exarg_T *eap); +static void ex_nohlsearch(exarg_T *eap); +static void ex_match(exarg_T *eap); +static void ex_X(exarg_T *eap); +static void ex_fold(exarg_T *eap); +static void ex_foldopen(exarg_T *eap); +static void ex_folddo(exarg_T *eap); +#ifndef HAVE_WORKING_LIBINTL +# define ex_language ex_ni +#endif +# define ex_wsverb ex_ni +# define ex_nbclose ex_ni +# define ex_nbkey ex_ni +# define ex_nbstart ex_ni + + + + +/* + * Declare cmdnames[]. + */ +#define DO_DECLARE_EXCMD +#include "ex_cmds_defs.h" + +/* + * Table used to quickly search for a command, based on its first character. + */ +static cmdidx_T cmdidxs[27] = +{ + CMD_append, + CMD_buffer, + CMD_change, + CMD_delete, + CMD_edit, + CMD_file, + CMD_global, + CMD_help, + CMD_insert, + CMD_join, + CMD_k, + CMD_list, + CMD_move, + CMD_next, + CMD_open, + CMD_print, + CMD_quit, + CMD_read, + CMD_substitute, + CMD_t, + CMD_undo, + CMD_vglobal, + CMD_write, + CMD_xit, + CMD_yank, + CMD_z, + CMD_bang +}; + +static char_u dollar_command[2] = {'$', 0}; + + +/* Struct for storing a line inside a while/for loop */ +typedef struct { + char_u *line; /* command line */ + linenr_T lnum; /* sourcing_lnum of the line */ +} wcmd_T; + +/* + * Structure used to store info for line position in a while or for loop. + * This is required, because do_one_cmd() may invoke ex_function(), which + * reads more lines that may come from the while/for loop. + */ +struct loop_cookie { + garray_T *lines_gap; /* growarray with line info */ + int current_line; /* last read line from growarray */ + int repeating; /* TRUE when looping a second time */ + /* When "repeating" is FALSE use "getline" and "cookie" to get lines */ + char_u *(*getline)(int, void *, int); + void *cookie; +}; + +static char_u *get_loop_line(int c, void *cookie, int indent); +static void store_loop_line(garray_T *gap, char_u *line); +static void free_cmdlines(garray_T *gap); + +/* Struct to save a few things while debugging. Used in do_cmdline() only. */ +struct dbg_stuff { + int trylevel; + int force_abort; + except_T *caught_stack; + char_u *vv_exception; + char_u *vv_throwpoint; + int did_emsg; + int got_int; + int did_throw; + int need_rethrow; + int check_cstack; + except_T *current_exception; +}; + +static void save_dbg_stuff(struct dbg_stuff *dsp); +static void restore_dbg_stuff(struct dbg_stuff *dsp); + +static void save_dbg_stuff(struct dbg_stuff *dsp) +{ + dsp->trylevel = trylevel; trylevel = 0; + dsp->force_abort = force_abort; force_abort = FALSE; + dsp->caught_stack = caught_stack; caught_stack = NULL; + dsp->vv_exception = v_exception(NULL); + dsp->vv_throwpoint = v_throwpoint(NULL); + + /* Necessary for debugging an inactive ":catch", ":finally", ":endtry" */ + dsp->did_emsg = did_emsg; did_emsg = FALSE; + dsp->got_int = got_int; got_int = FALSE; + dsp->did_throw = did_throw; did_throw = FALSE; + dsp->need_rethrow = need_rethrow; need_rethrow = FALSE; + dsp->check_cstack = check_cstack; check_cstack = FALSE; + dsp->current_exception = current_exception; current_exception = NULL; +} + +static void restore_dbg_stuff(struct dbg_stuff *dsp) +{ + suppress_errthrow = FALSE; + trylevel = dsp->trylevel; + force_abort = dsp->force_abort; + caught_stack = dsp->caught_stack; + (void)v_exception(dsp->vv_exception); + (void)v_throwpoint(dsp->vv_throwpoint); + did_emsg = dsp->did_emsg; + got_int = dsp->got_int; + did_throw = dsp->did_throw; + need_rethrow = dsp->need_rethrow; + check_cstack = dsp->check_cstack; + current_exception = dsp->current_exception; +} + + +/* + * do_exmode(): Repeatedly get commands for the "Ex" mode, until the ":vi" + * command is given. + */ +void +do_exmode ( + int improved /* TRUE for "improved Ex" mode */ +) +{ + int save_msg_scroll; + int prev_msg_row; + linenr_T prev_line; + int changedtick; + + if (improved) + exmode_active = EXMODE_VIM; + else + exmode_active = EXMODE_NORMAL; + State = NORMAL; + + /* When using ":global /pat/ visual" and then "Q" we return to continue + * the :global command. */ + if (global_busy) + return; + + save_msg_scroll = msg_scroll; + ++RedrawingDisabled; /* don't redisplay the window */ + ++no_wait_return; /* don't wait for return */ + + MSG(_("Entering Ex mode. Type \"visual\" to go to Normal mode.")); + while (exmode_active) { + /* Check for a ":normal" command and no more characters left. */ + if (ex_normal_busy > 0 && typebuf.tb_len == 0) { + exmode_active = FALSE; + break; + } + msg_scroll = TRUE; + need_wait_return = FALSE; + ex_pressedreturn = FALSE; + ex_no_reprint = FALSE; + changedtick = curbuf->b_changedtick; + prev_msg_row = msg_row; + prev_line = curwin->w_cursor.lnum; + if (improved) { + cmdline_row = msg_row; + do_cmdline(NULL, getexline, NULL, 0); + } else + do_cmdline(NULL, getexmodeline, NULL, DOCMD_NOWAIT); + lines_left = Rows - 1; + + if ((prev_line != curwin->w_cursor.lnum + || changedtick != curbuf->b_changedtick) && !ex_no_reprint) { + if (curbuf->b_ml.ml_flags & ML_EMPTY) + EMSG(_(e_emptybuf)); + else { + if (ex_pressedreturn) { + /* go up one line, to overwrite the ":<CR>" line, so the + * output doesn't contain empty lines. */ + msg_row = prev_msg_row; + if (prev_msg_row == Rows - 1) + msg_row--; + } + msg_col = 0; + print_line_no_prefix(curwin->w_cursor.lnum, FALSE, FALSE); + msg_clr_eos(); + } + } else if (ex_pressedreturn && !ex_no_reprint) { /* must be at EOF */ + if (curbuf->b_ml.ml_flags & ML_EMPTY) + EMSG(_(e_emptybuf)); + else + EMSG(_("E501: At end-of-file")); + } + } + + --RedrawingDisabled; + --no_wait_return; + update_screen(CLEAR); + need_wait_return = FALSE; + msg_scroll = save_msg_scroll; +} + +/* + * Execute a simple command line. Used for translated commands like "*". + */ +int do_cmdline_cmd(char_u *cmd) +{ + return do_cmdline(cmd, NULL, NULL, + DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED); +} + +/* + * do_cmdline(): execute one Ex command line + * + * 1. Execute "cmdline" when it is not NULL. + * If "cmdline" is NULL, or more lines are needed, fgetline() is used. + * 2. Split up in parts separated with '|'. + * + * This function can be called recursively! + * + * flags: + * DOCMD_VERBOSE - The command will be included in the error message. + * DOCMD_NOWAIT - Don't call wait_return() and friends. + * DOCMD_REPEAT - Repeat execution until fgetline() returns NULL. + * DOCMD_KEYTYPED - Don't reset KeyTyped. + * DOCMD_EXCRESET - Reset the exception environment (used for debugging). + * DOCMD_KEEPLINE - Store first typed line (for repeating with "."). + * + * return FAIL if cmdline could not be executed, OK otherwise + */ +int do_cmdline(cmdline, fgetline, cookie, flags) +char_u *cmdline; +char_u *(*fgetline)(int, void *, int); +void *cookie; /* argument for fgetline() */ +int flags; +{ + char_u *next_cmdline; /* next cmd to execute */ + char_u *cmdline_copy = NULL; /* copy of cmd line */ + int used_getline = FALSE; /* used "fgetline" to obtain command */ + static int recursive = 0; /* recursive depth */ + int msg_didout_before_start = 0; + int count = 0; /* line number count */ + int did_inc = FALSE; /* incremented RedrawingDisabled */ + int retval = OK; + struct condstack cstack; /* conditional stack */ + garray_T lines_ga; /* keep lines for ":while"/":for" */ + int current_line = 0; /* active line in lines_ga */ + char_u *fname = NULL; /* function or script name */ + linenr_T *breakpoint = NULL; /* ptr to breakpoint field in cookie */ + int *dbg_tick = NULL; /* ptr to dbg_tick field in cookie */ + struct dbg_stuff debug_saved; /* saved things for debug mode */ + int initial_trylevel; + struct msglist **saved_msg_list = NULL; + struct msglist *private_msg_list; + + /* "fgetline" and "cookie" passed to do_one_cmd() */ + char_u *(*cmd_getline)(int, void *, int); + void *cmd_cookie; + struct loop_cookie cmd_loop_cookie; + void *real_cookie; + int getline_is_func; + static int call_depth = 0; /* recursiveness */ + + /* For every pair of do_cmdline()/do_one_cmd() calls, use an extra memory + * location for storing error messages to be converted to an exception. + * This ensures that the do_errthrow() call in do_one_cmd() does not + * combine the messages stored by an earlier invocation of do_one_cmd() + * with the command name of the later one. This would happen when + * BufWritePost autocommands are executed after a write error. */ + saved_msg_list = msg_list; + msg_list = &private_msg_list; + private_msg_list = NULL; + + /* It's possible to create an endless loop with ":execute", catch that + * here. The value of 200 allows nested function calls, ":source", etc. */ + if (call_depth == 200) { + EMSG(_("E169: Command too recursive")); + /* When converting to an exception, we do not include the command name + * since this is not an error of the specific command. */ + do_errthrow((struct condstack *)NULL, (char_u *)NULL); + msg_list = saved_msg_list; + return FAIL; + } + ++call_depth; + + cstack.cs_idx = -1; + cstack.cs_looplevel = 0; + cstack.cs_trylevel = 0; + cstack.cs_emsg_silent_list = NULL; + cstack.cs_lflags = 0; + ga_init(&lines_ga, (int)sizeof(wcmd_T), 10); + + real_cookie = getline_cookie(fgetline, cookie); + + /* Inside a function use a higher nesting level. */ + getline_is_func = getline_equal(fgetline, cookie, get_func_line); + if (getline_is_func && ex_nesting_level == func_level(real_cookie)) + ++ex_nesting_level; + + /* Get the function or script name and the address where the next breakpoint + * line and the debug tick for a function or script are stored. */ + if (getline_is_func) { + fname = func_name(real_cookie); + breakpoint = func_breakpoint(real_cookie); + dbg_tick = func_dbg_tick(real_cookie); + } else if (getline_equal(fgetline, cookie, getsourceline)) { + fname = sourcing_name; + breakpoint = source_breakpoint(real_cookie); + dbg_tick = source_dbg_tick(real_cookie); + } + + /* + * Initialize "force_abort" and "suppress_errthrow" at the top level. + */ + if (!recursive) { + force_abort = FALSE; + suppress_errthrow = FALSE; + } + + /* + * If requested, store and reset the global values controlling the + * exception handling (used when debugging). Otherwise clear it to avoid + * a bogus compiler warning when the optimizer uses inline functions... + */ + if (flags & DOCMD_EXCRESET) + save_dbg_stuff(&debug_saved); + else + memset(&debug_saved, 0, 1); + + initial_trylevel = trylevel; + + /* + * "did_throw" will be set to TRUE when an exception is being thrown. + */ + did_throw = FALSE; + /* + * "did_emsg" will be set to TRUE when emsg() is used, in which case we + * cancel the whole command line, and any if/endif or loop. + * If force_abort is set, we cancel everything. + */ + did_emsg = FALSE; + + /* + * KeyTyped is only set when calling vgetc(). Reset it here when not + * calling vgetc() (sourced command lines). + */ + if (!(flags & DOCMD_KEYTYPED) + && !getline_equal(fgetline, cookie, getexline)) + KeyTyped = FALSE; + + /* + * Continue executing command lines: + * - when inside an ":if", ":while" or ":for" + * - for multiple commands on one line, separated with '|' + * - when repeating until there are no more lines (for ":source") + */ + next_cmdline = cmdline; + do { + getline_is_func = getline_equal(fgetline, cookie, get_func_line); + + /* stop skipping cmds for an error msg after all endif/while/for */ + if (next_cmdline == NULL + && !force_abort + && cstack.cs_idx < 0 + && !(getline_is_func && func_has_abort(real_cookie)) + ) + did_emsg = FALSE; + + /* + * 1. If repeating a line in a loop, get a line from lines_ga. + * 2. If no line given: Get an allocated line with fgetline(). + * 3. If a line is given: Make a copy, so we can mess with it. + */ + + /* 1. If repeating, get a previous line from lines_ga. */ + if (cstack.cs_looplevel > 0 && current_line < lines_ga.ga_len) { + /* Each '|' separated command is stored separately in lines_ga, to + * be able to jump to it. Don't use next_cmdline now. */ + free(cmdline_copy); + cmdline_copy = NULL; + + /* Check if a function has returned or, unless it has an unclosed + * try conditional, aborted. */ + if (getline_is_func) { + if (do_profiling == PROF_YES) + func_line_end(real_cookie); + if (func_has_ended(real_cookie)) { + retval = FAIL; + break; + } + } else if (do_profiling == PROF_YES + && getline_equal(fgetline, cookie, getsourceline)) + script_line_end(); + + /* Check if a sourced file hit a ":finish" command. */ + if (source_finished(fgetline, cookie)) { + retval = FAIL; + break; + } + + /* If breakpoints have been added/deleted need to check for it. */ + if (breakpoint != NULL && dbg_tick != NULL + && *dbg_tick != debug_tick) { + *breakpoint = dbg_find_breakpoint( + getline_equal(fgetline, cookie, getsourceline), + fname, sourcing_lnum); + *dbg_tick = debug_tick; + } + + next_cmdline = ((wcmd_T *)(lines_ga.ga_data))[current_line].line; + sourcing_lnum = ((wcmd_T *)(lines_ga.ga_data))[current_line].lnum; + + /* Did we encounter a breakpoint? */ + if (breakpoint != NULL && *breakpoint != 0 + && *breakpoint <= sourcing_lnum) { + dbg_breakpoint(fname, sourcing_lnum); + /* Find next breakpoint. */ + *breakpoint = dbg_find_breakpoint( + getline_equal(fgetline, cookie, getsourceline), + fname, sourcing_lnum); + *dbg_tick = debug_tick; + } + if (do_profiling == PROF_YES) { + if (getline_is_func) + func_line_start(real_cookie); + else if (getline_equal(fgetline, cookie, getsourceline)) + script_line_start(); + } + } + + if (cstack.cs_looplevel > 0) { + /* Inside a while/for loop we need to store the lines and use them + * again. Pass a different "fgetline" function to do_one_cmd() + * below, so that it stores lines in or reads them from + * "lines_ga". Makes it possible to define a function inside a + * while/for loop. */ + cmd_getline = get_loop_line; + cmd_cookie = (void *)&cmd_loop_cookie; + cmd_loop_cookie.lines_gap = &lines_ga; + cmd_loop_cookie.current_line = current_line; + cmd_loop_cookie.getline = fgetline; + cmd_loop_cookie.cookie = cookie; + cmd_loop_cookie.repeating = (current_line < lines_ga.ga_len); + } else { + cmd_getline = fgetline; + cmd_cookie = cookie; + } + + /* 2. If no line given, get an allocated line with fgetline(). */ + if (next_cmdline == NULL) { + /* + * Need to set msg_didout for the first line after an ":if", + * otherwise the ":if" will be overwritten. + */ + if (count == 1 && getline_equal(fgetline, cookie, getexline)) + msg_didout = TRUE; + if (fgetline == NULL || (next_cmdline = fgetline(':', cookie, + cstack.cs_idx < + 0 ? 0 : (cstack.cs_idx + 1) * 2 + )) == NULL) { + /* Don't call wait_return for aborted command line. The NULL + * returned for the end of a sourced file or executed function + * doesn't do this. */ + if (KeyTyped && !(flags & DOCMD_REPEAT)) + need_wait_return = FALSE; + retval = FAIL; + break; + } + used_getline = TRUE; + + /* + * Keep the first typed line. Clear it when more lines are typed. + */ + if (flags & DOCMD_KEEPLINE) { + free(repeat_cmdline); + if (count == 0) + repeat_cmdline = vim_strsave(next_cmdline); + else + repeat_cmdline = NULL; + } + } + /* 3. Make a copy of the command so we can mess with it. */ + else if (cmdline_copy == NULL) { + next_cmdline = vim_strsave(next_cmdline); + if (next_cmdline == NULL) { + EMSG(_(e_outofmem)); + retval = FAIL; + break; + } + } + cmdline_copy = next_cmdline; + + /* + * Save the current line when inside a ":while" or ":for", and when + * the command looks like a ":while" or ":for", because we may need it + * later. When there is a '|' and another command, it is stored + * separately, because we need to be able to jump back to it from an + * :endwhile/:endfor. + */ + if (current_line == lines_ga.ga_len + && (cstack.cs_looplevel || has_loop_cmd(next_cmdline))) { + store_loop_line(&lines_ga, next_cmdline); + } + did_endif = FALSE; + + if (count++ == 0) { + /* + * All output from the commands is put below each other, without + * waiting for a return. Don't do this when executing commands + * from a script or when being called recursive (e.g. for ":e + * +command file"). + */ + if (!(flags & DOCMD_NOWAIT) && !recursive) { + msg_didout_before_start = msg_didout; + msg_didany = FALSE; /* no output yet */ + msg_start(); + msg_scroll = TRUE; /* put messages below each other */ + ++no_wait_return; /* don't wait for return until finished */ + ++RedrawingDisabled; + did_inc = TRUE; + } + } + + if (p_verbose >= 15 && sourcing_name != NULL) { + ++no_wait_return; + verbose_enter_scroll(); + + smsg((char_u *)_("line %" PRId64 ": %s"), + (int64_t)sourcing_lnum, cmdline_copy); + if (msg_silent == 0) + msg_puts((char_u *)"\n"); /* don't overwrite this */ + + verbose_leave_scroll(); + --no_wait_return; + } + + /* + * 2. Execute one '|' separated command. + * do_one_cmd() will return NULL if there is no trailing '|'. + * "cmdline_copy" can change, e.g. for '%' and '#' expansion. + */ + ++recursive; + next_cmdline = do_one_cmd(&cmdline_copy, flags & DOCMD_VERBOSE, + &cstack, + cmd_getline, cmd_cookie); + --recursive; + + if (cmd_cookie == (void *)&cmd_loop_cookie) + /* Use "current_line" from "cmd_loop_cookie", it may have been + * incremented when defining a function. */ + current_line = cmd_loop_cookie.current_line; + + if (next_cmdline == NULL) { + free(cmdline_copy); + cmdline_copy = NULL; + /* + * If the command was typed, remember it for the ':' register. + * Do this AFTER executing the command to make :@: work. + */ + if (getline_equal(fgetline, cookie, getexline) + && new_last_cmdline != NULL) { + free(last_cmdline); + last_cmdline = new_last_cmdline; + new_last_cmdline = NULL; + } + } else { + /* need to copy the command after the '|' to cmdline_copy, for the + * next do_one_cmd() */ + STRMOVE(cmdline_copy, next_cmdline); + next_cmdline = cmdline_copy; + } + + + /* reset did_emsg for a function that is not aborted by an error */ + if (did_emsg && !force_abort + && getline_equal(fgetline, cookie, get_func_line) + && !func_has_abort(real_cookie)) + did_emsg = FALSE; + + if (cstack.cs_looplevel > 0) { + ++current_line; + + /* + * An ":endwhile", ":endfor" and ":continue" is handled here. + * If we were executing commands, jump back to the ":while" or + * ":for". + * If we were not executing commands, decrement cs_looplevel. + */ + if (cstack.cs_lflags & (CSL_HAD_CONT | CSL_HAD_ENDLOOP)) { + cstack.cs_lflags &= ~(CSL_HAD_CONT | CSL_HAD_ENDLOOP); + + /* Jump back to the matching ":while" or ":for". Be careful + * not to use a cs_line[] from an entry that isn't a ":while" + * or ":for": It would make "current_line" invalid and can + * cause a crash. */ + if (!did_emsg && !got_int && !did_throw + && cstack.cs_idx >= 0 + && (cstack.cs_flags[cstack.cs_idx] + & (CSF_WHILE | CSF_FOR)) + && cstack.cs_line[cstack.cs_idx] >= 0 + && (cstack.cs_flags[cstack.cs_idx] & CSF_ACTIVE)) { + current_line = cstack.cs_line[cstack.cs_idx]; + /* remember we jumped there */ + cstack.cs_lflags |= CSL_HAD_LOOP; + line_breakcheck(); /* check if CTRL-C typed */ + + /* Check for the next breakpoint at or after the ":while" + * or ":for". */ + if (breakpoint != NULL) { + *breakpoint = dbg_find_breakpoint( + getline_equal(fgetline, cookie, getsourceline), + fname, + ((wcmd_T *)lines_ga.ga_data)[current_line].lnum-1); + *dbg_tick = debug_tick; + } + } else { + /* can only get here with ":endwhile" or ":endfor" */ + if (cstack.cs_idx >= 0) + rewind_conditionals(&cstack, cstack.cs_idx - 1, + CSF_WHILE | CSF_FOR, &cstack.cs_looplevel); + } + } + /* + * For a ":while" or ":for" we need to remember the line number. + */ + else if (cstack.cs_lflags & CSL_HAD_LOOP) { + cstack.cs_lflags &= ~CSL_HAD_LOOP; + cstack.cs_line[cstack.cs_idx] = current_line - 1; + } + } + + /* + * When not inside any ":while" loop, clear remembered lines. + */ + if (cstack.cs_looplevel == 0) { + if (lines_ga.ga_len > 0) { + sourcing_lnum = + ((wcmd_T *)lines_ga.ga_data)[lines_ga.ga_len - 1].lnum; + free_cmdlines(&lines_ga); + } + current_line = 0; + } + + /* + * A ":finally" makes did_emsg, got_int, and did_throw pending for + * being restored at the ":endtry". Reset them here and set the + * ACTIVE and FINALLY flags, so that the finally clause gets executed. + * This includes the case where a missing ":endif", ":endwhile" or + * ":endfor" was detected by the ":finally" itself. + */ + if (cstack.cs_lflags & CSL_HAD_FINA) { + cstack.cs_lflags &= ~CSL_HAD_FINA; + report_make_pending(cstack.cs_pending[cstack.cs_idx] + & (CSTP_ERROR | CSTP_INTERRUPT | CSTP_THROW), + did_throw ? (void *)current_exception : NULL); + did_emsg = got_int = did_throw = FALSE; + cstack.cs_flags[cstack.cs_idx] |= CSF_ACTIVE | CSF_FINALLY; + } + + /* Update global "trylevel" for recursive calls to do_cmdline() from + * within this loop. */ + trylevel = initial_trylevel + cstack.cs_trylevel; + + /* + * If the outermost try conditional (across function calls and sourced + * files) is aborted because of an error, an interrupt, or an uncaught + * exception, cancel everything. If it is left normally, reset + * force_abort to get the non-EH compatible abortion behavior for + * the rest of the script. + */ + if (trylevel == 0 && !did_emsg && !got_int && !did_throw) + force_abort = FALSE; + + /* Convert an interrupt to an exception if appropriate. */ + (void)do_intthrow(&cstack); + + } + /* + * Continue executing command lines when: + * - no CTRL-C typed, no aborting error, no exception thrown or try + * conditionals need to be checked for executing finally clauses or + * catching an interrupt exception + * - didn't get an error message or lines are not typed + * - there is a command after '|', inside a :if, :while, :for or :try, or + * looping for ":source" command or function call. + */ + while (!((got_int + || (did_emsg && force_abort) || did_throw + ) + && cstack.cs_trylevel == 0 + ) + && !(did_emsg + /* Keep going when inside try/catch, so that the error can be + * deal with, except when it is a syntax error, it may cause + * the :endtry to be missed. */ + && (cstack.cs_trylevel == 0 || did_emsg_syntax) + && used_getline + && (getline_equal(fgetline, cookie, getexmodeline) + || getline_equal(fgetline, cookie, getexline))) + && (next_cmdline != NULL + || cstack.cs_idx >= 0 + || (flags & DOCMD_REPEAT))); + + free(cmdline_copy); + did_emsg_syntax = FALSE; + free_cmdlines(&lines_ga); + ga_clear(&lines_ga); + + if (cstack.cs_idx >= 0) { + /* + * If a sourced file or executed function ran to its end, report the + * unclosed conditional. + */ + if (!got_int && !did_throw + && ((getline_equal(fgetline, cookie, getsourceline) + && !source_finished(fgetline, cookie)) + || (getline_equal(fgetline, cookie, get_func_line) + && !func_has_ended(real_cookie)))) { + if (cstack.cs_flags[cstack.cs_idx] & CSF_TRY) + EMSG(_(e_endtry)); + else if (cstack.cs_flags[cstack.cs_idx] & CSF_WHILE) + EMSG(_(e_endwhile)); + else if (cstack.cs_flags[cstack.cs_idx] & CSF_FOR) + EMSG(_(e_endfor)); + else + EMSG(_(e_endif)); + } + + /* + * Reset "trylevel" in case of a ":finish" or ":return" or a missing + * ":endtry" in a sourced file or executed function. If the try + * conditional is in its finally clause, ignore anything pending. + * If it is in a catch clause, finish the caught exception. + * Also cleanup any "cs_forinfo" structures. + */ + do { + int idx = cleanup_conditionals(&cstack, 0, TRUE); + + if (idx >= 0) + --idx; /* remove try block not in its finally clause */ + rewind_conditionals(&cstack, idx, CSF_WHILE | CSF_FOR, + &cstack.cs_looplevel); + } while (cstack.cs_idx >= 0); + trylevel = initial_trylevel; + } + + /* If a missing ":endtry", ":endwhile", ":endfor", or ":endif" or a memory + * lack was reported above and the error message is to be converted to an + * exception, do this now after rewinding the cstack. */ + do_errthrow(&cstack, getline_equal(fgetline, cookie, get_func_line) |