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