Patch 8.1.1210
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.
Files:      src/usercmd.c, src/proto/usercmd.pro, Filelist, src/Make_bc5.mak,
            src/Make_cyg_ming.mak, src/Make_dice.mak, src/Make_ivc.mak,
            src/Make_manx.mak, src/Make_morph.mak, src/Make_mvc.mak,
            src/Make_sas.mak, src/Make_vms.mms, src/Makefile, src/README.md,
            src/buffer.c, src/eval.c, src/evalfunc.c, src/ex_cmds.h,
            src/ex_docmd.c, src/proto/ex_docmd.pro, src/ex_getln.c,
            src/feature.h, src/macros.h, src/misc2.c, src/proto.h,
            src/structs.h, src/version.c, runtime/doc/eval.txt,
            runtime/doc/various.txt


*** ../vim-8.1.1209/src/usercmd.c       2019-04-27 12:58:18.994395422 +0200
--- src/usercmd.c       2019-04-27 12:54:00.423836989 +0200
***************
*** 0 ****
--- 1,1656 ----
+ /* vi:set ts=8 sts=4 sw=4 noet:
+  *
+  * VIM - Vi IMproved  by Bram Moolenaar
+  *
+  * Do ":help uganda"  in Vim to read copying and usage conditions.
+  * Do ":help credits" in Vim to see a list of people who contributed.
+  * See README.txt for an overview of the Vim source code.
+  */
+ 
+ /*
+  * usercmd.c: User defined command support
+  */
+ 
+ #include "vim.h"
+ 
+ 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;
+ 
+ // List of all user commands.
+ 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])
+ 
+ /*
+  * 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}
+ };
+ 
+ /*
+  * List of names of address types.  Must be alphabetical for completion.
+  */
+ 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}
+ };
+ 
+ #define UC_BUFFER     1       // -buffer: local to current buffer
+ 
+ /*
+  * 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.
+  */
+     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 UNUSED)  // 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;
+ }
+ 
+ #if defined(FEAT_CMDL_COMPL) || defined(PROTO)
+ 
+     char_u *
+ set_context_in_user_cmd(expand_T *xp, char_u *arg_in)
+ {
+     char_u    *arg = arg_in;
+     char_u    *p;
+ 
+     // 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;
+       return NULL;
+     }
+ 
+     // And finally comes a normal command
+     return skipwhite(p);
+ }
+ 
+     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;
+ }
+ 
+     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 // FEAT_CMDL_COMPL
+ 
+ /*
+  * List user commands starting with "name[name_len]".
+  */
+     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"));
+ }
+ 
+     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;
+ }
+ 
+ /*
+  * Parse address type argument
+  */
+     static 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;
+ }
+ 
+ /*
+  * 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;
+ }
+ 
+ /*
+  * Scan attributes in the ":command" command.
+  * Return FAIL when something is wrong.
+  */
+     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;
+ }
+ 
+ /*
+  * Add a user command to the list or replace an existing one.
+  */
+     static int
+ uc_add_command(
+     char_u    *name,
+     size_t    name_len,
+     char_u    *rep,
+     long      argt,
+     long      def,
+     int               flags,
+     int               compl,
+     char_u    *compl_arg UNUSED,
+     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
+ #ifdef FEAT_EVAL
+                   && (cmd->uc_script_ctx.sc_sid != current_sctx.sc_sid
+                         || cmd->uc_script_ctx.sc_seq == current_sctx.sc_seq)
+ #endif
+                   )
+           {
+               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;
+ }
+ 
+ /*
+  * ":command ..." implementation
+  */
+     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" implementation
+  * 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);
+ }
+ 
+ /*
+  * ":delcommand" implementation
+  */
+     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;
+ }
+ 
+ /*
+  * Execute a user defined command.
+  */
+     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);
+ }
*** ../vim-8.1.1209/src/proto/usercmd.pro       2019-04-27 12:58:18.998395402 
+0200
--- src/proto/usercmd.pro       2019-04-26 23:38:55.830203144 +0200
***************
*** 0 ****
--- 1,18 ----
+ /* usercmd.c */
+ char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int 
*compl);
+ char_u *set_context_in_user_cmd(expand_T *xp, char_u *arg_in);
+ char_u *get_user_command_name(int idx);
+ char_u *get_user_commands(expand_T *xp, int idx);
+ char_u *get_user_cmd_addr_type(expand_T *xp, int idx);
+ char_u *get_user_cmd_flags(expand_T *xp, int idx);
+ char_u *get_user_cmd_nargs(expand_T *xp, int idx);
+ char_u *get_user_cmd_complete(expand_T *xp, int idx);
+ char *uc_fun_cmd(void);
+ void ex_command(exarg_T *eap);
+ void ex_comclear(exarg_T *eap);
+ void uc_clear(garray_T *gap);
+ void ex_delcommand(exarg_T *eap);
+ void do_ucmd(exarg_T *eap);
+ int parse_compl_arg(char_u *value, int vallen, int *complp, long *argt, 
char_u **compl_arg);
+ int cmdcomplete_str_to_type(char_u *complete_str);
+ /* vim: set ft=c : */
*** ../vim-8.1.1209/Filelist    2019-04-21 11:34:36.331256556 +0200
--- Filelist    2019-04-26 22:48:16.009112851 +0200
***************
*** 98,103 ****
--- 98,104 ----
                src/textprop.c \
                src/ui.c \
                src/undo.c \
+               src/usercmd.c \
                src/userfunc.c \
                src/version.c \
                src/version.h \
***************
*** 212,217 ****
--- 213,219 ----
                src/proto/textprop.pro \
                src/proto/ui.pro \
                src/proto/undo.pro \
+               src/proto/usercmd.pro \
                src/proto/userfunc.pro \
                src/proto/version.pro \
                src/proto/winclip.pro \
