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