xref: /vim-8.2.3635/src/usercmd.c (revision 26190b27)
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		res = -1;
1166     garray_T	*gap;
1167     char_u	*arg = eap->arg;
1168     int		buffer_only = FALSE;
1169 
1170     if (STRNCMP(arg, "-buffer", 7) == 0 && VIM_ISWHITE(arg[7]))
1171     {
1172 	buffer_only = TRUE;
1173 	arg = skipwhite(arg + 7);
1174     }
1175 
1176     gap = &curbuf->b_ucmds;
1177     for (;;)
1178     {
1179 	for (i = 0; i < gap->ga_len; ++i)
1180 	{
1181 	    cmd = USER_CMD_GA(gap, i);
1182 	    res = STRCMP(arg, cmd->uc_name);
1183 	    if (res <= 0)
1184 		break;
1185 	}
1186 	if (gap == &ucmds || res == 0 || buffer_only)
1187 	    break;
1188 	gap = &ucmds;
1189     }
1190 
1191     if (res != 0)
1192     {
1193 	semsg(_(buffer_only
1194 		    ? e_no_such_user_defined_command_in_current_buffer_str
1195 		    : e_no_such_user_defined_command_str), arg);
1196 	return;
1197     }
1198 
1199     vim_free(cmd->uc_name);
1200     vim_free(cmd->uc_rep);
1201 # if defined(FEAT_EVAL)
1202     vim_free(cmd->uc_compl_arg);
1203 # endif
1204 
1205     --gap->ga_len;
1206 
1207     if (i < gap->ga_len)
1208 	mch_memmove(cmd, cmd + 1, (gap->ga_len - i) * sizeof(ucmd_T));
1209 }
1210 
1211 /*
1212  * Split and quote args for <f-args>.
1213  */
1214     static char_u *
1215 uc_split_args(char_u *arg, size_t *lenp)
1216 {
1217     char_u *buf;
1218     char_u *p;
1219     char_u *q;
1220     int len;
1221 
1222     // Precalculate length
1223     p = arg;
1224     len = 2; // Initial and final quotes
1225 
1226     while (*p)
1227     {
1228 	if (p[0] == '\\' && p[1] == '\\')
1229 	{
1230 	    len += 2;
1231 	    p += 2;
1232 	}
1233 	else if (p[0] == '\\' && VIM_ISWHITE(p[1]))
1234 	{
1235 	    len += 1;
1236 	    p += 2;
1237 	}
1238 	else if (*p == '\\' || *p == '"')
1239 	{
1240 	    len += 2;
1241 	    p += 1;
1242 	}
1243 	else if (VIM_ISWHITE(*p))
1244 	{
1245 	    p = skipwhite(p);
1246 	    if (*p == NUL)
1247 		break;
1248 	    len += 4; // ", "
1249 	}
1250 	else
1251 	{
1252 	    int charlen = (*mb_ptr2len)(p);
1253 
1254 	    len += charlen;
1255 	    p += charlen;
1256 	}
1257     }
1258 
1259     buf = alloc(len + 1);
1260     if (buf == NULL)
1261     {
1262 	*lenp = 0;
1263 	return buf;
1264     }
1265 
1266     p = arg;
1267     q = buf;
1268     *q++ = '"';
1269     while (*p)
1270     {
1271 	if (p[0] == '\\' && p[1] == '\\')
1272 	{
1273 	    *q++ = '\\';
1274 	    *q++ = '\\';
1275 	    p += 2;
1276 	}
1277 	else if (p[0] == '\\' && VIM_ISWHITE(p[1]))
1278 	{
1279 	    *q++ = p[1];
1280 	    p += 2;
1281 	}
1282 	else if (*p == '\\' || *p == '"')
1283 	{
1284 	    *q++ = '\\';
1285 	    *q++ = *p++;
1286 	}
1287 	else if (VIM_ISWHITE(*p))
1288 	{
1289 	    p = skipwhite(p);
1290 	    if (*p == NUL)
1291 		break;
1292 	    *q++ = '"';
1293 	    *q++ = ',';
1294 	    *q++ = ' ';
1295 	    *q++ = '"';
1296 	}
1297 	else
1298 	{
1299 	    MB_COPY_CHAR(p, q);
1300 	}
1301     }
1302     *q++ = '"';
1303     *q = 0;
1304 
1305     *lenp = len;
1306     return buf;
1307 }
1308 
1309     static size_t
1310 add_cmd_modifier(char_u *buf, char *mod_str, int *multi_mods)
1311 {
1312     size_t result;
1313 
1314     result = STRLEN(mod_str);
1315     if (*multi_mods)
1316 	result += 1;
1317     if (buf != NULL)
1318     {
1319 	if (*multi_mods)
1320 	    STRCAT(buf, " ");
1321 	STRCAT(buf, mod_str);
1322     }
1323 
1324     *multi_mods = 1;
1325 
1326     return result;
1327 }
1328 
1329 /*
1330  * Add modifiers from "cmod->cmod_split" to "buf".  Set "multi_mods" when one
1331  * was added.  Return the number of bytes added.
1332  */
1333     size_t
1334 add_win_cmd_modifers(char_u *buf, cmdmod_T *cmod, int *multi_mods)
1335 {
1336     size_t result = 0;
1337 
1338     // :aboveleft and :leftabove
1339     if (cmod->cmod_split & WSP_ABOVE)
1340 	result += add_cmd_modifier(buf, "aboveleft", multi_mods);
1341     // :belowright and :rightbelow
1342     if (cmod->cmod_split & WSP_BELOW)
1343 	result += add_cmd_modifier(buf, "belowright", multi_mods);
1344     // :botright
1345     if (cmod->cmod_split & WSP_BOT)
1346 	result += add_cmd_modifier(buf, "botright", multi_mods);
1347 
1348     // :tab
1349     if (cmod->cmod_tab > 0)
1350 	result += add_cmd_modifier(buf, "tab", multi_mods);
1351     // :topleft
1352     if (cmod->cmod_split & WSP_TOP)
1353 	result += add_cmd_modifier(buf, "topleft", multi_mods);
1354     // :vertical
1355     if (cmod->cmod_split & WSP_VERT)
1356 	result += add_cmd_modifier(buf, "vertical", multi_mods);
1357     return result;
1358 }
1359 
1360 /*
1361  * Generate text for the "cmod" command modifiers.
1362  * If "buf" is NULL just return the length.
1363  */
1364     size_t
1365 produce_cmdmods(char_u *buf, cmdmod_T *cmod, int quote)
1366 {
1367     size_t  result = 0;
1368     int	    multi_mods = 0;
1369     int	    i;
1370     typedef struct {
1371 	int flag;
1372 	char *name;
1373     } mod_entry_T;
1374     static mod_entry_T mod_entries[] = {
1375 #ifdef FEAT_BROWSE_CMD
1376 	{CMOD_BROWSE, "browse"},
1377 #endif
1378 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
1379 	{CMOD_CONFIRM, "confirm"},
1380 #endif
1381 	{CMOD_HIDE, "hide"},
1382 	{CMOD_KEEPALT, "keepalt"},
1383 	{CMOD_KEEPJUMPS, "keepjumps"},
1384 	{CMOD_KEEPMARKS, "keepmarks"},
1385 	{CMOD_KEEPPATTERNS, "keeppatterns"},
1386 	{CMOD_LOCKMARKS, "lockmarks"},
1387 	{CMOD_NOSWAPFILE, "noswapfile"},
1388 	{CMOD_UNSILENT, "unsilent"},
1389 	{CMOD_NOAUTOCMD, "noautocmd"},
1390 #ifdef HAVE_SANDBOX
1391 	{CMOD_SANDBOX, "sandbox"},
1392 #endif
1393 	{0, NULL}
1394     };
1395 
1396     result = quote ? 2 : 0;
1397     if (buf != NULL)
1398     {
1399 	if (quote)
1400 	    *buf++ = '"';
1401 	*buf = '\0';
1402     }
1403 
1404     // the modifiers that are simple flags
1405     for (i = 0; mod_entries[i].name != NULL; ++i)
1406 	if (cmod->cmod_flags & mod_entries[i].flag)
1407 	    result += add_cmd_modifier(buf, mod_entries[i].name, &multi_mods);
1408 
1409     // :silent
1410     if (cmod->cmod_flags & CMOD_SILENT)
1411 	result += add_cmd_modifier(buf,
1412 			(cmod->cmod_flags & CMOD_ERRSILENT) ? "silent!"
1413 						      : "silent", &multi_mods);
1414     // :verbose
1415     if (p_verbose > 0)
1416 	result += add_cmd_modifier(buf, "verbose", &multi_mods);
1417     // flags from cmod->cmod_split
1418     result += add_win_cmd_modifers(buf, cmod, &multi_mods);
1419     if (quote && buf != NULL)
1420     {
1421 	buf += result - 2;
1422 	*buf = '"';
1423     }
1424     return result;
1425 }
1426 
1427 /*
1428  * Check for a <> code in a user command.
1429  * "code" points to the '<'.  "len" the length of the <> (inclusive).
1430  * "buf" is where the result is to be added.
1431  * "split_buf" points to a buffer used for splitting, caller should free it.
1432  * "split_len" is the length of what "split_buf" contains.
1433  * Returns the length of the replacement, which has been added to "buf".
1434  * Returns -1 if there was no match, and only the "<" has been copied.
1435  */
1436     static size_t
1437 uc_check_code(
1438     char_u	*code,
1439     size_t	len,
1440     char_u	*buf,
1441     ucmd_T	*cmd,		// the user command we're expanding
1442     exarg_T	*eap,		// ex arguments
1443     char_u	**split_buf,
1444     size_t	*split_len)
1445 {
1446     size_t	result = 0;
1447     char_u	*p = code + 1;
1448     size_t	l = len - 2;
1449     int		quote = 0;
1450     enum {
1451 	ct_ARGS,
1452 	ct_BANG,
1453 	ct_COUNT,
1454 	ct_LINE1,
1455 	ct_LINE2,
1456 	ct_RANGE,
1457 	ct_MODS,
1458 	ct_REGISTER,
1459 	ct_LT,
1460 	ct_NONE
1461     } type = ct_NONE;
1462 
1463     if ((vim_strchr((char_u *)"qQfF", *p) != NULL) && p[1] == '-')
1464     {
1465 	quote = (*p == 'q' || *p == 'Q') ? 1 : 2;
1466 	p += 2;
1467 	l -= 2;
1468     }
1469 
1470     ++l;
1471     if (l <= 1)
1472 	type = ct_NONE;
1473     else if (STRNICMP(p, "args>", l) == 0)
1474 	type = ct_ARGS;
1475     else if (STRNICMP(p, "bang>", l) == 0)
1476 	type = ct_BANG;
1477     else if (STRNICMP(p, "count>", l) == 0)
1478 	type = ct_COUNT;
1479     else if (STRNICMP(p, "line1>", l) == 0)
1480 	type = ct_LINE1;
1481     else if (STRNICMP(p, "line2>", l) == 0)
1482 	type = ct_LINE2;
1483     else if (STRNICMP(p, "range>", l) == 0)
1484 	type = ct_RANGE;
1485     else if (STRNICMP(p, "lt>", l) == 0)
1486 	type = ct_LT;
1487     else if (STRNICMP(p, "reg>", l) == 0 || STRNICMP(p, "register>", l) == 0)
1488 	type = ct_REGISTER;
1489     else if (STRNICMP(p, "mods>", l) == 0)
1490 	type = ct_MODS;
1491 
1492     switch (type)
1493     {
1494     case ct_ARGS:
1495 	// Simple case first
1496 	if (*eap->arg == NUL)
1497 	{
1498 	    if (quote == 1)
1499 	    {
1500 		result = 2;
1501 		if (buf != NULL)
1502 		    STRCPY(buf, "''");
1503 	    }
1504 	    else
1505 		result = 0;
1506 	    break;
1507 	}
1508 
1509 	// When specified there is a single argument don't split it.
1510 	// Works for ":Cmd %" when % is "a b c".
1511 	if ((eap->argt & EX_NOSPC) && quote == 2)
1512 	    quote = 1;
1513 
1514 	switch (quote)
1515 	{
1516 	case 0: // No quoting, no splitting
1517 	    result = STRLEN(eap->arg);
1518 	    if (buf != NULL)
1519 		STRCPY(buf, eap->arg);
1520 	    break;
1521 	case 1: // Quote, but don't split
1522 	    result = STRLEN(eap->arg) + 2;
1523 	    for (p = eap->arg; *p; ++p)
1524 	    {
1525 		if (enc_dbcs != 0 && (*mb_ptr2len)(p) == 2)
1526 		    // DBCS can contain \ in a trail byte, skip the
1527 		    // double-byte character.
1528 		    ++p;
1529 		else
1530 		     if (*p == '\\' || *p == '"')
1531 		    ++result;
1532 	    }
1533 
1534 	    if (buf != NULL)
1535 	    {
1536 		*buf++ = '"';
1537 		for (p = eap->arg; *p; ++p)
1538 		{
1539 		    if (enc_dbcs != 0 && (*mb_ptr2len)(p) == 2)
1540 			// DBCS can contain \ in a trail byte, copy the
1541 			// double-byte character to avoid escaping.
1542 			*buf++ = *p++;
1543 		    else
1544 			 if (*p == '\\' || *p == '"')
1545 			*buf++ = '\\';
1546 		    *buf++ = *p;
1547 		}
1548 		*buf = '"';
1549 	    }
1550 
1551 	    break;
1552 	case 2: // Quote and split (<f-args>)
1553 	    // This is hard, so only do it once, and cache the result
1554 	    if (*split_buf == NULL)
1555 		*split_buf = uc_split_args(eap->arg, split_len);
1556 
1557 	    result = *split_len;
1558 	    if (buf != NULL && result != 0)
1559 		STRCPY(buf, *split_buf);
1560 
1561 	    break;
1562 	}
1563 	break;
1564 
1565     case ct_BANG:
1566 	result = eap->forceit ? 1 : 0;
1567 	if (quote)
1568 	    result += 2;
1569 	if (buf != NULL)
1570 	{
1571 	    if (quote)
1572 		*buf++ = '"';
1573 	    if (eap->forceit)
1574 		*buf++ = '!';
1575 	    if (quote)
1576 		*buf = '"';
1577 	}
1578 	break;
1579 
1580     case ct_LINE1:
1581     case ct_LINE2:
1582     case ct_RANGE:
1583     case ct_COUNT:
1584     {
1585 	char num_buf[20];
1586 	long num = (type == ct_LINE1) ? eap->line1 :
1587 		   (type == ct_LINE2) ? eap->line2 :
1588 		   (type == ct_RANGE) ? eap->addr_count :
1589 		   (eap->addr_count > 0) ? eap->line2 : cmd->uc_def;
1590 	size_t num_len;
1591 
1592 	sprintf(num_buf, "%ld", num);
1593 	num_len = STRLEN(num_buf);
1594 	result = num_len;
1595 
1596 	if (quote)
1597 	    result += 2;
1598 
1599 	if (buf != NULL)
1600 	{
1601 	    if (quote)
1602 		*buf++ = '"';
1603 	    STRCPY(buf, num_buf);
1604 	    buf += num_len;
1605 	    if (quote)
1606 		*buf = '"';
1607 	}
1608 
1609 	break;
1610     }
1611 
1612     case ct_MODS:
1613     {
1614 	result = produce_cmdmods(buf, &cmdmod, quote);
1615 	break;
1616     }
1617 
1618     case ct_REGISTER:
1619 	result = eap->regname ? 1 : 0;
1620 	if (quote)
1621 	    result += 2;
1622 	if (buf != NULL)
1623 	{
1624 	    if (quote)
1625 		*buf++ = '\'';
1626 	    if (eap->regname)
1627 		*buf++ = eap->regname;
1628 	    if (quote)
1629 		*buf = '\'';
1630 	}
1631 	break;
1632 
1633     case ct_LT:
1634 	result = 1;
1635 	if (buf != NULL)
1636 	    *buf = '<';
1637 	break;
1638 
1639     default:
1640 	// Not recognized: just copy the '<' and return -1.
1641 	result = (size_t)-1;
1642 	if (buf != NULL)
1643 	    *buf = '<';
1644 	break;
1645     }
1646 
1647     return result;
1648 }
1649 
1650 /*
1651  * Execute a user defined command.
1652  */
1653     void
1654 do_ucmd(exarg_T *eap)
1655 {
1656     char_u	*buf;
1657     char_u	*p;
1658     char_u	*q;
1659 
1660     char_u	*start;
1661     char_u	*end = NULL;
1662     char_u	*ksp;
1663     size_t	len, totlen;
1664 
1665     size_t	split_len = 0;
1666     char_u	*split_buf = NULL;
1667     ucmd_T	*cmd;
1668     sctx_T	save_current_sctx = current_sctx;
1669 
1670     if (eap->cmdidx == CMD_USER)
1671 	cmd = USER_CMD(eap->useridx);
1672     else
1673 	cmd = USER_CMD_GA(&curbuf->b_ucmds, eap->useridx);
1674 
1675     /*
1676      * Replace <> in the command by the arguments.
1677      * First round: "buf" is NULL, compute length, allocate "buf".
1678      * Second round: copy result into "buf".
1679      */
1680     buf = NULL;
1681     for (;;)
1682     {
1683 	p = cmd->uc_rep;    // source
1684 	q = buf;	    // destination
1685 	totlen = 0;
1686 
1687 	for (;;)
1688 	{
1689 	    start = vim_strchr(p, '<');
1690 	    if (start != NULL)
1691 		end = vim_strchr(start + 1, '>');
1692 	    if (buf != NULL)
1693 	    {
1694 		for (ksp = p; *ksp != NUL && *ksp != K_SPECIAL; ++ksp)
1695 		    ;
1696 		if (*ksp == K_SPECIAL
1697 			&& (start == NULL || ksp < start || end == NULL)
1698 			&& ((ksp[1] == KS_SPECIAL && ksp[2] == KE_FILLER)
1699 # ifdef FEAT_GUI
1700 			    || (ksp[1] == KS_EXTRA && ksp[2] == (int)KE_CSI)
1701 # endif
1702 			    ))
1703 		{
1704 		    // K_SPECIAL has been put in the buffer as K_SPECIAL
1705 		    // KS_SPECIAL KE_FILLER, like for mappings, but
1706 		    // do_cmdline() doesn't handle that, so convert it back.
1707 		    // Also change K_SPECIAL KS_EXTRA KE_CSI into CSI.
1708 		    len = ksp - p;
1709 		    if (len > 0)
1710 		    {
1711 			mch_memmove(q, p, len);
1712 			q += len;
1713 		    }
1714 		    *q++ = ksp[1] == KS_SPECIAL ? K_SPECIAL : CSI;
1715 		    p = ksp + 3;
1716 		    continue;
1717 		}
1718 	    }
1719 
1720 	    // break if no <item> is found
1721 	    if (start == NULL || end == NULL)
1722 		break;
1723 
1724 	    // Include the '>'
1725 	    ++end;
1726 
1727 	    // Take everything up to the '<'
1728 	    len = start - p;
1729 	    if (buf == NULL)
1730 		totlen += len;
1731 	    else
1732 	    {
1733 		mch_memmove(q, p, len);
1734 		q += len;
1735 	    }
1736 
1737 	    len = uc_check_code(start, end - start, q, cmd, eap,
1738 			     &split_buf, &split_len);
1739 	    if (len == (size_t)-1)
1740 	    {
1741 		// no match, continue after '<'
1742 		p = start + 1;
1743 		len = 1;
1744 	    }
1745 	    else
1746 		p = end;
1747 	    if (buf == NULL)
1748 		totlen += len;
1749 	    else
1750 		q += len;
1751 	}
1752 	if (buf != NULL)	    // second time here, finished
1753 	{
1754 	    STRCPY(q, p);
1755 	    break;
1756 	}
1757 
1758 	totlen += STRLEN(p);	    // Add on the trailing characters
1759 	buf = alloc(totlen + 1);
1760 	if (buf == NULL)
1761 	{
1762 	    vim_free(split_buf);
1763 	    return;
1764 	}
1765     }
1766 
1767     current_sctx.sc_version = cmd->uc_script_ctx.sc_version;
1768 #ifdef FEAT_EVAL
1769     current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid;
1770 #endif
1771     (void)do_cmdline(buf, eap->getline, eap->cookie,
1772 				   DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED);
1773     current_sctx = save_current_sctx;
1774     vim_free(buf);
1775     vim_free(split_buf);
1776 }
1777