*** ../vim-8.1.1209/src/Make_bc5.mak    2019-04-21 11:34:36.331256556 +0200
--- src/Make_bc5.mak    2019-04-26 22:59:10.810260994 +0200
***************
*** 565,570 ****
--- 565,571 ----
        $(OBJDIR)\term.obj \
        $(OBJDIR)\ui.obj \
        $(OBJDIR)\undo.obj \
+       $(OBJDIR)\usercmd.obj \
        $(OBJDIR)\userfunc.obj \
        $(OBJDIR)\version.obj \
        $(OBJDIR)\window.obj \
*** ../vim-8.1.1209/src/Make_cyg_ming.mak       2019-04-21 11:34:36.331256556 
+0200
--- src/Make_cyg_ming.mak       2019-04-26 22:59:26.026194275 +0200
***************
*** 757,762 ****
--- 757,763 ----
        $(OUTDIR)/textprop.o \
        $(OUTDIR)/ui.o \
        $(OUTDIR)/undo.o \
+       $(OUTDIR)/usercmd.o \
        $(OUTDIR)/userfunc.o \
        $(OUTDIR)/version.o \
        $(OUTDIR)/vimrc.o \
*** ../vim-8.1.1209/src/Make_dice.mak   2019-04-21 11:34:36.331256556 +0200
--- src/Make_dice.mak   2019-04-26 22:59:54.982067281 +0200
***************
*** 83,88 ****
--- 83,89 ----
        term.c \
        ui.c \
        undo.c \
+       usercmd.c \
        userfunc.c \
        window.c \
        version.c
***************
*** 144,149 ****
--- 145,151 ----
        o/term.o \
        o/ui.o \
        o/undo.o \
+       o/usercmd.o \
        o/userfunc.o \
        o/window.o \
        $(TERMLIB)
***************
*** 288,293 ****
--- 290,297 ----
  
  o/undo.o:     undo.c  $(SYMS)
  
+ o/usercmd.o:  usercmd.c  $(SYMS)
+ 
  o/userfunc.o:         userfunc.c  $(SYMS)
  
  o/window.o:   window.c  $(SYMS)
*** ../vim-8.1.1209/src/Make_ivc.mak    2019-04-21 11:34:36.331256556 +0200
--- src/Make_ivc.mak    2019-04-26 23:00:12.165991891 +0200
***************
*** 269,274 ****
--- 269,275 ----
        "$(INTDIR)/term.obj" \
        "$(INTDIR)/ui.obj" \
        "$(INTDIR)/undo.obj" \
+       "$(INTDIR)/usercmd.obj" \
        "$(INTDIR)/userfunc.obj" \
        "$(INTDIR)/version.obj" \
        "$(INTDIR)/window.obj"
***************
*** 728,733 ****
--- 729,738 ----
  # End Source File
  # Begin Source File
  
+ SOURCE=.\usercmd.c
+ # End Source File
+ # Begin Source File
+ 
  SOURCE=.\userfunc.c
  # End Source File
  # Begin Source File
*** ../vim-8.1.1209/src/Make_manx.mak   2019-04-21 11:34:36.331256556 +0200
--- src/Make_manx.mak   2019-04-26 23:00:42.393859245 +0200
***************
*** 93,98 ****
--- 93,99 ----
        term.c \
        ui.c \
        undo.c \
+       usercmd.c \
        userfunc.c \
        window.c \
        version.c
***************
*** 156,161 ****
--- 157,163 ----
        obj/term.o \
        obj/ui.o \
        obj/undo.o \
+       obj/usercmd.o \
        obj/userfunc.o \
        obj/window.o \
        $(TERMLIB)
***************
*** 218,223 ****
--- 220,226 ----
        proto/termlib.pro \
        proto/ui.pro \
        proto/undo.pro \
+       proto/usercmd.pro \
        proto/userfunc.pro \
        proto/window.pro
  
***************
*** 443,448 ****
--- 446,454 ----
  obj/undo.o:   undo.c
        $(CCSYM) $@ undo.c
  
+ obj/usercmd.o:        usercmd.c
+       $(CCSYM) $@ usercmd.c
+ 
  obj/userfunc.o:       userfunc.c
        $(CCSYM) $@ userfunc.c
  
*** ../vim-8.1.1209/src/Make_morph.mak  2019-04-21 11:34:36.331256556 +0200
--- src/Make_morph.mak  2019-04-26 23:00:55.717800764 +0200
***************
*** 81,86 ****
--- 81,87 ----
        term.c                                                  \
        ui.c                                                    \
        undo.c                                                  \
+       usercmd.c                                               \
        userfunc.c                                              \
        version.c                                               \
        window.c                                                \
*** ../vim-8.1.1209/src/Make_mvc.mak    2019-04-21 11:34:36.331256556 +0200
--- src/Make_mvc.mak    2019-04-26 23:01:17.701704254 +0200
***************
*** 765,770 ****
--- 765,771 ----
        $(OUTDIR)\textprop.obj \
        $(OUTDIR)\ui.obj \
        $(OUTDIR)\undo.obj \
+       $(OUTDIR)\usercmd.obj \
        $(OUTDIR)\userfunc.obj \
        $(OUTDIR)\winclip.obj \
        $(OUTDIR)\window.obj \
***************
*** 1550,1555 ****
--- 1551,1558 ----
  
  $(OUTDIR)/undo.obj:   $(OUTDIR) undo.c  $(INCL)
  
+ $(OUTDIR)/usercmd.obj:        $(OUTDIR) usercmd.c  $(INCL)
+ 
  $(OUTDIR)/userfunc.obj:       $(OUTDIR) userfunc.c  $(INCL)
  
  $(OUTDIR)/window.obj: $(OUTDIR) window.c  $(INCL)
***************
*** 1693,1698 ****
--- 1696,1702 ----
        proto/textprop.pro \
        proto/ui.pro \
        proto/undo.pro \
+       proto/usercmd.pro \
        proto/userfunc.pro \
        proto/window.pro \
        $(NETBEANS_PRO) \
