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