diff src/ex_docmd.c @ 16411:5b5c5daf57de v8.1.1210

patch 8.1.1210: support for user commands is spread out commit https://github.com/vim/vim/commit/ac9fb18020d7e8bf16d02d45fbb02cf47328aaf7 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Apr 27 13:04:13 2019 +0200 patch 8.1.1210: support for user commands is spread out Problem: Support for user commands is spread out. No good reason to make user commands optional. Solution: Move user command support to usercmd.c. Always enable the user_commands feature.
author Bram Moolenaar <Bram@vim.org>
date Sat, 27 Apr 2019 13:15:07 +0200
parents 47d6e874955c
children 8c3a1bd270bb
line wrap: on
line diff
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -19,48 +19,6 @@ static int	ex_pressedreturn = FALSE;
 # define ex_hardcopy	ex_ni
 #endif
 
-#ifdef FEAT_USR_CMDS
-typedef struct ucmd
-{
-    char_u	*uc_name;	/* The command name */
-    long_u	uc_argt;	/* The argument type */
-    char_u	*uc_rep;	/* The command's replacement string */
-    long	uc_def;		/* The default value for a range/count */
-    int		uc_compl;	/* completion type */
-    int		uc_addr_type;	/* The command's address type */
-# ifdef FEAT_EVAL
-    sctx_T	uc_script_ctx;	/* SCTX where the command was defined */
-#  ifdef FEAT_CMDL_COMPL
-    char_u	*uc_compl_arg;	/* completion argument if any */
-#  endif
-# endif
-} ucmd_T;
-
-#define UC_BUFFER	1	/* -buffer: local to current buffer */
-
-static garray_T ucmds = {0, 0, sizeof(ucmd_T), 4, NULL};
-
-#define USER_CMD(i) (&((ucmd_T *)(ucmds.ga_data))[i])
-#define USER_CMD_GA(gap, i) (&((ucmd_T *)((gap)->ga_data))[i])
-
-static void do_ucmd(exarg_T *eap);
-static void ex_command(exarg_T *eap);
-static void ex_delcommand(exarg_T *eap);
-# ifdef FEAT_CMDL_COMPL
-static char_u *get_user_command_name(int idx);
-# endif
-
-/* Wether a command index indicates a user command. */
-# define IS_USER_CMDIDX(idx) ((int)(idx) < 0)
-
-#else
-# define ex_command	ex_ni
-# define ex_comclear	ex_ni
-# define ex_delcommand	ex_ni
-/* Wether a command index indicates a user command. */
-# define IS_USER_CMDIDX(idx) (FALSE)
-#endif
-
 #ifdef FEAT_EVAL
 static char_u	*do_one_cmd(char_u **, int, struct condstack *, char_u *(*fgetline)(int, void *, int), void *cookie);
 #else
@@ -300,10 +258,6 @@ static void	ex_redrawtabline(exarg_T *ea
 static void	close_redir(void);
 static void	ex_mkrc(exarg_T *eap);
 static void	ex_mark(exarg_T *eap);
-#ifdef FEAT_USR_CMDS
-static char	*uc_fun_cmd(void);
-static char_u	*find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int *compl);
-#endif
 static void	ex_startinsert(exarg_T *eap);
 static void	ex_stopinsert(exarg_T *eap);
 #ifdef FEAT_FIND_ID
@@ -1929,21 +1883,20 @@ do_one_cmd(
 		) ? find_command(&ea, NULL) : ea.cmd;
     }
 
-#ifdef FEAT_USR_CMDS
     if (p == NULL)
     {
 	if (!ea.skip)
 	    errormsg = _("E464: Ambiguous use of user-defined command");
 	goto doend;
     }
-    /* Check for wrong commands. */
+    // Check for wrong commands.
     if (*p == '!' && ea.cmd[1] == 0151 && ea.cmd[0] == 78
 	    && !IS_USER_CMDIDX(ea.cmdidx))
     {
 	errormsg = uc_fun_cmd();
 	goto doend;
     }