*** ../vim-8.1.1209/src/Make_sas.mak    2019-04-21 11:34:36.335256531 +0200
--- src/Make_sas.mak    2019-04-26 23:01:55.393538739 +0200
***************
*** 146,151 ****
--- 146,152 ----
        term.c \
        ui.c \
        undo.c \
+       usercmd.c \
        userfunc.c \
        window.c \
        version.c
***************
*** 208,213 ****
--- 209,215 ----
        term.o \
        ui.o \
        undo.o \
+       usercmd.o \
        userfunc.o \
        window.o \
        $(TERMLIB)
***************
*** 271,276 ****
--- 273,279 ----
        proto/termlib.pro \
        proto/ui.pro \
        proto/undo.pro \
+       proto/usercmd.pro \
        proto/userfunc.pro \
        proto/window.pro
  
***************
*** 445,450 ****
--- 448,455 ----
  proto/ui.pro:         ui.c
  undo.o:                       undo.c
  proto/undo.pro:               undo.c
+ usercmd.o:            usercmd.c
+ proto/usercmd.pro:    usercmd.c
  userfunc.o:           userfunc.c
  proto/userfunc.pro:   userfunc.c
  window.o:             window.c
*** ../vim-8.1.1209/src/Make_vms.mms    2019-04-21 11:34:36.335256531 +0200
--- src/Make_vms.mms    2019-04-26 23:02:58.693260644 +0200
***************
*** 2,8 ****
  # Makefile for Vim on OpenVMS
  #
  # Maintainer:   Zoltan Arpadffy <arpad...@polarhome.com>
! # Last change:  2019 Mar 22
  #
  # This has script been tested on VMS 6.2 to 8.2 on DEC Alpha, VAX and IA64
  # with MMS and MMK
--- 2,8 ----
  # Makefile for Vim on OpenVMS
  #
  # Maintainer:   Zoltan Arpadffy <arpad...@polarhome.com>
! # Last change:  2019 Apr 26
  #
  # This has script been tested on VMS 6.2 to 8.2 on DEC Alpha, VAX and IA64
  # with MMS and MMK
***************
*** 315,322 ****
        menu.c mbyte.c memfile.c memline.c message.c misc1.c misc2.c move.c \
        normal.c ops.c option.c popupmnu.c quickfix.c regexp.c search.c \
        sha256.c sign.c spell.c spellfile.c syntax.c tag.c term.c termlib.c \
!       textprop.c ui.c undo.c userfunc.c version.c screen.c window.c \
!       os_unix.c os_vms.c pathdef.c \
        $(GUI_SRC) $(PERL_SRC) $(PYTHON_SRC) $(TCL_SRC) \
        $(RUBY_SRC) $(HANGULIN_SRC) $(MZSCH_SRC) $(XDIFF_SRC)
  
--- 315,322 ----
        menu.c mbyte.c memfile.c memline.c message.c misc1.c misc2.c move.c \
        normal.c ops.c option.c popupmnu.c quickfix.c regexp.c search.c \
        sha256.c sign.c spell.c spellfile.c syntax.c tag.c term.c termlib.c \
!       textprop.c ui.c undo.c usercmd.c userfunc.c version.c screen.c \
!       window.c os_unix.c os_vms.c pathdef.c \
        $(GUI_SRC) $(PERL_SRC) $(PYTHON_SRC) $(TCL_SRC) \
        $(RUBY_SRC) $(HANGULIN_SRC) $(MZSCH_SRC) $(XDIFF_SRC)
  
***************
*** 330,336 ****
        move.obj mbyte.obj normal.obj ops.obj option.obj popupmnu.obj \
        quickfix.obj regexp.obj search.obj sha256.obj sign.obj spell.obj \
        spellfile.obj syntax.obj tag.obj term.obj termlib.obj textprop.obj \
