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