blob: 280bf8334873894f6ca23b7cd5b715b6d44b03eb [file] [log] [blame]
Bram Moolenaar66b51422019-08-18 21:44:12 +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 * cmdexpand.c: functions for command-line completion
12 */
13
14#include "vim.h"
15
16static int cmd_showtail; // Only show path tail in lists ?
17
18static void set_expand_context(expand_T *xp);
Bram Moolenaar61d7c0d2020-01-05 14:38:40 +010019static int ExpandGeneric(expand_T *xp, regmatch_T *regmatch,
20 int *num_file, char_u ***file,
21 char_u *((*func)(expand_T *, int)), int escaped);
Bram Moolenaar66b51422019-08-18 21:44:12 +020022static int ExpandFromContext(expand_T *xp, char_u *, int *, char_u ***, int);
23static int expand_showtail(expand_T *xp);
Bram Moolenaar66b51422019-08-18 21:44:12 +020024static int expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, int flagsarg);
Bram Moolenaar0a52df52019-08-18 22:26:31 +020025#if defined(FEAT_EVAL)
Bram Moolenaar66b51422019-08-18 21:44:12 +020026static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***file);
27static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file);
Bram Moolenaar66b51422019-08-18 21:44:12 +020028#endif
29
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +000030#ifdef FEAT_WILDMENU
31// "compl_match_array" points the currently displayed list of entries in the
32// popup menu. It is NULL when there is no popup menu.
33static pumitem_T *compl_match_array = NULL;
34static int compl_match_arraysize;
35// First column in cmdline of the matched item for completion.
36static int compl_startcol;
37static int compl_selected;
38#endif
39
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +000040#define SHOW_FILE_TEXT(m) (showtail ? sm_gettail(files_found[m]) : files_found[m])
41
Bram Moolenaar66b51422019-08-18 21:44:12 +020042 static int
43sort_func_compare(const void *s1, const void *s2)
44{
45 char_u *p1 = *(char_u **)s1;
46 char_u *p2 = *(char_u **)s2;
47
48 if (*p1 != '<' && *p2 == '<') return -1;
49 if (*p1 == '<' && *p2 != '<') return 1;
50 return STRCMP(p1, p2);
51}
Bram Moolenaar66b51422019-08-18 21:44:12 +020052
53 static void
54ExpandEscape(
55 expand_T *xp,
56 char_u *str,
57 int numfiles,
58 char_u **files,
59 int options)
60{
61 int i;
62 char_u *p;
Bram Moolenaar21c1a0c2021-10-17 17:20:23 +010063 int vse_what = xp->xp_context == EXPAND_BUFFERS
64 ? VSE_BUFFER : VSE_NONE;
Bram Moolenaar66b51422019-08-18 21:44:12 +020065
66 // May change home directory back to "~"
67 if (options & WILD_HOME_REPLACE)
68 tilde_replace(str, numfiles, files);
69
70 if (options & WILD_ESCAPE)
71 {
72 if (xp->xp_context == EXPAND_FILES
73 || xp->xp_context == EXPAND_FILES_IN_PATH
74 || xp->xp_context == EXPAND_SHELLCMD
75 || xp->xp_context == EXPAND_BUFFERS
76 || xp->xp_context == EXPAND_DIRECTORIES)
77 {
78 // Insert a backslash into a file name before a space, \, %, #
79 // and wildmatch characters, except '~'.
80 for (i = 0; i < numfiles; ++i)
81 {
82 // for ":set path=" we need to escape spaces twice
83 if (xp->xp_backslash == XP_BS_THREE)
84 {
85 p = vim_strsave_escaped(files[i], (char_u *)" ");
86 if (p != NULL)
87 {
88 vim_free(files[i]);
89 files[i] = p;
90#if defined(BACKSLASH_IN_FILENAME)
91 p = vim_strsave_escaped(files[i], (char_u *)" ");
92 if (p != NULL)
93 {
94 vim_free(files[i]);
95 files[i] = p;
96 }
97#endif
98 }
99 }
100#ifdef BACKSLASH_IN_FILENAME
Bram Moolenaar21c1a0c2021-10-17 17:20:23 +0100101 p = vim_strsave_fnameescape(files[i], vse_what);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200102#else
Bram Moolenaar21c1a0c2021-10-17 17:20:23 +0100103 p = vim_strsave_fnameescape(files[i],
104 xp->xp_shell ? VSE_SHELL : vse_what);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200105#endif
106 if (p != NULL)
107 {
108 vim_free(files[i]);
109 files[i] = p;
110 }
111
112 // If 'str' starts with "\~", replace "~" at start of
113 // files[i] with "\~".
114 if (str[0] == '\\' && str[1] == '~' && files[i][0] == '~')
115 escape_fname(&files[i]);
116 }
117 xp->xp_backslash = XP_BS_NONE;
118
119 // If the first file starts with a '+' escape it. Otherwise it
120 // could be seen as "+cmd".
121 if (*files[0] == '+')
122 escape_fname(&files[0]);
123 }
124 else if (xp->xp_context == EXPAND_TAGS)
125 {
126 // Insert a backslash before characters in a tag name that
127 // would terminate the ":tag" command.
128 for (i = 0; i < numfiles; ++i)
129 {
130 p = vim_strsave_escaped(files[i], (char_u *)"\\|\"");
131 if (p != NULL)
132 {
133 vim_free(files[i]);
134 files[i] = p;
135 }
136 }
137 }
138 }
139}
140
141/*
142 * Return FAIL if this is not an appropriate context in which to do
143 * completion of anything, return OK if it is (even if there are no matches).
144 * For the caller, this means that the character is just passed through like a
145 * normal character (instead of being expanded). This allows :s/^I^D etc.
146 */
147 int
148nextwild(
149 expand_T *xp,
150 int type,
151 int options, // extra options for ExpandOne()
152 int escape) // if TRUE, escape the returned matches
153{
154 cmdline_info_T *ccline = get_cmdline_info();
155 int i, j;
156 char_u *p1;
157 char_u *p2;
158 int difflen;
159 int v;
160
161 if (xp->xp_numfiles == -1)
162 {
163 set_expand_context(xp);
164 cmd_showtail = expand_showtail(xp);
165 }
166
167 if (xp->xp_context == EXPAND_UNSUCCESSFUL)
168 {
169 beep_flush();
170 return OK; // Something illegal on command line
171 }
172 if (xp->xp_context == EXPAND_NOTHING)
173 {
174 // Caller can use the character as a normal char instead
175 return FAIL;
176 }
177
178 msg_puts("..."); // show that we are busy
179 out_flush();
180
181 i = (int)(xp->xp_pattern - ccline->cmdbuff);
182 xp->xp_pattern_len = ccline->cmdpos - i;
183
184 if (type == WILD_NEXT || type == WILD_PREV)
185 {
186 // Get next/previous match for a previous expanded pattern.
187 p2 = ExpandOne(xp, NULL, NULL, 0, type);
188 }
189 else
190 {
191 // Translate string into pattern and expand it.
192 if ((p1 = addstar(xp->xp_pattern, xp->xp_pattern_len,
193 xp->xp_context)) == NULL)
194 p2 = NULL;
195 else
196 {
197 int use_options = options |
198 WILD_HOME_REPLACE|WILD_ADD_SLASH|WILD_SILENT;
199 if (escape)
200 use_options |= WILD_ESCAPE;
201
202 if (p_wic)
203 use_options += WILD_ICASE;
204 p2 = ExpandOne(xp, p1,
205 vim_strnsave(&ccline->cmdbuff[i], xp->xp_pattern_len),
206 use_options, type);
207 vim_free(p1);
208 // longest match: make sure it is not shorter, happens with :help
209 if (p2 != NULL && type == WILD_LONGEST)
210 {
211 for (j = 0; j < xp->xp_pattern_len; ++j)
212 if (ccline->cmdbuff[i + j] == '*'
213 || ccline->cmdbuff[i + j] == '?')
214 break;
215 if ((int)STRLEN(p2) < j)
216 VIM_CLEAR(p2);
217 }
218 }
219 }
220
221 if (p2 != NULL && !got_int)
222 {
223 difflen = (int)STRLEN(p2) - xp->xp_pattern_len;
224 if (ccline->cmdlen + difflen + 4 > ccline->cmdbufflen)
225 {
226 v = realloc_cmdbuff(ccline->cmdlen + difflen + 4);
227 xp->xp_pattern = ccline->cmdbuff + i;
228 }
229 else
230 v = OK;
231 if (v == OK)
232 {
233 mch_memmove(&ccline->cmdbuff[ccline->cmdpos + difflen],
234 &ccline->cmdbuff[ccline->cmdpos],
235 (size_t)(ccline->cmdlen - ccline->cmdpos + 1));
236 mch_memmove(&ccline->cmdbuff[i], p2, STRLEN(p2));
237 ccline->cmdlen += difflen;
238 ccline->cmdpos += difflen;
239 }
240 }
241 vim_free(p2);
242
243 redrawcmd();
244 cursorcmd();
245
246 // When expanding a ":map" command and no matches are found, assume that
247 // the key is supposed to be inserted literally
248 if (xp->xp_context == EXPAND_MAPPINGS && p2 == NULL)
249 return FAIL;
250
251 if (xp->xp_numfiles <= 0 && p2 == NULL)
252 beep_flush();
253 else if (xp->xp_numfiles == 1)
254 // free expanded pattern
255 (void)ExpandOne(xp, NULL, NULL, 0, WILD_FREE);
256
257 return OK;
258}
259
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000260#if defined(FEAT_WILDMENU) || defined(PROTO)
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +0000261
262/*
263 * Create and display a cmdline completion popup menu with items from
264 * 'files_found'.
265 */
266 static int
267cmdline_pum_create(
268 cmdline_info_T *ccline,
269 expand_T *xp,
270 char_u **files_found,
271 int num_files,
272 int showtail)
273{
274 int i;
275 int columns;
276
277 // Add all the completion matches
278 compl_match_arraysize = num_files;
279 compl_match_array = ALLOC_MULT(pumitem_T, compl_match_arraysize);
280 for (i = 0; i < num_files; i++)
281 {
282 compl_match_array[i].pum_text = SHOW_FILE_TEXT(i);
283 compl_match_array[i].pum_info = NULL;
284 compl_match_array[i].pum_extra = NULL;
285 compl_match_array[i].pum_kind = NULL;
286 }
287
288 // Compute the popup menu starting column
289 compl_startcol = vim_strsize(ccline->cmdbuff) + 1;
290 columns = vim_strsize(xp->xp_pattern);
291 if (showtail)
292 {
293 columns += vim_strsize(sm_gettail(files_found[0]));
294 columns -= vim_strsize(files_found[0]);
295 }
296 if (columns >= compl_startcol)
297 compl_startcol = 0;
298 else
299 compl_startcol -= columns;
300
301 // no default selection
302 compl_selected = -1;
303
304 cmdline_pum_display();
305
306 return EXPAND_OK;
307}
308
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000309/*
310 * Display the cmdline completion matches in a popup menu
311 */
312void cmdline_pum_display(void)
313{
314 pum_display(compl_match_array, compl_match_arraysize, compl_selected);
315}
316
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +0000317/*
318 * Returns TRUE if the cmdline completion popup menu is being displayed.
319 */
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000320int cmdline_pum_active(void)
321{
322 return p_wmnu && pum_visible() && compl_match_array != NULL;
323}
324
325/*
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +0000326 * Remove the cmdline completion popup menu (if present), free the list of
327 * items and refresh the screen.
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000328 */
329void cmdline_pum_remove(void)
330{
331 pum_undisplay();
332 VIM_CLEAR(compl_match_array);
333 update_screen(0);
334}
335
336void cmdline_pum_cleanup(cmdline_info_T *cclp)
337{
338 cmdline_pum_remove();
339 wildmenu_cleanup(cclp);
340}
341
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +0000342/*
343 * Returns the starting column number to use for the cmdline completion popup
344 * menu.
345 */
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000346int cmdline_compl_startcol(void)
347{
348 return compl_startcol;
349}
350#endif
351
Bram Moolenaar66b51422019-08-18 21:44:12 +0200352/*
353 * Do wildcard expansion on the string 'str'.
354 * Chars that should not be expanded must be preceded with a backslash.
355 * Return a pointer to allocated memory containing the new string.
356 * Return NULL for failure.
357 *
358 * "orig" is the originally expanded string, copied to allocated memory. It
359 * should either be kept in orig_save or freed. When "mode" is WILD_NEXT or
360 * WILD_PREV "orig" should be NULL.
361 *
362 * Results are cached in xp->xp_files and xp->xp_numfiles, except when "mode"
363 * is WILD_EXPAND_FREE or WILD_ALL.
364 *
365 * mode = WILD_FREE: just free previously expanded matches
366 * mode = WILD_EXPAND_FREE: normal expansion, do not keep matches
367 * mode = WILD_EXPAND_KEEP: normal expansion, keep matches
368 * mode = WILD_NEXT: use next match in multiple match, wrap to first
369 * mode = WILD_PREV: use previous match in multiple match, wrap to first
370 * mode = WILD_ALL: return all matches concatenated
371 * mode = WILD_LONGEST: return longest matched part
372 * mode = WILD_ALL_KEEP: get all matches, keep matches
373 *
374 * options = WILD_LIST_NOTFOUND: list entries without a match
375 * options = WILD_HOME_REPLACE: do home_replace() for buffer names
376 * options = WILD_USE_NL: Use '\n' for WILD_ALL
377 * options = WILD_NO_BEEP: Don't beep for multiple matches
378 * options = WILD_ADD_SLASH: add a slash after directory names
379 * options = WILD_KEEP_ALL: don't remove 'wildignore' entries
380 * options = WILD_SILENT: don't print warning messages
381 * options = WILD_ESCAPE: put backslash before special chars
382 * options = WILD_ICASE: ignore case for files
Bram Moolenaarec680282020-06-12 19:35:32 +0200383 * options = WILD_ALLLINKS; keep broken links
Bram Moolenaar66b51422019-08-18 21:44:12 +0200384 *
385 * The variables xp->xp_context and xp->xp_backslash must have been set!
386 */
387 char_u *
388ExpandOne(
389 expand_T *xp,
390 char_u *str,
391 char_u *orig, // allocated copy of original of expanded string
392 int options,
393 int mode)
394{
395 char_u *ss = NULL;
396 static int findex;
397 static char_u *orig_save = NULL; // kept value of orig
398 int orig_saved = FALSE;
399 int i;
400 long_u len;
401 int non_suf_match; // number without matching suffix
402
403 // first handle the case of using an old match
404 if (mode == WILD_NEXT || mode == WILD_PREV)
405 {
406 if (xp->xp_numfiles > 0)
407 {
408 if (mode == WILD_PREV)
409 {
410 if (findex == -1)
411 findex = xp->xp_numfiles;
412 --findex;
413 }
414 else // mode == WILD_NEXT
415 ++findex;
416
417 // When wrapping around, return the original string, set findex to
418 // -1.
419 if (findex < 0)
420 {
421 if (orig_save == NULL)
422 findex = xp->xp_numfiles - 1;
423 else
424 findex = -1;
425 }
426 if (findex >= xp->xp_numfiles)
427 {
428 if (orig_save == NULL)
429 findex = 0;
430 else
431 findex = -1;
432 }
433#ifdef FEAT_WILDMENU
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000434 if (compl_match_array)
435 {
436 compl_selected = findex;
437 cmdline_pum_display();
438 }
439 else if (p_wmnu)
Bram Moolenaar66b51422019-08-18 21:44:12 +0200440 win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files,
441 findex, cmd_showtail);
442#endif
443 if (findex == -1)
444 return vim_strsave(orig_save);
445 return vim_strsave(xp->xp_files[findex]);
446 }
447 else
448 return NULL;
449 }
450
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000451 if (mode == WILD_CANCEL)
452 ss = vim_strsave(orig_save ? orig_save : (char_u *)"");
453 else if (mode == WILD_APPLY)
454 ss = vim_strsave(findex == -1 ? (orig_save ?
455 orig_save : (char_u *)"") : xp->xp_files[findex]);
456
Bram Moolenaar66b51422019-08-18 21:44:12 +0200457 // free old names
458 if (xp->xp_numfiles != -1 && mode != WILD_ALL && mode != WILD_LONGEST)
459 {
460 FreeWild(xp->xp_numfiles, xp->xp_files);
461 xp->xp_numfiles = -1;
462 VIM_CLEAR(orig_save);
463 }
464 findex = 0;
465
466 if (mode == WILD_FREE) // only release file name
467 return NULL;
468
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000469 if (xp->xp_numfiles == -1 && mode != WILD_APPLY && mode != WILD_CANCEL)
Bram Moolenaar66b51422019-08-18 21:44:12 +0200470 {
471 vim_free(orig_save);
472 orig_save = orig;
473 orig_saved = TRUE;
474
475 // Do the expansion.
476 if (ExpandFromContext(xp, str, &xp->xp_numfiles, &xp->xp_files,
477 options) == FAIL)
478 {
479#ifdef FNAME_ILLEGAL
480 // Illegal file name has been silently skipped. But when there
481 // are wildcards, the real problem is that there was no match,
482 // causing the pattern to be added, which has illegal characters.
483 if (!(options & WILD_SILENT) && (options & WILD_LIST_NOTFOUND))
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000484 semsg(_(e_no_match_str_2), str);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200485#endif
486 }
487 else if (xp->xp_numfiles == 0)
488 {
489 if (!(options & WILD_SILENT))
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000490 semsg(_(e_no_match_str_2), str);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200491 }
492 else
493 {
494 // Escape the matches for use on the command line.
495 ExpandEscape(xp, str, xp->xp_numfiles, xp->xp_files, options);
496
497 // Check for matching suffixes in file names.
498 if (mode != WILD_ALL && mode != WILD_ALL_KEEP
499 && mode != WILD_LONGEST)
500 {
501 if (xp->xp_numfiles)
502 non_suf_match = xp->xp_numfiles;
503 else
504 non_suf_match = 1;
505 if ((xp->xp_context == EXPAND_FILES
506 || xp->xp_context == EXPAND_DIRECTORIES)
507 && xp->xp_numfiles > 1)
508 {
509 // More than one match; check suffix.
510 // The files will have been sorted on matching suffix in
511 // expand_wildcards, only need to check the first two.
512 non_suf_match = 0;
513 for (i = 0; i < 2; ++i)
514 if (match_suffix(xp->xp_files[i]))
515 ++non_suf_match;
516 }
517 if (non_suf_match != 1)
518 {
519 // Can we ever get here unless it's while expanding
520 // interactively? If not, we can get rid of this all
521 // together. Don't really want to wait for this message
522 // (and possibly have to hit return to continue!).
523 if (!(options & WILD_SILENT))
Bram Moolenaar12f3c1b2021-12-05 21:46:34 +0000524 emsg(_(e_too_many_file_names));
Bram Moolenaar66b51422019-08-18 21:44:12 +0200525 else if (!(options & WILD_NO_BEEP))
526 beep_flush();
527 }
528 if (!(non_suf_match != 1 && mode == WILD_EXPAND_FREE))
529 ss = vim_strsave(xp->xp_files[0]);
530 }
531 }
532 }
533
534 // Find longest common part
535 if (mode == WILD_LONGEST && xp->xp_numfiles > 0)
536 {
537 int mb_len = 1;
538 int c0, ci;
539
540 for (len = 0; xp->xp_files[0][len]; len += mb_len)
541 {
542 if (has_mbyte)
543 {
544 mb_len = (*mb_ptr2len)(&xp->xp_files[0][len]);
545 c0 =(* mb_ptr2char)(&xp->xp_files[0][len]);
546 }
547 else
548 c0 = xp->xp_files[0][len];
549 for (i = 1; i < xp->xp_numfiles; ++i)
550 {
551 if (has_mbyte)
552 ci =(* mb_ptr2char)(&xp->xp_files[i][len]);
553 else
554 ci = xp->xp_files[i][len];
555 if (p_fic && (xp->xp_context == EXPAND_DIRECTORIES
556 || xp->xp_context == EXPAND_FILES
557 || xp->xp_context == EXPAND_SHELLCMD
558 || xp->xp_context == EXPAND_BUFFERS))
559 {
560 if (MB_TOLOWER(c0) != MB_TOLOWER(ci))
561 break;
562 }
563 else if (c0 != ci)
564 break;
565 }
566 if (i < xp->xp_numfiles)
567 {
568 if (!(options & WILD_NO_BEEP))
569 vim_beep(BO_WILD);
570 break;
571 }
572 }
573
574 ss = alloc(len + 1);
575 if (ss)
576 vim_strncpy(ss, xp->xp_files[0], (size_t)len);
577 findex = -1; // next p_wc gets first one
578 }
579
580 // Concatenate all matching names
581 if (mode == WILD_ALL && xp->xp_numfiles > 0)
582 {
583 len = 0;
584 for (i = 0; i < xp->xp_numfiles; ++i)
585 len += (long_u)STRLEN(xp->xp_files[i]) + 1;
586 ss = alloc(len);
587 if (ss != NULL)
588 {
589 *ss = NUL;
590 for (i = 0; i < xp->xp_numfiles; ++i)
591 {
592 STRCAT(ss, xp->xp_files[i]);
593 if (i != xp->xp_numfiles - 1)
594 STRCAT(ss, (options & WILD_USE_NL) ? "\n" : " ");
595 }
596 }
597 }
598
599 if (mode == WILD_EXPAND_FREE || mode == WILD_ALL)
600 ExpandCleanup(xp);
601
602 // Free "orig" if it wasn't stored in "orig_save".
603 if (!orig_saved)
604 vim_free(orig);
605
606 return ss;
607}
608
609/*
610 * Prepare an expand structure for use.
611 */
612 void
613ExpandInit(expand_T *xp)
614{
Bram Moolenaarc841aff2020-07-25 14:11:55 +0200615 CLEAR_POINTER(xp);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200616 xp->xp_backslash = XP_BS_NONE;
Bram Moolenaar66b51422019-08-18 21:44:12 +0200617 xp->xp_numfiles = -1;
Bram Moolenaar66b51422019-08-18 21:44:12 +0200618}
619
620/*
621 * Cleanup an expand structure after use.
622 */
623 void
624ExpandCleanup(expand_T *xp)
625{
626 if (xp->xp_numfiles >= 0)
627 {
628 FreeWild(xp->xp_numfiles, xp->xp_files);
629 xp->xp_numfiles = -1;
630 }
631}
632
633/*
634 * Show all matches for completion on the command line.
635 * Returns EXPAND_NOTHING when the character that triggered expansion should
636 * be inserted like a normal character.
637 */
638 int
639showmatches(expand_T *xp, int wildmenu UNUSED)
640{
641 cmdline_info_T *ccline = get_cmdline_info();
Bram Moolenaar66b51422019-08-18 21:44:12 +0200642 int num_files;
643 char_u **files_found;
644 int i, j, k;
645 int maxlen;
646 int lines;
647 int columns;
648 char_u *p;
649 int lastlen;
650 int attr;
651 int showtail;
652
653 if (xp->xp_numfiles == -1)
654 {
655 set_expand_context(xp);
656 i = expand_cmdline(xp, ccline->cmdbuff, ccline->cmdpos,
657 &num_files, &files_found);
658 showtail = expand_showtail(xp);
659 if (i != EXPAND_OK)
660 return i;
661
662 }
663 else
664 {
665 num_files = xp->xp_numfiles;
666 files_found = xp->xp_files;
667 showtail = cmd_showtail;
668 }
669
670#ifdef FEAT_WILDMENU
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000671 if (wildmenu && vim_strchr(p_wop, WOP_PUM) != NULL)
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +0000672 // cmdline completion popup menu (with wildoptions=pum)
673 return cmdline_pum_create(ccline, xp, files_found, num_files, showtail);
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +0000674#endif
675
676#ifdef FEAT_WILDMENU
Bram Moolenaar66b51422019-08-18 21:44:12 +0200677 if (!wildmenu)
678 {
679#endif
680 msg_didany = FALSE; // lines_left will be set
681 msg_start(); // prepare for paging
682 msg_putchar('\n');
683 out_flush();
684 cmdline_row = msg_row;
685 msg_didany = FALSE; // lines_left will be set again
686 msg_start(); // prepare for paging
687#ifdef FEAT_WILDMENU
688 }
689#endif
690
691 if (got_int)
692 got_int = FALSE; // only int. the completion, not the cmd line
693#ifdef FEAT_WILDMENU
694 else if (wildmenu)
695 win_redr_status_matches(xp, num_files, files_found, -1, showtail);
696#endif
697 else
698 {
699 // find the length of the longest file name
700 maxlen = 0;
701 for (i = 0; i < num_files; ++i)
702 {
703 if (!showtail && (xp->xp_context == EXPAND_FILES
704 || xp->xp_context == EXPAND_SHELLCMD
705 || xp->xp_context == EXPAND_BUFFERS))
706 {
707 home_replace(NULL, files_found[i], NameBuff, MAXPATHL, TRUE);
708 j = vim_strsize(NameBuff);
709 }
710 else
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +0000711 j = vim_strsize(SHOW_FILE_TEXT(i));
Bram Moolenaar66b51422019-08-18 21:44:12 +0200712 if (j > maxlen)
713 maxlen = j;
714 }
715
716 if (xp->xp_context == EXPAND_TAGS_LISTFILES)
717 lines = num_files;
718 else
719 {
720 // compute the number of columns and lines for the listing
721 maxlen += 2; // two spaces between file names
722 columns = ((int)Columns + 2) / maxlen;
723 if (columns < 1)
724 columns = 1;
725 lines = (num_files + columns - 1) / columns;
726 }
727
728 attr = HL_ATTR(HLF_D); // find out highlighting for directories
729
730 if (xp->xp_context == EXPAND_TAGS_LISTFILES)
731 {
732 msg_puts_attr(_("tagname"), HL_ATTR(HLF_T));
733 msg_clr_eos();
734 msg_advance(maxlen - 3);
735 msg_puts_attr(_(" kind file\n"), HL_ATTR(HLF_T));
736 }
737
738 // list the files line by line
739 for (i = 0; i < lines; ++i)
740 {
741 lastlen = 999;
742 for (k = i; k < num_files; k += lines)
743 {
744 if (xp->xp_context == EXPAND_TAGS_LISTFILES)
745 {
746 msg_outtrans_attr(files_found[k], HL_ATTR(HLF_D));
747 p = files_found[k] + STRLEN(files_found[k]) + 1;
748 msg_advance(maxlen + 1);
749 msg_puts((char *)p);
750 msg_advance(maxlen + 3);
751 msg_outtrans_long_attr(p + 2, HL_ATTR(HLF_D));
752 break;
753 }
754 for (j = maxlen - lastlen; --j >= 0; )
755 msg_putchar(' ');
756 if (xp->xp_context == EXPAND_FILES
757 || xp->xp_context == EXPAND_SHELLCMD
758 || xp->xp_context == EXPAND_BUFFERS)
759 {
760 // highlight directories
761 if (xp->xp_numfiles != -1)
762 {
763 char_u *halved_slash;
764 char_u *exp_path;
Bram Moolenaarf1552d02019-08-21 12:54:18 +0200765 char_u *path;
Bram Moolenaar66b51422019-08-18 21:44:12 +0200766
767 // Expansion was done before and special characters
768 // were escaped, need to halve backslashes. Also
769 // $HOME has been replaced with ~/.
770 exp_path = expand_env_save_opt(files_found[k], TRUE);
Bram Moolenaarf1552d02019-08-21 12:54:18 +0200771 path = exp_path != NULL ? exp_path : files_found[k];
772 halved_slash = backslash_halve_save(path);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200773 j = mch_isdir(halved_slash != NULL ? halved_slash
774 : files_found[k]);
775 vim_free(exp_path);
Bram Moolenaarf1552d02019-08-21 12:54:18 +0200776 if (halved_slash != path)
777 vim_free(halved_slash);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200778 }
779 else
780 // Expansion was done here, file names are literal.
781 j = mch_isdir(files_found[k]);
782 if (showtail)
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +0000783 p = SHOW_FILE_TEXT(k);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200784 else
785 {
786 home_replace(NULL, files_found[k], NameBuff, MAXPATHL,
787 TRUE);
788 p = NameBuff;
789 }
790 }
791 else
792 {
793 j = FALSE;
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +0000794 p = SHOW_FILE_TEXT(k);
Bram Moolenaar66b51422019-08-18 21:44:12 +0200795 }
796 lastlen = msg_outtrans_attr(p, j ? attr : 0);
797 }
798 if (msg_col > 0) // when not wrapped around
799 {
800 msg_clr_eos();
801 msg_putchar('\n');
802 }
803 out_flush(); // show one line at a time
804 if (got_int)
805 {
806 got_int = FALSE;
807 break;
808 }
809 }
810
811 // we redraw the command below the lines that we have just listed
812 // This is a bit tricky, but it saves a lot of screen updating.
813 cmdline_row = msg_row; // will put it back later
814 }
815
816 if (xp->xp_numfiles == -1)
817 FreeWild(num_files, files_found);
818
819 return EXPAND_OK;
820}
821
822/*
823 * Private gettail for showmatches() (and win_redr_status_matches()):
824 * Find tail of file name path, but ignore trailing "/".
825 */
826 char_u *
827sm_gettail(char_u *s)
828{
829 char_u *p;
830 char_u *t = s;
831 int had_sep = FALSE;
832
833 for (p = s; *p != NUL; )
834 {
835 if (vim_ispathsep(*p)
836#ifdef BACKSLASH_IN_FILENAME
837 && !rem_backslash(p)
838#endif
839 )
840 had_sep = TRUE;
841 else if (had_sep)
842 {
843 t = p;
844 had_sep = FALSE;
845 }
846 MB_PTR_ADV(p);
847 }
848 return t;
849}
850
851/*
852 * Return TRUE if we only need to show the tail of completion matches.
853 * When not completing file names or there is a wildcard in the path FALSE is
854 * returned.
855 */
856 static int
857expand_showtail(expand_T *xp)
858{
859 char_u *s;
860 char_u *end;
861
862 // When not completing file names a "/" may mean something different.
863 if (xp->xp_context != EXPAND_FILES
864 && xp->xp_context != EXPAND_SHELLCMD
865 && xp->xp_context != EXPAND_DIRECTORIES)
866 return FALSE;
867
868 end = gettail(xp->xp_pattern);
869 if (end == xp->xp_pattern) // there is no path separator
870 return FALSE;
871
872 for (s = xp->xp_pattern; s < end; s++)
873 {
874 // Skip escaped wildcards. Only when the backslash is not a path
875 // separator, on DOS the '*' "path\*\file" must not be skipped.
876 if (rem_backslash(s))
877 ++s;
878 else if (vim_strchr((char_u *)"*?[", *s) != NULL)
879 return FALSE;
880 }
881 return TRUE;
882}
883
884/*
885 * Prepare a string for expansion.
886 * When expanding file names: The string will be used with expand_wildcards().
887 * Copy "fname[len]" into allocated memory and add a '*' at the end.
888 * When expanding other names: The string will be used with regcomp(). Copy
889 * the name into allocated memory and prepend "^".
890 */
891 char_u *
892addstar(
893 char_u *fname,
894 int len,
895 int context) // EXPAND_FILES etc.
896{
897 char_u *retval;
898 int i, j;
899 int new_len;
900 char_u *tail;
901 int ends_in_star;
902
903 if (context != EXPAND_FILES
904 && context != EXPAND_FILES_IN_PATH
905 && context != EXPAND_SHELLCMD
906 && context != EXPAND_DIRECTORIES)
907 {
908 // Matching will be done internally (on something other than files).
909 // So we convert the file-matching-type wildcards into our kind for
910 // use with vim_regcomp(). First work out how long it will be:
911
912 // For help tags the translation is done in find_help_tags().
913 // For a tag pattern starting with "/" no translation is needed.
914 if (context == EXPAND_HELP
915 || context == EXPAND_COLORS
916 || context == EXPAND_COMPILER
917 || context == EXPAND_OWNSYNTAX
918 || context == EXPAND_FILETYPE
919 || context == EXPAND_PACKADD
920 || ((context == EXPAND_TAGS_LISTFILES
921 || context == EXPAND_TAGS)
922 && fname[0] == '/'))
923 retval = vim_strnsave(fname, len);
924 else
925 {
926 new_len = len + 2; // +2 for '^' at start, NUL at end
927 for (i = 0; i < len; i++)
928 {
929 if (fname[i] == '*' || fname[i] == '~')
930 new_len++; // '*' needs to be replaced by ".*"
931 // '~' needs to be replaced by "\~"
932
933 // Buffer names are like file names. "." should be literal
934 if (context == EXPAND_BUFFERS && fname[i] == '.')
935 new_len++; // "." becomes "\."
936
937 // Custom expansion takes care of special things, match
938 // backslashes literally (perhaps also for other types?)
939 if ((context == EXPAND_USER_DEFINED
940 || context == EXPAND_USER_LIST) && fname[i] == '\\')
941 new_len++; // '\' becomes "\\"
942 }
943 retval = alloc(new_len);
944 if (retval != NULL)
945 {
946 retval[0] = '^';
947 j = 1;
948 for (i = 0; i < len; i++, j++)
949 {
950 // Skip backslash. But why? At least keep it for custom
951 // expansion.
952 if (context != EXPAND_USER_DEFINED
953 && context != EXPAND_USER_LIST
954 && fname[i] == '\\'
955 && ++i == len)
956 break;
957
958 switch (fname[i])
959 {
960 case '*': retval[j++] = '.';
961 break;
962 case '~': retval[j++] = '\\';
963 break;
964 case '?': retval[j] = '.';
965 continue;
966 case '.': if (context == EXPAND_BUFFERS)
967 retval[j++] = '\\';
968 break;
969 case '\\': if (context == EXPAND_USER_DEFINED
970 || context == EXPAND_USER_LIST)
971 retval[j++] = '\\';
972 break;
973 }
974 retval[j] = fname[i];
975 }
976 retval[j] = NUL;
977 }
978 }
979 }
980 else
981 {
982 retval = alloc(len + 4);
983 if (retval != NULL)
984 {
985 vim_strncpy(retval, fname, len);
986
987 // Don't add a star to *, ~, ~user, $var or `cmd`.
988 // * would become **, which walks the whole tree.
989 // ~ would be at the start of the file name, but not the tail.
990 // $ could be anywhere in the tail.
991 // ` could be anywhere in the file name.
992 // When the name ends in '$' don't add a star, remove the '$'.
993 tail = gettail(retval);
994 ends_in_star = (len > 0 && retval[len - 1] == '*');
995#ifndef BACKSLASH_IN_FILENAME
996 for (i = len - 2; i >= 0; --i)
997 {
998 if (retval[i] != '\\')
999 break;
1000 ends_in_star = !ends_in_star;
1001 }
1002#endif
1003 if ((*retval != '~' || tail != retval)
1004 && !ends_in_star
1005 && vim_strchr(tail, '$') == NULL
1006 && vim_strchr(retval, '`') == NULL)
1007 retval[len++] = '*';
1008 else if (len > 0 && retval[len - 1] == '$')
1009 --len;
1010 retval[len] = NUL;
1011 }
1012 }
1013 return retval;
1014}
1015
1016/*
1017 * Must parse the command line so far to work out what context we are in.
1018 * Completion can then be done based on that context.
1019 * This routine sets the variables:
1020 * xp->xp_pattern The start of the pattern to be expanded within
1021 * the command line (ends at the cursor).
1022 * xp->xp_context The type of thing to expand. Will be one of:
1023 *
1024 * EXPAND_UNSUCCESSFUL Used sometimes when there is something illegal on
1025 * the command line, like an unknown command. Caller
1026 * should beep.
1027 * EXPAND_NOTHING Unrecognised context for completion, use char like
1028 * a normal char, rather than for completion. eg
1029 * :s/^I/
1030 * EXPAND_COMMANDS Cursor is still touching the command, so complete
1031 * it.
1032 * EXPAND_BUFFERS Complete file names for :buf and :sbuf commands.
1033 * EXPAND_FILES After command with EX_XFILE set, or after setting
1034 * with P_EXPAND set. eg :e ^I, :w>>^I
1035 * EXPAND_DIRECTORIES In some cases this is used instead of the latter
1036 * when we know only directories are of interest. eg
1037 * :set dir=^I
1038 * EXPAND_SHELLCMD After ":!cmd", ":r !cmd" or ":w !cmd".
1039 * EXPAND_SETTINGS Complete variable names. eg :set d^I
1040 * EXPAND_BOOL_SETTINGS Complete boolean variables only, eg :set no^I
1041 * EXPAND_TAGS Complete tags from the files in p_tags. eg :ta a^I
1042 * EXPAND_TAGS_LISTFILES As above, but list filenames on ^D, after :tselect
1043 * EXPAND_HELP Complete tags from the file 'helpfile'/tags
1044 * EXPAND_EVENTS Complete event names
1045 * EXPAND_SYNTAX Complete :syntax command arguments
1046 * EXPAND_HIGHLIGHT Complete highlight (syntax) group names
1047 * EXPAND_AUGROUP Complete autocommand group names
1048 * EXPAND_USER_VARS Complete user defined variable names, eg :unlet a^I
1049 * EXPAND_MAPPINGS Complete mapping and abbreviation names,
1050 * eg :unmap a^I , :cunab x^I
1051 * EXPAND_FUNCTIONS Complete internal or user defined function names,
1052 * eg :call sub^I
1053 * EXPAND_USER_FUNC Complete user defined function names, eg :delf F^I
1054 * EXPAND_EXPRESSION Complete internal or user defined function/variable
1055 * names in expressions, eg :while s^I
1056 * EXPAND_ENV_VARS Complete environment variable names
1057 * EXPAND_USER Complete user names
1058 */
1059 static void
1060set_expand_context(expand_T *xp)
1061{
1062 cmdline_info_T *ccline = get_cmdline_info();
1063
1064 // only expansion for ':', '>' and '=' command-lines
1065 if (ccline->cmdfirstc != ':'
1066#ifdef FEAT_EVAL
1067 && ccline->cmdfirstc != '>' && ccline->cmdfirstc != '='
1068 && !ccline->input_fn
1069#endif
1070 )
1071 {
1072 xp->xp_context = EXPAND_NOTHING;
1073 return;
1074 }
1075 set_cmd_context(xp, ccline->cmdbuff, ccline->cmdlen, ccline->cmdpos, TRUE);
1076}
1077
Bram Moolenaard0190392019-08-23 21:17:35 +02001078/*
1079 * This is all pretty much copied from do_one_cmd(), with all the extra stuff
1080 * we don't need/want deleted. Maybe this could be done better if we didn't
1081 * repeat all this stuff. The only problem is that they may not stay
1082 * perfectly compatible with each other, but then the command line syntax
1083 * probably won't change that much -- webb.
1084 */
1085 static char_u *
1086set_one_cmd_context(
1087 expand_T *xp,
1088 char_u *buff) // buffer for command string
1089{
1090 char_u *p;
1091 char_u *cmd, *arg;
1092 int len = 0;
1093 exarg_T ea;
1094 int compl = EXPAND_NOTHING;
1095 int delim;
1096 int forceit = FALSE;
1097 int usefilter = FALSE; // filter instead of file name
1098
1099 ExpandInit(xp);
1100 xp->xp_pattern = buff;
Shougo Matsushitaae38a9d2021-10-21 11:39:53 +01001101 xp->xp_line = buff;
Bram Moolenaard0190392019-08-23 21:17:35 +02001102 xp->xp_context = EXPAND_COMMANDS; // Default until we get past command
1103 ea.argt = 0;
1104
1105 // 1. skip comment lines and leading space, colons or bars
1106 for (cmd = buff; vim_strchr((char_u *)" \t:|", *cmd) != NULL; cmd++)
1107 ;
1108 xp->xp_pattern = cmd;
1109
1110 if (*cmd == NUL)
1111 return NULL;
1112 if (*cmd == '"') // ignore comment lines
1113 {
1114 xp->xp_context = EXPAND_NOTHING;
1115 return NULL;
1116 }
1117
1118 // 3. Skip over the range to find the command.
Bram Moolenaar3bd8de42020-09-14 16:37:34 +02001119 cmd = skip_range(cmd, TRUE, &xp->xp_context);
Bram Moolenaard0190392019-08-23 21:17:35 +02001120 xp->xp_pattern = cmd;
1121 if (*cmd == NUL)
1122 return NULL;
1123 if (*cmd == '"')
1124 {
1125 xp->xp_context = EXPAND_NOTHING;
1126 return NULL;
1127 }
1128
1129 if (*cmd == '|' || *cmd == '\n')
1130 return cmd + 1; // There's another command
1131
1132 // Isolate the command and search for it in the command table.
1133 // Exceptions:
1134 // - the 'k' command can directly be followed by any character, but
1135 // do accept "keepmarks", "keepalt" and "keepjumps".
1136 // - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
1137 if (*cmd == 'k' && cmd[1] != 'e')
1138 {
1139 ea.cmdidx = CMD_k;
1140 p = cmd + 1;
1141 }
1142 else
1143 {
1144 p = cmd;
1145 while (ASCII_ISALPHA(*p) || *p == '*') // Allow * wild card
1146 ++p;
Bram Moolenaardf749a22021-03-28 15:29:43 +02001147 // A user command may contain digits.
1148 // Include "9" for "vim9*" commands; "vim9cmd" and "vim9script".
1149 if (ASCII_ISUPPER(cmd[0]) || STRNCMP("vim9", cmd, 4) == 0)
Bram Moolenaard0190392019-08-23 21:17:35 +02001150 while (ASCII_ISALNUM(*p) || *p == '*')
1151 ++p;
1152 // for python 3.x: ":py3*" commands completion
1153 if (cmd[0] == 'p' && cmd[1] == 'y' && p == cmd + 2 && *p == '3')
1154 {
1155 ++p;
1156 while (ASCII_ISALPHA(*p) || *p == '*')
1157 ++p;
1158 }
1159 // check for non-alpha command
1160 if (p == cmd && vim_strchr((char_u *)"@*!=><&~#", *p) != NULL)
1161 ++p;
1162 len = (int)(p - cmd);
1163
1164 if (len == 0)
1165 {
1166 xp->xp_context = EXPAND_UNSUCCESSFUL;
1167 return NULL;
1168 }
1169
1170 ea.cmdidx = excmd_get_cmdidx(cmd, len);
1171
1172 if (cmd[0] >= 'A' && cmd[0] <= 'Z')
1173 while (ASCII_ISALNUM(*p) || *p == '*') // Allow * wild card
1174 ++p;
1175 }
1176
Bram Moolenaar8e7d6222020-12-18 19:49:56 +01001177 // If the cursor is touching the command, and it ends in an alphanumeric
Bram Moolenaard0190392019-08-23 21:17:35 +02001178 // character, complete the command name.
1179 if (*p == NUL && ASCII_ISALNUM(p[-1]))
1180 return NULL;
1181
1182 if (ea.cmdidx == CMD_SIZE)
1183 {
1184 if (*cmd == 's' && vim_strchr((char_u *)"cgriI", cmd[1]) != NULL)
1185 {
1186 ea.cmdidx = CMD_substitute;
1187 p = cmd + 1;
1188 }
1189 else if (cmd[0] >= 'A' && cmd[0] <= 'Z')
1190 {
1191 ea.cmd = cmd;
1192 p = find_ucmd(&ea, p, NULL, xp, &compl);
1193 if (p == NULL)
1194 ea.cmdidx = CMD_SIZE; // ambiguous user command
1195 }
1196 }
1197 if (ea.cmdidx == CMD_SIZE)
1198 {
1199 // Not still touching the command and it was an illegal one
1200 xp->xp_context = EXPAND_UNSUCCESSFUL;
1201 return NULL;
1202 }
1203
1204 xp->xp_context = EXPAND_NOTHING; // Default now that we're past command
1205
1206 if (*p == '!') // forced commands
1207 {
1208 forceit = TRUE;
1209 ++p;
1210 }
1211
1212 // 6. parse arguments
1213 if (!IS_USER_CMDIDX(ea.cmdidx))
1214 ea.argt = excmd_get_argt(ea.cmdidx);
1215
1216 arg = skipwhite(p);
1217
Bram Moolenaar743d0622020-07-03 18:15:06 +02001218 // Skip over ++argopt argument
1219 if ((ea.argt & EX_ARGOPT) && *arg != NUL && STRNCMP(arg, "++", 2) == 0)
1220 {
1221 p = arg;
1222 while (*p && !vim_isspace(*p))
1223 MB_PTR_ADV(p);
1224 arg = skipwhite(p);
1225 }
1226
Bram Moolenaard0190392019-08-23 21:17:35 +02001227 if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update)
1228 {
1229 if (*arg == '>') // append
1230 {
1231 if (*++arg == '>')
1232 ++arg;
1233 arg = skipwhite(arg);
1234 }
1235 else if (*arg == '!' && ea.cmdidx == CMD_write) // :w !filter
1236 {
1237 ++arg;
1238 usefilter = TRUE;
1239 }
1240 }
1241
1242 if (ea.cmdidx == CMD_read)
1243 {
1244 usefilter = forceit; // :r! filter if forced
1245 if (*arg == '!') // :r !filter
1246 {
1247 ++arg;
1248 usefilter = TRUE;
1249 }
1250 }
1251
1252 if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift)
1253 {
1254 while (*arg == *cmd) // allow any number of '>' or '<'
1255 ++arg;
1256 arg = skipwhite(arg);
1257 }
1258
1259 // Does command allow "+command"?
1260 if ((ea.argt & EX_CMDARG) && !usefilter && *arg == '+')
1261 {
1262 // Check if we're in the +command
1263 p = arg + 1;
1264 arg = skip_cmd_arg(arg, FALSE);
1265
1266 // Still touching the command after '+'?
1267 if (*arg == NUL)
1268 return p;
1269
1270 // Skip space(s) after +command to get to the real argument
1271 arg = skipwhite(arg);
1272 }
1273
Bram Moolenaarc8cb8832020-06-18 21:14:30 +02001274
Bram Moolenaard0190392019-08-23 21:17:35 +02001275 // Check for '|' to separate commands and '"' to start comments.
1276 // Don't do this for ":read !cmd" and ":write !cmd".
1277 if ((ea.argt & EX_TRLBAR) && !usefilter)
1278 {
1279 p = arg;
1280 // ":redir @" is not the start of a comment
1281 if (ea.cmdidx == CMD_redir && p[0] == '@' && p[1] == '"')
1282 p += 2;
1283 while (*p)
1284 {
1285 if (*p == Ctrl_V)
1286 {
1287 if (p[1] != NUL)
1288 ++p;
1289 }
1290 else if ( (*p == '"' && !(ea.argt & EX_NOTRLCOM))
1291 || *p == '|' || *p == '\n')
1292 {
1293 if (*(p - 1) != '\\')
1294 {
1295 if (*p == '|' || *p == '\n')
1296 return p + 1;
1297 return NULL; // It's a comment
1298 }
1299 }
1300 MB_PTR_ADV(p);
1301 }
1302 }
1303
1304 if (!(ea.argt & EX_EXTRA) && *arg != NUL
1305 && vim_strchr((char_u *)"|\"", *arg) == NULL)
1306 // no arguments allowed but there is something
1307 return NULL;
1308
1309 // Find start of last argument (argument just before cursor):
1310 p = buff;
1311 xp->xp_pattern = p;
1312 len = (int)STRLEN(buff);
1313 while (*p && p < buff + len)
1314 {
1315 if (*p == ' ' || *p == TAB)
1316 {
1317 // argument starts after a space
1318 xp->xp_pattern = ++p;
1319 }
1320 else
1321 {
1322 if (*p == '\\' && *(p + 1) != NUL)
1323 ++p; // skip over escaped character
1324 MB_PTR_ADV(p);
1325 }
1326 }
1327
1328 if (ea.argt & EX_XFILE)
1329 {
1330 int c;
1331 int in_quote = FALSE;
1332 char_u *bow = NULL; // Beginning of word
1333
1334 // Allow spaces within back-quotes to count as part of the argument
1335 // being expanded.
1336 xp->xp_pattern = skipwhite(arg);
1337 p = xp->xp_pattern;
1338 while (*p != NUL)
1339 {
1340 if (has_mbyte)
1341 c = mb_ptr2char(p);
1342 else
1343 c = *p;
1344 if (c == '\\' && p[1] != NUL)
1345 ++p;
1346 else if (c == '`')
1347 {
1348 if (!in_quote)
1349 {
1350 xp->xp_pattern = p;
1351 bow = p + 1;
1352 }
1353 in_quote = !in_quote;
1354 }
1355 // An argument can contain just about everything, except
1356 // characters that end the command and white space.
1357 else if (c == '|' || c == '\n' || c == '"' || (VIM_ISWHITE(c)
1358#ifdef SPACE_IN_FILENAME
1359 && (!(ea.argt & EX_NOSPC) || usefilter)
1360#endif
1361 ))
1362 {
1363 len = 0; // avoid getting stuck when space is in 'isfname'
1364 while (*p != NUL)
1365 {
1366 if (has_mbyte)
1367 c = mb_ptr2char(p);
1368 else
1369 c = *p;
1370 if (c == '`' || vim_isfilec_or_wc(c))
1371 break;
1372 if (has_mbyte)
1373 len = (*mb_ptr2len)(p);
1374 else
1375 len = 1;
1376 MB_PTR_ADV(p);
1377 }
1378 if (in_quote)
1379 bow = p;
1380 else
1381 xp->xp_pattern = p;
1382 p -= len;
1383 }
1384 MB_PTR_ADV(p);
1385 }
1386
1387 // If we are still inside the quotes, and we passed a space, just
1388 // expand from there.
1389 if (bow != NULL && in_quote)
1390 xp->xp_pattern = bow;
1391 xp->xp_context = EXPAND_FILES;
1392
1393 // For a shell command more chars need to be escaped.
1394 if (usefilter || ea.cmdidx == CMD_bang || ea.cmdidx == CMD_terminal)
1395 {
1396#ifndef BACKSLASH_IN_FILENAME
1397 xp->xp_shell = TRUE;
1398#endif
1399 // When still after the command name expand executables.
1400 if (xp->xp_pattern == skipwhite(arg))
1401 xp->xp_context = EXPAND_SHELLCMD;
1402 }
1403
Albert Liu6024c042021-08-27 20:59:35 +02001404 // Check for environment variable.
1405 if (*xp->xp_pattern == '$')
Bram Moolenaard0190392019-08-23 21:17:35 +02001406 {
1407 for (p = xp->xp_pattern + 1; *p != NUL; ++p)
1408 if (!vim_isIDc(*p))
1409 break;
1410 if (*p == NUL)
1411 {
1412 xp->xp_context = EXPAND_ENV_VARS;
1413 ++xp->xp_pattern;
1414 // Avoid that the assignment uses EXPAND_FILES again.
1415 if (compl != EXPAND_USER_DEFINED && compl != EXPAND_USER_LIST)
1416 compl = EXPAND_ENV_VARS;
1417 }
1418 }
Albert Liu6024c042021-08-27 20:59:35 +02001419 // Check for user names.
Bram Moolenaard0190392019-08-23 21:17:35 +02001420 if (*xp->xp_pattern == '~')
1421 {
1422 for (p = xp->xp_pattern + 1; *p != NUL && *p != '/'; ++p)
1423 ;
1424 // Complete ~user only if it partially matches a user name.
1425 // A full match ~user<Tab> will be replaced by user's home
1426 // directory i.e. something like ~user<Tab> -> /home/user/
1427 if (*p == NUL && p > xp->xp_pattern + 1
1428 && match_user(xp->xp_pattern + 1) >= 1)
1429 {
1430 xp->xp_context = EXPAND_USER;
1431 ++xp->xp_pattern;
1432 }
1433 }
1434 }
1435
1436 // 6. Switch on command name.
1437 switch (ea.cmdidx)
1438 {
1439 case CMD_find:
1440 case CMD_sfind:
1441 case CMD_tabfind:
1442 if (xp->xp_context == EXPAND_FILES)
1443 xp->xp_context = EXPAND_FILES_IN_PATH;
1444 break;
1445 case CMD_cd:
1446 case CMD_chdir:
1447 case CMD_tcd:
1448 case CMD_tchdir:
1449 case CMD_lcd:
1450 case CMD_lchdir:
1451 if (xp->xp_context == EXPAND_FILES)
1452 xp->xp_context = EXPAND_DIRECTORIES;
1453 break;
1454 case CMD_help:
1455 xp->xp_context = EXPAND_HELP;
1456 xp->xp_pattern = arg;
1457 break;
1458
1459 // Command modifiers: return the argument.
1460 // Also for commands with an argument that is a command.
1461 case CMD_aboveleft:
1462 case CMD_argdo:
1463 case CMD_belowright:
1464 case CMD_botright:
1465 case CMD_browse:
1466 case CMD_bufdo:
1467 case CMD_cdo:
1468 case CMD_cfdo:
1469 case CMD_confirm:
1470 case CMD_debug:
1471 case CMD_folddoclosed:
1472 case CMD_folddoopen:
1473 case CMD_hide:
1474 case CMD_keepalt:
1475 case CMD_keepjumps:
1476 case CMD_keepmarks:
1477 case CMD_keeppatterns:
1478 case CMD_ldo:
1479 case CMD_leftabove:
1480 case CMD_lfdo:
1481 case CMD_lockmarks:
1482 case CMD_noautocmd:
1483 case CMD_noswapfile:
1484 case CMD_rightbelow:
1485 case CMD_sandbox:
1486 case CMD_silent:
1487 case CMD_tab:
1488 case CMD_tabdo:
1489 case CMD_topleft:
1490 case CMD_verbose:
1491 case CMD_vertical:
1492 case CMD_windo:
Bram Moolenaare70e12b2021-06-13 17:20:08 +02001493 case CMD_vim9cmd:
1494 case CMD_legacy:
Bram Moolenaard0190392019-08-23 21:17:35 +02001495 return arg;
1496
1497 case CMD_filter:
1498 if (*arg != NUL)
1499 arg = skip_vimgrep_pat(arg, NULL, NULL);
1500 if (arg == NULL || *arg == NUL)
1501 {
1502 xp->xp_context = EXPAND_NOTHING;
1503 return NULL;
1504 }
1505 return skipwhite(arg);
1506
1507#ifdef FEAT_SEARCH_EXTRA
1508 case CMD_match:
1509 if (*arg == NUL || !ends_excmd(*arg))
1510 {
1511 // also complete "None"
1512 set_context_in_echohl_cmd(xp, arg);
1513 arg = skipwhite(skiptowhite(arg));
1514 if (*arg != NUL)
1515 {
1516 xp->xp_context = EXPAND_NOTHING;
Bram Moolenaarf4e20992020-12-21 19:59:08 +01001517 arg = skip_regexp(arg + 1, *arg, magic_isset());
Bram Moolenaard0190392019-08-23 21:17:35 +02001518 }
1519 }
1520 return find_nextcmd(arg);
1521#endif
1522
1523 // All completion for the +cmdline_compl feature goes here.
1524
1525 case CMD_command:
1526 return set_context_in_user_cmd(xp, arg);
1527
1528 case CMD_delcommand:
1529 xp->xp_context = EXPAND_USER_COMMANDS;
1530 xp->xp_pattern = arg;
1531 break;
1532
1533 case CMD_global:
1534 case CMD_vglobal:
1535 delim = *arg; // get the delimiter
1536 if (delim)
1537 ++arg; // skip delimiter if there is one
1538
1539 while (arg[0] != NUL && arg[0] != delim)
1540 {
1541 if (arg[0] == '\\' && arg[1] != NUL)
1542 ++arg;
1543 ++arg;
1544 }
1545 if (arg[0] != NUL)
1546 return arg + 1;
1547 break;
1548 case CMD_and:
1549 case CMD_substitute:
1550 delim = *arg;
1551 if (delim)
1552 {
1553 // skip "from" part
1554 ++arg;
Bram Moolenaarf4e20992020-12-21 19:59:08 +01001555 arg = skip_regexp(arg, delim, magic_isset());
Bram Moolenaard0190392019-08-23 21:17:35 +02001556 }
1557 // skip "to" part
1558 while (arg[0] != NUL && arg[0] != delim)
1559 {
1560 if (arg[0] == '\\' && arg[1] != NUL)
1561 ++arg;
1562 ++arg;
1563 }
1564 if (arg[0] != NUL) // skip delimiter
1565 ++arg;
1566 while (arg[0] && vim_strchr((char_u *)"|\"#", arg[0]) == NULL)
1567 ++arg;
1568 if (arg[0] != NUL)
1569 return arg;
1570 break;
1571 case CMD_isearch:
1572 case CMD_dsearch:
1573 case CMD_ilist:
1574 case CMD_dlist:
1575 case CMD_ijump:
1576 case CMD_psearch:
1577 case CMD_djump:
1578 case CMD_isplit:
1579 case CMD_dsplit:
1580 arg = skipwhite(skipdigits(arg)); // skip count
1581 if (*arg == '/') // Match regexp, not just whole words
1582 {
1583 for (++arg; *arg && *arg != '/'; arg++)
1584 if (*arg == '\\' && arg[1] != NUL)
1585 arg++;
1586 if (*arg)
1587 {
1588 arg = skipwhite(arg + 1);
1589
1590 // Check for trailing illegal characters
1591 if (*arg && vim_strchr((char_u *)"|\"\n", *arg) == NULL)
1592 xp->xp_context = EXPAND_NOTHING;
1593 else
1594 return arg;
1595 }
1596 }
1597 break;
1598
1599 case CMD_autocmd:
1600 return set_context_in_autocmd(xp, arg, FALSE);
1601 case CMD_doautocmd:
1602 case CMD_doautoall:
1603 return set_context_in_autocmd(xp, arg, TRUE);
1604 case CMD_set:
1605 set_context_in_set_cmd(xp, arg, 0);
1606 break;
1607 case CMD_setglobal:
1608 set_context_in_set_cmd(xp, arg, OPT_GLOBAL);
1609 break;
1610 case CMD_setlocal:
1611 set_context_in_set_cmd(xp, arg, OPT_LOCAL);
1612 break;
1613 case CMD_tag:
1614 case CMD_stag:
1615 case CMD_ptag:
1616 case CMD_ltag:
1617 case CMD_tselect:
1618 case CMD_stselect:
1619 case CMD_ptselect:
1620 case CMD_tjump:
1621 case CMD_stjump:
1622 case CMD_ptjump:
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +00001623 if (vim_strchr(p_wop, WOP_TAGFILE) != NULL)
Bram Moolenaard0190392019-08-23 21:17:35 +02001624 xp->xp_context = EXPAND_TAGS_LISTFILES;
1625 else
1626 xp->xp_context = EXPAND_TAGS;
1627 xp->xp_pattern = arg;
1628 break;
1629 case CMD_augroup:
1630 xp->xp_context = EXPAND_AUGROUP;
1631 xp->xp_pattern = arg;
1632 break;
1633#ifdef FEAT_SYN_HL
1634 case CMD_syntax:
1635 set_context_in_syntax_cmd(xp, arg);
1636 break;
1637#endif
1638#ifdef FEAT_EVAL
Bram Moolenaar30fd8202020-09-26 15:09:30 +02001639 case CMD_final:
Bram Moolenaar8f76e6b2019-11-26 16:50:30 +01001640 case CMD_const:
Bram Moolenaard0190392019-08-23 21:17:35 +02001641 case CMD_let:
Bram Moolenaar30fd8202020-09-26 15:09:30 +02001642 case CMD_var:
Bram Moolenaard0190392019-08-23 21:17:35 +02001643 case CMD_if:
1644 case CMD_elseif:
1645 case CMD_while:
1646 case CMD_for:
1647 case CMD_echo:
1648 case CMD_echon:
1649 case CMD_execute:
1650 case CMD_echomsg:
1651 case CMD_echoerr:
1652 case CMD_call:
1653 case CMD_return:
1654 case CMD_cexpr:
1655 case CMD_caddexpr:
1656 case CMD_cgetexpr:
1657 case CMD_lexpr:
1658 case CMD_laddexpr:
1659 case CMD_lgetexpr:
1660 set_context_for_expression(xp, arg, ea.cmdidx);
1661 break;
1662
1663 case CMD_unlet:
1664 while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL)
1665 arg = xp->xp_pattern + 1;
1666
1667 xp->xp_context = EXPAND_USER_VARS;
1668 xp->xp_pattern = arg;
1669
1670 if (*xp->xp_pattern == '$')
1671 {
1672 xp->xp_context = EXPAND_ENV_VARS;
1673 ++xp->xp_pattern;
1674 }
1675
1676 break;
1677
1678 case CMD_function:
1679 case CMD_delfunction:
1680 xp->xp_context = EXPAND_USER_FUNC;
1681 xp->xp_pattern = arg;
1682 break;
Bram Moolenaar4ee9d8e2021-06-13 18:38:48 +02001683 case CMD_disassemble:
1684 set_context_in_disassemble_cmd(xp, arg);
1685 break;
Bram Moolenaard0190392019-08-23 21:17:35 +02001686
1687 case CMD_echohl:
1688 set_context_in_echohl_cmd(xp, arg);
1689 break;
1690#endif
1691 case CMD_highlight:
1692 set_context_in_highlight_cmd(xp, arg);
1693 break;
1694#ifdef FEAT_CSCOPE
1695 case CMD_cscope:
1696 case CMD_lcscope:
1697 case CMD_scscope:
1698 set_context_in_cscope_cmd(xp, arg, ea.cmdidx);
1699 break;
1700#endif
1701#ifdef FEAT_SIGNS
1702 case CMD_sign:
1703 set_context_in_sign_cmd(xp, arg);
1704 break;
1705#endif
1706 case CMD_bdelete:
1707 case CMD_bwipeout:
1708 case CMD_bunload:
1709 while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL)
1710 arg = xp->xp_pattern + 1;
1711 // FALLTHROUGH
1712 case CMD_buffer:
1713 case CMD_sbuffer:
1714 case CMD_checktime:
1715 xp->xp_context = EXPAND_BUFFERS;
1716 xp->xp_pattern = arg;
1717 break;
Bram Moolenaarae7dba82019-12-29 13:56:33 +01001718#ifdef FEAT_DIFF
1719 case CMD_diffget:
1720 case CMD_diffput:
1721 // If current buffer is in diff mode, complete buffer names
1722 // which are in diff mode, and different than current buffer.
1723 xp->xp_context = EXPAND_DIFF_BUFFERS;
1724 xp->xp_pattern = arg;
1725 break;
1726#endif
Bram Moolenaard0190392019-08-23 21:17:35 +02001727 case CMD_USER:
1728 case CMD_USER_BUF:
1729 if (compl != EXPAND_NOTHING)
1730 {
1731 // EX_XFILE: file names are handled above
1732 if (!(ea.argt & EX_XFILE))
1733 {
1734#ifdef FEAT_MENU
1735 if (compl == EXPAND_MENUS)
1736 return set_context_in_menu_cmd(xp, cmd, arg, forceit);
1737#endif
1738 if (compl == EXPAND_COMMANDS)
1739 return arg;
1740 if (compl == EXPAND_MAPPINGS)
1741 return set_context_in_map_cmd(xp, (char_u *)"map",
1742 arg, forceit, FALSE, FALSE, CMD_map);
1743 // Find start of last argument.
1744 p = arg;
1745 while (*p)
1746 {
1747 if (*p == ' ')
1748 // argument starts after a space
1749 arg = p + 1;
1750 else if (*p == '\\' && *(p + 1) != NUL)
1751 ++p; // skip over escaped character
1752 MB_PTR_ADV(p);
1753 }
1754 xp->xp_pattern = arg;
1755 }
1756 xp->xp_context = compl;
1757 }
1758 break;
1759
1760 case CMD_map: case CMD_noremap:
1761 case CMD_nmap: case CMD_nnoremap:
1762 case CMD_vmap: case CMD_vnoremap:
1763 case CMD_omap: case CMD_onoremap:
1764 case CMD_imap: case CMD_inoremap:
1765 case CMD_cmap: case CMD_cnoremap:
1766 case CMD_lmap: case CMD_lnoremap:
1767 case CMD_smap: case CMD_snoremap:
1768 case CMD_tmap: case CMD_tnoremap:
1769 case CMD_xmap: case CMD_xnoremap:
1770 return set_context_in_map_cmd(xp, cmd, arg, forceit,
1771 FALSE, FALSE, ea.cmdidx);
1772 case CMD_unmap:
1773 case CMD_nunmap:
1774 case CMD_vunmap:
1775 case CMD_ounmap:
1776 case CMD_iunmap:
1777 case CMD_cunmap:
1778 case CMD_lunmap:
1779 case CMD_sunmap:
1780 case CMD_tunmap:
1781 case CMD_xunmap:
1782 return set_context_in_map_cmd(xp, cmd, arg, forceit,
1783 FALSE, TRUE, ea.cmdidx);
1784 case CMD_mapclear:
1785 case CMD_nmapclear:
1786 case CMD_vmapclear:
1787 case CMD_omapclear:
1788 case CMD_imapclear:
1789 case CMD_cmapclear:
1790 case CMD_lmapclear:
1791 case CMD_smapclear:
1792 case CMD_tmapclear:
1793 case CMD_xmapclear:
1794 xp->xp_context = EXPAND_MAPCLEAR;
1795 xp->xp_pattern = arg;
1796 break;
1797
1798 case CMD_abbreviate: case CMD_noreabbrev:
1799 case CMD_cabbrev: case CMD_cnoreabbrev:
1800 case CMD_iabbrev: case CMD_inoreabbrev:
1801 return set_context_in_map_cmd(xp, cmd, arg, forceit,
1802 TRUE, FALSE, ea.cmdidx);
1803 case CMD_unabbreviate:
1804 case CMD_cunabbrev:
1805 case CMD_iunabbrev:
1806 return set_context_in_map_cmd(xp, cmd, arg, forceit,
1807 TRUE, TRUE, ea.cmdidx);
1808#ifdef FEAT_MENU
1809 case CMD_menu: case CMD_noremenu: case CMD_unmenu:
1810 case CMD_amenu: case CMD_anoremenu: case CMD_aunmenu:
1811 case CMD_nmenu: case CMD_nnoremenu: case CMD_nunmenu:
1812 case CMD_vmenu: case CMD_vnoremenu: case CMD_vunmenu:
1813 case CMD_omenu: case CMD_onoremenu: case CMD_ounmenu:
1814 case CMD_imenu: case CMD_inoremenu: case CMD_iunmenu:
1815 case CMD_cmenu: case CMD_cnoremenu: case CMD_cunmenu:
1816 case CMD_tlmenu: case CMD_tlnoremenu: case CMD_tlunmenu:
1817 case CMD_tmenu: case CMD_tunmenu:
1818 case CMD_popup: case CMD_tearoff: case CMD_emenu:
1819 return set_context_in_menu_cmd(xp, cmd, arg, forceit);
1820#endif
1821
1822 case CMD_colorscheme:
1823 xp->xp_context = EXPAND_COLORS;
1824 xp->xp_pattern = arg;
1825 break;
1826
1827 case CMD_compiler:
1828 xp->xp_context = EXPAND_COMPILER;
1829 xp->xp_pattern = arg;
1830 break;
1831
1832 case CMD_ownsyntax:
1833 xp->xp_context = EXPAND_OWNSYNTAX;
1834 xp->xp_pattern = arg;
1835 break;
1836
1837 case CMD_setfiletype:
1838 xp->xp_context = EXPAND_FILETYPE;
1839 xp->xp_pattern = arg;
1840 break;
1841
1842 case CMD_packadd:
1843 xp->xp_context = EXPAND_PACKADD;
1844 xp->xp_pattern = arg;
1845 break;
1846
1847#if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
1848 case CMD_language:
1849 p = skiptowhite(arg);
1850 if (*p == NUL)
1851 {
1852 xp->xp_context = EXPAND_LANGUAGE;
1853 xp->xp_pattern = arg;
1854 }
1855 else
1856 {
1857 if ( STRNCMP(arg, "messages", p - arg) == 0
1858 || STRNCMP(arg, "ctype", p - arg) == 0
Bram Moolenaar84cf6bd2020-06-16 20:03:43 +02001859 || STRNCMP(arg, "time", p - arg) == 0
1860 || STRNCMP(arg, "collate", p - arg) == 0)
Bram Moolenaard0190392019-08-23 21:17:35 +02001861 {
1862 xp->xp_context = EXPAND_LOCALES;
1863 xp->xp_pattern = skipwhite(p);
1864 }
1865 else
1866 xp->xp_context = EXPAND_NOTHING;
1867 }
1868 break;
1869#endif
1870#if defined(FEAT_PROFILE)
1871 case CMD_profile:
1872 set_context_in_profile_cmd(xp, arg);
1873 break;
1874#endif
1875 case CMD_behave:
1876 xp->xp_context = EXPAND_BEHAVE;
1877 xp->xp_pattern = arg;
1878 break;
1879
1880 case CMD_messages:
1881 xp->xp_context = EXPAND_MESSAGES;
1882 xp->xp_pattern = arg;
1883 break;
1884
1885 case CMD_history:
1886 xp->xp_context = EXPAND_HISTORY;
1887 xp->xp_pattern = arg;
1888 break;
1889#if defined(FEAT_PROFILE)
1890 case CMD_syntime:
1891 xp->xp_context = EXPAND_SYNTIME;
1892 xp->xp_pattern = arg;
1893 break;
1894#endif
1895
1896 case CMD_argdelete:
1897 while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL)
1898 arg = xp->xp_pattern + 1;
1899 xp->xp_context = EXPAND_ARGLIST;
1900 xp->xp_pattern = arg;
1901 break;
1902
1903 default:
1904 break;
1905 }
1906 return NULL;
1907}
1908
Bram Moolenaar66b51422019-08-18 21:44:12 +02001909 void
1910set_cmd_context(
1911 expand_T *xp,
1912 char_u *str, // start of command line
1913 int len, // length of command line (excl. NUL)
1914 int col, // position of cursor
1915 int use_ccline UNUSED) // use ccline for info
1916{
1917#ifdef FEAT_EVAL
1918 cmdline_info_T *ccline = get_cmdline_info();
1919#endif
1920 int old_char = NUL;
1921 char_u *nextcomm;
1922
1923 // Avoid a UMR warning from Purify, only save the character if it has been
1924 // written before.
1925 if (col < len)
1926 old_char = str[col];
1927 str[col] = NUL;
1928 nextcomm = str;
1929
1930#ifdef FEAT_EVAL
1931 if (use_ccline && ccline->cmdfirstc == '=')
1932 {
Bram Moolenaar66b51422019-08-18 21:44:12 +02001933 // pass CMD_SIZE because there is no real command
1934 set_context_for_expression(xp, str, CMD_SIZE);
Bram Moolenaar66b51422019-08-18 21:44:12 +02001935 }
1936 else if (use_ccline && ccline->input_fn)
1937 {
1938 xp->xp_context = ccline->xp_context;
1939 xp->xp_pattern = ccline->cmdbuff;
Bram Moolenaar66b51422019-08-18 21:44:12 +02001940 xp->xp_arg = ccline->xp_arg;
Bram Moolenaar66b51422019-08-18 21:44:12 +02001941 }
1942 else
1943#endif
1944 while (nextcomm != NULL)
1945 nextcomm = set_one_cmd_context(xp, nextcomm);
1946
1947 // Store the string here so that call_user_expand_func() can get to them
1948 // easily.
1949 xp->xp_line = str;
1950 xp->xp_col = col;
1951
1952 str[col] = old_char;
1953}
1954
1955/*
1956 * Expand the command line "str" from context "xp".
1957 * "xp" must have been set by set_cmd_context().
1958 * xp->xp_pattern points into "str", to where the text that is to be expanded
1959 * starts.
1960 * Returns EXPAND_UNSUCCESSFUL when there is something illegal before the
1961 * cursor.
1962 * Returns EXPAND_NOTHING when there is nothing to expand, might insert the
1963 * key that triggered expansion literally.
1964 * Returns EXPAND_OK otherwise.
1965 */
1966 int
1967expand_cmdline(
1968 expand_T *xp,
1969 char_u *str, // start of command line
1970 int col, // position of cursor
1971 int *matchcount, // return: nr of matches
1972 char_u ***matches) // return: array of pointers to matches
1973{
1974 char_u *file_str = NULL;
1975 int options = WILD_ADD_SLASH|WILD_SILENT;
1976
1977 if (xp->xp_context == EXPAND_UNSUCCESSFUL)
1978 {
1979 beep_flush();
1980 return EXPAND_UNSUCCESSFUL; // Something illegal on command line
1981 }
1982 if (xp->xp_context == EXPAND_NOTHING)
1983 {
1984 // Caller can use the character as a normal char instead
1985 return EXPAND_NOTHING;
1986 }
1987
1988 // add star to file name, or convert to regexp if not exp. files.
1989 xp->xp_pattern_len = (int)(str + col - xp->xp_pattern);
1990 file_str = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
1991 if (file_str == NULL)
1992 return EXPAND_UNSUCCESSFUL;
1993
1994 if (p_wic)
1995 options += WILD_ICASE;
1996
1997 // find all files that match the description
1998 if (ExpandFromContext(xp, file_str, matchcount, matches, options) == FAIL)
1999 {
2000 *matchcount = 0;
2001 *matches = NULL;
2002 }
2003 vim_free(file_str);
2004
2005 return EXPAND_OK;
2006}
2007
Bram Moolenaar66b51422019-08-18 21:44:12 +02002008/*
Bram Moolenaard0190392019-08-23 21:17:35 +02002009 * Function given to ExpandGeneric() to obtain the possible arguments of the
2010 * ":behave {mswin,xterm}" command.
2011 */
2012 static char_u *
2013get_behave_arg(expand_T *xp UNUSED, int idx)
2014{
2015 if (idx == 0)
2016 return (char_u *)"mswin";
2017 if (idx == 1)
2018 return (char_u *)"xterm";
2019 return NULL;
2020}
2021
2022/*
2023 * Function given to ExpandGeneric() to obtain the possible arguments of the
2024 * ":messages {clear}" command.
2025 */
2026 static char_u *
2027get_messages_arg(expand_T *xp UNUSED, int idx)
2028{
2029 if (idx == 0)
2030 return (char_u *)"clear";
2031 return NULL;
2032}
2033
2034 static char_u *
2035get_mapclear_arg(expand_T *xp UNUSED, int idx)
2036{
2037 if (idx == 0)
2038 return (char_u *)"<buffer>";
2039 return NULL;
2040}
2041
2042/*
Bram Moolenaar66b51422019-08-18 21:44:12 +02002043 * Do the expansion based on xp->xp_context and "pat".
2044 */
2045 static int
2046ExpandFromContext(
2047 expand_T *xp,
2048 char_u *pat,
2049 int *num_file,
2050 char_u ***file,
2051 int options) // WILD_ flags
2052{
Bram Moolenaar66b51422019-08-18 21:44:12 +02002053 regmatch_T regmatch;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002054 int ret;
2055 int flags;
Bram Moolenaarcc390ff2020-02-29 22:06:30 +01002056 char_u *tofree = NULL;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002057
2058 flags = EW_DIR; // include directories
2059 if (options & WILD_LIST_NOTFOUND)
2060 flags |= EW_NOTFOUND;
2061 if (options & WILD_ADD_SLASH)
2062 flags |= EW_ADDSLASH;
2063 if (options & WILD_KEEP_ALL)
2064 flags |= EW_KEEPALL;
2065 if (options & WILD_SILENT)
2066 flags |= EW_SILENT;
Bram Moolenaarb40c2572019-10-19 21:01:05 +02002067 if (options & WILD_NOERROR)
2068 flags |= EW_NOERROR;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002069 if (options & WILD_ALLLINKS)
2070 flags |= EW_ALLLINKS;
2071
2072 if (xp->xp_context == EXPAND_FILES
2073 || xp->xp_context == EXPAND_DIRECTORIES
2074 || xp->xp_context == EXPAND_FILES_IN_PATH)
2075 {
2076 // Expand file or directory names.
2077 int free_pat = FALSE;
2078 int i;
2079
2080 // for ":set path=" and ":set tags=" halve backslashes for escaped
2081 // space
2082 if (xp->xp_backslash != XP_BS_NONE)
2083 {
2084 free_pat = TRUE;
2085 pat = vim_strsave(pat);
2086 for (i = 0; pat[i]; ++i)
2087 if (pat[i] == '\\')
2088 {
2089 if (xp->xp_backslash == XP_BS_THREE
2090 && pat[i + 1] == '\\'
2091 && pat[i + 2] == '\\'
2092 && pat[i + 3] == ' ')
2093 STRMOVE(pat + i, pat + i + 3);
2094 if (xp->xp_backslash == XP_BS_ONE
2095 && pat[i + 1] == ' ')
2096 STRMOVE(pat + i, pat + i + 1);
2097 }
2098 }
2099
2100 if (xp->xp_context == EXPAND_FILES)
2101 flags |= EW_FILE;
2102 else if (xp->xp_context == EXPAND_FILES_IN_PATH)
2103 flags |= (EW_FILE | EW_PATH);
2104 else
2105 flags = (flags | EW_DIR) & ~EW_FILE;
2106 if (options & WILD_ICASE)
2107 flags |= EW_ICASE;
2108
2109 // Expand wildcards, supporting %:h and the like.
2110 ret = expand_wildcards_eval(&pat, num_file, file, flags);
2111 if (free_pat)
2112 vim_free(pat);
2113#ifdef BACKSLASH_IN_FILENAME
2114 if (p_csl[0] != NUL && (options & WILD_IGNORE_COMPLETESLASH) == 0)
2115 {
K.Takata54119102022-02-03 13:33:03 +00002116 int j;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002117
K.Takata54119102022-02-03 13:33:03 +00002118 for (j = 0; j < *num_file; ++j)
Bram Moolenaar66b51422019-08-18 21:44:12 +02002119 {
K.Takata54119102022-02-03 13:33:03 +00002120 char_u *ptr = (*file)[j];
Bram Moolenaar66b51422019-08-18 21:44:12 +02002121
2122 while (*ptr != NUL)
2123 {
2124 if (p_csl[0] == 's' && *ptr == '\\')
2125 *ptr = '/';
2126 else if (p_csl[0] == 'b' && *ptr == '/')
2127 *ptr = '\\';
2128 ptr += (*mb_ptr2len)(ptr);
2129 }
2130 }
2131 }
2132#endif
2133 return ret;
2134 }
2135
2136 *file = (char_u **)"";
2137 *num_file = 0;
2138 if (xp->xp_context == EXPAND_HELP)
2139 {
2140 // With an empty argument we would get all the help tags, which is
2141 // very slow. Get matches for "help" instead.
2142 if (find_help_tags(*pat == NUL ? (char_u *)"help" : pat,
2143 num_file, file, FALSE) == OK)
2144 {
2145#ifdef FEAT_MULTI_LANG
2146 cleanup_help_tags(*num_file, *file);
2147#endif
2148 return OK;
2149 }
2150 return FAIL;
2151 }
2152
Bram Moolenaar66b51422019-08-18 21:44:12 +02002153 if (xp->xp_context == EXPAND_SHELLCMD)
2154 return expand_shellcmd(pat, num_file, file, flags);
2155 if (xp->xp_context == EXPAND_OLD_SETTING)
2156 return ExpandOldSetting(num_file, file);
2157 if (xp->xp_context == EXPAND_BUFFERS)
2158 return ExpandBufnames(pat, num_file, file, options);
Bram Moolenaarae7dba82019-12-29 13:56:33 +01002159#ifdef FEAT_DIFF
2160 if (xp->xp_context == EXPAND_DIFF_BUFFERS)
2161 return ExpandBufnames(pat, num_file, file, options | BUF_DIFF_FILTER);
2162#endif
Bram Moolenaar66b51422019-08-18 21:44:12 +02002163 if (xp->xp_context == EXPAND_TAGS
2164 || xp->xp_context == EXPAND_TAGS_LISTFILES)
2165 return expand_tags(xp->xp_context == EXPAND_TAGS, pat, num_file, file);
2166 if (xp->xp_context == EXPAND_COLORS)
2167 {
2168 char *directories[] = {"colors", NULL};
2169 return ExpandRTDir(pat, DIP_START + DIP_OPT, num_file, file,
2170 directories);
2171 }
2172 if (xp->xp_context == EXPAND_COMPILER)
2173 {
2174 char *directories[] = {"compiler", NULL};
2175 return ExpandRTDir(pat, 0, num_file, file, directories);
2176 }
2177 if (xp->xp_context == EXPAND_OWNSYNTAX)
2178 {
2179 char *directories[] = {"syntax", NULL};
2180 return ExpandRTDir(pat, 0, num_file, file, directories);
2181 }
2182 if (xp->xp_context == EXPAND_FILETYPE)
2183 {
2184 char *directories[] = {"syntax", "indent", "ftplugin", NULL};
2185 return ExpandRTDir(pat, 0, num_file, file, directories);
2186 }
2187# if defined(FEAT_EVAL)
2188 if (xp->xp_context == EXPAND_USER_LIST)
2189 return ExpandUserList(xp, num_file, file);
2190# endif
2191 if (xp->xp_context == EXPAND_PACKADD)
2192 return ExpandPackAddDir(pat, num_file, file);
2193
Bram Moolenaarcc390ff2020-02-29 22:06:30 +01002194 // When expanding a function name starting with s:, match the <SNR>nr_
2195 // prefix.
Bram Moolenaar47016f52021-08-26 16:39:58 +02002196 if ((xp->xp_context == EXPAND_USER_FUNC
2197 || xp->xp_context == EXPAND_DISASSEMBLE)
2198 && STRNCMP(pat, "^s:", 3) == 0)
Bram Moolenaarcc390ff2020-02-29 22:06:30 +01002199 {
2200 int len = (int)STRLEN(pat) + 20;
2201
2202 tofree = alloc(len);
Bram Moolenaarb54b8e02020-03-01 01:05:53 +01002203 vim_snprintf((char *)tofree, len, "^<SNR>\\d\\+_%s", pat + 3);
Bram Moolenaarcc390ff2020-02-29 22:06:30 +01002204 pat = tofree;
2205 }
2206
Bram Moolenaarf4e20992020-12-21 19:59:08 +01002207 regmatch.regprog = vim_regcomp(pat, magic_isset() ? RE_MAGIC : 0);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002208 if (regmatch.regprog == NULL)
2209 return FAIL;
2210
2211 // set ignore-case according to p_ic, p_scs and pat
2212 regmatch.rm_ic = ignorecase(pat);
2213
2214 if (xp->xp_context == EXPAND_SETTINGS
2215 || xp->xp_context == EXPAND_BOOL_SETTINGS)
2216 ret = ExpandSettings(xp, &regmatch, num_file, file);
2217 else if (xp->xp_context == EXPAND_MAPPINGS)
2218 ret = ExpandMappings(&regmatch, num_file, file);
2219# if defined(FEAT_EVAL)
2220 else if (xp->xp_context == EXPAND_USER_DEFINED)
2221 ret = ExpandUserDefined(xp, &regmatch, num_file, file);
2222# endif
2223 else
2224 {
2225 static struct expgen
2226 {
2227 int context;
2228 char_u *((*func)(expand_T *, int));
2229 int ic;
2230 int escaped;
2231 } tab[] =
2232 {
2233 {EXPAND_COMMANDS, get_command_name, FALSE, TRUE},
2234 {EXPAND_BEHAVE, get_behave_arg, TRUE, TRUE},
2235 {EXPAND_MAPCLEAR, get_mapclear_arg, TRUE, TRUE},
2236 {EXPAND_MESSAGES, get_messages_arg, TRUE, TRUE},
2237 {EXPAND_HISTORY, get_history_arg, TRUE, TRUE},
2238 {EXPAND_USER_COMMANDS, get_user_commands, FALSE, TRUE},
2239 {EXPAND_USER_ADDR_TYPE, get_user_cmd_addr_type, FALSE, TRUE},
2240 {EXPAND_USER_CMD_FLAGS, get_user_cmd_flags, FALSE, TRUE},
2241 {EXPAND_USER_NARGS, get_user_cmd_nargs, FALSE, TRUE},
2242 {EXPAND_USER_COMPLETE, get_user_cmd_complete, FALSE, TRUE},
2243# ifdef FEAT_EVAL
2244 {EXPAND_USER_VARS, get_user_var_name, FALSE, TRUE},
2245 {EXPAND_FUNCTIONS, get_function_name, FALSE, TRUE},
2246 {EXPAND_USER_FUNC, get_user_func_name, FALSE, TRUE},
Bram Moolenaar4ee9d8e2021-06-13 18:38:48 +02002247 {EXPAND_DISASSEMBLE, get_disassemble_argument, FALSE, TRUE},
Bram Moolenaar66b51422019-08-18 21:44:12 +02002248 {EXPAND_EXPRESSION, get_expr_name, FALSE, TRUE},
2249# endif
2250# ifdef FEAT_MENU
2251 {EXPAND_MENUS, get_menu_name, FALSE, TRUE},
2252 {EXPAND_MENUNAMES, get_menu_names, FALSE, TRUE},
2253# endif
2254# ifdef FEAT_SYN_HL
2255 {EXPAND_SYNTAX, get_syntax_name, TRUE, TRUE},
2256# endif
2257# ifdef FEAT_PROFILE
2258 {EXPAND_SYNTIME, get_syntime_arg, TRUE, TRUE},
2259# endif
2260 {EXPAND_HIGHLIGHT, get_highlight_name, TRUE, TRUE},
Bram Moolenaarb4d82e22021-09-01 13:03:39 +02002261 {EXPAND_EVENTS, get_event_name, TRUE, FALSE},
2262 {EXPAND_AUGROUP, get_augroup_name, TRUE, FALSE},
Bram Moolenaar66b51422019-08-18 21:44:12 +02002263# ifdef FEAT_CSCOPE
2264 {EXPAND_CSCOPE, get_cscope_name, TRUE, TRUE},
2265# endif
2266# ifdef FEAT_SIGNS
2267 {EXPAND_SIGN, get_sign_name, TRUE, TRUE},
2268# endif
2269# ifdef FEAT_PROFILE
2270 {EXPAND_PROFILE, get_profile_name, TRUE, TRUE},
2271# endif
2272# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
2273 {EXPAND_LANGUAGE, get_lang_arg, TRUE, FALSE},
2274 {EXPAND_LOCALES, get_locales, TRUE, FALSE},
2275# endif
2276 {EXPAND_ENV_VARS, get_env_name, TRUE, TRUE},
2277 {EXPAND_USER, get_users, TRUE, FALSE},
2278 {EXPAND_ARGLIST, get_arglist_name, TRUE, FALSE},
2279 };
2280 int i;
2281
2282 // Find a context in the table and call the ExpandGeneric() with the
2283 // right function to do the expansion.
2284 ret = FAIL;
K.Takataeeec2542021-06-02 13:28:16 +02002285 for (i = 0; i < (int)ARRAY_LENGTH(tab); ++i)
Bram Moolenaar66b51422019-08-18 21:44:12 +02002286 if (xp->xp_context == tab[i].context)
2287 {
2288 if (tab[i].ic)
2289 regmatch.rm_ic = TRUE;
2290 ret = ExpandGeneric(xp, &regmatch, num_file, file,
2291 tab[i].func, tab[i].escaped);
2292 break;
2293 }
2294 }
2295
2296 vim_regfree(regmatch.regprog);
Bram Moolenaarcc390ff2020-02-29 22:06:30 +01002297 vim_free(tofree);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002298
2299 return ret;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002300}
2301
Bram Moolenaar66b51422019-08-18 21:44:12 +02002302/*
2303 * Expand a list of names.
2304 *
2305 * Generic function for command line completion. It calls a function to
2306 * obtain strings, one by one. The strings are matched against a regexp
2307 * program. Matching strings are copied into an array, which is returned.
2308 *
2309 * Returns OK when no problems encountered, FAIL for error (out of memory).
2310 */
Bram Moolenaar61d7c0d2020-01-05 14:38:40 +01002311 static int
Bram Moolenaar66b51422019-08-18 21:44:12 +02002312ExpandGeneric(
2313 expand_T *xp,
2314 regmatch_T *regmatch,
2315 int *num_file,
2316 char_u ***file,
2317 char_u *((*func)(expand_T *, int)),
2318 // returns a string from the list
2319 int escaped)
2320{
2321 int i;
2322 int count = 0;
2323 int round;
2324 char_u *str;
2325
2326 // do this loop twice:
2327 // round == 0: count the number of matching names
2328 // round == 1: copy the matching names into allocated memory
2329 for (round = 0; round <= 1; ++round)
2330 {
2331 for (i = 0; ; ++i)
2332 {
2333 str = (*func)(xp, i);
2334 if (str == NULL) // end of list
2335 break;
2336 if (*str == NUL) // skip empty strings
2337 continue;
2338
2339 if (vim_regexec(regmatch, str, (colnr_T)0))
2340 {
2341 if (round)
2342 {
2343 if (escaped)
2344 str = vim_strsave_escaped(str, (char_u *)" \t\\.");
2345 else
2346 str = vim_strsave(str);
Bram Moolenaar61d7c0d2020-01-05 14:38:40 +01002347 if (str == NULL)
2348 {
2349 FreeWild(count, *file);
2350 *num_file = 0;
2351 *file = NULL;
2352 return FAIL;
2353 }
Bram Moolenaar66b51422019-08-18 21:44:12 +02002354 (*file)[count] = str;
2355# ifdef FEAT_MENU
2356 if (func == get_menu_names && str != NULL)
2357 {
2358 // test for separator added by get_menu_names()
2359 str += STRLEN(str) - 1;
2360 if (*str == '\001')
2361 *str = '.';
2362 }
2363# endif
2364 }
2365 ++count;
2366 }
2367 }
2368 if (round == 0)
2369 {
2370 if (count == 0)
2371 return OK;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002372 *file = ALLOC_MULT(char_u *, count);
2373 if (*file == NULL)
2374 {
Bram Moolenaar61d7c0d2020-01-05 14:38:40 +01002375 *num_file = 0;
2376 *file = NULL;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002377 return FAIL;
2378 }
Bram Moolenaar61d7c0d2020-01-05 14:38:40 +01002379 *num_file = count;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002380 count = 0;
2381 }
2382 }
2383
2384 // Sort the results. Keep menu's in the specified order.
2385 if (xp->xp_context != EXPAND_MENUNAMES && xp->xp_context != EXPAND_MENUS)
2386 {
2387 if (xp->xp_context == EXPAND_EXPRESSION
2388 || xp->xp_context == EXPAND_FUNCTIONS
naohiro onodfe04db2021-09-12 15:45:10 +02002389 || xp->xp_context == EXPAND_USER_FUNC
2390 || xp->xp_context == EXPAND_DISASSEMBLE)
Bram Moolenaar66b51422019-08-18 21:44:12 +02002391 // <SNR> functions should be sorted to the end.
2392 qsort((void *)*file, (size_t)*num_file, sizeof(char_u *),
2393 sort_func_compare);
2394 else
2395 sort_strings(*file, *num_file);
2396 }
2397
Bram Moolenaar0a52df52019-08-18 22:26:31 +02002398#if defined(FEAT_SYN_HL)
Bram Moolenaar66b51422019-08-18 21:44:12 +02002399 // Reset the variables used for special highlight names expansion, so that
2400 // they don't show up when getting normal highlight names by ID.
2401 reset_expand_highlight();
Bram Moolenaar0a52df52019-08-18 22:26:31 +02002402#endif
Bram Moolenaar66b51422019-08-18 21:44:12 +02002403 return OK;
2404}
2405
2406/*
2407 * Complete a shell command.
2408 * Returns FAIL or OK;
2409 */
2410 static int
2411expand_shellcmd(
2412 char_u *filepat, // pattern to match with command names
2413 int *num_file, // return: number of matches
2414 char_u ***file, // return: array with matches
2415 int flagsarg) // EW_ flags
2416{
2417 char_u *pat;
2418 int i;
2419 char_u *path = NULL;
2420 int mustfree = FALSE;
2421 garray_T ga;
Bram Moolenaar8b7aa2f2020-01-07 21:05:49 +01002422 char_u *buf;
Bram Moolenaar66b51422019-08-18 21:44:12 +02002423 size_t l;
2424 char_u *s, *e;
2425 int flags = flagsarg;
2426 int ret;
2427 int did_curdir = FALSE;
2428 hashtab_T found_ht;
2429 hashitem_T *hi;
2430 hash_T hash;
2431
Bram Moolenaar8b7aa2f2020-01-07 21:05:49 +01002432 buf = alloc(MAXPATHL);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002433 if (buf == NULL)
2434 return FAIL;
2435
Bram Moolenaar8b7aa2f2020-01-07 21:05:49 +01002436 // for ":set path=" and ":set tags=" halve backslashes for escaped space
Bram Moolenaar66b51422019-08-18 21:44:12 +02002437 pat = vim_strsave(filepat);
Bram Moolenaar8b7aa2f2020-01-07 21:05:49 +01002438 if (pat == NULL)
2439 {
2440 vim_free(buf);
2441 return FAIL;
2442 }
2443
Bram Moolenaar66b51422019-08-18 21:44:12 +02002444 for (i = 0; pat[i]; ++i)
2445 if (pat[i] == '\\' && pat[i + 1] == ' ')
2446 STRMOVE(pat + i, pat + i + 1);
2447
2448 flags |= EW_FILE | EW_EXEC | EW_SHELLCMD;
2449
2450 if (pat[0] == '.' && (vim_ispathsep(pat[1])
2451 || (pat[1] == '.' && vim_ispathsep(pat[2]))))
2452 path = (char_u *)".";
2453 else
2454 {
2455 // For an absolute name we don't use $PATH.
2456 if (!mch_isFullName(pat))
2457 path = vim_getenv((char_u *)"PATH", &mustfree);
2458 if (path == NULL)
2459 path = (char_u *)"";
2460 }
2461
2462 // Go over all directories in $PATH. Expand matches in that directory and
2463 // collect them in "ga". When "." is not in $PATH also expand for the
2464 // current directory, to find "subdir/cmd".
Bram Moolenaar04935fb2022-01-08 16:19:22 +00002465 ga_init2(&ga, sizeof(char *), 10);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002466 hash_init(&found_ht);
2467 for (s = path; ; s = e)
2468 {
2469# if defined(MSWIN)
2470 e = vim_strchr(s, ';');
2471# else
2472 e = vim_strchr(s, ':');
2473# endif
2474 if (e == NULL)
2475 e = s + STRLEN(s);
2476
2477 if (*s == NUL)
2478 {
2479 if (did_curdir)
2480 break;
2481 // Find directories in the current directory, path is empty.
2482 did_curdir = TRUE;
2483 flags |= EW_DIR;
2484 }
2485 else if (STRNCMP(s, ".", (int)(e - s)) == 0)
2486 {
2487 did_curdir = TRUE;
2488 flags |= EW_DIR;
2489 }
2490 else
2491 // Do not match directories inside a $PATH item.
2492 flags &= ~EW_DIR;
2493
2494 l = e - s;
2495 if (l > MAXPATHL - 5)
2496 break;
2497 vim_strncpy(buf, s, l);
2498 add_pathsep(buf);
2499 l = STRLEN(buf);
2500 vim_strncpy(buf + l, pat, MAXPATHL - 1 - l);
2501
2502 // Expand matches in one directory of $PATH.
2503 ret = expand_wildcards(1, &buf, num_file, file, flags);
2504 if (ret == OK)
2505 {
2506 if (ga_grow(&ga, *num_file) == FAIL)
2507 FreeWild(*num_file, *file);
2508 else
2509 {
2510 for (i = 0; i < *num_file; ++i)
2511 {
2512 char_u *name = (*file)[i];
2513
2514 if (STRLEN(name) > l)
2515 {
2516 // Check if this name was already found.
2517 hash = hash_hash(name + l);
2518 hi = hash_lookup(&found_ht, name + l, hash);
2519 if (HASHITEM_EMPTY(hi))
2520 {
2521 // Remove the path that was prepended.
2522 STRMOVE(name, name + l);
2523 ((char_u **)ga.ga_data)[ga.ga_len++] = name;
2524 hash_add_item(&found_ht, hi, name, hash);
2525 name = NULL;
2526 }
2527 }
2528 vim_free(name);
2529 }
2530 vim_free(*file);
2531 }
2532 }
2533 if (*e != NUL)
2534 ++e;
2535 }
2536 *file = ga.ga_data;
2537 *num_file = ga.ga_len;
2538
2539 vim_free(buf);
2540 vim_free(pat);
2541 if (mustfree)
2542 vim_free(path);
2543 hash_clear(&found_ht);
2544 return OK;
2545}
2546
2547# if defined(FEAT_EVAL)
2548/*
2549 * Call "user_expand_func()" to invoke a user defined Vim script function and
Bram Moolenaarc841aff2020-07-25 14:11:55 +02002550 * return the result (either a string, a List or NULL).
Bram Moolenaar66b51422019-08-18 21:44:12 +02002551 */
2552 static void *
2553call_user_expand_func(
2554 void *(*user_expand_func)(char_u *, int, typval_T *),
2555 expand_T *xp,
2556 int *num_file,
2557 char_u ***file)
2558{
2559 cmdline_info_T *ccline = get_cmdline_info();
2560 int keep = 0;
2561 typval_T args[4];
2562 sctx_T save_current_sctx = current_sctx;
2563 char_u *pat = NULL;
2564 void *ret;
2565
2566 if (xp->xp_arg == NULL || xp->xp_arg[0] == '\0' || xp->xp_line == NULL)
2567 return NULL;
2568 *num_file = 0;
2569 *file = NULL;
2570
2571 if (ccline->cmdbuff != NULL)
2572 {
2573 keep = ccline->cmdbuff[ccline->cmdlen];
2574 ccline->cmdbuff[ccline->cmdlen] = 0;
2575 }
2576
2577 pat = vim_strnsave(xp->xp_pattern, xp->xp_pattern_len);
2578
2579 args[0].v_type = VAR_STRING;
2580 args[0].vval.v_string = pat;
2581 args[1].v_type = VAR_STRING;
2582 args[1].vval.v_string = xp->xp_line;
2583 args[2].v_type = VAR_NUMBER;
2584 args[2].vval.v_number = xp->xp_col;
2585 args[3].v_type = VAR_UNKNOWN;
2586
2587 current_sctx = xp->xp_script_ctx;
2588
2589 ret = user_expand_func(xp->xp_arg, 3, args);
2590
2591 current_sctx = save_current_sctx;
2592 if (ccline->cmdbuff != NULL)
2593 ccline->cmdbuff[ccline->cmdlen] = keep;
2594
2595 vim_free(pat);
2596 return ret;
2597}
2598
2599/*
2600 * Expand names with a function defined by the user.
2601 */
2602 static int
2603ExpandUserDefined(
2604 expand_T *xp,
2605 regmatch_T *regmatch,
2606 int *num_file,
2607 char_u ***file)
2608{
2609 char_u *retstr;
2610 char_u *s;
2611 char_u *e;
2612 int keep;
2613 garray_T ga;
2614 int skip;
2615
2616 retstr = call_user_expand_func(call_func_retstr, xp, num_file, file);
2617 if (retstr == NULL)
2618 return FAIL;
2619
Bram Moolenaar04935fb2022-01-08 16:19:22 +00002620 ga_init2(&ga, sizeof(char *), 3);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002621 for (s = retstr; *s != NUL; s = e)
2622 {
2623 e = vim_strchr(s, '\n');
2624 if (e == NULL)
2625 e = s + STRLEN(s);
2626 keep = *e;
2627 *e = NUL;
2628
2629 skip = xp->xp_pattern[0] && vim_regexec(regmatch, s, (colnr_T)0) == 0;
2630 *e = keep;
2631
2632 if (!skip)
2633 {
2634 if (ga_grow(&ga, 1) == FAIL)
2635 break;
Bram Moolenaardf44a272020-06-07 20:49:05 +02002636 ((char_u **)ga.ga_data)[ga.ga_len] = vim_strnsave(s, e - s);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002637 ++ga.ga_len;
2638 }
2639
2640 if (*e != NUL)
2641 ++e;
2642 }
2643 vim_free(retstr);
2644 *file = ga.ga_data;
2645 *num_file = ga.ga_len;
2646 return OK;
2647}
2648
2649/*
2650 * Expand names with a list returned by a function defined by the user.
2651 */
2652 static int
2653ExpandUserList(
2654 expand_T *xp,
2655 int *num_file,
2656 char_u ***file)
2657{
2658 list_T *retlist;
2659 listitem_T *li;
2660 garray_T ga;
2661
2662 retlist = call_user_expand_func(call_func_retlist, xp, num_file, file);
2663 if (retlist == NULL)
2664 return FAIL;
2665
Bram Moolenaar04935fb2022-01-08 16:19:22 +00002666 ga_init2(&ga, sizeof(char *), 3);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002667 // Loop over the items in the list.
Bram Moolenaaraeea7212020-04-02 18:50:46 +02002668 FOR_ALL_LIST_ITEMS(retlist, li)
Bram Moolenaar66b51422019-08-18 21:44:12 +02002669 {
2670 if (li->li_tv.v_type != VAR_STRING || li->li_tv.vval.v_string == NULL)
2671 continue; // Skip non-string items and empty strings
2672
2673 if (ga_grow(&ga, 1) == FAIL)
2674 break;
2675
2676 ((char_u **)ga.ga_data)[ga.ga_len] =
2677 vim_strsave(li->li_tv.vval.v_string);
2678 ++ga.ga_len;
2679 }
2680 list_unref(retlist);
2681
2682 *file = ga.ga_data;
2683 *num_file = ga.ga_len;
2684 return OK;
2685}
2686# endif
2687
2688/*
Bram Moolenaar66b51422019-08-18 21:44:12 +02002689 * Expand "file" for all comma-separated directories in "path".
2690 * Adds the matches to "ga". Caller must init "ga".
2691 */
2692 void
2693globpath(
2694 char_u *path,
2695 char_u *file,
2696 garray_T *ga,
2697 int expand_options)
2698{
2699 expand_T xpc;
2700 char_u *buf;
2701 int i;
2702 int num_p;
2703 char_u **p;
2704
2705 buf = alloc(MAXPATHL);
2706 if (buf == NULL)
2707 return;
2708
2709 ExpandInit(&xpc);
2710 xpc.xp_context = EXPAND_FILES;
2711
2712 // Loop over all entries in {path}.
2713 while (*path != NUL)
2714 {
2715 // Copy one item of the path to buf[] and concatenate the file name.
2716 copy_option_part(&path, buf, MAXPATHL, ",");
2717 if (STRLEN(buf) + STRLEN(file) + 2 < MAXPATHL)
2718 {
2719# if defined(MSWIN)
2720 // Using the platform's path separator (\) makes vim incorrectly
2721 // treat it as an escape character, use '/' instead.
2722 if (*buf != NUL && !after_pathsep(buf, buf + STRLEN(buf)))
2723 STRCAT(buf, "/");
2724# else
2725 add_pathsep(buf);
2726# endif
2727 STRCAT(buf, file);
2728 if (ExpandFromContext(&xpc, buf, &num_p, &p,
2729 WILD_SILENT|expand_options) != FAIL && num_p > 0)
2730 {
2731 ExpandEscape(&xpc, buf, num_p, p, WILD_SILENT|expand_options);
2732
2733 if (ga_grow(ga, num_p) == OK)
Bram Moolenaarf0f80552020-01-05 22:05:49 +01002734 // take over the pointers and put them in "ga"
Bram Moolenaar66b51422019-08-18 21:44:12 +02002735 for (i = 0; i < num_p; ++i)
2736 {
Bram Moolenaarf0f80552020-01-05 22:05:49 +01002737 ((char_u **)ga->ga_data)[ga->ga_len] = p[i];
Bram Moolenaar66b51422019-08-18 21:44:12 +02002738 ++ga->ga_len;
2739 }
Bram Moolenaarf0f80552020-01-05 22:05:49 +01002740 vim_free(p);
Bram Moolenaar66b51422019-08-18 21:44:12 +02002741 }
2742 }
2743 }
2744
2745 vim_free(buf);
2746}
Bram Moolenaar66b51422019-08-18 21:44:12 +02002747
Bram Moolenaareadee482020-09-04 15:37:31 +02002748#ifdef FEAT_WILDMENU
2749
2750/*
2751 * Translate some keys pressed when 'wildmenu' is used.
2752 */
2753 int
2754wildmenu_translate_key(
2755 cmdline_info_T *cclp,
2756 int key,
2757 expand_T *xp,
2758 int did_wild_list)
2759{
2760 int c = key;
2761
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +00002762#ifdef FEAT_WILDMENU
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +00002763 if (cmdline_pum_active())
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +00002764 {
Yegappan Lakshmanan560dff42022-02-10 19:52:10 +00002765 // When the popup menu is used for cmdline completion:
2766 // Up : go to the previous item in the menu
2767 // Down : go to the next item in the menu
2768 // Left : go to the parent directory
2769 // Right: list the files in the selected directory
2770 switch (c)
2771 {
2772 case K_UP: c = K_LEFT; break;
2773 case K_DOWN: c = K_RIGHT; break;
2774 case K_LEFT: c = K_UP; break;
2775 case K_RIGHT: c = K_DOWN; break;
2776 default: break;
2777 }
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +00002778 }
2779#endif
2780
Bram Moolenaareadee482020-09-04 15:37:31 +02002781 if (did_wild_list && p_wmnu)
2782 {
2783 if (c == K_LEFT)
2784 c = Ctrl_P;
2785 else if (c == K_RIGHT)
2786 c = Ctrl_N;
2787 }
Yegappan Lakshmanan3908ef52022-02-08 12:08:07 +00002788
Bram Moolenaareadee482020-09-04 15:37:31 +02002789 // Hitting CR after "emenu Name.": complete submenu
2790 if (xp->xp_context == EXPAND_MENUNAMES && p_wmnu
2791 && cclp->cmdpos > 1
2792 && cclp->cmdbuff[cclp->cmdpos - 1] == '.'
2793 && cclp->cmdbuff[cclp->cmdpos - 2] != '\\'
2794 && (c == '\n' || c == '\r' || c == K_KENTER))
2795 c = K_DOWN;
2796
2797 return c;
2798}
2799
2800/*
2801 * Delete characters on the command line, from "from" to the current
2802 * position.
2803 */
2804 static void
2805cmdline_del(cmdline_info_T *cclp, int from)
2806{
2807 mch_memmove(cclp->cmdbuff + from, cclp->cmdbuff + cclp->cmdpos,
2808 (size_t)(cclp->cmdlen - cclp->cmdpos + 1));
2809 cclp->cmdlen -= cclp->cmdpos - from;
2810 cclp->cmdpos = from;
2811}
2812
2813/*
2814 * Handle a key pressed when wild menu is displayed
2815 */
2816 int
2817wildmenu_process_key(cmdline_info_T *cclp, int key, expand_T *xp)
2818{
2819 int c = key;
2820 int i;
2821 int j;
2822
2823 if (!p_wmnu)
2824 return c;
2825
2826 // Special translations for 'wildmenu'
2827 if (xp->xp_context == EXPAND_MENUNAMES)
2828 {
2829 // Hitting <Down> after "emenu Name.": complete submenu
2830 if (c == K_DOWN && cclp->cmdpos > 0
2831 && cclp->cmdbuff[cclp->cmdpos - 1] == '.')
Bram Moolenaarb0ac4ea2020-12-26 12:06:54 +01002832 {
Bram Moolenaareadee482020-09-04 15:37:31 +02002833 c = p_wc;
Bram Moolenaarb0ac4ea2020-12-26 12:06:54 +01002834 KeyTyped = TRUE; // in case the key was mapped
2835 }
Bram Moolenaareadee482020-09-04 15:37:31 +02002836 else if (c == K_UP)
2837 {
2838 // Hitting <Up>: Remove one submenu name in front of the
2839 // cursor
2840 int found = FALSE;
2841
2842 j = (int)(xp->xp_pattern - cclp->cmdbuff);
2843 i = 0;
2844 while (--j > 0)
2845 {
2846 // check for start of menu name
2847 if (cclp->cmdbuff[j] == ' '
2848 && cclp->cmdbuff[j - 1] != '\\')
2849 {
2850 i = j + 1;
2851 break;
2852 }
2853 // check for start of submenu name
2854 if (cclp->cmdbuff[j] == '.'
2855 && cclp->cmdbuff[j - 1] != '\\')
2856 {
2857 if (found)
2858 {
2859 i = j + 1;
2860 break;
2861 }
2862 else
2863 found = TRUE;
2864 }
2865 }
2866 if (i > 0)
2867 cmdline_del(cclp, i);
2868 c = p_wc;
Bram Moolenaarb0ac4ea2020-12-26 12:06:54 +01002869 KeyTyped = TRUE; // in case the key was mapped
Bram Moolenaareadee482020-09-04 15:37:31 +02002870 xp->xp_context = EXPAND_NOTHING;
2871 }
2872 }
2873 if ((xp->xp_context == EXPAND_FILES
2874 || xp->xp_context == EXPAND_DIRECTORIES
2875 || xp->xp_context == EXPAND_SHELLCMD))
2876 {
2877 char_u upseg[5];
2878
2879 upseg[0] = PATHSEP;
2880 upseg[1] = '.';
2881 upseg[2] = '.';
2882 upseg[3] = PATHSEP;
2883 upseg[4] = NUL;
2884
2885 if (c == K_DOWN
2886 && cclp->cmdpos > 0
2887 && cclp->cmdbuff[cclp->cmdpos - 1] == PATHSEP
2888 && (cclp->cmdpos < 3
2889 || cclp->cmdbuff[cclp->cmdpos - 2] != '.'
2890 || cclp->cmdbuff[cclp->cmdpos - 3] != '.'))
2891 {
2892 // go down a directory
2893 c = p_wc;
Bram Moolenaarb0ac4ea2020-12-26 12:06:54 +01002894 KeyTyped = TRUE; // in case the key was mapped
Bram Moolenaareadee482020-09-04 15:37:31 +02002895 }
2896 else if (STRNCMP(xp->xp_pattern, upseg + 1, 3) == 0 && c == K_DOWN)
2897 {
2898 // If in a direct ancestor, strip off one ../ to go down
2899 int found = FALSE;
2900
2901 j = cclp->cmdpos;
2902 i = (int)(xp->xp_pattern - cclp->cmdbuff);
2903 while (--j > i)
2904 {
2905 if (has_mbyte)
2906 j -= (*mb_head_off)(cclp->cmdbuff, cclp->cmdbuff + j);
2907 if (vim_ispathsep(cclp->cmdbuff[j]))
2908 {
2909 found = TRUE;
2910 break;
2911 }
2912 }
2913 if (found
2914 && cclp->cmdbuff[j - 1] == '.'
2915 && cclp->cmdbuff[j - 2] == '.'
2916 && (vim_ispathsep(cclp->cmdbuff[j - 3]) || j == i + 2))
2917 {
2918 cmdline_del(cclp, j - 2);
2919 c = p_wc;
Bram Moolenaarb0ac4ea2020-12-26 12:06:54 +01002920 KeyTyped = TRUE; // in case the key was mapped
Bram Moolenaareadee482020-09-04 15:37:31 +02002921 }
2922 }
2923 else if (c == K_UP)
2924 {
2925 // go up a directory
2926 int found = FALSE;
2927
2928 j = cclp->cmdpos - 1;
2929 i = (int)(xp->xp_pattern - cclp->cmdbuff);
2930 while (--j > i)
2931 {
2932 if (has_mbyte)
2933 j -= (*mb_head_off)(cclp->cmdbuff, cclp->cmdbuff + j);
2934 if (vim_ispathsep(cclp->cmdbuff[j])
2935# ifdef BACKSLASH_IN_FILENAME
2936 && vim_strchr((char_u *)" *?[{`$%#",
2937 cclp->cmdbuff[j + 1]) == NULL
2938# endif
2939 )
2940 {
2941 if (found)
2942 {
2943 i = j + 1;
2944 break;
2945 }
2946 else
2947 found = TRUE;
2948 }
2949 }
2950
2951 if (!found)
2952 j = i;
2953 else if (STRNCMP(cclp->cmdbuff + j, upseg, 4) == 0)
2954 j += 4;
2955 else if (STRNCMP(cclp->cmdbuff + j, upseg + 1, 3) == 0
2956 && j == i)
2957 j += 3;
2958 else
2959 j = 0;
2960 if (j > 0)
2961 {
2962 // TODO this is only for DOS/UNIX systems - need to put in
2963 // machine-specific stuff here and in upseg init
2964 cmdline_del(cclp, j);
2965 put_on_cmdline(upseg + 1, 3, FALSE);
2966 }
2967 else if (cclp->cmdpos > i)
2968 cmdline_del(cclp, i);
2969
2970 // Now complete in the new directory. Set KeyTyped in case the
2971 // Up key came from a mapping.
2972 c = p_wc;
2973 KeyTyped = TRUE;
2974 }
2975 }
2976
2977 return c;
2978}
2979
2980/*
2981 * Free expanded names when finished walking through the matches
2982 */
2983 void
2984wildmenu_cleanup(cmdline_info_T *cclp)
2985{
2986 int skt = KeyTyped;
2987 int old_RedrawingDisabled = RedrawingDisabled;
2988
2989 if (!p_wmnu || wild_menu_showing == 0)
2990 return;
2991
2992 if (cclp->input_fn)
2993 RedrawingDisabled = 0;
2994
2995 if (wild_menu_showing == WM_SCROLLED)
2996 {
2997 // Entered command line, move it up
2998 cmdline_row--;
2999 redrawcmd();
3000 }
3001 else if (save_p_ls != -1)
3002 {
3003 // restore 'laststatus' and 'winminheight'
3004 p_ls = save_p_ls;
3005 p_wmh = save_p_wmh;
3006 last_status(FALSE);
3007 update_screen(VALID); // redraw the screen NOW
3008 redrawcmd();
3009 save_p_ls = -1;
3010 }
3011 else
3012 {
3013 win_redraw_last_status(topframe);
3014 redraw_statuslines();
3015 }
3016 KeyTyped = skt;
3017 wild_menu_showing = 0;
3018 if (cclp->input_fn)
3019 RedrawingDisabled = old_RedrawingDisabled;
3020}
3021#endif
3022
Bram Moolenaar0a52df52019-08-18 22:26:31 +02003023#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar66b51422019-08-18 21:44:12 +02003024/*
3025 * "getcompletion()" function
3026 */
3027 void
3028f_getcompletion(typval_T *argvars, typval_T *rettv)
3029{
3030 char_u *pat;
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003031 char_u *type;
Bram Moolenaar66b51422019-08-18 21:44:12 +02003032 expand_T xpc;
3033 int filtered = FALSE;
3034 int options = WILD_SILENT | WILD_USE_NL | WILD_ADD_SLASH
Shougo Matsushitaae38a9d2021-10-21 11:39:53 +01003035 | WILD_NO_BEEP | WILD_HOME_REPLACE;
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003036
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02003037 if (in_vim9script()
3038 && (check_for_string_arg(argvars, 0) == FAIL
3039 || check_for_string_arg(argvars, 1) == FAIL
3040 || check_for_opt_bool_arg(argvars, 2) == FAIL))
3041 return;
3042
ii144785fe02021-11-21 12:13:56 +00003043 pat = tv_get_string(&argvars[0]);
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003044 if (argvars[1].v_type != VAR_STRING)
3045 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00003046 semsg(_(e_invalid_argument_str), "type must be a string");
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003047 return;
3048 }
3049 type = tv_get_string(&argvars[1]);
Bram Moolenaar66b51422019-08-18 21:44:12 +02003050
3051 if (argvars[2].v_type != VAR_UNKNOWN)
Bram Moolenaard217a872020-09-05 18:31:33 +02003052 filtered = tv_get_bool_chk(&argvars[2], NULL);
Bram Moolenaar66b51422019-08-18 21:44:12 +02003053
3054 if (p_wic)
3055 options |= WILD_ICASE;
3056
3057 // For filtered results, 'wildignore' is used
3058 if (!filtered)
3059 options |= WILD_KEEP_ALL;
3060
3061 ExpandInit(&xpc);
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003062 if (STRCMP(type, "cmdline") == 0)
Bram Moolenaar66b51422019-08-18 21:44:12 +02003063 {
ii144785fe02021-11-21 12:13:56 +00003064 set_one_cmd_context(&xpc, pat);
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003065 xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
ii144785fe02021-11-21 12:13:56 +00003066 xpc.xp_col = (int)STRLEN(pat);
Bram Moolenaar66b51422019-08-18 21:44:12 +02003067 }
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003068 else
3069 {
ii144785fe02021-11-21 12:13:56 +00003070 xpc.xp_pattern = pat;
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003071 xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
3072
3073 xpc.xp_context = cmdcomplete_str_to_type(type);
3074 if (xpc.xp_context == EXPAND_NOTHING)
3075 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00003076 semsg(_(e_invalid_argument_str), type);
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003077 return;
3078 }
Bram Moolenaar66b51422019-08-18 21:44:12 +02003079
3080# if defined(FEAT_MENU)
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003081 if (xpc.xp_context == EXPAND_MENUS)
3082 {
3083 set_context_in_menu_cmd(&xpc, (char_u *)"menu", xpc.xp_pattern, FALSE);
3084 xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
3085 }
Bram Moolenaar66b51422019-08-18 21:44:12 +02003086# endif
3087# ifdef FEAT_CSCOPE
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003088 if (xpc.xp_context == EXPAND_CSCOPE)
3089 {
3090 set_context_in_cscope_cmd(&xpc, xpc.xp_pattern, CMD_cscope);
3091 xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
3092 }
Bram Moolenaar66b51422019-08-18 21:44:12 +02003093# endif
3094# ifdef FEAT_SIGNS
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003095 if (xpc.xp_context == EXPAND_SIGN)
3096 {
3097 set_context_in_sign_cmd(&xpc, xpc.xp_pattern);
3098 xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern);
3099 }
Bram Moolenaar66b51422019-08-18 21:44:12 +02003100# endif
Bram Moolenaar1f1fd442020-06-07 18:45:14 +02003101 }
Bram Moolenaar66b51422019-08-18 21:44:12 +02003102
3103 pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context);
3104 if ((rettv_list_alloc(rettv) != FAIL) && (pat != NULL))
3105 {
3106 int i;
3107
3108 ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP);
3109
3110 for (i = 0; i < xpc.xp_numfiles; i++)
3111 list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1);
3112 }
3113 vim_free(pat);
3114 ExpandCleanup(&xpc);
3115}
Bram Moolenaar0a52df52019-08-18 22:26:31 +02003116#endif // FEAT_EVAL