!       ui.obj undo.obj userfunc.obj screen.obj version.obj \
        window.obj os_unix.obj os_vms.obj pathdef.obj if_mzsch.obj \
        $(GUI_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(TCL_OBJ) \
        $(RUBY_OBJ) $(HANGULIN_OBJ) $(MZSCH_OBJ) $(XDIFF_OBJ)
--- 330,336 ----
        move.obj mbyte.obj normal.obj ops.obj option.obj popupmnu.obj \
        quickfix.obj regexp.obj search.obj sha256.obj sign.obj spell.obj \
        spellfile.obj syntax.obj tag.obj term.obj termlib.obj textprop.obj \
!       ui.obj undo.obj usercmd.obj userfunc.obj screen.obj version.obj \
        window.obj os_unix.obj os_vms.obj pathdef.obj if_mzsch.obj \
        $(GUI_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(TCL_OBJ) \
        $(RUBY_OBJ) $(HANGULIN_OBJ) $(MZSCH_OBJ) $(XDIFF_OBJ)
***************
*** 744,753 ****
--- 744,759 ----
   ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \
   [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \
  
+ usercmd.obj : usercmd.c vim.h [.auto]config.h feature.h os_unix.h \
+  ascii.h keymap.h term.h macros.h option.h structs.h \
+  regexp.h gui.h beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h \
+  proto.h globals.h
+ 
  userfunc.obj : userfunc.c vim.h [.auto]config.h feature.h os_unix.h \
   ascii.h keymap.h term.h macros.h option.h structs.h \
   regexp.h gui.h beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h \
   proto.h globals.h
+ 
  version.obj : version.c vim.h [.auto]config.h feature.h os_unix.h \
   ascii.h keymap.h term.h macros.h structs.h regexp.h \
   gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \
*** ../vim-8.1.1209/src/Makefile        2019-04-25 20:28:53.327979592 +0200
--- src/Makefile        2019-04-26 23:03:46.865048917 +0200
***************
*** 1635,1640 ****
--- 1635,1641 ----
        textprop.c \
        ui.c \
        undo.c \
+       usercmd.c \
        userfunc.c \
        version.c \
        window.c \
***************
*** 1747,1752 ****
--- 1748,1754 ----
        objects/textprop.o \
        objects/ui.o \
        objects/undo.o \
+       objects/usercmd.o \
        objects/userfunc.o \
        objects/version.o \
        objects/window.o \
***************
*** 1885,1890 ****
--- 1887,1893 ----
        textprop.pro \
        ui.pro \
        undo.pro \
+       usercmd.pro \
        userfunc.pro \
        version.pro \
        window.pro \
***************
*** 3242,3247 ****
--- 3245,3253 ----
  objects/undo.o: undo.c
        $(CCC) -o $@ undo.c
  
+ objects/usercmd.o: usercmd.c
+       $(CCC) -o $@ usercmd.c
+ 
  objects/userfunc.o: userfunc.c
        $(CCC) -o $@ userfunc.c
  
***************
*** 3657,3662 ****
--- 3663,3672 ----
   auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
   proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
   proto.h globals.h
+ objects/usercmd.o: usercmd.c vim.h protodef.h auto/config.h feature.h 
os_unix.h \
+  auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
+  proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
+  proto.h globals.h
  objects/userfunc.o: userfunc.c vim.h protodef.h auto/config.h feature.h 
os_unix.h \
   auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
   proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
*** ../vim-8.1.1209/src/README.md       2019-04-26 21:31:34.019272940 +0200
--- src/README.md       2019-04-26 23:05:11.488676798 +0200
***************
*** 28,33 ****
--- 28,34 ----
  debugger.c    | vim script debugger
  diff.c                | diff mode (vimdiff)
  eval.c                | expression evaluation
+ evalfunc.c    | built-in functions
  fileio.c      | reading and writing files
  findfile.c    | search for files in 'path'
  fold.c                | folding
***************
*** 40,46 ****
  memline.c     | storing lines for buffers in memory
  menu.c                | menus
  message.c     | (error) messages
! ops.c           | handling operators ("d", "y", "p")
  option.c      | options
  quickfix.c    | quickfix commands (":make", ":cn")
  regexp.c      | pattern matching
--- 41,47 ----
  memline.c     | storing lines for buffers in memory
  menu.c                | menus
  message.c     | (error) messages
! ops.c         | handling operators ("d", "y", "p")
  option.c      | options
  quickfix.c    | quickfix commands (":make", ":cn")
  regexp.c      | pattern matching
***************
*** 49,57 ****
  sign.c                | signs
  spell.c               | spell checking
  syntax.c      |  syntax and other highlighting
! tag.c           | tags
  term.c                | terminal handling, termcap codes
  undo.c                | undo and redo
  window.c      | handling split windows
  
  
--- 50,60 ----
  sign.c                | signs
  spell.c               | spell checking
  syntax.c      |  syntax and other highlighting
! tag.c         | tags
  term.c                | terminal handling, termcap codes
  undo.c                | undo and redo
+ usercmd.c     | user defined commands
+ userfunc.c    | user defined functions
  window.c      | handling split windows
  
  
*** ../vim-8.1.1209/src/buffer.c        2019-04-07 14:19:06.323149516 +0200
--- src/buffer.c        2019-04-26 23:07:09.612157048 +0200
***************
*** 925,935 ****
        CHANGEDTICK(buf) = tick;
      }
  #endif
! #ifdef FEAT_USR_CMDS
!     uc_clear(&buf->b_ucmds);          /* clear local user commands */
! #endif
  #ifdef FEAT_SIGNS
!     buf_delete_signs(buf, (char_u *)"*");     // delete any signs */
  #endif
  #ifdef FEAT_NETBEANS_INTG
      netbeans_file_killed(buf);
--- 925,933 ----
        CHANGEDTICK(buf) = tick;
      }
  #endif
!     uc_clear(&buf->b_ucmds);          // clear local user commands
  #ifdef FEAT_SIGNS
!     buf_delete_signs(buf, (char_u *)"*");     // delete any signs
  #endif
  #ifdef FEAT_NETBEANS_INTG
      netbeans_file_killed(buf);
*** ../vim-8.1.1209/src/eval.c  2019-04-26 20:32:57.082296551 +0200
--- src/eval.c  2019-04-26 23:08:08.499897821 +0200
***************
*** 1120,1129 ****
      return retval;
  }
  
! #if (defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL)) \
        || defined(FEAT_COMPL_FUNC) || defined(PROTO)
  
! # if (defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL)) || defined(PROTO)
  /*
   * Call Vim script function "func" and return the result as a string.
   * Returns NULL when calling the function fails.
--- 1120,1129 ----
      return retval;
  }
  
! #if defined(FEAT_CMDL_COMPL) \
        || defined(FEAT_COMPL_FUNC) || defined(PROTO)
  
! # if defined(FEAT_CMDL_COMPL) || defined(PROTO)
  /*
   * Call Vim script function "func" and return the result as a string.
   * Returns NULL when calling the function fails.
*** ../vim-8.1.1209/src/evalfunc.c      2019-04-26 22:33:44.896723710 +0200
--- src/evalfunc.c      2019-04-26 23:08:24.535827217 +0200
***************
*** 6611,6620 ****
  #if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
        "unnamedplus",
  #endif
- #ifdef FEAT_USR_CMDS
        "user-commands",    /* was accidentally included in 5.4 */
        "user_commands",
- #endif
  #ifdef FEAT_VARTABS
        "vartabs",
  #endif
--- 6611,6618 ----
*** ../vim-8.1.1209/src/ex_cmds.h       2019-04-04 18:15:05.766857086 +0200
--- src/ex_cmds.h       2019-04-26 23:27:26.194430566 +0200
***************
*** 1753,1765 ****
                        ADDR_LINES),
  
  #ifndef DO_DECLARE_EXCMD
- #ifdef FEAT_USR_CMDS
      CMD_SIZE,         /* MUST be after all real commands! */
      CMD_USER = -1,    /* User-defined command */
      CMD_USER_BUF = -2 /* User-defined command local to buffer */
