xref: /vim-8.2.3635/src/usercmd.c (revision a1198124)
1ac9fb180SBram Moolenaar /* vi:set ts=8 sts=4 sw=4 noet:
2ac9fb180SBram Moolenaar  *
3ac9fb180SBram Moolenaar  * VIM - Vi IMproved	by Bram Moolenaar
4ac9fb180SBram Moolenaar  *
5ac9fb180SBram Moolenaar  * Do ":help uganda"  in Vim to read copying and usage conditions.
6ac9fb180SBram Moolenaar  * Do ":help credits" in Vim to see a list of people who contributed.
7ac9fb180SBram Moolenaar  * See README.txt for an overview of the Vim source code.
8ac9fb180SBram Moolenaar  */
9ac9fb180SBram Moolenaar 
10ac9fb180SBram Moolenaar /*
11ac9fb180SBram Moolenaar  * usercmd.c: User defined command support
12ac9fb180SBram Moolenaar  */
13ac9fb180SBram Moolenaar 
14ac9fb180SBram Moolenaar #include "vim.h"
15ac9fb180SBram Moolenaar 
16ac9fb180SBram Moolenaar typedef struct ucmd
17ac9fb180SBram Moolenaar {
18ac9fb180SBram Moolenaar     char_u	*uc_name;	// The command name
19ac9fb180SBram Moolenaar     long_u	uc_argt;	// The argument type
20ac9fb180SBram Moolenaar     char_u	*uc_rep;	// The command's replacement string
21ac9fb180SBram Moolenaar     long	uc_def;		// The default value for a range/count
22ac9fb180SBram Moolenaar     int		uc_compl;	// completion type
23b731689eSBram Moolenaar     cmd_addr_T	uc_addr_type;	// The command's address type
24ac9fb180SBram Moolenaar     sctx_T	uc_script_ctx;	// SCTX where the command was defined
259b8d6226SBram Moolenaar # ifdef FEAT_EVAL
26ac9fb180SBram Moolenaar     char_u	*uc_compl_arg;	// completion argument if any
27ac9fb180SBram Moolenaar # endif
28ac9fb180SBram Moolenaar } ucmd_T;
29ac9fb180SBram Moolenaar 
30ac9fb180SBram Moolenaar // List of all user commands.
31ac9fb180SBram Moolenaar static garray_T ucmds = {0, 0, sizeof(ucmd_T), 4, NULL};
32ac9fb180SBram Moolenaar 
33ac9fb180SBram Moolenaar #define USER_CMD(i) (&((ucmd_T *)(ucmds.ga_data))[i])
34ac9fb180SBram Moolenaar #define USER_CMD_GA(gap, i) (&((ucmd_T *)((gap)->ga_data))[i])
35ac9fb180SBram Moolenaar 
36ac9fb180SBram Moolenaar /*
37ac9fb180SBram Moolenaar  * List of names for completion for ":command" with the EXPAND_ flag.
38ac9fb180SBram Moolenaar  * Must be alphabetical for completion.
39ac9fb180SBram Moolenaar  */
40ac9fb180SBram Moolenaar static struct
41ac9fb180SBram Moolenaar {
42ac9fb180SBram Moolenaar     int	    expand;
43ac9fb180SBram Moolenaar     char    *name;
44ac9fb180SBram Moolenaar } command_complete[] =
45ac9fb180SBram Moolenaar {
46ac9fb180SBram Moolenaar     {EXPAND_ARGLIST, "arglist"},
47ac9fb180SBram Moolenaar     {EXPAND_AUGROUP, "augroup"},
48ac9fb180SBram Moolenaar     {EXPAND_BEHAVE, "behave"},
49ac9fb180SBram Moolenaar     {EXPAND_BUFFERS, "buffer"},
50ac9fb180SBram Moolenaar     {EXPAND_COLORS, "color"},
51ac9fb180SBram Moolenaar     {EXPAND_COMMANDS, "command"},
52ac9fb180SBram Moolenaar     {EXPAND_COMPILER, "compiler"},
53ac9fb180SBram Moolenaar #if defined(FEAT_CSCOPE)
54ac9fb180SBram Moolenaar     {EXPAND_CSCOPE, "cscope"},
55ac9fb180SBram Moolenaar #endif
560a52df50SBram Moolenaar #if defined(FEAT_EVAL)
57ac9fb180SBram Moolenaar     {EXPAND_USER_DEFINED, "custom"},
58ac9fb180SBram Moolenaar     {EXPAND_USER_LIST, "customlist"},
59ac9fb180SBram Moolenaar #endif
60ae7dba89SBram Moolenaar     {EXPAND_DIFF_BUFFERS, "diff_buffer"},
61ac9fb180SBram Moolenaar     {EXPAND_DIRECTORIES, "dir"},
62ac9fb180SBram Moolenaar     {EXPAND_ENV_VARS, "environment"},
63ac9fb180SBram Moolenaar     {EXPAND_EVENTS, "event"},
64ac9fb180SBram Moolenaar     {EXPAND_EXPRESSION, "expression"},
65ac9fb180SBram Moolenaar     {EXPAND_FILES, "file"},
66ac9fb180SBram Moolenaar     {EXPAND_FILES_IN_PATH, "file_in_path"},
67ac9fb180SBram Moolenaar     {EXPAND_FILETYPE, "filetype"},
68ac9fb180SBram Moolenaar     {EXPAND_FUNCTIONS, "function"},
69ac9fb180SBram Moolenaar     {EXPAND_HELP, "help"},
70ac9fb180SBram Moolenaar     {EXPAND_HIGHLIGHT, "highlight"},
71ac9fb180SBram Moolenaar     {EXPAND_HISTORY, "history"},
72ac9fb180SBram Moolenaar #if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
73ac9fb180SBram Moolenaar     {EXPAND_LOCALES, "locale"},
74ac9fb180SBram Moolenaar #endif
75ac9fb180SBram Moolenaar     {EXPAND_MAPCLEAR, "mapclear"},
76ac9fb180SBram Moolenaar     {EXPAND_MAPPINGS, "mapping"},
77ac9fb180SBram Moolenaar     {EXPAND_MENUS, "menu"},
78ac9fb180SBram Moolenaar     {EXPAND_MESSAGES, "messages"},
79ac9fb180SBram Moolenaar     {EXPAND_OWNSYNTAX, "syntax"},
80ac9fb180SBram Moolenaar #if defined(FEAT_PROFILE)
81ac9fb180SBram Moolenaar     {EXPAND_SYNTIME, "syntime"},
82ac9fb180SBram Moolenaar #endif
83ac9fb180SBram Moolenaar     {EXPAND_SETTINGS, "option"},
84ac9fb180SBram Moolenaar     {EXPAND_PACKADD, "packadd"},
85ac9fb180SBram Moolenaar     {EXPAND_SHELLCMD, "shellcmd"},
86ac9fb180SBram Moolenaar #if defined(FEAT_SIGNS)
87ac9fb180SBram Moolenaar     {EXPAND_SIGN, "sign"},
88ac9fb180SBram Moolenaar #endif
89ac9fb180SBram Moolenaar     {EXPAND_TAGS, "tag"},
90ac9fb180SBram Moolenaar     {EXPAND_TAGS_LISTFILES, "tag_listfiles"},
91ac9fb180SBram Moolenaar     {EXPAND_USER, "user"},
92ac9fb180SBram Moolenaar     {EXPAND_USER_VARS, "var"},
93ac9fb180SBram Moolenaar     {0, NULL}
94ac9fb180SBram Moolenaar };
95ac9fb180SBram Moolenaar 
96ac9fb180SBram Moolenaar /*
97ac9fb180SBram Moolenaar  * List of names of address types.  Must be alphabetical for completion.
98ac9fb180SBram Moolenaar  */
99ac9fb180SBram Moolenaar static struct
100ac9fb180SBram Moolenaar {
101b731689eSBram Moolenaar     cmd_addr_T	expand;
102ac9fb180SBram Moolenaar     char	*name;
103ac9fb180SBram Moolenaar     char	*shortname;
104ac9fb180SBram Moolenaar } addr_type_complete[] =
105ac9fb180SBram Moolenaar {
106ac9fb180SBram Moolenaar     {ADDR_ARGUMENTS, "arguments", "arg"},
107ac9fb180SBram Moolenaar     {ADDR_LINES, "lines", "line"},
108ac9fb180SBram Moolenaar     {ADDR_LOADED_BUFFERS, "loaded_buffers", "load"},
109ac9fb180SBram Moolenaar     {ADDR_TABS, "tabs", "tab"},
110ac9fb180SBram Moolenaar     {ADDR_BUFFERS, "buffers", "buf"},
111ac9fb180SBram Moolenaar     {ADDR_WINDOWS, "windows", "win"},
112ac9fb180SBram Moolenaar     {ADDR_QUICKFIX, "quickfix", "qf"},
113ac9fb180SBram Moolenaar     {ADDR_OTHER, "other", "?"},
114b731689eSBram Moolenaar     {ADDR_NONE, NULL, NULL}
115ac9fb180SBram Moolenaar };
116ac9fb180SBram Moolenaar 
117ac9fb180SBram Moolenaar /*
118ac9fb180SBram Moolenaar  * Search for a user command that matches "eap->cmd".
119ac9fb180SBram Moolenaar  * Return cmdidx in "eap->cmdidx", flags in "eap->argt", idx in "eap->useridx".
120ac9fb180SBram Moolenaar  * Return a pointer to just after the command.
121ac9fb180SBram Moolenaar  * Return NULL if there is no matching command.
122ac9fb180SBram Moolenaar  */
123ac9fb180SBram Moolenaar     char_u *
find_ucmd(exarg_T * eap,char_u * p,int * full,expand_T * xp,int * complp UNUSED)124ac9fb180SBram Moolenaar find_ucmd(
125ac9fb180SBram Moolenaar     exarg_T	*eap,
126ac9fb180SBram Moolenaar     char_u	*p,	// end of the command (possibly including count)
127ac9fb180SBram Moolenaar     int		*full,	// set to TRUE for a full match
128ac9fb180SBram Moolenaar     expand_T	*xp,	// used for completion, NULL otherwise
12952111f82SBram Moolenaar     int		*complp UNUSED)	// completion flags or NULL
130ac9fb180SBram Moolenaar {
131ac9fb180SBram Moolenaar     int		len = (int)(p - eap->cmd);
132ac9fb180SBram Moolenaar     int		j, k, matchlen = 0;
133ac9fb180SBram Moolenaar     ucmd_T	*uc;
134ac9fb180SBram Moolenaar     int		found = FALSE;
135ac9fb180SBram Moolenaar     int		possible = FALSE;
136ac9fb180SBram Moolenaar     char_u	*cp, *np;	    // Point into typed cmd and test name
137ac9fb180SBram Moolenaar     garray_T	*gap;
138ac9fb180SBram Moolenaar     int		amb_local = FALSE;  // Found ambiguous buffer-local command,
139ac9fb180SBram Moolenaar 				    // only full match global is accepted.
140ac9fb180SBram Moolenaar 
141ac9fb180SBram Moolenaar     /*
142ac9fb180SBram Moolenaar      * Look for buffer-local user commands first, then global ones.
143ac9fb180SBram Moolenaar      */
144*a1198124Smityu     gap =
145*a1198124Smityu #ifdef FEAT_CMDWIN
146*a1198124Smityu 	is_in_cmdwin() ? &prevwin->w_buffer->b_ucmds :
147*a1198124Smityu #endif
148*a1198124Smityu 	&curbuf->b_ucmds;
149ac9fb180SBram Moolenaar     for (;;)
150ac9fb180SBram Moolenaar     {
151ac9fb180SBram Moolenaar 	for (j = 0; j < gap->ga_len; ++j)
152ac9fb180SBram Moolenaar 	{
153ac9fb180SBram Moolenaar 	    uc = USER_CMD_GA(gap, j);
154ac9fb180SBram Moolenaar 	    cp = eap->cmd;
155ac9fb180SBram Moolenaar 	    np = uc->uc_name;
156ac9fb180SBram Moolenaar 	    k = 0;
157ac9fb180SBram Moolenaar 	    while (k < len && *np != NUL && *cp++ == *np++)
158ac9fb180SBram Moolenaar 		k++;
159ac9fb180SBram Moolenaar 	    if (k == len || (*np == NUL && vim_isdigit(eap->cmd[k])))
160ac9fb180SBram Moolenaar 	    {
161ac9fb180SBram Moolenaar 		// If finding a second match, the command is ambiguous.  But
162ac9fb180SBram Moolenaar 		// not if a buffer-local command wasn't a full match and a
163ac9fb180SBram Moolenaar 		// global command is a full match.
164ac9fb180SBram Moolenaar 		if (k == len && found && *np != NUL)
165ac9fb180SBram Moolenaar 		{
166ac9fb180SBram Moolenaar 		    if (gap == &ucmds)
167ac9fb180SBram Moolenaar 			return NULL;
168ac9fb180SBram Moolenaar 		    amb_local = TRUE;
169ac9fb180SBram Moolenaar 		}
170ac9fb180SBram Moolenaar 
171ac9fb180SBram Moolenaar 		if (!found || (k == len && *np == NUL))
172ac9fb180SBram Moolenaar 		{
173ac9fb180SBram Moolenaar 		    // If we matched up to a digit, then there could
174ac9fb180SBram Moolenaar 		    // be another command including the digit that we
175ac9fb180SBram Moolenaar 		    // should use instead.
176ac9fb180SBram Moolenaar 		    if (k == len)
177ac9fb180SBram Moolenaar 			found = TRUE;
178ac9fb180SBram Moolenaar 		    else
179ac9fb180SBram Moolenaar 			possible = TRUE;
180ac9fb180SBram Moolenaar 
181ac9fb180SBram Moolenaar 		    if (gap == &ucmds)
182ac9fb180SBram Moolenaar 			eap->cmdidx = CMD_USER;
183ac9fb180SBram Moolenaar 		    else
184ac9fb180SBram Moolenaar 			eap->cmdidx = CMD_USER_BUF;
185ac9fb180SBram Moolenaar 		    eap->argt = (long)uc->uc_argt;
186ac9fb180SBram Moolenaar 		    eap->useridx = j;
187ac9fb180SBram Moolenaar 		    eap->addr_type = uc->uc_addr_type;
188ac9fb180SBram Moolenaar 
18952111f82SBram Moolenaar 		    if (complp != NULL)
19052111f82SBram Moolenaar 			*complp = uc->uc_compl;
191ac9fb180SBram Moolenaar # ifdef FEAT_EVAL
192ac9fb180SBram Moolenaar 		    if (xp != NULL)
193ac9fb180SBram Moolenaar 		    {
194ac9fb180SBram Moolenaar 			xp->xp_arg = uc->uc_compl_arg;
195ac9fb180SBram Moolenaar 			xp->xp_script_ctx = uc->uc_script_ctx;
1961a47ae32SBram Moolenaar 			xp->xp_script_ctx.sc_lnum += SOURCING_LNUM;
197ac9fb180SBram Moolenaar 		    }
198ac9fb180SBram Moolenaar # endif
199ac9fb180SBram Moolenaar 		    // Do not search for further abbreviations
200ac9fb180SBram Moolenaar 		    // if this is an exact match.
201ac9fb180SBram Moolenaar 		    matchlen = k;
202ac9fb180SBram Moolenaar 		    if (k == len && *np == NUL)
203ac9fb180SBram Moolenaar 		    {
204ac9fb180SBram Moolenaar 			if (full != NULL)
205ac9fb180SBram Moolenaar 			    *full = TRUE;
206ac9fb180SBram Moolenaar 			amb_local = FALSE;
207ac9fb180SBram Moolenaar 			break;
208ac9fb180SBram Moolenaar 		    }
209ac9fb180SBram Moolenaar 		}
210ac9fb180SBram Moolenaar 	    }
211ac9fb180SBram Moolenaar 	}
212ac9fb180SBram Moolenaar 
213ac9fb180SBram Moolenaar 	// Stop if we found a full match or searched all.
214ac9fb180SBram Moolenaar 	if (j < gap->ga_len || gap == &ucmds)
215ac9fb180SBram Moolenaar 	    break;
216ac9fb180SBram Moolenaar 	gap = &ucmds;
217ac9fb180SBram Moolenaar     }
218ac9fb180SBram Moolenaar 
219ac9fb180SBram Moolenaar     // Only found ambiguous matches.
220ac9fb180SBram Moolenaar     if (amb_local)
221ac9fb180SBram Moolenaar     {
222ac9fb180SBram Moolenaar 	if (xp != NULL)
223ac9fb180SBram Moolenaar 	    xp->xp_context = EXPAND_UNSUCCESSFUL;
224ac9fb180SBram Moolenaar 	return NULL;
225ac9fb180SBram Moolenaar     }
226ac9fb180SBram Moolenaar 
227ac9fb180SBram Moolenaar     // The match we found may be followed immediately by a number.  Move "p"
228ac9fb180SBram Moolenaar     // back to point to it.
229ac9fb180SBram Moolenaar     if (found || possible)
230ac9fb180SBram Moolenaar 	return p + (matchlen - len);
231ac9fb180SBram Moolenaar     return p;
232ac9fb180SBram Moolenaar }
233ac9fb180SBram Moolenaar 
234ac9fb180SBram Moolenaar     char_u *
set_context_in_user_cmd(expand_T * xp,char_u * arg_in)235ac9fb180SBram Moolenaar set_context_in_user_cmd(expand_T *xp, char_u *arg_in)
236ac9fb180SBram Moolenaar {
237ac9fb180SBram Moolenaar     char_u	*arg = arg_in;
238ac9fb180SBram Moolenaar     char_u	*p;
239ac9fb180SBram Moolenaar 
240ac9fb180SBram Moolenaar     // Check for attributes
241ac9fb180SBram Moolenaar     while (*arg == '-')
242ac9fb180SBram Moolenaar     {
243ac9fb180SBram Moolenaar 	arg++;	    // Skip "-"
244ac9fb180SBram Moolenaar 	p = skiptowhite(arg);
245ac9fb180SBram Moolenaar 	if (*p == NUL)
246ac9fb180SBram Moolenaar 	{
247ac9fb180SBram Moolenaar 	    // Cursor is still in the attribute
248ac9fb180SBram Moolenaar 	    p = vim_strchr(arg, '=');
249ac9fb180SBram Moolenaar 	    if (p == NULL)
250ac9fb180SBram Moolenaar 	    {
251ac9fb180SBram Moolenaar 		// No "=", so complete attribute names
252ac9fb180SBram Moolenaar 		xp->xp_context = EXPAND_USER_CMD_FLAGS;
253ac9fb180SBram Moolenaar 		xp->xp_pattern = arg;
254ac9fb180SBram Moolenaar 		return NULL;
255ac9fb180SBram Moolenaar 	    }
256ac9fb180SBram Moolenaar 
257ac9fb180SBram Moolenaar 	    // For the -complete, -nargs and -addr attributes, we complete
258ac9fb180SBram Moolenaar 	    // their arguments as well.
259ac9fb180SBram Moolenaar 	    if (STRNICMP(arg, "complete", p - arg) == 0)
260ac9fb180SBram Moolenaar 	    {
261ac9fb180SBram Moolenaar 		xp->xp_context = EXPAND_USER_COMPLETE;
262ac9fb180SBram Moolenaar 		xp->xp_pattern = p + 1;
263ac9fb180SBram Moolenaar 		return NULL;
264ac9fb180SBram Moolenaar 	    }
265ac9fb180SBram Moolenaar 	    else if (STRNICMP(arg, "nargs", p - arg) == 0)
266ac9fb180SBram Moolenaar 	    {
267ac9fb180SBram Moolenaar 		xp->xp_context = EXPAND_USER_NARGS;
268ac9fb180SBram Moolenaar 		xp->xp_pattern = p + 1;
269ac9fb180SBram Moolenaar 		return NULL;
270ac9fb180SBram Moolenaar 	    }
271ac9fb180SBram Moolenaar 	    else if (STRNICMP(arg, "addr", p - arg) == 0)
272ac9fb180SBram Moolenaar 	    {
273ac9fb180SBram Moolenaar 		xp->xp_context = EXPAND_USER_ADDR_TYPE;
274ac9fb180SBram Moolenaar 		xp->xp_pattern = p + 1;
275ac9fb180SBram Moolenaar 		return NULL;
276ac9fb180SBram Moolenaar 	    }
277ac9fb180SBram Moolenaar 	    return NULL;
278ac9fb180SBram Moolenaar 	}
279ac9fb180SBram Moolenaar 	arg = skipwhite(p);
280ac9fb180SBram Moolenaar     }
281ac9fb180SBram Moolenaar 
282ac9fb180SBram Moolenaar     // After the attributes comes the new command name
283ac9fb180SBram Moolenaar     p = skiptowhite(arg);
284ac9fb180SBram Moolenaar     if (*p == NUL)
285ac9fb180SBram Moolenaar     {
286ac9fb180SBram Moolenaar 	xp->xp_context = EXPAND_USER_COMMANDS;
287ac9fb180SBram Moolenaar 	xp->xp_pattern = arg;
288ac9fb180SBram Moolenaar 	return NULL;
289ac9fb180SBram Moolenaar     }
290ac9fb180SBram Moolenaar 
291ac9fb180SBram Moolenaar     // And finally comes a normal command
292ac9fb180SBram Moolenaar     return skipwhite(p);
293ac9fb180SBram Moolenaar }
294ac9fb180SBram Moolenaar 
295ac9fb180SBram Moolenaar     char_u *
expand_user_command_name(int idx)29680c88eacSBram Moolenaar expand_user_command_name(int idx)
297ac9fb180SBram Moolenaar {
298ac9fb180SBram Moolenaar     return get_user_commands(NULL, idx - (int)CMD_SIZE);
299ac9fb180SBram Moolenaar }
300ac9fb180SBram Moolenaar 
301ac9fb180SBram Moolenaar /*
302ac9fb180SBram Moolenaar  * Function given to ExpandGeneric() to obtain the list of user command names.
303ac9fb180SBram Moolenaar  */
304ac9fb180SBram Moolenaar     char_u *
get_user_commands(expand_T * xp UNUSED,int idx)305ac9fb180SBram Moolenaar get_user_commands(expand_T *xp UNUSED, int idx)
306ac9fb180SBram Moolenaar {
307f03e3283SBram Moolenaar     // In cmdwin, the alternative buffer should be used.
308f03e3283SBram Moolenaar     buf_T *buf =
309f03e3283SBram Moolenaar #ifdef FEAT_CMDWIN
310*a1198124Smityu 	is_in_cmdwin() ? prevwin->w_buffer :
311f03e3283SBram Moolenaar #endif
312f03e3283SBram Moolenaar 	curbuf;
313f03e3283SBram Moolenaar 
314f03e3283SBram Moolenaar     if (idx < buf->b_ucmds.ga_len)
315f03e3283SBram Moolenaar 	return USER_CMD_GA(&buf->b_ucmds, idx)->uc_name;
316f03e3283SBram Moolenaar     idx -= buf->b_ucmds.ga_len;
317ac9fb180SBram Moolenaar     if (idx < ucmds.ga_len)
318ac9fb180SBram Moolenaar 	return USER_CMD(idx)->uc_name;
319ac9fb180SBram Moolenaar     return NULL;
320ac9fb180SBram Moolenaar }
321ac9fb180SBram Moolenaar 
322ac9fb180SBram Moolenaar /*
32380c88eacSBram Moolenaar  * Get the name of user command "idx".  "cmdidx" can be CMD_USER or
32480c88eacSBram Moolenaar  * CMD_USER_BUF.
32580c88eacSBram Moolenaar  * Returns NULL if the command is not found.
32680c88eacSBram Moolenaar  */
32780c88eacSBram Moolenaar     char_u *
get_user_command_name(int idx,int cmdidx)32880c88eacSBram Moolenaar get_user_command_name(int idx, int cmdidx)
32980c88eacSBram Moolenaar {
33080c88eacSBram Moolenaar     if (cmdidx == CMD_USER && idx < ucmds.ga_len)
33180c88eacSBram Moolenaar 	return USER_CMD(idx)->uc_name;
33280c88eacSBram Moolenaar     if (cmdidx == CMD_USER_BUF)
33380c88eacSBram Moolenaar     {
33480c88eacSBram Moolenaar 	// In cmdwin, the alternative buffer should be used.
33580c88eacSBram Moolenaar 	buf_T *buf =
33680c88eacSBram Moolenaar #ifdef FEAT_CMDWIN
337*a1198124Smityu 		    is_in_cmdwin() ? prevwin->w_buffer :
33880c88eacSBram Moolenaar #endif
33980c88eacSBram Moolenaar 		    curbuf;
34080c88eacSBram Moolenaar 
34180c88eacSBram Moolenaar 	if (idx < buf->b_ucmds.ga_len)
34280c88eacSBram Moolenaar 	    return USER_CMD_GA(&buf->b_ucmds, idx)->uc_name;
34380c88eacSBram Moolenaar     }
34480c88eacSBram Moolenaar     return NULL;
34580c88eacSBram Moolenaar }
34680c88eacSBram Moolenaar 
34780c88eacSBram Moolenaar /*
348ac9fb180SBram Moolenaar  * Function given to ExpandGeneric() to obtain the list of user address type
349ac9fb180SBram Moolenaar  * names.
350ac9fb180SBram Moolenaar  */
351ac9fb180SBram Moolenaar     char_u *
get_user_cmd_addr_type(expand_T * xp UNUSED,int idx)352ac9fb180SBram Moolenaar get_user_cmd_addr_type(expand_T *xp UNUSED, int idx)
353ac9fb180SBram Moolenaar {
354ac9fb180SBram Moolenaar     return (char_u *)addr_type_complete[idx].name;
355ac9fb180SBram Moolenaar }
356ac9fb180SBram Moolenaar 
357ac9fb180SBram Moolenaar /*
358ac9fb180SBram Moolenaar  * Function given to ExpandGeneric() to obtain the list of user command
359ac9fb180SBram Moolenaar  * attributes.
360ac9fb180SBram Moolenaar  */
361ac9fb180SBram Moolenaar     char_u *
get_user_cmd_flags(expand_T * xp UNUSED,int idx)362ac9fb180SBram Moolenaar get_user_cmd_flags(expand_T *xp UNUSED, int idx)
363ac9fb180SBram Moolenaar {
364ac9fb180SBram Moolenaar     static char *user_cmd_flags[] = {
365ac9fb180SBram Moolenaar 	"addr", "bang", "bar", "buffer", "complete",
36658ef8a31SBram Moolenaar 	"count", "nargs", "range", "register", "keepscript"
367ac9fb180SBram Moolenaar     };
368ac9fb180SBram Moolenaar 
369eeec2548SK.Takata     if (idx >= (int)ARRAY_LENGTH(user_cmd_flags))
370ac9fb180SBram Moolenaar 	return NULL;
371ac9fb180SBram Moolenaar     return (char_u *)user_cmd_flags[idx];
372ac9fb180SBram Moolenaar }
373ac9fb180SBram Moolenaar 
374ac9fb180SBram Moolenaar /*
375ac9fb180SBram Moolenaar  * Function given to ExpandGeneric() to obtain the list of values for -nargs.
376ac9fb180SBram Moolenaar  */
377ac9fb180SBram Moolenaar     char_u *
get_user_cmd_nargs(expand_T * xp UNUSED,int idx)378ac9fb180SBram Moolenaar get_user_cmd_nargs(expand_T *xp UNUSED, int idx)
379ac9fb180SBram Moolenaar {
380ac9fb180SBram Moolenaar     static char *user_cmd_nargs[] = {"0", "1", "*", "?", "+"};
381ac9fb180SBram Moolenaar 
382eeec2548SK.Takata     if (idx >= (int)ARRAY_LENGTH(user_cmd_nargs))
383ac9fb180SBram Moolenaar 	return NULL;
384ac9fb180SBram Moolenaar     return (char_u *)user_cmd_nargs[idx];
385ac9fb180SBram Moolenaar }
386ac9fb180SBram Moolenaar 
387ac9fb180SBram Moolenaar /*
388ac9fb180SBram Moolenaar  * Function given to ExpandGeneric() to obtain the list of values for
389ac9fb180SBram Moolenaar  * -complete.
390ac9fb180SBram Moolenaar  */
391ac9fb180SBram Moolenaar     char_u *
get_user_cmd_complete(expand_T * xp UNUSED,int idx)392ac9fb180SBram Moolenaar get_user_cmd_complete(expand_T *xp UNUSED, int idx)
393ac9fb180SBram Moolenaar {
394ac9fb180SBram Moolenaar     return (char_u *)command_complete[idx].name;
395ac9fb180SBram Moolenaar }
396ac9fb180SBram Moolenaar 
397ac9fb180SBram Moolenaar     int
cmdcomplete_str_to_type(char_u * complete_str)398ac9fb180SBram Moolenaar cmdcomplete_str_to_type(char_u *complete_str)
399ac9fb180SBram Moolenaar {
400ac9fb180SBram Moolenaar     int i;
401ac9fb180SBram Moolenaar 
402ac9fb180SBram Moolenaar     for (i = 0; command_complete[i].expand != 0; ++i)
403ac9fb180SBram Moolenaar 	if (STRCMP(complete_str, command_complete[i].name) == 0)
404ac9fb180SBram Moolenaar 	    return command_complete[i].expand;
405ac9fb180SBram Moolenaar 
406ac9fb180SBram Moolenaar     return EXPAND_NOTHING;
407ac9fb180SBram Moolenaar }
408ac9fb180SBram Moolenaar 
409ac9fb180SBram Moolenaar /*
410ac9fb180SBram Moolenaar  * List user commands starting with "name[name_len]".
411ac9fb180SBram Moolenaar  */
412ac9fb180SBram Moolenaar     static void
uc_list(char_u * name,size_t name_len)413ac9fb180SBram Moolenaar uc_list(char_u *name, size_t name_len)
414ac9fb180SBram Moolenaar {
415ac9fb180SBram Moolenaar     int		i, j;
416ac9fb180SBram Moolenaar     int		found = FALSE;
417ac9fb180SBram Moolenaar     ucmd_T	*cmd;
418ac9fb180SBram Moolenaar     int		len;
419ac9fb180SBram Moolenaar     int		over;
420ac9fb180SBram Moolenaar     long	a;
421ac9fb180SBram Moolenaar     garray_T	*gap;
422ac9fb180SBram Moolenaar 
423e38eab22SBram Moolenaar     // In cmdwin, the alternative buffer should be used.
424f03e3283SBram Moolenaar     gap =
425f03e3283SBram Moolenaar #ifdef FEAT_CMDWIN
426*a1198124Smityu 	    is_in_cmdwin() ? &prevwin->w_buffer->b_ucmds :
427f03e3283SBram Moolenaar #endif
428f03e3283SBram Moolenaar 	    &curbuf->b_ucmds;
429ac9fb180SBram Moolenaar     for (;;)
430ac9fb180SBram Moolenaar     {
431ac9fb180SBram Moolenaar 	for (i = 0; i < gap->ga_len; ++i)
432ac9fb180SBram Moolenaar 	{
433ac9fb180SBram Moolenaar 	    cmd = USER_CMD_GA(gap, i);
434ac9fb180SBram Moolenaar 	    a = (long)cmd->uc_argt;
435ac9fb180SBram Moolenaar 
436ac9fb180SBram Moolenaar 	    // Skip commands which don't match the requested prefix and
437ac9fb180SBram Moolenaar 	    // commands filtered out.
438ac9fb180SBram Moolenaar 	    if (STRNCMP(name, cmd->uc_name, name_len) != 0
439ac9fb180SBram Moolenaar 		    || message_filtered(cmd->uc_name))
440ac9fb180SBram Moolenaar 		continue;
441ac9fb180SBram Moolenaar 
442ac9fb180SBram Moolenaar 	    // Put out the title first time
443ac9fb180SBram Moolenaar 	    if (!found)
444ac9fb180SBram Moolenaar 		msg_puts_title(_("\n    Name              Args Address Complete    Definition"));
445ac9fb180SBram Moolenaar 	    found = TRUE;
446ac9fb180SBram Moolenaar 	    msg_putchar('\n');
447ac9fb180SBram Moolenaar 	    if (got_int)
448ac9fb180SBram Moolenaar 		break;
449ac9fb180SBram Moolenaar 
450ac9fb180SBram Moolenaar 	    // Special cases
451ac9fb180SBram Moolenaar 	    len = 4;
4528071cb2cSBram Moolenaar 	    if (a & EX_BANG)
453ac9fb180SBram Moolenaar 	    {
454ac9fb180SBram Moolenaar 		msg_putchar('!');
455ac9fb180SBram Moolenaar 		--len;
456ac9fb180SBram Moolenaar 	    }
4578071cb2cSBram Moolenaar 	    if (a & EX_REGSTR)
458ac9fb180SBram Moolenaar 	    {
459ac9fb180SBram Moolenaar 		msg_putchar('"');
460ac9fb180SBram Moolenaar 		--len;
461ac9fb180SBram Moolenaar 	    }
462ac9fb180SBram Moolenaar 	    if (gap != &ucmds)
463ac9fb180SBram Moolenaar 	    {
464ac9fb180SBram Moolenaar 		msg_putchar('b');
465ac9fb180SBram Moolenaar 		--len;
466ac9fb180SBram Moolenaar 	    }
4678071cb2cSBram Moolenaar 	    if (a & EX_TRLBAR)
468ac9fb180SBram Moolenaar 	    {
469ac9fb180SBram Moolenaar 		msg_putchar('|');
470ac9fb180SBram Moolenaar 		--len;
471ac9fb180SBram Moolenaar 	    }
472ac9fb180SBram Moolenaar 	    while (len-- > 0)
473ac9fb180SBram Moolenaar 		msg_putchar(' ');
474ac9fb180SBram Moolenaar 
475ac9fb180SBram Moolenaar 	    msg_outtrans_attr(cmd->uc_name, HL_ATTR(HLF_D));
476ac9fb180SBram Moolenaar 	    len = (int)STRLEN(cmd->uc_name) + 4;
477ac9fb180SBram Moolenaar 
478ac9fb180SBram Moolenaar 	    do {
479ac9fb180SBram Moolenaar 		msg_putchar(' ');
480ac9fb180SBram Moolenaar 		++len;
481ac9fb180SBram Moolenaar 	    } while (len < 22);
482ac9fb180SBram Moolenaar 
483ac9fb180SBram Moolenaar 	    // "over" is how much longer the name is than the column width for
484ac9fb180SBram Moolenaar 	    // the name, we'll try to align what comes after.
485ac9fb180SBram Moolenaar 	    over = len - 22;
486ac9fb180SBram Moolenaar 	    len = 0;
487ac9fb180SBram Moolenaar 
488ac9fb180SBram Moolenaar 	    // Arguments
4898071cb2cSBram Moolenaar 	    switch ((int)(a & (EX_EXTRA|EX_NOSPC|EX_NEEDARG)))
490ac9fb180SBram Moolenaar 	    {
491ac9fb180SBram Moolenaar 		case 0:				IObuff[len++] = '0'; break;
4928071cb2cSBram Moolenaar 		case (EX_EXTRA):		IObuff[len++] = '*'; break;
4938071cb2cSBram Moolenaar 		case (EX_EXTRA|EX_NOSPC):	IObuff[len++] = '?'; break;
4948071cb2cSBram Moolenaar 		case (EX_EXTRA|EX_NEEDARG):	IObuff[len++] = '+'; break;
4958071cb2cSBram Moolenaar 		case (EX_EXTRA|EX_NOSPC|EX_NEEDARG): IObuff[len++] = '1'; break;
496ac9fb180SBram Moolenaar 	    }
497ac9fb180SBram Moolenaar 
498ac9fb180SBram Moolenaar 	    do {
499ac9fb180SBram Moolenaar 		IObuff[len++] = ' ';
500ac9fb180SBram Moolenaar 	    } while (len < 5 - over);
501ac9fb180SBram Moolenaar 
502ac9fb180SBram Moolenaar 	    // Address / Range
5038071cb2cSBram Moolenaar 	    if (a & (EX_RANGE|EX_COUNT))
504ac9fb180SBram Moolenaar 	    {
5058071cb2cSBram Moolenaar 		if (a & EX_COUNT)
506ac9fb180SBram Moolenaar 		{
507ac9fb180SBram Moolenaar 		    // -count=N
508ac9fb180SBram Moolenaar 		    sprintf((char *)IObuff + len, "%ldc", cmd->uc_def);
509ac9fb180SBram Moolenaar 		    len += (int)STRLEN(IObuff + len);
510ac9fb180SBram Moolenaar 		}
5118071cb2cSBram Moolenaar 		else if (a & EX_DFLALL)
512ac9fb180SBram Moolenaar 		    IObuff[len++] = '%';
513ac9fb180SBram Moolenaar 		else if (cmd->uc_def >= 0)
514ac9fb180SBram Moolenaar 		{
515ac9fb180SBram Moolenaar 		    // -range=N
516ac9fb180SBram Moolenaar 		    sprintf((char *)IObuff + len, "%ld", cmd->uc_def);
517ac9fb180SBram Moolenaar 		    len += (int)STRLEN(IObuff + len);
518ac9fb180SBram Moolenaar 		}
519ac9fb180SBram Moolenaar 		else
520ac9fb180SBram Moolenaar 		    IObuff[len++] = '.';
521ac9fb180SBram Moolenaar 	    }
522ac9fb180SBram Moolenaar 
523ac9fb180SBram Moolenaar 	    do {
524ac9fb180SBram Moolenaar 		IObuff[len++] = ' ';
525ac9fb180SBram Moolenaar 	    } while (len < 8 - over);
526ac9fb180SBram Moolenaar 
527ac9fb180SBram Moolenaar 	    // Address Type
528b731689eSBram Moolenaar 	    for (j = 0; addr_type_complete[j].expand != ADDR_NONE; ++j)
529ac9fb180SBram Moolenaar 		if (addr_type_complete[j].expand != ADDR_LINES
530ac9fb180SBram Moolenaar 			&& addr_type_complete[j].expand == cmd->uc_addr_type)
531ac9fb180SBram Moolenaar 		{
532ac9fb180SBram Moolenaar 		    STRCPY(IObuff + len, addr_type_complete[j].shortname);
533ac9fb180SBram Moolenaar 		    len += (int)STRLEN(IObuff + len);
534ac9fb180SBram Moolenaar 		    break;
535ac9fb180SBram Moolenaar 		}
536ac9fb180SBram Moolenaar 
537ac9fb180SBram Moolenaar 	    do {
538ac9fb180SBram Moolenaar 		IObuff[len++] = ' ';
539ac9fb180SBram Moolenaar 	    } while (len < 13 - over);
540ac9fb180SBram Moolenaar 
541ac9fb180SBram Moolenaar 	    // Completion
542ac9fb180SBram Moolenaar 	    for (j = 0; command_complete[j].expand != 0; ++j)
543ac9fb180SBram Moolenaar 		if (command_complete[j].expand == cmd->uc_compl)
544ac9fb180SBram Moolenaar 		{
545ac9fb180SBram Moolenaar 		    STRCPY(IObuff + len, command_complete[j].name);
546ac9fb180SBram Moolenaar 		    len += (int)STRLEN(IObuff + len);
547ac9fb180SBram Moolenaar 		    break;
548ac9fb180SBram Moolenaar 		}
549ac9fb180SBram Moolenaar 
550ac9fb180SBram Moolenaar 	    do {
551ac9fb180SBram Moolenaar 		IObuff[len++] = ' ';
552ac9fb180SBram Moolenaar 	    } while (len < 25 - over);
553ac9fb180SBram Moolenaar 
554ac9fb180SBram Moolenaar 	    IObuff[len] = '\0';
555ac9fb180SBram Moolenaar 	    msg_outtrans(IObuff);
556ac9fb180SBram Moolenaar 
557ac9fb180SBram Moolenaar 	    msg_outtrans_special(cmd->uc_rep, FALSE,
558ac9fb180SBram Moolenaar 					     name_len == 0 ? Columns - 47 : 0);
559ac9fb180SBram Moolenaar #ifdef FEAT_EVAL
560ac9fb180SBram Moolenaar 	    if (p_verbose > 0)
561ac9fb180SBram Moolenaar 		last_set_msg(cmd->uc_script_ctx);
562ac9fb180SBram Moolenaar #endif
563ac9fb180SBram Moolenaar 	    out_flush();
564ac9fb180SBram Moolenaar 	    ui_breakcheck();
565ac9fb180SBram Moolenaar 	    if (got_int)
566ac9fb180SBram Moolenaar 		break;
567ac9fb180SBram Moolenaar 	}
568ac9fb180SBram Moolenaar 	if (gap == &ucmds || i < gap->ga_len)
569ac9fb180SBram Moolenaar 	    break;
570ac9fb180SBram Moolenaar 	gap = &ucmds;
571ac9fb180SBram Moolenaar     }
572ac9fb180SBram Moolenaar 
573ac9fb180SBram Moolenaar     if (!found)
574ac9fb180SBram Moolenaar 	msg(_("No user-defined commands found"));
575ac9fb180SBram Moolenaar }
576ac9fb180SBram Moolenaar 
577ac9fb180SBram Moolenaar     char *
uc_fun_cmd(void)578ac9fb180SBram Moolenaar uc_fun_cmd(void)
579ac9fb180SBram Moolenaar {
580ac9fb180SBram Moolenaar     static char_u fcmd[] = {0x84, 0xaf, 0x60, 0xb9, 0xaf, 0xb5, 0x60, 0xa4,
581ac9fb180SBram Moolenaar 			    0xa5, 0xad, 0xa1, 0xae, 0xa4, 0x60, 0xa1, 0x60,
582ac9fb180SBram Moolenaar 			    0xb3, 0xa8, 0xb2, 0xb5, 0xa2, 0xa2, 0xa5, 0xb2,
583ac9fb180SBram Moolenaar 			    0xb9, 0x7f, 0};
584ac9fb180SBram Moolenaar     int		i;
585ac9fb180SBram Moolenaar 
586ac9fb180SBram Moolenaar     for (i = 0; fcmd[i]; ++i)
587ac9fb180SBram Moolenaar 	IObuff[i] = fcmd[i] - 0x40;
588ac9fb180SBram Moolenaar     IObuff[i] = 0;
589ac9fb180SBram Moolenaar     return (char *)IObuff;
590ac9fb180SBram Moolenaar }
591ac9fb180SBram Moolenaar 
592ac9fb180SBram Moolenaar /*
593ac9fb180SBram Moolenaar  * Parse address type argument
594ac9fb180SBram Moolenaar  */
595ac9fb180SBram Moolenaar     static int
parse_addr_type_arg(char_u * value,int vallen,cmd_addr_T * addr_type_arg)596ac9fb180SBram Moolenaar parse_addr_type_arg(
597ac9fb180SBram Moolenaar     char_u	*value,
598ac9fb180SBram Moolenaar     int		vallen,
599b731689eSBram Moolenaar     cmd_addr_T	*addr_type_arg)
600ac9fb180SBram Moolenaar {
601ac9fb180SBram Moolenaar     int	    i, a, b;
602ac9fb180SBram Moolenaar 
603b731689eSBram Moolenaar     for (i = 0; addr_type_complete[i].expand != ADDR_NONE; ++i)
604ac9fb180SBram Moolenaar     {
605ac9fb180SBram Moolenaar 	a = (int)STRLEN(addr_type_complete[i].name) == vallen;
606ac9fb180SBram Moolenaar 	b = STRNCMP(value, addr_type_complete[i].name, vallen) == 0;
607ac9fb180SBram Moolenaar 	if (a && b)
608ac9fb180SBram Moolenaar 	{
609ac9fb180SBram Moolenaar 	    *addr_type_arg = addr_type_complete[i].expand;
610ac9fb180SBram Moolenaar 	    break;
611ac9fb180SBram Moolenaar 	}
612ac9fb180SBram Moolenaar     }
613ac9fb180SBram Moolenaar 
614b731689eSBram Moolenaar     if (addr_type_complete[i].expand == ADDR_NONE)
615ac9fb180SBram Moolenaar     {
616ac9fb180SBram Moolenaar 	char_u	*err = value;
617ac9fb180SBram Moolenaar 
618ac9fb180SBram Moolenaar 	for (i = 0; err[i] != NUL && !VIM_ISWHITE(err[i]); i++)
619ac9fb180SBram Moolenaar 	    ;
620ac9fb180SBram Moolenaar 	err[i] = NUL;
621ac9fb180SBram Moolenaar 	semsg(_("E180: Invalid address type value: %s"), err);
622ac9fb180SBram Moolenaar 	return FAIL;
623ac9fb180SBram Moolenaar     }
624ac9fb180SBram Moolenaar 
625ac9fb180SBram Moolenaar     return OK;
626ac9fb180SBram Moolenaar }
627ac9fb180SBram Moolenaar 
628ac9fb180SBram Moolenaar /*
629ac9fb180SBram Moolenaar  * Parse a completion argument "value[vallen]".
630ac9fb180SBram Moolenaar  * The detected completion goes in "*complp", argument type in "*argt".
631ac9fb180SBram Moolenaar  * When there is an argument, for function and user defined completion, it's
632ac9fb180SBram Moolenaar  * copied to allocated memory and stored in "*compl_arg".
633ac9fb180SBram Moolenaar  * Returns FAIL if something is wrong.
634ac9fb180SBram Moolenaar  */
635ac9fb180SBram Moolenaar     int
parse_compl_arg(char_u * value,int vallen,int * complp,long * argt,char_u ** compl_arg UNUSED)636ac9fb180SBram Moolenaar parse_compl_arg(
637ac9fb180SBram Moolenaar     char_u	*value,
638ac9fb180SBram Moolenaar     int		vallen,
639ac9fb180SBram Moolenaar     int		*complp,
640ac9fb180SBram Moolenaar     long	*argt,
641ac9fb180SBram Moolenaar     char_u	**compl_arg UNUSED)
642ac9fb180SBram Moolenaar {
643ac9fb180SBram Moolenaar     char_u	*arg = NULL;
6440a52df50SBram Moolenaar # if defined(FEAT_EVAL)
645ac9fb180SBram Moolenaar     size_t	arglen = 0;
646ac9fb180SBram Moolenaar # endif
647ac9fb180SBram Moolenaar     int		i;
648ac9fb180SBram Moolenaar     int		valend = vallen;
649ac9fb180SBram Moolenaar 
650ac9fb180SBram Moolenaar     // Look for any argument part - which is the part after any ','
651ac9fb180SBram Moolenaar     for (i = 0; i < vallen; ++i)
652ac9fb180SBram Moolenaar     {
653ac9fb180SBram Moolenaar 	if (value[i] == ',')
654ac9fb180SBram Moolenaar 	{
655ac9fb180SBram Moolenaar 	    arg = &value[i + 1];
6560a52df50SBram Moolenaar # if defined(FEAT_EVAL)
657ac9fb180SBram Moolenaar 	    arglen = vallen - i - 1;
658ac9fb180SBram Moolenaar # endif
659ac9fb180SBram Moolenaar 	    valend = i;
660ac9fb180SBram Moolenaar 	    break;
661ac9fb180SBram Moolenaar 	}
662ac9fb180SBram Moolenaar     }
663ac9fb180SBram Moolenaar 
664ac9fb180SBram Moolenaar     for (i = 0; command_complete[i].expand != 0; ++i)
665ac9fb180SBram Moolenaar     {
666ac9fb180SBram Moolenaar 	if ((int)STRLEN(command_complete[i].name) == valend
667ac9fb180SBram Moolenaar 		&& STRNCMP(value, command_complete[i].name, valend) == 0)
668ac9fb180SBram Moolenaar 	{
669ac9fb180SBram Moolenaar 	    *complp = command_complete[i].expand;
670ac9fb180SBram Moolenaar 	    if (command_complete[i].expand == EXPAND_BUFFERS)
6718071cb2cSBram Moolenaar 		*argt |= EX_BUFNAME;
672ac9fb180SBram Moolenaar 	    else if (command_complete[i].expand == EXPAND_DIRECTORIES
673ac9fb180SBram Moolenaar 		    || command_complete[i].expand == EXPAND_FILES)
6748071cb2cSBram Moolenaar 		*argt |= EX_XFILE;
675ac9fb180SBram Moolenaar 	    break;
676ac9fb180SBram Moolenaar 	}
677ac9fb180SBram Moolenaar     }
678ac9fb180SBram Moolenaar 
679ac9fb180SBram Moolenaar     if (command_complete[i].expand == 0)
680ac9fb180SBram Moolenaar     {
681ac9fb180SBram Moolenaar 	semsg(_("E180: Invalid complete value: %s"), value);
682ac9fb180SBram Moolenaar 	return FAIL;
683ac9fb180SBram Moolenaar     }
684ac9fb180SBram Moolenaar 
6850a52df50SBram Moolenaar # if defined(FEAT_EVAL)
686ac9fb180SBram Moolenaar     if (*complp != EXPAND_USER_DEFINED && *complp != EXPAND_USER_LIST
687ac9fb180SBram Moolenaar 							       && arg != NULL)
688ac9fb180SBram Moolenaar # else
689ac9fb180SBram Moolenaar     if (arg != NULL)
690ac9fb180SBram Moolenaar # endif
691ac9fb180SBram Moolenaar     {
692ac9fb180SBram Moolenaar 	emsg(_("E468: Completion argument only allowed for custom completion"));
693ac9fb180SBram Moolenaar 	return FAIL;
694ac9fb180SBram Moolenaar     }
695ac9fb180SBram Moolenaar 
6960a52df50SBram Moolenaar # if defined(FEAT_EVAL)
697ac9fb180SBram Moolenaar     if ((*complp == EXPAND_USER_DEFINED || *complp == EXPAND_USER_LIST)
698ac9fb180SBram Moolenaar 							       && arg == NULL)
699ac9fb180SBram Moolenaar     {
700ac9fb180SBram Moolenaar 	emsg(_("E467: Custom completion requires a function argument"));
701ac9fb180SBram Moolenaar 	return FAIL;
702ac9fb180SBram Moolenaar     }
703ac9fb180SBram Moolenaar 
704ac9fb180SBram Moolenaar     if (arg != NULL)
70571ccd03eSBram Moolenaar 	*compl_arg = vim_strnsave(arg, arglen);
706ac9fb180SBram Moolenaar # endif
707ac9fb180SBram Moolenaar     return OK;
708ac9fb180SBram Moolenaar }
709ac9fb180SBram Moolenaar 
710ac9fb180SBram Moolenaar /*
711ac9fb180SBram Moolenaar  * Scan attributes in the ":command" command.
712ac9fb180SBram Moolenaar  * Return FAIL when something is wrong.
713ac9fb180SBram Moolenaar  */
714ac9fb180SBram Moolenaar     static int
uc_scan_attr(char_u * attr,size_t len,long * argt,long * def,int * flags,int * complp,char_u ** compl_arg,cmd_addr_T * addr_type_arg)715ac9fb180SBram Moolenaar uc_scan_attr(
716ac9fb180SBram Moolenaar     char_u	*attr,
717ac9fb180SBram Moolenaar     size_t	len,
718ac9fb180SBram Moolenaar     long	*argt,
719ac9fb180SBram Moolenaar     long	*def,
720ac9fb180SBram Moolenaar     int		*flags,
72152111f82SBram Moolenaar     int		*complp,
722ac9fb180SBram Moolenaar     char_u	**compl_arg,
723b731689eSBram Moolenaar     cmd_addr_T	*addr_type_arg)
724ac9fb180SBram Moolenaar {
725ac9fb180SBram Moolenaar     char_u	*p;
726ac9fb180SBram Moolenaar 
727ac9fb180SBram Moolenaar     if (len == 0)
728ac9fb180SBram Moolenaar     {
729ac9fb180SBram Moolenaar 	emsg(_("E175: No attribute specified"));
730ac9fb180SBram Moolenaar 	return FAIL;
731ac9fb180SBram Moolenaar     }
732ac9fb180SBram Moolenaar 
733ac9fb180SBram Moolenaar     // First, try the simple attributes (no arguments)
734ac9fb180SBram Moolenaar     if (STRNICMP(attr, "bang", len) == 0)
7358071cb2cSBram Moolenaar 	*argt |= EX_BANG;
736ac9fb180SBram Moolenaar     else if (STRNICMP(attr, "buffer", len) == 0)
737ac9fb180SBram Moolenaar 	*flags |= UC_BUFFER;
738ac9fb180SBram Moolenaar     else if (STRNICMP(attr, "register", len) == 0)
7398071cb2cSBram Moolenaar 	*argt |= EX_REGSTR;
74058ef8a31SBram Moolenaar     else if (STRNICMP(attr, "keepscript", len) == 0)
74158ef8a31SBram Moolenaar 	*argt |= EX_KEEPSCRIPT;
742ac9fb180SBram Moolenaar     else if (STRNICMP(attr, "bar", len) == 0)
7438071cb2cSBram Moolenaar 	*argt |= EX_TRLBAR;
744ac9fb180SBram Moolenaar     else
745ac9fb180SBram Moolenaar     {
746ac9fb180SBram Moolenaar 	int	i;
747ac9fb180SBram Moolenaar 	char_u	*val = NULL;
748ac9fb180SBram Moolenaar 	size_t	vallen = 0;
749ac9fb180SBram Moolenaar 	size_t	attrlen = len;
750ac9fb180SBram Moolenaar 
751ac9fb180SBram Moolenaar 	// Look for the attribute name - which is the part before any '='
752ac9fb180SBram Moolenaar 	for (i = 0; i < (int)len; ++i)
753ac9fb180SBram Moolenaar 	{
754ac9fb180SBram Moolenaar 	    if (attr[i] == '=')
755ac9fb180SBram Moolenaar 	    {
756ac9fb180SBram Moolenaar 		val = &attr[i + 1];
757ac9fb180SBram Moolenaar 		vallen = len - i - 1;
758ac9fb180SBram Moolenaar 		attrlen = i;
759ac9fb180SBram Moolenaar 		break;
760ac9fb180SBram Moolenaar 	    }
761ac9fb180SBram Moolenaar 	}
762ac9fb180SBram Moolenaar 
763ac9fb180SBram Moolenaar 	if (STRNICMP(attr, "nargs", attrlen) == 0)
764ac9fb180SBram Moolenaar 	{
765ac9fb180SBram Moolenaar 	    if (vallen == 1)
766ac9fb180SBram Moolenaar 	    {
767ac9fb180SBram Moolenaar 		if (*val == '0')
768ac9fb180SBram Moolenaar 		    // Do nothing - this is the default
769ac9fb180SBram Moolenaar 		    ;
770ac9fb180SBram Moolenaar 		else if (*val == '1')
7718071cb2cSBram Moolenaar 		    *argt |= (EX_EXTRA | EX_NOSPC | EX_NEEDARG);
772ac9fb180SBram Moolenaar 		else if (*val == '*')
7738071cb2cSBram Moolenaar 		    *argt |= EX_EXTRA;
774ac9fb180SBram Moolenaar 		else if (*val == '?')
7758071cb2cSBram Moolenaar 		    *argt |= (EX_EXTRA | EX_NOSPC);
776ac9fb180SBram Moolenaar 		else if (*val == '+')
7778071cb2cSBram Moolenaar 		    *argt |= (EX_EXTRA | EX_NEEDARG);
778ac9fb180SBram Moolenaar 		else
779ac9fb180SBram Moolenaar 		    goto wrong_nargs;
780ac9fb180SBram Moolenaar 	    }
781ac9fb180SBram Moolenaar 	    else
782ac9fb180SBram Moolenaar 	    {
783ac9fb180SBram Moolenaar wrong_nargs:
784ac9fb180SBram Moolenaar 		emsg(_("E176: Invalid number of arguments"));
785ac9fb180SBram Moolenaar 		return FAIL;
786ac9fb180SBram Moolenaar 	    }
787ac9fb180SBram Moolenaar 	}
788ac9fb180SBram Moolenaar 	else if (STRNICMP(attr, "range", attrlen) == 0)
789ac9fb180SBram Moolenaar 	{
7908071cb2cSBram Moolenaar 	    *argt |= EX_RANGE;
791ac9fb180SBram Moolenaar 	    if (vallen == 1 && *val == '%')
7928071cb2cSBram Moolenaar 		*argt |= EX_DFLALL;
793ac9fb180SBram Moolenaar 	    else if (val != NULL)
794ac9fb180SBram Moolenaar 	    {
795ac9fb180SBram Moolenaar 		p = val;
796ac9fb180SBram Moolenaar 		if (*def >= 0)
797ac9fb180SBram Moolenaar 		{
798ac9fb180SBram Moolenaar two_count:
799ac9fb180SBram Moolenaar 		    emsg(_("E177: Count cannot be specified twice"));
800ac9fb180SBram Moolenaar 		    return FAIL;
801ac9fb180SBram Moolenaar 		}
802ac9fb180SBram Moolenaar 
803ac9fb180SBram Moolenaar 		*def = getdigits(&p);
8048071cb2cSBram Moolenaar 		*argt |= EX_ZEROR;
805ac9fb180SBram Moolenaar 
806ac9fb180SBram Moolenaar 		if (p != val + vallen || vallen == 0)
807ac9fb180SBram Moolenaar 		{
808ac9fb180SBram Moolenaar invalid_count:
809ac9fb180SBram Moolenaar 		    emsg(_("E178: Invalid default value for count"));
810ac9fb180SBram Moolenaar 		    return FAIL;
811ac9fb180SBram Moolenaar 		}
812ac9fb180SBram Moolenaar 	    }
813b731689eSBram Moolenaar 	    // default for -range is using buffer lines
814b731689eSBram Moolenaar 	    if (*addr_type_arg == ADDR_NONE)
815b731689eSBram Moolenaar 		*addr_type_arg = ADDR_LINES;
816ac9fb180SBram Moolenaar 	}
817ac9fb180SBram Moolenaar 	else if (STRNICMP(attr, "count", attrlen) == 0)
818ac9fb180SBram Moolenaar 	{
8198071cb2cSBram Moolenaar 	    *argt |= (EX_COUNT | EX_ZEROR | EX_RANGE);
820b731689eSBram Moolenaar 	    // default for -count is using any number
821b731689eSBram Moolenaar 	    if (*addr_type_arg == ADDR_NONE)
822b731689eSBram Moolenaar 		*addr_type_arg = ADDR_OTHER;
823ac9fb180SBram Moolenaar 
824ac9fb180SBram Moolenaar 	    if (val != NULL)
825ac9fb180SBram Moolenaar 	    {
826ac9fb180SBram Moolenaar 		p = val;
827ac9fb180SBram Moolenaar 		if (*def >= 0)
828ac9fb180SBram Moolenaar 		    goto two_count;
829ac9fb180SBram Moolenaar 
830ac9fb180SBram Moolenaar 		*def = getdigits(&p);
831ac9fb180SBram Moolenaar 
832ac9fb180SBram Moolenaar 		if (p != val + vallen)
833ac9fb180SBram Moolenaar 		    goto invalid_count;
834ac9fb180SBram Moolenaar 	    }
835ac9fb180SBram Moolenaar 
836ac9fb180SBram Moolenaar 	    if (*def < 0)
837ac9fb180SBram Moolenaar 		*def = 0;
838ac9fb180SBram Moolenaar 	}
839ac9fb180SBram Moolenaar 	else if (STRNICMP(attr, "complete", attrlen) == 0)
840ac9fb180SBram Moolenaar 	{
841ac9fb180SBram Moolenaar 	    if (val == NULL)
842ac9fb180SBram Moolenaar 	    {
843ac9fb180SBram Moolenaar 		emsg(_("E179: argument required for -complete"));
844ac9fb180SBram Moolenaar 		return FAIL;
845ac9fb180SBram Moolenaar 	    }
846ac9fb180SBram Moolenaar 
84752111f82SBram Moolenaar 	    if (parse_compl_arg(val, (int)vallen, complp, argt, compl_arg)
848ac9fb180SBram Moolenaar 								      == FAIL)
849ac9fb180SBram Moolenaar 		return FAIL;
850ac9fb180SBram Moolenaar 	}
851ac9fb180SBram Moolenaar 	else if (STRNICMP(attr, "addr", attrlen) == 0)
852ac9fb180SBram Moolenaar 	{
8538071cb2cSBram Moolenaar 	    *argt |= EX_RANGE;
854ac9fb180SBram Moolenaar 	    if (val == NULL)
855ac9fb180SBram Moolenaar 	    {
856ac9fb180SBram Moolenaar 		emsg(_("E179: argument required for -addr"));
857ac9fb180SBram Moolenaar 		return FAIL;
858ac9fb180SBram Moolenaar 	    }
859b731689eSBram Moolenaar 	    if (parse_addr_type_arg(val, (int)vallen, addr_type_arg) == FAIL)
860ac9fb180SBram Moolenaar 		return FAIL;
861e4f5f3aaSBram Moolenaar 	    if (*addr_type_arg != ADDR_LINES)
8628071cb2cSBram Moolenaar 		*argt |= EX_ZEROR;
863ac9fb180SBram Moolenaar 	}
864ac9fb180SBram Moolenaar 	else
865ac9fb180SBram Moolenaar 	{
866ac9fb180SBram Moolenaar 	    char_u ch = attr[len];
867ac9fb180SBram Moolenaar 	    attr[len] = '\0';
868ac9fb180SBram Moolenaar 	    semsg(_("E181: Invalid attribute: %s"), attr);
869ac9fb180SBram Moolenaar 	    attr[len] = ch;
870ac9fb180SBram Moolenaar 	    return FAIL;
871ac9fb180SBram Moolenaar 	}
872ac9fb180SBram Moolenaar     }
873ac9fb180SBram Moolenaar 
874ac9fb180SBram Moolenaar     return OK;
875ac9fb180SBram Moolenaar }
876ac9fb180SBram Moolenaar 
877ac9fb180SBram Moolenaar /*
878ac9fb180SBram Moolenaar  * Add a user command to the list or replace an existing one.
879ac9fb180SBram Moolenaar  */
880ac9fb180SBram Moolenaar     static int
uc_add_command(char_u * name,size_t name_len,char_u * rep,long argt,long def,int flags,int compl,char_u * compl_arg UNUSED,cmd_addr_T addr_type,int force)881ac9fb180SBram Moolenaar uc_add_command(
882ac9fb180SBram Moolenaar     char_u	*name,
883ac9fb180SBram Moolenaar     size_t	name_len,
884ac9fb180SBram Moolenaar     char_u	*rep,
885ac9fb180SBram Moolenaar     long	argt,
886ac9fb180SBram Moolenaar     long	def,
887ac9fb180SBram Moolenaar     int		flags,
888ac9fb180SBram Moolenaar     int		compl,
889ac9fb180SBram Moolenaar     char_u	*compl_arg UNUSED,
890b731689eSBram Moolenaar     cmd_addr_T	addr_type,
891ac9fb180SBram Moolenaar     int		force)
892ac9fb180SBram Moolenaar {
893ac9fb180SBram Moolenaar     ucmd_T	*cmd = NULL;
894ac9fb180SBram Moolenaar     char_u	*p;
895ac9fb180SBram Moolenaar     int		i;
896ac9fb180SBram Moolenaar     int		cmp = 1;
897ac9fb180SBram Moolenaar     char_u	*rep_buf = NULL;
898ac9fb180SBram Moolenaar     garray_T	*gap;
899ac9fb180SBram Moolenaar 
900459fd785SBram Moolenaar     replace_termcodes(rep, &rep_buf, 0, NULL);
901ac9fb180SBram Moolenaar     if (rep_buf == NULL)
902ac9fb180SBram Moolenaar     {
9035d7c2df5SBram Moolenaar 	// can't replace termcodes - try using the string as is
904ac9fb180SBram Moolenaar 	rep_buf = vim_strsave(rep);
905ac9fb180SBram Moolenaar 
9065d7c2df5SBram Moolenaar 	// give up if out of memory
907ac9fb180SBram Moolenaar 	if (rep_buf == NULL)
908ac9fb180SBram Moolenaar 	    return FAIL;
909ac9fb180SBram Moolenaar     }
910ac9fb180SBram Moolenaar 
911ac9fb180SBram Moolenaar     // get address of growarray: global or in curbuf
912ac9fb180SBram Moolenaar     if (flags & UC_BUFFER)
913ac9fb180SBram Moolenaar     {
914ac9fb180SBram Moolenaar 	gap = &curbuf->b_ucmds;
915ac9fb180SBram Moolenaar 	if (gap->ga_itemsize == 0)
916ac9fb180SBram Moolenaar 	    ga_init2(gap, (int)sizeof(ucmd_T), 4);
917ac9fb180SBram Moolenaar     }
918ac9fb180SBram Moolenaar     else
919ac9fb180SBram Moolenaar 	gap = &ucmds;
920ac9fb180SBram Moolenaar 
921ac9fb180SBram Moolenaar     // Search for the command in the already defined commands.
922ac9fb180SBram Moolenaar     for (i = 0; i < gap->ga_len; ++i)
923ac9fb180SBram Moolenaar     {
924ac9fb180SBram Moolenaar 	size_t len;
925ac9fb180SBram Moolenaar 
926ac9fb180SBram Moolenaar 	cmd = USER_CMD_GA(gap, i);
927ac9fb180SBram Moolenaar 	len = STRLEN(cmd->uc_name);
928ac9fb180SBram Moolenaar 	cmp = STRNCMP(name, cmd->uc_name, name_len);
929ac9fb180SBram Moolenaar 	if (cmp == 0)
930ac9fb180SBram Moolenaar 	{
931ac9fb180SBram Moolenaar 	    if (name_len < len)
932ac9fb180SBram Moolenaar 		cmp = -1;
933ac9fb180SBram Moolenaar 	    else if (name_len > len)
934ac9fb180SBram Moolenaar 		cmp = 1;
935ac9fb180SBram Moolenaar 	}
936ac9fb180SBram Moolenaar 
937ac9fb180SBram Moolenaar 	if (cmp == 0)
938ac9fb180SBram Moolenaar 	{
939ac9fb180SBram Moolenaar 	    // Command can be replaced with "command!" and when sourcing the
940ac9fb180SBram Moolenaar 	    // same script again, but only once.
941ac9fb180SBram Moolenaar 	    if (!force
942ac9fb180SBram Moolenaar #ifdef FEAT_EVAL
943ac9fb180SBram Moolenaar 		    && (cmd->uc_script_ctx.sc_sid != current_sctx.sc_sid
944ac9fb180SBram Moolenaar 			  || cmd->uc_script_ctx.sc_seq == current_sctx.sc_seq)
945ac9fb180SBram Moolenaar #endif
946ac9fb180SBram Moolenaar 		    )
947ac9fb180SBram Moolenaar 	    {
948ac9fb180SBram Moolenaar 		semsg(_("E174: Command already exists: add ! to replace it: %s"),
949ac9fb180SBram Moolenaar 									 name);
950ac9fb180SBram Moolenaar 		goto fail;
951ac9fb180SBram Moolenaar 	    }
952ac9fb180SBram Moolenaar 
953ac9fb180SBram Moolenaar 	    VIM_CLEAR(cmd->uc_rep);
9540a52df50SBram Moolenaar #if defined(FEAT_EVAL)
955ac9fb180SBram Moolenaar 	    VIM_CLEAR(cmd->uc_compl_arg);
956ac9fb180SBram Moolenaar #endif
957ac9fb180SBram Moolenaar 	    break;
958ac9fb180SBram Moolenaar 	}
959ac9fb180SBram Moolenaar 
960ac9fb180SBram Moolenaar 	// Stop as soon as we pass the name to add
961ac9fb180SBram Moolenaar 	if (cmp < 0)
962ac9fb180SBram Moolenaar 	    break;
963ac9fb180SBram Moolenaar     }
964ac9fb180SBram Moolenaar 
965ac9fb180SBram Moolenaar     // Extend the array unless we're replacing an existing command
966ac9fb180SBram Moolenaar     if (cmp != 0)
967ac9fb180SBram Moolenaar     {
968ac9fb180SBram Moolenaar 	if (ga_grow(gap, 1) != OK)
969ac9fb180SBram Moolenaar 	    goto fail;
97071ccd03eSBram Moolenaar 	if ((p = vim_strnsave(name, name_len)) == NULL)
971ac9fb180SBram Moolenaar 	    goto fail;
972ac9fb180SBram Moolenaar 
973ac9fb180SBram Moolenaar 	cmd = USER_CMD_GA(gap, i);
974ac9fb180SBram Moolenaar 	mch_memmove(cmd + 1, cmd, (gap->ga_len - i) * sizeof(ucmd_T));
975ac9fb180SBram Moolenaar 
976ac9fb180SBram Moolenaar 	++gap->ga_len;
977ac9fb180SBram Moolenaar 
978ac9fb180SBram Moolenaar 	cmd->uc_name = p;
979ac9fb180SBram Moolenaar     }
980ac9fb180SBram Moolenaar 
981ac9fb180SBram Moolenaar     cmd->uc_rep = rep_buf;
982ac9fb180SBram Moolenaar     cmd->uc_argt = argt;
983ac9fb180SBram Moolenaar     cmd->uc_def = def;
984ac9fb180SBram Moolenaar     cmd->uc_compl = compl;
985ac9fb180SBram Moolenaar     cmd->uc_script_ctx = current_sctx;
9865d7c2df5SBram Moolenaar     if (flags & UC_VIM9)
9875d7c2df5SBram Moolenaar 	cmd->uc_script_ctx.sc_version = SCRIPT_VERSION_VIM9;
9889b8d6226SBram Moolenaar #ifdef FEAT_EVAL
9891a47ae32SBram Moolenaar     cmd->uc_script_ctx.sc_lnum += SOURCING_LNUM;
990ac9fb180SBram Moolenaar     cmd->uc_compl_arg = compl_arg;
991ac9fb180SBram Moolenaar #endif
992ac9fb180SBram Moolenaar     cmd->uc_addr_type = addr_type;
993ac9fb180SBram Moolenaar 
994ac9fb180SBram Moolenaar     return OK;
995ac9fb180SBram Moolenaar 
996ac9fb180SBram Moolenaar fail:
997ac9fb180SBram Moolenaar     vim_free(rep_buf);
9980a52df50SBram Moolenaar #if defined(FEAT_EVAL)
999ac9fb180SBram Moolenaar     vim_free(compl_arg);
1000ac9fb180SBram Moolenaar #endif
1001ac9fb180SBram Moolenaar     return FAIL;
1002ac9fb180SBram Moolenaar }
1003ac9fb180SBram Moolenaar 
1004ac9fb180SBram Moolenaar /*
100573b8b0aeSBram Moolenaar  * If "p" starts with "{" then read a block of commands until "}".
100673b8b0aeSBram Moolenaar  * Used for ":command" and ":autocmd".
100773b8b0aeSBram Moolenaar  */
100873b8b0aeSBram Moolenaar     char_u *
may_get_cmd_block(exarg_T * eap,char_u * p,char_u ** tofree,int * flags)100973b8b0aeSBram Moolenaar may_get_cmd_block(exarg_T *eap, char_u *p, char_u **tofree, int *flags)
101073b8b0aeSBram Moolenaar {
101173b8b0aeSBram Moolenaar     char_u *retp = p;
101273b8b0aeSBram Moolenaar 
101373b8b0aeSBram Moolenaar     if (*p == '{' && ends_excmd2(eap->arg, skipwhite(p + 1))
101473b8b0aeSBram Moolenaar 						       && eap->getline != NULL)
101573b8b0aeSBram Moolenaar     {
101673b8b0aeSBram Moolenaar 	garray_T    ga;
101773b8b0aeSBram Moolenaar 	char_u	    *line = NULL;
101873b8b0aeSBram Moolenaar 
101973b8b0aeSBram Moolenaar 	ga_init2(&ga, sizeof(char_u *), 10);
102073b8b0aeSBram Moolenaar 	if (ga_add_string(&ga, p) == FAIL)
102173b8b0aeSBram Moolenaar 	    return retp;
102273b8b0aeSBram Moolenaar 
1023e4db17fbSBram Moolenaar 	// If the argument ends in "}" it must have been concatenated already
1024e4db17fbSBram Moolenaar 	// for ISN_EXEC.
1025e4db17fbSBram Moolenaar 	if (p[STRLEN(p) - 1] != '}')
102673b8b0aeSBram Moolenaar 	    // Read lines between '{' and '}'.  Does not support nesting or
102773b8b0aeSBram Moolenaar 	    // here-doc constructs.
102873b8b0aeSBram Moolenaar 	    for (;;)
102973b8b0aeSBram Moolenaar 	    {
103073b8b0aeSBram Moolenaar 		vim_free(line);
103173b8b0aeSBram Moolenaar 		if ((line = eap->getline(':', eap->cookie,
103273b8b0aeSBram Moolenaar 					   0, GETLINE_CONCAT_CONTBAR)) == NULL)
103373b8b0aeSBram Moolenaar 		{
103473b8b0aeSBram Moolenaar 		    emsg(_(e_missing_rcurly));
103573b8b0aeSBram Moolenaar 		    break;
103673b8b0aeSBram Moolenaar 		}
103773b8b0aeSBram Moolenaar 		if (ga_add_string(&ga, line) == FAIL)
103873b8b0aeSBram Moolenaar 		    break;
103973b8b0aeSBram Moolenaar 		if (*skipwhite(line) == '}')
104073b8b0aeSBram Moolenaar 		    break;
104173b8b0aeSBram Moolenaar 	    }
104273b8b0aeSBram Moolenaar 	vim_free(line);
104373b8b0aeSBram Moolenaar 	retp = *tofree = ga_concat_strings(&ga, "\n");
104473b8b0aeSBram Moolenaar 	ga_clear_strings(&ga);
104573b8b0aeSBram Moolenaar 	*flags |= UC_VIM9;
104673b8b0aeSBram Moolenaar     }
104773b8b0aeSBram Moolenaar     return retp;
104873b8b0aeSBram Moolenaar }
104973b8b0aeSBram Moolenaar 
105073b8b0aeSBram Moolenaar /*
1051ac9fb180SBram Moolenaar  * ":command ..." implementation
1052ac9fb180SBram Moolenaar  */
1053ac9fb180SBram Moolenaar     void
ex_command(exarg_T * eap)1054ac9fb180SBram Moolenaar ex_command(exarg_T *eap)
1055ac9fb180SBram Moolenaar {
1056ac9fb180SBram Moolenaar     char_u	*name;
1057ac9fb180SBram Moolenaar     char_u	*end;
1058ac9fb180SBram Moolenaar     char_u	*p;
1059ac9fb180SBram Moolenaar     long	argt = 0;
1060ac9fb180SBram Moolenaar     long	def = -1;
1061ac9fb180SBram Moolenaar     int		flags = 0;
1062ac9fb180SBram Moolenaar     int		compl = EXPAND_NOTHING;
1063ac9fb180SBram Moolenaar     char_u	*compl_arg = NULL;
1064b731689eSBram Moolenaar     cmd_addr_T	addr_type_arg = ADDR_NONE;
1065ac9fb180SBram Moolenaar     int		has_attr = (eap->arg[0] == '-');
1066ac9fb180SBram Moolenaar     int		name_len;
1067ac9fb180SBram Moolenaar 
1068ac9fb180SBram Moolenaar     p = eap->arg;
1069ac9fb180SBram Moolenaar 
1070ac9fb180SBram Moolenaar     // Check for attributes
1071ac9fb180SBram Moolenaar     while (*p == '-')
1072ac9fb180SBram Moolenaar     {
1073ac9fb180SBram Moolenaar 	++p;
1074ac9fb180SBram Moolenaar 	end = skiptowhite(p);
1075ac9fb180SBram Moolenaar 	if (uc_scan_attr(p, end - p, &argt, &def, &flags, &compl,
1076ac9fb180SBram Moolenaar 					   &compl_arg, &addr_type_arg) == FAIL)
1077ac9fb180SBram Moolenaar 	    return;
1078ac9fb180SBram Moolenaar 	p = skipwhite(end);
1079ac9fb180SBram Moolenaar     }
1080ac9fb180SBram Moolenaar 
1081ac9fb180SBram Moolenaar     // Get the name (if any) and skip to the following argument
1082ac9fb180SBram Moolenaar     name = p;
1083ac9fb180SBram Moolenaar     if (ASCII_ISALPHA(*p))
1084ac9fb180SBram Moolenaar 	while (ASCII_ISALNUM(*p))
1085ac9fb180SBram Moolenaar 	    ++p;
1086a72cfb80SBram Moolenaar     if (!ends_excmd2(eap->arg, p) && !VIM_ISWHITE(*p))
1087ac9fb180SBram Moolenaar     {
1088ac9fb180SBram Moolenaar 	emsg(_("E182: Invalid command name"));
1089ac9fb180SBram Moolenaar 	return;
1090ac9fb180SBram Moolenaar     }
1091ac9fb180SBram Moolenaar     end = p;
1092ac9fb180SBram Moolenaar     name_len = (int)(end - name);
1093ac9fb180SBram Moolenaar 
1094ac9fb180SBram Moolenaar     // If there is nothing after the name, and no attributes were specified,
1095ac9fb180SBram Moolenaar     // we are listing commands
1096ac9fb180SBram Moolenaar     p = skipwhite(end);
1097a72cfb80SBram Moolenaar     if (!has_attr && ends_excmd2(eap->arg, p))
1098ac9fb180SBram Moolenaar 	uc_list(name, end - name);
1099ac9fb180SBram Moolenaar     else if (!ASCII_ISUPPER(*name))
1100ac9fb180SBram Moolenaar 	emsg(_("E183: User defined commands must start with an uppercase letter"));
1101ac9fb180SBram Moolenaar     else if ((name_len == 1 && *name == 'X')
1102ac9fb180SBram Moolenaar 	  || (name_len <= 4
1103ac9fb180SBram Moolenaar 		  && STRNCMP(name, "Next", name_len > 4 ? 4 : name_len) == 0))
1104ac9fb180SBram Moolenaar 	emsg(_("E841: Reserved name, cannot be used for user defined command"));
1105de69a735SMartin Tournoij     else if (compl > 0 && (argt & EX_EXTRA) == 0)
1106cc7eb2aaSBram Moolenaar     {
1107cc7eb2aaSBram Moolenaar 	// Some plugins rely on silently ignoring the mistake, only make this
1108cc7eb2aaSBram Moolenaar 	// an error in Vim9 script.
1109cc7eb2aaSBram Moolenaar 	if (in_vim9script())
111041a3485dSBram Moolenaar 	    emsg(_(e_complete_used_without_allowing_arguments));
1111ac9fb180SBram Moolenaar 	else
1112cc7eb2aaSBram Moolenaar 	    give_warning_with_source(
111341a3485dSBram Moolenaar 		       (char_u *)_(e_complete_used_without_allowing_arguments),
111441a3485dSBram Moolenaar 								   TRUE, TRUE);
1115cc7eb2aaSBram Moolenaar     }
1116cc7eb2aaSBram Moolenaar     else
11175d7c2df5SBram Moolenaar     {
11185d7c2df5SBram Moolenaar 	char_u *tofree = NULL;
11195d7c2df5SBram Moolenaar 
112073b8b0aeSBram Moolenaar 	p = may_get_cmd_block(eap, p, &tofree, &flags);
11215d7c2df5SBram Moolenaar 
1122ac9fb180SBram Moolenaar 	uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg,
1123ac9fb180SBram Moolenaar 						  addr_type_arg, eap->forceit);
11245d7c2df5SBram Moolenaar 	vim_free(tofree);
11255d7c2df5SBram Moolenaar     }
1126ac9fb180SBram Moolenaar }
1127ac9fb180SBram Moolenaar 
1128ac9fb180SBram Moolenaar /*
1129ac9fb180SBram Moolenaar  * ":comclear" implementation
1130ac9fb180SBram Moolenaar  * Clear all user commands, global and for current buffer.
1131ac9fb180SBram Moolenaar  */
1132ac9fb180SBram Moolenaar     void
ex_comclear(exarg_T * eap UNUSED)1133ac9fb180SBram Moolenaar ex_comclear(exarg_T *eap UNUSED)
1134ac9fb180SBram Moolenaar {
1135ac9fb180SBram Moolenaar     uc_clear(&ucmds);
1136e5c83286SBram Moolenaar     if (curbuf != NULL)
1137ac9fb180SBram Moolenaar 	uc_clear(&curbuf->b_ucmds);
1138ac9fb180SBram Moolenaar }
1139ac9fb180SBram Moolenaar 
1140ac9fb180SBram Moolenaar /*
1141ac9fb180SBram Moolenaar  * Clear all user commands for "gap".
1142ac9fb180SBram Moolenaar  */
1143ac9fb180SBram Moolenaar     void
uc_clear(garray_T * gap)1144ac9fb180SBram Moolenaar uc_clear(garray_T *gap)
1145ac9fb180SBram Moolenaar {
1146ac9fb180SBram Moolenaar     int		i;
1147ac9fb180SBram Moolenaar     ucmd_T	*cmd;
1148ac9fb180SBram Moolenaar 
1149ac9fb180SBram Moolenaar     for (i = 0; i < gap->ga_len; ++i)
1150ac9fb180SBram Moolenaar     {
1151ac9fb180SBram Moolenaar 	cmd = USER_CMD_GA(gap, i);
1152ac9fb180SBram Moolenaar 	vim_free(cmd->uc_name);
1153ac9fb180SBram Moolenaar 	vim_free(cmd->uc_rep);
11540a52df50SBram Moolenaar # if defined(FEAT_EVAL)
1155ac9fb180SBram Moolenaar 	vim_free(cmd->uc_compl_arg);
1156ac9fb180SBram Moolenaar # endif
1157ac9fb180SBram Moolenaar     }
1158ac9fb180SBram Moolenaar     ga_clear(gap);
1159ac9fb180SBram Moolenaar }
1160ac9fb180SBram Moolenaar 
1161ac9fb180SBram Moolenaar /*
1162ac9fb180SBram Moolenaar  * ":delcommand" implementation
1163ac9fb180SBram Moolenaar  */
1164ac9fb180SBram Moolenaar     void
ex_delcommand(exarg_T * eap)1165ac9fb180SBram Moolenaar ex_delcommand(exarg_T *eap)
1166ac9fb180SBram Moolenaar {
1167ac9fb180SBram Moolenaar     int		i = 0;
1168ac9fb180SBram Moolenaar     ucmd_T	*cmd = NULL;
1169bdcba24dSBram Moolenaar     int		res = -1;
1170ac9fb180SBram Moolenaar     garray_T	*gap;
1171bdcba24dSBram Moolenaar     char_u	*arg = eap->arg;
1172bdcba24dSBram Moolenaar     int		buffer_only = FALSE;
1173bdcba24dSBram Moolenaar 
1174bdcba24dSBram Moolenaar     if (STRNCMP(arg, "-buffer", 7) == 0 && VIM_ISWHITE(arg[7]))
1175bdcba24dSBram Moolenaar     {
1176bdcba24dSBram Moolenaar 	buffer_only = TRUE;
1177bdcba24dSBram Moolenaar 	arg = skipwhite(arg + 7);
1178bdcba24dSBram Moolenaar     }
1179ac9fb180SBram Moolenaar 
1180ac9fb180SBram Moolenaar     gap = &curbuf->b_ucmds;
1181ac9fb180SBram Moolenaar     for (;;)
1182ac9fb180SBram Moolenaar     {
1183ac9fb180SBram Moolenaar 	for (i = 0; i < gap->ga_len; ++i)
1184ac9fb180SBram Moolenaar 	{
1185ac9fb180SBram Moolenaar 	    cmd = USER_CMD_GA(gap, i);
1186bdcba24dSBram Moolenaar 	    res = STRCMP(arg, cmd->uc_name);
1187bdcba24dSBram Moolenaar 	    if (res <= 0)
1188ac9fb180SBram Moolenaar 		break;
1189ac9fb180SBram Moolenaar 	}
1190bdcba24dSBram Moolenaar 	if (gap == &ucmds || res == 0 || buffer_only)
1191ac9fb180SBram Moolenaar 	    break;
1192ac9fb180SBram Moolenaar 	gap = &ucmds;
1193ac9fb180SBram Moolenaar     }
1194ac9fb180SBram Moolenaar 
1195bdcba24dSBram Moolenaar     if (res != 0)
1196ac9fb180SBram Moolenaar     {
1197bdcba24dSBram Moolenaar 	semsg(_(buffer_only
1198bdcba24dSBram Moolenaar 		    ? e_no_such_user_defined_command_in_current_buffer_str
1199bdcba24dSBram Moolenaar 		    : e_no_such_user_defined_command_str), arg);
1200ac9fb180SBram Moolenaar 	return;
1201ac9fb180SBram Moolenaar     }
1202ac9fb180SBram Moolenaar 
1203ac9fb180SBram Moolenaar     vim_free(cmd->uc_name);
1204ac9fb180SBram Moolenaar     vim_free(cmd->uc_rep);
12050a52df50SBram Moolenaar # if defined(FEAT_EVAL)
1206ac9fb180SBram Moolenaar     vim_free(cmd->uc_compl_arg);
1207ac9fb180SBram Moolenaar # endif
1208ac9fb180SBram Moolenaar 
1209ac9fb180SBram Moolenaar     --gap->ga_len;
1210ac9fb180SBram Moolenaar 
1211ac9fb180SBram Moolenaar     if (i < gap->ga_len)
1212ac9fb180SBram Moolenaar 	mch_memmove(cmd, cmd + 1, (gap->ga_len - i) * sizeof(ucmd_T));
1213ac9fb180SBram Moolenaar }
1214ac9fb180SBram Moolenaar 
1215ac9fb180SBram Moolenaar /*
1216ac9fb180SBram Moolenaar  * Split and quote args for <f-args>.
1217ac9fb180SBram Moolenaar  */
1218ac9fb180SBram Moolenaar     static char_u *
uc_split_args(char_u * arg,size_t * lenp)1219ac9fb180SBram Moolenaar uc_split_args(char_u *arg, size_t *lenp)
1220ac9fb180SBram Moolenaar {
1221ac9fb180SBram Moolenaar     char_u *buf;
1222ac9fb180SBram Moolenaar     char_u *p;
1223ac9fb180SBram Moolenaar     char_u *q;
1224ac9fb180SBram Moolenaar     int len;
1225ac9fb180SBram Moolenaar 
1226ac9fb180SBram Moolenaar     // Precalculate length
1227ac9fb180SBram Moolenaar     p = arg;
1228ac9fb180SBram Moolenaar     len = 2; // Initial and final quotes
1229ac9fb180SBram Moolenaar 
1230ac9fb180SBram Moolenaar     while (*p)
1231ac9fb180SBram Moolenaar     {
1232ac9fb180SBram Moolenaar 	if (p[0] == '\\' && p[1] == '\\')
1233ac9fb180SBram Moolenaar 	{
1234ac9fb180SBram Moolenaar 	    len += 2;
1235ac9fb180SBram Moolenaar 	    p += 2;
1236ac9fb180SBram Moolenaar 	}
1237ac9fb180SBram Moolenaar 	else if (p[0] == '\\' && VIM_ISWHITE(p[1]))
1238ac9fb180SBram Moolenaar 	{
1239ac9fb180SBram Moolenaar 	    len += 1;
1240ac9fb180SBram Moolenaar 	    p += 2;
1241ac9fb180SBram Moolenaar 	}
1242ac9fb180SBram Moolenaar 	else if (*p == '\\' || *p == '"')
1243ac9fb180SBram Moolenaar 	{
1244ac9fb180SBram Moolenaar 	    len += 2;
1245ac9fb180SBram Moolenaar 	    p += 1;
1246ac9fb180SBram Moolenaar 	}
1247ac9fb180SBram Moolenaar 	else if (VIM_ISWHITE(*p))
1248ac9fb180SBram Moolenaar 	{
1249ac9fb180SBram Moolenaar 	    p = skipwhite(p);
1250ac9fb180SBram Moolenaar 	    if (*p == NUL)
1251ac9fb180SBram Moolenaar 		break;
125220d89e0aSBram Moolenaar 	    len += 4; // ", "
1253ac9fb180SBram Moolenaar 	}
1254ac9fb180SBram Moolenaar 	else
1255ac9fb180SBram Moolenaar 	{
1256ac9fb180SBram Moolenaar 	    int charlen = (*mb_ptr2len)(p);
1257ac9fb180SBram Moolenaar 
1258ac9fb180SBram Moolenaar 	    len += charlen;
1259ac9fb180SBram Moolenaar 	    p += charlen;
1260ac9fb180SBram Moolenaar 	}
1261ac9fb180SBram Moolenaar     }
1262ac9fb180SBram Moolenaar 
1263ac9fb180SBram Moolenaar     buf = alloc(len + 1);
1264ac9fb180SBram Moolenaar     if (buf == NULL)
1265ac9fb180SBram Moolenaar     {
1266ac9fb180SBram Moolenaar 	*lenp = 0;
1267ac9fb180SBram Moolenaar 	return buf;
1268ac9fb180SBram Moolenaar     }
1269ac9fb180SBram Moolenaar 
1270ac9fb180SBram Moolenaar     p = arg;
1271ac9fb180SBram Moolenaar     q = buf;
1272ac9fb180SBram Moolenaar     *q++ = '"';
1273ac9fb180SBram Moolenaar     while (*p)
1274ac9fb180SBram Moolenaar     {
1275ac9fb180SBram Moolenaar 	if (p[0] == '\\' && p[1] == '\\')
1276ac9fb180SBram Moolenaar 	{
1277ac9fb180SBram Moolenaar 	    *q++ = '\\';
1278ac9fb180SBram Moolenaar 	    *q++ = '\\';
1279ac9fb180SBram Moolenaar 	    p += 2;
1280ac9fb180SBram Moolenaar 	}
1281ac9fb180SBram Moolenaar 	else if (p[0] == '\\' && VIM_ISWHITE(p[1]))
1282ac9fb180SBram Moolenaar 	{
1283ac9fb180SBram Moolenaar 	    *q++ = p[1];
1284ac9fb180SBram Moolenaar 	    p += 2;
1285ac9fb180SBram Moolenaar 	}
1286ac9fb180SBram Moolenaar 	else if (*p == '\\' || *p == '"')
1287ac9fb180SBram Moolenaar 	{
1288ac9fb180SBram Moolenaar 	    *q++ = '\\';
1289ac9fb180SBram Moolenaar 	    *q++ = *p++;
1290ac9fb180SBram Moolenaar 	}
1291ac9fb180SBram Moolenaar 	else if (VIM_ISWHITE(*p))
1292ac9fb180SBram Moolenaar 	{
1293ac9fb180SBram Moolenaar 	    p = skipwhite(p);
1294ac9fb180SBram Moolenaar 	    if (*p == NUL)
1295ac9fb180SBram Moolenaar 		break;
1296ac9fb180SBram Moolenaar 	    *q++ = '"';
1297ac9fb180SBram Moolenaar 	    *q++ = ',';
129820d89e0aSBram Moolenaar 	    *q++ = ' ';
1299ac9fb180SBram Moolenaar 	    *q++ = '"';
1300ac9fb180SBram Moolenaar 	}
1301ac9fb180SBram Moolenaar 	else
1302ac9fb180SBram Moolenaar 	{
1303ac9fb180SBram Moolenaar 	    MB_COPY_CHAR(p, q);
1304ac9fb180SBram Moolenaar 	}
1305ac9fb180SBram Moolenaar     }
1306ac9fb180SBram Moolenaar     *q++ = '"';
1307ac9fb180SBram Moolenaar     *q = 0;
1308ac9fb180SBram Moolenaar 
1309ac9fb180SBram Moolenaar     *lenp = len;
1310ac9fb180SBram Moolenaar     return buf;
1311ac9fb180SBram Moolenaar }
1312ac9fb180SBram Moolenaar 
1313ac9fb180SBram Moolenaar     static size_t
add_cmd_modifier(char_u * buf,char * mod_str,int * multi_mods)1314ac9fb180SBram Moolenaar add_cmd_modifier(char_u *buf, char *mod_str, int *multi_mods)
1315ac9fb180SBram Moolenaar {
1316ac9fb180SBram Moolenaar     size_t result;
1317ac9fb180SBram Moolenaar 
1318ac9fb180SBram Moolenaar     result = STRLEN(mod_str);
1319ac9fb180SBram Moolenaar     if (*multi_mods)
1320ac9fb180SBram Moolenaar 	result += 1;
1321ac9fb180SBram Moolenaar     if (buf != NULL)
1322ac9fb180SBram Moolenaar     {
1323ac9fb180SBram Moolenaar 	if (*multi_mods)
1324ac9fb180SBram Moolenaar 	    STRCAT(buf, " ");
1325ac9fb180SBram Moolenaar 	STRCAT(buf, mod_str);
1326ac9fb180SBram Moolenaar     }
1327ac9fb180SBram Moolenaar 
1328ac9fb180SBram Moolenaar     *multi_mods = 1;
1329ac9fb180SBram Moolenaar 
1330ac9fb180SBram Moolenaar     return result;
1331ac9fb180SBram Moolenaar }
1332ac9fb180SBram Moolenaar 
1333ac9fb180SBram Moolenaar /*
133402194d2bSBram Moolenaar  * Add modifiers from "cmod->cmod_split" to "buf".  Set "multi_mods" when one
1335e1004401SBram Moolenaar  * was added.  Return the number of bytes added.
13367a1637f4SBram Moolenaar  */
13377a1637f4SBram Moolenaar     size_t
add_win_cmd_modifers(char_u * buf,cmdmod_T * cmod,int * multi_mods)133802194d2bSBram Moolenaar add_win_cmd_modifers(char_u *buf, cmdmod_T *cmod, int *multi_mods)
13397a1637f4SBram Moolenaar {
13407a1637f4SBram Moolenaar     size_t result = 0;
13417a1637f4SBram Moolenaar 
13427a1637f4SBram Moolenaar     // :aboveleft and :leftabove
134302194d2bSBram Moolenaar     if (cmod->cmod_split & WSP_ABOVE)
13447a1637f4SBram Moolenaar 	result += add_cmd_modifier(buf, "aboveleft", multi_mods);
13457a1637f4SBram Moolenaar     // :belowright and :rightbelow
134602194d2bSBram Moolenaar     if (cmod->cmod_split & WSP_BELOW)
13477a1637f4SBram Moolenaar 	result += add_cmd_modifier(buf, "belowright", multi_mods);
13487a1637f4SBram Moolenaar     // :botright
134902194d2bSBram Moolenaar     if (cmod->cmod_split & WSP_BOT)
13507a1637f4SBram Moolenaar 	result += add_cmd_modifier(buf, "botright", multi_mods);
13517a1637f4SBram Moolenaar 
13527a1637f4SBram Moolenaar     // :tab
135302194d2bSBram Moolenaar     if (cmod->cmod_tab > 0)
13547a1637f4SBram Moolenaar 	result += add_cmd_modifier(buf, "tab", multi_mods);
13557a1637f4SBram Moolenaar     // :topleft
135602194d2bSBram Moolenaar     if (cmod->cmod_split & WSP_TOP)
13577a1637f4SBram Moolenaar 	result += add_cmd_modifier(buf, "topleft", multi_mods);
13587a1637f4SBram Moolenaar     // :vertical
135902194d2bSBram Moolenaar     if (cmod->cmod_split & WSP_VERT)
13607a1637f4SBram Moolenaar 	result += add_cmd_modifier(buf, "vertical", multi_mods);
13617a1637f4SBram Moolenaar     return result;
13627a1637f4SBram Moolenaar }
13637a1637f4SBram Moolenaar 
13647a1637f4SBram Moolenaar /*
136502194d2bSBram Moolenaar  * Generate text for the "cmod" command modifiers.
136602194d2bSBram Moolenaar  * If "buf" is NULL just return the length.
136702194d2bSBram Moolenaar  */
1368a360dbe3SBram Moolenaar     size_t
produce_cmdmods(char_u * buf,cmdmod_T * cmod,int quote)136902194d2bSBram Moolenaar produce_cmdmods(char_u *buf, cmdmod_T *cmod, int quote)
137002194d2bSBram Moolenaar {
1371a360dbe3SBram Moolenaar     size_t  result = 0;
137202194d2bSBram Moolenaar     int	    multi_mods = 0;
137302194d2bSBram Moolenaar     int	    i;
137402194d2bSBram Moolenaar     typedef struct {
137502194d2bSBram Moolenaar 	int flag;
137602194d2bSBram Moolenaar 	char *name;
137702194d2bSBram Moolenaar     } mod_entry_T;
137802194d2bSBram Moolenaar     static mod_entry_T mod_entries[] = {
137902194d2bSBram Moolenaar #ifdef FEAT_BROWSE_CMD
138002194d2bSBram Moolenaar 	{CMOD_BROWSE, "browse"},
138102194d2bSBram Moolenaar #endif
138202194d2bSBram Moolenaar #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
138302194d2bSBram Moolenaar 	{CMOD_CONFIRM, "confirm"},
138402194d2bSBram Moolenaar #endif
138502194d2bSBram Moolenaar 	{CMOD_HIDE, "hide"},
138602194d2bSBram Moolenaar 	{CMOD_KEEPALT, "keepalt"},
138702194d2bSBram Moolenaar 	{CMOD_KEEPJUMPS, "keepjumps"},
138802194d2bSBram Moolenaar 	{CMOD_KEEPMARKS, "keepmarks"},
138902194d2bSBram Moolenaar 	{CMOD_KEEPPATTERNS, "keeppatterns"},
139002194d2bSBram Moolenaar 	{CMOD_LOCKMARKS, "lockmarks"},
139102194d2bSBram Moolenaar 	{CMOD_NOSWAPFILE, "noswapfile"},
139202194d2bSBram Moolenaar 	{CMOD_UNSILENT, "unsilent"},
139302194d2bSBram Moolenaar 	{CMOD_NOAUTOCMD, "noautocmd"},
139402194d2bSBram Moolenaar #ifdef HAVE_SANDBOX
139502194d2bSBram Moolenaar 	{CMOD_SANDBOX, "sandbox"},
139602194d2bSBram Moolenaar #endif
139702194d2bSBram Moolenaar 	{0, NULL}
139802194d2bSBram Moolenaar     };
139902194d2bSBram Moolenaar 
140002194d2bSBram Moolenaar     result = quote ? 2 : 0;
140102194d2bSBram Moolenaar     if (buf != NULL)
140202194d2bSBram Moolenaar     {
140302194d2bSBram Moolenaar 	if (quote)
140402194d2bSBram Moolenaar 	    *buf++ = '"';
140502194d2bSBram Moolenaar 	*buf = '\0';
140602194d2bSBram Moolenaar     }
140702194d2bSBram Moolenaar 
140802194d2bSBram Moolenaar     // the modifiers that are simple flags
140902194d2bSBram Moolenaar     for (i = 0; mod_entries[i].name != NULL; ++i)
141002194d2bSBram Moolenaar 	if (cmod->cmod_flags & mod_entries[i].flag)
141102194d2bSBram Moolenaar 	    result += add_cmd_modifier(buf, mod_entries[i].name, &multi_mods);
141202194d2bSBram Moolenaar 
141302194d2bSBram Moolenaar     // :silent
141402194d2bSBram Moolenaar     if (cmod->cmod_flags & CMOD_SILENT)
141502194d2bSBram Moolenaar 	result += add_cmd_modifier(buf,
141602194d2bSBram Moolenaar 			(cmod->cmod_flags & CMOD_ERRSILENT) ? "silent!"
141702194d2bSBram Moolenaar 						      : "silent", &multi_mods);
141802194d2bSBram Moolenaar     // :verbose
141902194d2bSBram Moolenaar     if (p_verbose > 0)
142002194d2bSBram Moolenaar 	result += add_cmd_modifier(buf, "verbose", &multi_mods);
142102194d2bSBram Moolenaar     // flags from cmod->cmod_split
142202194d2bSBram Moolenaar     result += add_win_cmd_modifers(buf, cmod, &multi_mods);
142302194d2bSBram Moolenaar     if (quote && buf != NULL)
142402194d2bSBram Moolenaar     {
142502194d2bSBram Moolenaar 	buf += result - 2;
142602194d2bSBram Moolenaar 	*buf = '"';
142702194d2bSBram Moolenaar     }
142802194d2bSBram Moolenaar     return result;
142902194d2bSBram Moolenaar }
143002194d2bSBram Moolenaar 
143102194d2bSBram Moolenaar /*
1432ac9fb180SBram Moolenaar  * Check for a <> code in a user command.
1433ac9fb180SBram Moolenaar  * "code" points to the '<'.  "len" the length of the <> (inclusive).
1434ac9fb180SBram Moolenaar  * "buf" is where the result is to be added.
1435ac9fb180SBram Moolenaar  * "split_buf" points to a buffer used for splitting, caller should free it.
1436ac9fb180SBram Moolenaar  * "split_len" is the length of what "split_buf" contains.
1437ac9fb180SBram Moolenaar  * Returns the length of the replacement, which has been added to "buf".
1438ac9fb180SBram Moolenaar  * Returns -1 if there was no match, and only the "<" has been copied.
1439ac9fb180SBram Moolenaar  */
1440ac9fb180SBram Moolenaar     static size_t
uc_check_code(char_u * code,size_t len,char_u * buf,ucmd_T * cmd,exarg_T * eap,char_u ** split_buf,size_t * split_len)1441ac9fb180SBram Moolenaar uc_check_code(
1442ac9fb180SBram Moolenaar     char_u	*code,
1443ac9fb180SBram Moolenaar     size_t	len,
1444ac9fb180SBram Moolenaar     char_u	*buf,
1445ac9fb180SBram Moolenaar     ucmd_T	*cmd,		// the user command we're expanding
1446ac9fb180SBram Moolenaar     exarg_T	*eap,		// ex arguments
1447ac9fb180SBram Moolenaar     char_u	**split_buf,
1448ac9fb180SBram Moolenaar     size_t	*split_len)
1449ac9fb180SBram Moolenaar {
1450ac9fb180SBram Moolenaar     size_t	result = 0;
1451ac9fb180SBram Moolenaar     char_u	*p = code + 1;
1452ac9fb180SBram Moolenaar     size_t	l = len - 2;
1453ac9fb180SBram Moolenaar     int		quote = 0;
1454ac9fb180SBram Moolenaar     enum {
1455ac9fb180SBram Moolenaar 	ct_ARGS,
1456ac9fb180SBram Moolenaar 	ct_BANG,
1457ac9fb180SBram Moolenaar 	ct_COUNT,
1458ac9fb180SBram Moolenaar 	ct_LINE1,
1459ac9fb180SBram Moolenaar 	ct_LINE2,
1460ac9fb180SBram Moolenaar 	ct_RANGE,
1461ac9fb180SBram Moolenaar 	ct_MODS,
1462ac9fb180SBram Moolenaar 	ct_REGISTER,
1463ac9fb180SBram Moolenaar 	ct_LT,
1464ac9fb180SBram Moolenaar 	ct_NONE
1465ac9fb180SBram Moolenaar     } type = ct_NONE;
1466ac9fb180SBram Moolenaar 
1467ac9fb180SBram Moolenaar     if ((vim_strchr((char_u *)"qQfF", *p) != NULL) && p[1] == '-')
1468ac9fb180SBram Moolenaar     {
1469ac9fb180SBram Moolenaar 	quote = (*p == 'q' || *p == 'Q') ? 1 : 2;
1470ac9fb180SBram Moolenaar 	p += 2;
1471ac9fb180SBram Moolenaar 	l -= 2;
1472ac9fb180SBram Moolenaar     }
1473ac9fb180SBram Moolenaar 
1474ac9fb180SBram Moolenaar     ++l;
1475ac9fb180SBram Moolenaar     if (l <= 1)
1476ac9fb180SBram Moolenaar 	type = ct_NONE;
1477ac9fb180SBram Moolenaar     else if (STRNICMP(p, "args>", l) == 0)
1478ac9fb180SBram Moolenaar 	type = ct_ARGS;
1479ac9fb180SBram Moolenaar     else if (STRNICMP(p, "bang>", l) == 0)
1480ac9fb180SBram Moolenaar 	type = ct_BANG;
1481ac9fb180SBram Moolenaar     else if (STRNICMP(p, "count>", l) == 0)
1482ac9fb180SBram Moolenaar 	type = ct_COUNT;
1483ac9fb180SBram Moolenaar     else if (STRNICMP(p, "line1>", l) == 0)
1484ac9fb180SBram Moolenaar 	type = ct_LINE1;
1485ac9fb180SBram Moolenaar     else if (STRNICMP(p, "line2>", l) == 0)
1486ac9fb180SBram Moolenaar 	type = ct_LINE2;
1487ac9fb180SBram Moolenaar     else if (STRNICMP(p, "range>", l) == 0)
1488ac9fb180SBram Moolenaar 	type = ct_RANGE;
1489ac9fb180SBram Moolenaar     else if (STRNICMP(p, "lt>", l) == 0)
1490ac9fb180SBram Moolenaar 	type = ct_LT;
1491ac9fb180SBram Moolenaar     else if (STRNICMP(p, "reg>", l) == 0 || STRNICMP(p, "register>", l) == 0)
1492ac9fb180SBram Moolenaar 	type = ct_REGISTER;
1493ac9fb180SBram Moolenaar     else if (STRNICMP(p, "mods>", l) == 0)
1494ac9fb180SBram Moolenaar 	type = ct_MODS;
1495ac9fb180SBram Moolenaar 
1496ac9fb180SBram Moolenaar     switch (type)
1497ac9fb180SBram Moolenaar     {
1498ac9fb180SBram Moolenaar     case ct_ARGS:
1499ac9fb180SBram Moolenaar 	// Simple case first
1500ac9fb180SBram Moolenaar 	if (*eap->arg == NUL)
1501ac9fb180SBram Moolenaar 	{
1502ac9fb180SBram Moolenaar 	    if (quote == 1)
1503ac9fb180SBram Moolenaar 	    {
1504ac9fb180SBram Moolenaar 		result = 2;
1505ac9fb180SBram Moolenaar 		if (buf != NULL)
1506ac9fb180SBram Moolenaar 		    STRCPY(buf, "''");
1507ac9fb180SBram Moolenaar 	    }
1508ac9fb180SBram Moolenaar 	    else
1509ac9fb180SBram Moolenaar 		result = 0;
1510ac9fb180SBram Moolenaar 	    break;
1511ac9fb180SBram Moolenaar 	}
1512ac9fb180SBram Moolenaar 
1513ac9fb180SBram Moolenaar 	// When specified there is a single argument don't split it.
1514ac9fb180SBram Moolenaar 	// Works for ":Cmd %" when % is "a b c".
15158071cb2cSBram Moolenaar 	if ((eap->argt & EX_NOSPC) && quote == 2)
1516ac9fb180SBram Moolenaar 	    quote = 1;
1517ac9fb180SBram Moolenaar 
1518ac9fb180SBram Moolenaar 	switch (quote)
1519ac9fb180SBram Moolenaar 	{
1520ac9fb180SBram Moolenaar 	case 0: // No quoting, no splitting
1521ac9fb180SBram Moolenaar 	    result = STRLEN(eap->arg);
1522ac9fb180SBram Moolenaar 	    if (buf != NULL)
1523ac9fb180SBram Moolenaar 		STRCPY(buf, eap->arg);
1524ac9fb180SBram Moolenaar 	    break;
1525ac9fb180SBram Moolenaar 	case 1: // Quote, but don't split
1526ac9fb180SBram Moolenaar 	    result = STRLEN(eap->arg) + 2;
1527ac9fb180SBram Moolenaar 	    for (p = eap->arg; *p; ++p)
1528ac9fb180SBram Moolenaar 	    {
1529ac9fb180SBram Moolenaar 		if (enc_dbcs != 0 && (*mb_ptr2len)(p) == 2)
1530ac9fb180SBram Moolenaar 		    // DBCS can contain \ in a trail byte, skip the
1531ac9fb180SBram Moolenaar 		    // double-byte character.
1532ac9fb180SBram Moolenaar 		    ++p;
1533ac9fb180SBram Moolenaar 		else
1534ac9fb180SBram Moolenaar 		     if (*p == '\\' || *p == '"')
1535ac9fb180SBram Moolenaar 		    ++result;
1536ac9fb180SBram Moolenaar 	    }
1537ac9fb180SBram Moolenaar 
1538ac9fb180SBram Moolenaar 	    if (buf != NULL)
1539ac9fb180SBram Moolenaar 	    {
1540ac9fb180SBram Moolenaar 		*buf++ = '"';
1541ac9fb180SBram Moolenaar 		for (p = eap->arg; *p; ++p)
1542ac9fb180SBram Moolenaar 		{
1543ac9fb180SBram Moolenaar 		    if (enc_dbcs != 0 && (*mb_ptr2len)(p) == 2)
1544ac9fb180SBram Moolenaar 			// DBCS can contain \ in a trail byte, copy the
1545ac9fb180SBram Moolenaar 			// double-byte character to avoid escaping.
1546ac9fb180SBram Moolenaar 			*buf++ = *p++;
1547ac9fb180SBram Moolenaar 		    else
1548ac9fb180SBram Moolenaar 			 if (*p == '\\' || *p == '"')
1549ac9fb180SBram Moolenaar 			*buf++ = '\\';
1550ac9fb180SBram Moolenaar 		    *buf++ = *p;
1551ac9fb180SBram Moolenaar 		}
1552ac9fb180SBram Moolenaar 		*buf = '"';
1553ac9fb180SBram Moolenaar 	    }
1554ac9fb180SBram Moolenaar 
1555ac9fb180SBram Moolenaar 	    break;
1556ac9fb180SBram Moolenaar 	case 2: // Quote and split (<f-args>)
1557ac9fb180SBram Moolenaar 	    // This is hard, so only do it once, and cache the result
1558ac9fb180SBram Moolenaar 	    if (*split_buf == NULL)
1559ac9fb180SBram Moolenaar 		*split_buf = uc_split_args(eap->arg, split_len);
1560ac9fb180SBram Moolenaar 
1561ac9fb180SBram Moolenaar 	    result = *split_len;
1562ac9fb180SBram Moolenaar 	    if (buf != NULL && result != 0)
1563ac9fb180SBram Moolenaar 		STRCPY(buf, *split_buf);
1564ac9fb180SBram Moolenaar 
1565ac9fb180SBram Moolenaar 	    break;
1566ac9fb180SBram Moolenaar 	}
1567ac9fb180SBram Moolenaar 	break;
1568ac9fb180SBram Moolenaar 
1569ac9fb180SBram Moolenaar     case ct_BANG:
1570ac9fb180SBram Moolenaar 	result = eap->forceit ? 1 : 0;
1571ac9fb180SBram Moolenaar 	if (quote)
1572ac9fb180SBram Moolenaar 	    result += 2;
1573ac9fb180SBram Moolenaar 	if (buf != NULL)
1574ac9fb180SBram Moolenaar 	{
1575ac9fb180SBram Moolenaar 	    if (quote)
1576ac9fb180SBram Moolenaar 		*buf++ = '"';
1577ac9fb180SBram Moolenaar 	    if (eap->forceit)
1578ac9fb180SBram Moolenaar 		*buf++ = '!';
1579ac9fb180SBram Moolenaar 	    if (quote)
1580ac9fb180SBram Moolenaar 		*buf = '"';
1581ac9fb180SBram Moolenaar 	}
1582ac9fb180SBram Moolenaar 	break;
1583ac9fb180SBram Moolenaar 
1584ac9fb180SBram Moolenaar     case ct_LINE1:
1585ac9fb180SBram Moolenaar     case ct_LINE2:
1586ac9fb180SBram Moolenaar     case ct_RANGE:
1587ac9fb180SBram Moolenaar     case ct_COUNT:
1588ac9fb180SBram Moolenaar     {
1589ac9fb180SBram Moolenaar 	char num_buf[20];
1590ac9fb180SBram Moolenaar 	long num = (type == ct_LINE1) ? eap->line1 :
1591ac9fb180SBram Moolenaar 		   (type == ct_LINE2) ? eap->line2 :
1592ac9fb180SBram Moolenaar 		   (type == ct_RANGE) ? eap->addr_count :
1593ac9fb180SBram Moolenaar 		   (eap->addr_count > 0) ? eap->line2 : cmd->uc_def;
1594ac9fb180SBram Moolenaar 	size_t num_len;
1595ac9fb180SBram Moolenaar 
1596ac9fb180SBram Moolenaar 	sprintf(num_buf, "%ld", num);
1597ac9fb180SBram Moolenaar 	num_len = STRLEN(num_buf);
1598ac9fb180SBram Moolenaar 	result = num_len;
1599ac9fb180SBram Moolenaar 
1600ac9fb180SBram Moolenaar 	if (quote)
1601ac9fb180SBram Moolenaar 	    result += 2;
1602ac9fb180SBram Moolenaar 
1603ac9fb180SBram Moolenaar 	if (buf != NULL)
1604ac9fb180SBram Moolenaar 	{
1605ac9fb180SBram Moolenaar 	    if (quote)
1606ac9fb180SBram Moolenaar 		*buf++ = '"';
1607ac9fb180SBram Moolenaar 	    STRCPY(buf, num_buf);
1608ac9fb180SBram Moolenaar 	    buf += num_len;
1609ac9fb180SBram Moolenaar 	    if (quote)
1610ac9fb180SBram Moolenaar 		*buf = '"';
1611ac9fb180SBram Moolenaar 	}
1612ac9fb180SBram Moolenaar 
1613ac9fb180SBram Moolenaar 	break;
1614ac9fb180SBram Moolenaar     }
1615ac9fb180SBram Moolenaar 
1616ac9fb180SBram Moolenaar     case ct_MODS:
1617ac9fb180SBram Moolenaar     {
161802194d2bSBram Moolenaar 	result = produce_cmdmods(buf, &cmdmod, quote);
1619ac9fb180SBram Moolenaar 	break;
1620ac9fb180SBram Moolenaar     }
1621ac9fb180SBram Moolenaar 
1622ac9fb180SBram Moolenaar     case ct_REGISTER:
1623ac9fb180SBram Moolenaar 	result = eap->regname ? 1 : 0;
1624ac9fb180SBram Moolenaar 	if (quote)
1625ac9fb180SBram Moolenaar 	    result += 2;
1626ac9fb180SBram Moolenaar 	if (buf != NULL)
1627ac9fb180SBram Moolenaar 	{
1628ac9fb180SBram Moolenaar 	    if (quote)
1629ac9fb180SBram Moolenaar 		*buf++ = '\'';
1630ac9fb180SBram Moolenaar 	    if (eap->regname)
1631ac9fb180SBram Moolenaar 		*buf++ = eap->regname;
1632ac9fb180SBram Moolenaar 	    if (quote)
1633ac9fb180SBram Moolenaar 		*buf = '\'';
1634ac9fb180SBram Moolenaar 	}
1635ac9fb180SBram Moolenaar 	break;
1636ac9fb180SBram Moolenaar 
1637ac9fb180SBram Moolenaar     case ct_LT:
1638ac9fb180SBram Moolenaar 	result = 1;
1639ac9fb180SBram Moolenaar 	if (buf != NULL)
1640ac9fb180SBram Moolenaar 	    *buf = '<';
1641ac9fb180SBram Moolenaar 	break;
1642ac9fb180SBram Moolenaar 
1643ac9fb180SBram Moolenaar     default:
1644ac9fb180SBram Moolenaar 	// Not recognized: just copy the '<' and return -1.
1645ac9fb180SBram Moolenaar 	result = (size_t)-1;
1646ac9fb180SBram Moolenaar 	if (buf != NULL)
1647ac9fb180SBram Moolenaar 	    *buf = '<';
1648ac9fb180SBram Moolenaar 	break;
1649ac9fb180SBram Moolenaar     }
1650ac9fb180SBram Moolenaar 
1651ac9fb180SBram Moolenaar     return result;
1652ac9fb180SBram Moolenaar }
1653ac9fb180SBram Moolenaar 
1654ac9fb180SBram Moolenaar /*
1655ac9fb180SBram Moolenaar  * Execute a user defined command.
1656ac9fb180SBram Moolenaar  */
1657ac9fb180SBram Moolenaar     void
do_ucmd(exarg_T * eap)1658ac9fb180SBram Moolenaar do_ucmd(exarg_T *eap)
1659ac9fb180SBram Moolenaar {
1660ac9fb180SBram Moolenaar     char_u	*buf;
1661ac9fb180SBram Moolenaar     char_u	*p;
1662ac9fb180SBram Moolenaar     char_u	*q;
1663ac9fb180SBram Moolenaar 
1664ac9fb180SBram Moolenaar     char_u	*start;
1665ac9fb180SBram Moolenaar     char_u	*end = NULL;
1666ac9fb180SBram Moolenaar     char_u	*ksp;
1667ac9fb180SBram Moolenaar     size_t	len, totlen;
1668ac9fb180SBram Moolenaar 
1669ac9fb180SBram Moolenaar     size_t	split_len = 0;
1670ac9fb180SBram Moolenaar     char_u	*split_buf = NULL;
1671ac9fb180SBram Moolenaar     ucmd_T	*cmd;
1672ac9fb180SBram Moolenaar     sctx_T	save_current_sctx = current_sctx;
1673ac9fb180SBram Moolenaar 
1674ac9fb180SBram Moolenaar     if (eap->cmdidx == CMD_USER)
1675ac9fb180SBram Moolenaar 	cmd = USER_CMD(eap->useridx);
1676ac9fb180SBram Moolenaar     else
1677ac9fb180SBram Moolenaar 	cmd = USER_CMD_GA(&curbuf->b_ucmds, eap->useridx);
1678ac9fb180SBram Moolenaar 
1679ac9fb180SBram Moolenaar     /*
1680ac9fb180SBram Moolenaar      * Replace <> in the command by the arguments.
1681ac9fb180SBram Moolenaar      * First round: "buf" is NULL, compute length, allocate "buf".
1682ac9fb180SBram Moolenaar      * Second round: copy result into "buf".
1683ac9fb180SBram Moolenaar      */
1684ac9fb180SBram Moolenaar     buf = NULL;
1685ac9fb180SBram Moolenaar     for (;;)
1686ac9fb180SBram Moolenaar     {
1687ac9fb180SBram Moolenaar 	p = cmd->uc_rep;    // source
1688ac9fb180SBram Moolenaar 	q = buf;	    // destination
1689ac9fb180SBram Moolenaar 	totlen = 0;
1690ac9fb180SBram Moolenaar 
1691ac9fb180SBram Moolenaar 	for (;;)
1692ac9fb180SBram Moolenaar 	{
1693ac9fb180SBram Moolenaar 	    start = vim_strchr(p, '<');
1694ac9fb180SBram Moolenaar 	    if (start != NULL)
1695ac9fb180SBram Moolenaar 		end = vim_strchr(start + 1, '>');
1696ac9fb180SBram Moolenaar 	    if (buf != NULL)
1697ac9fb180SBram Moolenaar 	    {
1698ac9fb180SBram Moolenaar 		for (ksp = p; *ksp != NUL && *ksp != K_SPECIAL; ++ksp)
1699ac9fb180SBram Moolenaar 		    ;
1700ac9fb180SBram Moolenaar 		if (*ksp == K_SPECIAL
1701ac9fb180SBram Moolenaar 			&& (start == NULL || ksp < start || end == NULL)
1702ac9fb180SBram Moolenaar 			&& ((ksp[1] == KS_SPECIAL && ksp[2] == KE_FILLER)
1703ac9fb180SBram Moolenaar # ifdef FEAT_GUI
1704ac9fb180SBram Moolenaar 			    || (ksp[1] == KS_EXTRA && ksp[2] == (int)KE_CSI)
1705ac9fb180SBram Moolenaar # endif
1706ac9fb180SBram Moolenaar 			    ))
1707ac9fb180SBram Moolenaar 		{
1708ac9fb180SBram Moolenaar 		    // K_SPECIAL has been put in the buffer as K_SPECIAL
1709ac9fb180SBram Moolenaar 		    // KS_SPECIAL KE_FILLER, like for mappings, but
1710ac9fb180SBram Moolenaar 		    // do_cmdline() doesn't handle that, so convert it back.
1711ac9fb180SBram Moolenaar 		    // Also change K_SPECIAL KS_EXTRA KE_CSI into CSI.
1712ac9fb180SBram Moolenaar 		    len = ksp - p;
1713ac9fb180SBram Moolenaar 		    if (len > 0)
1714ac9fb180SBram Moolenaar 		    {
1715ac9fb180SBram Moolenaar 			mch_memmove(q, p, len);
1716ac9fb180SBram Moolenaar 			q += len;
1717ac9fb180SBram Moolenaar 		    }
1718ac9fb180SBram Moolenaar 		    *q++ = ksp[1] == KS_SPECIAL ? K_SPECIAL : CSI;
1719ac9fb180SBram Moolenaar 		    p = ksp + 3;
1720ac9fb180SBram Moolenaar 		    continue;
1721ac9fb180SBram Moolenaar 		}
1722ac9fb180SBram Moolenaar 	    }
1723ac9fb180SBram Moolenaar 
1724ac9fb180SBram Moolenaar 	    // break if no <item> is found
1725ac9fb180SBram Moolenaar 	    if (start == NULL || end == NULL)
1726ac9fb180SBram Moolenaar 		break;
1727ac9fb180SBram Moolenaar 
1728ac9fb180SBram Moolenaar 	    // Include the '>'
1729ac9fb180SBram Moolenaar 	    ++end;
1730ac9fb180SBram Moolenaar 
1731ac9fb180SBram Moolenaar 	    // Take everything up to the '<'
1732ac9fb180SBram Moolenaar 	    len = start - p;
1733ac9fb180SBram Moolenaar 	    if (buf == NULL)
1734ac9fb180SBram Moolenaar 		totlen += len;
1735ac9fb180SBram Moolenaar 	    else
1736ac9fb180SBram Moolenaar 	    {
1737ac9fb180SBram Moolenaar 		mch_memmove(q, p, len);
1738ac9fb180SBram Moolenaar 		q += len;
1739ac9fb180SBram Moolenaar 	    }
1740ac9fb180SBram Moolenaar 
1741ac9fb180SBram Moolenaar 	    len = uc_check_code(start, end - start, q, cmd, eap,
1742ac9fb180SBram Moolenaar 			     &split_buf, &split_len);
1743ac9fb180SBram Moolenaar 	    if (len == (size_t)-1)
1744ac9fb180SBram Moolenaar 	    {
1745ac9fb180SBram Moolenaar 		// no match, continue after '<'
1746ac9fb180SBram Moolenaar 		p = start + 1;
1747ac9fb180SBram Moolenaar 		len = 1;
1748ac9fb180SBram Moolenaar 	    }
1749ac9fb180SBram Moolenaar 	    else
1750ac9fb180SBram Moolenaar 		p = end;
1751ac9fb180SBram Moolenaar 	    if (buf == NULL)
1752ac9fb180SBram Moolenaar 		totlen += len;
1753ac9fb180SBram Moolenaar 	    else
1754ac9fb180SBram Moolenaar 		q += len;
1755ac9fb180SBram Moolenaar 	}
1756ac9fb180SBram Moolenaar 	if (buf != NULL)	    // second time here, finished
1757ac9fb180SBram Moolenaar 	{
1758ac9fb180SBram Moolenaar 	    STRCPY(q, p);
1759ac9fb180SBram Moolenaar 	    break;
1760ac9fb180SBram Moolenaar 	}
1761ac9fb180SBram Moolenaar 
1762ac9fb180SBram Moolenaar 	totlen += STRLEN(p);	    // Add on the trailing characters
1763964b3746SBram Moolenaar 	buf = alloc(totlen + 1);
1764ac9fb180SBram Moolenaar 	if (buf == NULL)
1765ac9fb180SBram Moolenaar 	{
1766ac9fb180SBram Moolenaar 	    vim_free(split_buf);
1767ac9fb180SBram Moolenaar 	    return;
1768ac9fb180SBram Moolenaar 	}
1769ac9fb180SBram Moolenaar     }
1770ac9fb180SBram Moolenaar 
177158ef8a31SBram Moolenaar     if ((cmd->uc_argt & EX_KEEPSCRIPT) == 0)
177258ef8a31SBram Moolenaar     {
17739b8d6226SBram Moolenaar 	current_sctx.sc_version = cmd->uc_script_ctx.sc_version;
1774ac9fb180SBram Moolenaar #ifdef FEAT_EVAL
1775ac9fb180SBram Moolenaar 	current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid;
1776ac9fb180SBram Moolenaar #endif
177758ef8a31SBram Moolenaar     }
1778ac9fb180SBram Moolenaar     (void)do_cmdline(buf, eap->getline, eap->cookie,
1779ac9fb180SBram Moolenaar 				   DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED);
178058ef8a31SBram Moolenaar     if ((cmd->uc_argt & EX_KEEPSCRIPT) == 0)
1781ac9fb180SBram Moolenaar 	current_sctx = save_current_sctx;
1782ac9fb180SBram Moolenaar     vim_free(buf);
1783ac9fb180SBram Moolenaar     vim_free(split_buf);
1784ac9fb180SBram Moolenaar }
1785