diff options
Diffstat (limited to 'src/ex_docmd.c')
-rw-r--r-- | src/ex_docmd.c | 9711 |
1 files changed, 9711 insertions, 0 deletions
diff --git a/src/ex_docmd.c b/src/ex_docmd.c new file mode 100644 index 0000000000..f1331e918d --- /dev/null +++ b/src/ex_docmd.c @@ -0,0 +1,9711 @@ +/* vi:set ts=8 sts=4 sw=4: + * + * 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 "vim.h" + +#ifdef HAVE_FCNTL_H +# include <fcntl.h> /* for chdir() */ +#endif + +static int quitmore = 0; +static int ex_pressedreturn = FALSE; +#ifndef FEAT_PRINTER +# define ex_hardcopy ex_ni +#endif + +#ifdef FEAT_USR_CMDS +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 */ + scid_T uc_scriptID; /* SID where the command was defined */ + int uc_compl; /* completion type */ +# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL) + char_u *uc_compl_arg; /* completion argument if any */ +# endif +} ucmd_T; + +#define UC_BUFFER 1 /* -buffer: local to current buffer */ + +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 __ARGS((exarg_T *eap)); +static void ex_command __ARGS((exarg_T *eap)); +static void ex_comclear __ARGS((exarg_T *eap)); +static void ex_delcommand __ARGS((exarg_T *eap)); +# ifdef FEAT_CMDL_COMPL +static char_u *get_user_command_name __ARGS((int idx)); +# endif + +#else +# define ex_command ex_ni +# define ex_comclear ex_ni +# define ex_delcommand ex_ni +#endif + +#ifdef FEAT_EVAL +static char_u *do_one_cmd __ARGS((char_u **, int, struct condstack *, char_u *(*getline)(int, void *, int), void *cookie)); +#else +static char_u *do_one_cmd __ARGS((char_u **, int, char_u *(*getline)(int, void *, int), void *cookie)); +static int if_level = 0; /* depth in :if */ +#endif +static int checkforcmd __ARGS((char_u **pp, char *cmd, int len)); +static char_u *find_command __ARGS((exarg_T *eap, int *full)); + +static void ex_abbreviate __ARGS((exarg_T *eap)); +static void ex_map __ARGS((exarg_T *eap)); +static void ex_unmap __ARGS((exarg_T *eap)); +static void ex_mapclear __ARGS((exarg_T *eap)); +static void ex_abclear __ARGS((exarg_T *eap)); +#ifndef FEAT_MENU +# define ex_emenu ex_ni +# define ex_menu ex_ni +# define ex_menutranslate ex_ni +#endif +#ifdef FEAT_AUTOCMD +static void ex_autocmd __ARGS((exarg_T *eap)); +static void ex_doautocmd __ARGS((exarg_T *eap)); +#else +# define ex_autocmd ex_ni +# define ex_doautocmd ex_ni +# define ex_doautoall ex_ni +#endif +#ifdef FEAT_LISTCMDS +static void ex_bunload __ARGS((exarg_T *eap)); +static void ex_buffer __ARGS((exarg_T *eap)); +static void ex_bmodified __ARGS((exarg_T *eap)); +static void ex_bnext __ARGS((exarg_T *eap)); +static void ex_bprevious __ARGS((exarg_T *eap)); +static void ex_brewind __ARGS((exarg_T *eap)); +static void ex_blast __ARGS((exarg_T *eap)); +#else +# define ex_bunload ex_ni +# define ex_buffer ex_ni +# define ex_bmodified ex_ni +# define ex_bnext ex_ni +# define ex_bprevious ex_ni +# define ex_brewind ex_ni +# define ex_blast ex_ni +# define buflist_list ex_ni +# define ex_checktime ex_ni +#endif +#if !defined(FEAT_LISTCMDS) || !defined(FEAT_WINDOWS) +# define ex_buffer_all ex_ni +#endif +static char_u *getargcmd __ARGS((char_u **)); +static char_u *skip_cmd_arg __ARGS((char_u *p, int rembs)); +static int getargopt __ARGS((exarg_T *eap)); +#ifndef FEAT_QUICKFIX +# define ex_make ex_ni +# define ex_cc ex_ni +# define ex_cnext ex_ni +# define ex_cfile ex_ni +# define qf_list ex_ni +# define qf_age ex_ni +# define ex_helpgrep ex_ni +#endif +#if !defined(FEAT_QUICKFIX) || !defined(FEAT_WINDOWS) +# define ex_cclose ex_ni +# define ex_copen ex_ni +# define ex_cwindow ex_ni +#endif + +static int check_more __ARGS((int, int)); +static linenr_T get_address __ARGS((char_u **, int skip, int to_other_file)); +#if !defined(FEAT_PERL) || !defined(FEAT_PYTHON) || !defined(FEAT_TCL) \ + || !defined(FEAT_RUBY) +static void ex_script_ni __ARGS((exarg_T *eap)); +#endif +static char_u *invalid_range __ARGS((exarg_T *eap)); +static void correct_range __ARGS((exarg_T *eap)); +static char_u *repl_cmdline __ARGS((exarg_T *eap, char_u *src, int srclen, char_u *repl, char_u **cmdlinep)); +static void ex_highlight __ARGS((exarg_T *eap)); +static void ex_colorscheme __ARGS((exarg_T *eap)); +static void ex_quit __ARGS((exarg_T *eap)); +static void ex_cquit __ARGS((exarg_T *eap)); +static void ex_quit_all __ARGS((exarg_T *eap)); +#ifdef FEAT_WINDOWS +static void ex_close __ARGS((exarg_T *eap)); +static void ex_win_close __ARGS((exarg_T *eap, win_T *win)); +static void ex_only __ARGS((exarg_T *eap)); +static void ex_all __ARGS((exarg_T *eap)); +static void ex_resize __ARGS((exarg_T *eap)); +static void ex_stag __ARGS((exarg_T *eap)); +#else +# define ex_close ex_ni +# define ex_only ex_ni +# define ex_all ex_ni +# define ex_resize ex_ni +# define ex_splitview ex_ni +# define ex_stag ex_ni +#endif +#if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) +static void ex_pclose __ARGS((exarg_T *eap)); +static void ex_ptag __ARGS((exarg_T *eap)); +static void ex_pedit __ARGS((exarg_T *eap)); +#else +# define ex_pclose ex_ni +# define ex_ptag ex_ni +# define ex_pedit ex_ni +#endif +static void ex_hide __ARGS((exarg_T *eap)); +static void ex_stop __ARGS((exarg_T *eap)); +static void ex_exit __ARGS((exarg_T *eap)); +static void ex_print __ARGS((exarg_T *eap)); +#ifdef FEAT_BYTEOFF +static void ex_goto __ARGS((exarg_T *eap)); +#else +# define ex_goto ex_ni +#endif +static void ex_shell __ARGS((exarg_T *eap)); +static void ex_preserve __ARGS((exarg_T *eap)); +static void ex_recover __ARGS((exarg_T *eap)); +#ifndef FEAT_LISTCMDS +# define ex_argedit ex_ni +# define ex_argadd ex_ni +# define ex_argdelete ex_ni +# define ex_listdo ex_ni +#endif +static void ex_mode __ARGS((exarg_T *eap)); +static void ex_wrongmodifier __ARGS((exarg_T *eap)); +static void ex_find __ARGS((exarg_T *eap)); +static void ex_edit __ARGS((exarg_T *eap)); +#if !defined(FEAT_GUI) && !defined(FEAT_CLIENTSERVER) +# define ex_drop ex_ni +#endif +#ifndef FEAT_GUI +# define ex_gui ex_nogui +static void ex_nogui __ARGS((exarg_T *eap)); +#endif +#if defined(FEAT_GUI_W32) && defined(FEAT_MENU) && defined(FEAT_TEAROFF) +static void ex_tearoff __ARGS((exarg_T *eap)); +#else +# define ex_tearoff ex_ni +#endif +#if (defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_GTK)) && defined(FEAT_MENU) +static void ex_popup __ARGS((exarg_T *eap)); +#else +# define ex_popup ex_ni +#endif +#ifndef FEAT_GUI_MSWIN +# define ex_simalt ex_ni +#endif +#if !defined(FEAT_GUI_MSWIN) && !defined(FEAT_GUI_GTK) && !defined(FEAT_GUI_MOTIF) +# define gui_mch_find_dialog ex_ni +# define gui_mch_replace_dialog ex_ni +#endif +#ifndef FEAT_GUI_GTK +# define ex_helpfind ex_ni +#endif +#ifndef FEAT_CSCOPE +# define do_cscope ex_ni +# define do_scscope ex_ni +# define do_cstag ex_ni +#endif +#ifndef FEAT_SYN_HL +# define ex_syntax ex_ni +#endif +#ifndef FEAT_PERL +# define ex_perl ex_script_ni +# define ex_perldo ex_ni +#endif +#ifndef FEAT_PYTHON +# define ex_python ex_script_ni +# define ex_pyfile ex_ni +#endif +#ifndef FEAT_TCL +# define ex_tcl ex_script_ni +# define ex_tcldo ex_ni +# define ex_tclfile ex_ni +#endif +#ifndef FEAT_RUBY +# define ex_ruby ex_script_ni +# define ex_rubydo ex_ni +# define ex_rubyfile ex_ni +#endif +#ifndef FEAT_SNIFF +# define ex_sniff ex_ni +#endif +#ifndef FEAT_KEYMAP +# define ex_loadkeymap ex_ni +#endif +static void ex_swapname __ARGS((exarg_T *eap)); +static void ex_syncbind __ARGS((exarg_T *eap)); +static void ex_read __ARGS((exarg_T *eap)); +static void ex_cd __ARGS((exarg_T *eap)); +static void ex_pwd __ARGS((exarg_T *eap)); +static void ex_equal __ARGS((exarg_T *eap)); +static void ex_sleep __ARGS((exarg_T *eap)); +static void do_exmap __ARGS((exarg_T *eap, int isabbrev)); +static void ex_winsize __ARGS((exarg_T *eap)); +#ifdef FEAT_WINDOWS +static void ex_wincmd __ARGS((exarg_T *eap)); +#else +# define ex_wincmd ex_ni +#endif +#if defined(FEAT_GUI) || defined(UNIX) || defined(VMS) +static void ex_winpos __ARGS((exarg_T *eap)); +#else +# define ex_winpos ex_ni +#endif +static void ex_operators __ARGS((exarg_T *eap)); +static void ex_put __ARGS((exarg_T *eap)); +static void ex_copymove __ARGS((exarg_T *eap)); +static void ex_submagic __ARGS((exarg_T *eap)); +static void ex_join __ARGS((exarg_T *eap)); +static void ex_at __ARGS((exarg_T *eap)); +static void ex_bang __ARGS((exarg_T *eap)); +static void ex_undo __ARGS((exarg_T *eap)); +static void ex_redo __ARGS((exarg_T *eap)); +static void ex_redir __ARGS((exarg_T *eap)); +static void ex_redraw __ARGS((exarg_T *eap)); +static void ex_redrawstatus __ARGS((exarg_T *eap)); +static void close_redir __ARGS((void)); +static void ex_mkrc __ARGS((exarg_T *eap)); +static void ex_mark __ARGS((exarg_T *eap)); +#ifdef FEAT_USR_CMDS +static char_u *uc_fun_cmd __ARGS((void)); +#endif +#ifdef FEAT_EX_EXTRA +static void ex_normal __ARGS((exarg_T *eap)); +static void ex_startinsert __ARGS((exarg_T *eap)); +static void ex_stopinsert __ARGS((exarg_T *eap)); +#else +# define ex_normal ex_ni +# define ex_align ex_ni +# define ex_retab ex_ni +# define ex_startinsert ex_ni +# define ex_stopinsert ex_ni +# define ex_helptags ex_ni +#endif +#ifdef FEAT_FIND_ID +static void ex_checkpath __ARGS((exarg_T *eap)); +static void ex_findpat __ARGS((exarg_T *eap)); +#else +# define ex_findpat ex_ni +# define ex_checkpath ex_ni +#endif +#if defined(FEAT_FIND_ID) && defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) +static void ex_psearch __ARGS((exarg_T *eap)); +#else +# define ex_psearch ex_ni +#endif +static void ex_tag __ARGS((exarg_T *eap)); +static void ex_tag_cmd __ARGS((exarg_T *eap, char_u *name)); +#ifndef FEAT_EVAL +# define ex_scriptnames ex_ni +# define ex_finish ex_ni +# define ex_echo ex_ni +# define ex_echohl ex_ni +# define ex_execute ex_ni +# define ex_call ex_ni +# define ex_if ex_ni +# define ex_endif ex_ni +# define ex_else ex_ni +# define ex_while ex_ni +# define ex_continue ex_ni +# define ex_break ex_ni +# define ex_endwhile ex_ni +# define ex_throw ex_ni +# define ex_try ex_ni +# define ex_catch ex_ni +# define ex_finally ex_ni +# define ex_endtry ex_ni +# define ex_endfunction ex_ni +# define ex_let ex_ni +# define ex_unlet ex_ni +# define ex_function ex_ni +# define ex_delfunction ex_ni +# define ex_return ex_ni +#endif +static char_u *arg_all __ARGS((void)); +#ifdef FEAT_SESSION +static int makeopens __ARGS((FILE *fd, char_u *dirnow)); +static int put_view __ARGS((FILE *fd, win_T *wp, int add_edit, unsigned *flagp)); +static void ex_loadview __ARGS((exarg_T *eap)); +static char_u *get_view_file __ARGS((int c)); +#else +# define ex_loadview ex_ni +#endif +#ifndef FEAT_EVAL +# define ex_compiler ex_ni +#endif +#ifdef FEAT_VIMINFO +static void ex_viminfo __ARGS((exarg_T *eap)); +#else +# define ex_viminfo ex_ni +#endif +static void ex_behave __ARGS((exarg_T *eap)); +#ifdef FEAT_AUTOCMD +static void ex_filetype __ARGS((exarg_T *eap)); +static void ex_setfiletype __ARGS((exarg_T *eap)); +#else +# define ex_filetype ex_ni +# define ex_setfiletype ex_ni +#endif +#ifndef FEAT_DIFF +# define ex_diffpatch ex_ni +# define ex_diffgetput ex_ni +# define ex_diffsplit ex_ni +# define ex_diffthis ex_ni +# define ex_diffupdate ex_ni +#endif +static void ex_digraphs __ARGS((exarg_T *eap)); +static void ex_set __ARGS((exarg_T *eap)); +#if !defined(FEAT_EVAL) || !defined(FEAT_AUTOCMD) +# define ex_options ex_ni +#endif +#ifdef FEAT_SEARCH_EXTRA +static void ex_nohlsearch __ARGS((exarg_T *eap)); +static void ex_match __ARGS((exarg_T *eap)); +#else +# define ex_nohlsearch ex_ni +# define ex_match ex_ni +#endif +#ifdef FEAT_CRYPT +static void ex_X __ARGS((exarg_T *eap)); +#else +# define ex_X ex_ni +#endif +#ifdef FEAT_FOLDING +static void ex_fold __ARGS((exarg_T *eap)); +static void ex_foldopen __ARGS((exarg_T *eap)); +static void ex_folddo __ARGS((exarg_T *eap)); +#else +# define ex_fold ex_ni +# define ex_foldopen ex_ni +# define ex_folddo ex_ni +#endif +#if !((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \ + && (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE))) +# define ex_language ex_ni +#endif +#ifndef FEAT_SIGNS +# define ex_sign ex_ni +#endif +#ifndef FEAT_SUN_WORKSHOP +# define ex_wsverb ex_ni +#endif + +#ifndef FEAT_EVAL +# define ex_debug ex_ni +# define ex_breakadd ex_ni +# define ex_debuggreedy ex_ni +# define ex_breakdel ex_ni +# define ex_breaklist ex_ni +#endif + +#ifndef FEAT_CMDHIST +# define ex_history ex_ni +#endif +#ifndef FEAT_JUMPLIST +# define ex_jumps ex_ni +# define ex_changes ex_ni +#endif + +/* + * Declare cmdnames[]. + */ +#define DO_DECLARE_EXCMD +#include "ex_cmds.h" + +/* + * Table used to quickly search for a command, based on its first character. + */ +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}; + + +#ifdef FEAT_EVAL +/* Struct for storing a line inside a while 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 loop. + * This is required, because do_one_cmd() may invoke ex_function(), which + * reads more lines that may come from the while loop. + */ +struct while_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) __ARGS((int, void *, int)); + void *cookie; +}; + +static char_u *get_while_line __ARGS((int c, void *cookie, int indent)); +static int store_while_line __ARGS((garray_T *gap, char_u *line)); +static void free_cmdlines __ARGS((garray_T *gap)); +#endif + + +/* + * do_exmode(): Repeatedly get commands for the "Ex" mode, until the ":vi" + * command is given. + */ + void +do_exmode(improved) + int improved; /* TRUE for "improved Ex" mode */ +{ + int save_msg_scroll; + int prev_msg_row; + linenr_T prev_line; + + save_msg_scroll = msg_scroll; + ++RedrawingDisabled; /* don't redisplay the window */ + ++no_wait_return; /* don't wait for return */ + if (improved) + exmode_active = EXMODE_VIM; + else + { + settmode(TMODE_COOK); + exmode_active = EXMODE_NORMAL; + } + + State = NORMAL; +#ifdef FEAT_GUI + /* Ignore scrollbar and mouse events in Ex mode */ + ++hold_gui_events; +#endif +#ifdef FEAT_SNIFF + want_sniff_request = 0; /* No K_SNIFF wanted */ +#endif + + MSG(_("Entering Ex mode. Type \"visual\" to go to Normal mode.")); + while (exmode_active) + { + msg_scroll = TRUE; + need_wait_return = FALSE; + ex_pressedreturn = FALSE; + ex_no_reprint = FALSE; + prev_msg_row = msg_row; + prev_line = curwin->w_cursor.lnum; +#ifdef FEAT_SNIFF + ProcessSniffRequests(); +#endif + 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 && !ex_no_reprint) + { + if (ex_pressedreturn) + { + /* go up one line, to overwrite the ":<CR>" line, so the + * output doensn'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); + msg_clr_eos(); + } + else if (ex_pressedreturn) /* must be at EOF */ + EMSG(_("E501: At end-of-file")); + } + +#ifdef FEAT_GUI + --hold_gui_events; +#endif + if (!improved) + settmode(TMODE_RAW); + --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(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, getline() 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 getline() 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, getline, cookie, flags) + char_u *cmdline; + char_u *(*getline) __ARGS((int, void *, int)); + void *cookie; /* argument for getline() */ + int flags; +{ + char_u *next_cmdline; /* next cmd to execute */ + char_u *cmdline_copy = NULL; /* copy of cmd line */ + int used_getline = FALSE; /* used "getline" 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; +#ifdef FEAT_EVAL + struct condstack cstack; /* conditional stack */ + garray_T lines_ga; /* keep lines for ":while" */ + 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 */ + int saved_trylevel = 0; + int saved_force_abort = 0; + except_T *saved_caught_stack = NULL; + char_u *saved_vv_exception = NULL; + char_u *saved_vv_throwpoint = NULL; + int saved_did_emsg = 0; + int saved_got_int = 0; + int saved_did_throw = 0; + int saved_need_rethrow = 0; + int saved_check_cstack = 0; + except_T *saved_current_exception = NULL; + int initial_trylevel; + struct msglist **saved_msg_list = NULL; + struct msglist *private_msg_list; + + /* "getline" and "cookie" passed to do_one_cmd() */ + char_u *(*cmd_getline) __ARGS((int, void *, int)); + void *cmd_cookie; + struct while_cookie cmd_while_cookie; + void *real_cookie; +#else +# define cmd_getline getline +# define cmd_cookie cookie +#endif + static int call_depth = 0; /* recursiveness */ + +#ifdef FEAT_EVAL + /* 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; +#endif + + /* 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")); +#ifdef FEAT_EVAL + /* 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; +#endif + return FAIL; + } + ++call_depth; + +#ifdef FEAT_EVAL + cstack.cs_idx = -1; + cstack.cs_whilelevel = 0; + cstack.cs_trylevel = 0; + cstack.cs_emsg_silent_list = NULL; + cstack.cs_had_while = FALSE; + cstack.cs_had_endwhile = FALSE; + cstack.cs_had_continue = FALSE; + cstack.cs_had_finally = FALSE; + ga_init2(&lines_ga, (int)sizeof(wcmd_T), 10); + + real_cookie = getline_cookie(getline, cookie); + + /* Inside a function use a higher nesting level. */ + if (getline_equal(getline, cookie, get_func_line) + && 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_equal(getline, cookie, get_func_line)) + { + fname = func_name(real_cookie); + breakpoint = func_breakpoint(real_cookie); + dbg_tick = func_dbg_tick(real_cookie); + } + else if (getline_equal(getline, 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). + */ + else if (flags & DOCMD_EXCRESET) + { + saved_trylevel = trylevel; trylevel = 0; + saved_force_abort = force_abort; force_abort = FALSE; + saved_caught_stack = caught_stack; caught_stack = NULL; + saved_vv_exception = v_exception(NULL); + saved_vv_throwpoint = v_throwpoint(NULL); + /* Necessary for debugging an inactive ":catch", ":finally", or + * ":endtry": */ + saved_did_emsg = did_emsg, did_emsg = FALSE; + saved_got_int = got_int, got_int = FALSE; + saved_did_throw = did_throw, did_throw = FALSE; + saved_need_rethrow = need_rethrow, need_rethrow = FALSE; + saved_check_cstack = check_cstack, check_cstack = FALSE; + saved_current_exception = current_exception; current_exception=NULL; + } + + initial_trylevel = trylevel; + + /* + * "did_throw" will be set to TRUE when an exception is being thrown. + */ + did_throw = FALSE; +#endif + /* + * "did_emsg" will be set to TRUE when emsg() is used, in which case we + * cancel the whole command line, and any if/endif while/endwhile 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(getline, cookie, getexline)) + KeyTyped = FALSE; + + /* + * Continue executing command lines: + * - when inside an ":if" or ":while" + * - for multiple commands on one line, separated with '|' + * - when repeating until there are no more lines (for ":source") + */ + next_cmdline = cmdline; + do + { + /* stop skipping cmds for an error msg after all endifs and endwhiles */ + if (next_cmdline == NULL +#ifdef FEAT_EVAL + && !force_abort + && cstack.cs_idx < 0 + && !(getline_equal(getline, cookie, get_func_line) + && func_has_abort(real_cookie)) +#endif + ) + did_emsg = FALSE; + + /* + * 1. If repeating a line with ":while", get a line from lines_ga. + * 2. If no line given: Get an allocated line with getline(). + * 3. If a line is given: Make a copy, so we can mess with it. + */ + +#ifdef FEAT_EVAL + /* 1. If repeating, get a previous line from lines_ga. */ + if (cstack.cs_whilelevel && 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. */ + vim_free(cmdline_copy); + cmdline_copy = NULL; + + /* Check if a function has returned or, unless it has an unclosed + * try conditional, aborted. */ + if (getline_equal(getline, cookie, get_func_line) + && func_has_ended(real_cookie)) + { + retval = FAIL; + break; + } + + /* Check if a sourced file hit a ":finish" command. */ + if (source_finished(getline, 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(getline, 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(getline, cookie, getsourceline), + fname, sourcing_lnum); + *dbg_tick = debug_tick; + } + } + + if (cstack.cs_whilelevel) + { + /* Inside a while loop we need to store the lines and use them + * again. Pass a different "getline" 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 loop. */ + cmd_getline = get_while_line; + cmd_cookie = (void *)&cmd_while_cookie; + cmd_while_cookie.lines_gap = &lines_ga; + cmd_while_cookie.current_line = current_line; + cmd_while_cookie.getline = getline; + cmd_while_cookie.cookie = cookie; + cmd_while_cookie.repeating = (current_line < lines_ga.ga_len); + } + else + { + cmd_getline = getline; + cmd_cookie = cookie; + } +#endif + + /* 2. If no line given, get an allocated line with getline(). */ + 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(getline, cookie, getexline)) + msg_didout = TRUE; + if (getline == NULL || (next_cmdline = getline(':', cookie, +#ifdef FEAT_EVAL + cstack.cs_idx < 0 ? 0 : (cstack.cs_idx + 1) * 2 +#else + 0 +#endif + )) == 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) + { + vim_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; + +#ifdef FEAT_EVAL + /* + * Save the current line when inside a ":while", and when the command + * looks like a ":while", 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. + */ + if ( current_line == lines_ga.ga_len + && (cstack.cs_whilelevel || has_while_cmd(next_cmdline))) + { + if (store_while_line(&lines_ga, next_cmdline) == FAIL) + { + retval = FAIL; + break; + } + } + did_endif = FALSE; +#endif + + 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; /* dont wait for return until finished */ + ++RedrawingDisabled; + did_inc = TRUE; + } + } + + if (p_verbose >= 15 && sourcing_name != NULL) + { + int c = -1; + + ++no_wait_return; + msg_scroll = TRUE; /* always scroll up, don't overwrite */ + /* Truncate long lines, smsg() can't handle that. */ + if (STRLEN(cmdline_copy) > 200) + { + c = cmdline_copy[200]; + cmdline_copy[200] = NUL; + } + smsg((char_u *)_("line %ld: %s"), + (long)sourcing_lnum, cmdline_copy); + msg_puts((char_u *)"\n"); /* don't overwrite this either */ + if (c >= 0) + cmdline_copy[200] = c; + cmdline_row = msg_row; + --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, +#ifdef FEAT_EVAL + &cstack, +#endif + cmd_getline, cmd_cookie); + --recursive; + +#ifdef FEAT_EVAL + if (cmd_cookie == (void *)&cmd_while_cookie) + /* Use "current_line" from "cmd_while_cookie", it may have been + * incremented when defining a function. */ + current_line = cmd_while_cookie.current_line; +#endif + + if (next_cmdline == NULL) + { + vim_free(cmdline_copy); + cmdline_copy = NULL; +#ifdef FEAT_CMDHIST + /* + * If the command was typed, remember it for the ':' register. + * Do this AFTER executing the command to make :@: work. + */ + if (getline_equal(getline, cookie, getexline) + && new_last_cmdline != NULL) + { + vim_free(last_cmdline); + last_cmdline = new_last_cmdline; + new_last_cmdline = NULL; + } +#endif + } + else + { + /* need to copy the command after the '|' to cmdline_copy, for the + * next do_one_cmd() */ + mch_memmove(cmdline_copy, next_cmdline, STRLEN(next_cmdline) + 1); + next_cmdline = cmdline_copy; + } + + +#ifdef FEAT_EVAL + /* reset did_emsg for a function that is not aborted by an error */ + if (did_emsg && !force_abort + && getline_equal(getline, cookie, get_func_line) + && !func_has_abort(real_cookie)) + did_emsg = FALSE; + + if (cstack.cs_whilelevel) + { + ++current_line; + + /* + * An ":endwhile" and ":continue" is handled here. + * If we were executing commands, jump back to the ":while". + * If we were not executing commands, decrement whilelevel. + */ + if (cstack.cs_had_endwhile || cstack.cs_had_continue) + { + if (cstack.cs_had_endwhile) + cstack.cs_had_endwhile = FALSE; + else + cstack.cs_had_continue = FALSE; + + /* Jump back to the matching ":while". Be careful not to use + * a cs_line[] from an entry that isn't a ":while": 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) + && cstack.cs_line[cstack.cs_idx] >= 0 + && (cstack.cs_flags[cstack.cs_idx] & CSF_ACTIVE)) + { + current_line = cstack.cs_line[cstack.cs_idx]; + cstack.cs_had_while = TRUE; /* note we jumped there */ + line_breakcheck(); /* check if CTRL-C typed */ + + /* Check for the next breakpoint at or after the ":while".*/ + if (breakpoint != NULL) + { + *breakpoint = dbg_find_breakpoint( + getline_equal(getline, cookie, getsourceline), + fname, + ((wcmd_T *)lines_ga.ga_data)[current_line].lnum-1); + *dbg_tick = debug_tick; + } + } + else /* can only get here with ":endwhile" */ + { + --cstack.cs_whilelevel; + if (cstack.cs_idx >= 0) + --cstack.cs_idx; + } + } + + /* + * For a ":while" we need to remember the line number. + */ + else if (cstack.cs_had_while) + { + cstack.cs_had_while = FALSE; + cstack.cs_line[cstack.cs_idx] = current_line - 1; + } + } + + /* + * When not inside any ":while" loop, clear remembered lines. + */ + if (!cstack.cs_whilelevel) + { + 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" or ":endwhile" was + * detected by the ":finally" itself. + */ + if (cstack.cs_had_finally) + { + cstack.cs_had_finally = FALSE; + 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 (accross 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); +#endif /* FEAT_EVAL */ + + } + /* + * Continue executing command lines when: |