- #else
-     CMD_SIZE  /* MUST be the last one! */
- #endif
  #endif
  };
  
--- 1753,1761 ----
***************
*** 1795,1803 ****
      int               force_ff;       /* ++ff= argument (first char of 
argument) */
      int               force_enc;      /* ++enc= argument (index in cmd[]) */
      int               bad_char;       /* BAD_KEEP, BAD_DROP or replacement 
byte */
- #ifdef FEAT_USR_CMDS
      int               useridx;        /* user command index */
- #endif
      char      *errmsg;        /* returned error message */
      char_u    *(*getline)(int, void *, int);
      void      *cookie;        /* argument for getline() */
--- 1791,1797 ----
*** ../vim-8.1.1209/src/ex_docmd.c      2019-04-25 21:27:40.562186830 +0200
--- src/ex_docmd.c      2019-04-27 12:51:27.040691361 +0200
***************
*** 19,66 ****
  # 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
--- 19,24 ----
***************
*** 300,309 ****
  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
--- 258,263 ----
***************
*** 1929,1949 ****
                ) ? 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. */
      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)
--- 1883,1902 ----
                ) ? find_command(&ea, NULL) : ea.cmd;
      }
  
      if (p == NULL)
      {
        if (!ea.skip)
            errormsg = _("E464: Ambiguous use of user-defined command");
        goto doend;
      }
!     // 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;
      }
! 
      if (ea.cmdidx == CMD_SIZE)
      {
        if (!ea.skip)
***************
*** 2508,2514 ****
   * 7. Execute the command.
   */
  
- #ifdef FEAT_USR_CMDS
      if (IS_USER_CMDIDX(ea.cmdidx))
      {
        /*
--- 2461,2466 ----
***************
*** 2517,2526 ****
        do_ucmd(&ea);
      }
      else
- #endif
      {
        /*
!        * Call the function to execute the command.
         */
        ea.errmsg = NULL;
        (cmdnames[ea.cmdidx].cmd_func)(&ea);
--- 2469,2477 ----
        do_ucmd(&ea);
      }
      else
      {
        /*
!        * Call the function to execute the builtin command.
         */
        ea.errmsg = NULL;
        (cmdnames[ea.cmdidx].cmd_func)(&ea);
***************
*** 3235,3252 ****
                break;
            }
  
! #ifdef FEAT_USR_CMDS
!       /* 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. */
            while (ASCII_ISALNUM(*p))
                ++p;
            p = find_ucmd(eap, p, full, NULL, NULL);
        }
- #endif
        if (p == eap->cmd)
            eap->cmdidx = CMD_SIZE;
      }
--- 3186,3201 ----
                break;
            }
  
!       // 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.
            while (ASCII_ISALNUM(*p))
                ++p;
            p = find_ucmd(eap, p, full, NULL, NULL);
        }
        if (p == eap->cmd)
            eap->cmdidx = CMD_SIZE;
      }
***************
*** 3254,3377 ****
      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
  {
--- 3203,3208 ----
***************
*** 3483,3492 ****
      char_u            *cmd, *arg;
      int                       len = 0;
      exarg_T           ea;
- #if defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL)
-     int                       compl = EXPAND_NOTHING;
- #endif
  #ifdef FEAT_CMDL_COMPL
      int                       delim;
  #endif
      int                       forceit = FALSE;
--- 3314,3321 ----
      char_u            *cmd, *arg;
      int                       len = 0;
      exarg_T           ea;
  #ifdef FEAT_CMDL_COMPL
+     int                       compl = EXPAND_NOTHING;
      int                       delim;
  #endif
      int                       forceit = FALSE;
***************
*** 3572,3582 ****
                                                            (size_t)len) == 0)
                break;
  
- #ifdef FEAT_USR_CMDS
        if (cmd[0] >= 'A' && cmd[0] <= 'Z')
!           while (ASCII_ISALNUM(*p) || *p == '*')      /* Allow * wild card */
                ++p;
- #endif
      }
  
      /*
--- 3401,3409 ----
                                                            (size_t)len) == 0)
                break;
  
        if (cmd[0] >= 'A' && cmd[0] <= 'Z')
!           while (ASCII_ISALNUM(*p) || *p == '*')      // Allow * wild card
                ++p;
      }
  
      /*
***************
*** 3593,3613 ****
            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)
                    &compl
! # else
                    NULL
! # endif
                    );
            if (p == NULL)
!               ea.cmdidx = CMD_SIZE;   /* ambiguous user command */
        }
