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