-#endif
+
     if (ea.cmdidx == CMD_SIZE)
     {
 	if (!ea.skip)
@@ -2508,7 +2461,6 @@ do_one_cmd(
  * 7. Execute the command.
  */
 
-#ifdef FEAT_USR_CMDS
     if (IS_USER_CMDIDX(ea.cmdidx))
     {
 	/*
@@ -2517,10 +2469,9 @@ do_one_cmd(
 	do_ucmd(&ea);
     }
     else
-#endif
     {
 	/*
-	 * Call the function to execute the command.
+	 * Call the function to execute the builtin command.
 	 */
 	ea.errmsg = NULL;
 	(cmdnames[ea.cmdidx].cmd_func)(&ea);
@@ -3235,18 +3186,16 @@ find_command(exarg_T *eap, int *full UNU
 		break;
 	    }
 
-#ifdef FEAT_USR_CMDS
-	/* Look for a user defined command as a last resort.  Let ":Print" be
-	 * overruled by a user defined command. */
+	// Look for a user defined command as a last resort.  Let ":Print" be
+	// overruled by a user defined command.
 	if ((eap->cmdidx == CMD_SIZE || eap->cmdidx == CMD_Print)
 		&& *eap->cmd >= 'A' && *eap->cmd <= 'Z')
 	{
-	    /* User defined commands may contain digits. */
+	    // User defined commands may contain digits.
 	    while (ASCII_ISALNUM(*p))
 		++p;
 	    p = find_ucmd(eap, p, full, NULL, NULL);
 	}
-#endif
 	if (p == eap->cmd)
 	    eap->cmdidx = CMD_SIZE;
     }
@@ -3254,124 +3203,6 @@ find_command(exarg_T *eap, int *full UNU
     return p;
 }
 
-#ifdef FEAT_USR_CMDS
-/*
- * Search for a user command that matches "eap->cmd".
- * Return cmdidx in "eap->cmdidx", flags in "eap->argt", idx in "eap->useridx".
- * Return a pointer to just after the command.
- * Return NULL if there is no matching command.
- */
-    static char_u *
-find_ucmd(
-    exarg_T	*eap,
-    char_u	*p,	/* end of the command (possibly including count) */
-    int		*full,	/* set to TRUE for a full match */
-    expand_T	*xp,	/* used for completion, NULL otherwise */
-    int		*compl)	/* completion flags or NULL */
-{
-    int		len = (int)(p - eap->cmd);
-    int		j, k, matchlen = 0;
-    ucmd_T	*uc;
-    int		found = FALSE;
-    int		possible = FALSE;
-    char_u	*cp, *np;	    /* Point into typed cmd and test name */
-    garray_T	*gap;
-    int		amb_local = FALSE;  /* Found ambiguous buffer-local command,
-				       only full match global is accepted. */
-
-    /*
-     * Look for buffer-local user commands first, then global ones.
-     */
-    gap = &curbuf->b_ucmds;
-    for (;;)
-    {
-	for (j = 0; j < gap->ga_len; ++j)
-	{
-	    uc = USER_CMD_GA(gap, j);
-	    cp = eap->cmd;
-	    np = uc->uc_name;
-	    k = 0;
-	    while (k < len && *np != NUL && *cp++ == *np++)
-		k++;
-	    if (k == len || (*np == NUL && vim_isdigit(eap->cmd[k])))
-	    {
-		/* If finding a second match, the command is ambiguous.  But
-		 * not if a buffer-local command wasn't a full match and a
-		 * global command is a full match. */
-		if (k == len && found && *np != NUL)
-		{
-		    if (gap == &ucmds)
-			return NULL;
-		    amb_local = TRUE;
-		}
-
-		if (!found || (k == len && *np == NUL))
-		{
-		    /* If we matched up to a digit, then there could
-		     * be another command including the digit that we
-		     * should use instead.
-		     */
-		    if (k == len)
-			found = TRUE;
-		    else
-			possible = TRUE;
-
-		    if (gap == &ucmds)
-			eap->cmdidx = CMD_USER;
-		    else
-			eap->cmdidx = CMD_USER_BUF;
-		    eap->argt = (long)uc->uc_argt;
-		    eap->useridx = j;
-		    eap->addr_type = uc->uc_addr_type;
-
-# ifdef FEAT_CMDL_COMPL
-		    if (compl != NULL)
-			*compl = uc->uc_compl;
-#  ifdef FEAT_EVAL
-		    if (xp != NULL)
-		    {
-			xp->xp_arg = uc->uc_compl_arg;
-			xp->xp_script_ctx = uc->uc_script_ctx;
-			xp->xp_script_ctx.sc_lnum += sourcing_lnum;
-		    }
-#  endif
-# endif
-		    /* Do not search for further abbreviations
-		     * if this is an exact match. */
-		    matchlen = k;
-		    if (k == len && *np == NUL)
-		    {
-			if (full != NULL)
-			    *full = TRUE;
-			amb_local = FALSE;
-			break;
-		    }
-		}
-	    }
-	}
-
-	/* Stop if we found a full match or searched all. */
-	if (j < gap->ga_len || gap == &ucmds)
-	    break;
-	gap = &ucmds;
-    }
-
-    /* Only found ambiguous matches. */
-    if (amb_local)
-    {
-	if (xp != NULL)
-	    xp->xp_context = EXPAND_UNSUCCESSFUL;
-	return NULL;
-    }
-
-    /* The match we found may be followed immediately by a number.  Move "p"
-     * back to point to it. */
-    if (found || possible)
-	return p + (matchlen - len);
-    return p;
-}
-#endif
-
 #if defined(FEAT_EVAL) || defined(PROTO)
 static struct cmdmod
 {
@@ -3483,10 +3314,8 @@ set_one_cmd_context(
     char_u		*cmd, *arg;
     int			len = 0;
     exarg_T		ea;
-#if defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL)
+#ifdef FEAT_CMDL_COMPL
     int			compl = EXPAND_NOTHING;
-#endif
-#ifdef FEAT_CMDL_COMPL
     int			delim;
 #endif
     int			forceit = FALSE;
@@ -3572,11 +3401,9 @@ set_one_cmd_context(
 							    (size_t)len) == 0)
 		break;
 
-#ifdef FEAT_USR_CMDS
 	if (cmd[0] >= 'A' && cmd[0] <= 'Z')
-	    while (ASCII_ISALNUM(*p) || *p == '*')	/* Allow * wild card */
+	    while (ASCII_ISALNUM(*p) || *p == '*')	// Allow * wild card
 		++p;
-#endif
     }
 
     /*
@@ -3593,21 +3420,19 @@ set_one_cmd_context(
 	    ea.cmdidx = CMD_substitute;
 	    p = cmd + 1;
 	}
-#ifdef FEAT_USR_CMDS
 	else if (cmd[0] >= 'A' && cmd[0] <= 'Z')
 	{
 	    ea.cmd = cmd;
 	    p = find_ucmd(&ea, p, NULL, xp,
-# if defined(FEAT_CMDL_COMPL)
+#if defined(FEAT_CMDL_COMPL)
 		    &compl
-# else
+#else
 		    NULL
-# endif
+#endif
 		    );
 	    if (p == NULL)
-		ea.cmdidx = CMD_SIZE;	/* ambiguous user command */
-	}
-#endif
+		ea.cmdidx = CMD_SIZE;	// ambiguous user command
+	}
     }
     if (ea.cmdidx == CMD_SIZE)
     {
@@ -3828,7 +3653,7 @@ set_one_cmd_context(
 	    {
 		xp->xp_context = EXPAND_ENV_VARS;
 		++xp->xp_pattern;
-#if defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL)
+#if defined(FEAT_CMDL_COMPL)
 		/* Avoid that the assignment uses EXPAND_FILES again. */
 		if (compl != EXPAND_USER_DEFINED && compl != EXPAND_USER_LIST)
 		    compl = EXPAND_ENV_VARS;
@@ -3944,68 +3769,13 @@ set_one_cmd_context(
  * All completion for the +cmdline_compl feature goes here.
  */
 
-# ifdef FEAT_USR_CMDS
 	case CMD_command:
-	    /* Check for attributes */
-	    while (*arg == '-')
-	    {
-		arg++;	    /* Skip "-" */
-		p = skiptowhite(arg);
-		if (*p == NUL)
-		{
-		    /* Cursor is still in the attribute */
-		    p = vim_strchr(arg, '=');
-		    if (p == NULL)
-		    {
-			/* No "=", so complete attribute names */
-			xp->xp_context = EXPAND_USER_CMD_FLAGS;
-			xp->xp_pattern = arg;
-			return NULL;
-		    }
-
-		    /* For the -complete, -nargs and -addr attributes, we complete
-		     * their arguments as well.
-		     */
-		    if (STRNICMP(arg, "complete", p - arg) == 0)
-		    {
-			xp->xp_context = EXPAND_USER_COMPLETE;
-			xp->xp_pattern = p + 1;
-			return NULL;
-		    }
-		    else if (STRNICMP(arg, "nargs", p - arg) == 0)
-		    {
-			xp->xp_context = EXPAND_USER_NARGS;
-			xp->xp_pattern = p + 1;
-			return NULL;
-		    }
-		    else if (STRNICMP(arg, "addr", p - arg) == 0)
-		    {
-			xp->xp_context = EXPAND_USER_ADDR_TYPE;
-			xp->xp_pattern = p + 1;
-			return NULL;
-		    }
-		    return NULL;
-		}
-		arg = skipwhite(p);
-	    }
-
-	    /* After the attributes comes the new command name */
-	    p = skiptowhite(arg);
-	    if (*p == NUL)
-	    {
-		xp->xp_context = EXPAND_USER_COMMANDS;
-		xp->xp_pattern = arg;
-		break;
-	    }
-
-	    /* And finally comes a normal command */
-	    return skipwhite(p);
+	    return set_context_in_user_cmd(xp, arg);
 
 	case CMD_delcommand:
 	    xp->xp_context = EXPAND_USER_COMMANDS;
 	    xp->xp_pattern = arg;
 	    break;
-# endif
 
 	case CMD_global:
 	case CMD_vglobal:
@@ -4186,32 +3956,32 @@ set_one_cmd_context(
 	    xp->xp_context = EXPAND_BUFFERS;
 	    xp->xp_pattern = arg;
 	    break;
-#ifdef FEAT_USR_CMDS
+
 	case CMD_USER:
 	case CMD_USER_BUF:
 	    if (compl != EXPAND_NOTHING)
 	    {
-		/* XFILE: file names are handled above */
+		// XFILE: file names are handled above
 		if (!(ea.argt & XFILE))
 		{
-# ifdef FEAT_MENU
+#ifdef FEAT_MENU
 		    if (compl == EXPAND_MENUS)
 			return set_context_in_menu_cmd(xp, cmd, arg, forceit);
-# endif
+#endif
 		    if (compl == EXPAND_COMMANDS)
 			return arg;
 		    if (compl == EXPAND_MAPPINGS)
 			return set_context_in_map_cmd(xp, (char_u *)"map",
 					 arg, forceit, FALSE, FALSE, CMD_map);
-		    /* Find start of last argument. */
+		    // Find start of last argument.
 		    p = arg;
 		    while (*p)
 		    {
 			if (*p == ' ')
-			    /* argument starts after a space */
+			    // argument starts after a space
 			    arg = p + 1;
 			else if (*p == '\\' && *(p + 1) != NUL)
-			    ++p; /* skip over escaped character */
+			    ++p; // skip over escaped character
 			MB_PTR_ADV(p);
 		    }
 		    xp->xp_pattern = arg;
@@ -4219,7 +3989,7 @@ set_one_cmd_context(
 		xp->xp_context = compl;
 	    }
 	    break;
-#endif
+
 	case CMD_map:	    case CMD_noremap:
 	case CMD_nmap:	    case CMD_nnoremap:
 	case CMD_vmap:	    case CMD_vnoremap:
@@ -5771,7 +5541,7 @@ check_more(
     return OK;
 }
 
-#ifdef FEAT_CMDL_COMPL
+#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
 /*
  * Function given to ExpandGeneric() to obtain the list of command names.
  */
@@ -5779,1440 +5549,11 @@ check_more(
 get_command_name(expand_T *xp UNUSED, int idx)
 {
     if (idx >= (int)CMD_SIZE)
-# ifdef FEAT_USR_CMDS
 	return get_user_command_name(idx);
-# else
-	return NULL;
-# endif
     return cmdnames[idx].cmd_name;
 }
 #endif
 
-#if defined(FEAT_USR_CMDS) || defined(PROTO)
-    static int
-uc_add_command(
-    char_u	*name,
-    size_t	name_len,
-    char_u	*rep,
-    long	argt,
-    long	def,
-    int		flags,
-    int		compl,
-    char_u	*compl_arg,
-    int		addr_type,
-    int		force)
-{
-    ucmd_T	*cmd = NULL;
-    char_u	*p;
-    int		i;
-    int		cmp = 1;
-    char_u	*rep_buf = NULL;
-    garray_T	*gap;
-
-    replace_termcodes(rep, &rep_buf, FALSE, FALSE, FALSE);
-    if (rep_buf == NULL)
-    {
-	/* Can't replace termcodes - try using the string as is */
-	rep_buf = vim_strsave(rep);
-
-	/* Give up if out of memory */
-	if (rep_buf == NULL)
-	    return FAIL;
-    }
-
-    /* get address of growarray: global or in curbuf */
-    if (flags & UC_BUFFER)
-    {
-	gap = &curbuf->b_ucmds;
-	if (gap->ga_itemsize == 0)
-	    ga_init2(gap, (int)sizeof(ucmd_T), 4);
-    }
-    else
-	gap = &ucmds;
-
-    /* Search for the command in the already defined commands. */
-    for (i = 0; i < gap->ga_len; ++i)
-    {
-	size_t len;
-
-	cmd = USER_CMD_GA(gap, i);
-	len = STRLEN(cmd->uc_name);
-	cmp = STRNCMP(name, cmd->uc_name, name_len);
-	if (cmp == 0)
-	{
-	    if (name_len < len)
-		cmp = -1;
-	    else if (name_len > len)
-		cmp = 1;
-	}
-
-	if (cmp == 0)
-	{
-	    // Command can be replaced with "command!" and when sourcing the
-	    // same script again, but only once.
-	    if (!force && (cmd->uc_script_ctx.sc_sid != current_sctx.sc_sid
-			  || cmd->uc_script_ctx.sc_seq == current_sctx.sc_seq))
-	    {
-		semsg(_("E174: Command already exists: add ! to replace it: %s"),
-									 name);
-		goto fail;
-	    }
-
-	    VIM_CLEAR(cmd->uc_rep);
-#if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
-	    VIM_CLEAR(cmd->uc_compl_arg);
-#endif
-	    break;
-	}
-
-	/* Stop as soon as we pass the name to add */
-	if (cmp < 0)
-	    break;
-    }
-
-    /* Extend the array unless we're replacing an existing command */
-    if (cmp != 0)
-    {
-	if (ga_grow(gap, 1) != OK)
-	    goto fail;
-	if ((p = vim_strnsave(name, (int)name_len)) == NULL)
-	    goto fail;
-
-	cmd = USER_CMD_GA(gap, i);
-	mch_memmove(cmd + 1, cmd, (gap->ga_len - i) * sizeof(ucmd_T));
-
-	++gap->ga_len;
-
-	cmd->uc_name = p;
-    }
-
-    cmd->uc_rep = rep_buf;
-    cmd->uc_argt = argt;
-    cmd->uc_def = def;
-    cmd->uc_compl = compl;
-#ifdef FEAT_EVAL
-    cmd->uc_script_ctx = current_sctx;
-    cmd->uc_script_ctx.sc_lnum += sourcing_lnum;
-# ifdef FEAT_CMDL_COMPL
-    cmd->uc_compl_arg = compl_arg;
-# endif
-#endif
-    cmd->uc_addr_type = addr_type;
-
-    return OK;
-
-fail:
-    vim_free(rep_buf);
-#if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
-    vim_free(compl_arg);
-#endif
-    return FAIL;
-}
-#endif
-
-#if defined(FEAT_USR_CMDS)
-static struct
-{
-    int	    expand;
-    char    *name;
-    char    *shortname;
-} addr_type_complete[] =
-{
-    {ADDR_ARGUMENTS, "arguments", "arg"},
-    {ADDR_LINES, "lines", "line"},
-    {ADDR_LOADED_BUFFERS, "loaded_buffers", "load"},
-    {ADDR_TABS, "tabs", "tab"},
-    {ADDR_BUFFERS, "buffers", "buf"},
-    {ADDR_WINDOWS, "windows", "win"},
-    {ADDR_QUICKFIX, "quickfix", "qf"},
-    {ADDR_OTHER, "other", "?"},
-    {-1, NULL, NULL}
-};
-#endif
-
-#if defined(FEAT_USR_CMDS) || defined(FEAT_EVAL) || defined(PROTO)
-/*
- * List of names for completion for ":command" with the EXPAND_ flag.
- * Must be alphabetical for completion.
- */
-static struct
-{
-    int	    expand;
-    char    *name;
-} command_complete[] =
-{
-    {EXPAND_ARGLIST, "arglist"},
-    {EXPAND_AUGROUP, "augroup"},
-    {EXPAND_BEHAVE, "behave"},
-    {EXPAND_BUFFERS, "buffer"},
-    {EXPAND_COLORS, "color"},
-    {EXPAND_COMMANDS, "command"},
-    {EXPAND_COMPILER, "compiler"},
-#if defined(FEAT_CSCOPE)
-    {EXPAND_CSCOPE, "cscope"},
-#endif
-#if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
-    {EXPAND_USER_DEFINED, "custom"},
-    {EXPAND_USER_LIST, "customlist"},
-#endif
-    {EXPAND_DIRECTORIES, "dir"},
-    {EXPAND_ENV_VARS, "environment"},
-    {EXPAND_EVENTS, "event"},
-    {EXPAND_EXPRESSION, "expression"},
-    {EXPAND_FILES, "file"},
-    {EXPAND_FILES_IN_PATH, "file_in_path"},
-    {EXPAND_FILETYPE, "filetype"},
-    {EXPAND_FUNCTIONS, "function"},
-    {EXPAND_HELP, "help"},
-    {EXPAND_HIGHLIGHT, "highlight"},
-#if defined(FEAT_CMDHIST)
-    {EXPAND_HISTORY, "history"},
-#endif
-#if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
-    {EXPAND_LOCALES, "locale"},
-#endif
-    {EXPAND_MAPCLEAR, "mapclear"},
-    {EXPAND_MAPPINGS, "mapping"},
-    {EXPAND_MENUS, "menu"},
-    {EXPAND_MESSAGES, "messages"},
-    {EXPAND_OWNSYNTAX, "syntax"},
-#if defined(FEAT_PROFILE)
-    {EXPAND_SYNTIME, "syntime"},
-#endif
-    {EXPAND_SETTINGS, "option"},
-    {EXPAND_PACKADD, "packadd"},
-    {EXPAND_SHELLCMD, "shellcmd"},
-#if defined(FEAT_SIGNS)
-    {EXPAND_SIGN, "sign"},
-#endif
-    {EXPAND_TAGS, "tag"},
-    {EXPAND_TAGS_LISTFILES, "tag_listfiles"},
-    {EXPAND_USER, "user"},
-    {EXPAND_USER_VARS, "var"},
-    {0, NULL}
-};
-#endif
-
-#if defined(FEAT_USR_CMDS) || defined(PROTO)
-    static void
-uc_list(char_u *name, size_t name_len)
-{
-    int		i, j;
-    int		found = FALSE;
-    ucmd_T	*cmd;
-    int		len;
-    int		over;
-    long	a;
-    garray_T	*gap;
-
-    gap = &curbuf->b_ucmds;
-    for (;;)
-    {
-	for (i = 0; i < gap->ga_len; ++i)
-	{
-	    cmd = USER_CMD_GA(gap, i);
-	    a = (long)cmd->uc_argt;
-
-	    /* Skip commands which don't match the requested prefix and
-	     * commands filtered out. */
-	    if (STRNCMP(name, cmd->uc_name, name_len) != 0
-		    || message_filtered(cmd->uc_name))
-		continue;
-
-	    /* Put out the title first time */
-	    if (!found)
-		msg_puts_title(_("\n    Name              Args Address Complete    Definition"));
-	    found = TRUE;
-	    msg_putchar('\n');
-	    if (got_int)
-		break;
-
-	    // Special cases
-	    len = 4;
-	    if (a & BANG)
-	    {
-		msg_putchar('!');
-		--len;
-	    }
-	    if (a & REGSTR)
-	    {
-		msg_putchar('"');
-		--len;
-	    }
-	    if (gap != &ucmds)
-	    {
-		msg_putchar('b');
-		--len;
-	    }
-	    if (a & TRLBAR)
-	    {
-		msg_putchar('|');
-		--len;
-	    }
-	    while (len-- > 0)
-		msg_putchar(' ');
-
-	    msg_outtrans_attr(cmd->uc_name, HL_ATTR(HLF_D));
-	    len = (int)STRLEN(cmd->uc_name) + 4;
-
-	    do {
-		msg_putchar(' ');
-		++len;
-	    } while (len < 22);
-
-	    // "over" is how much longer the name is than the column width for
-	    // the name, we'll try to align what comes after.
-	    over = len - 22;
-	    len = 0;
-
-	    // Arguments
-	    switch ((int)(a & (EXTRA|NOSPC|NEEDARG)))
-	    {
-		case 0:			    IObuff[len++] = '0'; break;
-		case (EXTRA):		    IObuff[len++] = '*'; break;
-		case (EXTRA|NOSPC):	    IObuff[len++] = '?'; break;
-		case (EXTRA|NEEDARG):	    IObuff[len++] = '+'; break;
-		case (EXTRA|NOSPC|NEEDARG): IObuff[len++] = '1'; break;
-	    }
-
-	    do {
-		IObuff[len++] = ' ';
-	    } while (len < 5 - over);
-
-	    // Address / Range
-	    if (a & (RANGE|COUNT))
-	    {
-		if (a & COUNT)
-		{
-		    // -count=N
-		    sprintf((char *)IObuff + len, "%ldc", cmd->uc_def);
-		    len += (int)STRLEN(IObuff + len);
-		}
-		else if (a & DFLALL)
-		    IObuff[len++] = '%';
-		else if (cmd->uc_def >= 0)
-		{
-		    // -range=N
-		    sprintf((char *)IObuff + len, "%ld", cmd->uc_def);
-		    len += (int)STRLEN(IObuff + len);
-		}
-		else
-		    IObuff[len++] = '.';
-	    }
-
-	    do {
-		IObuff[len++] = ' ';
-	    } while (len < 8 - over);
-
-	    // Address Type
-	    for (j = 0; addr_type_complete[j].expand != -1; ++j)
-		if (addr_type_complete[j].expand != ADDR_LINES
-			&& addr_type_complete[j].expand == cmd->uc_addr_type)
-		{
-		    STRCPY(IObuff + len, addr_type_complete[j].shortname);
-		    len += (int)STRLEN(IObuff + len);
-		    break;
-		}
-
-	    do {
-		IObuff[len++] = ' ';
-	    } while (len < 13 - over);
-
-	    // Completion
-	    for (j = 0; command_complete[j].expand != 0; ++j)
-		if (command_complete[j].expand == cmd->uc_compl)
-		{
-		    STRCPY(IObuff + len, command_complete[j].name);
-		    len += (int)STRLEN(IObuff + len);
-		    break;
-		}
-
-	    do {
-		IObuff[len++] = ' ';
-	    } while (len < 25 - over);
-
-	    IObuff[len] = '\0';
-	    msg_outtrans(IObuff);
-
-	    msg_outtrans_special(cmd->uc_rep, FALSE,
-					     name_len == 0 ? Columns - 47 : 0);
-#ifdef FEAT_EVAL
-	    if (p_verbose > 0)
-		last_set_msg(cmd->uc_script_ctx);
-#endif
-	    out_flush();
-	    ui_breakcheck();
-	    if (got_int)
-		break;
-	}
-	if (gap == &ucmds || i < gap->ga_len)
-	    break;
-	gap = &ucmds;
-    }
-
-    if (!found)
-	msg(_("No user-defined commands found"));
-}
-
-    static char *
-uc_fun_cmd(void)
-{
-    static char_u fcmd[] = {0x84, 0xaf, 0x60, 0xb9, 0xaf, 0xb5, 0x60, 0xa4,
-			    0xa5, 0xad, 0xa1, 0xae, 0xa4, 0x60, 0xa1, 0x60,
-			    0xb3, 0xa8, 0xb2, 0xb5, 0xa2, 0xa2, 0xa5, 0xb2,
-			    0xb9, 0x7f, 0};
-    int		i;
-
-    for (i = 0; fcmd[i]; ++i)
-	IObuff[i] = fcmd[i] - 0x40;
-    IObuff[i] = 0;
-    return (char *)IObuff;
-}
-
-    static int
-uc_scan_attr(
-    char_u	*attr,
-    size_t	len,
-    long	*argt,
-    long	*def,
-    int		*flags,
-    int		*compl,
-    char_u	**compl_arg,
-    int		*addr_type_arg)
-{
-    char_u	*p;
-
-    if (len == 0)
-    {
-	emsg(_("E175: No attribute specified"));
-	return FAIL;
-    }
-
-    /* First, try the simple attributes (no arguments) */
-    if (STRNICMP(attr, "bang", len) == 0)
-	*argt |= BANG;
-    else if (STRNICMP(attr, "buffer", len) == 0)
-	*flags |= UC_BUFFER;
-    else if (STRNICMP(attr, "register", len) == 0)
-	*argt |= REGSTR;
-    else if (STRNICMP(attr, "bar", len) == 0)
-	*argt |= TRLBAR;
-    else
-    {
-	int	i;
-	char_u	*val = NULL;
-	size_t	vallen = 0;
-	size_t	attrlen = len;
-
-	/* Look for the attribute name - which is the part before any '=' */
-	for (i = 0; i < (int)len; ++i)
-	{
-	    if (attr[i] == '=')
-	    {
-		val = &attr[i + 1];
-		vallen = len - i - 1;
-		attrlen = i;
-		break;
-	    }
-	}
-
-	if (STRNICMP(attr, "nargs", attrlen) == 0)
-	{
-	    if (vallen == 1)
-	    {
-		if (*val == '0')
-		    /* Do nothing - this is the default */;
-		else if (*val == '1')
-		    *argt |= (EXTRA | NOSPC | NEEDARG);
-		else if (*val == '*')
-		    *argt |= EXTRA;
-		else if (*val == '?')
-		    *argt |= (EXTRA | NOSPC);
-		else if (*val == '+')
-		    *argt |= (EXTRA | NEEDARG);
-		else
-		    goto wrong_nargs;
-	    }
-	    else
-	    {
-wrong_nargs:
-		emsg(_("E176: Invalid number of arguments"));
-		return FAIL;
-	    }
-	}
-	else if (STRNICMP(attr, "range", attrlen) == 0)
-	{
-	    *argt |= RANGE;
-	    if (vallen == 1 && *val == '%')
-		*argt |= DFLALL;
-	    else if (val != NULL)
-	    {
-		p = val;
-		if (*def >= 0)
-		{
-two_count:
-		    emsg(_("E177: Count cannot be specified twice"));
-		    return FAIL;
-		}
-
-		*def = getdigits(&p);
-		*argt |= (ZEROR | NOTADR);
-
-		if (p != val + vallen || vallen == 0)
-		{
-invalid_count:
-		    emsg(_("E178: Invalid default value for count"));
-		    return FAIL;
-		}
-	    }
-	}
-	else if (STRNICMP(attr, "count", attrlen) == 0)
-	{
-	    *argt |= (COUNT | ZEROR | RANGE | NOTADR);
-
-	    if (val != NULL)
-	    {
-		p = val;
-		if (*def >= 0)
-		    goto two_count;
-
-		*def = getdigits(&p);
-
-		if (p != val + vallen)
-		    goto invalid_count;
-	    }
-
-	    if (*def < 0)
-		*def = 0;
-	}
-	else if (STRNICMP(attr, "complete", attrlen) == 0)
-	{
-	    if (val == NULL)
-	    {
-		emsg(_("E179: argument required for -complete"));
-		return FAIL;
-	    }
-
-	    if (parse_compl_arg(val, (int)vallen, compl, argt, compl_arg)
-								      == FAIL)
-		return FAIL;
-	}
-	else if (STRNICMP(attr, "addr", attrlen) == 0)
-	{
-	    *argt |= RANGE;
-	    if (val == NULL)
-	    {
-		emsg(_("E179: argument required for -addr"));
-		return FAIL;
-	    }
-	    if (parse_addr_type_arg(val, (int)vallen, argt, addr_type_arg)
-								      == FAIL)
-		return FAIL;
-	    if (addr_type_arg != ADDR_LINES)
-		*argt |= (ZEROR | NOTADR) ;
-	}
-	else
-	{
-	    char_u ch = attr[len];
-	    attr[len] = '\0';
-	    semsg(_("E181: Invalid attribute: %s"), attr);
-	    attr[len] = ch;
-	    return FAIL;
-	}
-    }
-
-    return OK;
-}
-
-/*
- * ":command ..."
- */
-    static void
-ex_command(exarg_T *eap)
-{
-    char_u  *name;
-    char_u  *end;
-    char_u  *p;
-    long    argt = 0;
-    long    def = -1;
-    int	    flags = 0;
-    int	    compl = EXPAND_NOTHING;
-    char_u  *compl_arg = NULL;
-    int	    addr_type_arg = ADDR_LINES;
-    int	    has_attr = (eap->arg[0] == '-');
-    int	    name_len;
-
-    p = eap->arg;
-
-    /* Check for attributes */
-    while (*p == '-')
-    {
-	++p;
-	end = skiptowhite(p);
-	if (uc_scan_attr(p, end - p, &argt, &def, &flags, &compl,
-						    &compl_arg, &addr_type_arg)
-		== FAIL)
-	    return;
-	p = skipwhite(end);
-    }
-
-    /* Get the name (if any) and skip to the following argument */
-    name = p;
-    if (ASCII_ISALPHA(*p))
-	while (ASCII_ISALNUM(*p))
-	    ++p;
-    if (!ends_excmd(*p) && !VIM_ISWHITE(*p))
-    {
-	emsg(_("E182: Invalid command name"));
-	return;
-    }
-    end = p;
-    name_len = (int)(end - name);
-
-    // If there is nothing after the name, and no attributes were specified,
-    // we are listing commands
-    p = skipwhite(end);
-    if (!has_attr && ends_excmd(*p))
-    {
-	uc_list(name, end - name);
-    }
-    else if (!ASCII_ISUPPER(*name))
-    {
-	emsg(_("E183: User defined commands must start with an uppercase letter"));
-	return;
-    }
-    else if ((name_len == 1 && *name == 'X')
-	  || (name_len <= 4
-		  && STRNCMP(name, "Next", name_len > 4 ? 4 : name_len) == 0))
-    {
-	emsg(_("E841: Reserved name, cannot be used for user defined command"));
-	return;
-    }
-    else
-	uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg,
-						  addr_type_arg, eap->forceit);
-}
-
-/*
- * ":comclear"
- * Clear all user commands, global and for current buffer.
- */
-    void
-ex_comclear(exarg_T *eap UNUSED)
-{
-    uc_clear(&ucmds);
-    uc_clear(&curbuf->b_ucmds);
-}
-
-/*
- * Clear all user commands for "gap".
- */
-    void
-uc_clear(garray_T *gap)
-{
-    int		i;
-    ucmd_T	*cmd;
-
-    for (i = 0; i < gap->ga_len; ++i)
-    {
-	cmd = USER_CMD_GA(gap, i);
-	vim_free(cmd->uc_name);
-	vim_free(cmd->uc_rep);
-# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
-	vim_free(cmd->uc_compl_arg);
-# endif
-    }
-    ga_clear(gap);
-}
-
-    static void
-ex_delcommand(exarg_T *eap)
-{
-    int		i = 0;
-    ucmd_T	*cmd = NULL;
-    int		cmp = -1;
-    garray_T	*gap;
-
-    gap = &curbuf->b_ucmds;
-    for (;;)
-    {
-	for (i = 0; i < gap->ga_len; ++i)
-	{
-	    cmd = USER_CMD_GA(gap, i);
-	    cmp = STRCMP(eap->arg, cmd->uc_name);
-	    if (cmp <= 0)
-		break;
-	}
-	if (gap == &ucmds || cmp == 0)
-	    break;
-	gap = &ucmds;
-    }
-
-    if (cmp != 0)
-    {
-	semsg(_("E184: No such user-defined command: %s"), eap->arg);
-	return;
-    }
-
-    vim_free(cmd->uc_name);
-    vim_free(cmd->uc_rep);
-# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
-    vim_free(cmd->uc_compl_arg);
-# endif
-
-    --gap->ga_len;
-
-    if (i < gap->ga_len)
-	mch_memmove(cmd, cmd + 1, (gap->ga_len - i) * sizeof(ucmd_T));
-}
-
-/*
- * split and quote args for <f-args>
- */
-    static char_u *
-uc_split_args(char_u *arg, size_t *lenp)
-{
-    char_u *buf;
-    char_u *p;
-    char_u *q;
-    int len;
-
-    /* Precalculate length */
-    p = arg;
-    len = 2; /* Initial and final quotes */
-
-    while (*p)
-    {
-	if (p[0] == '\\' && p[1] == '\\')
-	{
-	    len += 2;
-	    p += 2;
-	}
-	else if (p[0] == '\\' && VIM_ISWHITE(p[1]))
-	{
-	    len += 1;
-	    p += 2;
-	}
-	else if (*p == '\\' || *p == '"')
-	{
-	    len += 2;
-	    p += 1;
-	}
-	else if (VIM_ISWHITE(*p))
-	{
-	    p = skipwhite(p);
-	    if (*p == NUL)
-		break;
-	    len += 3; /* "," */
-	}
-	else
-	{
-	    int charlen = (*mb_ptr2len)(p);
-
-	    len += charlen;
-	    p += charlen;
-	}
-    }
-
-    buf = alloc(len + 1);
-    if (buf == NULL)
-    {
-	*lenp = 0;
-	return buf;
-    }
-
-    p = arg;
-    q = buf;
-    *q++ = '"';
-    while (*p)
-    {
-	if (p[0] == '\\' && p[1] == '\\')
-	{
-	    *q++ = '\\';
-	    *q++ = '\\';
-	    p += 2;
-	}
-	else if (p[0] == '\\' && VIM_ISWHITE(p[1]))
-	{
-	    *q++ = p[1];
-	    p += 2;
-	}
-	else if (*p == '\\' || *p == '"')
-	{
-	    *q++ = '\\';
-	    *q++ = *p++;
-	}
-	else if (VIM_ISWHITE(*p))
-	{
-	    p = skipwhite(p);
-	    if (*p == NUL)
-		break;
-	    *q++ = '"';
-	    *q++ = ',';
-	    *q++ = '"';
-	}
-	else
-	{
-	    MB_COPY_CHAR(p, q);
-	}
-    }
-    *q++ = '"';
-    *q = 0;
-
-    *lenp = len;
-    return buf;
-}
-
-    static size_t
-add_cmd_modifier(char_u *buf, char *mod_str, int *multi_mods)
-{
-    size_t result;
-
-    result = STRLEN(mod_str);
-    if (*multi_mods)
-	result += 1;
-    if (buf != NULL)
-    {
-	if (*multi_mods)
-	    STRCAT(buf, " ");
-	STRCAT(buf, mod_str);
-    }
-
-    *multi_mods = 1;
-
-    return result;
-}
-
-/*
- * Check for a <> code in a user command.
- * "code" points to the '<'.  "len" the length of the <> (inclusive).
- * "buf" is where the result is to be added.
- * "split_buf" points to a buffer used for splitting, caller should free it.
- * "split_len" is the length of what "split_buf" contains.
- * Returns the length of the replacement, which has been added to "buf".
- * Returns -1 if there was no match, and only the "<" has been copied.
- */
-    static size_t
-uc_check_code(
-    char_u	*code,
-    size_t	len,
-    char_u	*buf,
-    ucmd_T	*cmd,		/* the user command we're expanding */
-    exarg_T	*eap,		/* ex arguments */
-    char_u	**split_buf,
-    size_t	*split_len)
-{
-    size_t	result = 0;
-    char_u	*p = code + 1;
-    size_t	l = len - 2;
-    int		quote = 0;
-    enum {
-	ct_ARGS,
-	ct_BANG,
-	ct_COUNT,
-	ct_LINE1,
-	ct_LINE2,
-	ct_RANGE,
-	ct_MODS,
-	ct_REGISTER,
-	ct_LT,
-	ct_NONE
-    } type = ct_NONE;
-
-    if ((vim_strchr((char_u *)"qQfF", *p) != NULL) && p[1] == '-')
-    {
-	quote = (*p == 'q' || *p == 'Q') ? 1 : 2;
-	p += 2;
-	l -= 2;
-    }
-
-    ++l;
-    if (l <= 1)
-	type = ct_NONE;
-    else if (STRNICMP(p, "args>", l) == 0)
-	type = ct_ARGS;
-    else if (STRNICMP(p, "bang>", l) == 0)
-	type = ct_BANG;
-    else if (STRNICMP(p, "count>", l) == 0)
-	type = ct_COUNT;
-    else if (STRNICMP(p, "line1>", l) == 0)
-	type = ct_LINE1;
-    else if (STRNICMP(p, "line2>", l) == 0)
-	type = ct_LINE2;
-    else if (STRNICMP(p, "range>", l) == 0)
-	type = ct_RANGE;
-    else if (STRNICMP(p, "lt>", l) == 0)
-	type = ct_LT;
-    else if (STRNICMP(p, "reg>", l) == 0 || STRNICMP(p, "register>", l) == 0)
-	type = ct_REGISTER;
-    else if (STRNICMP(p, "mods>", l) == 0)
-	type = ct_MODS;
-
-    switch (type)
-    {
-    case ct_ARGS:
-	/* Simple case first */
-	if (*eap->arg == NUL)
-	{
-	    if (quote == 1)
-	    {
-		result = 2;
-		if (buf != NULL)
-		    STRCPY(buf, "''");
-	    }
-	    else
-		result = 0;
-	    break;
-	}
-
-	/* When specified there is a single argument don't split it.
-	 * Works for ":Cmd %" when % is "a b c". */
-	if ((eap->argt & NOSPC) && quote == 2)
-	    quote = 1;
-
-	switch (quote)
-	{
-	case 0: /* No quoting, no splitting */
-	    result = STRLEN(eap->arg);
-	    if (buf != NULL)
-		STRCPY(buf, eap->arg);
-	    break;
-	case 1: /* Quote, but don't split */
-	    result = STRLEN(eap->arg) + 2;
-	    for (p = eap->arg; *p; ++p)
-	    {
-		if (enc_dbcs != 0 && (*mb_ptr2len)(p) == 2)
-		    /* DBCS can contain \ in a trail byte, skip the
-		     * double-byte character. */
-		    ++p;
-		else
-		     if (*p == '\\' || *p == '"')
-		    ++result;
-	    }
-
-	    if (buf != NULL)
-	    {
-		*buf++ = '"';
-		for (p = eap->arg; *p; ++p)
-		{
-		    if (enc_dbcs != 0 && (*mb_ptr2len)(p) == 2)
-			/* DBCS can contain \ in a trail byte, copy the
-			 * double-byte character to avoid escaping. */
-			*buf++ = *p++;
-		    else
-			 if (*p == '\\' || *p == '"')
-			*buf++ = '\\';
-		    *buf++ = *p;
-		}
-		*buf = '"';
-	    }
-
-	    break;
-	case 2: /* Quote and split (<f-args>) */
-	    /* This is hard, so only do it once, and cache the result */
-	    if (*split_buf == NULL)
-		*split_buf = uc_split_args(eap->arg, split_len);
-
-	    result = *split_len;
-	    if (buf != NULL && result != 0)
-		STRCPY(buf, *split_buf);
-
-	    break;
-	}
-	break;
-
-    case ct_BANG:
-	result = eap->forceit ? 1 : 0;
-	if (quote)
-	    result += 2;
-	if (buf != NULL)
-	{
-	    if (quote)
-		*buf++ = '"';
-	    if (eap->forceit)
-		*buf++ = '!';
-	    if (quote)
-		*buf = '"';
-	}
-	break;
-
-    case ct_LINE1:
-    case ct_LINE2:
-    case ct_RANGE:
-    case ct_COUNT:
-    {
-	char num_buf[20];
-	long num = (type == ct_LINE1) ? eap->line1 :
-		   (type == ct_LINE2) ? eap->line2 :
-		   (type == ct_RANGE) ? eap->addr_count :
-		   (eap->addr_count > 0) ? eap->line2 : cmd->uc_def;
-	size_t num_len;
-
-	sprintf(num_buf, "%ld", num);
-	num_len = STRLEN(num_buf);
-	result = num_len;
-
-	if (quote)
-	    result += 2;
-
-	if (buf != NULL)
-	{
-	    if (quote)
-		*buf++ = '"';
-	    STRCPY(buf, num_buf);
-	    buf += num_len;
-	    if (quote)
-		*buf = '"';
-	}
-
-	break;
-    }
-
-    case ct_MODS:
-    {
-	int multi_mods = 0;
-	typedef struct {
-	    int *varp;
-	    char *name;
-	} mod_entry_T;
-	static mod_entry_T mod_entries[] = {
-#ifdef FEAT_BROWSE_CMD
-	    {&cmdmod.browse, "browse"},
-#endif
-#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
-	    {&cmdmod.confirm, "confirm"},
-#endif
-	    {&cmdmod.hide, "hide"},
-	    {&cmdmod.keepalt, "keepalt"},
-	    {&cmdmod.keepjumps, "keepjumps"},
-	    {&cmdmod.keepmarks, "keepmarks"},
-	    {&cmdmod.keeppatterns, "keeppatterns"},
-	    {&cmdmod.lockmarks, "lockmarks"},
-	    {&cmdmod.noswapfile, "noswapfile"},
-	    {NULL, NULL}
-	};
-	int i;
-
-	result = quote ? 2 : 0;
-	if (buf != NULL)
-	{
-	    if (quote)
-		*buf++ = '"';
-	    *buf = '\0';
-	}
-
-	/* :aboveleft and :leftabove */
-	if (cmdmod.split & WSP_ABOVE)
-	    result += add_cmd_modifier(buf, "aboveleft", &multi_mods);
-	/* :belowright and :rightbelow */
-	if (cmdmod.split & WSP_BELOW)
-	    result += add_cmd_modifier(buf, "belowright", &multi_mods);
-	/* :botright */
-	if (cmdmod.split & WSP_BOT)
-	    result += add_cmd_modifier(buf, "botright", &multi_mods);
-
-	/* the modifiers that are simple flags */
-	for (i = 0; mod_entries[i].varp != NULL; ++i)
-	    if (*mod_entries[i].varp)
-		result += add_cmd_modifier(buf, mod_entries[i].name,
-								 &multi_mods);
-
-	/* TODO: How to support :noautocmd? */
-#ifdef HAVE_SANDBOX
-	/* TODO: How to support :sandbox?*/
-#endif
-	/* :silent */
-	if (msg_silent > 0)
-	    result += add_cmd_modifier(buf,
-		    emsg_silent > 0 ? "silent!" : "silent", &multi_mods);
-	/* :tab */
-	if (cmdmod.tab > 0)
-	    result += add_cmd_modifier(buf, "tab", &multi_mods);
-	/* :topleft */
-	if (cmdmod.split & WSP_TOP)
-	    result += add_cmd_modifier(buf, "topleft", &multi_mods);
-	/* TODO: How to support :unsilent?*/
-	/* :verbose */
-	if (p_verbose > 0)
-	    result += add_cmd_modifier(buf, "verbose", &multi_mods);
-	/* :vertical */
-	if (cmdmod.split & WSP_VERT)
-	    result += add_cmd_modifier(buf, "vertical", &multi_mods);
-	if (quote && buf != NULL)
-	{
-	    buf += result - 2;
-	    *buf = '"';
-	}
-	break;
-    }
-
-    case ct_REGISTER:
-	result = eap->regname ? 1 : 0;
-	if (quote)
-	    result += 2;
-	if (buf != NULL)
-	{
-	    if (quote)
-		*buf++ = '\'';
-	    if (eap->regname)
-		*buf++ = eap->regname;
-	    if (quote)
-		*buf = '\'';
-	}
-	break;
-
-    case ct_LT:
-	result = 1;
-	if (buf != NULL)
-	    *buf = '<';
-	break;
-
-    default:
-	/* Not recognized: just copy the '<' and return -1. */
-	result = (size_t)-1;
-	if (buf != NULL)
-	    *buf = '<';
-	break;
-    }
-
-    return result;
-}
-
-    static void
-do_ucmd(exarg_T *eap)
-{
-    char_u	*buf;
-    char_u	*p;
-    char_u	*q;
-
-    char_u	*start;
-    char_u	*end = NULL;
-    char_u	*ksp;
-    size_t	len, totlen;
-
-    size_t	split_len = 0;
-    char_u	*split_buf = NULL;
-    ucmd_T	*cmd;
-#ifdef FEAT_EVAL
-    sctx_T	save_current_sctx = current_sctx;
-#endif
-
-    if (eap->cmdidx == CMD_USER)
-	cmd = USER_CMD(eap->useridx);
-    else
-	cmd = USER_CMD_GA(&curbuf->b_ucmds, eap->useridx);
-
-    /*
-     * Replace <> in the command by the arguments.
-     * First round: "buf" is NULL, compute length, allocate "buf".
-     * Second round: copy result into "buf".
-     */
-    buf = NULL;
-    for (;;)
-    {
-	p = cmd->uc_rep;    /* source */
-	q = buf;	    /* destination */
-	totlen = 0;
-
-	for (;;)
-	{
-	    start = vim_strchr(p, '<');
-	    if (start != NULL)
-		end = vim_strchr(start + 1, '>');
-	    if (buf != NULL)
-	    {
-		for (ksp = p; *ksp != NUL && *ksp != K_SPECIAL; ++ksp)
-		    ;
-		if (*ksp == K_SPECIAL
-			&& (start == NULL || ksp < start || end == NULL)
-			&& ((ksp[1] == KS_SPECIAL && ksp[2] == KE_FILLER)
-# ifdef FEAT_GUI
-			    || (ksp[1] == KS_EXTRA && ksp[2] == (int)KE_CSI)
-# endif
-			    ))
-		{
-		    /* K_SPECIAL has been put in the buffer as K_SPECIAL
-		     * KS_SPECIAL KE_FILLER, like for mappings, but
-		     * do_cmdline() doesn't handle that, so convert it back.
-		     * Also change K_SPECIAL KS_EXTRA KE_CSI into CSI. */
-		    len = ksp - p;
-		    if (len > 0)
-		    {
-			mch_memmove(q, p, len);
-			q += len;
-		    }
-		    *q++ = ksp[1] == KS_SPECIAL ? K_SPECIAL : CSI;
-		    p = ksp + 3;
-		    continue;
-		}
-	    }
-
-	    /* break if no <item> is found */
-	    if (start == NULL || end == NULL)
-		break;
-
-	    /* Include the '>' */
-	    ++end;
-
-	    /* Take everything up to the '<' */
-	    len = start - p;
-	    if (buf == NULL)
-		totlen += len;
-	    else
-	    {
-		mch_memmove(q, p, len);
-		q += len;
-	    }
-
-	    len = uc_check_code(start, end - start, q, cmd, eap,
-			     &split_buf, &split_len);
-	    if (len == (size_t)-1)
-	    {
-		/* no match, continue after '<' */
-		p = start + 1;
-		len = 1;
-	    }
-	    else
-		p = end;
-	    if (buf == NULL)
-		totlen += len;
-	    else
-		q += len;
-	}
-	if (buf != NULL)	    /* second time here, finished */
-	{
-	    STRCPY(q, p);
-	    break;
-	}
-
-	totlen += STRLEN(p);	    /* Add on the trailing characters */
-	buf = alloc((unsigned)(totlen + 1));
-	if (buf == NULL)
-	{
-	    vim_free(split_buf);
-	    return;
-	}
-    }
-
-#ifdef FEAT_EVAL
-    current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid;
-#endif
-    (void)do_cmdline(buf, eap->getline, eap->cookie,
-				   DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED);
-#ifdef FEAT_EVAL
-    current_sctx = save_current_sctx;
-#endif
-    vim_free(buf);
-    vim_free(split_buf);
-}
-
-# if defined(FEAT_CMDL_COMPL) || defined(PROTO)
-    static char_u *
-get_user_command_name(int idx)
-{
-    return get_user_commands(NULL, idx - (int)CMD_SIZE);
-}
-
-/*
- * Function given to ExpandGeneric() to obtain the list of user command names.
- */
-    char_u *
-get_user_commands(expand_T *xp UNUSED, int idx)
-{
-    if (idx < curbuf->b_ucmds.ga_len)
-	return USER_CMD_GA(&curbuf->b_ucmds, idx)->uc_name;
-    idx -= curbuf->b_ucmds.ga_len;
-    if (idx < ucmds.ga_len)
-	return USER_CMD(idx)->uc_name;
-    return NULL;
-}
-
-/*
- * Function given to ExpandGeneric() to obtain the list of user address type names.
- */
-    char_u *
-get_user_cmd_addr_type(expand_T *xp UNUSED, int idx)
-{
-    return (char_u *)addr_type_complete[idx].name;
-}
-
-/*
- * Function given to ExpandGeneric() to obtain the list of user command
- * attributes.
- */
-    char_u *
-get_user_cmd_flags(expand_T *xp UNUSED, int idx)
-{
-    static char *user_cmd_flags[] =
-	{"addr", "bang", "bar", "buffer", "complete",
-	    "count", "nargs", "range", "register"};
-
-    if (idx >= (int)(sizeof(user_cmd_flags) / sizeof(user_cmd_flags[0])))
-	return NULL;
-    return (char_u *)user_cmd_flags[idx];
-}
-
-/*
- * Function given to ExpandGeneric() to obtain the list of values for -nargs.
- */
-    char_u *
-get_user_cmd_nargs(expand_T *xp UNUSED, int idx)
-{
-    static char *user_cmd_nargs[] = {"0", "1", "*", "?", "+"};
-
-    if (idx >= (int)(sizeof(user_cmd_nargs) / sizeof(user_cmd_nargs[0])))
-	return NULL;
-    return (char_u *)user_cmd_nargs[idx];
-}
-
-/*
- * Function given to ExpandGeneric() to obtain the list of values for -complete.
- */
-    char_u *
-get_user_cmd_complete(expand_T *xp UNUSED, int idx)
-{
-    return (char_u *)command_complete[idx].name;
-}
-# endif /* FEAT_CMDL_COMPL */
-
-/*
- * Parse address type argument
- */
-    int
-parse_addr_type_arg(
-    char_u	*value,
-    int		vallen,
-    long	*argt,
-    int		*addr_type_arg)
-{
-    int	    i, a, b;
-
-    for (i = 0; addr_type_complete[i].expand != -1; ++i)
-    {
-	a = (int)STRLEN(addr_type_complete[i].name) == vallen;
-	b = STRNCMP(value, addr_type_complete[i].name, vallen) == 0;
-	if (a && b)
-	{
-	    *addr_type_arg = addr_type_complete[i].expand;
-	    break;
-	}
-    }
-
-    if (addr_type_complete[i].expand == -1)
-    {
-	char_u	*err = value;
-
-	for (i = 0; err[i] != NUL && !VIM_ISWHITE(err[i]); i++)
-	    ;
-	err[i] = NUL;
-	semsg(_("E180: Invalid address type value: %s"), err);
-	return FAIL;
-    }
-
-    if (*addr_type_arg != ADDR_LINES)
-	*argt |= NOTADR;
-
-    return OK;
-}
-
-#endif	/* FEAT_USR_CMDS */
-
-#if defined(FEAT_USR_CMDS) || defined(FEAT_EVAL) || defined(PROTO)
-/*
- * Parse a completion argument "value[vallen]".
- * The detected completion goes in "*complp", argument type in "*argt".
- * When there is an argument, for function and user defined completion, it's
- * copied to allocated memory and stored in "*compl_arg".
- * Returns FAIL if something is wrong.
- */
-    int
-parse_compl_arg(
-    char_u	*value,
-    int		vallen,
-    int		*complp,
-    long	*argt,
-    char_u	**compl_arg UNUSED)
-{
-    char_u	*arg = NULL;
-# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
-    size_t	arglen = 0;
-# endif
-    int		i;
-    int		valend = vallen;
-
-    /* Look for any argument part - which is the part after any ',' */
-    for (i = 0; i < vallen; ++i)
-    {
-	if (value[i] == ',')
-	{
-	    arg = &value[i + 1];
-# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
-	    arglen = vallen - i - 1;
-# endif
-	    valend = i;
-	    break;
-	}
-    }
-
-    for (i = 0; command_complete[i].expand != 0; ++i)
-    {
-	if ((int)STRLEN(command_complete[i].name) == valend
-		&& STRNCMP(value, command_complete[i].name, valend) == 0)
-	{
-	    *complp = command_complete[i].expand;
-	    if (command_complete[i].expand == EXPAND_BUFFERS)
-		*argt |= BUFNAME;
-	    else if (command_complete[i].expand == EXPAND_DIRECTORIES
-		    || command_complete[i].expand == EXPAND_FILES)
-		*argt |= XFILE;
-	    break;
-	}
-    }
-
-    if (command_complete[i].expand == 0)
-    {
-	semsg(_("E180: Invalid complete value: %s"), value);
-	return FAIL;
-    }
-
-# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
-    if (*complp != EXPAND_USER_DEFINED && *complp != EXPAND_USER_LIST
-							       && arg != NULL)
-# else
-    if (arg != NULL)
-# endif
-    {
-	emsg(_("E468: Completion argument only allowed for custom completion"));
-	return FAIL;
-    }
-
-# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
-    if ((*complp == EXPAND_USER_DEFINED || *complp == EXPAND_USER_LIST)
-							       && arg == NULL)
-    {
-	emsg(_("E467: Custom completion requires a function argument"));
-	return FAIL;
-    }
-
-    if (arg != NULL)
-	*compl_arg = vim_strnsave(arg, (int)arglen);
-# endif
-    return OK;
-}
-
-    int
-cmdcomplete_str_to_type(char_u *complete_str)
-{
-    int i;
-
-    for (i = 0; command_complete[i].expand != 0; ++i)
-	if (STRCMP(complete_str, command_complete[i].name) == 0)
-	    return command_complete[i].expand;
-
-    return EXPAND_NOTHING;
-}
-#endif
-
     static void
 ex_colorscheme(exarg_T *eap)
 {