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