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