- #endif
      }
      if (ea.cmdidx == CMD_SIZE)
      {
--- 3420,3438 ----
            ea.cmdidx = CMD_substitute;
            p = cmd + 1;
        }
        else if (cmd[0] >= 'A' && cmd[0] <= 'Z')
        {
            ea.cmd = cmd;
            p = find_ucmd(&ea, p, NULL, xp,
! #if defined(FEAT_CMDL_COMPL)
                    &compl
! #else
                    NULL
! #endif
                    );
            if (p == NULL)
!               ea.cmdidx = CMD_SIZE;   // ambiguous user command
        }
      }
      if (ea.cmdidx == CMD_SIZE)
      {
***************
*** 3828,3834 ****
            {
                xp->xp_context = EXPAND_ENV_VARS;
                ++xp->xp_pattern;
! #if defined(FEAT_USR_CMDS) && 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;
--- 3653,3659 ----
            {
                xp->xp_context = EXPAND_ENV_VARS;
                ++xp->xp_pattern;
! #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,4011 ****
   * 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);
  
        case CMD_delcommand:
            xp->xp_context = EXPAND_USER_COMMANDS;
            xp->xp_pattern = arg;
            break;
- # endif
  
        case CMD_global:
        case CMD_vglobal:
--- 3769,3781 ----
   * All completion for the +cmdline_compl feature goes here.
   */
  
        case CMD_command:
!           return set_context_in_user_cmd(xp, arg);
  
        case CMD_delcommand:
            xp->xp_context = EXPAND_USER_COMMANDS;
            xp->xp_pattern = arg;
            break;
  
        case CMD_global:
        case CMD_vglobal:
***************
*** 4186,4217 ****
            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 */
                if (!(ea.argt & XFILE))
                {
! # ifdef FEAT_MENU
                    if (compl == EXPAND_MENUS)
                        return set_context_in_menu_cmd(xp, cmd, arg, forceit);
! # 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. */
                    p = arg;
                    while (*p)
                    {
                        if (*p == ' ')
!                           /* argument starts after a space */
                            arg = p + 1;
                        else if (*p == '\\' && *(p + 1) != NUL)
!                           ++p; /* skip over escaped character */
                        MB_PTR_ADV(p);
                    }
                    xp->xp_pattern = arg;
--- 3956,3987 ----
            xp->xp_context = EXPAND_BUFFERS;
            xp->xp_pattern = arg;
            break;
! 
        case CMD_USER:
        case CMD_USER_BUF:
            if (compl != EXPAND_NOTHING)
            {
!               // XFILE: file names are handled above
                if (!(ea.argt & XFILE))
                {
! #ifdef FEAT_MENU
                    if (compl == EXPAND_MENUS)
                        return set_context_in_menu_cmd(xp, cmd, arg, forceit);
! #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.
                    p = arg;
                    while (*p)
                    {
                        if (*p == ' ')
!                           // argument starts after a space
                            arg = p + 1;
                        else if (*p == '\\' && *(p + 1) != NUL)
!                           ++p; // skip over escaped character
                        MB_PTR_ADV(p);
                    }
                    xp->xp_pattern = arg;
***************
*** 4219,4225 ****
                xp->xp_context = compl;
            }
            break;
! #endif
        case CMD_map:       case CMD_noremap:
        case CMD_nmap:      case CMD_nnoremap:
        case CMD_vmap:      case CMD_vnoremap:
--- 3989,3995 ----
                xp->xp_context = compl;
            }
            break;
! 
        case CMD_map:       case CMD_noremap:
        case CMD_nmap:      case CMD_nnoremap:
        case CMD_vmap:      case CMD_vnoremap:
***************
*** 5771,5777 ****
      return OK;
  }
  
! #ifdef FEAT_CMDL_COMPL
  /*
   * Function given to ExpandGeneric() to obtain the list of command names.
   */
--- 5541,5547 ----
      return OK;
  }
  
! #if defined(FEAT_CMDL_COMPL) || defined(PROTO)
  /*
   * Function given to ExpandGeneric() to obtain the list of command names.
   */
***************
*** 5779,7218 ****
  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)
  {
--- 5549,5559 ----
*** ../vim-8.1.1209/src/proto/ex_docmd.pro      2019-01-13 23:38:33.407773189 
+0100
--- src/proto/ex_docmd.pro      2019-04-26 23:39:53.321890880 +0200
***************
*** 19,34 ****
  char_u *find_nextcmd(char_u *p);
  char_u *check_nextcmd(char_u *p);
  char_u *get_command_name(expand_T *xp, int idx);
- void ex_comclear(exarg_T *eap);
- void uc_clear(garray_T *gap);
- char_u *get_user_commands(expand_T *xp, int idx);
- char_u *get_user_cmd_addr_type(expand_T *xp, int idx);
- char_u *get_user_cmd_flags(expand_T *xp, int idx);
- char_u *get_user_cmd_nargs(expand_T *xp, int idx);
- char_u *get_user_cmd_complete(expand_T *xp, int idx);
- int parse_addr_type_arg(char_u *value, int vallen, long *argt, int 
*addr_type_arg);
- int parse_compl_arg(char_u *value, int vallen, int *complp, long *argt, 
char_u **compl_arg);
- int cmdcomplete_str_to_type(char_u *complete_str);
  void not_exiting(void);
  void tabpage_close(int forceit);
  void tabpage_close_other(tabpage_T *tp, int forceit);
--- 19,24 ----
*** ../vim-8.1.1209/src/ex_getln.c      2019-04-11 13:45:53.125298538 +0200
--- src/ex_getln.c      2019-04-26 23:26:22.726894948 +0200
***************
*** 111,117 ****
  # ifdef FEAT_CMDHIST
  static char_u *get_history_arg(expand_T *xp, int idx);
  # endif
! # if defined(FEAT_USR_CMDS) && defined(FEAT_EVAL)
  static int    ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int 
*num_file, char_u ***file);
  static int    ExpandUserList(expand_T *xp, int *num_file, char_u ***file);
  # endif
--- 111,117 ----
  # ifdef FEAT_CMDHIST
  static char_u *get_history_arg(expand_T *xp, int idx);
  # endif
! # if defined(FEAT_EVAL)
  static int    ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int 
*num_file, char_u ***file);
  static int    ExpandUserList(expand_T *xp, int *num_file, char_u ***file);
  # endif
***************
*** 939,945 ****
      {
        xpc.xp_context = ccline.xp_context;
        xpc.xp_pattern = ccline.cmdbuff;
! # if defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL)
        xpc.xp_arg = ccline.xp_arg;
  # endif
      }
--- 939,945 ----
      {
        xpc.xp_context = ccline.xp_context;
        xpc.xp_pattern = ccline.cmdbuff;
! # if defined(FEAT_CMDL_COMPL)
        xpc.xp_arg = ccline.xp_arg;
  # endif
      }
***************
*** 4210,4216 ****
  #endif
      xp->xp_numfiles = -1;
      xp->xp_files = NULL;
! #if defined(FEAT_USR_CMDS) && defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
      xp->xp_arg = NULL;
  #endif
      xp->xp_line = NULL;
--- 4210,4216 ----
  #endif
      xp->xp_numfiles = -1;
      xp->xp_files = NULL;
! #if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
      xp->xp_arg = NULL;
  #endif
      xp->xp_line = NULL;
***************
*** 4879,4885 ****
      {
        xp->xp_context = ccline.xp_context;
        xp->xp_pattern = ccline.cmdbuff;
! # if defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL)
        xp->xp_arg = ccline.xp_arg;
  # endif
      }
--- 4879,4885 ----
      {
        xp->xp_context = ccline.xp_context;
        xp->xp_pattern = ccline.cmdbuff;
! # if defined(FEAT_CMDL_COMPL)
        xp->xp_arg = ccline.xp_arg;
  # endif
      }
***************
*** 5130,5136 ****
        char *directories[] = {"syntax", "indent", "ftplugin", NULL};
        return ExpandRTDir(pat, 0, num_file, file, directories);
      }
! # if defined(FEAT_USR_CMDS) && defined(FEAT_EVAL)
      if (xp->xp_context == EXPAND_USER_LIST)
        return ExpandUserList(xp, num_file, file);
  # endif
--- 5130,5136 ----
        char *directories[] = {"syntax", "indent", "ftplugin", NULL};
        return ExpandRTDir(pat, 0, num_file, file, directories);
      }
! # if defined(FEAT_EVAL)
      if (xp->xp_context == EXPAND_USER_LIST)
        return ExpandUserList(xp, num_file, file);
  # endif
***************
*** 5149,5155 ****
        ret = ExpandSettings(xp, &regmatch, num_file, file);
      else if (xp->xp_context == EXPAND_MAPPINGS)
        ret = ExpandMappings(&regmatch, num_file, file);
! # if defined(FEAT_USR_CMDS) && defined(FEAT_EVAL)
      else if (xp->xp_context == EXPAND_USER_DEFINED)
        ret = ExpandUserDefined(xp, &regmatch, num_file, file);
  # endif
--- 5149,5155 ----
        ret = ExpandSettings(xp, &regmatch, num_file, file);
      else if (xp->xp_context == EXPAND_MAPPINGS)
        ret = ExpandMappings(&regmatch, num_file, file);
! # if defined(FEAT_EVAL)
      else if (xp->xp_context == EXPAND_USER_DEFINED)
        ret = ExpandUserDefined(xp, &regmatch, num_file, file);
  # endif
***************
*** 5170,5182 ****
  #ifdef FEAT_CMDHIST
            {EXPAND_HISTORY, get_history_arg, TRUE, TRUE},
  #endif
- #ifdef FEAT_USR_CMDS
            {EXPAND_USER_COMMANDS, get_user_commands, FALSE, TRUE},
            {EXPAND_USER_ADDR_TYPE, get_user_cmd_addr_type, FALSE, TRUE},
            {EXPAND_USER_CMD_FLAGS, get_user_cmd_flags, FALSE, TRUE},
            {EXPAND_USER_NARGS, get_user_cmd_nargs, FALSE, TRUE},
            {EXPAND_USER_COMPLETE, get_user_cmd_complete, FALSE, TRUE},
- #endif
  #ifdef FEAT_EVAL
            {EXPAND_USER_VARS, get_user_var_name, FALSE, TRUE},
            {EXPAND_FUNCTIONS, get_function_name, FALSE, TRUE},
--- 5170,5180 ----
***************
*** 5473,5479 ****
  }
  
  
! # if defined(FEAT_USR_CMDS) && defined(FEAT_EVAL)
  /*
   * Call "user_expand_func()" to invoke a user defined Vim script function and
   * return the result (either a string or a List).
--- 5471,5477 ----
  }
  
  
! # if defined(FEAT_EVAL)
  /*
   * Call "user_expand_func()" to invoke a user defined Vim script function and
   * return the result (either a string or a List).
*** ../vim-8.1.1209/src/feature.h       2019-03-30 21:19:16.426170240 +0100
--- src/feature.h       2019-04-26 23:27:34.074374224 +0200
***************
*** 379,388 ****
  
  /*
   * +user_commands     Allow the user to define his own commands.
   */
- #ifdef FEAT_NORMAL
- # define FEAT_USR_CMDS
- #endif
  
  /*
   * +printer           ":hardcopy" command
--- 379,386 ----
  
  /*
   * +user_commands     Allow the user to define his own commands.
+  *                    Now always enabled.
   */
  
  /*
   * +printer           ":hardcopy" command
*** ../vim-8.1.1209/src/macros.h        2019-03-30 18:46:57.356077354 +0100
--- src/macros.h        2019-04-26 23:12:15.806808513 +0200
***************
*** 336,338 ****
--- 336,341 ----
            (p) = NULL; \
        } \
      } while (0)
+ 
+ /* Wether a command index indicates a user command. */
+ #define IS_USER_CMDIDX(idx) ((int)(idx) < 0)
*** ../vim-8.1.1209/src/misc2.c 2019-03-30 13:53:26.174425093 +0100
--- src/misc2.c 2019-04-26 23:51:39.458250238 +0200
***************
*** 1082,1091 ****
      ui_remove_balloon();
  # endif
  
! # if defined(FEAT_USR_CMDS)
!     /* Clear user commands (before deleting buffers). */
      ex_comclear(NULL);
- # endif
  
  # ifdef FEAT_MENU
      /* Clear menus. */
--- 1082,1089 ----
      ui_remove_balloon();
  # endif
  
!     // Clear user commands (before deleting buffers).
      ex_comclear(NULL);
  
  # ifdef FEAT_MENU
      /* Clear menus. */
***************
*** 1130,1136 ****
--- 1128,1136 ----
      free_search_patterns();
      free_old_sub();
      free_last_insert();
+ # if defined(FEAT_INS_EXPAND)
      free_insexpand_stuff();
+ # endif
      free_prev_shellcmd();
      free_regexp_stuff();
      free_tag_stuff();
*** ../vim-8.1.1209/src/proto.h 2019-04-21 11:34:36.335256531 +0200
--- src/proto.h 2019-04-26 23:05:42.872538740 +0200
***************
*** 227,232 ****
--- 227,233 ----
  # endif
  # include "ui.pro"
  # include "undo.pro"
