📄 ex_docmd.c
字号:
/* 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.
*/
/*
* ex_docmd.c: functions for executing an Ex command line.
*/
#include "vim.h"
#define DO_DECLARE_EXCMD
#include "ex_cmds.h" /* Declare the cmdnames struct. */
#ifdef HAVE_FCNTL_H
# include <fcntl.h> /* for chdir() */
#endif
static int quitmore = 0;
static int ex_pressedreturn = FALSE;
#ifdef WANT_EVAL
/*
* For conditional commands a stack is kept of nested conditionals.
* When cs_idx < 0, there is no conditional command.
*/
#define CSTACK_LEN 50
struct condstack
{
char cs_flags[CSTACK_LEN]; /* CSF_ flags */
int cs_line[CSTACK_LEN]; /* line number of ":while" line */
int cs_idx; /* current entry, or -1 if none */
int cs_whilelevel; /* number of nested ":while"s */
char cs_had_while; /* just found ":while" */
char cs_had_continue; /* just found ":continue" */
char cs_had_endwhile; /* just found ":endwhile" */
};
#define CSF_TRUE 1 /* condition was TRUE */
#define CSF_ACTIVE 2 /* current state is active */
#define CSF_WHILE 4 /* is a ":while" */
#endif
#ifdef USER_COMMANDS
typedef struct ucmd
{
char_u *uc_name; /* The command name */
long 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 */
} UCMD;
struct growarray ucmds = { 0, 0, sizeof(UCMD), 4, NULL };
#define USER_CMD(i) (&((UCMD *)(ucmds.ga_data))[i])
#endif
#ifdef WANT_EVAL
static void free_cmdlines __ARGS((struct growarray *gap));
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));
#endif
static int buf_write_all __ARGS((BUF *));
static int do_write __ARGS((EXARG *eap));
static char_u *getargcmd __ARGS((char_u **));
static char_u *skip_cmd_arg __ARGS((char_u *p));
#ifdef QUICKFIX
static void do_make __ARGS((char_u *, char_u *));
static char_u *get_mef_name __ARGS((int newname));
static void do_cfile __ARGS((EXARG *eap));
#endif
static int do_arglist __ARGS((char_u *));
static int rem_backslash __ARGS((char_u *str));
static int check_readonly __ARGS((int *forceit));
static int check_changed __ARGS((BUF *, int, int, int, int));
#if defined(GUI_DIALOG) || defined(CON_DIALOG)
static void dialog_changed __ARGS((BUF *buf, int checkall));
#endif
#if defined(USE_BROWSE) && (defined(GUI_DIALOG) || defined(CON_DIALOG))
static void browse_save_fname __ARGS((BUF *buf));
#endif
static int check_more __ARGS((int, int));
static linenr_t get_address __ARGS((char_u **, int skip));
static char_u * invalid_range __ARGS((EXARG *eap));
static void correct_range __ARGS((EXARG *eap));
static void do_quit __ARGS((EXARG *eap));
static void do_quit_all __ARGS((int forceit));
static void do_close __ARGS((EXARG *eap));
static void do_suspend __ARGS((int forceit));
static void do_exit __ARGS((EXARG *eap));
static void do_wqall __ARGS((EXARG *eap));
static void do_print __ARGS((EXARG *eap));
static void do_argfile __ARGS((EXARG *eap, int argn));
static void do_next __ARGS((EXARG *eap));
static void do_recover __ARGS((EXARG *eap));
static void do_args __ARGS((EXARG *eap));
static void do_wnext __ARGS((EXARG *eap));
static void do_resize __ARGS((EXARG *eap));
static void do_splitview __ARGS((EXARG *eap));
static void do_exedit __ARGS((EXARG *eap, WIN *old_curwin));
#ifdef USE_GUI
static void do_gui __ARGS((EXARG *eap));
#endif
static void do_swapname __ARGS((void));
static void do_read __ARGS((EXARG *eap));
static void do_cd __ARGS((EXARG *eap));
static void do_pwd __ARGS((void));
static void do_sleep __ARGS((EXARG *eap));
static void do_exmap __ARGS((EXARG *eap, int isabbrev));
static void do_winsize __ARGS((char_u *arg));
static void do_exops __ARGS((EXARG *eap));
static void do_copymove __ARGS((EXARG *eap));
static void do_exjoin __ARGS((EXARG *eap));
static void do_exat __ARGS((EXARG *eap));
static void do_redir __ARGS((EXARG *eap));
static void close_redir __ARGS((void));
static void do_mkrc __ARGS((EXARG *eap, char_u *defname));
static FILE *open_exfile __ARGS((EXARG *eap, char *mode));
static void do_setmark __ARGS((EXARG *eap));
#ifdef EX_EXTRA
static void do_normal __ARGS((EXARG *eap));
#endif
#ifdef FIND_IN_PATH
static char_u *do_findpat __ARGS((EXARG *eap, int action));
#endif
static void do_ex_tag __ARGS((EXARG *eap, int dt));
#ifdef WANT_EVAL
static char_u *do_if __ARGS((EXARG *eap, struct condstack *cstack));
static char_u *do_else __ARGS((EXARG *eap, struct condstack *cstack));
static char_u *do_while __ARGS((EXARG *eap, struct condstack *cstack));
static char_u *do_continue __ARGS((struct condstack *cstack));
static char_u *do_break __ARGS((struct condstack *cstack));
static char_u *do_endwhile __ARGS((struct condstack *cstack));
static int has_while_cmd __ARGS((char_u *p));
static int did_endif = FALSE; /* just had ":endif" */
#endif
static int makeopens __ARGS((FILE *fd));
static int put_eol __ARGS((FILE *fd));
static void cmd_source __ARGS((char_u *fname, int forceit));
#ifdef USER_COMMANDS
static int uc_add_command __ARGS((char_u *name, size_t name_len, char_u *rep, long argt, long def, int compl, int force));
static void uc_list __ARGS((char_u *name, size_t name_len));
static int uc_scan_attr __ARGS((char_u *attr, size_t len, long *argt, long *def, int *compl));
static void do_command __ARGS((EXARG *eap));
static void do_comclear __ARGS((void));
static void do_delcommand __ARGS((EXARG *eap));
static char_u *uc_split_args __ARGS((char_u *arg, size_t *lenp));
static size_t uc_check_code __ARGS((char_u *code, size_t len, char_u *buf, UCMD *cmd, EXARG *eap, char_u **split_buf, size_t *split_len));
static void do_ucmd __ARGS((UCMD *cmd, EXARG *eap));
#endif
#if defined(GUI_DIALOG) || defined(CON_DIALOG)
static void dialog_msg __ARGS((char_u *buff, char *format, char_u *fname));
#endif
static void ex_behave __ARGS((char_u *arg));
/*
* Table used to quickly search for a command, based on its first character.
*/
CMDIDX 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_Next
};
/*
* do_exmode(): Repeatedly get commands for the "Ex" mode, until the ":vi"
* command is given.
*/
void
do_exmode()
{
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 */
settmode(TMODE_COOK);
State = NORMAL;
exmode_active = TRUE;
#ifdef USE_SNIFF
want_sniff_request = 0; /* No K_SNIFF wanted */
#endif
MSG("Entering Ex mode. Type \"visual\" to get out.");
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 USE_SNIFF
ProcessSniffRequests();
#endif
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("At end-of-file");
}
settmode(TMODE_RAW);
--RedrawingDisabled;
--no_wait_return;
update_screen(CLEAR);
need_wait_return = FALSE;
msg_scroll = save_msg_scroll;
}
/*
* 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.
*
* 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 */
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 USE_BROWSE
int save_browse = browse;
#endif
#if defined(GUI_DIALOG) || defined(CON_DIALOG)
int save_confirm = confirm;
#endif
#ifdef WANT_EVAL
struct condstack cstack; /* conditional stack */
struct growarray lines_ga; /* keep lines for ":while" */
int current_line = 0; /* active line in lines_ga */
#endif
#ifdef WANT_EVAL
cstack.cs_idx = -1;
cstack.cs_whilelevel = 0;
cstack.cs_had_while = FALSE;
cstack.cs_had_endwhile = FALSE;
cstack.cs_had_continue = FALSE;
ga_init2(&lines_ga, (int)sizeof(char_u *), 10);
#endif
/*
* Reset browse and confirm. They are restored when returning, for
* recursive calls.
*/
#ifdef USE_BROWSE
browse = FALSE;
#endif
#if defined(GUI_DIALOG) || defined(CON_DIALOG)
confirm = 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.
*/
did_emsg = FALSE;
/*
* KeyTyped is only set when calling vgetc(). Reset it here when not
* calling vgetc() (sourced command lines).
*/
if (getline != 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 WANT_EVAL
&& cstack.cs_idx < 0
#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 WANT_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 aborted */
if (getline == get_func_line && func_has_ended(cookie))
{
retval = FAIL;
break;
}
next_cmdline = ((char_u **)(lines_ga.ga_data))[current_line];
}
#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 == getexline)
msg_didout = TRUE;
if (getline == NULL || (next_cmdline = getline(':', cookie,
#ifdef WANT_EVAL
cstack.cs_idx < 0 ? 0 : (cstack.cs_idx + 1) * 2
#else
0
#endif
)) == NULL)
{
/* don't call wait_return for aborted command line */
if (KeyTyped)
need_wait_return = FALSE;
retval = FAIL;
break;
}
}
/* 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)
{
retval = FAIL;
break;
}
}
cmdline_copy = next_cmdline;
#ifdef WANT_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))
&& ga_grow(&lines_ga, 1) == OK)
{
((char_u **)(lines_ga.ga_data))[current_line] =
vim_strsave(next_cmdline);
++lines_ga.ga_len;
--lines_ga.ga_room;
}
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;
}
}
/*
* 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 WANT_EVAL
&cstack,
#endif
getline, cookie);
--recursive;
if (next_cmdline == NULL)
{
vim_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 == getexline && new_last_cmdline != NULL)
{
vim_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() */
mch_memmove(cmdline_copy, next_cmdline, STRLEN(next_cmdline) + 1);
next_cmdline = cmdline_copy;
}
#ifdef WANT_EVAL
/* reset did_emsg for a function that is not aborted by an error */
if (did_emsg && getline == get_func_line && !func_has_abort(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"? */
if (!did_emsg && 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 */
}
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 a ":while", clear remembered lines.
*/
if (!cstack.cs_whilelevel)
{
free_cmdlines(&lines_ga);
current_line = 0;
}
#endif /* WANT_EVAL */
}
/*
* Continue executing command lines when:
* - no CTRL-C typed
* - didn't get an error message or lines are not typed
* - there is a command after '|', inside a :if or :while, or looping for
* ":source" command.
*/
while (!got_int
&& !(did_emsg && (getline == getexmodeline || getline == getexline))
&& (next_cmdline != NULL
#ifdef WANT_EVAL
|| cstack.cs_idx >= 0
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -