summaryrefslogtreecommitdiff
path: root/data/vim/patches/8.1.1210
diff options
context:
space:
mode:
Diffstat (limited to 'data/vim/patches/8.1.1210')
-rw-r--r--data/vim/patches/8.1.12104639
1 files changed, 4639 insertions, 0 deletions
diff --git a/data/vim/patches/8.1.1210 b/data/vim/patches/8.1.1210
new file mode 100644
index 000000000..4405c63ae
--- /dev/null
+++ b/data/vim/patches/8.1.1210
@@ -0,0 +1,4639 @@
+To: vim_dev@googlegroups.com
+Subject: Patch 8.1.1210
+Fcc: outbox
+From: Bram Moolenaar <Bram@moolenaar.net>
+Mime-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+------------
+
+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 <arpadffy@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 <arpadffy@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 -- Bram@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 ///