summaryrefslogtreecommitdiffstats
path: root/src/ex_docmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ex_docmd.c')
-rw-r--r--src/ex_docmd.c9711
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: