blob: f43a0a50f554b49290d694e26c84671e09b5015f [file] [log] [blame]
Bram Moolenaarac9fb182019-04-27 13:04:13 +02001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * usercmd.c: User defined command support
12 */
13
14#include "vim.h"
15
16typedef struct ucmd
17{
18 char_u *uc_name; // The command name
19 long_u uc_argt; // The argument type
20 char_u *uc_rep; // The command's replacement string
21 long uc_def; // The default value for a range/count
22 int uc_compl; // completion type
Bram Moolenaarb7316892019-05-01 18:08:42 +020023 cmd_addr_T uc_addr_type; // The command's address type
Bram Moolenaarac9fb182019-04-27 13:04:13 +020024# ifdef FEAT_EVAL
25 sctx_T uc_script_ctx; // SCTX where the command was defined
26# ifdef FEAT_CMDL_COMPL
27 char_u *uc_compl_arg; // completion argument if any
28# endif
29# endif
30} ucmd_T;
31
32// List of all user commands.
33static garray_T ucmds = {0, 0, sizeof(ucmd_T), 4, NULL};
34
35#define USER_CMD(i) (&((ucmd_T *)(ucmds.ga_data))[i])
36#define USER_CMD_GA(gap, i) (&((ucmd_T *)((gap)->ga_data))[i])
37
38/*
39 * List of names for completion for ":command" with the EXPAND_ flag.
40 * Must be alphabetical for completion.
41 */
42static struct
43{
44 int expand;
45 char *name;
46} command_complete[] =
47{
48 {EXPAND_ARGLIST, "arglist"},
49 {EXPAND_AUGROUP, "augroup"},
50 {EXPAND_BEHAVE, "behave"},
51 {EXPAND_BUFFERS, "buffer"},
52 {EXPAND_COLORS, "color"},
53 {EXPAND_COMMANDS, "command"},
54 {EXPAND_COMPILER, "compiler"},
55#if defined(FEAT_CSCOPE)
56 {EXPAND_CSCOPE, "cscope"},
57#endif
58#if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
59 {EXPAND_USER_DEFINED, "custom"},
60 {EXPAND_USER_LIST, "customlist"},
61#endif
62 {EXPAND_DIRECTORIES, "dir"},
63 {EXPAND_ENV_VARS, "environment"},
64 {EXPAND_EVENTS, "event"},
65 {EXPAND_EXPRESSION, "expression"},
66 {EXPAND_FILES, "file"},
67 {EXPAND_FILES_IN_PATH, "file_in_path"},
68 {EXPAND_FILETYPE, "filetype"},
69 {EXPAND_FUNCTIONS, "function"},
70 {EXPAND_HELP, "help"},
71 {EXPAND_HIGHLIGHT, "highlight"},
Bram Moolenaarac9fb182019-04-27 13:04:13 +020072 {EXPAND_HISTORY, "history"},
Bram Moolenaarac9fb182019-04-27 13:04:13 +020073#if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
74 {EXPAND_LOCALES, "locale"},
75#endif
76 {EXPAND_MAPCLEAR, "mapclear"},
77 {EXPAND_MAPPINGS, "mapping"},
78 {EXPAND_MENUS, "menu"},
79 {EXPAND_MESSAGES, "messages"},
80 {EXPAND_OWNSYNTAX, "syntax"},
81#if defined(FEAT_PROFILE)
82 {EXPAND_SYNTIME, "syntime"},
83#endif
84 {EXPAND_SETTINGS, "option"},
85 {EXPAND_PACKADD, "packadd"},
86 {EXPAND_SHELLCMD, "shellcmd"},
87#if defined(FEAT_SIGNS)
88 {EXPAND_SIGN, "sign"},
89#endif
90 {EXPAND_TAGS, "tag"},
91 {EXPAND_TAGS_LISTFILES, "tag_listfiles"},
92 {EXPAND_USER, "user"},
93 {EXPAND_USER_VARS, "var"},
94 {0, NULL}
95};
96
97/*
98 * List of names of address types. Must be alphabetical for completion.
99 */
100static struct
101{
Bram Moolenaarb7316892019-05-01 18:08:42 +0200102 cmd_addr_T expand;
103 char *name;
104 char *shortname;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200105} addr_type_complete[] =
106{
107 {ADDR_ARGUMENTS, "arguments", "arg"},
108 {ADDR_LINES, "lines", "line"},
109 {ADDR_LOADED_BUFFERS, "loaded_buffers", "load"},
110 {ADDR_TABS, "tabs", "tab"},
111 {ADDR_BUFFERS, "buffers", "buf"},
112 {ADDR_WINDOWS, "windows", "win"},
113 {ADDR_QUICKFIX, "quickfix", "qf"},
114 {ADDR_OTHER, "other", "?"},
Bram Moolenaarb7316892019-05-01 18:08:42 +0200115 {ADDR_NONE, NULL, NULL}
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200116};
117
118#define UC_BUFFER 1 // -buffer: local to current buffer
119
120/*
121 * Search for a user command that matches "eap->cmd".
122 * Return cmdidx in "eap->cmdidx", flags in "eap->argt", idx in "eap->useridx".
123 * Return a pointer to just after the command.
124 * Return NULL if there is no matching command.
125 */
126 char_u *
127find_ucmd(
128 exarg_T *eap,
129 char_u *p, // end of the command (possibly including count)
130 int *full, // set to TRUE for a full match
131 expand_T *xp, // used for completion, NULL otherwise
Bram Moolenaar52111f82019-04-29 21:30:45 +0200132 int *complp UNUSED) // completion flags or NULL
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200133{
134 int len = (int)(p - eap->cmd);
135 int j, k, matchlen = 0;
136 ucmd_T *uc;
137 int found = FALSE;
138 int possible = FALSE;
139 char_u *cp, *np; // Point into typed cmd and test name
140 garray_T *gap;
141 int amb_local = FALSE; // Found ambiguous buffer-local command,
142 // only full match global is accepted.
143
144 /*
145 * Look for buffer-local user commands first, then global ones.
146 */
147 gap = &curbuf->b_ucmds;
148 for (;;)
149 {
150 for (j = 0; j < gap->ga_len; ++j)
151 {
152 uc = USER_CMD_GA(gap, j);
153 cp = eap->cmd;
154 np = uc->uc_name;
155 k = 0;
156 while (k < len && *np != NUL && *cp++ == *np++)
157 k++;
158 if (k == len || (*np == NUL && vim_isdigit(eap->cmd[k])))
159 {
160 // If finding a second match, the command is ambiguous. But
161 // not if a buffer-local command wasn't a full match and a
162 // global command is a full match.
163 if (k == len && found && *np != NUL)
164 {
165 if (gap == &ucmds)
166 return NULL;
167 amb_local = TRUE;
168 }
169
170 if (!found || (k == len && *np == NUL))
171 {
172 // If we matched up to a digit, then there could
173 // be another command including the digit that we
174 // should use instead.
175 if (k == len)
176 found = TRUE;
177 else
178 possible = TRUE;
179
180 if (gap == &ucmds)
181 eap->cmdidx = CMD_USER;
182 else
183 eap->cmdidx = CMD_USER_BUF;
184 eap->argt = (long)uc->uc_argt;
185 eap->useridx = j;
186 eap->addr_type = uc->uc_addr_type;
187
188# ifdef FEAT_CMDL_COMPL
Bram Moolenaar52111f82019-04-29 21:30:45 +0200189 if (complp != NULL)
190 *complp = uc->uc_compl;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200191# ifdef FEAT_EVAL
192 if (xp != NULL)
193 {
194 xp->xp_arg = uc->uc_compl_arg;
195 xp->xp_script_ctx = uc->uc_script_ctx;
196 xp->xp_script_ctx.sc_lnum += sourcing_lnum;
197 }
198# endif
199# endif
200 // Do not search for further abbreviations
201 // if this is an exact match.
202 matchlen = k;
203 if (k == len && *np == NUL)
204 {
205 if (full != NULL)
206 *full = TRUE;
207 amb_local = FALSE;
208 break;
209 }
210 }
211 }
212 }
213
214 // Stop if we found a full match or searched all.
215 if (j < gap->ga_len || gap == &ucmds)
216 break;
217 gap = &ucmds;
218 }
219
220 // Only found ambiguous matches.
221 if (amb_local)
222 {
223 if (xp != NULL)
224 xp->xp_context = EXPAND_UNSUCCESSFUL;
225 return NULL;
226 }
227
228 // The match we found may be followed immediately by a number. Move "p"
229 // back to point to it.
230 if (found || possible)
231 return p + (matchlen - len);
232 return p;
233}
234
235#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
236
237 char_u *
238set_context_in_user_cmd(expand_T *xp, char_u *arg_in)
239{
240 char_u *arg = arg_in;
241 char_u *p;
242
243 // Check for attributes
244 while (*arg == '-')
245 {
246 arg++; // Skip "-"
247 p = skiptowhite(arg);
248 if (*p == NUL)
249 {
250 // Cursor is still in the attribute
251 p = vim_strchr(arg, '=');
252 if (p == NULL)
253 {
254 // No "=", so complete attribute names
255 xp->xp_context = EXPAND_USER_CMD_FLAGS;
256 xp->xp_pattern = arg;
257 return NULL;
258 }
259
260 // For the -complete, -nargs and -addr attributes, we complete
261 // their arguments as well.
262 if (STRNICMP(arg, "complete", p - arg) == 0)
263 {
264 xp->xp_context = EXPAND_USER_COMPLETE;
265 xp->xp_pattern = p + 1;
266 return NULL;
267 }
268 else if (STRNICMP(arg, "nargs", p - arg) == 0)
269 {
270 xp->xp_context = EXPAND_USER_NARGS;
271 xp->xp_pattern = p + 1;
272 return NULL;
273 }
274 else if (STRNICMP(arg, "addr", p - arg) == 0)
275 {
276 xp->xp_context = EXPAND_USER_ADDR_TYPE;
277 xp->xp_pattern = p + 1;
278 return NULL;
279 }
280 return NULL;
281 }
282 arg = skipwhite(p);
283 }
284
285 // After the attributes comes the new command name
286 p = skiptowhite(arg);
287 if (*p == NUL)
288 {
289 xp->xp_context = EXPAND_USER_COMMANDS;
290 xp->xp_pattern = arg;
291 return NULL;
292 }
293
294 // And finally comes a normal command
295 return skipwhite(p);
296}
297
298 char_u *
299get_user_command_name(int idx)
300{
301 return get_user_commands(NULL, idx - (int)CMD_SIZE);
302}
303
304/*
305 * Function given to ExpandGeneric() to obtain the list of user command names.
306 */
307 char_u *
308get_user_commands(expand_T *xp UNUSED, int idx)
309{
Bram Moolenaarf03e3282019-07-22 21:55:18 +0200310 // In cmdwin, the alternative buffer should be used.
311 buf_T *buf =
312#ifdef FEAT_CMDWIN
313 (cmdwin_type != 0 && get_cmdline_type() == NUL) ? prevwin->w_buffer :
314#endif
315 curbuf;
316
317 if (idx < buf->b_ucmds.ga_len)
318 return USER_CMD_GA(&buf->b_ucmds, idx)->uc_name;
319 idx -= buf->b_ucmds.ga_len;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200320 if (idx < ucmds.ga_len)
321 return USER_CMD(idx)->uc_name;
322 return NULL;
323}
324
325/*
326 * Function given to ExpandGeneric() to obtain the list of user address type
327 * names.
328 */
329 char_u *
330get_user_cmd_addr_type(expand_T *xp UNUSED, int idx)
331{
332 return (char_u *)addr_type_complete[idx].name;
333}
334
335/*
336 * Function given to ExpandGeneric() to obtain the list of user command
337 * attributes.
338 */
339 char_u *
340get_user_cmd_flags(expand_T *xp UNUSED, int idx)
341{
342 static char *user_cmd_flags[] = {
343 "addr", "bang", "bar", "buffer", "complete",
344 "count", "nargs", "range", "register"
345 };
346
347 if (idx >= (int)(sizeof(user_cmd_flags) / sizeof(user_cmd_flags[0])))
348 return NULL;
349 return (char_u *)user_cmd_flags[idx];
350}
351
352/*
353 * Function given to ExpandGeneric() to obtain the list of values for -nargs.
354 */
355 char_u *
356get_user_cmd_nargs(expand_T *xp UNUSED, int idx)
357{
358 static char *user_cmd_nargs[] = {"0", "1", "*", "?", "+"};
359
360 if (idx >= (int)(sizeof(user_cmd_nargs) / sizeof(user_cmd_nargs[0])))
361 return NULL;
362 return (char_u *)user_cmd_nargs[idx];
363}
364
365/*
366 * Function given to ExpandGeneric() to obtain the list of values for
367 * -complete.
368 */
369 char_u *
370get_user_cmd_complete(expand_T *xp UNUSED, int idx)
371{
372 return (char_u *)command_complete[idx].name;
373}
374
375 int
376cmdcomplete_str_to_type(char_u *complete_str)
377{
378 int i;
379
380 for (i = 0; command_complete[i].expand != 0; ++i)
381 if (STRCMP(complete_str, command_complete[i].name) == 0)
382 return command_complete[i].expand;
383
384 return EXPAND_NOTHING;
385}
386
387#endif // FEAT_CMDL_COMPL
388
389/*
390 * List user commands starting with "name[name_len]".
391 */
392 static void
393uc_list(char_u *name, size_t name_len)
394{
395 int i, j;
396 int found = FALSE;
397 ucmd_T *cmd;
398 int len;
399 int over;
400 long a;
401 garray_T *gap;
402
Bram Moolenaarf03e3282019-07-22 21:55:18 +0200403 /* In cmdwin, the alternative buffer should be used. */
404 gap =
405#ifdef FEAT_CMDWIN
406 (cmdwin_type != 0 && get_cmdline_type() == NUL) ?
407 &prevwin->w_buffer->b_ucmds :
408#endif
409 &curbuf->b_ucmds;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200410 for (;;)
411 {
412 for (i = 0; i < gap->ga_len; ++i)
413 {
414 cmd = USER_CMD_GA(gap, i);
415 a = (long)cmd->uc_argt;
416
417 // Skip commands which don't match the requested prefix and
418 // commands filtered out.
419 if (STRNCMP(name, cmd->uc_name, name_len) != 0
420 || message_filtered(cmd->uc_name))
421 continue;
422
423 // Put out the title first time
424 if (!found)
425 msg_puts_title(_("\n Name Args Address Complete Definition"));
426 found = TRUE;
427 msg_putchar('\n');
428 if (got_int)
429 break;
430
431 // Special cases
432 len = 4;
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200433 if (a & EX_BANG)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200434 {
435 msg_putchar('!');
436 --len;
437 }
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200438 if (a & EX_REGSTR)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200439 {
440 msg_putchar('"');
441 --len;
442 }
443 if (gap != &ucmds)
444 {
445 msg_putchar('b');
446 --len;
447 }
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200448 if (a & EX_TRLBAR)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200449 {
450 msg_putchar('|');
451 --len;
452 }
453 while (len-- > 0)
454 msg_putchar(' ');
455
456 msg_outtrans_attr(cmd->uc_name, HL_ATTR(HLF_D));
457 len = (int)STRLEN(cmd->uc_name) + 4;
458
459 do {
460 msg_putchar(' ');
461 ++len;
462 } while (len < 22);
463
464 // "over" is how much longer the name is than the column width for
465 // the name, we'll try to align what comes after.
466 over = len - 22;
467 len = 0;
468
469 // Arguments
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200470 switch ((int)(a & (EX_EXTRA|EX_NOSPC|EX_NEEDARG)))
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200471 {
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200472 case 0: IObuff[len++] = '0'; break;
473 case (EX_EXTRA): IObuff[len++] = '*'; break;
474 case (EX_EXTRA|EX_NOSPC): IObuff[len++] = '?'; break;
475 case (EX_EXTRA|EX_NEEDARG): IObuff[len++] = '+'; break;
476 case (EX_EXTRA|EX_NOSPC|EX_NEEDARG): IObuff[len++] = '1'; break;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200477 }
478
479 do {
480 IObuff[len++] = ' ';
481 } while (len < 5 - over);
482
483 // Address / Range
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200484 if (a & (EX_RANGE|EX_COUNT))
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200485 {
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200486 if (a & EX_COUNT)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200487 {
488 // -count=N
489 sprintf((char *)IObuff + len, "%ldc", cmd->uc_def);
490 len += (int)STRLEN(IObuff + len);
491 }
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200492 else if (a & EX_DFLALL)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200493 IObuff[len++] = '%';
494 else if (cmd->uc_def >= 0)
495 {
496 // -range=N
497 sprintf((char *)IObuff + len, "%ld", cmd->uc_def);
498 len += (int)STRLEN(IObuff + len);
499 }
500 else
501 IObuff[len++] = '.';
502 }
503
504 do {
505 IObuff[len++] = ' ';
506 } while (len < 8 - over);
507
508 // Address Type
Bram Moolenaarb7316892019-05-01 18:08:42 +0200509 for (j = 0; addr_type_complete[j].expand != ADDR_NONE; ++j)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200510 if (addr_type_complete[j].expand != ADDR_LINES
511 && addr_type_complete[j].expand == cmd->uc_addr_type)
512 {
513 STRCPY(IObuff + len, addr_type_complete[j].shortname);
514 len += (int)STRLEN(IObuff + len);
515 break;
516 }
517
518 do {
519 IObuff[len++] = ' ';
520 } while (len < 13 - over);
521
522 // Completion
523 for (j = 0; command_complete[j].expand != 0; ++j)
524 if (command_complete[j].expand == cmd->uc_compl)
525 {
526 STRCPY(IObuff + len, command_complete[j].name);
527 len += (int)STRLEN(IObuff + len);
528 break;
529 }
530
531 do {
532 IObuff[len++] = ' ';
533 } while (len < 25 - over);
534
535 IObuff[len] = '\0';
536 msg_outtrans(IObuff);
537
538 msg_outtrans_special(cmd->uc_rep, FALSE,
539 name_len == 0 ? Columns - 47 : 0);
540#ifdef FEAT_EVAL
541 if (p_verbose > 0)
542 last_set_msg(cmd->uc_script_ctx);
543#endif
544 out_flush();
545 ui_breakcheck();
546 if (got_int)
547 break;
548 }
549 if (gap == &ucmds || i < gap->ga_len)
550 break;
551 gap = &ucmds;
552 }
553
554 if (!found)
555 msg(_("No user-defined commands found"));
556}
557
558 char *
559uc_fun_cmd(void)
560{
561 static char_u fcmd[] = {0x84, 0xaf, 0x60, 0xb9, 0xaf, 0xb5, 0x60, 0xa4,
562 0xa5, 0xad, 0xa1, 0xae, 0xa4, 0x60, 0xa1, 0x60,
563 0xb3, 0xa8, 0xb2, 0xb5, 0xa2, 0xa2, 0xa5, 0xb2,
564 0xb9, 0x7f, 0};
565 int i;
566
567 for (i = 0; fcmd[i]; ++i)
568 IObuff[i] = fcmd[i] - 0x40;
569 IObuff[i] = 0;
570 return (char *)IObuff;
571}
572
573/*
574 * Parse address type argument
575 */
576 static int
577parse_addr_type_arg(
578 char_u *value,
579 int vallen,
Bram Moolenaarb7316892019-05-01 18:08:42 +0200580 cmd_addr_T *addr_type_arg)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200581{
582 int i, a, b;
583
Bram Moolenaarb7316892019-05-01 18:08:42 +0200584 for (i = 0; addr_type_complete[i].expand != ADDR_NONE; ++i)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200585 {
586 a = (int)STRLEN(addr_type_complete[i].name) == vallen;
587 b = STRNCMP(value, addr_type_complete[i].name, vallen) == 0;
588 if (a && b)
589 {
590 *addr_type_arg = addr_type_complete[i].expand;
591 break;
592 }
593 }
594
Bram Moolenaarb7316892019-05-01 18:08:42 +0200595 if (addr_type_complete[i].expand == ADDR_NONE)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200596 {
597 char_u *err = value;
598
599 for (i = 0; err[i] != NUL && !VIM_ISWHITE(err[i]); i++)
600 ;
601 err[i] = NUL;
602 semsg(_("E180: Invalid address type value: %s"), err);
603 return FAIL;
604 }
605
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200606 return OK;
607}
608
609/*
610 * Parse a completion argument "value[vallen]".
611 * The detected completion goes in "*complp", argument type in "*argt".
612 * When there is an argument, for function and user defined completion, it's
613 * copied to allocated memory and stored in "*compl_arg".
614 * Returns FAIL if something is wrong.
615 */
616 int
617parse_compl_arg(
618 char_u *value,
619 int vallen,
620 int *complp,
621 long *argt,
622 char_u **compl_arg UNUSED)
623{
624 char_u *arg = NULL;
625# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
626 size_t arglen = 0;
627# endif
628 int i;
629 int valend = vallen;
630
631 // Look for any argument part - which is the part after any ','
632 for (i = 0; i < vallen; ++i)
633 {
634 if (value[i] == ',')
635 {
636 arg = &value[i + 1];
637# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
638 arglen = vallen - i - 1;
639# endif
640 valend = i;
641 break;
642 }
643 }
644
645 for (i = 0; command_complete[i].expand != 0; ++i)
646 {
647 if ((int)STRLEN(command_complete[i].name) == valend
648 && STRNCMP(value, command_complete[i].name, valend) == 0)
649 {
650 *complp = command_complete[i].expand;
651 if (command_complete[i].expand == EXPAND_BUFFERS)
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200652 *argt |= EX_BUFNAME;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200653 else if (command_complete[i].expand == EXPAND_DIRECTORIES
654 || command_complete[i].expand == EXPAND_FILES)
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200655 *argt |= EX_XFILE;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200656 break;
657 }
658 }
659
660 if (command_complete[i].expand == 0)
661 {
662 semsg(_("E180: Invalid complete value: %s"), value);
663 return FAIL;
664 }
665
666# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
667 if (*complp != EXPAND_USER_DEFINED && *complp != EXPAND_USER_LIST
668 && arg != NULL)
669# else
670 if (arg != NULL)
671# endif
672 {
673 emsg(_("E468: Completion argument only allowed for custom completion"));
674 return FAIL;
675 }
676
677# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
678 if ((*complp == EXPAND_USER_DEFINED || *complp == EXPAND_USER_LIST)
679 && arg == NULL)
680 {
681 emsg(_("E467: Custom completion requires a function argument"));
682 return FAIL;
683 }
684
685 if (arg != NULL)
686 *compl_arg = vim_strnsave(arg, (int)arglen);
687# endif
688 return OK;
689}
690
691/*
692 * Scan attributes in the ":command" command.
693 * Return FAIL when something is wrong.
694 */
695 static int
696uc_scan_attr(
697 char_u *attr,
698 size_t len,
699 long *argt,
700 long *def,
701 int *flags,
Bram Moolenaar52111f82019-04-29 21:30:45 +0200702 int *complp,
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200703 char_u **compl_arg,
Bram Moolenaarb7316892019-05-01 18:08:42 +0200704 cmd_addr_T *addr_type_arg)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200705{
706 char_u *p;
707
708 if (len == 0)
709 {
710 emsg(_("E175: No attribute specified"));
711 return FAIL;
712 }
713
714 // First, try the simple attributes (no arguments)
715 if (STRNICMP(attr, "bang", len) == 0)
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200716 *argt |= EX_BANG;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200717 else if (STRNICMP(attr, "buffer", len) == 0)
718 *flags |= UC_BUFFER;
719 else if (STRNICMP(attr, "register", len) == 0)
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200720 *argt |= EX_REGSTR;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200721 else if (STRNICMP(attr, "bar", len) == 0)
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200722 *argt |= EX_TRLBAR;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200723 else
724 {
725 int i;
726 char_u *val = NULL;
727 size_t vallen = 0;
728 size_t attrlen = len;
729
730 // Look for the attribute name - which is the part before any '='
731 for (i = 0; i < (int)len; ++i)
732 {
733 if (attr[i] == '=')
734 {
735 val = &attr[i + 1];
736 vallen = len - i - 1;
737 attrlen = i;
738 break;
739 }
740 }
741
742 if (STRNICMP(attr, "nargs", attrlen) == 0)
743 {
744 if (vallen == 1)
745 {
746 if (*val == '0')
747 // Do nothing - this is the default
748 ;
749 else if (*val == '1')
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200750 *argt |= (EX_EXTRA | EX_NOSPC | EX_NEEDARG);
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200751 else if (*val == '*')
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200752 *argt |= EX_EXTRA;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200753 else if (*val == '?')
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200754 *argt |= (EX_EXTRA | EX_NOSPC);
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200755 else if (*val == '+')
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200756 *argt |= (EX_EXTRA | EX_NEEDARG);
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200757 else
758 goto wrong_nargs;
759 }
760 else
761 {
762wrong_nargs:
763 emsg(_("E176: Invalid number of arguments"));
764 return FAIL;
765 }
766 }
767 else if (STRNICMP(attr, "range", attrlen) == 0)
768 {
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200769 *argt |= EX_RANGE;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200770 if (vallen == 1 && *val == '%')
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200771 *argt |= EX_DFLALL;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200772 else if (val != NULL)
773 {
774 p = val;
775 if (*def >= 0)
776 {
777two_count:
778 emsg(_("E177: Count cannot be specified twice"));
779 return FAIL;
780 }
781
782 *def = getdigits(&p);
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200783 *argt |= EX_ZEROR;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200784
785 if (p != val + vallen || vallen == 0)
786 {
787invalid_count:
788 emsg(_("E178: Invalid default value for count"));
789 return FAIL;
790 }
791 }
Bram Moolenaarb7316892019-05-01 18:08:42 +0200792 // default for -range is using buffer lines
793 if (*addr_type_arg == ADDR_NONE)
794 *addr_type_arg = ADDR_LINES;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200795 }
796 else if (STRNICMP(attr, "count", attrlen) == 0)
797 {
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200798 *argt |= (EX_COUNT | EX_ZEROR | EX_RANGE);
Bram Moolenaarb7316892019-05-01 18:08:42 +0200799 // default for -count is using any number
800 if (*addr_type_arg == ADDR_NONE)
801 *addr_type_arg = ADDR_OTHER;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200802
803 if (val != NULL)
804 {
805 p = val;
806 if (*def >= 0)
807 goto two_count;
808
809 *def = getdigits(&p);
810
811 if (p != val + vallen)
812 goto invalid_count;
813 }
814
815 if (*def < 0)
816 *def = 0;
817 }
818 else if (STRNICMP(attr, "complete", attrlen) == 0)
819 {
820 if (val == NULL)
821 {
822 emsg(_("E179: argument required for -complete"));
823 return FAIL;
824 }
825
Bram Moolenaar52111f82019-04-29 21:30:45 +0200826 if (parse_compl_arg(val, (int)vallen, complp, argt, compl_arg)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200827 == FAIL)
828 return FAIL;
829 }
830 else if (STRNICMP(attr, "addr", attrlen) == 0)
831 {
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200832 *argt |= EX_RANGE;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200833 if (val == NULL)
834 {
835 emsg(_("E179: argument required for -addr"));
836 return FAIL;
837 }
Bram Moolenaare4f5f3a2019-05-04 14:05:08 +0200838 if (parse_addr_type_arg(val, (int)vallen, addr_type_arg) == FAIL)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200839 return FAIL;
Bram Moolenaare4f5f3a2019-05-04 14:05:08 +0200840 if (*addr_type_arg != ADDR_LINES)
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200841 *argt |= EX_ZEROR;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200842 }
843 else
844 {
845 char_u ch = attr[len];
846 attr[len] = '\0';
847 semsg(_("E181: Invalid attribute: %s"), attr);
848 attr[len] = ch;
849 return FAIL;
850 }
851 }
852
853 return OK;
854}
855
856/*
857 * Add a user command to the list or replace an existing one.
858 */
859 static int
860uc_add_command(
861 char_u *name,
862 size_t name_len,
863 char_u *rep,
864 long argt,
865 long def,
866 int flags,
867 int compl,
868 char_u *compl_arg UNUSED,
Bram Moolenaarb7316892019-05-01 18:08:42 +0200869 cmd_addr_T addr_type,
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200870 int force)
871{
872 ucmd_T *cmd = NULL;
873 char_u *p;
874 int i;
875 int cmp = 1;
876 char_u *rep_buf = NULL;
877 garray_T *gap;
878
879 replace_termcodes(rep, &rep_buf, FALSE, FALSE, FALSE);
880 if (rep_buf == NULL)
881 {
882 // Can't replace termcodes - try using the string as is
883 rep_buf = vim_strsave(rep);
884
885 // Give up if out of memory
886 if (rep_buf == NULL)
887 return FAIL;
888 }
889
890 // get address of growarray: global or in curbuf
891 if (flags & UC_BUFFER)
892 {
893 gap = &curbuf->b_ucmds;
894 if (gap->ga_itemsize == 0)
895 ga_init2(gap, (int)sizeof(ucmd_T), 4);
896 }
897 else
898 gap = &ucmds;
899
900 // Search for the command in the already defined commands.
901 for (i = 0; i < gap->ga_len; ++i)
902 {
903 size_t len;
904
905 cmd = USER_CMD_GA(gap, i);
906 len = STRLEN(cmd->uc_name);
907 cmp = STRNCMP(name, cmd->uc_name, name_len);
908 if (cmp == 0)
909 {
910 if (name_len < len)
911 cmp = -1;
912 else if (name_len > len)
913 cmp = 1;
914 }
915
916 if (cmp == 0)
917 {
918 // Command can be replaced with "command!" and when sourcing the
919 // same script again, but only once.
920 if (!force
921#ifdef FEAT_EVAL
922 && (cmd->uc_script_ctx.sc_sid != current_sctx.sc_sid
923 || cmd->uc_script_ctx.sc_seq == current_sctx.sc_seq)
924#endif
925 )
926 {
927 semsg(_("E174: Command already exists: add ! to replace it: %s"),
928 name);
929 goto fail;
930 }
931
932 VIM_CLEAR(cmd->uc_rep);
933#if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
934 VIM_CLEAR(cmd->uc_compl_arg);
935#endif
936 break;
937 }
938
939 // Stop as soon as we pass the name to add
940 if (cmp < 0)
941 break;
942 }
943
944 // Extend the array unless we're replacing an existing command
945 if (cmp != 0)
946 {
947 if (ga_grow(gap, 1) != OK)
948 goto fail;
949 if ((p = vim_strnsave(name, (int)name_len)) == NULL)
950 goto fail;
951
952 cmd = USER_CMD_GA(gap, i);
953 mch_memmove(cmd + 1, cmd, (gap->ga_len - i) * sizeof(ucmd_T));
954
955 ++gap->ga_len;
956
957 cmd->uc_name = p;
958 }
959
960 cmd->uc_rep = rep_buf;
961 cmd->uc_argt = argt;
962 cmd->uc_def = def;
963 cmd->uc_compl = compl;
964#ifdef FEAT_EVAL
965 cmd->uc_script_ctx = current_sctx;
966 cmd->uc_script_ctx.sc_lnum += sourcing_lnum;
967# ifdef FEAT_CMDL_COMPL
968 cmd->uc_compl_arg = compl_arg;
969# endif
970#endif
971 cmd->uc_addr_type = addr_type;
972
973 return OK;
974
975fail:
976 vim_free(rep_buf);
977#if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
978 vim_free(compl_arg);
979#endif
980 return FAIL;
981}
982
983/*
984 * ":command ..." implementation
985 */
986 void
987ex_command(exarg_T *eap)
988{
Bram Moolenaarb7316892019-05-01 18:08:42 +0200989 char_u *name;
990 char_u *end;
991 char_u *p;
992 long argt = 0;
993 long def = -1;
994 int flags = 0;
995 int compl = EXPAND_NOTHING;
996 char_u *compl_arg = NULL;
997 cmd_addr_T addr_type_arg = ADDR_NONE;
998 int has_attr = (eap->arg[0] == '-');
999 int name_len;
Bram Moolenaarac9fb182019-04-27 13:04:13 +02001000
1001 p = eap->arg;
1002
1003 // Check for attributes
1004 while (*p == '-')
1005 {
1006 ++p;
1007 end = skiptowhite(p);
1008 if (uc_scan_attr(p, end - p, &argt, &def, &flags, &compl,
1009 &compl_arg, &addr_type_arg) == FAIL)
1010 return;
1011 p = skipwhite(end);
1012 }
1013
1014 // Get the name (if any) and skip to the following argument
1015 name = p;
1016 if (ASCII_ISALPHA(*p))
1017 while (ASCII_ISALNUM(*p))
1018 ++p;
1019 if (!ends_excmd(*p) && !VIM_ISWHITE(*p))
1020 {
1021 emsg(_("E182: Invalid command name"));
1022 return;
1023 }
1024 end = p;
1025 name_len = (int)(end - name);
1026
1027 // If there is nothing after the name, and no attributes were specified,
1028 // we are listing commands
1029 p = skipwhite(end);
1030 if (!has_attr && ends_excmd(*p))
1031 {
1032 uc_list(name, end - name);
1033 }
1034 else if (!ASCII_ISUPPER(*name))
1035 {
1036 emsg(_("E183: User defined commands must start with an uppercase letter"));
1037 return;
1038 }
1039 else if ((name_len == 1 && *name == 'X')
1040 || (name_len <= 4
1041 && STRNCMP(name, "Next", name_len > 4 ? 4 : name_len) == 0))
1042 {
1043 emsg(_("E841: Reserved name, cannot be used for user defined command"));
1044 return;
1045 }
1046 else
1047 uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg,
1048 addr_type_arg, eap->forceit);
1049}
1050
1051/*
1052 * ":comclear" implementation
1053 * Clear all user commands, global and for current buffer.
1054 */
1055 void
1056ex_comclear(exarg_T *eap UNUSED)
1057{
1058 uc_clear(&ucmds);
Bram Moolenaare5c83282019-05-03 23:15:37 +02001059 if (curbuf != NULL)
1060 uc_clear(&curbuf->b_ucmds);
Bram Moolenaarac9fb182019-04-27 13:04:13 +02001061}
1062
1063/*
1064 * Clear all user commands for "gap".
1065 */
1066 void
1067uc_clear(garray_T *gap)
1068{
1069 int i;
1070 ucmd_T *cmd;
1071
1072 for (i = 0; i < gap->ga_len; ++i)
1073 {
1074 cmd = USER_CMD_GA(gap, i);
1075 vim_free(cmd->uc_name);
1076 vim_free(cmd->uc_rep);
1077# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
1078 vim_free(cmd->uc_compl_arg);
1079# endif
1080 }
1081 ga_clear(gap);
1082}
1083
1084/*
1085 * ":delcommand" implementation
1086 */
1087 void
1088ex_delcommand(exarg_T *eap)
1089{
1090 int i = 0;
1091 ucmd_T *cmd = NULL;
1092 int cmp = -1;
1093 garray_T *gap;
1094
1095 gap = &curbuf->b_ucmds;
1096 for (;;)
1097 {
1098 for (i = 0; i < gap->ga_len; ++i)
1099 {
1100 cmd = USER_CMD_GA(gap, i);
1101 cmp = STRCMP(eap->arg, cmd->uc_name);
1102 if (cmp <= 0)
1103 break;
1104 }
1105 if (gap == &ucmds || cmp == 0)
1106 break;
1107 gap = &ucmds;
1108 }
1109
1110 if (cmp != 0)
1111 {
1112 semsg(_("E184: No such user-defined command: %s"), eap->arg);
1113 return;
1114 }
1115
1116 vim_free(cmd->uc_name);
1117 vim_free(cmd->uc_rep);
1118# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
1119 vim_free(cmd->uc_compl_arg);
1120# endif
1121
1122 --gap->ga_len;
1123
1124 if (i < gap->ga_len)
1125 mch_memmove(cmd, cmd + 1, (gap->ga_len - i) * sizeof(ucmd_T));
1126}
1127
1128/*
1129 * Split and quote args for <f-args>.
1130 */
1131 static char_u *
1132uc_split_args(char_u *arg, size_t *lenp)
1133{
1134 char_u *buf;
1135 char_u *p;
1136 char_u *q;
1137 int len;
1138
1139 // Precalculate length
1140 p = arg;
1141 len = 2; // Initial and final quotes
1142
1143 while (*p)
1144 {
1145 if (p[0] == '\\' && p[1] == '\\')
1146 {
1147 len += 2;
1148 p += 2;
1149 }
1150 else if (p[0] == '\\' && VIM_ISWHITE(p[1]))
1151 {
1152 len += 1;
1153 p += 2;
1154 }
1155 else if (*p == '\\' || *p == '"')
1156 {
1157 len += 2;
1158 p += 1;
1159 }
1160 else if (VIM_ISWHITE(*p))
1161 {
1162 p = skipwhite(p);
1163 if (*p == NUL)
1164 break;
1165 len += 3; // ","
1166 }
1167 else
1168 {
1169 int charlen = (*mb_ptr2len)(p);
1170
1171 len += charlen;
1172 p += charlen;
1173 }
1174 }
1175
1176 buf = alloc(len + 1);
1177 if (buf == NULL)
1178 {
1179 *lenp = 0;
1180 return buf;
1181 }
1182
1183 p = arg;
1184 q = buf;
1185 *q++ = '"';
1186 while (*p)
1187 {
1188 if (p[0] == '\\' && p[1] == '\\')
1189 {
1190 *q++ = '\\';
1191 *q++ = '\\';
1192 p += 2;
1193 }
1194 else if (p[0] == '\\' && VIM_ISWHITE(p[1]))
1195 {
1196 *q++ = p[1];
1197 p += 2;
1198 }
1199 else if (*p == '\\' || *p == '"')
1200 {
1201 *q++ = '\\';
1202 *q++ = *p++;
1203 }
1204 else if (VIM_ISWHITE(*p))
1205 {
1206 p = skipwhite(p);
1207 if (*p == NUL)
1208 break;
1209 *q++ = '"';
1210 *q++ = ',';
1211 *q++ = '"';
1212 }
1213 else
1214 {
1215 MB_COPY_CHAR(p, q);
1216 }
1217 }
1218 *q++ = '"';
1219 *q = 0;
1220
1221 *lenp = len;
1222 return buf;
1223}
1224
1225 static size_t
1226add_cmd_modifier(char_u *buf, char *mod_str, int *multi_mods)
1227{
1228 size_t result;
1229
1230 result = STRLEN(mod_str);
1231 if (*multi_mods)
1232 result += 1;
1233 if (buf != NULL)
1234 {
1235 if (*multi_mods)
1236 STRCAT(buf, " ");
1237 STRCAT(buf, mod_str);
1238 }
1239
1240 *multi_mods = 1;
1241
1242 return result;
1243}
1244
1245/*
1246 * Check for a <> code in a user command.
1247 * "code" points to the '<'. "len" the length of the <> (inclusive).
1248 * "buf" is where the result is to be added.
1249 * "split_buf" points to a buffer used for splitting, caller should free it.
1250 * "split_len" is the length of what "split_buf" contains.
1251 * Returns the length of the replacement, which has been added to "buf".
1252 * Returns -1 if there was no match, and only the "<" has been copied.
1253 */
1254 static size_t
1255uc_check_code(
1256 char_u *code,
1257 size_t len,
1258 char_u *buf,
1259 ucmd_T *cmd, // the user command we're expanding
1260 exarg_T *eap, // ex arguments
1261 char_u **split_buf,
1262 size_t *split_len)
1263{
1264 size_t result = 0;
1265 char_u *p = code + 1;
1266 size_t l = len - 2;
1267 int quote = 0;
1268 enum {
1269 ct_ARGS,
1270 ct_BANG,
1271 ct_COUNT,
1272 ct_LINE1,
1273 ct_LINE2,
1274 ct_RANGE,
1275 ct_MODS,
1276 ct_REGISTER,
1277 ct_LT,
1278 ct_NONE
1279 } type = ct_NONE;
1280
1281 if ((vim_strchr((char_u *)"qQfF", *p) != NULL) && p[1] == '-')
1282 {
1283 quote = (*p == 'q' || *p == 'Q') ? 1 : 2;
1284 p += 2;
1285 l -= 2;
1286 }
1287
1288 ++l;
1289 if (l <= 1)
1290 type = ct_NONE;
1291 else if (STRNICMP(p, "args>", l) == 0)
1292 type = ct_ARGS;
1293 else if (STRNICMP(p, "bang>", l) == 0)
1294 type = ct_BANG;
1295 else if (STRNICMP(p, "count>", l) == 0)
1296 type = ct_COUNT;
1297 else if (STRNICMP(p, "line1>", l) == 0)
1298 type = ct_LINE1;
1299 else if (STRNICMP(p, "line2>", l) == 0)
1300 type = ct_LINE2;
1301 else if (STRNICMP(p, "range>", l) == 0)
1302 type = ct_RANGE;
1303 else if (STRNICMP(p, "lt>", l) == 0)
1304 type = ct_LT;
1305 else if (STRNICMP(p, "reg>", l) == 0 || STRNICMP(p, "register>", l) == 0)
1306 type = ct_REGISTER;
1307 else if (STRNICMP(p, "mods>", l) == 0)
1308 type = ct_MODS;
1309
1310 switch (type)
1311 {
1312 case ct_ARGS:
1313 // Simple case first
1314 if (*eap->arg == NUL)
1315 {
1316 if (quote == 1)
1317 {
1318 result = 2;
1319 if (buf != NULL)
1320 STRCPY(buf, "''");
1321 }
1322 else
1323 result = 0;
1324 break;
1325 }
1326
1327 // When specified there is a single argument don't split it.
1328 // Works for ":Cmd %" when % is "a b c".
Bram Moolenaar8071cb22019-07-12 17:58:01 +02001329 if ((eap->argt & EX_NOSPC) && quote == 2)
Bram Moolenaarac9fb182019-04-27 13:04:13 +02001330 quote = 1;
1331
1332 switch (quote)
1333 {
1334 case 0: // No quoting, no splitting
1335 result = STRLEN(eap->arg);
1336 if (buf != NULL)
1337 STRCPY(buf, eap->arg);
1338 break;
1339 case 1: // Quote, but don't split
1340 result = STRLEN(eap->arg) + 2;
1341 for (p = eap->arg; *p; ++p)
1342 {
1343 if (enc_dbcs != 0 && (*mb_ptr2len)(p) == 2)
1344 // DBCS can contain \ in a trail byte, skip the
1345 // double-byte character.
1346 ++p;
1347 else
1348 if (*p == '\\' || *p == '"')
1349 ++result;
1350 }
1351
1352 if (buf != NULL)
1353 {
1354 *buf++ = '"';
1355 for (p = eap->arg; *p; ++p)
1356 {
1357 if (enc_dbcs != 0 && (*mb_ptr2len)(p) == 2)
1358 // DBCS can contain \ in a trail byte, copy the
1359 // double-byte character to avoid escaping.
1360 *buf++ = *p++;
1361 else
1362 if (*p == '\\' || *p == '"')
1363 *buf++ = '\\';
1364 *buf++ = *p;
1365 }
1366 *buf = '"';
1367 }
1368
1369 break;
1370 case 2: // Quote and split (<f-args>)
1371 // This is hard, so only do it once, and cache the result
1372 if (*split_buf == NULL)
1373 *split_buf = uc_split_args(eap->arg, split_len);
1374
1375 result = *split_len;
1376 if (buf != NULL && result != 0)
1377 STRCPY(buf, *split_buf);
1378
1379 break;
1380 }
1381 break;
1382
1383 case ct_BANG:
1384 result = eap->forceit ? 1 : 0;
1385 if (quote)
1386 result += 2;
1387 if (buf != NULL)
1388 {
1389 if (quote)
1390 *buf++ = '"';
1391 if (eap->forceit)
1392 *buf++ = '!';
1393 if (quote)
1394 *buf = '"';
1395 }
1396 break;
1397
1398 case ct_LINE1:
1399 case ct_LINE2:
1400 case ct_RANGE:
1401 case ct_COUNT:
1402 {
1403 char num_buf[20];
1404 long num = (type == ct_LINE1) ? eap->line1 :
1405 (type == ct_LINE2) ? eap->line2 :
1406 (type == ct_RANGE) ? eap->addr_count :
1407 (eap->addr_count > 0) ? eap->line2 : cmd->uc_def;
1408 size_t num_len;
1409
1410 sprintf(num_buf, "%ld", num);
1411 num_len = STRLEN(num_buf);
1412 result = num_len;
1413
1414 if (quote)
1415 result += 2;
1416
1417 if (buf != NULL)
1418 {
1419 if (quote)
1420 *buf++ = '"';
1421 STRCPY(buf, num_buf);
1422 buf += num_len;
1423 if (quote)
1424 *buf = '"';
1425 }
1426
1427 break;
1428 }
1429
1430 case ct_MODS:
1431 {
1432 int multi_mods = 0;
1433 typedef struct {
1434 int *varp;
1435 char *name;
1436 } mod_entry_T;
1437 static mod_entry_T mod_entries[] = {
1438#ifdef FEAT_BROWSE_CMD
1439 {&cmdmod.browse, "browse"},
1440#endif
1441#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
1442 {&cmdmod.confirm, "confirm"},
1443#endif
1444 {&cmdmod.hide, "hide"},
1445 {&cmdmod.keepalt, "keepalt"},
1446 {&cmdmod.keepjumps, "keepjumps"},
1447 {&cmdmod.keepmarks, "keepmarks"},
1448 {&cmdmod.keeppatterns, "keeppatterns"},
1449 {&cmdmod.lockmarks, "lockmarks"},
1450 {&cmdmod.noswapfile, "noswapfile"},
1451 {NULL, NULL}
1452 };
1453 int i;
1454
1455 result = quote ? 2 : 0;
1456 if (buf != NULL)
1457 {
1458 if (quote)
1459 *buf++ = '"';
1460 *buf = '\0';
1461 }
1462
1463 // :aboveleft and :leftabove
1464 if (cmdmod.split & WSP_ABOVE)
1465 result += add_cmd_modifier(buf, "aboveleft", &multi_mods);
1466 // :belowright and :rightbelow
1467 if (cmdmod.split & WSP_BELOW)
1468 result += add_cmd_modifier(buf, "belowright", &multi_mods);
1469 // :botright
1470 if (cmdmod.split & WSP_BOT)
1471 result += add_cmd_modifier(buf, "botright", &multi_mods);
1472
1473 // the modifiers that are simple flags
1474 for (i = 0; mod_entries[i].varp != NULL; ++i)
1475 if (*mod_entries[i].varp)
1476 result += add_cmd_modifier(buf, mod_entries[i].name,
1477 &multi_mods);
1478
1479 // TODO: How to support :noautocmd?
1480#ifdef HAVE_SANDBOX
1481 // TODO: How to support :sandbox?
1482#endif
1483 // :silent
1484 if (msg_silent > 0)
1485 result += add_cmd_modifier(buf,
1486 emsg_silent > 0 ? "silent!" : "silent", &multi_mods);
1487 // :tab
1488 if (cmdmod.tab > 0)
1489 result += add_cmd_modifier(buf, "tab", &multi_mods);
1490 // :topleft
1491 if (cmdmod.split & WSP_TOP)
1492 result += add_cmd_modifier(buf, "topleft", &multi_mods);
1493 // TODO: How to support :unsilent?
1494 // :verbose
1495 if (p_verbose > 0)
1496 result += add_cmd_modifier(buf, "verbose", &multi_mods);
1497 // :vertical
1498 if (cmdmod.split & WSP_VERT)
1499 result += add_cmd_modifier(buf, "vertical", &multi_mods);
1500 if (quote && buf != NULL)
1501 {
1502 buf += result - 2;
1503 *buf = '"';
1504 }
1505 break;
1506 }
1507
1508 case ct_REGISTER:
1509 result = eap->regname ? 1 : 0;
1510 if (quote)
1511 result += 2;
1512 if (buf != NULL)
1513 {
1514 if (quote)
1515 *buf++ = '\'';
1516 if (eap->regname)
1517 *buf++ = eap->regname;
1518 if (quote)
1519 *buf = '\'';
1520 }
1521 break;
1522
1523 case ct_LT:
1524 result = 1;
1525 if (buf != NULL)
1526 *buf = '<';
1527 break;
1528
1529 default:
1530 // Not recognized: just copy the '<' and return -1.
1531 result = (size_t)-1;
1532 if (buf != NULL)
1533 *buf = '<';
1534 break;
1535 }
1536
1537 return result;
1538}
1539
1540/*
1541 * Execute a user defined command.
1542 */
1543 void
1544do_ucmd(exarg_T *eap)
1545{
1546 char_u *buf;
1547 char_u *p;
1548 char_u *q;
1549
1550 char_u *start;
1551 char_u *end = NULL;
1552 char_u *ksp;
1553 size_t len, totlen;
1554
1555 size_t split_len = 0;
1556 char_u *split_buf = NULL;
1557 ucmd_T *cmd;
1558#ifdef FEAT_EVAL
1559 sctx_T save_current_sctx = current_sctx;
1560#endif
1561
1562 if (eap->cmdidx == CMD_USER)
1563 cmd = USER_CMD(eap->useridx);
1564 else
1565 cmd = USER_CMD_GA(&curbuf->b_ucmds, eap->useridx);
1566
1567 /*
1568 * Replace <> in the command by the arguments.
1569 * First round: "buf" is NULL, compute length, allocate "buf".
1570 * Second round: copy result into "buf".
1571 */
1572 buf = NULL;
1573 for (;;)
1574 {
1575 p = cmd->uc_rep; // source
1576 q = buf; // destination
1577 totlen = 0;
1578
1579 for (;;)
1580 {
1581 start = vim_strchr(p, '<');
1582 if (start != NULL)
1583 end = vim_strchr(start + 1, '>');
1584 if (buf != NULL)
1585 {
1586 for (ksp = p; *ksp != NUL && *ksp != K_SPECIAL; ++ksp)
1587 ;
1588 if (*ksp == K_SPECIAL
1589 && (start == NULL || ksp < start || end == NULL)
1590 && ((ksp[1] == KS_SPECIAL && ksp[2] == KE_FILLER)
1591# ifdef FEAT_GUI
1592 || (ksp[1] == KS_EXTRA && ksp[2] == (int)KE_CSI)
1593# endif
1594 ))
1595 {
1596 // K_SPECIAL has been put in the buffer as K_SPECIAL
1597 // KS_SPECIAL KE_FILLER, like for mappings, but
1598 // do_cmdline() doesn't handle that, so convert it back.
1599 // Also change K_SPECIAL KS_EXTRA KE_CSI into CSI.
1600 len = ksp - p;
1601 if (len > 0)
1602 {
1603 mch_memmove(q, p, len);
1604 q += len;
1605 }
1606 *q++ = ksp[1] == KS_SPECIAL ? K_SPECIAL : CSI;
1607 p = ksp + 3;
1608 continue;
1609 }
1610 }
1611
1612 // break if no <item> is found
1613 if (start == NULL || end == NULL)
1614 break;
1615
1616 // Include the '>'
1617 ++end;
1618
1619 // Take everything up to the '<'
1620 len = start - p;
1621 if (buf == NULL)
1622 totlen += len;
1623 else
1624 {
1625 mch_memmove(q, p, len);
1626 q += len;
1627 }
1628
1629 len = uc_check_code(start, end - start, q, cmd, eap,
1630 &split_buf, &split_len);
1631 if (len == (size_t)-1)
1632 {
1633 // no match, continue after '<'
1634 p = start + 1;
1635 len = 1;
1636 }
1637 else
1638 p = end;
1639 if (buf == NULL)
1640 totlen += len;
1641 else
1642 q += len;
1643 }
1644 if (buf != NULL) // second time here, finished
1645 {
1646 STRCPY(q, p);
1647 break;
1648 }
1649
1650 totlen += STRLEN(p); // Add on the trailing characters
Bram Moolenaar964b3742019-05-24 18:54:09 +02001651 buf = alloc(totlen + 1);
Bram Moolenaarac9fb182019-04-27 13:04:13 +02001652 if (buf == NULL)
1653 {
1654 vim_free(split_buf);
1655 return;
1656 }
1657 }
1658
1659#ifdef FEAT_EVAL
1660 current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid;
1661#endif
1662 (void)do_cmdline(buf, eap->getline, eap->cookie,
1663 DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED);
1664#ifdef FEAT_EVAL
1665 current_sctx = save_current_sctx;
1666#endif
1667 vim_free(buf);
1668 vim_free(split_buf);
1669}