+ # include "usercmd.pro"
  # include "userfunc.pro"
  # include "version.pro"
  # include "window.pro"
*** ../vim-8.1.1209/src/structs.h       2019-04-25 22:21:56.931749183 +0200
--- src/structs.h       2019-04-26 23:28:26.458006469 +0200
***************
*** 549,555 ****
      int               xp_context;             /* type of expansion */
      char_u    *xp_pattern;            /* start of item to expand */
      int               xp_pattern_len;         /* bytes in xp_pattern before 
cursor */
! #if defined(FEAT_USR_CMDS) && defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
      char_u    *xp_arg;                /* completion function */
      sctx_T    xp_script_ctx;          /* SCTX for completion function */
  #endif
--- 549,555 ----
      int               xp_context;             /* type of expansion */
      char_u    *xp_pattern;            /* start of item to expand */
      int               xp_pattern_len;         /* bytes in xp_pattern before 
cursor */
! #if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
      char_u    *xp_arg;                /* completion function */
      sctx_T    xp_script_ctx;          /* SCTX for completion function */
  #endif
***************
*** 2143,2152 ****
      /* First abbreviation local to a buffer. */
      mapblock_T        *b_first_abbr;
  #endif
! #ifdef FEAT_USR_CMDS
!     /* User commands local to the buffer. */
      garray_T  b_ucmds;
- #endif
      /*
       * start and end of an operator, also used for '[ and ']
       */
--- 2143,2150 ----
      /* First abbreviation local to a buffer. */
      mapblock_T        *b_first_abbr;
  #endif
!     // User commands local to the buffer.
      garray_T  b_ucmds;
      /*
       * start and end of an operator, also used for '[ and ']
       */
*** ../vim-8.1.1209/src/version.c       2019-04-26 22:33:44.896723710 +0200
--- src/version.c       2019-04-26 23:33:32.156048100 +0200
***************
*** 672,682 ****
  #else
        "-toolbar",
  #endif
- #ifdef FEAT_USR_CMDS
        "+user_commands",
- #else
-       "-user_commands",
- #endif
  #ifdef FEAT_VARTABS
        "+vartabs",
  #else
--- 672,678 ----
*** ../vim-8.1.1209/runtime/doc/eval.txt        2019-04-20 14:39:42.796386124 
+0200
--- runtime/doc/eval.txt        2019-04-27 12:38:38.744951183 +0200
***************
*** 10491,10497 ****
  ttyout                        output is a terminal (tty)
  unix                  Unix version of Vim. *+unix*
  unnamedplus           Compiled with support for "unnamedplus" in 'clipboard'
! user_commands         User-defined commands.
  vcon                  Win32: Virtual console support is working, can use
                        'termguicolors'. Also see |+vtp|.
  vertsplit             Compiled with vertically split windows |:vsplit|.
--- 10550,10556 ----
  ttyout                        output is a terminal (tty)
  unix                  Unix version of Vim. *+unix*
  unnamedplus           Compiled with support for "unnamedplus" in 'clipboard'
! user_commands         User-defined commands. (always true)
  vcon                  Win32: Virtual console support is working, can use
                        'termguicolors'. Also see |+vtp|.
  vertsplit             Compiled with vertically split windows |:vsplit|.
***************
*** 10501,10506 ****
--- 10560,10566 ----
  viminfo                       Compiled with viminfo support.
  vimscript-1           Compiled Vim script version 1 support
  vimscript-2           Compiled Vim script version 2 support
+ vimscript-3           Compiled Vim script version 3 support
  virtualedit           Compiled with 'virtualedit' option. (always true)
  visual                        Compiled with Visual mode. (always true)
  visualextra           Compiled with extra Visual mode commands. (always
***************
*** 12700,12706 ****
  
  These items are not allowed in the sandbox:
        - changing the buffer text
!       - defining or changing mapping, autocommands, functions, user commands
        - setting certain options (see |option-summary|)
        - setting certain v: variables (see |v:var|)  *E794*
        - executing a shell command
--- 12771,12777 ----
  
  These items are not allowed in the sandbox:
        - changing the buffer text
!       - defining or changing mapping, autocommands, user commands
        - setting certain options (see |option-summary|)
        - setting certain v: variables (see |v:var|)  *E794*
        - executing a shell command
*** ../vim-8.1.1209/runtime/doc/various.txt     2019-01-11 14:37:16.689248837 
+0100
--- runtime/doc/various.txt     2019-04-27 12:40:20.376390811 +0200
***************
*** 458,464 ****
  N  *+timers*          the |timer_start()| function
  N  *+title*           Setting the window 'title' and 'icon'
  N  *+toolbar*         |gui-toolbar|
! N  *+user_commands*   User-defined commands. |user-commands|
  B  *+vartabs*         Variable-width tabstops. |'vartabstop'|
  N  *+viminfo*         |'viminfo'|
     *+vertsplit*               Vertically split windows |:vsplit|; Always 
enabled
--- 456,463 ----
  N  *+timers*          the |timer_start()| function
  N  *+title*           Setting the window 'title' and 'icon'
  N  *+toolbar*         |gui-toolbar|
! T  *+user_commands*   User-defined commands. |user-commands|
!                       Always enabled since 8.1.1210.
  B  *+vartabs*         Variable-width tabstops. |'vartabstop'|
  N  *+viminfo*         |'viminfo'|
     *+vertsplit*               Vertically split windows |:vsplit|; Always 
enabled
*** ../vim-8.1.1209/src/version.c       2019-04-26 22:33:44.896723710 +0200
--- src/version.c       2019-04-26 23:33:32.156048100 +0200
***************
*** 773,774 ****
--- 769,772 ----
  {   /* Add new patch number below this line */
+ /**/
+     1210,
  /**/

-- 
Shit makes the flowers grow and that's beautiful

 /// Bram Moolenaar -- b...@moolenaar.net -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui