⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ex_docmd.c

📁 VIM文本编辑器
💻 C
📖 第 1 页 / 共 5 页
字号:
/* 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 + -