blob: c04c80e23d56be814fe79b5d9e3c38fb57651a5e [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{
Bram Moolenaarf03e3282019-07-22 21:55:18 +0200312 // In cmdwin, the alternative buffer should be used.
313 buf_T *buf =
314#ifdef FEAT_CMDWIN
315 (cmdwin_type != 0 && get_cmdline_type() == NUL) ? prevwin->w_buffer :
316#endif
317 curbuf;
318
319 if (idx < buf->b_ucmds.ga_len)
320 return USER_CMD_GA(&buf->b_ucmds, idx)->uc_name;
321 idx -= buf->b_ucmds.ga_len;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200322 if (idx < ucmds.ga_len)
323 return USER_CMD(idx)->uc_name;
324 return NULL;
325}
326
327/*
328 * Function given to ExpandGeneric() to obtain the list of user address type
329 * names.
330 */
331 char_u *
332get_user_cmd_addr_type(expand_T *xp UNUSED, int idx)
333{
334 return (char_u *)addr_type_complete[idx].name;
335}
336
337/*
338 * Function given to ExpandGeneric() to obtain the list of user command
339 * attributes.
340 */
341 char_u *
342get_user_cmd_flags(expand_T *xp UNUSED, int idx)
343{
344 static char *user_cmd_flags[] = {
345 "addr", "bang", "bar", "buffer", "complete",
346 "count", "nargs", "range", "register"
347 };
348
349 if (idx >= (int)(sizeof(user_cmd_flags) / sizeof(user_cmd_flags[0])))
350 return NULL;
351 return (char_u *)user_cmd_flags[idx];
352}
353
354/*
355 * Function given to ExpandGeneric() to obtain the list of values for -nargs.
356 */
357 char_u *
358get_user_cmd_nargs(expand_T *xp UNUSED, int idx)
359{
360 static char *user_cmd_nargs[] = {"0", "1", "*", "?", "+"};
361
362 if (idx >= (int)(sizeof(user_cmd_nargs) / sizeof(user_cmd_nargs[0])))
363 return NULL;
364 return (char_u *)user_cmd_nargs[idx];
365}
366
367/*
368 * Function given to ExpandGeneric() to obtain the list of values for
369 * -complete.
370 */
371 char_u *
372get_user_cmd_complete(expand_T *xp UNUSED, int idx)
373{
374 return (char_u *)command_complete[idx].name;
375}
376
377 int
378cmdcomplete_str_to_type(char_u *complete_str)
379{
380 int i;
381
382 for (i = 0; command_complete[i].expand != 0; ++i)
383 if (STRCMP(complete_str, command_complete[i].name) == 0)
384 return command_complete[i].expand;
385
386 return EXPAND_NOTHING;
387}
388
389#endif // FEAT_CMDL_COMPL
390
391/*
392 * List user commands starting with "name[name_len]".
393 */
394 static void
395uc_list(char_u *name, size_t name_len)
396{
397 int i, j;
398 int found = FALSE;
399 ucmd_T *cmd;
400 int len;
401 int over;
402 long a;
403 garray_T *gap;
404
Bram Moolenaarf03e3282019-07-22 21:55:18 +0200405 /* In cmdwin, the alternative buffer should be used. */
406 gap =
407#ifdef FEAT_CMDWIN
408 (cmdwin_type != 0 && get_cmdline_type() == NUL) ?
409 &prevwin->w_buffer->b_ucmds :
410#endif
411 &curbuf->b_ucmds;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200412 for (;;)
413 {
414 for (i = 0; i < gap->ga_len; ++i)
415 {
416 cmd = USER_CMD_GA(gap, i);
417 a = (long)cmd->uc_argt;
418
419 // Skip commands which don't match the requested prefix and
420 // commands filtered out.
421 if (STRNCMP(name, cmd->uc_name, name_len) != 0
422 || message_filtered(cmd->uc_name))
423 continue;
424
425 // Put out the title first time
426 if (!found)
427 msg_puts_title(_("\n Name Args Address Complete Definition"));
428 found = TRUE;
429 msg_putchar('\n');
430 if (got_int)
431 break;
432
433 // Special cases
434 len = 4;
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200435 if (a & EX_BANG)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200436 {
437 msg_putchar('!');
438 --len;
439 }
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200440 if (a & EX_REGSTR)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200441 {
442 msg_putchar('"');
443 --len;
444 }
445 if (gap != &ucmds)
446 {
447 msg_putchar('b');
448 --len;
449 }
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200450 if (a & EX_TRLBAR)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200451 {
452 msg_putchar('|');
453 --len;
454 }
455 while (len-- > 0)
456 msg_putchar(' ');
457
458 msg_outtrans_attr(cmd->uc_name, HL_ATTR(HLF_D));
459 len = (int)STRLEN(cmd->uc_name) + 4;
460
461 do {
462 msg_putchar(' ');
463 ++len;
464 } while (len < 22);
465
466 // "over" is how much longer the name is than the column width for
467 // the name, we'll try to align what comes after.
468 over = len - 22;
469 len = 0;
470
471 // Arguments
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200472 switch ((int)(a & (EX_EXTRA|EX_NOSPC|EX_NEEDARG)))
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200473 {
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200474 case 0: IObuff[len++] = '0'; break;
475 case (EX_EXTRA): IObuff[len++] = '*'; break;
476 case (EX_EXTRA|EX_NOSPC): IObuff[len++] = '?'; break;
477 case (EX_EXTRA|EX_NEEDARG): IObuff[len++] = '+'; break;
478 case (EX_EXTRA|EX_NOSPC|EX_NEEDARG): IObuff[len++] = '1'; break;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200479 }
480
481 do {
482 IObuff[len++] = ' ';
483 } while (len < 5 - over);
484
485 // Address / Range
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200486 if (a & (EX_RANGE|EX_COUNT))
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200487 {
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200488 if (a & EX_COUNT)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200489 {
490 // -count=N
491 sprintf((char *)IObuff + len, "%ldc", cmd->uc_def);
492 len += (int)STRLEN(IObuff + len);
493 }
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200494 else if (a & EX_DFLALL)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200495 IObuff[len++] = '%';
496 else if (cmd->uc_def >= 0)
497 {
498 // -range=N
499 sprintf((char *)IObuff + len, "%ld", cmd->uc_def);
500 len += (int)STRLEN(IObuff + len);
501 }
502 else
503 IObuff[len++] = '.';
504 }
505
506 do {
507 IObuff[len++] = ' ';
508 } while (len < 8 - over);
509
510 // Address Type
Bram Moolenaarb7316892019-05-01 18:08:42 +0200511 for (j = 0; addr_type_complete[j].expand != ADDR_NONE; ++j)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200512 if (addr_type_complete[j].expand != ADDR_LINES
513 && addr_type_complete[j].expand == cmd->uc_addr_type)
514 {
515 STRCPY(IObuff + len, addr_type_complete[j].shortname);
516 len += (int)STRLEN(IObuff + len);
517 break;
518 }
519
520 do {
521 IObuff[len++] = ' ';
522 } while (len < 13 - over);
523
524 // Completion
525 for (j = 0; command_complete[j].expand != 0; ++j)
526 if (command_complete[j].expand == cmd->uc_compl)
527 {
528 STRCPY(IObuff + len, command_complete[j].name);
529 len += (int)STRLEN(IObuff + len);
530 break;
531 }
532
533 do {
534 IObuff[len++] = ' ';
535 } while (len < 25 - over);
536
537 IObuff[len] = '\0';
538 msg_outtrans(IObuff);
539
540 msg_outtrans_special(cmd->uc_rep, FALSE,
541 name_len == 0 ? Columns - 47 : 0);
542#ifdef FEAT_EVAL
543 if (p_verbose > 0)
544 last_set_msg(cmd->uc_script_ctx);
545#endif
546 out_flush();
547 ui_breakcheck();
548 if (got_int)
549 break;
550 }
551 if (gap == &ucmds || i < gap->ga_len)
552 break;
553 gap = &ucmds;
554 }
555
556 if (!found)
557 msg(_("No user-defined commands found"));
558}
559
560 char *
561uc_fun_cmd(void)
562{
563 static char_u fcmd[] = {0x84, 0xaf, 0x60, 0xb9, 0xaf, 0xb5, 0x60, 0xa4,
564 0xa5, 0xad, 0xa1, 0xae, 0xa4, 0x60, 0xa1, 0x60,
565 0xb3, 0xa8, 0xb2, 0xb5, 0xa2, 0xa2, 0xa5, 0xb2,
566 0xb9, 0x7f, 0};
567 int i;
568
569 for (i = 0; fcmd[i]; ++i)
570 IObuff[i] = fcmd[i] - 0x40;
571 IObuff[i] = 0;
572 return (char *)IObuff;
573}
574
575/*
576 * Parse address type argument
577 */
578 static int
579parse_addr_type_arg(
580 char_u *value,
581 int vallen,
Bram Moolenaarb7316892019-05-01 18:08:42 +0200582 cmd_addr_T *addr_type_arg)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200583{
584 int i, a, b;
585
Bram Moolenaarb7316892019-05-01 18:08:42 +0200586 for (i = 0; addr_type_complete[i].expand != ADDR_NONE; ++i)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200587 {
588 a = (int)STRLEN(addr_type_complete[i].name) == vallen;
589 b = STRNCMP(value, addr_type_complete[i].name, vallen) == 0;
590 if (a && b)
591 {
592 *addr_type_arg = addr_type_complete[i].expand;
593 break;
594 }
595 }
596
Bram Moolenaarb7316892019-05-01 18:08:42 +0200597 if (addr_type_complete[i].expand == ADDR_NONE)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200598 {
599 char_u *err = value;
600
601 for (i = 0; err[i] != NUL && !VIM_ISWHITE(err[i]); i++)
602 ;
603 err[i] = NUL;
604 semsg(_("E180: Invalid address type value: %s"), err);
605 return FAIL;
606 }
607
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200608 return OK;
609}
610
611/*
612 * Parse a completion argument "value[vallen]".
613 * The detected completion goes in "*complp", argument type in "*argt".
614 * When there is an argument, for function and user defined completion, it's
615 * copied to allocated memory and stored in "*compl_arg".
616 * Returns FAIL if something is wrong.
617 */
618 int
619parse_compl_arg(
620 char_u *value,
621 int vallen,
622 int *complp,
623 long *argt,
624 char_u **compl_arg UNUSED)
625{
626 char_u *arg = NULL;
627# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
628 size_t arglen = 0;
629# endif
630 int i;
631 int valend = vallen;
632
633 // Look for any argument part - which is the part after any ','
634 for (i = 0; i < vallen; ++i)
635 {
636 if (value[i] == ',')
637 {
638 arg = &value[i + 1];
639# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
640 arglen = vallen - i - 1;
641# endif
642 valend = i;
643 break;
644 }
645 }
646
647 for (i = 0; command_complete[i].expand != 0; ++i)
648 {
649 if ((int)STRLEN(command_complete[i].name) == valend
650 && STRNCMP(value, command_complete[i].name, valend) == 0)
651 {
652 *complp = command_complete[i].expand;
653 if (command_complete[i].expand == EXPAND_BUFFERS)
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200654 *argt |= EX_BUFNAME;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200655 else if (command_complete[i].expand == EXPAND_DIRECTORIES
656 || command_complete[i].expand == EXPAND_FILES)
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200657 *argt |= EX_XFILE;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200658 break;
659 }
660 }
661
662 if (command_complete[i].expand == 0)
663 {
664 semsg(_("E180: Invalid complete value: %s"), value);
665 return FAIL;
666 }
667
668# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
669 if (*complp != EXPAND_USER_DEFINED && *complp != EXPAND_USER_LIST
670 && arg != NULL)
671# else
672 if (arg != NULL)
673# endif
674 {
675 emsg(_("E468: Completion argument only allowed for custom completion"));
676 return FAIL;
677 }
678
679# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
680 if ((*complp == EXPAND_USER_DEFINED || *complp == EXPAND_USER_LIST)
681 && arg == NULL)
682 {
683 emsg(_("E467: Custom completion requires a function argument"));
684 return FAIL;
685 }
686
687 if (arg != NULL)
688 *compl_arg = vim_strnsave(arg, (int)arglen);
689# endif
690 return OK;
691}
692
693/*
694 * Scan attributes in the ":command" command.
695 * Return FAIL when something is wrong.
696 */
697 static int
698uc_scan_attr(
699 char_u *attr,
700 size_t len,
701 long *argt,
702 long *def,
703 int *flags,
Bram Moolenaar52111f82019-04-29 21:30:45 +0200704 int *complp,
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200705 char_u **compl_arg,
Bram Moolenaarb7316892019-05-01 18:08:42 +0200706 cmd_addr_T *addr_type_arg)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200707{
708 char_u *p;
709
710 if (len == 0)
711 {
712 emsg(_("E175: No attribute specified"));
713 return FAIL;
714 }
715
716 // First, try the simple attributes (no arguments)
717 if (STRNICMP(attr, "bang", len) == 0)
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200718 *argt |= EX_BANG;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200719 else if (STRNICMP(attr, "buffer", len) == 0)
720 *flags |= UC_BUFFER;
721 else if (STRNICMP(attr, "register", len) == 0)
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200722 *argt |= EX_REGSTR;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200723 else if (STRNICMP(attr, "bar", len) == 0)
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200724 *argt |= EX_TRLBAR;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200725 else
726 {
727 int i;
728 char_u *val = NULL;
729 size_t vallen = 0;
730 size_t attrlen = len;
731
732 // Look for the attribute name - which is the part before any '='
733 for (i = 0; i < (int)len; ++i)
734 {
735 if (attr[i] == '=')
736 {
737 val = &attr[i + 1];
738 vallen = len - i - 1;
739 attrlen = i;
740 break;
741 }
742 }
743
744 if (STRNICMP(attr, "nargs", attrlen) == 0)
745 {
746 if (vallen == 1)
747 {
748 if (*val == '0')
749 // Do nothing - this is the default
750 ;
751 else if (*val == '1')
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200752 *argt |= (EX_EXTRA | EX_NOSPC | EX_NEEDARG);
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200753 else if (*val == '*')
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200754 *argt |= EX_EXTRA;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200755 else if (*val == '?')
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200756 *argt |= (EX_EXTRA | EX_NOSPC);
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200757 else if (*val == '+')
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200758 *argt |= (EX_EXTRA | EX_NEEDARG);
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200759 else
760 goto wrong_nargs;
761 }
762 else
763 {
764wrong_nargs:
765 emsg(_("E176: Invalid number of arguments"));
766 return FAIL;
767 }
768 }
769 else if (STRNICMP(attr, "range", attrlen) == 0)
770 {
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200771 *argt |= EX_RANGE;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200772 if (vallen == 1 && *val == '%')
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200773 *argt |= EX_DFLALL;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200774 else if (val != NULL)
775 {
776 p = val;
777 if (*def >= 0)
778 {
779two_count:
780 emsg(_("E177: Count cannot be specified twice"));
781 return FAIL;
782 }
783
784 *def = getdigits(&p);
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200785 *argt |= EX_ZEROR;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200786
787 if (p != val + vallen || vallen == 0)
788 {
789invalid_count:
790 emsg(_("E178: Invalid default value for count"));
791 return FAIL;
792 }
793 }
Bram Moolenaarb7316892019-05-01 18:08:42 +0200794 // default for -range is using buffer lines
795 if (*addr_type_arg == ADDR_NONE)
796 *addr_type_arg = ADDR_LINES;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200797 }
798 else if (STRNICMP(attr, "count", attrlen) == 0)
799 {
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200800 *argt |= (EX_COUNT | EX_ZEROR | EX_RANGE);
Bram Moolenaarb7316892019-05-01 18:08:42 +0200801 // default for -count is using any number
802 if (*addr_type_arg == ADDR_NONE)
803 *addr_type_arg = ADDR_OTHER;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200804
805 if (val != NULL)
806 {
807 p = val;
808 if (*def >= 0)
809 goto two_count;
810
811 *def = getdigits(&p);
812
813 if (p != val + vallen)
814 goto invalid_count;
815 }
816
817 if (*def < 0)
818 *def = 0;
819 }
820 else if (STRNICMP(attr, "complete", attrlen) == 0)
821 {
822 if (val == NULL)
823 {
824 emsg(_("E179: argument required for -complete"));
825 return FAIL;
826 }
827
Bram Moolenaar52111f82019-04-29 21:30:45 +0200828 if (parse_compl_arg(val, (int)vallen, complp, argt, compl_arg)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200829 == FAIL)
830 return FAIL;
831 }
832 else if (STRNICMP(attr, "addr", attrlen) == 0)
833 {
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200834 *argt |= EX_RANGE;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200835 if (val == NULL)
836 {
837 emsg(_("E179: argument required for -addr"));
838 return FAIL;
839 }
Bram Moolenaare4f5f3a2019-05-04 14:05:08 +0200840 if (parse_addr_type_arg(val, (int)vallen, addr_type_arg) == FAIL)
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200841 return FAIL;
Bram Moolenaare4f5f3a2019-05-04 14:05:08 +0200842 if (*addr_type_arg != ADDR_LINES)
Bram Moolenaar8071cb22019-07-12 17:58:01 +0200843 *argt |= EX_ZEROR;
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200844 }
845 else
846 {
847 char_u ch = attr[len];
848 attr[len] = '\0';
849 semsg(_("E181: Invalid attribute: %s"), attr);
850 attr[len] = ch;
851 return FAIL;
852 }
853 }
854
855 return OK;
856}
857
858/*
859 * Add a user command to the list or replace an existing one.
860 */
861 static int
862uc_add_command(
863 char_u *name,
864 size_t name_len,
865 char_u *rep,
866 long argt,
867 long def,
868 int flags,
869 int compl,
870 char_u *compl_arg UNUSED,
Bram Moolenaarb7316892019-05-01 18:08:42 +0200871 cmd_addr_T addr_type,
Bram Moolenaarac9fb182019-04-27 13:04:13 +0200872 int force)
873{
874 ucmd_T *cmd = NULL;
875 char_u *p;
876 int i;
877 int cmp = 1;
878 char_u *rep_buf = NULL;
879 garray_T *gap;
880
881 replace_termcodes(rep, &rep_buf, FALSE, FALSE, FALSE);
882 if (rep_buf == NULL)
883 {
884 // Can't replace termcodes - try using the string as is
885 rep_buf = vim_strsave(rep);
886
887 // Give up if out of memory
888 if (rep_buf == NULL)
889 return FAIL;
890 }
891
892 // get address of growarray: global or in curbuf
893 if (flags & UC_BUFFER)
894 {
895 gap = &curbuf->b_ucmds;
896 if (gap->ga_itemsize == 0)
897 ga_init2(gap, (int)sizeof(ucmd_T), 4);
898 }
899 else
900 gap = &ucmds;
901
902 // Search for the command in the already defined commands.
903 for (i = 0; i < gap->ga_len; ++i)
904 {
905 size_t len;
906
907 cmd = USER_CMD_GA(gap, i);
908 len = STRLEN(cmd->uc_name);
909 cmp = STRNCMP(name, cmd->uc_name, name_len);
910 if (cmp == 0)
911 {
912 if (name_len < len)
913 cmp = -1;
914 else if (name_len > len)
915 cmp = 1;
916 }
917
918 if (cmp == 0)
919 {
920 // Command can be replaced with "command!" and when sourcing the
921 // same script again, but only once.
922 if (!force
923#ifdef FEAT_EVAL
924 && (cmd->uc_script_ctx.sc_sid != current_sctx.sc_sid
925 || cmd->uc_script_ctx.sc_seq == current_sctx.sc_seq)
926#endif
927 )
928 {
929 semsg(_("E174: Command already exists: add ! to replace it: %s"),
930 name);
931 goto fail;
932 }
933
934 VIM_CLEAR(cmd->uc_rep);
935#if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
936 VIM_CLEAR(cmd->uc_compl_arg);
937#endif
938 break;
939 }
940
941 // Stop as soon as we pass the name to add
942 if (cmp < 0)
943 break;
944 }
945
946 // Extend the array unless we're replacing an existing command
947 if (cmp != 0)
948 {
949 if (ga_grow(gap, 1) != OK)
950 goto fail;
951 if ((p = vim_strnsave(name, (int)name_len)) == NULL)
952 goto fail;
953
954 cmd = USER_CMD_GA(gap, i);
955 mch_memmove(cmd + 1, cmd, (gap->ga_len - i) * sizeof(ucmd_T));
956
957 ++gap->ga_len;
958
959 cmd->uc_name = p;
960 }
961
962 cmd->uc_rep = rep_buf;
963 cmd->uc_argt = argt;
964 cmd->uc_def = def;
965 cmd->uc_compl = compl;
966#ifdef FEAT_EVAL
967 cmd->uc_script_ctx = current_sctx;
968 cmd->uc_script_ctx.sc_lnum += sourcing_lnum;
969# ifdef FEAT_CMDL_COMPL
970 cmd->uc_compl_arg = compl_arg;
971# endif
972#endif
973 cmd->uc_addr_type = addr_type;
974
975 return OK;
976
977fail:
978 vim_free(rep_buf);
979#if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
980 vim_free(compl_arg);
981#endif
982 return FAIL;
983}
984
985/*
986 * ":command ..." implementation
987 */
988 void
989ex_command(exarg_T *eap)
990{
Bram Moolenaarb7316892019-05-01 18:08:42 +0200991 char_u *name;
992 char_u *end;
993 char_u *p;
994 long argt = 0;
995 long def = -1;
996 int flags = 0;
997 int compl = EXPAND_NOTHING;
998 char_u *compl_arg = NULL;
999 cmd_addr_T addr_type_arg = ADDR_NONE;
1000 int has_attr = (eap->arg[0] == '-');
1001 int name_len;
Bram Moolenaarac9fb182019-04-27 13:04:13 +02001002
1003 p = eap->arg;
1004
1005 // Check for attributes
1006 while (*p == '-')
1007 {
1008 ++p;
1009 end = skiptowhite(p);
1010 if (uc_scan_attr(p, end - p, &argt, &def, &flags, &compl,
1011 &compl_arg, &addr_type_arg) == FAIL)
1012 return;
1013 p = skipwhite(end);
1014 }
1015
1016 // Get the name (if any) and skip to the following argument
1017 name = p;
1018 if (ASCII_ISALPHA(*p))
1019 while (ASCII_ISALNUM(*p))
1020 ++p;
1021 if (!ends_excmd(*p) && !VIM_ISWHITE(*p))
1022 {
1023 emsg(_("E182: Invalid command name"));
1024 return;
1025 }
1026 end = p;
1027 name_len = (int)(end - name);
1028
1029 // If there is nothing after the name, and no attributes were specified,
1030 // we are listing commands
1031 p = skipwhite(end);
1032 if (!has_attr && ends_excmd(*p))
1033 {
1034 uc_list(name, end - name);
1035 }
1036 else if (!ASCII_ISUPPER(*name))
1037 {
1038 emsg(_("E183: User defined commands must start with an uppercase letter"));
1039 return;
1040 }
1041 else if ((name_len == 1 && *name == 'X')
1042 || (name_len <= 4
1043 && STRNCMP(name, "Next", name_len > 4 ? 4 : name_len) == 0))
1044 {
1045 emsg(_("E841: Reserved name, cannot be used for user defined command"));
1046 return;
1047 }
1048 else
1049 uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg,
1050 addr_type_arg, eap->forceit);
1051}
1052
1053/*
1054 * ":comclear" implementation
1055 * Clear all user commands, global and for current buffer.
1056 */
1057 void
1058ex_comclear(exarg_T *eap UNUSED)
1059{
1060 uc_clear(&ucmds);
Bram Moolenaare5c83282019-05-03 23:15:37 +02001061 if (curbuf != NULL)
1062 uc_clear(&curbuf->b_ucmds);
Bram Moolenaarac9fb182019-04-27 13:04:13 +02001063}
1064
1065/*
1066 * Clear all user commands for "gap".
1067 */
1068 void
1069uc_clear(garray_T *gap)
1070{
1071 int i;
1072 ucmd_T *cmd;
1073
1074 for (i = 0; i < gap->ga_len; ++i)
1075 {
1076 cmd = USER_CMD_GA(gap, i);
1077 vim_free(cmd->uc_name);
1078 vim_free(cmd->uc_rep);
1079# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
1080 vim_free(cmd->uc_compl_arg);
1081# endif
1082 }
1083 ga_clear(gap);
1084}
1085
1086/*
1087 * ":delcommand" implementation
1088 */
1089 void
1090ex_delcommand(exarg_T *eap)
1091{
1092 int i = 0;
1093 ucmd_T *cmd = NULL;
1094 int cmp = -1;
1095 garray_T *gap;
1096
1097 gap = &curbuf->b_ucmds;
1098 for (;;)
1099 {
1100 for (i = 0; i < gap->ga_len; ++i)
1101 {
1102 cmd = USER_CMD_GA(gap, i);
1103 cmp = STRCMP(eap->arg, cmd->uc_name);
1104 if (cmp <= 0)
1105 break;
1106 }
1107 if (gap == &ucmds || cmp == 0)
1108 break;
1109 gap = &ucmds;
1110 }
1111
1112 if (cmp != 0)
1113 {
1114 semsg(_("E184: No such user-defined command: %s"), eap->arg);
1115 return;
1116 }
1117
1118 vim_free(cmd->uc_name);
1119 vim_free(cmd->uc_rep);
1120# if defined(FEAT_EVAL) && defined(FEAT_CMDL_COMPL)
1121 vim_free(cmd->uc_compl_arg);
1122# endif
1123
1124 --gap->ga_len;
1125
1126 if (i < gap->ga_len)
1127 mch_memmove(cmd, cmd + 1, (gap->ga_len - i) * sizeof(ucmd_T));
1128}
1129
1130/*
1131 * Split and quote args for <f-args>.
1132 */
1133 static char_u *
1134uc_split_args(char_u *arg, size_t *lenp)
1135{
1136 char_u *buf;
1137 char_u *p;
1138 char_u *q;
1139 int len;
1140
1141 // Precalculate length
1142 p = arg;
1143 len = 2; // Initial and final quotes
1144
1145 while (*p)
1146 {
1147 if (p[0] == '\\' && p[1] == '\\')
1148 {
1149 len += 2;
1150 p += 2;
1151 }
1152 else if (p[0] == '\\' && VIM_ISWHITE(p[1]))
1153 {
1154 len += 1;
1155 p += 2;
1156 }
1157 else if (*p == '\\' || *p == '"')
1158 {
1159 len += 2;
1160 p += 1;
1161 }
1162 else if (VIM_ISWHITE(*p))
1163 {
1164 p = skipwhite(p);
1165 if (*p == NUL)
1166 break;
1167 len += 3; // ","
1168 }
1169 else
1170 {
1171 int charlen = (*mb_ptr2len)(p);
1172
1173 len += charlen;
1174 p += charlen;
1175 }
1176 }
1177
1178 buf = alloc(len + 1);
1179 if (buf == NULL)
1180 {
1181 *lenp = 0;
1182 return buf;
1183 }
1184
1185 p = arg;
1186 q = buf;
1187 *q++ = '"';
1188 while (*p)
1189 {
1190 if (p[0] == '\\' && p[1] == '\\')
1191 {
1192 *q++ = '\\';
1193 *q++ = '\\';
1194 p += 2;
1195 }
1196 else if (p[0] == '\\' && VIM_ISWHITE(p[1]))
1197 {
1198 *q++ = p[1];
1199 p += 2;
1200 }
1201 else if (*p == '\\' || *p == '"')
1202 {
1203 *q++ = '\\';
1204 *q++ = *p++;
1205 }
1206 else if (VIM_ISWHITE(*p))
1207 {
1208 p = skipwhite(p);
1209 if (*p == NUL)
1210 break;
1211 *q++ = '"';
1212 *q++ = ',';
1213 *q++ = '"';
1214 }
1215 else
1216 {
1217 MB_COPY_CHAR(p, q);
1218 }
1219 }
1220 *q++ = '"';
1221 *q = 0;
1222
1223 *lenp = len;
1224 return buf;
1225}
1226
1227 static size_t
1228add_cmd_modifier(char_u *buf, char *mod_str, int *multi_mods)
1229{
1230 size_t result;
1231
1232 result = STRLEN(mod_str);
1233 if (*multi_mods)
1234 result += 1;
1235 if (buf != NULL)
1236 {
1237 if (*multi_mods)
1238 STRCAT(buf, " ");
1239 STRCAT(buf, mod_str);
1240 }
1241
1242 *multi_mods = 1;
1243
1244 return result;
1245}
1246
1247/*
1248 * Check for a <> code in a user command.
1249 * "code" points to the '<'. "len" the length of the <> (inclusive).
1250 * "buf" is where the result is to be added.
1251 * "split_buf" points to a buffer used for splitting, caller should free it.
1252 * "split_len" is the length of what "split_buf" contains.
1253 * Returns the length of the replacement, which has been added to "buf".
1254 * Returns -1 if there was no match, and only the "<" has been copied.
1255 */
1256 static size_t
1257uc_check_code(
1258 char_u *code,
1259 size_t len,
1260 char_u *buf,
1261 ucmd_T *cmd, // the user command we're expanding
1262 exarg_T *eap, // ex arguments
1263 char_u **split_buf,
1264 size_t *split_len)
1265{
1266 size_t result = 0;
1267 char_u *p = code + 1;
1268 size_t l = len - 2;
1269 int quote = 0;
1270 enum {
1271 ct_ARGS,
1272 ct_BANG,
1273 ct_COUNT,
1274 ct_LINE1,
1275 ct_LINE2,
1276 ct_RANGE,
1277 ct_MODS,
1278 ct_REGISTER,
1279 ct_LT,
1280 ct_NONE
1281 } type = ct_NONE;
1282
1283 if ((vim_strchr((char_u *)"qQfF", *p) != NULL) && p[1] == '-')
1284 {
1285 quote = (*p == 'q' || *p == 'Q') ? 1 : 2;
1286 p += 2;
1287 l -= 2;
1288 }
1289
1290 ++l;
1291 if (l <= 1)
1292 type = ct_NONE;
1293 else if (STRNICMP(p, "args>", l) == 0)
1294 type = ct_ARGS;
1295 else if (STRNICMP(p, "bang>", l) == 0)
1296 type = ct_BANG;
1297 else if (STRNICMP(p, "count>", l) == 0)
1298 type = ct_COUNT;
1299 else if (STRNICMP(p, "line1>", l) == 0)
1300 type = ct_LINE1;
1301 else if (STRNICMP(p, "line2>", l) == 0)
1302 type = ct_LINE2;
1303 else if (STRNICMP(p, "range>", l) == 0)
1304 type = ct_RANGE;
1305 else if (STRNICMP(p, "lt>", l) == 0)
1306 type = ct_LT;
1307 else if (STRNICMP(p, "reg>", l) == 0 || STRNICMP(p, "register>", l) == 0)
1308 type = ct_REGISTER;
1309 else if (STRNICMP(p, "mods>", l) == 0)
1310 type = ct_MODS;
1311
1312 switch (type)
1313 {
1314 case ct_ARGS:
1315 // Simple case first
1316 if (*eap->arg == NUL)
1317 {
1318 if (quote == 1)
1319 {
1320 result = 2;
1321 if (buf != NULL)
1322 STRCPY(buf, "''");
1323 }
1324 else
1325 result = 0;
1326 break;
1327 }
1328
1329 // When specified there is a single argument don't split it.
1330 // Works for ":Cmd %" when % is "a b c".
Bram Moolenaar8071cb22019-07-12 17:58:01 +02001331 if ((eap->argt & EX_NOSPC) && quote == 2)
Bram Moolenaarac9fb182019-04-27 13:04:13 +02001332 quote = 1;
1333
1334 switch (quote)
1335 {
1336 case 0: // No quoting, no splitting
1337 result = STRLEN(eap->arg);
1338 if (buf != NULL)
1339 STRCPY(buf, eap->arg);
1340 break;
1341 case 1: // Quote, but don't split
1342 result = STRLEN(eap->arg) + 2;
1343 for (p = eap->arg; *p; ++p)
1344 {
1345 if (enc_dbcs != 0 && (*mb_ptr2len)(p) == 2)
1346 // DBCS can contain \ in a trail byte, skip the
1347 // double-byte character.
1348 ++p;
1349 else
1350 if (*p == '\\' || *p == '"')
1351 ++result;
1352 }
1353
1354 if (buf != NULL)
1355 {
1356 *buf++ = '"';
1357 for (p = eap->arg; *p; ++p)
1358 {
1359 if (enc_dbcs != 0 && (*mb_ptr2len)(p) == 2)
1360 // DBCS can contain \ in a trail byte, copy the
1361 // double-byte character to avoid escaping.
1362 *buf++ = *p++;
1363 else
1364 if (*p == '\\' || *p == '"')
1365 *buf++ = '\\';
1366 *buf++ = *p;
1367 }
1368 *buf = '"';
1369 }
1370
1371 break;
1372 case 2: // Quote and split (<f-args>)
1373 // This is hard, so only do it once, and cache the result
1374 if (*split_buf == NULL)
1375 *split_buf = uc_split_args(eap->arg, split_len);
1376
1377 result = *split_len;
1378 if (buf != NULL && result != 0)
1379 STRCPY(buf, *split_buf);
1380
1381 break;
1382 }
1383 break;
1384
1385 case ct_BANG:
1386 result = eap->forceit ? 1 : 0;
1387 if (quote)
1388 result += 2;
1389 if (buf != NULL)
1390 {
1391 if (quote)
1392 *buf++ = '"';
1393 if (eap->forceit)
1394 *buf++ = '!';
1395 if (quote)
1396 *buf = '"';
1397 }
1398 break;
1399
1400 case ct_LINE1:
1401 case ct_LINE2:
1402 case ct_RANGE:
1403 case ct_COUNT:
1404 {
1405 char num_buf[20];
1406 long num = (type == ct_LINE1) ? eap->line1 :
1407 (type == ct_LINE2) ? eap->line2 :
1408 (type == ct_RANGE) ? eap->addr_count :
1409 (eap->addr_count > 0) ? eap->line2 : cmd->uc_def;
1410 size_t num_len;
1411
1412 sprintf(num_buf, "%ld", num);
1413 num_len = STRLEN(num_buf);
1414 result = num_len;
1415
1416 if (quote)
1417 result += 2;
1418
1419 if (buf != NULL)
1420 {
1421 if (quote)
1422 *buf++ = '"';
1423 STRCPY(buf, num_buf);
1424 buf += num_len;
1425 if (quote)
1426 *buf = '"';
1427 }
1428
1429 break;
1430 }
1431
1432 case ct_MODS:
1433 {
1434 int multi_mods = 0;
1435 typedef struct {
1436 int *varp;
1437 char *name;
1438 } mod_entry_T;
1439 static mod_entry_T mod_entries[] = {
1440#ifdef FEAT_BROWSE_CMD
1441 {&cmdmod.browse, "browse"},
1442#endif
1443#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
1444 {&cmdmod.confirm, "confirm"},
1445#endif
1446 {&cmdmod.hide, "hide"},
1447 {&cmdmod.keepalt, "keepalt"},
1448 {&cmdmod.keepjumps, "keepjumps"},
1449 {&cmdmod.keepmarks, "keepmarks"},
1450 {&cmdmod.keeppatterns, "keeppatterns"},
1451 {&cmdmod.lockmarks, "lockmarks"},
1452 {&cmdmod.noswapfile, "noswapfile"},
1453 {NULL, NULL}
1454 };
1455 int i;
1456
1457 result = quote ? 2 : 0;
1458 if (buf != NULL)
1459 {
1460 if (quote)
1461 *buf++ = '"';
1462 *buf = '\0';
1463 }
1464
1465 // :aboveleft and :leftabove
1466 if (cmdmod.split & WSP_ABOVE)
1467 result += add_cmd_modifier(buf, "aboveleft", &multi_mods);
1468 // :belowright and :rightbelow
1469 if (cmdmod.split & WSP_BELOW)
1470 result += add_cmd_modifier(buf, "belowright", &multi_mods);
1471 // :botright
1472 if (cmdmod.split & WSP_BOT)
1473 result += add_cmd_modifier(buf, "botright", &multi_mods);
1474
1475 // the modifiers that are simple flags
1476 for (i = 0; mod_entries[i].varp != NULL; ++i)
1477 if (*mod_entries[i].varp)
1478 result += add_cmd_modifier(buf, mod_entries[i].name,
1479 &multi_mods);
1480
1481 // TODO: How to support :noautocmd?
1482#ifdef HAVE_SANDBOX
1483 // TODO: How to support :sandbox?
1484#endif
1485 // :silent
1486 if (msg_silent > 0)
1487 result += add_cmd_modifier(buf,
1488 emsg_silent > 0 ? "silent!" : "silent", &multi_mods);
1489 // :tab
1490 if (cmdmod.tab > 0)
1491 result += add_cmd_modifier(buf, "tab", &multi_mods);
1492 // :topleft
1493 if (cmdmod.split & WSP_TOP)
1494 result += add_cmd_modifier(buf, "topleft", &multi_mods);
1495 // TODO: How to support :unsilent?
1496 // :verbose
1497 if (p_verbose > 0)
1498 result += add_cmd_modifier(buf, "verbose", &multi_mods);
1499 // :vertical
1500 if (cmdmod.split & WSP_VERT)
1501 result += add_cmd_modifier(buf, "vertical", &multi_mods);
1502 if (quote && buf != NULL)
1503 {
1504 buf += result - 2;
1505 *buf = '"';
1506 }
1507 break;
1508 }
1509
1510 case ct_REGISTER:
1511 result = eap->regname ? 1 : 0;
1512 if (quote)
1513 result += 2;
1514 if (buf != NULL)
1515 {
1516 if (quote)
1517 *buf++ = '\'';
1518 if (eap->regname)
1519 *buf++ = eap->regname;
1520 if (quote)
1521 *buf = '\'';
1522 }
1523 break;
1524
1525 case ct_LT:
1526 result = 1;
1527 if (buf != NULL)
1528 *buf = '<';
1529 break;
1530
1531 default:
1532 // Not recognized: just copy the '<' and return -1.
1533 result = (size_t)-1;
1534 if (buf != NULL)
1535 *buf = '<';
1536 break;
1537 }
1538
1539 return result;
1540}
1541
1542/*
1543 * Execute a user defined command.
1544 */
1545 void
1546do_ucmd(exarg_T *eap)
1547{
1548 char_u *buf;
1549 char_u *p;
1550 char_u *q;
1551
1552 char_u *start;
1553 char_u *end = NULL;
1554 char_u *ksp;
1555 size_t len, totlen;
1556
1557 size_t split_len = 0;
1558 char_u *split_buf = NULL;
1559 ucmd_T *cmd;
1560#ifdef FEAT_EVAL
1561 sctx_T save_current_sctx = current_sctx;
1562#endif
1563
1564 if (eap->cmdidx == CMD_USER)
1565 cmd = USER_CMD(eap->useridx);
1566 else
1567 cmd = USER_CMD_GA(&curbuf->b_ucmds, eap->useridx);
1568
1569 /*
1570 * Replace <> in the command by the arguments.
1571 * First round: "buf" is NULL, compute length, allocate "buf".
1572 * Second round: copy result into "buf".
1573 */
1574 buf = NULL;
1575 for (;;)
1576 {
1577 p = cmd->uc_rep; // source
1578 q = buf; // destination
1579 totlen = 0;
1580
1581 for (;;)
1582 {
1583 start = vim_strchr(p, '<');
1584 if (start != NULL)
1585 end = vim_strchr(start + 1, '>');
1586 if (buf != NULL)
1587 {
1588 for (ksp = p; *ksp != NUL && *ksp != K_SPECIAL; ++ksp)
1589 ;
1590 if (*ksp == K_SPECIAL
1591 && (start == NULL || ksp < start || end == NULL)
1592 && ((ksp[1] == KS_SPECIAL && ksp[2] == KE_FILLER)
1593# ifdef FEAT_GUI
1594 || (ksp[1] == KS_EXTRA && ksp[2] == (int)KE_CSI)
1595# endif
1596 ))
1597 {
1598 // K_SPECIAL has been put in the buffer as K_SPECIAL
1599 // KS_SPECIAL KE_FILLER, like for mappings, but
1600 // do_cmdline() doesn't handle that, so convert it back.
1601 // Also change K_SPECIAL KS_EXTRA KE_CSI into CSI.
1602 len = ksp - p;
1603 if (len > 0)
1604 {
1605 mch_memmove(q, p, len);
1606 q += len;
1607 }
1608 *q++ = ksp[1] == KS_SPECIAL ? K_SPECIAL : CSI;
1609 p = ksp + 3;
1610 continue;
1611 }
1612 }
1613
1614 // break if no <item> is found
1615 if (start == NULL || end == NULL)
1616 break;
1617
1618 // Include the '>'
1619 ++end;
1620
1621 // Take everything up to the '<'
1622 len = start - p;
1623 if (buf == NULL)
1624 totlen += len;
1625 else
1626 {
1627 mch_memmove(q, p, len);
1628 q += len;
1629 }
1630
1631 len = uc_check_code(start, end - start, q, cmd, eap,
1632 &split_buf, &split_len);
1633 if (len == (size_t)-1)
1634 {
1635 // no match, continue after '<'
1636 p = start + 1;
1637 len = 1;
1638 }
1639 else
1640 p = end;
1641 if (buf == NULL)
1642 totlen += len;
1643 else
1644 q += len;
1645 }
1646 if (buf != NULL) // second time here, finished
1647 {
1648 STRCPY(q, p);
1649 break;
1650 }
1651
1652 totlen += STRLEN(p); // Add on the trailing characters
Bram Moolenaar964b3742019-05-24 18:54:09 +02001653 buf = alloc(totlen + 1);
Bram Moolenaarac9fb182019-04-27 13:04:13 +02001654 if (buf == NULL)
1655 {
1656 vim_free(split_buf);
1657 return;
1658 }
1659 }
1660
1661#ifdef FEAT_EVAL
1662 current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid;
1663#endif
1664 (void)do_cmdline(buf, eap->getline, eap->cookie,
1665 DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED);
1666#ifdef FEAT_EVAL
1667 current_sctx = save_current_sctx;
1668#endif
1669 vim_free(buf);
1670 vim_free(split_buf);
1671}