xref: /vim-8.2.3635/src/filepath.c (revision 851c7a69)
1b005cd80SBram Moolenaar /* vi:set ts=8 sts=4 sw=4 noet:
2b005cd80SBram Moolenaar  *
3b005cd80SBram Moolenaar  * VIM - Vi IMproved	by Bram Moolenaar
4b005cd80SBram Moolenaar  *
5b005cd80SBram Moolenaar  * Do ":help uganda"  in Vim to read copying and usage conditions.
6b005cd80SBram Moolenaar  * Do ":help credits" in Vim to see a list of people who contributed.
7b005cd80SBram Moolenaar  * See README.txt for an overview of the Vim source code.
8b005cd80SBram Moolenaar  */
9b005cd80SBram Moolenaar 
10b005cd80SBram Moolenaar /*
119810cfbeSBram Moolenaar  * filepath.c: dealing with file names and paths.
12b005cd80SBram Moolenaar  */
13b005cd80SBram Moolenaar 
14b005cd80SBram Moolenaar #include "vim.h"
15b005cd80SBram Moolenaar 
16b005cd80SBram Moolenaar #ifdef MSWIN
17b005cd80SBram Moolenaar /*
18b005cd80SBram Moolenaar  * Functions for ":8" filename modifier: get 8.3 version of a filename.
19b005cd80SBram Moolenaar  */
20b005cd80SBram Moolenaar 
21b005cd80SBram Moolenaar /*
22b005cd80SBram Moolenaar  * Get the short path (8.3) for the filename in "fnamep".
23b005cd80SBram Moolenaar  * Only works for a valid file name.
24b005cd80SBram Moolenaar  * When the path gets longer "fnamep" is changed and the allocated buffer
25b005cd80SBram Moolenaar  * is put in "bufp".
26b005cd80SBram Moolenaar  * *fnamelen is the length of "fnamep" and set to 0 for a nonexistent path.
27b005cd80SBram Moolenaar  * Returns OK on success, FAIL on failure.
28b005cd80SBram Moolenaar  */
29b005cd80SBram Moolenaar     static int
get_short_pathname(char_u ** fnamep,char_u ** bufp,int * fnamelen)30b005cd80SBram Moolenaar get_short_pathname(char_u **fnamep, char_u **bufp, int *fnamelen)
31b005cd80SBram Moolenaar {
32b005cd80SBram Moolenaar     int		l, len;
333f39697bSBram Moolenaar     WCHAR	*newbuf;
343f39697bSBram Moolenaar     WCHAR	*wfname;
35b005cd80SBram Moolenaar 
363f39697bSBram Moolenaar     len = MAXPATHL;
373f39697bSBram Moolenaar     newbuf = malloc(len * sizeof(*newbuf));
383f39697bSBram Moolenaar     if (newbuf == NULL)
393f39697bSBram Moolenaar 	return FAIL;
403f39697bSBram Moolenaar 
413f39697bSBram Moolenaar     wfname = enc_to_utf16(*fnamep, NULL);
423f39697bSBram Moolenaar     if (wfname == NULL)
433f39697bSBram Moolenaar     {
443f39697bSBram Moolenaar 	vim_free(newbuf);
453f39697bSBram Moolenaar 	return FAIL;
463f39697bSBram Moolenaar     }
473f39697bSBram Moolenaar 
483f39697bSBram Moolenaar     l = GetShortPathNameW(wfname, newbuf, len);
49b005cd80SBram Moolenaar     if (l > len - 1)
50b005cd80SBram Moolenaar     {
5126262f87SBram Moolenaar 	// If that doesn't work (not enough space), then save the string
5226262f87SBram Moolenaar 	// and try again with a new buffer big enough.
533f39697bSBram Moolenaar 	WCHAR *newbuf_t = newbuf;
543f39697bSBram Moolenaar 	newbuf = vim_realloc(newbuf, (l + 1) * sizeof(*newbuf));
55b005cd80SBram Moolenaar 	if (newbuf == NULL)
563f39697bSBram Moolenaar 	{
573f39697bSBram Moolenaar 	    vim_free(wfname);
583f39697bSBram Moolenaar 	    vim_free(newbuf_t);
59b005cd80SBram Moolenaar 	    return FAIL;
60b005cd80SBram Moolenaar 	}
613f39697bSBram Moolenaar 	// Really should always succeed, as the buffer is big enough.
623f39697bSBram Moolenaar 	l = GetShortPathNameW(wfname, newbuf, l+1);
633f39697bSBram Moolenaar     }
643f39697bSBram Moolenaar     if (l != 0)
653f39697bSBram Moolenaar     {
663f39697bSBram Moolenaar 	char_u *p = utf16_to_enc(newbuf, NULL);
67c74fbfedSBram Moolenaar 
683f39697bSBram Moolenaar 	if (p != NULL)
693f39697bSBram Moolenaar 	{
703f39697bSBram Moolenaar 	    vim_free(*bufp);
713f39697bSBram Moolenaar 	    *fnamep = *bufp = p;
723f39697bSBram Moolenaar 	}
733f39697bSBram Moolenaar 	else
743f39697bSBram Moolenaar 	{
753f39697bSBram Moolenaar 	    vim_free(wfname);
763f39697bSBram Moolenaar 	    vim_free(newbuf);
773f39697bSBram Moolenaar 	    return FAIL;
783f39697bSBram Moolenaar 	}
793f39697bSBram Moolenaar     }
803f39697bSBram Moolenaar     vim_free(wfname);
813f39697bSBram Moolenaar     vim_free(newbuf);
82b005cd80SBram Moolenaar 
832ade7147SBram Moolenaar     *fnamelen = l == 0 ? l : (int)STRLEN(*bufp);
84b005cd80SBram Moolenaar     return OK;
85b005cd80SBram Moolenaar }
86b005cd80SBram Moolenaar 
87b005cd80SBram Moolenaar /*
88b005cd80SBram Moolenaar  * Get the short path (8.3) for the filename in "fname". The converted
89b005cd80SBram Moolenaar  * path is returned in "bufp".
90b005cd80SBram Moolenaar  *
91b005cd80SBram Moolenaar  * Some of the directories specified in "fname" may not exist. This function
92b005cd80SBram Moolenaar  * will shorten the existing directories at the beginning of the path and then
93b005cd80SBram Moolenaar  * append the remaining non-existing path.
94b005cd80SBram Moolenaar  *
95b005cd80SBram Moolenaar  * fname - Pointer to the filename to shorten.  On return, contains the
96b005cd80SBram Moolenaar  *	   pointer to the shortened pathname
97b005cd80SBram Moolenaar  * bufp -  Pointer to an allocated buffer for the filename.
98b005cd80SBram Moolenaar  * fnamelen - Length of the filename pointed to by fname
99b005cd80SBram Moolenaar  *
100b005cd80SBram Moolenaar  * Returns OK on success (or nothing done) and FAIL on failure (out of memory).
101b005cd80SBram Moolenaar  */
102b005cd80SBram Moolenaar     static int
shortpath_for_invalid_fname(char_u ** fname,char_u ** bufp,int * fnamelen)103b005cd80SBram Moolenaar shortpath_for_invalid_fname(
104b005cd80SBram Moolenaar     char_u	**fname,
105b005cd80SBram Moolenaar     char_u	**bufp,
106b005cd80SBram Moolenaar     int		*fnamelen)
107b005cd80SBram Moolenaar {
108b005cd80SBram Moolenaar     char_u	*short_fname, *save_fname, *pbuf_unused;
109b005cd80SBram Moolenaar     char_u	*endp, *save_endp;
110b005cd80SBram Moolenaar     char_u	ch;
111b005cd80SBram Moolenaar     int		old_len, len;
112b005cd80SBram Moolenaar     int		new_len, sfx_len;
113b005cd80SBram Moolenaar     int		retval = OK;
114b005cd80SBram Moolenaar 
11526262f87SBram Moolenaar     // Make a copy
116b005cd80SBram Moolenaar     old_len = *fnamelen;
117b005cd80SBram Moolenaar     save_fname = vim_strnsave(*fname, old_len);
118b005cd80SBram Moolenaar     pbuf_unused = NULL;
119b005cd80SBram Moolenaar     short_fname = NULL;
120b005cd80SBram Moolenaar 
12126262f87SBram Moolenaar     endp = save_fname + old_len - 1; // Find the end of the copy
122b005cd80SBram Moolenaar     save_endp = endp;
123b005cd80SBram Moolenaar 
124b005cd80SBram Moolenaar     /*
125b005cd80SBram Moolenaar      * Try shortening the supplied path till it succeeds by removing one
126b005cd80SBram Moolenaar      * directory at a time from the tail of the path.
127b005cd80SBram Moolenaar      */
128b005cd80SBram Moolenaar     len = 0;
129b005cd80SBram Moolenaar     for (;;)
130b005cd80SBram Moolenaar     {
13126262f87SBram Moolenaar 	// go back one path-separator
132b005cd80SBram Moolenaar 	while (endp > save_fname && !after_pathsep(save_fname, endp + 1))
133b005cd80SBram Moolenaar 	    --endp;
134b005cd80SBram Moolenaar 	if (endp <= save_fname)
13526262f87SBram Moolenaar 	    break;		// processed the complete path
136b005cd80SBram Moolenaar 
137b005cd80SBram Moolenaar 	/*
138b005cd80SBram Moolenaar 	 * Replace the path separator with a NUL and try to shorten the
139b005cd80SBram Moolenaar 	 * resulting path.
140b005cd80SBram Moolenaar 	 */
141b005cd80SBram Moolenaar 	ch = *endp;
142b005cd80SBram Moolenaar 	*endp = 0;
143b005cd80SBram Moolenaar 	short_fname = save_fname;
144b005cd80SBram Moolenaar 	len = (int)STRLEN(short_fname) + 1;
145b005cd80SBram Moolenaar 	if (get_short_pathname(&short_fname, &pbuf_unused, &len) == FAIL)
146b005cd80SBram Moolenaar 	{
147b005cd80SBram Moolenaar 	    retval = FAIL;
148b005cd80SBram Moolenaar 	    goto theend;
149b005cd80SBram Moolenaar 	}
15026262f87SBram Moolenaar 	*endp = ch;	// preserve the string
151b005cd80SBram Moolenaar 
152b005cd80SBram Moolenaar 	if (len > 0)
15326262f87SBram Moolenaar 	    break;	// successfully shortened the path
154b005cd80SBram Moolenaar 
15526262f87SBram Moolenaar 	// failed to shorten the path. Skip the path separator
156b005cd80SBram Moolenaar 	--endp;
157b005cd80SBram Moolenaar     }
158b005cd80SBram Moolenaar 
159b005cd80SBram Moolenaar     if (len > 0)
160b005cd80SBram Moolenaar     {
161b005cd80SBram Moolenaar 	/*
162b005cd80SBram Moolenaar 	 * Succeeded in shortening the path. Now concatenate the shortened
163b005cd80SBram Moolenaar 	 * path with the remaining path at the tail.
164b005cd80SBram Moolenaar 	 */
165b005cd80SBram Moolenaar 
166217e1b83SBram Moolenaar 	// Compute the length of the new path.
167b005cd80SBram Moolenaar 	sfx_len = (int)(save_endp - endp) + 1;
168b005cd80SBram Moolenaar 	new_len = len + sfx_len;
169b005cd80SBram Moolenaar 
170b005cd80SBram Moolenaar 	*fnamelen = new_len;
171b005cd80SBram Moolenaar 	vim_free(*bufp);
172b005cd80SBram Moolenaar 	if (new_len > old_len)
173b005cd80SBram Moolenaar 	{
17426262f87SBram Moolenaar 	    // There is not enough space in the currently allocated string,
17526262f87SBram Moolenaar 	    // copy it to a buffer big enough.
176b005cd80SBram Moolenaar 	    *fname = *bufp = vim_strnsave(short_fname, new_len);
177b005cd80SBram Moolenaar 	    if (*fname == NULL)
178b005cd80SBram Moolenaar 	    {
179b005cd80SBram Moolenaar 		retval = FAIL;
180b005cd80SBram Moolenaar 		goto theend;
181b005cd80SBram Moolenaar 	    }
182b005cd80SBram Moolenaar 	}
183b005cd80SBram Moolenaar 	else
184b005cd80SBram Moolenaar 	{
18526262f87SBram Moolenaar 	    // Transfer short_fname to the main buffer (it's big enough),
18626262f87SBram Moolenaar 	    // unless get_short_pathname() did its work in-place.
187b005cd80SBram Moolenaar 	    *fname = *bufp = save_fname;
188b005cd80SBram Moolenaar 	    if (short_fname != save_fname)
189b005cd80SBram Moolenaar 		vim_strncpy(save_fname, short_fname, len);
190b005cd80SBram Moolenaar 	    save_fname = NULL;
191b005cd80SBram Moolenaar 	}
192b005cd80SBram Moolenaar 
19326262f87SBram Moolenaar 	// concat the not-shortened part of the path
194b005cd80SBram Moolenaar 	vim_strncpy(*fname + len, endp, sfx_len);
195b005cd80SBram Moolenaar 	(*fname)[new_len] = NUL;
196b005cd80SBram Moolenaar     }
197b005cd80SBram Moolenaar 
198b005cd80SBram Moolenaar theend:
199b005cd80SBram Moolenaar     vim_free(pbuf_unused);
200b005cd80SBram Moolenaar     vim_free(save_fname);
201b005cd80SBram Moolenaar 
202b005cd80SBram Moolenaar     return retval;
203b005cd80SBram Moolenaar }
204b005cd80SBram Moolenaar 
205b005cd80SBram Moolenaar /*
206b005cd80SBram Moolenaar  * Get a pathname for a partial path.
207b005cd80SBram Moolenaar  * Returns OK for success, FAIL for failure.
208b005cd80SBram Moolenaar  */
209b005cd80SBram Moolenaar     static int
shortpath_for_partial(char_u ** fnamep,char_u ** bufp,int * fnamelen)210b005cd80SBram Moolenaar shortpath_for_partial(
211b005cd80SBram Moolenaar     char_u	**fnamep,
212b005cd80SBram Moolenaar     char_u	**bufp,
213b005cd80SBram Moolenaar     int		*fnamelen)
214b005cd80SBram Moolenaar {
215b005cd80SBram Moolenaar     int		sepcount, len, tflen;
216b005cd80SBram Moolenaar     char_u	*p;
217b005cd80SBram Moolenaar     char_u	*pbuf, *tfname;
218b005cd80SBram Moolenaar     int		hasTilde;
219b005cd80SBram Moolenaar 
22026262f87SBram Moolenaar     // Count up the path separators from the RHS.. so we know which part
22126262f87SBram Moolenaar     // of the path to return.
222b005cd80SBram Moolenaar     sepcount = 0;
223b005cd80SBram Moolenaar     for (p = *fnamep; p < *fnamep + *fnamelen; MB_PTR_ADV(p))
224b005cd80SBram Moolenaar 	if (vim_ispathsep(*p))
225b005cd80SBram Moolenaar 	    ++sepcount;
226b005cd80SBram Moolenaar 
22726262f87SBram Moolenaar     // Need full path first (use expand_env() to remove a "~/")
228b005cd80SBram Moolenaar     hasTilde = (**fnamep == '~');
229b005cd80SBram Moolenaar     if (hasTilde)
230b005cd80SBram Moolenaar 	pbuf = tfname = expand_env_save(*fnamep);
231b005cd80SBram Moolenaar     else
232b005cd80SBram Moolenaar 	pbuf = tfname = FullName_save(*fnamep, FALSE);
233b005cd80SBram Moolenaar 
234b005cd80SBram Moolenaar     len = tflen = (int)STRLEN(tfname);
235b005cd80SBram Moolenaar 
236b005cd80SBram Moolenaar     if (get_short_pathname(&tfname, &pbuf, &len) == FAIL)
237b005cd80SBram Moolenaar 	return FAIL;
238b005cd80SBram Moolenaar 
239b005cd80SBram Moolenaar     if (len == 0)
240b005cd80SBram Moolenaar     {
24126262f87SBram Moolenaar 	// Don't have a valid filename, so shorten the rest of the
24226262f87SBram Moolenaar 	// path if we can. This CAN give us invalid 8.3 filenames, but
24326262f87SBram Moolenaar 	// there's not a lot of point in guessing what it might be.
244b005cd80SBram Moolenaar 	len = tflen;
245b005cd80SBram Moolenaar 	if (shortpath_for_invalid_fname(&tfname, &pbuf, &len) == FAIL)
246b005cd80SBram Moolenaar 	    return FAIL;
247b005cd80SBram Moolenaar     }
248b005cd80SBram Moolenaar 
24926262f87SBram Moolenaar     // Count the paths backward to find the beginning of the desired string.
250b005cd80SBram Moolenaar     for (p = tfname + len - 1; p >= tfname; --p)
251b005cd80SBram Moolenaar     {
252b005cd80SBram Moolenaar 	if (has_mbyte)
253b005cd80SBram Moolenaar 	    p -= mb_head_off(tfname, p);
254b005cd80SBram Moolenaar 	if (vim_ispathsep(*p))
255b005cd80SBram Moolenaar 	{
256b005cd80SBram Moolenaar 	    if (sepcount == 0 || (hasTilde && sepcount == 1))
257b005cd80SBram Moolenaar 		break;
258b005cd80SBram Moolenaar 	    else
259b005cd80SBram Moolenaar 		sepcount --;
260b005cd80SBram Moolenaar 	}
261b005cd80SBram Moolenaar     }
262b005cd80SBram Moolenaar     if (hasTilde)
263b005cd80SBram Moolenaar     {
264b005cd80SBram Moolenaar 	--p;
265b005cd80SBram Moolenaar 	if (p >= tfname)
266b005cd80SBram Moolenaar 	    *p = '~';
267b005cd80SBram Moolenaar 	else
268b005cd80SBram Moolenaar 	    return FAIL;
269b005cd80SBram Moolenaar     }
270b005cd80SBram Moolenaar     else
271b005cd80SBram Moolenaar 	++p;
272b005cd80SBram Moolenaar 
27326262f87SBram Moolenaar     // Copy in the string - p indexes into tfname - allocated at pbuf
274b005cd80SBram Moolenaar     vim_free(*bufp);
275b005cd80SBram Moolenaar     *fnamelen = (int)STRLEN(p);
276b005cd80SBram Moolenaar     *bufp = pbuf;
277b005cd80SBram Moolenaar     *fnamep = p;
278b005cd80SBram Moolenaar 
279b005cd80SBram Moolenaar     return OK;
280b005cd80SBram Moolenaar }
281b005cd80SBram Moolenaar #endif // MSWIN
282b005cd80SBram Moolenaar 
283b005cd80SBram Moolenaar /*
284b005cd80SBram Moolenaar  * Adjust a filename, according to a string of modifiers.
285b005cd80SBram Moolenaar  * *fnamep must be NUL terminated when called.  When returning, the length is
286b005cd80SBram Moolenaar  * determined by *fnamelen.
287b005cd80SBram Moolenaar  * Returns VALID_ flags or -1 for failure.
288b005cd80SBram Moolenaar  * When there is an error, *fnamep is set to NULL.
289b005cd80SBram Moolenaar  */
290b005cd80SBram Moolenaar     int
modify_fname(char_u * src,int tilde_file,int * usedlen,char_u ** fnamep,char_u ** bufp,int * fnamelen)291b005cd80SBram Moolenaar modify_fname(
292b005cd80SBram Moolenaar     char_u	*src,		// string with modifiers
293b005cd80SBram Moolenaar     int		tilde_file,	// "~" is a file name, not $HOME
294b005cd80SBram Moolenaar     int		*usedlen,	// characters after src that are used
295b005cd80SBram Moolenaar     char_u	**fnamep,	// file name so far
296b005cd80SBram Moolenaar     char_u	**bufp,		// buffer for allocated file name or NULL
297b005cd80SBram Moolenaar     int		*fnamelen)	// length of fnamep
298b005cd80SBram Moolenaar {
299b005cd80SBram Moolenaar     int		valid = 0;
300b005cd80SBram Moolenaar     char_u	*tail;
301b005cd80SBram Moolenaar     char_u	*s, *p, *pbuf;
302b005cd80SBram Moolenaar     char_u	dirname[MAXPATHL];
303b005cd80SBram Moolenaar     int		c;
304b005cd80SBram Moolenaar     int		has_fullname = 0;
305d816cd94SBram Moolenaar     int		has_homerelative = 0;
306b005cd80SBram Moolenaar #ifdef MSWIN
307b005cd80SBram Moolenaar     char_u	*fname_start = *fnamep;
308b005cd80SBram Moolenaar     int		has_shortname = 0;
309b005cd80SBram Moolenaar #endif
310b005cd80SBram Moolenaar 
311b005cd80SBram Moolenaar repeat:
31226262f87SBram Moolenaar     // ":p" - full path/file_name
313b005cd80SBram Moolenaar     if (src[*usedlen] == ':' && src[*usedlen + 1] == 'p')
314b005cd80SBram Moolenaar     {
315b005cd80SBram Moolenaar 	has_fullname = 1;
316b005cd80SBram Moolenaar 
317b005cd80SBram Moolenaar 	valid |= VALID_PATH;
318b005cd80SBram Moolenaar 	*usedlen += 2;
319b005cd80SBram Moolenaar 
32026262f87SBram Moolenaar 	// Expand "~/path" for all systems and "~user/path" for Unix and VMS
321b005cd80SBram Moolenaar 	if ((*fnamep)[0] == '~'
322b005cd80SBram Moolenaar #if !defined(UNIX) && !(defined(VMS) && defined(USER_HOME))
323b005cd80SBram Moolenaar 		&& ((*fnamep)[1] == '/'
324b005cd80SBram Moolenaar # ifdef BACKSLASH_IN_FILENAME
325b005cd80SBram Moolenaar 		    || (*fnamep)[1] == '\\'
326b005cd80SBram Moolenaar # endif
327b005cd80SBram Moolenaar 		    || (*fnamep)[1] == NUL)
328b005cd80SBram Moolenaar #endif
329b005cd80SBram Moolenaar 		&& !(tilde_file && (*fnamep)[1] == NUL)
330b005cd80SBram Moolenaar 	   )
331b005cd80SBram Moolenaar 	{
332b005cd80SBram Moolenaar 	    *fnamep = expand_env_save(*fnamep);
33326262f87SBram Moolenaar 	    vim_free(*bufp);	// free any allocated file name
334b005cd80SBram Moolenaar 	    *bufp = *fnamep;
335b005cd80SBram Moolenaar 	    if (*fnamep == NULL)
336b005cd80SBram Moolenaar 		return -1;
337b005cd80SBram Moolenaar 	}
338b005cd80SBram Moolenaar 
33926262f87SBram Moolenaar 	// When "/." or "/.." is used: force expansion to get rid of it.
340b005cd80SBram Moolenaar 	for (p = *fnamep; *p != NUL; MB_PTR_ADV(p))
341b005cd80SBram Moolenaar 	{
342b005cd80SBram Moolenaar 	    if (vim_ispathsep(*p)
343b005cd80SBram Moolenaar 		    && p[1] == '.'
344b005cd80SBram Moolenaar 		    && (p[2] == NUL
345b005cd80SBram Moolenaar 			|| vim_ispathsep(p[2])
346b005cd80SBram Moolenaar 			|| (p[2] == '.'
347b005cd80SBram Moolenaar 			    && (p[3] == NUL || vim_ispathsep(p[3])))))
348b005cd80SBram Moolenaar 		break;
349b005cd80SBram Moolenaar 	}
350b005cd80SBram Moolenaar 
35126262f87SBram Moolenaar 	// FullName_save() is slow, don't use it when not needed.
352b005cd80SBram Moolenaar 	if (*p != NUL || !vim_isAbsName(*fnamep))
353b005cd80SBram Moolenaar 	{
354b005cd80SBram Moolenaar 	    *fnamep = FullName_save(*fnamep, *p != NUL);
35526262f87SBram Moolenaar 	    vim_free(*bufp);	// free any allocated file name
356b005cd80SBram Moolenaar 	    *bufp = *fnamep;
357b005cd80SBram Moolenaar 	    if (*fnamep == NULL)
358b005cd80SBram Moolenaar 		return -1;
359b005cd80SBram Moolenaar 	}
360b005cd80SBram Moolenaar 
361b005cd80SBram Moolenaar #ifdef MSWIN
362b005cd80SBram Moolenaar # if _WIN32_WINNT >= 0x0500
363b005cd80SBram Moolenaar 	if (vim_strchr(*fnamep, '~') != NULL)
364b005cd80SBram Moolenaar 	{
365b005cd80SBram Moolenaar 	    // Expand 8.3 filename to full path.  Needed to make sure the same
366b005cd80SBram Moolenaar 	    // file does not have two different names.
367b005cd80SBram Moolenaar 	    // Note: problem does not occur if _WIN32_WINNT < 0x0500.
368b005cd80SBram Moolenaar 	    WCHAR *wfname = enc_to_utf16(*fnamep, NULL);
369b005cd80SBram Moolenaar 	    WCHAR buf[_MAX_PATH];
370b005cd80SBram Moolenaar 
371b005cd80SBram Moolenaar 	    if (wfname != NULL)
372b005cd80SBram Moolenaar 	    {
373b005cd80SBram Moolenaar 		if (GetLongPathNameW(wfname, buf, _MAX_PATH))
374b005cd80SBram Moolenaar 		{
375b005cd80SBram Moolenaar 		    char_u *p = utf16_to_enc(buf, NULL);
376b005cd80SBram Moolenaar 
377b005cd80SBram Moolenaar 		    if (p != NULL)
378b005cd80SBram Moolenaar 		    {
379b005cd80SBram Moolenaar 			vim_free(*bufp);    // free any allocated file name
380b005cd80SBram Moolenaar 			*bufp = *fnamep = p;
381b005cd80SBram Moolenaar 		    }
382b005cd80SBram Moolenaar 		}
383b005cd80SBram Moolenaar 		vim_free(wfname);
384b005cd80SBram Moolenaar 	    }
385b005cd80SBram Moolenaar 	}
386b005cd80SBram Moolenaar # endif
387b005cd80SBram Moolenaar #endif
38826262f87SBram Moolenaar 	// Append a path separator to a directory.
389b005cd80SBram Moolenaar 	if (mch_isdir(*fnamep))
390b005cd80SBram Moolenaar 	{
39126262f87SBram Moolenaar 	    // Make room for one or two extra characters.
39271ccd03eSBram Moolenaar 	    *fnamep = vim_strnsave(*fnamep, STRLEN(*fnamep) + 2);
39326262f87SBram Moolenaar 	    vim_free(*bufp);	// free any allocated file name
394b005cd80SBram Moolenaar 	    *bufp = *fnamep;
395b005cd80SBram Moolenaar 	    if (*fnamep == NULL)
396b005cd80SBram Moolenaar 		return -1;
397b005cd80SBram Moolenaar 	    add_pathsep(*fnamep);
398b005cd80SBram Moolenaar 	}
399b005cd80SBram Moolenaar     }
400b005cd80SBram Moolenaar 
40126262f87SBram Moolenaar     // ":." - path relative to the current directory
40226262f87SBram Moolenaar     // ":~" - path relative to the home directory
40326262f87SBram Moolenaar     // ":8" - shortname path - postponed till after
404b005cd80SBram Moolenaar     while (src[*usedlen] == ':'
405b005cd80SBram Moolenaar 		  && ((c = src[*usedlen + 1]) == '.' || c == '~' || c == '8'))
406b005cd80SBram Moolenaar     {
407b005cd80SBram Moolenaar 	*usedlen += 2;
408b005cd80SBram Moolenaar 	if (c == '8')
409b005cd80SBram Moolenaar 	{
410b005cd80SBram Moolenaar #ifdef MSWIN
41126262f87SBram Moolenaar 	    has_shortname = 1; // Postpone this.
412b005cd80SBram Moolenaar #endif
413b005cd80SBram Moolenaar 	    continue;
414b005cd80SBram Moolenaar 	}
415b005cd80SBram Moolenaar 	pbuf = NULL;
41626262f87SBram Moolenaar 	// Need full path first (use expand_env() to remove a "~/")
417d816cd94SBram Moolenaar 	if (!has_fullname && !has_homerelative)
418b005cd80SBram Moolenaar 	{
4190e390f40SBram Moolenaar 	    if ((c == '.' || c == '~') && **fnamep == '~')
420b005cd80SBram Moolenaar 		p = pbuf = expand_env_save(*fnamep);
421b005cd80SBram Moolenaar 	    else
422b005cd80SBram Moolenaar 		p = pbuf = FullName_save(*fnamep, FALSE);
423b005cd80SBram Moolenaar 	}
424b005cd80SBram Moolenaar 	else
425b005cd80SBram Moolenaar 	    p = *fnamep;
426b005cd80SBram Moolenaar 
427b005cd80SBram Moolenaar 	has_fullname = 0;
428b005cd80SBram Moolenaar 
429b005cd80SBram Moolenaar 	if (p != NULL)
430b005cd80SBram Moolenaar 	{
431b005cd80SBram Moolenaar 	    if (c == '.')
432b005cd80SBram Moolenaar 	    {
433d816cd94SBram Moolenaar 		size_t	namelen;
434d816cd94SBram Moolenaar 
435b005cd80SBram Moolenaar 		mch_dirname(dirname, MAXPATHL);
436d816cd94SBram Moolenaar 		if (has_homerelative)
437d816cd94SBram Moolenaar 		{
438d816cd94SBram Moolenaar 		    s = vim_strsave(dirname);
439b005cd80SBram Moolenaar 		    if (s != NULL)
440b005cd80SBram Moolenaar 		    {
441d816cd94SBram Moolenaar 			home_replace(NULL, s, dirname, MAXPATHL, TRUE);
442d816cd94SBram Moolenaar 			vim_free(s);
443d816cd94SBram Moolenaar 		    }
444d816cd94SBram Moolenaar 		}
445d816cd94SBram Moolenaar 		namelen = STRLEN(dirname);
446d816cd94SBram Moolenaar 
447d816cd94SBram Moolenaar 		// Do not call shorten_fname() here since it removes the prefix
448d816cd94SBram Moolenaar 		// even though the path does not have a prefix.
449d816cd94SBram Moolenaar 		if (fnamencmp(p, dirname, namelen) == 0)
450d816cd94SBram Moolenaar 		{
451d816cd94SBram Moolenaar 		    p += namelen;
452a78e9c61SBram Moolenaar 		    if (vim_ispathsep(*p))
453a78e9c61SBram Moolenaar 		    {
454d816cd94SBram Moolenaar 			while (*p && vim_ispathsep(*p))
455d816cd94SBram Moolenaar 			    ++p;
456d816cd94SBram Moolenaar 			*fnamep = p;
457b005cd80SBram Moolenaar 			if (pbuf != NULL)
458b005cd80SBram Moolenaar 			{
459a78e9c61SBram Moolenaar 			    // free any allocated file name
460a78e9c61SBram Moolenaar 			    vim_free(*bufp);
461b005cd80SBram Moolenaar 			    *bufp = pbuf;
462b005cd80SBram Moolenaar 			    pbuf = NULL;
463b005cd80SBram Moolenaar 			}
464b005cd80SBram Moolenaar 		    }
465b005cd80SBram Moolenaar 		}
466a78e9c61SBram Moolenaar 	    }
467b005cd80SBram Moolenaar 	    else
468b005cd80SBram Moolenaar 	    {
469b005cd80SBram Moolenaar 		home_replace(NULL, p, dirname, MAXPATHL, TRUE);
47026262f87SBram Moolenaar 		// Only replace it when it starts with '~'
471b005cd80SBram Moolenaar 		if (*dirname == '~')
472b005cd80SBram Moolenaar 		{
473b005cd80SBram Moolenaar 		    s = vim_strsave(dirname);
474b005cd80SBram Moolenaar 		    if (s != NULL)
475b005cd80SBram Moolenaar 		    {
476b005cd80SBram Moolenaar 			*fnamep = s;
477b005cd80SBram Moolenaar 			vim_free(*bufp);
478b005cd80SBram Moolenaar 			*bufp = s;
479d816cd94SBram Moolenaar 			has_homerelative = TRUE;
480b005cd80SBram Moolenaar 		    }
481b005cd80SBram Moolenaar 		}
482b005cd80SBram Moolenaar 	    }
483b005cd80SBram Moolenaar 	    vim_free(pbuf);
484b005cd80SBram Moolenaar 	}
485b005cd80SBram Moolenaar     }
486b005cd80SBram Moolenaar 
487b005cd80SBram Moolenaar     tail = gettail(*fnamep);
488b005cd80SBram Moolenaar     *fnamelen = (int)STRLEN(*fnamep);
489b005cd80SBram Moolenaar 
49026262f87SBram Moolenaar     // ":h" - head, remove "/file_name", can be repeated
49126262f87SBram Moolenaar     // Don't remove the first "/" or "c:\"
492b005cd80SBram Moolenaar     while (src[*usedlen] == ':' && src[*usedlen + 1] == 'h')
493b005cd80SBram Moolenaar     {
494b005cd80SBram Moolenaar 	valid |= VALID_HEAD;
495b005cd80SBram Moolenaar 	*usedlen += 2;
496b005cd80SBram Moolenaar 	s = get_past_head(*fnamep);
497b005cd80SBram Moolenaar 	while (tail > s && after_pathsep(s, tail))
498b005cd80SBram Moolenaar 	    MB_PTR_BACK(*fnamep, tail);
499b005cd80SBram Moolenaar 	*fnamelen = (int)(tail - *fnamep);
500b005cd80SBram Moolenaar #ifdef VMS
501b005cd80SBram Moolenaar 	if (*fnamelen > 0)
50226262f87SBram Moolenaar 	    *fnamelen += 1; // the path separator is part of the path
503b005cd80SBram Moolenaar #endif
504b005cd80SBram Moolenaar 	if (*fnamelen == 0)
505b005cd80SBram Moolenaar 	{
50626262f87SBram Moolenaar 	    // Result is empty.  Turn it into "." to make ":cd %:h" work.
507b005cd80SBram Moolenaar 	    p = vim_strsave((char_u *)".");
508b005cd80SBram Moolenaar 	    if (p == NULL)
509b005cd80SBram Moolenaar 		return -1;
510b005cd80SBram Moolenaar 	    vim_free(*bufp);
511b005cd80SBram Moolenaar 	    *bufp = *fnamep = tail = p;
512b005cd80SBram Moolenaar 	    *fnamelen = 1;
513b005cd80SBram Moolenaar 	}
514b005cd80SBram Moolenaar 	else
515b005cd80SBram Moolenaar 	{
516b005cd80SBram Moolenaar 	    while (tail > s && !after_pathsep(s, tail))
517b005cd80SBram Moolenaar 		MB_PTR_BACK(*fnamep, tail);
518b005cd80SBram Moolenaar 	}
519b005cd80SBram Moolenaar     }
520b005cd80SBram Moolenaar 
52126262f87SBram Moolenaar     // ":8" - shortname
522b005cd80SBram Moolenaar     if (src[*usedlen] == ':' && src[*usedlen + 1] == '8')
523b005cd80SBram Moolenaar     {
524b005cd80SBram Moolenaar 	*usedlen += 2;
525b005cd80SBram Moolenaar #ifdef MSWIN
526b005cd80SBram Moolenaar 	has_shortname = 1;
527b005cd80SBram Moolenaar #endif
528b005cd80SBram Moolenaar     }
529b005cd80SBram Moolenaar 
530b005cd80SBram Moolenaar #ifdef MSWIN
531b005cd80SBram Moolenaar     /*
532b005cd80SBram Moolenaar      * Handle ":8" after we have done 'heads' and before we do 'tails'.
533b005cd80SBram Moolenaar      */
534b005cd80SBram Moolenaar     if (has_shortname)
535b005cd80SBram Moolenaar     {
53626262f87SBram Moolenaar 	// Copy the string if it is shortened by :h and when it wasn't copied
53726262f87SBram Moolenaar 	// yet, because we are going to change it in place.  Avoids changing
53826262f87SBram Moolenaar 	// the buffer name for "%:8".
539b005cd80SBram Moolenaar 	if (*fnamelen < (int)STRLEN(*fnamep) || *fnamep == fname_start)
540b005cd80SBram Moolenaar 	{
541b005cd80SBram Moolenaar 	    p = vim_strnsave(*fnamep, *fnamelen);
542b005cd80SBram Moolenaar 	    if (p == NULL)
543b005cd80SBram Moolenaar 		return -1;
544b005cd80SBram Moolenaar 	    vim_free(*bufp);
545b005cd80SBram Moolenaar 	    *bufp = *fnamep = p;
546b005cd80SBram Moolenaar 	}
547b005cd80SBram Moolenaar 
54826262f87SBram Moolenaar 	// Split into two implementations - makes it easier.  First is where
54926262f87SBram Moolenaar 	// there isn't a full name already, second is where there is.
550b005cd80SBram Moolenaar 	if (!has_fullname && !vim_isAbsName(*fnamep))
551b005cd80SBram Moolenaar 	{
552b005cd80SBram Moolenaar 	    if (shortpath_for_partial(fnamep, bufp, fnamelen) == FAIL)
553b005cd80SBram Moolenaar 		return -1;
554b005cd80SBram Moolenaar 	}
555b005cd80SBram Moolenaar 	else
556b005cd80SBram Moolenaar 	{
557b005cd80SBram Moolenaar 	    int		l = *fnamelen;
558b005cd80SBram Moolenaar 
55926262f87SBram Moolenaar 	    // Simple case, already have the full-name.
56026262f87SBram Moolenaar 	    // Nearly always shorter, so try first time.
561b005cd80SBram Moolenaar 	    if (get_short_pathname(fnamep, bufp, &l) == FAIL)
562b005cd80SBram Moolenaar 		return -1;
563b005cd80SBram Moolenaar 
564b005cd80SBram Moolenaar 	    if (l == 0)
565b005cd80SBram Moolenaar 	    {
56626262f87SBram Moolenaar 		// Couldn't find the filename, search the paths.
567b005cd80SBram Moolenaar 		l = *fnamelen;
568b005cd80SBram Moolenaar 		if (shortpath_for_invalid_fname(fnamep, bufp, &l) == FAIL)
569b005cd80SBram Moolenaar 		    return -1;
570b005cd80SBram Moolenaar 	    }
571b005cd80SBram Moolenaar 	    *fnamelen = l;
572b005cd80SBram Moolenaar 	}
573b005cd80SBram Moolenaar     }
574b005cd80SBram Moolenaar #endif // MSWIN
575b005cd80SBram Moolenaar 
57626262f87SBram Moolenaar     // ":t" - tail, just the basename
577b005cd80SBram Moolenaar     if (src[*usedlen] == ':' && src[*usedlen + 1] == 't')
578b005cd80SBram Moolenaar     {
579b005cd80SBram Moolenaar 	*usedlen += 2;
580b005cd80SBram Moolenaar 	*fnamelen -= (int)(tail - *fnamep);
581b005cd80SBram Moolenaar 	*fnamep = tail;
582b005cd80SBram Moolenaar     }
583b005cd80SBram Moolenaar 
58426262f87SBram Moolenaar     // ":e" - extension, can be repeated
58526262f87SBram Moolenaar     // ":r" - root, without extension, can be repeated
586b005cd80SBram Moolenaar     while (src[*usedlen] == ':'
587b005cd80SBram Moolenaar 	    && (src[*usedlen + 1] == 'e' || src[*usedlen + 1] == 'r'))
588b005cd80SBram Moolenaar     {
58926262f87SBram Moolenaar 	// find a '.' in the tail:
59026262f87SBram Moolenaar 	// - for second :e: before the current fname
59126262f87SBram Moolenaar 	// - otherwise: The last '.'
592b005cd80SBram Moolenaar 	if (src[*usedlen + 1] == 'e' && *fnamep > tail)
593b005cd80SBram Moolenaar 	    s = *fnamep - 2;
594b005cd80SBram Moolenaar 	else
595b005cd80SBram Moolenaar 	    s = *fnamep + *fnamelen - 1;
596b005cd80SBram Moolenaar 	for ( ; s > tail; --s)
597b005cd80SBram Moolenaar 	    if (s[0] == '.')
598b005cd80SBram Moolenaar 		break;
59926262f87SBram Moolenaar 	if (src[*usedlen + 1] == 'e')		// :e
600b005cd80SBram Moolenaar 	{
601b005cd80SBram Moolenaar 	    if (s > tail)
602b005cd80SBram Moolenaar 	    {
603b005cd80SBram Moolenaar 		*fnamelen += (int)(*fnamep - (s + 1));
604b005cd80SBram Moolenaar 		*fnamep = s + 1;
605b005cd80SBram Moolenaar #ifdef VMS
60626262f87SBram Moolenaar 		// cut version from the extension
607b005cd80SBram Moolenaar 		s = *fnamep + *fnamelen - 1;
608b005cd80SBram Moolenaar 		for ( ; s > *fnamep; --s)
609b005cd80SBram Moolenaar 		    if (s[0] == ';')
610b005cd80SBram Moolenaar 			break;
611b005cd80SBram Moolenaar 		if (s > *fnamep)
612b005cd80SBram Moolenaar 		    *fnamelen = s - *fnamep;
613b005cd80SBram Moolenaar #endif
614b005cd80SBram Moolenaar 	    }
615b005cd80SBram Moolenaar 	    else if (*fnamep <= tail)
616b005cd80SBram Moolenaar 		*fnamelen = 0;
617b005cd80SBram Moolenaar 	}
61826262f87SBram Moolenaar 	else				// :r
619b005cd80SBram Moolenaar 	{
620b189295bSBram Moolenaar 	    char_u *limit = *fnamep;
621b189295bSBram Moolenaar 
622b189295bSBram Moolenaar 	    if (limit < tail)
623b189295bSBram Moolenaar 		limit = tail;
624b189295bSBram Moolenaar 	    if (s > limit)	// remove one extension
625b005cd80SBram Moolenaar 		*fnamelen = (int)(s - *fnamep);
626b005cd80SBram Moolenaar 	}
627b005cd80SBram Moolenaar 	*usedlen += 2;
628b005cd80SBram Moolenaar     }
629b005cd80SBram Moolenaar 
63026262f87SBram Moolenaar     // ":s?pat?foo?" - substitute
63126262f87SBram Moolenaar     // ":gs?pat?foo?" - global substitute
632b005cd80SBram Moolenaar     if (src[*usedlen] == ':'
633b005cd80SBram Moolenaar 	    && (src[*usedlen + 1] == 's'
634b005cd80SBram Moolenaar 		|| (src[*usedlen + 1] == 'g' && src[*usedlen + 2] == 's')))
635b005cd80SBram Moolenaar     {
636b005cd80SBram Moolenaar 	char_u	    *str;
637b005cd80SBram Moolenaar 	char_u	    *pat;
638b005cd80SBram Moolenaar 	char_u	    *sub;
639b005cd80SBram Moolenaar 	int	    sep;
640b005cd80SBram Moolenaar 	char_u	    *flags;
641b005cd80SBram Moolenaar 	int	    didit = FALSE;
642b005cd80SBram Moolenaar 
643b005cd80SBram Moolenaar 	flags = (char_u *)"";
644b005cd80SBram Moolenaar 	s = src + *usedlen + 2;
645b005cd80SBram Moolenaar 	if (src[*usedlen + 1] == 'g')
646b005cd80SBram Moolenaar 	{
647b005cd80SBram Moolenaar 	    flags = (char_u *)"g";
648b005cd80SBram Moolenaar 	    ++s;
649b005cd80SBram Moolenaar 	}
650b005cd80SBram Moolenaar 
651b005cd80SBram Moolenaar 	sep = *s++;
652b005cd80SBram Moolenaar 	if (sep)
653b005cd80SBram Moolenaar 	{
65426262f87SBram Moolenaar 	    // find end of pattern
655b005cd80SBram Moolenaar 	    p = vim_strchr(s, sep);
656b005cd80SBram Moolenaar 	    if (p != NULL)
657b005cd80SBram Moolenaar 	    {
65871ccd03eSBram Moolenaar 		pat = vim_strnsave(s, p - s);
659b005cd80SBram Moolenaar 		if (pat != NULL)
660b005cd80SBram Moolenaar 		{
661b005cd80SBram Moolenaar 		    s = p + 1;
66226262f87SBram Moolenaar 		    // find end of substitution
663b005cd80SBram Moolenaar 		    p = vim_strchr(s, sep);
664b005cd80SBram Moolenaar 		    if (p != NULL)
665b005cd80SBram Moolenaar 		    {
66671ccd03eSBram Moolenaar 			sub = vim_strnsave(s, p - s);
667b005cd80SBram Moolenaar 			str = vim_strnsave(*fnamep, *fnamelen);
668b005cd80SBram Moolenaar 			if (sub != NULL && str != NULL)
669b005cd80SBram Moolenaar 			{
670b005cd80SBram Moolenaar 			    *usedlen = (int)(p + 1 - src);
671b005cd80SBram Moolenaar 			    s = do_string_sub(str, pat, sub, NULL, flags);
672b005cd80SBram Moolenaar 			    if (s != NULL)
673b005cd80SBram Moolenaar 			    {
674b005cd80SBram Moolenaar 				*fnamep = s;
675b005cd80SBram Moolenaar 				*fnamelen = (int)STRLEN(s);
676b005cd80SBram Moolenaar 				vim_free(*bufp);
677b005cd80SBram Moolenaar 				*bufp = s;
678b005cd80SBram Moolenaar 				didit = TRUE;
679b005cd80SBram Moolenaar 			    }
680b005cd80SBram Moolenaar 			}
681b005cd80SBram Moolenaar 			vim_free(sub);
682b005cd80SBram Moolenaar 			vim_free(str);
683b005cd80SBram Moolenaar 		    }
684b005cd80SBram Moolenaar 		    vim_free(pat);
685b005cd80SBram Moolenaar 		}
686b005cd80SBram Moolenaar 	    }
68726262f87SBram Moolenaar 	    // after using ":s", repeat all the modifiers
688b005cd80SBram Moolenaar 	    if (didit)
689b005cd80SBram Moolenaar 		goto repeat;
690b005cd80SBram Moolenaar 	}
691b005cd80SBram Moolenaar     }
692b005cd80SBram Moolenaar 
693b005cd80SBram Moolenaar     if (src[*usedlen] == ':' && src[*usedlen + 1] == 'S')
694b005cd80SBram Moolenaar     {
69526262f87SBram Moolenaar 	// vim_strsave_shellescape() needs a NUL terminated string.
696b005cd80SBram Moolenaar 	c = (*fnamep)[*fnamelen];
697b005cd80SBram Moolenaar 	if (c != NUL)
698b005cd80SBram Moolenaar 	    (*fnamep)[*fnamelen] = NUL;
699b005cd80SBram Moolenaar 	p = vim_strsave_shellescape(*fnamep, FALSE, FALSE);
700b005cd80SBram Moolenaar 	if (c != NUL)
701b005cd80SBram Moolenaar 	    (*fnamep)[*fnamelen] = c;
702b005cd80SBram Moolenaar 	if (p == NULL)
703b005cd80SBram Moolenaar 	    return -1;
704b005cd80SBram Moolenaar 	vim_free(*bufp);
705b005cd80SBram Moolenaar 	*bufp = *fnamep = p;
706b005cd80SBram Moolenaar 	*fnamelen = (int)STRLEN(p);
707b005cd80SBram Moolenaar 	*usedlen += 2;
708b005cd80SBram Moolenaar     }
709b005cd80SBram Moolenaar 
710b005cd80SBram Moolenaar     return valid;
711b005cd80SBram Moolenaar }
712b005cd80SBram Moolenaar 
713273af497SBram Moolenaar /*
714273af497SBram Moolenaar  * Shorten the path of a file from "~/foo/../.bar/fname" to "~/f/../.b/fname"
715273af497SBram Moolenaar  * "trim_len" specifies how many characters to keep for each directory.
716273af497SBram Moolenaar  * Must be 1 or more.
717273af497SBram Moolenaar  * It's done in-place.
718273af497SBram Moolenaar  */
719273af497SBram Moolenaar     static void
shorten_dir_len(char_u * str,int trim_len)720273af497SBram Moolenaar shorten_dir_len(char_u *str, int trim_len)
721273af497SBram Moolenaar {
722273af497SBram Moolenaar     char_u	*tail, *s, *d;
723273af497SBram Moolenaar     int		skip = FALSE;
724273af497SBram Moolenaar     int		dirchunk_len = 0;
725273af497SBram Moolenaar 
726273af497SBram Moolenaar     tail = gettail(str);
727273af497SBram Moolenaar     d = str;
728273af497SBram Moolenaar     for (s = str; ; ++s)
729273af497SBram Moolenaar     {
730273af497SBram Moolenaar 	if (s >= tail)		    // copy the whole tail
731273af497SBram Moolenaar 	{
732273af497SBram Moolenaar 	    *d++ = *s;
733273af497SBram Moolenaar 	    if (*s == NUL)
734273af497SBram Moolenaar 		break;
735273af497SBram Moolenaar 	}
736273af497SBram Moolenaar 	else if (vim_ispathsep(*s))	    // copy '/' and next char
737273af497SBram Moolenaar 	{
738273af497SBram Moolenaar 	    *d++ = *s;
739273af497SBram Moolenaar 	    skip = FALSE;
740273af497SBram Moolenaar 	    dirchunk_len = 0;
741273af497SBram Moolenaar 	}
742273af497SBram Moolenaar 	else if (!skip)
743273af497SBram Moolenaar 	{
744273af497SBram Moolenaar 	    *d++ = *s;			// copy next char
745273af497SBram Moolenaar 	    if (*s != '~' && *s != '.') // and leading "~" and "."
746273af497SBram Moolenaar 	    {
747273af497SBram Moolenaar 		++dirchunk_len; // only count word chars for the size
748273af497SBram Moolenaar 
749273af497SBram Moolenaar 		// keep copying chars until we have our preferred length (or
750273af497SBram Moolenaar 		// until the above if/else branches move us along)
751273af497SBram Moolenaar 		if (dirchunk_len >= trim_len)
752273af497SBram Moolenaar 		    skip = TRUE;
753273af497SBram Moolenaar 	    }
754273af497SBram Moolenaar 
755273af497SBram Moolenaar 	    if (has_mbyte)
756273af497SBram Moolenaar 	    {
757273af497SBram Moolenaar 		int l = mb_ptr2len(s);
758273af497SBram Moolenaar 
759273af497SBram Moolenaar 		while (--l > 0)
760273af497SBram Moolenaar 		    *d++ = *++s;
761273af497SBram Moolenaar 	    }
762273af497SBram Moolenaar 	}
763273af497SBram Moolenaar     }
764273af497SBram Moolenaar }
765273af497SBram Moolenaar 
766273af497SBram Moolenaar /*
767273af497SBram Moolenaar  * Shorten the path of a file from "~/foo/../.bar/fname" to "~/f/../.b/fname"
768273af497SBram Moolenaar  * It's done in-place.
769273af497SBram Moolenaar  */
770273af497SBram Moolenaar     void
shorten_dir(char_u * str)771273af497SBram Moolenaar shorten_dir(char_u *str)
772273af497SBram Moolenaar {
773273af497SBram Moolenaar     shorten_dir_len(str, 1);
774273af497SBram Moolenaar }
775273af497SBram Moolenaar 
776b005cd80SBram Moolenaar #if defined(FEAT_EVAL) || defined(PROTO)
777b005cd80SBram Moolenaar 
778b005cd80SBram Moolenaar /*
779b005cd80SBram Moolenaar  * "chdir(dir)" function
780b005cd80SBram Moolenaar  */
781b005cd80SBram Moolenaar     void
f_chdir(typval_T * argvars,typval_T * rettv)782b005cd80SBram Moolenaar f_chdir(typval_T *argvars, typval_T *rettv)
783b005cd80SBram Moolenaar {
784b005cd80SBram Moolenaar     char_u	*cwd;
785b005cd80SBram Moolenaar     cdscope_T	scope = CDSCOPE_GLOBAL;
786b005cd80SBram Moolenaar 
787b005cd80SBram Moolenaar     rettv->v_type = VAR_STRING;
788b005cd80SBram Moolenaar     rettv->vval.v_string = NULL;
789b005cd80SBram Moolenaar 
790b005cd80SBram Moolenaar     if (argvars[0].v_type != VAR_STRING)
791c5809439SBram Moolenaar     {
792d816cd94SBram Moolenaar 	// Returning an empty string means it failed.
793002bc799SBram Moolenaar 	// No error message, for historic reasons.
794c5809439SBram Moolenaar 	if (in_vim9script())
795c5809439SBram Moolenaar 	    (void) check_for_string_arg(argvars, 0);
796b005cd80SBram Moolenaar 	return;
797c5809439SBram Moolenaar     }
798b005cd80SBram Moolenaar 
799b005cd80SBram Moolenaar     // Return the current directory
800b005cd80SBram Moolenaar     cwd = alloc(MAXPATHL);
801b005cd80SBram Moolenaar     if (cwd != NULL)
802b005cd80SBram Moolenaar     {
803b005cd80SBram Moolenaar 	if (mch_dirname(cwd, MAXPATHL) != FAIL)
804b005cd80SBram Moolenaar 	{
805b005cd80SBram Moolenaar #ifdef BACKSLASH_IN_FILENAME
806b005cd80SBram Moolenaar 	    slash_adjust(cwd);
807b005cd80SBram Moolenaar #endif
808b005cd80SBram Moolenaar 	    rettv->vval.v_string = vim_strsave(cwd);
809b005cd80SBram Moolenaar 	}
810b005cd80SBram Moolenaar 	vim_free(cwd);
811b005cd80SBram Moolenaar     }
812b005cd80SBram Moolenaar 
813b005cd80SBram Moolenaar     if (curwin->w_localdir != NULL)
814b005cd80SBram Moolenaar 	scope = CDSCOPE_WINDOW;
815b005cd80SBram Moolenaar     else if (curtab->tp_localdir != NULL)
816b005cd80SBram Moolenaar 	scope = CDSCOPE_TABPAGE;
817b005cd80SBram Moolenaar 
818b005cd80SBram Moolenaar     if (!changedir_func(argvars[0].vval.v_string, TRUE, scope))
819b005cd80SBram Moolenaar 	// Directory change failed
820b005cd80SBram Moolenaar 	VIM_CLEAR(rettv->vval.v_string);
821b005cd80SBram Moolenaar }
822b005cd80SBram Moolenaar 
823b005cd80SBram Moolenaar /*
824b005cd80SBram Moolenaar  * "delete()" function
825b005cd80SBram Moolenaar  */
826b005cd80SBram Moolenaar     void
f_delete(typval_T * argvars,typval_T * rettv)827b005cd80SBram Moolenaar f_delete(typval_T *argvars, typval_T *rettv)
828b005cd80SBram Moolenaar {
829b005cd80SBram Moolenaar     char_u	nbuf[NUMBUFLEN];
830b005cd80SBram Moolenaar     char_u	*name;
831b005cd80SBram Moolenaar     char_u	*flags;
832b005cd80SBram Moolenaar 
833b005cd80SBram Moolenaar     rettv->vval.v_number = -1;
834b005cd80SBram Moolenaar     if (check_restricted() || check_secure())
835b005cd80SBram Moolenaar 	return;
836b005cd80SBram Moolenaar 
8374490ec4eSYegappan Lakshmanan     if (in_vim9script()
8384490ec4eSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
8394490ec4eSYegappan Lakshmanan 		|| check_for_opt_string_arg(argvars, 1) == FAIL))
8404490ec4eSYegappan Lakshmanan 	return;
8414490ec4eSYegappan Lakshmanan 
842b005cd80SBram Moolenaar     name = tv_get_string(&argvars[0]);
843b005cd80SBram Moolenaar     if (name == NULL || *name == NUL)
844b005cd80SBram Moolenaar     {
845b005cd80SBram Moolenaar 	emsg(_(e_invarg));
846b005cd80SBram Moolenaar 	return;
847b005cd80SBram Moolenaar     }
848b005cd80SBram Moolenaar 
849b005cd80SBram Moolenaar     if (argvars[1].v_type != VAR_UNKNOWN)
850b005cd80SBram Moolenaar 	flags = tv_get_string_buf(&argvars[1], nbuf);
851b005cd80SBram Moolenaar     else
852b005cd80SBram Moolenaar 	flags = (char_u *)"";
853b005cd80SBram Moolenaar 
854b005cd80SBram Moolenaar     if (*flags == NUL)
85526262f87SBram Moolenaar 	// delete a file
856b005cd80SBram Moolenaar 	rettv->vval.v_number = mch_remove(name) == 0 ? 0 : -1;
857b005cd80SBram Moolenaar     else if (STRCMP(flags, "d") == 0)
85826262f87SBram Moolenaar 	// delete an empty directory
859b005cd80SBram Moolenaar 	rettv->vval.v_number = mch_rmdir(name) == 0 ? 0 : -1;
860b005cd80SBram Moolenaar     else if (STRCMP(flags, "rf") == 0)
86126262f87SBram Moolenaar 	// delete a directory recursively
862b005cd80SBram Moolenaar 	rettv->vval.v_number = delete_recursive(name);
863b005cd80SBram Moolenaar     else
864108010aaSBram Moolenaar 	semsg(_(e_invalid_expression_str), flags);
865b005cd80SBram Moolenaar }
866b005cd80SBram Moolenaar 
867b005cd80SBram Moolenaar /*
868b005cd80SBram Moolenaar  * "executable()" function
869b005cd80SBram Moolenaar  */
870b005cd80SBram Moolenaar     void
f_executable(typval_T * argvars,typval_T * rettv)871b005cd80SBram Moolenaar f_executable(typval_T *argvars, typval_T *rettv)
872b005cd80SBram Moolenaar {
87332105ae8SBram Moolenaar     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
8747bb4e74cSBram Moolenaar 	return;
875b005cd80SBram Moolenaar 
87626262f87SBram Moolenaar     // Check in $PATH and also check directly if there is a directory name.
8777bb4e74cSBram Moolenaar     rettv->vval.v_number = mch_can_exe(tv_get_string(&argvars[0]), NULL, TRUE);
878b005cd80SBram Moolenaar }
879b005cd80SBram Moolenaar 
880b005cd80SBram Moolenaar /*
881b005cd80SBram Moolenaar  * "exepath()" function
882b005cd80SBram Moolenaar  */
883b005cd80SBram Moolenaar     void
f_exepath(typval_T * argvars,typval_T * rettv)884b005cd80SBram Moolenaar f_exepath(typval_T *argvars, typval_T *rettv)
885b005cd80SBram Moolenaar {
886b005cd80SBram Moolenaar     char_u *p = NULL;
887b005cd80SBram Moolenaar 
88832105ae8SBram Moolenaar     if (in_vim9script() && check_for_nonempty_string_arg(argvars, 0) == FAIL)
8897bb4e74cSBram Moolenaar 	return;
890b005cd80SBram Moolenaar     (void)mch_can_exe(tv_get_string(&argvars[0]), &p, TRUE);
891b005cd80SBram Moolenaar     rettv->v_type = VAR_STRING;
892b005cd80SBram Moolenaar     rettv->vval.v_string = p;
893b005cd80SBram Moolenaar }
894b005cd80SBram Moolenaar 
895b005cd80SBram Moolenaar /*
896b005cd80SBram Moolenaar  * "filereadable()" function
897b005cd80SBram Moolenaar  */
898b005cd80SBram Moolenaar     void
f_filereadable(typval_T * argvars,typval_T * rettv)899b005cd80SBram Moolenaar f_filereadable(typval_T *argvars, typval_T *rettv)
900b005cd80SBram Moolenaar {
901b005cd80SBram Moolenaar     int		fd;
902b005cd80SBram Moolenaar     char_u	*p;
903b005cd80SBram Moolenaar     int		n;
904b005cd80SBram Moolenaar 
90532105ae8SBram Moolenaar     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
9067bb4e74cSBram Moolenaar 	return;
9074490ec4eSYegappan Lakshmanan 
908b005cd80SBram Moolenaar #ifndef O_NONBLOCK
909b005cd80SBram Moolenaar # define O_NONBLOCK 0
910b005cd80SBram Moolenaar #endif
911b005cd80SBram Moolenaar     p = tv_get_string(&argvars[0]);
912b005cd80SBram Moolenaar     if (*p && !mch_isdir(p) && (fd = mch_open((char *)p,
913b005cd80SBram Moolenaar 					      O_RDONLY | O_NONBLOCK, 0)) >= 0)
914b005cd80SBram Moolenaar     {
915b005cd80SBram Moolenaar 	n = TRUE;
916b005cd80SBram Moolenaar 	close(fd);
917b005cd80SBram Moolenaar     }
918b005cd80SBram Moolenaar     else
919b005cd80SBram Moolenaar 	n = FALSE;
920b005cd80SBram Moolenaar 
921b005cd80SBram Moolenaar     rettv->vval.v_number = n;
922b005cd80SBram Moolenaar }
923b005cd80SBram Moolenaar 
924b005cd80SBram Moolenaar /*
925b005cd80SBram Moolenaar  * Return 0 for not writable, 1 for writable file, 2 for a dir which we have
926b005cd80SBram Moolenaar  * rights to write into.
927b005cd80SBram Moolenaar  */
928b005cd80SBram Moolenaar     void
f_filewritable(typval_T * argvars,typval_T * rettv)929b005cd80SBram Moolenaar f_filewritable(typval_T *argvars, typval_T *rettv)
930b005cd80SBram Moolenaar {
93132105ae8SBram Moolenaar     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
9327bb4e74cSBram Moolenaar 	return;
933b005cd80SBram Moolenaar     rettv->vval.v_number = filewritable(tv_get_string(&argvars[0]));
934b005cd80SBram Moolenaar }
935b005cd80SBram Moolenaar 
936840d16fdSBram Moolenaar     static void
findfilendir(typval_T * argvars UNUSED,typval_T * rettv,int find_what UNUSED)937b005cd80SBram Moolenaar findfilendir(
938b005cd80SBram Moolenaar     typval_T	*argvars UNUSED,
939b005cd80SBram Moolenaar     typval_T	*rettv,
940b005cd80SBram Moolenaar     int		find_what UNUSED)
941b005cd80SBram Moolenaar {
942b005cd80SBram Moolenaar #ifdef FEAT_SEARCHPATH
943b005cd80SBram Moolenaar     char_u	*fname;
944b005cd80SBram Moolenaar     char_u	*fresult = NULL;
945b005cd80SBram Moolenaar     char_u	*path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path;
946b005cd80SBram Moolenaar     char_u	*p;
947b005cd80SBram Moolenaar     char_u	pathbuf[NUMBUFLEN];
948b005cd80SBram Moolenaar     int		count = 1;
949b005cd80SBram Moolenaar     int		first = TRUE;
950b005cd80SBram Moolenaar     int		error = FALSE;
951b005cd80SBram Moolenaar #endif
952b005cd80SBram Moolenaar 
953b005cd80SBram Moolenaar     rettv->vval.v_string = NULL;
954b005cd80SBram Moolenaar     rettv->v_type = VAR_STRING;
9554490ec4eSYegappan Lakshmanan     if (in_vim9script()
9564490ec4eSYegappan Lakshmanan 	    && (check_for_nonempty_string_arg(argvars, 0) == FAIL
9574490ec4eSYegappan Lakshmanan 		|| check_for_opt_string_arg(argvars, 1) == FAIL
9584490ec4eSYegappan Lakshmanan 		|| (argvars[1].v_type != VAR_UNKNOWN
9594490ec4eSYegappan Lakshmanan 		    && check_for_opt_number_arg(argvars, 2) == FAIL)))
9607bb4e74cSBram Moolenaar 	return;
961b005cd80SBram Moolenaar 
962b005cd80SBram Moolenaar #ifdef FEAT_SEARCHPATH
963b005cd80SBram Moolenaar     fname = tv_get_string(&argvars[0]);
964b005cd80SBram Moolenaar 
965b005cd80SBram Moolenaar     if (argvars[1].v_type != VAR_UNKNOWN)
966b005cd80SBram Moolenaar     {
967b005cd80SBram Moolenaar 	p = tv_get_string_buf_chk(&argvars[1], pathbuf);
968b005cd80SBram Moolenaar 	if (p == NULL)
969b005cd80SBram Moolenaar 	    error = TRUE;
970b005cd80SBram Moolenaar 	else
971b005cd80SBram Moolenaar 	{
972b005cd80SBram Moolenaar 	    if (*p != NUL)
973b005cd80SBram Moolenaar 		path = p;
974b005cd80SBram Moolenaar 
975b005cd80SBram Moolenaar 	    if (argvars[2].v_type != VAR_UNKNOWN)
976b005cd80SBram Moolenaar 		count = (int)tv_get_number_chk(&argvars[2], &error);
977b005cd80SBram Moolenaar 	}
978b005cd80SBram Moolenaar     }
979b005cd80SBram Moolenaar 
980b005cd80SBram Moolenaar     if (count < 0 && rettv_list_alloc(rettv) == FAIL)
981b005cd80SBram Moolenaar 	error = TRUE;
982b005cd80SBram Moolenaar 
983b005cd80SBram Moolenaar     if (*fname != NUL && !error)
984b005cd80SBram Moolenaar     {
985b005cd80SBram Moolenaar 	do
986b005cd80SBram Moolenaar 	{
987b005cd80SBram Moolenaar 	    if (rettv->v_type == VAR_STRING || rettv->v_type == VAR_LIST)
988b005cd80SBram Moolenaar 		vim_free(fresult);
989b005cd80SBram Moolenaar 	    fresult = find_file_in_path_option(first ? fname : NULL,
990b005cd80SBram Moolenaar 					       first ? (int)STRLEN(fname) : 0,
991b005cd80SBram Moolenaar 					0, first, path,
992b005cd80SBram Moolenaar 					find_what,
993b005cd80SBram Moolenaar 					curbuf->b_ffname,
994b005cd80SBram Moolenaar 					find_what == FINDFILE_DIR
995b005cd80SBram Moolenaar 					    ? (char_u *)"" : curbuf->b_p_sua);
996b005cd80SBram Moolenaar 	    first = FALSE;
997b005cd80SBram Moolenaar 
998b005cd80SBram Moolenaar 	    if (fresult != NULL && rettv->v_type == VAR_LIST)
999b005cd80SBram Moolenaar 		list_append_string(rettv->vval.v_list, fresult, -1);
1000b005cd80SBram Moolenaar 
1001b005cd80SBram Moolenaar 	} while ((rettv->v_type == VAR_LIST || --count > 0) && fresult != NULL);
1002b005cd80SBram Moolenaar     }
1003b005cd80SBram Moolenaar 
1004b005cd80SBram Moolenaar     if (rettv->v_type == VAR_STRING)
1005b005cd80SBram Moolenaar 	rettv->vval.v_string = fresult;
1006b005cd80SBram Moolenaar #endif
1007b005cd80SBram Moolenaar }
1008b005cd80SBram Moolenaar 
1009b005cd80SBram Moolenaar /*
1010b005cd80SBram Moolenaar  * "finddir({fname}[, {path}[, {count}]])" function
1011b005cd80SBram Moolenaar  */
1012b005cd80SBram Moolenaar     void
f_finddir(typval_T * argvars,typval_T * rettv)1013b005cd80SBram Moolenaar f_finddir(typval_T *argvars, typval_T *rettv)
1014b005cd80SBram Moolenaar {
1015b005cd80SBram Moolenaar     findfilendir(argvars, rettv, FINDFILE_DIR);
1016b005cd80SBram Moolenaar }
1017b005cd80SBram Moolenaar 
1018b005cd80SBram Moolenaar /*
1019b005cd80SBram Moolenaar  * "findfile({fname}[, {path}[, {count}]])" function
1020b005cd80SBram Moolenaar  */
1021b005cd80SBram Moolenaar     void
f_findfile(typval_T * argvars,typval_T * rettv)1022b005cd80SBram Moolenaar f_findfile(typval_T *argvars, typval_T *rettv)
1023b005cd80SBram Moolenaar {
1024b005cd80SBram Moolenaar     findfilendir(argvars, rettv, FINDFILE_FILE);
1025b005cd80SBram Moolenaar }
1026b005cd80SBram Moolenaar 
1027b005cd80SBram Moolenaar /*
1028b005cd80SBram Moolenaar  * "fnamemodify({fname}, {mods})" function
1029b005cd80SBram Moolenaar  */
1030b005cd80SBram Moolenaar     void
f_fnamemodify(typval_T * argvars,typval_T * rettv)1031b005cd80SBram Moolenaar f_fnamemodify(typval_T *argvars, typval_T *rettv)
1032b005cd80SBram Moolenaar {
1033b005cd80SBram Moolenaar     char_u	*fname;
1034b005cd80SBram Moolenaar     char_u	*mods;
1035b005cd80SBram Moolenaar     int		usedlen = 0;
1036c5308523SBram Moolenaar     int		len = 0;
1037b005cd80SBram Moolenaar     char_u	*fbuf = NULL;
1038b005cd80SBram Moolenaar     char_u	buf[NUMBUFLEN];
1039b005cd80SBram Moolenaar 
10404490ec4eSYegappan Lakshmanan     if (in_vim9script()
10414490ec4eSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
104232105ae8SBram Moolenaar 		|| check_for_string_arg(argvars, 1) == FAIL))
10437bb4e74cSBram Moolenaar 	return;
10444490ec4eSYegappan Lakshmanan 
1045b005cd80SBram Moolenaar     fname = tv_get_string_chk(&argvars[0]);
1046b005cd80SBram Moolenaar     mods = tv_get_string_buf_chk(&argvars[1], buf);
1047c5308523SBram Moolenaar     if (mods == NULL || fname == NULL)
1048b005cd80SBram Moolenaar 	fname = NULL;
1049c5308523SBram Moolenaar     else
1050b005cd80SBram Moolenaar     {
1051b005cd80SBram Moolenaar 	len = (int)STRLEN(fname);
1052c5308523SBram Moolenaar 	if (mods != NULL && *mods != NUL)
1053b005cd80SBram Moolenaar 	    (void)modify_fname(mods, FALSE, &usedlen, &fname, &fbuf, &len);
1054b005cd80SBram Moolenaar     }
1055b005cd80SBram Moolenaar 
1056b005cd80SBram Moolenaar     rettv->v_type = VAR_STRING;
1057b005cd80SBram Moolenaar     if (fname == NULL)
1058b005cd80SBram Moolenaar 	rettv->vval.v_string = NULL;
1059b005cd80SBram Moolenaar     else
1060b005cd80SBram Moolenaar 	rettv->vval.v_string = vim_strnsave(fname, len);
1061b005cd80SBram Moolenaar     vim_free(fbuf);
1062b005cd80SBram Moolenaar }
1063b005cd80SBram Moolenaar 
1064b005cd80SBram Moolenaar /*
1065b005cd80SBram Moolenaar  * "getcwd()" function
1066b005cd80SBram Moolenaar  *
1067b005cd80SBram Moolenaar  * Return the current working directory of a window in a tab page.
1068b005cd80SBram Moolenaar  * First optional argument 'winnr' is the window number or -1 and the second
1069b005cd80SBram Moolenaar  * optional argument 'tabnr' is the tab page number.
1070b005cd80SBram Moolenaar  *
1071b005cd80SBram Moolenaar  * If no arguments are supplied, then return the directory of the current
1072b005cd80SBram Moolenaar  * window.
1073b005cd80SBram Moolenaar  * If only 'winnr' is specified and is not -1 or 0 then return the directory of
1074b005cd80SBram Moolenaar  * the specified window.
1075b005cd80SBram Moolenaar  * If 'winnr' is 0 then return the directory of the current window.
1076b005cd80SBram Moolenaar  * If both 'winnr and 'tabnr' are specified and 'winnr' is -1 then return the
1077b005cd80SBram Moolenaar  * directory of the specified tab page.  Otherwise return the directory of the
1078b005cd80SBram Moolenaar  * specified window in the specified tab page.
1079b005cd80SBram Moolenaar  * If the window or the tab page doesn't exist then return NULL.
1080b005cd80SBram Moolenaar  */
1081b005cd80SBram Moolenaar     void
f_getcwd(typval_T * argvars,typval_T * rettv)1082b005cd80SBram Moolenaar f_getcwd(typval_T *argvars, typval_T *rettv)
1083b005cd80SBram Moolenaar {
1084b005cd80SBram Moolenaar     win_T	*wp = NULL;
1085b005cd80SBram Moolenaar     tabpage_T	*tp = NULL;
1086b005cd80SBram Moolenaar     char_u	*cwd;
1087b005cd80SBram Moolenaar     int		global = FALSE;
1088b005cd80SBram Moolenaar 
1089b005cd80SBram Moolenaar     rettv->v_type = VAR_STRING;
1090b005cd80SBram Moolenaar     rettv->vval.v_string = NULL;
1091b005cd80SBram Moolenaar 
10924490ec4eSYegappan Lakshmanan     if (in_vim9script()
10934490ec4eSYegappan Lakshmanan 	    && (check_for_opt_number_arg(argvars, 0) == FAIL
10944490ec4eSYegappan Lakshmanan 		|| (argvars[0].v_type != VAR_UNKNOWN
10954490ec4eSYegappan Lakshmanan 		    && check_for_opt_number_arg(argvars, 1) == FAIL)))
10964490ec4eSYegappan Lakshmanan 	return;
10974490ec4eSYegappan Lakshmanan 
1098b005cd80SBram Moolenaar     if (argvars[0].v_type == VAR_NUMBER
1099b005cd80SBram Moolenaar 	    && argvars[0].vval.v_number == -1
1100b005cd80SBram Moolenaar 	    && argvars[1].v_type == VAR_UNKNOWN)
1101b005cd80SBram Moolenaar 	global = TRUE;
1102b005cd80SBram Moolenaar     else
1103b005cd80SBram Moolenaar 	wp = find_tabwin(&argvars[0], &argvars[1], &tp);
1104b005cd80SBram Moolenaar 
1105*851c7a69SBram Moolenaar     if (wp != NULL && wp->w_localdir != NULL
1106*851c7a69SBram Moolenaar 					   && argvars[0].v_type != VAR_UNKNOWN)
1107b005cd80SBram Moolenaar 	rettv->vval.v_string = vim_strsave(wp->w_localdir);
1108*851c7a69SBram Moolenaar     else if (tp != NULL && tp->tp_localdir != NULL
1109*851c7a69SBram Moolenaar 					   && argvars[0].v_type != VAR_UNKNOWN)
1110b005cd80SBram Moolenaar 	rettv->vval.v_string = vim_strsave(tp->tp_localdir);
1111b005cd80SBram Moolenaar     else if (wp != NULL || tp != NULL || global)
1112b005cd80SBram Moolenaar     {
1113*851c7a69SBram Moolenaar 	if (globaldir != NULL && argvars[0].v_type != VAR_UNKNOWN)
1114b005cd80SBram Moolenaar 	    rettv->vval.v_string = vim_strsave(globaldir);
1115b005cd80SBram Moolenaar 	else
1116b005cd80SBram Moolenaar 	{
1117b005cd80SBram Moolenaar 	    cwd = alloc(MAXPATHL);
1118b005cd80SBram Moolenaar 	    if (cwd != NULL)
1119b005cd80SBram Moolenaar 	    {
1120b005cd80SBram Moolenaar 		if (mch_dirname(cwd, MAXPATHL) != FAIL)
1121b005cd80SBram Moolenaar 		    rettv->vval.v_string = vim_strsave(cwd);
1122b005cd80SBram Moolenaar 		vim_free(cwd);
1123b005cd80SBram Moolenaar 	    }
1124b005cd80SBram Moolenaar 	}
1125b005cd80SBram Moolenaar     }
1126b005cd80SBram Moolenaar #ifdef BACKSLASH_IN_FILENAME
1127b005cd80SBram Moolenaar     if (rettv->vval.v_string != NULL)
1128b005cd80SBram Moolenaar 	slash_adjust(rettv->vval.v_string);
1129b005cd80SBram Moolenaar #endif
1130b005cd80SBram Moolenaar }
1131b005cd80SBram Moolenaar 
1132b005cd80SBram Moolenaar /*
11336c9ba042SBram Moolenaar  * Convert "st" to file permission string.
11346c9ba042SBram Moolenaar  */
11356c9ba042SBram Moolenaar     char_u *
getfpermst(stat_T * st,char_u * perm)11366c9ba042SBram Moolenaar getfpermst(stat_T *st, char_u *perm)
11376c9ba042SBram Moolenaar {
11386c9ba042SBram Moolenaar     char_u	    flags[] = "rwx";
11396c9ba042SBram Moolenaar     int		    i;
11406c9ba042SBram Moolenaar 
11416c9ba042SBram Moolenaar     for (i = 0; i < 9; i++)
11426c9ba042SBram Moolenaar     {
11436c9ba042SBram Moolenaar 	if (st->st_mode & (1 << (8 - i)))
11446c9ba042SBram Moolenaar 	    perm[i] = flags[i % 3];
11456c9ba042SBram Moolenaar 	else
11466c9ba042SBram Moolenaar 	    perm[i] = '-';
11476c9ba042SBram Moolenaar     }
11486c9ba042SBram Moolenaar     return perm;
11496c9ba042SBram Moolenaar }
11506c9ba042SBram Moolenaar 
11516c9ba042SBram Moolenaar /*
1152b005cd80SBram Moolenaar  * "getfperm({fname})" function
1153b005cd80SBram Moolenaar  */
1154b005cd80SBram Moolenaar     void
f_getfperm(typval_T * argvars,typval_T * rettv)1155b005cd80SBram Moolenaar f_getfperm(typval_T *argvars, typval_T *rettv)
1156b005cd80SBram Moolenaar {
1157b005cd80SBram Moolenaar     char_u	*fname;
1158b005cd80SBram Moolenaar     stat_T	st;
1159b005cd80SBram Moolenaar     char_u	*perm = NULL;
11606c9ba042SBram Moolenaar     char_u	permbuf[] = "---------";
1161b005cd80SBram Moolenaar 
116232105ae8SBram Moolenaar     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
11637bb4e74cSBram Moolenaar 	return;
11644490ec4eSYegappan Lakshmanan 
1165b005cd80SBram Moolenaar     fname = tv_get_string(&argvars[0]);
1166b005cd80SBram Moolenaar 
1167b005cd80SBram Moolenaar     rettv->v_type = VAR_STRING;
1168b005cd80SBram Moolenaar     if (mch_stat((char *)fname, &st) >= 0)
11696c9ba042SBram Moolenaar 	perm = vim_strsave(getfpermst(&st, permbuf));
1170b005cd80SBram Moolenaar     rettv->vval.v_string = perm;
1171b005cd80SBram Moolenaar }
1172b005cd80SBram Moolenaar 
1173b005cd80SBram Moolenaar /*
1174b005cd80SBram Moolenaar  * "getfsize({fname})" function
1175b005cd80SBram Moolenaar  */
1176b005cd80SBram Moolenaar     void
f_getfsize(typval_T * argvars,typval_T * rettv)1177b005cd80SBram Moolenaar f_getfsize(typval_T *argvars, typval_T *rettv)
1178b005cd80SBram Moolenaar {
1179b005cd80SBram Moolenaar     char_u	*fname;
1180b005cd80SBram Moolenaar     stat_T	st;
1181b005cd80SBram Moolenaar 
118232105ae8SBram Moolenaar     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
11837bb4e74cSBram Moolenaar 	return;
11847bb4e74cSBram Moolenaar 
1185b005cd80SBram Moolenaar     fname = tv_get_string(&argvars[0]);
1186b005cd80SBram Moolenaar     if (mch_stat((char *)fname, &st) >= 0)
1187b005cd80SBram Moolenaar     {
1188b005cd80SBram Moolenaar 	if (mch_isdir(fname))
1189b005cd80SBram Moolenaar 	    rettv->vval.v_number = 0;
1190b005cd80SBram Moolenaar 	else
1191b005cd80SBram Moolenaar 	{
1192b005cd80SBram Moolenaar 	    rettv->vval.v_number = (varnumber_T)st.st_size;
1193b005cd80SBram Moolenaar 
119426262f87SBram Moolenaar 	    // non-perfect check for overflow
1195b005cd80SBram Moolenaar 	    if ((off_T)rettv->vval.v_number != (off_T)st.st_size)
1196b005cd80SBram Moolenaar 		rettv->vval.v_number = -2;
1197b005cd80SBram Moolenaar 	}
1198b005cd80SBram Moolenaar     }
1199b005cd80SBram Moolenaar     else
1200b005cd80SBram Moolenaar 	  rettv->vval.v_number = -1;
1201b005cd80SBram Moolenaar }
1202b005cd80SBram Moolenaar 
1203b005cd80SBram Moolenaar /*
1204b005cd80SBram Moolenaar  * "getftime({fname})" function
1205b005cd80SBram Moolenaar  */
1206b005cd80SBram Moolenaar     void
f_getftime(typval_T * argvars,typval_T * rettv)1207b005cd80SBram Moolenaar f_getftime(typval_T *argvars, typval_T *rettv)
1208b005cd80SBram Moolenaar {
1209b005cd80SBram Moolenaar     char_u	*fname;
1210b005cd80SBram Moolenaar     stat_T	st;
1211b005cd80SBram Moolenaar 
121232105ae8SBram Moolenaar     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
12137bb4e74cSBram Moolenaar 	return;
12144490ec4eSYegappan Lakshmanan 
1215b005cd80SBram Moolenaar     fname = tv_get_string(&argvars[0]);
1216b005cd80SBram Moolenaar     if (mch_stat((char *)fname, &st) >= 0)
1217b005cd80SBram Moolenaar 	rettv->vval.v_number = (varnumber_T)st.st_mtime;
1218b005cd80SBram Moolenaar     else
1219b005cd80SBram Moolenaar 	rettv->vval.v_number = -1;
1220b005cd80SBram Moolenaar }
1221b005cd80SBram Moolenaar 
1222b005cd80SBram Moolenaar /*
12236c9ba042SBram Moolenaar  * Convert "st" to file type string.
12246c9ba042SBram Moolenaar  */
12256c9ba042SBram Moolenaar     char_u *
getftypest(stat_T * st)12266c9ba042SBram Moolenaar getftypest(stat_T *st)
12276c9ba042SBram Moolenaar {
12286c9ba042SBram Moolenaar     char    *t;
12296c9ba042SBram Moolenaar 
12306c9ba042SBram Moolenaar     if (S_ISREG(st->st_mode))
12316c9ba042SBram Moolenaar 	t = "file";
12326c9ba042SBram Moolenaar     else if (S_ISDIR(st->st_mode))
12336c9ba042SBram Moolenaar 	t = "dir";
12346c9ba042SBram Moolenaar     else if (S_ISLNK(st->st_mode))
12356c9ba042SBram Moolenaar 	t = "link";
12366c9ba042SBram Moolenaar     else if (S_ISBLK(st->st_mode))
12376c9ba042SBram Moolenaar 	t = "bdev";
12386c9ba042SBram Moolenaar     else if (S_ISCHR(st->st_mode))
12396c9ba042SBram Moolenaar 	t = "cdev";
12406c9ba042SBram Moolenaar     else if (S_ISFIFO(st->st_mode))
12416c9ba042SBram Moolenaar 	t = "fifo";
12426c9ba042SBram Moolenaar     else if (S_ISSOCK(st->st_mode))
12436c9ba042SBram Moolenaar 	t = "socket";
12446c9ba042SBram Moolenaar     else
12456c9ba042SBram Moolenaar 	t = "other";
12466c9ba042SBram Moolenaar     return (char_u*)t;
12476c9ba042SBram Moolenaar }
12486c9ba042SBram Moolenaar 
12496c9ba042SBram Moolenaar /*
1250b005cd80SBram Moolenaar  * "getftype({fname})" function
1251b005cd80SBram Moolenaar  */
1252b005cd80SBram Moolenaar     void
f_getftype(typval_T * argvars,typval_T * rettv)1253b005cd80SBram Moolenaar f_getftype(typval_T *argvars, typval_T *rettv)
1254b005cd80SBram Moolenaar {
1255b005cd80SBram Moolenaar     char_u	*fname;
1256b005cd80SBram Moolenaar     stat_T	st;
1257b005cd80SBram Moolenaar     char_u	*type = NULL;
1258b005cd80SBram Moolenaar 
125932105ae8SBram Moolenaar     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
12607bb4e74cSBram Moolenaar 	return;
12614490ec4eSYegappan Lakshmanan 
1262b005cd80SBram Moolenaar     fname = tv_get_string(&argvars[0]);
1263b005cd80SBram Moolenaar 
1264b005cd80SBram Moolenaar     rettv->v_type = VAR_STRING;
1265b005cd80SBram Moolenaar     if (mch_lstat((char *)fname, &st) >= 0)
12666c9ba042SBram Moolenaar 	type = vim_strsave(getftypest(&st));
1267b005cd80SBram Moolenaar     rettv->vval.v_string = type;
1268b005cd80SBram Moolenaar }
1269b005cd80SBram Moolenaar 
1270b005cd80SBram Moolenaar /*
1271b005cd80SBram Moolenaar  * "glob()" function
1272b005cd80SBram Moolenaar  */
1273b005cd80SBram Moolenaar     void
f_glob(typval_T * argvars,typval_T * rettv)1274b005cd80SBram Moolenaar f_glob(typval_T *argvars, typval_T *rettv)
1275b005cd80SBram Moolenaar {
1276b005cd80SBram Moolenaar     int		options = WILD_SILENT|WILD_USE_NL;
1277b005cd80SBram Moolenaar     expand_T	xpc;
1278b005cd80SBram Moolenaar     int		error = FALSE;
1279b005cd80SBram Moolenaar 
128083494b4aSYegappan Lakshmanan     if (in_vim9script()
128183494b4aSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
128283494b4aSYegappan Lakshmanan 		|| check_for_opt_bool_arg(argvars, 1) == FAIL
128383494b4aSYegappan Lakshmanan 		|| (argvars[1].v_type != VAR_UNKNOWN
128483494b4aSYegappan Lakshmanan 		    && (check_for_opt_bool_arg(argvars, 2) == FAIL
128583494b4aSYegappan Lakshmanan 			|| (argvars[2].v_type != VAR_UNKNOWN
128683494b4aSYegappan Lakshmanan 			    && check_for_opt_bool_arg(argvars, 3) == FAIL)))))
128783494b4aSYegappan Lakshmanan 	return;
128883494b4aSYegappan Lakshmanan 
128926262f87SBram Moolenaar     // When the optional second argument is non-zero, don't remove matches
129026262f87SBram Moolenaar     // for 'wildignore' and don't put matches for 'suffixes' at the end.
1291b005cd80SBram Moolenaar     rettv->v_type = VAR_STRING;
1292b005cd80SBram Moolenaar     if (argvars[1].v_type != VAR_UNKNOWN)
1293b005cd80SBram Moolenaar     {
12945892ea15SBram Moolenaar 	if (tv_get_bool_chk(&argvars[1], &error))
1295b005cd80SBram Moolenaar 	    options |= WILD_KEEP_ALL;
1296b005cd80SBram Moolenaar 	if (argvars[2].v_type != VAR_UNKNOWN)
1297b005cd80SBram Moolenaar 	{
12985892ea15SBram Moolenaar 	    if (tv_get_bool_chk(&argvars[2], &error))
1299b005cd80SBram Moolenaar 		rettv_list_set(rettv, NULL);
1300b005cd80SBram Moolenaar 	    if (argvars[3].v_type != VAR_UNKNOWN
13015892ea15SBram Moolenaar 				    && tv_get_bool_chk(&argvars[3], &error))
1302b005cd80SBram Moolenaar 		options |= WILD_ALLLINKS;
1303b005cd80SBram Moolenaar 	}
1304b005cd80SBram Moolenaar     }
1305b005cd80SBram Moolenaar     if (!error)
1306b005cd80SBram Moolenaar     {
1307b005cd80SBram Moolenaar 	ExpandInit(&xpc);
1308b005cd80SBram Moolenaar 	xpc.xp_context = EXPAND_FILES;
1309b005cd80SBram Moolenaar 	if (p_wic)
1310b005cd80SBram Moolenaar 	    options += WILD_ICASE;
1311b005cd80SBram Moolenaar 	if (rettv->v_type == VAR_STRING)
1312b005cd80SBram Moolenaar 	    rettv->vval.v_string = ExpandOne(&xpc, tv_get_string(&argvars[0]),
1313b005cd80SBram Moolenaar 						     NULL, options, WILD_ALL);
1314b005cd80SBram Moolenaar 	else if (rettv_list_alloc(rettv) != FAIL)
1315b005cd80SBram Moolenaar 	{
1316b005cd80SBram Moolenaar 	  int i;
1317b005cd80SBram Moolenaar 
1318b005cd80SBram Moolenaar 	  ExpandOne(&xpc, tv_get_string(&argvars[0]),
1319b005cd80SBram Moolenaar 						NULL, options, WILD_ALL_KEEP);
1320b005cd80SBram Moolenaar 	  for (i = 0; i < xpc.xp_numfiles; i++)
1321b005cd80SBram Moolenaar 	      list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1);
1322b005cd80SBram Moolenaar 
1323b005cd80SBram Moolenaar 	  ExpandCleanup(&xpc);
1324b005cd80SBram Moolenaar 	}
1325b005cd80SBram Moolenaar     }
1326b005cd80SBram Moolenaar     else
1327b005cd80SBram Moolenaar 	rettv->vval.v_string = NULL;
1328b005cd80SBram Moolenaar }
1329b005cd80SBram Moolenaar 
1330b005cd80SBram Moolenaar /*
1331b005cd80SBram Moolenaar  * "glob2regpat()" function
1332b005cd80SBram Moolenaar  */
1333b005cd80SBram Moolenaar     void
f_glob2regpat(typval_T * argvars,typval_T * rettv)1334b005cd80SBram Moolenaar f_glob2regpat(typval_T *argvars, typval_T *rettv)
1335b005cd80SBram Moolenaar {
13363cfa5b16SBram Moolenaar     char_u	buf[NUMBUFLEN];
13374490ec4eSYegappan Lakshmanan     char_u	*pat;
1338b005cd80SBram Moolenaar 
13394490ec4eSYegappan Lakshmanan     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
13404490ec4eSYegappan Lakshmanan 	return;
13414490ec4eSYegappan Lakshmanan 
13424490ec4eSYegappan Lakshmanan     pat = tv_get_string_buf_chk_strict(&argvars[0], buf, in_vim9script());
1343b005cd80SBram Moolenaar     rettv->v_type = VAR_STRING;
1344b005cd80SBram Moolenaar     rettv->vval.v_string = (pat == NULL)
1345b005cd80SBram Moolenaar 			 ? NULL : file_pat_to_reg_pat(pat, NULL, NULL, FALSE);
1346b005cd80SBram Moolenaar }
1347b005cd80SBram Moolenaar 
1348b005cd80SBram Moolenaar /*
1349b005cd80SBram Moolenaar  * "globpath()" function
1350b005cd80SBram Moolenaar  */
1351b005cd80SBram Moolenaar     void
f_globpath(typval_T * argvars,typval_T * rettv)1352b005cd80SBram Moolenaar f_globpath(typval_T *argvars, typval_T *rettv)
1353b005cd80SBram Moolenaar {
1354b005cd80SBram Moolenaar     int		flags = WILD_IGNORE_COMPLETESLASH;
1355b005cd80SBram Moolenaar     char_u	buf1[NUMBUFLEN];
135683494b4aSYegappan Lakshmanan     char_u	*file;
1357b005cd80SBram Moolenaar     int		error = FALSE;
1358b005cd80SBram Moolenaar     garray_T	ga;
1359b005cd80SBram Moolenaar     int		i;
1360b005cd80SBram Moolenaar 
136183494b4aSYegappan Lakshmanan     if (in_vim9script()
136283494b4aSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
136383494b4aSYegappan Lakshmanan 		|| check_for_string_arg(argvars, 1) == FAIL
136483494b4aSYegappan Lakshmanan 		|| check_for_opt_bool_arg(argvars, 2) == FAIL
136583494b4aSYegappan Lakshmanan 		|| (argvars[2].v_type != VAR_UNKNOWN
136683494b4aSYegappan Lakshmanan 		    && (check_for_opt_bool_arg(argvars, 3) == FAIL
136783494b4aSYegappan Lakshmanan 			|| (argvars[3].v_type != VAR_UNKNOWN
136883494b4aSYegappan Lakshmanan 			    && check_for_opt_bool_arg(argvars, 4) == FAIL)))))
136983494b4aSYegappan Lakshmanan 	return;
137083494b4aSYegappan Lakshmanan 
137183494b4aSYegappan Lakshmanan     file = tv_get_string_buf_chk(&argvars[1], buf1);
137283494b4aSYegappan Lakshmanan 
1373b005cd80SBram Moolenaar     // When the optional second argument is non-zero, don't remove matches
1374b005cd80SBram Moolenaar     // for 'wildignore' and don't put matches for 'suffixes' at the end.
1375b005cd80SBram Moolenaar     rettv->v_type = VAR_STRING;
1376b005cd80SBram Moolenaar     if (argvars[2].v_type != VAR_UNKNOWN)
1377b005cd80SBram Moolenaar     {
1378f966ce5eSBram Moolenaar 	if (tv_get_bool_chk(&argvars[2], &error))
1379b005cd80SBram Moolenaar 	    flags |= WILD_KEEP_ALL;
1380b005cd80SBram Moolenaar 	if (argvars[3].v_type != VAR_UNKNOWN)
1381b005cd80SBram Moolenaar 	{
1382f966ce5eSBram Moolenaar 	    if (tv_get_bool_chk(&argvars[3], &error))
1383b005cd80SBram Moolenaar 		rettv_list_set(rettv, NULL);
1384b005cd80SBram Moolenaar 	    if (argvars[4].v_type != VAR_UNKNOWN
1385f966ce5eSBram Moolenaar 				    && tv_get_bool_chk(&argvars[4], &error))
1386b005cd80SBram Moolenaar 		flags |= WILD_ALLLINKS;
1387b005cd80SBram Moolenaar 	}
1388b005cd80SBram Moolenaar     }
1389b005cd80SBram Moolenaar     if (file != NULL && !error)
1390b005cd80SBram Moolenaar     {
1391b005cd80SBram Moolenaar 	ga_init2(&ga, (int)sizeof(char_u *), 10);
1392b005cd80SBram Moolenaar 	globpath(tv_get_string(&argvars[0]), file, &ga, flags);
1393b005cd80SBram Moolenaar 	if (rettv->v_type == VAR_STRING)
1394b005cd80SBram Moolenaar 	    rettv->vval.v_string = ga_concat_strings(&ga, "\n");
1395b005cd80SBram Moolenaar 	else if (rettv_list_alloc(rettv) != FAIL)
1396b005cd80SBram Moolenaar 	    for (i = 0; i < ga.ga_len; ++i)
1397b005cd80SBram Moolenaar 		list_append_string(rettv->vval.v_list,
1398b005cd80SBram Moolenaar 					    ((char_u **)(ga.ga_data))[i], -1);
1399b005cd80SBram Moolenaar 	ga_clear_strings(&ga);
1400b005cd80SBram Moolenaar     }
1401b005cd80SBram Moolenaar     else
1402b005cd80SBram Moolenaar 	rettv->vval.v_string = NULL;
1403b005cd80SBram Moolenaar }
1404b005cd80SBram Moolenaar 
1405b005cd80SBram Moolenaar /*
1406b005cd80SBram Moolenaar  * "isdirectory()" function
1407b005cd80SBram Moolenaar  */
1408b005cd80SBram Moolenaar     void
f_isdirectory(typval_T * argvars,typval_T * rettv)1409b005cd80SBram Moolenaar f_isdirectory(typval_T *argvars, typval_T *rettv)
1410b005cd80SBram Moolenaar {
14114490ec4eSYegappan Lakshmanan     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
14124490ec4eSYegappan Lakshmanan 	return;
14134490ec4eSYegappan Lakshmanan 
1414b005cd80SBram Moolenaar     rettv->vval.v_number = mch_isdir(tv_get_string(&argvars[0]));
1415b005cd80SBram Moolenaar }
1416b005cd80SBram Moolenaar 
1417b005cd80SBram Moolenaar /*
1418b005cd80SBram Moolenaar  * Create the directory in which "dir" is located, and higher levels when
1419b005cd80SBram Moolenaar  * needed.
1420b005cd80SBram Moolenaar  * Return OK or FAIL.
1421b005cd80SBram Moolenaar  */
1422b005cd80SBram Moolenaar     static int
mkdir_recurse(char_u * dir,int prot)1423b005cd80SBram Moolenaar mkdir_recurse(char_u *dir, int prot)
1424b005cd80SBram Moolenaar {
1425b005cd80SBram Moolenaar     char_u	*p;
1426b005cd80SBram Moolenaar     char_u	*updir;
1427b005cd80SBram Moolenaar     int		r = FAIL;
1428b005cd80SBram Moolenaar 
142926262f87SBram Moolenaar     // Get end of directory name in "dir".
143026262f87SBram Moolenaar     // We're done when it's "/" or "c:/".
1431b005cd80SBram Moolenaar     p = gettail_sep(dir);
1432b005cd80SBram Moolenaar     if (p <= get_past_head(dir))
1433b005cd80SBram Moolenaar 	return OK;
1434b005cd80SBram Moolenaar 
143526262f87SBram Moolenaar     // If the directory exists we're done.  Otherwise: create it.
143671ccd03eSBram Moolenaar     updir = vim_strnsave(dir, p - dir);
1437b005cd80SBram Moolenaar     if (updir == NULL)
1438b005cd80SBram Moolenaar 	return FAIL;
1439b005cd80SBram Moolenaar     if (mch_isdir(updir))
1440b005cd80SBram Moolenaar 	r = OK;
1441b005cd80SBram Moolenaar     else if (mkdir_recurse(updir, prot) == OK)
1442b005cd80SBram Moolenaar 	r = vim_mkdir_emsg(updir, prot);
1443b005cd80SBram Moolenaar     vim_free(updir);
1444b005cd80SBram Moolenaar     return r;
1445b005cd80SBram Moolenaar }
1446b005cd80SBram Moolenaar 
1447b005cd80SBram Moolenaar /*
1448b005cd80SBram Moolenaar  * "mkdir()" function
1449b005cd80SBram Moolenaar  */
1450b005cd80SBram Moolenaar     void
f_mkdir(typval_T * argvars,typval_T * rettv)1451b005cd80SBram Moolenaar f_mkdir(typval_T *argvars, typval_T *rettv)
1452b005cd80SBram Moolenaar {
1453b005cd80SBram Moolenaar     char_u	*dir;
1454b005cd80SBram Moolenaar     char_u	buf[NUMBUFLEN];
1455b005cd80SBram Moolenaar     int		prot = 0755;
1456b005cd80SBram Moolenaar 
1457b005cd80SBram Moolenaar     rettv->vval.v_number = FAIL;
1458b005cd80SBram Moolenaar     if (check_restricted() || check_secure())
1459b005cd80SBram Moolenaar 	return;
1460b005cd80SBram Moolenaar 
14614490ec4eSYegappan Lakshmanan     if (in_vim9script()
14624490ec4eSYegappan Lakshmanan 	    && (check_for_nonempty_string_arg(argvars, 0) == FAIL
14634490ec4eSYegappan Lakshmanan 		|| check_for_opt_string_arg(argvars, 1) == FAIL
14644490ec4eSYegappan Lakshmanan 		|| (argvars[1].v_type != VAR_UNKNOWN
14654490ec4eSYegappan Lakshmanan 		    && check_for_opt_number_arg(argvars, 2) == FAIL)))
14664490ec4eSYegappan Lakshmanan 	return;
14674490ec4eSYegappan Lakshmanan 
1468b005cd80SBram Moolenaar     dir = tv_get_string_buf(&argvars[0], buf);
1469b005cd80SBram Moolenaar     if (*dir == NUL)
1470b005cd80SBram Moolenaar 	return;
1471b005cd80SBram Moolenaar 
1472b005cd80SBram Moolenaar     if (*gettail(dir) == NUL)
147326262f87SBram Moolenaar 	// remove trailing slashes
1474b005cd80SBram Moolenaar 	*gettail_sep(dir) = NUL;
1475b005cd80SBram Moolenaar 
1476b005cd80SBram Moolenaar     if (argvars[1].v_type != VAR_UNKNOWN)
1477b005cd80SBram Moolenaar     {
1478b005cd80SBram Moolenaar 	if (argvars[2].v_type != VAR_UNKNOWN)
1479b005cd80SBram Moolenaar 	{
1480b005cd80SBram Moolenaar 	    prot = (int)tv_get_number_chk(&argvars[2], NULL);
1481b005cd80SBram Moolenaar 	    if (prot == -1)
1482b005cd80SBram Moolenaar 		return;
1483b005cd80SBram Moolenaar 	}
1484b005cd80SBram Moolenaar 	if (STRCMP(tv_get_string(&argvars[1]), "p") == 0)
1485b005cd80SBram Moolenaar 	{
1486b005cd80SBram Moolenaar 	    if (mch_isdir(dir))
1487b005cd80SBram Moolenaar 	    {
148826262f87SBram Moolenaar 		// With the "p" flag it's OK if the dir already exists.
1489b005cd80SBram Moolenaar 		rettv->vval.v_number = OK;
1490b005cd80SBram Moolenaar 		return;
1491b005cd80SBram Moolenaar 	    }
1492b005cd80SBram Moolenaar 	    mkdir_recurse(dir, prot);
1493b005cd80SBram Moolenaar 	}
1494b005cd80SBram Moolenaar     }
1495b005cd80SBram Moolenaar     rettv->vval.v_number = vim_mkdir_emsg(dir, prot);
1496b005cd80SBram Moolenaar }
1497b005cd80SBram Moolenaar 
1498b005cd80SBram Moolenaar /*
1499af7645d3SBram Moolenaar  * "pathshorten()" function
1500af7645d3SBram Moolenaar  */
1501af7645d3SBram Moolenaar     void
f_pathshorten(typval_T * argvars,typval_T * rettv)1502af7645d3SBram Moolenaar f_pathshorten(typval_T *argvars, typval_T *rettv)
1503af7645d3SBram Moolenaar {
1504af7645d3SBram Moolenaar     char_u	*p;
15056a33ef0dSBram Moolenaar     int		trim_len = 1;
15066a33ef0dSBram Moolenaar 
15071a71d31bSYegappan Lakshmanan     if (in_vim9script()
15081a71d31bSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
150983494b4aSYegappan Lakshmanan 		|| check_for_opt_number_arg(argvars, 1) == FAIL))
15101a71d31bSYegappan Lakshmanan 	return;
15111a71d31bSYegappan Lakshmanan 
15126a33ef0dSBram Moolenaar     if (argvars[1].v_type != VAR_UNKNOWN)
15136a33ef0dSBram Moolenaar     {
15146a33ef0dSBram Moolenaar 	trim_len = (int)tv_get_number(&argvars[1]);
15156a33ef0dSBram Moolenaar 	if (trim_len < 1)
15166a33ef0dSBram Moolenaar 	    trim_len = 1;
15176a33ef0dSBram Moolenaar     }
1518af7645d3SBram Moolenaar 
1519af7645d3SBram Moolenaar     rettv->v_type = VAR_STRING;
1520af7645d3SBram Moolenaar     p = tv_get_string_chk(&argvars[0]);
15216a33ef0dSBram Moolenaar 
1522af7645d3SBram Moolenaar     if (p == NULL)
1523af7645d3SBram Moolenaar 	rettv->vval.v_string = NULL;
1524af7645d3SBram Moolenaar     else
1525af7645d3SBram Moolenaar     {
1526af7645d3SBram Moolenaar 	p = vim_strsave(p);
1527af7645d3SBram Moolenaar 	rettv->vval.v_string = p;
1528af7645d3SBram Moolenaar 	if (p != NULL)
15296a33ef0dSBram Moolenaar 	    shorten_dir_len(p, trim_len);
1530af7645d3SBram Moolenaar     }
1531af7645d3SBram Moolenaar }
1532af7645d3SBram Moolenaar 
1533af7645d3SBram Moolenaar /*
1534f8abbf37SBram Moolenaar  * Common code for readdir_checkitem() and readdirex_checkitem().
1535f8abbf37SBram Moolenaar  * Either "name" or "dict" is NULL.
153680147ddaSBram Moolenaar  */
153780147ddaSBram Moolenaar     static int
checkitem_common(void * context,char_u * name,dict_T * dict)1538f8abbf37SBram Moolenaar checkitem_common(void *context, char_u *name, dict_T *dict)
153980147ddaSBram Moolenaar {
154080147ddaSBram Moolenaar     typval_T	*expr = (typval_T *)context;
154180147ddaSBram Moolenaar     typval_T	save_val;
154280147ddaSBram Moolenaar     typval_T	rettv;
154380147ddaSBram Moolenaar     typval_T	argv[2];
154480147ddaSBram Moolenaar     int		retval = 0;
154580147ddaSBram Moolenaar     int		error = FALSE;
154680147ddaSBram Moolenaar 
154780147ddaSBram Moolenaar     prepare_vimvar(VV_VAL, &save_val);
1548f8abbf37SBram Moolenaar     if (name != NULL)
1549f8abbf37SBram Moolenaar     {
155080147ddaSBram Moolenaar 	set_vim_var_string(VV_VAL, name, -1);
155180147ddaSBram Moolenaar 	argv[0].v_type = VAR_STRING;
155280147ddaSBram Moolenaar 	argv[0].vval.v_string = name;
1553f8abbf37SBram Moolenaar     }
1554f8abbf37SBram Moolenaar     else
1555f8abbf37SBram Moolenaar     {
1556f8abbf37SBram Moolenaar 	set_vim_var_dict(VV_VAL, dict);
1557f8abbf37SBram Moolenaar 	argv[0].v_type = VAR_DICT;
1558f8abbf37SBram Moolenaar 	argv[0].vval.v_dict = dict;
1559f8abbf37SBram Moolenaar     }
156080147ddaSBram Moolenaar 
156180147ddaSBram Moolenaar     if (eval_expr_typval(expr, argv, 1, &rettv) == FAIL)
156280147ddaSBram Moolenaar 	goto theend;
156380147ddaSBram Moolenaar 
1564f8abbf37SBram Moolenaar     // We want to use -1, but also true/false should be allowed.
1565f8abbf37SBram Moolenaar     if (rettv.v_type == VAR_SPECIAL || rettv.v_type == VAR_BOOL)
1566f8abbf37SBram Moolenaar     {
1567f8abbf37SBram Moolenaar 	rettv.v_type = VAR_NUMBER;
1568f8abbf37SBram Moolenaar 	rettv.vval.v_number = rettv.vval.v_number == VVAL_TRUE;
1569f8abbf37SBram Moolenaar     }
157080147ddaSBram Moolenaar     retval = tv_get_number_chk(&rettv, &error);
157180147ddaSBram Moolenaar     if (error)
157280147ddaSBram Moolenaar 	retval = -1;
157380147ddaSBram Moolenaar     clear_tv(&rettv);
157480147ddaSBram Moolenaar 
157580147ddaSBram Moolenaar theend:
1576f8abbf37SBram Moolenaar     if (name != NULL)
157780147ddaSBram Moolenaar 	set_vim_var_string(VV_VAL, NULL, 0);
1578f8abbf37SBram Moolenaar     else
1579f8abbf37SBram Moolenaar 	set_vim_var_dict(VV_VAL, NULL);
158080147ddaSBram Moolenaar     restore_vimvar(VV_VAL, &save_val);
158180147ddaSBram Moolenaar     return retval;
158280147ddaSBram Moolenaar }
158380147ddaSBram Moolenaar 
1584f8abbf37SBram Moolenaar /*
1585f8abbf37SBram Moolenaar  * Evaluate "expr" (= "context") for readdir().
1586f8abbf37SBram Moolenaar  */
1587f8abbf37SBram Moolenaar     static int
readdir_checkitem(void * context,void * item)1588f8abbf37SBram Moolenaar readdir_checkitem(void *context, void *item)
1589f8abbf37SBram Moolenaar {
1590f8abbf37SBram Moolenaar     char_u	*name = (char_u *)item;
1591f8abbf37SBram Moolenaar 
1592f8abbf37SBram Moolenaar     return checkitem_common(context, name, NULL);
1593f8abbf37SBram Moolenaar }
1594f8abbf37SBram Moolenaar 
159584cf6bd8SBram Moolenaar     static int
readdirex_dict_arg(typval_T * tv,int * cmp)159684cf6bd8SBram Moolenaar readdirex_dict_arg(typval_T *tv, int *cmp)
159784cf6bd8SBram Moolenaar {
159884cf6bd8SBram Moolenaar     char_u     *compare;
159984cf6bd8SBram Moolenaar 
160084cf6bd8SBram Moolenaar     if (tv->v_type != VAR_DICT)
160184cf6bd8SBram Moolenaar     {
160284cf6bd8SBram Moolenaar 	emsg(_(e_dictreq));
160384cf6bd8SBram Moolenaar 	return FAIL;
160484cf6bd8SBram Moolenaar     }
160584cf6bd8SBram Moolenaar 
160684cf6bd8SBram Moolenaar     if (dict_find(tv->vval.v_dict, (char_u *)"sort", -1) != NULL)
160784cf6bd8SBram Moolenaar 	compare = dict_get_string(tv->vval.v_dict, (char_u *)"sort", FALSE);
160884cf6bd8SBram Moolenaar     else
160984cf6bd8SBram Moolenaar     {
161084cf6bd8SBram Moolenaar 	semsg(_(e_no_dict_key), "sort");
161184cf6bd8SBram Moolenaar 	return FAIL;
161284cf6bd8SBram Moolenaar     }
161384cf6bd8SBram Moolenaar 
161484cf6bd8SBram Moolenaar     if (STRCMP(compare, (char_u *) "none") == 0)
161584cf6bd8SBram Moolenaar 	*cmp = READDIR_SORT_NONE;
161684cf6bd8SBram Moolenaar     else if (STRCMP(compare, (char_u *) "case") == 0)
161784cf6bd8SBram Moolenaar 	*cmp = READDIR_SORT_BYTE;
161884cf6bd8SBram Moolenaar     else if (STRCMP(compare, (char_u *) "icase") == 0)
161984cf6bd8SBram Moolenaar 	*cmp = READDIR_SORT_IC;
162084cf6bd8SBram Moolenaar     else if (STRCMP(compare, (char_u *) "collate") == 0)
162184cf6bd8SBram Moolenaar 	*cmp = READDIR_SORT_COLLATE;
162284cf6bd8SBram Moolenaar     return OK;
162384cf6bd8SBram Moolenaar }
162484cf6bd8SBram Moolenaar 
162580147ddaSBram Moolenaar /*
1626b005cd80SBram Moolenaar  * "readdir()" function
1627b005cd80SBram Moolenaar  */
1628b005cd80SBram Moolenaar     void
f_readdir(typval_T * argvars,typval_T * rettv)1629b005cd80SBram Moolenaar f_readdir(typval_T *argvars, typval_T *rettv)
1630b005cd80SBram Moolenaar {
1631b005cd80SBram Moolenaar     typval_T	*expr;
1632b005cd80SBram Moolenaar     int		ret;
1633b005cd80SBram Moolenaar     char_u	*path;
1634b005cd80SBram Moolenaar     char_u	*p;
1635b005cd80SBram Moolenaar     garray_T	ga;
1636b005cd80SBram Moolenaar     int		i;
163784cf6bd8SBram Moolenaar     int         sort = READDIR_SORT_BYTE;
1638b005cd80SBram Moolenaar 
1639b005cd80SBram Moolenaar     if (rettv_list_alloc(rettv) == FAIL)
1640b005cd80SBram Moolenaar 	return;
16415bca906bSYegappan Lakshmanan 
16425bca906bSYegappan Lakshmanan     if (in_vim9script()
16435bca906bSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
16445bca906bSYegappan Lakshmanan 		|| (argvars[1].v_type != VAR_UNKNOWN
16455bca906bSYegappan Lakshmanan 		    && check_for_opt_dict_arg(argvars, 2) == FAIL)))
16465bca906bSYegappan Lakshmanan 	return;
16475bca906bSYegappan Lakshmanan 
1648b005cd80SBram Moolenaar     path = tv_get_string(&argvars[0]);
1649b005cd80SBram Moolenaar     expr = &argvars[1];
1650b005cd80SBram Moolenaar 
165184cf6bd8SBram Moolenaar     if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN &&
165284cf6bd8SBram Moolenaar 	    readdirex_dict_arg(&argvars[2], &sort) == FAIL)
165384cf6bd8SBram Moolenaar 	return;
165484cf6bd8SBram Moolenaar 
16556c9ba042SBram Moolenaar     ret = readdir_core(&ga, path, FALSE, (void *)expr,
165684cf6bd8SBram Moolenaar 	    (expr->v_type == VAR_UNKNOWN) ? NULL : readdir_checkitem, sort);
16576c9ba042SBram Moolenaar     if (ret == OK)
1658b005cd80SBram Moolenaar     {
1659b005cd80SBram Moolenaar 	for (i = 0; i < ga.ga_len; i++)
1660b005cd80SBram Moolenaar 	{
1661b005cd80SBram Moolenaar 	    p = ((char_u **)ga.ga_data)[i];
1662b005cd80SBram Moolenaar 	    list_append_string(rettv->vval.v_list, p, -1);
1663b005cd80SBram Moolenaar 	}
1664b005cd80SBram Moolenaar     }
1665b005cd80SBram Moolenaar     ga_clear_strings(&ga);
1666b005cd80SBram Moolenaar }
1667b005cd80SBram Moolenaar 
1668b005cd80SBram Moolenaar /*
16696c9ba042SBram Moolenaar  * Evaluate "expr" (= "context") for readdirex().
16706c9ba042SBram Moolenaar  */
16716c9ba042SBram Moolenaar     static int
readdirex_checkitem(void * context,void * item)16726c9ba042SBram Moolenaar readdirex_checkitem(void *context, void *item)
16736c9ba042SBram Moolenaar {
16746c9ba042SBram Moolenaar     dict_T	*dict = (dict_T*)item;
16756c9ba042SBram Moolenaar 
1676f8abbf37SBram Moolenaar     return checkitem_common(context, NULL, dict);
16776c9ba042SBram Moolenaar }
16786c9ba042SBram Moolenaar 
16796c9ba042SBram Moolenaar /*
16806c9ba042SBram Moolenaar  * "readdirex()" function
16816c9ba042SBram Moolenaar  */
16826c9ba042SBram Moolenaar     void
f_readdirex(typval_T * argvars,typval_T * rettv)16836c9ba042SBram Moolenaar f_readdirex(typval_T *argvars, typval_T *rettv)
16846c9ba042SBram Moolenaar {
16856c9ba042SBram Moolenaar     typval_T	*expr;
16866c9ba042SBram Moolenaar     int		ret;
16876c9ba042SBram Moolenaar     char_u	*path;
16886c9ba042SBram Moolenaar     garray_T	ga;
16896c9ba042SBram Moolenaar     int		i;
169084cf6bd8SBram Moolenaar     int         sort = READDIR_SORT_BYTE;
16916c9ba042SBram Moolenaar 
16926c9ba042SBram Moolenaar     if (rettv_list_alloc(rettv) == FAIL)
16936c9ba042SBram Moolenaar 	return;
16945bca906bSYegappan Lakshmanan 
16955bca906bSYegappan Lakshmanan     if (in_vim9script()
16965bca906bSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
16975bca906bSYegappan Lakshmanan 		|| (argvars[1].v_type != VAR_UNKNOWN
16985bca906bSYegappan Lakshmanan 		    && check_for_opt_dict_arg(argvars, 2) == FAIL)))
16995bca906bSYegappan Lakshmanan 	return;
17005bca906bSYegappan Lakshmanan 
17016c9ba042SBram Moolenaar     path = tv_get_string(&argvars[0]);
17026c9ba042SBram Moolenaar     expr = &argvars[1];
17036c9ba042SBram Moolenaar 
170484cf6bd8SBram Moolenaar     if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN &&
170584cf6bd8SBram Moolenaar 	    readdirex_dict_arg(&argvars[2], &sort) == FAIL)
170684cf6bd8SBram Moolenaar 	return;
170784cf6bd8SBram Moolenaar 
17086c9ba042SBram Moolenaar     ret = readdir_core(&ga, path, TRUE, (void *)expr,
170984cf6bd8SBram Moolenaar 	    (expr->v_type == VAR_UNKNOWN) ? NULL : readdirex_checkitem, sort);
17106c9ba042SBram Moolenaar     if (ret == OK)
17116c9ba042SBram Moolenaar     {
17126c9ba042SBram Moolenaar 	for (i = 0; i < ga.ga_len; i++)
17136c9ba042SBram Moolenaar 	{
17146c9ba042SBram Moolenaar 	    dict_T  *dict = ((dict_T**)ga.ga_data)[i];
17156c9ba042SBram Moolenaar 	    list_append_dict(rettv->vval.v_list, dict);
17166c9ba042SBram Moolenaar 	    dict_unref(dict);
17176c9ba042SBram Moolenaar 	}
17186c9ba042SBram Moolenaar     }
17196c9ba042SBram Moolenaar     ga_clear(&ga);
17206c9ba042SBram Moolenaar }
17216c9ba042SBram Moolenaar 
17226c9ba042SBram Moolenaar /*
1723b005cd80SBram Moolenaar  * "readfile()" function
1724b005cd80SBram Moolenaar  */
1725c423ad77SBram Moolenaar     static void
read_file_or_blob(typval_T * argvars,typval_T * rettv,int always_blob)1726c423ad77SBram Moolenaar read_file_or_blob(typval_T *argvars, typval_T *rettv, int always_blob)
1727b005cd80SBram Moolenaar {
1728b005cd80SBram Moolenaar     int		binary = FALSE;
1729c423ad77SBram Moolenaar     int		blob = always_blob;
1730b005cd80SBram Moolenaar     int		failed = FALSE;
1731b005cd80SBram Moolenaar     char_u	*fname;
1732b005cd80SBram Moolenaar     FILE	*fd;
173326262f87SBram Moolenaar     char_u	buf[(IOSIZE/256)*256];	// rounded to avoid odd + 1
1734b005cd80SBram Moolenaar     int		io_size = sizeof(buf);
173526262f87SBram Moolenaar     int		readlen;		// size of last fread()
173626262f87SBram Moolenaar     char_u	*prev	 = NULL;	// previously read bytes, if any
173726262f87SBram Moolenaar     long	prevlen  = 0;		// length of data in prev
173826262f87SBram Moolenaar     long	prevsize = 0;		// size of prev buffer
1739b005cd80SBram Moolenaar     long	maxline  = MAXLNUM;
1740b005cd80SBram Moolenaar     long	cnt	 = 0;
174126262f87SBram Moolenaar     char_u	*p;			// position in buf
174226262f87SBram Moolenaar     char_u	*start;			// start of current line
1743b005cd80SBram Moolenaar 
1744b005cd80SBram Moolenaar     if (argvars[1].v_type != VAR_UNKNOWN)
1745b005cd80SBram Moolenaar     {
1746b005cd80SBram Moolenaar 	if (STRCMP(tv_get_string(&argvars[1]), "b") == 0)
1747b005cd80SBram Moolenaar 	    binary = TRUE;
1748b005cd80SBram Moolenaar 	if (STRCMP(tv_get_string(&argvars[1]), "B") == 0)
1749b005cd80SBram Moolenaar 	    blob = TRUE;
1750b005cd80SBram Moolenaar 
1751b005cd80SBram Moolenaar 	if (argvars[2].v_type != VAR_UNKNOWN)
1752b005cd80SBram Moolenaar 	    maxline = (long)tv_get_number(&argvars[2]);
1753b005cd80SBram Moolenaar     }
1754b005cd80SBram Moolenaar 
175515352dc6SBram Moolenaar     if ((blob ? rettv_blob_alloc(rettv) : rettv_list_alloc(rettv)) == FAIL)
1756b005cd80SBram Moolenaar 	return;
1757b005cd80SBram Moolenaar 
175826262f87SBram Moolenaar     // Always open the file in binary mode, library functions have a mind of
175926262f87SBram Moolenaar     // their own about CR-LF conversion.
1760b005cd80SBram Moolenaar     fname = tv_get_string(&argvars[0]);
176115352dc6SBram Moolenaar 
176215352dc6SBram Moolenaar     if (mch_isdir(fname))
176315352dc6SBram Moolenaar     {
1764108010aaSBram Moolenaar 	semsg(_(e_src_is_directory), fname);
176515352dc6SBram Moolenaar 	return;
176615352dc6SBram Moolenaar     }
1767b005cd80SBram Moolenaar     if (*fname == NUL || (fd = mch_fopen((char *)fname, READBIN)) == NULL)
1768b005cd80SBram Moolenaar     {
1769b005cd80SBram Moolenaar 	semsg(_(e_notopen), *fname == NUL ? (char_u *)_("<empty>") : fname);
1770b005cd80SBram Moolenaar 	return;
1771b005cd80SBram Moolenaar     }
1772b005cd80SBram Moolenaar 
1773b005cd80SBram Moolenaar     if (blob)
1774b005cd80SBram Moolenaar     {
1775b005cd80SBram Moolenaar 	if (read_blob(fd, rettv->vval.v_blob) == FAIL)
1776b005cd80SBram Moolenaar 	{
177715352dc6SBram Moolenaar 	    semsg(_(e_notread), fname);
177815352dc6SBram Moolenaar 	    // An empty blob is returned on error.
1779b005cd80SBram Moolenaar 	    blob_free(rettv->vval.v_blob);
178015352dc6SBram Moolenaar 	    rettv->vval.v_blob = NULL;
1781b005cd80SBram Moolenaar 	}
1782b005cd80SBram Moolenaar 	fclose(fd);
1783b005cd80SBram Moolenaar 	return;
1784b005cd80SBram Moolenaar     }
1785b005cd80SBram Moolenaar 
1786b005cd80SBram Moolenaar     while (cnt < maxline || maxline < 0)
1787b005cd80SBram Moolenaar     {
1788b005cd80SBram Moolenaar 	readlen = (int)fread(buf, 1, io_size, fd);
1789b005cd80SBram Moolenaar 
179026262f87SBram Moolenaar 	// This for loop processes what was read, but is also entered at end
179126262f87SBram Moolenaar 	// of file so that either:
179226262f87SBram Moolenaar 	// - an incomplete line gets written
179326262f87SBram Moolenaar 	// - a "binary" file gets an empty line at the end if it ends in a
179426262f87SBram Moolenaar 	//   newline.
1795b005cd80SBram Moolenaar 	for (p = buf, start = buf;
1796b005cd80SBram Moolenaar 		p < buf + readlen || (readlen <= 0 && (prevlen > 0 || binary));
1797b005cd80SBram Moolenaar 		++p)
1798b005cd80SBram Moolenaar 	{
1799b005cd80SBram Moolenaar 	    if (*p == '\n' || readlen <= 0)
1800b005cd80SBram Moolenaar 	    {
1801b005cd80SBram Moolenaar 		listitem_T  *li;
1802b005cd80SBram Moolenaar 		char_u	    *s	= NULL;
1803b005cd80SBram Moolenaar 		long_u	    len = p - start;
1804b005cd80SBram Moolenaar 
180526262f87SBram Moolenaar 		// Finished a line.  Remove CRs before NL.
1806b005cd80SBram Moolenaar 		if (readlen > 0 && !binary)
1807b005cd80SBram Moolenaar 		{
1808b005cd80SBram Moolenaar 		    while (len > 0 && start[len - 1] == '\r')
1809b005cd80SBram Moolenaar 			--len;
181026262f87SBram Moolenaar 		    // removal may cross back to the "prev" string
1811b005cd80SBram Moolenaar 		    if (len == 0)
1812b005cd80SBram Moolenaar 			while (prevlen > 0 && prev[prevlen - 1] == '\r')
1813b005cd80SBram Moolenaar 			    --prevlen;
1814b005cd80SBram Moolenaar 		}
1815b005cd80SBram Moolenaar 		if (prevlen == 0)
181671ccd03eSBram Moolenaar 		    s = vim_strnsave(start, len);
1817b005cd80SBram Moolenaar 		else
1818b005cd80SBram Moolenaar 		{
181926262f87SBram Moolenaar 		    // Change "prev" buffer to be the right size.  This way
182026262f87SBram Moolenaar 		    // the bytes are only copied once, and very long lines are
182126262f87SBram Moolenaar 		    // allocated only once.
1822b005cd80SBram Moolenaar 		    if ((s = vim_realloc(prev, prevlen + len + 1)) != NULL)
1823b005cd80SBram Moolenaar 		    {
1824b005cd80SBram Moolenaar 			mch_memmove(s + prevlen, start, len);
1825b005cd80SBram Moolenaar 			s[prevlen + len] = NUL;
182626262f87SBram Moolenaar 			prev = NULL; // the list will own the string
1827b005cd80SBram Moolenaar 			prevlen = prevsize = 0;
1828b005cd80SBram Moolenaar 		    }
1829b005cd80SBram Moolenaar 		}
1830b005cd80SBram Moolenaar 		if (s == NULL)
1831b005cd80SBram Moolenaar 		{
1832b005cd80SBram Moolenaar 		    do_outofmem_msg((long_u) prevlen + len + 1);
1833b005cd80SBram Moolenaar 		    failed = TRUE;
1834b005cd80SBram Moolenaar 		    break;
1835b005cd80SBram Moolenaar 		}
1836b005cd80SBram Moolenaar 
1837b005cd80SBram Moolenaar 		if ((li = listitem_alloc()) == NULL)
1838b005cd80SBram Moolenaar 		{
1839b005cd80SBram Moolenaar 		    vim_free(s);
1840b005cd80SBram Moolenaar 		    failed = TRUE;
1841b005cd80SBram Moolenaar 		    break;
1842b005cd80SBram Moolenaar 		}
1843b005cd80SBram Moolenaar 		li->li_tv.v_type = VAR_STRING;
1844b005cd80SBram Moolenaar 		li->li_tv.v_lock = 0;
1845b005cd80SBram Moolenaar 		li->li_tv.vval.v_string = s;
1846b005cd80SBram Moolenaar 		list_append(rettv->vval.v_list, li);
1847b005cd80SBram Moolenaar 
184826262f87SBram Moolenaar 		start = p + 1; // step over newline
1849b005cd80SBram Moolenaar 		if ((++cnt >= maxline && maxline >= 0) || readlen <= 0)
1850b005cd80SBram Moolenaar 		    break;
1851b005cd80SBram Moolenaar 	    }
1852b005cd80SBram Moolenaar 	    else if (*p == NUL)
1853b005cd80SBram Moolenaar 		*p = '\n';
185426262f87SBram Moolenaar 	    // Check for utf8 "bom"; U+FEFF is encoded as EF BB BF.  Do this
185526262f87SBram Moolenaar 	    // when finding the BF and check the previous two bytes.
1856b005cd80SBram Moolenaar 	    else if (*p == 0xbf && enc_utf8 && !binary)
1857b005cd80SBram Moolenaar 	    {
185826262f87SBram Moolenaar 		// Find the two bytes before the 0xbf.	If p is at buf, or buf
185926262f87SBram Moolenaar 		// + 1, these may be in the "prev" string.
1860b005cd80SBram Moolenaar 		char_u back1 = p >= buf + 1 ? p[-1]
1861b005cd80SBram Moolenaar 				     : prevlen >= 1 ? prev[prevlen - 1] : NUL;
1862b005cd80SBram Moolenaar 		char_u back2 = p >= buf + 2 ? p[-2]
1863b005cd80SBram Moolenaar 			  : p == buf + 1 && prevlen >= 1 ? prev[prevlen - 1]
1864b005cd80SBram Moolenaar 			  : prevlen >= 2 ? prev[prevlen - 2] : NUL;
1865b005cd80SBram Moolenaar 
1866b005cd80SBram Moolenaar 		if (back2 == 0xef && back1 == 0xbb)
1867b005cd80SBram Moolenaar 		{
1868b005cd80SBram Moolenaar 		    char_u *dest = p - 2;
1869b005cd80SBram Moolenaar 
187026262f87SBram Moolenaar 		    // Usually a BOM is at the beginning of a file, and so at
187126262f87SBram Moolenaar 		    // the beginning of a line; then we can just step over it.
1872b005cd80SBram Moolenaar 		    if (start == dest)
1873b005cd80SBram Moolenaar 			start = p + 1;
1874b005cd80SBram Moolenaar 		    else
1875b005cd80SBram Moolenaar 		    {
187626262f87SBram Moolenaar 			// have to shuffle buf to close gap
1877b005cd80SBram Moolenaar 			int adjust_prevlen = 0;
1878b005cd80SBram Moolenaar 
1879b005cd80SBram Moolenaar 			if (dest < buf)
1880b005cd80SBram Moolenaar 			{
1881c423ad77SBram Moolenaar 			    // must be 1 or 2
1882c423ad77SBram Moolenaar 			    adjust_prevlen = (int)(buf - dest);
1883b005cd80SBram Moolenaar 			    dest = buf;
1884b005cd80SBram Moolenaar 			}
1885b005cd80SBram Moolenaar 			if (readlen > p - buf + 1)
1886b005cd80SBram Moolenaar 			    mch_memmove(dest, p + 1, readlen - (p - buf) - 1);
1887b005cd80SBram Moolenaar 			readlen -= 3 - adjust_prevlen;
1888b005cd80SBram Moolenaar 			prevlen -= adjust_prevlen;
1889b005cd80SBram Moolenaar 			p = dest - 1;
1890b005cd80SBram Moolenaar 		    }
1891b005cd80SBram Moolenaar 		}
1892b005cd80SBram Moolenaar 	    }
189326262f87SBram Moolenaar 	} // for
1894b005cd80SBram Moolenaar 
1895b005cd80SBram Moolenaar 	if (failed || (cnt >= maxline && maxline >= 0) || readlen <= 0)
1896b005cd80SBram Moolenaar 	    break;
1897b005cd80SBram Moolenaar 	if (start < p)
1898b005cd80SBram Moolenaar 	{
189926262f87SBram Moolenaar 	    // There's part of a line in buf, store it in "prev".
1900b005cd80SBram Moolenaar 	    if (p - start + prevlen >= prevsize)
1901b005cd80SBram Moolenaar 	    {
190226262f87SBram Moolenaar 		// need bigger "prev" buffer
1903b005cd80SBram Moolenaar 		char_u *newprev;
1904b005cd80SBram Moolenaar 
190526262f87SBram Moolenaar 		// A common use case is ordinary text files and "prev" gets a
190626262f87SBram Moolenaar 		// fragment of a line, so the first allocation is made
190726262f87SBram Moolenaar 		// small, to avoid repeatedly 'allocing' large and
190826262f87SBram Moolenaar 		// 'reallocing' small.
1909b005cd80SBram Moolenaar 		if (prevsize == 0)
1910b005cd80SBram Moolenaar 		    prevsize = (long)(p - start);
1911b005cd80SBram Moolenaar 		else
1912b005cd80SBram Moolenaar 		{
1913b005cd80SBram Moolenaar 		    long grow50pc = (prevsize * 3) / 2;
1914b005cd80SBram Moolenaar 		    long growmin  = (long)((p - start) * 2 + prevlen);
1915b005cd80SBram Moolenaar 		    prevsize = grow50pc > growmin ? grow50pc : growmin;
1916b005cd80SBram Moolenaar 		}
1917b005cd80SBram Moolenaar 		newprev = vim_realloc(prev, prevsize);
1918b005cd80SBram Moolenaar 		if (newprev == NULL)
1919b005cd80SBram Moolenaar 		{
1920b005cd80SBram Moolenaar 		    do_outofmem_msg((long_u)prevsize);
1921b005cd80SBram Moolenaar 		    failed = TRUE;
1922b005cd80SBram Moolenaar 		    break;
1923b005cd80SBram Moolenaar 		}
1924b005cd80SBram Moolenaar 		prev = newprev;
1925b005cd80SBram Moolenaar 	    }
192626262f87SBram Moolenaar 	    // Add the line part to end of "prev".
1927b005cd80SBram Moolenaar 	    mch_memmove(prev + prevlen, start, p - start);
1928b005cd80SBram Moolenaar 	    prevlen += (long)(p - start);
1929b005cd80SBram Moolenaar 	}
193026262f87SBram Moolenaar     } // while
1931b005cd80SBram Moolenaar 
193226262f87SBram Moolenaar     // For a negative line count use only the lines at the end of the file,
193326262f87SBram Moolenaar     // free the rest.
1934b005cd80SBram Moolenaar     if (!failed && maxline < 0)
1935b005cd80SBram Moolenaar 	while (cnt > -maxline)
1936b005cd80SBram Moolenaar 	{
1937b005cd80SBram Moolenaar 	    listitem_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first);
1938b005cd80SBram Moolenaar 	    --cnt;
1939b005cd80SBram Moolenaar 	}
1940b005cd80SBram Moolenaar 
1941b005cd80SBram Moolenaar     if (failed)
1942b005cd80SBram Moolenaar     {
1943b005cd80SBram Moolenaar 	// an empty list is returned on error
1944b005cd80SBram Moolenaar 	list_free(rettv->vval.v_list);
1945b005cd80SBram Moolenaar 	rettv_list_alloc(rettv);
1946b005cd80SBram Moolenaar     }
1947b005cd80SBram Moolenaar 
1948b005cd80SBram Moolenaar     vim_free(prev);
1949b005cd80SBram Moolenaar     fclose(fd);
1950b005cd80SBram Moolenaar }
1951b005cd80SBram Moolenaar 
1952b005cd80SBram Moolenaar /*
1953c423ad77SBram Moolenaar  * "readblob()" function
1954c423ad77SBram Moolenaar  */
1955c423ad77SBram Moolenaar     void
f_readblob(typval_T * argvars,typval_T * rettv)1956c423ad77SBram Moolenaar f_readblob(typval_T *argvars, typval_T *rettv)
1957c423ad77SBram Moolenaar {
19584490ec4eSYegappan Lakshmanan     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
19594490ec4eSYegappan Lakshmanan 	return;
19604490ec4eSYegappan Lakshmanan 
1961c423ad77SBram Moolenaar     read_file_or_blob(argvars, rettv, TRUE);
1962c423ad77SBram Moolenaar }
1963c423ad77SBram Moolenaar 
1964c423ad77SBram Moolenaar /*
1965c423ad77SBram Moolenaar  * "readfile()" function
1966c423ad77SBram Moolenaar  */
1967c423ad77SBram Moolenaar     void
f_readfile(typval_T * argvars,typval_T * rettv)1968c423ad77SBram Moolenaar f_readfile(typval_T *argvars, typval_T *rettv)
1969c423ad77SBram Moolenaar {
19704490ec4eSYegappan Lakshmanan     if (in_vim9script()
19714490ec4eSYegappan Lakshmanan 	    && (check_for_nonempty_string_arg(argvars, 0) == FAIL
19724490ec4eSYegappan Lakshmanan 		|| check_for_opt_string_arg(argvars, 1) == FAIL
19734490ec4eSYegappan Lakshmanan 		|| (argvars[1].v_type != VAR_UNKNOWN
19744490ec4eSYegappan Lakshmanan 		    && check_for_opt_number_arg(argvars, 2) == FAIL)))
19754490ec4eSYegappan Lakshmanan 	return;
19764490ec4eSYegappan Lakshmanan 
1977c423ad77SBram Moolenaar     read_file_or_blob(argvars, rettv, FALSE);
1978c423ad77SBram Moolenaar }
1979c423ad77SBram Moolenaar 
1980c423ad77SBram Moolenaar /*
1981b005cd80SBram Moolenaar  * "resolve()" function
1982b005cd80SBram Moolenaar  */
1983b005cd80SBram Moolenaar     void
f_resolve(typval_T * argvars,typval_T * rettv)1984b005cd80SBram Moolenaar f_resolve(typval_T *argvars, typval_T *rettv)
1985b005cd80SBram Moolenaar {
1986b005cd80SBram Moolenaar     char_u	*p;
1987b005cd80SBram Moolenaar #ifdef HAVE_READLINK
1988b005cd80SBram Moolenaar     char_u	*buf = NULL;
1989b005cd80SBram Moolenaar #endif
1990b005cd80SBram Moolenaar 
19914490ec4eSYegappan Lakshmanan     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
19924490ec4eSYegappan Lakshmanan 	return;
19934490ec4eSYegappan Lakshmanan 
1994b005cd80SBram Moolenaar     p = tv_get_string(&argvars[0]);
1995b005cd80SBram Moolenaar #ifdef FEAT_SHORTCUT
1996b005cd80SBram Moolenaar     {
1997b005cd80SBram Moolenaar 	char_u	*v = NULL;
1998b005cd80SBram Moolenaar 
1999b005cd80SBram Moolenaar 	v = mch_resolve_path(p, TRUE);
2000b005cd80SBram Moolenaar 	if (v != NULL)
2001b005cd80SBram Moolenaar 	    rettv->vval.v_string = v;
2002b005cd80SBram Moolenaar 	else
2003b005cd80SBram Moolenaar 	    rettv->vval.v_string = vim_strsave(p);
2004b005cd80SBram Moolenaar     }
2005b005cd80SBram Moolenaar #else
2006b005cd80SBram Moolenaar # ifdef HAVE_READLINK
2007b005cd80SBram Moolenaar     {
2008b005cd80SBram Moolenaar 	char_u	*cpy;
2009b005cd80SBram Moolenaar 	int	len;
2010b005cd80SBram Moolenaar 	char_u	*remain = NULL;
2011b005cd80SBram Moolenaar 	char_u	*q;
2012b005cd80SBram Moolenaar 	int	is_relative_to_current = FALSE;
2013b005cd80SBram Moolenaar 	int	has_trailing_pathsep = FALSE;
2014b005cd80SBram Moolenaar 	int	limit = 100;
2015b005cd80SBram Moolenaar 
2016b005cd80SBram Moolenaar 	p = vim_strsave(p);
201770188f5bSBram Moolenaar 	if (p == NULL)
201870188f5bSBram Moolenaar 	    goto fail;
2019b005cd80SBram Moolenaar 	if (p[0] == '.' && (vim_ispathsep(p[1])
2020b005cd80SBram Moolenaar 				   || (p[1] == '.' && (vim_ispathsep(p[2])))))
2021b005cd80SBram Moolenaar 	    is_relative_to_current = TRUE;
2022b005cd80SBram Moolenaar 
2023b005cd80SBram Moolenaar 	len = STRLEN(p);
202450c4e9e0SBram Moolenaar 	if (len > 1 && after_pathsep(p, p + len))
2025b005cd80SBram Moolenaar 	{
2026b005cd80SBram Moolenaar 	    has_trailing_pathsep = TRUE;
202726262f87SBram Moolenaar 	    p[len - 1] = NUL; // the trailing slash breaks readlink()
2028b005cd80SBram Moolenaar 	}
2029b005cd80SBram Moolenaar 
2030b005cd80SBram Moolenaar 	q = getnextcomp(p);
2031b005cd80SBram Moolenaar 	if (*q != NUL)
2032b005cd80SBram Moolenaar 	{
203326262f87SBram Moolenaar 	    // Separate the first path component in "p", and keep the
203426262f87SBram Moolenaar 	    // remainder (beginning with the path separator).
2035b005cd80SBram Moolenaar 	    remain = vim_strsave(q - 1);
2036b005cd80SBram Moolenaar 	    q[-1] = NUL;
2037b005cd80SBram Moolenaar 	}
2038b005cd80SBram Moolenaar 
2039b005cd80SBram Moolenaar 	buf = alloc(MAXPATHL + 1);
2040b005cd80SBram Moolenaar 	if (buf == NULL)
204170188f5bSBram Moolenaar 	{
204270188f5bSBram Moolenaar 	    vim_free(p);
2043b005cd80SBram Moolenaar 	    goto fail;
204470188f5bSBram Moolenaar 	}
2045b005cd80SBram Moolenaar 
2046b005cd80SBram Moolenaar 	for (;;)
2047b005cd80SBram Moolenaar 	{
2048b005cd80SBram Moolenaar 	    for (;;)
2049b005cd80SBram Moolenaar 	    {
2050b005cd80SBram Moolenaar 		len = readlink((char *)p, (char *)buf, MAXPATHL);
2051b005cd80SBram Moolenaar 		if (len <= 0)
2052b005cd80SBram Moolenaar 		    break;
2053b005cd80SBram Moolenaar 		buf[len] = NUL;
2054b005cd80SBram Moolenaar 
2055b005cd80SBram Moolenaar 		if (limit-- == 0)
2056b005cd80SBram Moolenaar 		{
2057b005cd80SBram Moolenaar 		    vim_free(p);
2058b005cd80SBram Moolenaar 		    vim_free(remain);
2059b005cd80SBram Moolenaar 		    emsg(_("E655: Too many symbolic links (cycle?)"));
2060b005cd80SBram Moolenaar 		    rettv->vval.v_string = NULL;
2061b005cd80SBram Moolenaar 		    goto fail;
2062b005cd80SBram Moolenaar 		}
2063b005cd80SBram Moolenaar 
206426262f87SBram Moolenaar 		// Ensure that the result will have a trailing path separator
206526262f87SBram Moolenaar 		// if the argument has one.
2066b005cd80SBram Moolenaar 		if (remain == NULL && has_trailing_pathsep)
2067b005cd80SBram Moolenaar 		    add_pathsep(buf);
2068b005cd80SBram Moolenaar 
206926262f87SBram Moolenaar 		// Separate the first path component in the link value and
207026262f87SBram Moolenaar 		// concatenate the remainders.
2071b005cd80SBram Moolenaar 		q = getnextcomp(vim_ispathsep(*buf) ? buf + 1 : buf);
2072b005cd80SBram Moolenaar 		if (*q != NUL)
2073b005cd80SBram Moolenaar 		{
2074b005cd80SBram Moolenaar 		    if (remain == NULL)
2075b005cd80SBram Moolenaar 			remain = vim_strsave(q - 1);
2076b005cd80SBram Moolenaar 		    else
2077b005cd80SBram Moolenaar 		    {
2078b005cd80SBram Moolenaar 			cpy = concat_str(q - 1, remain);
2079b005cd80SBram Moolenaar 			if (cpy != NULL)
2080b005cd80SBram Moolenaar 			{
2081b005cd80SBram Moolenaar 			    vim_free(remain);
2082b005cd80SBram Moolenaar 			    remain = cpy;
2083b005cd80SBram Moolenaar 			}
2084b005cd80SBram Moolenaar 		    }
2085b005cd80SBram Moolenaar 		    q[-1] = NUL;
2086b005cd80SBram Moolenaar 		}
2087b005cd80SBram Moolenaar 
2088b005cd80SBram Moolenaar 		q = gettail(p);
2089b005cd80SBram Moolenaar 		if (q > p && *q == NUL)
2090b005cd80SBram Moolenaar 		{
209126262f87SBram Moolenaar 		    // Ignore trailing path separator.
2092b005cd80SBram Moolenaar 		    q[-1] = NUL;
2093b005cd80SBram Moolenaar 		    q = gettail(p);
2094b005cd80SBram Moolenaar 		}
2095b005cd80SBram Moolenaar 		if (q > p && !mch_isFullName(buf))
2096b005cd80SBram Moolenaar 		{
209726262f87SBram Moolenaar 		    // symlink is relative to directory of argument
2098b005cd80SBram Moolenaar 		    cpy = alloc(STRLEN(p) + STRLEN(buf) + 1);
2099b005cd80SBram Moolenaar 		    if (cpy != NULL)
2100b005cd80SBram Moolenaar 		    {
2101b005cd80SBram Moolenaar 			STRCPY(cpy, p);
2102b005cd80SBram Moolenaar 			STRCPY(gettail(cpy), buf);
2103b005cd80SBram Moolenaar 			vim_free(p);
2104b005cd80SBram Moolenaar 			p = cpy;
2105b005cd80SBram Moolenaar 		    }
2106b005cd80SBram Moolenaar 		}
2107b005cd80SBram Moolenaar 		else
2108b005cd80SBram Moolenaar 		{
2109b005cd80SBram Moolenaar 		    vim_free(p);
2110b005cd80SBram Moolenaar 		    p = vim_strsave(buf);
2111b005cd80SBram Moolenaar 		}
2112b005cd80SBram Moolenaar 	    }
2113b005cd80SBram Moolenaar 
2114b005cd80SBram Moolenaar 	    if (remain == NULL)
2115b005cd80SBram Moolenaar 		break;
2116b005cd80SBram Moolenaar 
211726262f87SBram Moolenaar 	    // Append the first path component of "remain" to "p".
2118b005cd80SBram Moolenaar 	    q = getnextcomp(remain + 1);
2119b005cd80SBram Moolenaar 	    len = q - remain - (*q != NUL);
2120b005cd80SBram Moolenaar 	    cpy = vim_strnsave(p, STRLEN(p) + len);
2121b005cd80SBram Moolenaar 	    if (cpy != NULL)
2122b005cd80SBram Moolenaar 	    {
2123b005cd80SBram Moolenaar 		STRNCAT(cpy, remain, len);
2124b005cd80SBram Moolenaar 		vim_free(p);
2125b005cd80SBram Moolenaar 		p = cpy;
2126b005cd80SBram Moolenaar 	    }
212726262f87SBram Moolenaar 	    // Shorten "remain".
2128b005cd80SBram Moolenaar 	    if (*q != NUL)
2129b005cd80SBram Moolenaar 		STRMOVE(remain, q - 1);
2130b005cd80SBram Moolenaar 	    else
2131b005cd80SBram Moolenaar 		VIM_CLEAR(remain);
2132b005cd80SBram Moolenaar 	}
2133b005cd80SBram Moolenaar 
213426262f87SBram Moolenaar 	// If the result is a relative path name, make it explicitly relative to
213526262f87SBram Moolenaar 	// the current directory if and only if the argument had this form.
2136b005cd80SBram Moolenaar 	if (!vim_ispathsep(*p))
2137b005cd80SBram Moolenaar 	{
2138b005cd80SBram Moolenaar 	    if (is_relative_to_current
2139b005cd80SBram Moolenaar 		    && *p != NUL
2140b005cd80SBram Moolenaar 		    && !(p[0] == '.'
2141b005cd80SBram Moolenaar 			&& (p[1] == NUL
2142b005cd80SBram Moolenaar 			    || vim_ispathsep(p[1])
2143b005cd80SBram Moolenaar 			    || (p[1] == '.'
2144b005cd80SBram Moolenaar 				&& (p[2] == NUL
2145b005cd80SBram Moolenaar 				    || vim_ispathsep(p[2]))))))
2146b005cd80SBram Moolenaar 	    {
214726262f87SBram Moolenaar 		// Prepend "./".
2148b005cd80SBram Moolenaar 		cpy = concat_str((char_u *)"./", p);
2149b005cd80SBram Moolenaar 		if (cpy != NULL)
2150b005cd80SBram Moolenaar 		{
2151b005cd80SBram Moolenaar 		    vim_free(p);
2152b005cd80SBram Moolenaar 		    p = cpy;
2153b005cd80SBram Moolenaar 		}
2154b005cd80SBram Moolenaar 	    }
2155b005cd80SBram Moolenaar 	    else if (!is_relative_to_current)
2156b005cd80SBram Moolenaar 	    {
215726262f87SBram Moolenaar 		// Strip leading "./".
2158b005cd80SBram Moolenaar 		q = p;
2159b005cd80SBram Moolenaar 		while (q[0] == '.' && vim_ispathsep(q[1]))
2160b005cd80SBram Moolenaar 		    q += 2;
2161b005cd80SBram Moolenaar 		if (q > p)
2162b005cd80SBram Moolenaar 		    STRMOVE(p, p + 2);
2163b005cd80SBram Moolenaar 	    }
2164b005cd80SBram Moolenaar 	}
2165b005cd80SBram Moolenaar 
216626262f87SBram Moolenaar 	// Ensure that the result will have no trailing path separator
216726262f87SBram Moolenaar 	// if the argument had none.  But keep "/" or "//".
2168b005cd80SBram Moolenaar 	if (!has_trailing_pathsep)
2169b005cd80SBram Moolenaar 	{
2170b005cd80SBram Moolenaar 	    q = p + STRLEN(p);
2171b005cd80SBram Moolenaar 	    if (after_pathsep(p, q))
2172b005cd80SBram Moolenaar 		*gettail_sep(p) = NUL;
2173b005cd80SBram Moolenaar 	}
2174b005cd80SBram Moolenaar 
2175b005cd80SBram Moolenaar 	rettv->vval.v_string = p;
2176b005cd80SBram Moolenaar     }
2177b005cd80SBram Moolenaar # else
2178b005cd80SBram Moolenaar     rettv->vval.v_string = vim_strsave(p);
2179b005cd80SBram Moolenaar # endif
2180b005cd80SBram Moolenaar #endif
2181b005cd80SBram Moolenaar 
2182b005cd80SBram Moolenaar     simplify_filename(rettv->vval.v_string);
2183b005cd80SBram Moolenaar 
2184b005cd80SBram Moolenaar #ifdef HAVE_READLINK
2185b005cd80SBram Moolenaar fail:
2186b005cd80SBram Moolenaar     vim_free(buf);
2187b005cd80SBram Moolenaar #endif
2188b005cd80SBram Moolenaar     rettv->v_type = VAR_STRING;
2189b005cd80SBram Moolenaar }
2190b005cd80SBram Moolenaar 
2191b005cd80SBram Moolenaar /*
2192b005cd80SBram Moolenaar  * "tempname()" function
2193b005cd80SBram Moolenaar  */
2194b005cd80SBram Moolenaar     void
f_tempname(typval_T * argvars UNUSED,typval_T * rettv)2195b005cd80SBram Moolenaar f_tempname(typval_T *argvars UNUSED, typval_T *rettv)
2196b005cd80SBram Moolenaar {
2197b005cd80SBram Moolenaar     static int	x = 'A';
2198b005cd80SBram Moolenaar 
2199b005cd80SBram Moolenaar     rettv->v_type = VAR_STRING;
2200b005cd80SBram Moolenaar     rettv->vval.v_string = vim_tempname(x, FALSE);
2201b005cd80SBram Moolenaar 
220226262f87SBram Moolenaar     // Advance 'x' to use A-Z and 0-9, so that there are at least 34 different
220326262f87SBram Moolenaar     // names.  Skip 'I' and 'O', they are used for shell redirection.
2204b005cd80SBram Moolenaar     do
2205b005cd80SBram Moolenaar     {
2206b005cd80SBram Moolenaar 	if (x == 'Z')
2207b005cd80SBram Moolenaar 	    x = '0';
2208b005cd80SBram Moolenaar 	else if (x == '9')
2209b005cd80SBram Moolenaar 	    x = 'A';
2210b005cd80SBram Moolenaar 	else
2211b005cd80SBram Moolenaar 	{
2212b005cd80SBram Moolenaar #ifdef EBCDIC
2213b005cd80SBram Moolenaar 	    if (x == 'I')
2214b005cd80SBram Moolenaar 		x = 'J';
2215b005cd80SBram Moolenaar 	    else if (x == 'R')
2216b005cd80SBram Moolenaar 		x = 'S';
2217b005cd80SBram Moolenaar 	    else
2218b005cd80SBram Moolenaar #endif
2219b005cd80SBram Moolenaar 		++x;
2220b005cd80SBram Moolenaar 	}
2221b005cd80SBram Moolenaar     } while (x == 'I' || x == 'O');
2222b005cd80SBram Moolenaar }
2223b005cd80SBram Moolenaar 
2224b005cd80SBram Moolenaar /*
2225b005cd80SBram Moolenaar  * "writefile()" function
2226b005cd80SBram Moolenaar  */
2227b005cd80SBram Moolenaar     void
f_writefile(typval_T * argvars,typval_T * rettv)2228b005cd80SBram Moolenaar f_writefile(typval_T *argvars, typval_T *rettv)
2229b005cd80SBram Moolenaar {
2230b005cd80SBram Moolenaar     int		binary = FALSE;
2231b005cd80SBram Moolenaar     int		append = FALSE;
2232b005cd80SBram Moolenaar #ifdef HAVE_FSYNC
2233b005cd80SBram Moolenaar     int		do_fsync = p_fs;
2234b005cd80SBram Moolenaar #endif
2235b005cd80SBram Moolenaar     char_u	*fname;
2236b005cd80SBram Moolenaar     FILE	*fd;
2237b005cd80SBram Moolenaar     int		ret = 0;
2238b005cd80SBram Moolenaar     listitem_T	*li;
2239b005cd80SBram Moolenaar     list_T	*list = NULL;
2240b005cd80SBram Moolenaar     blob_T	*blob = NULL;
2241b005cd80SBram Moolenaar 
2242b005cd80SBram Moolenaar     rettv->vval.v_number = -1;
2243b005cd80SBram Moolenaar     if (check_secure())
2244b005cd80SBram Moolenaar 	return;
2245b005cd80SBram Moolenaar 
22460ad871dcSYegappan Lakshmanan     if (in_vim9script()
22470ad871dcSYegappan Lakshmanan 	    && (check_for_list_or_blob_arg(argvars, 0) == FAIL
22480ad871dcSYegappan Lakshmanan 		|| check_for_string_arg(argvars, 1) == FAIL
22490ad871dcSYegappan Lakshmanan 		|| check_for_opt_string_arg(argvars, 2) == FAIL))
22500ad871dcSYegappan Lakshmanan 	return;
22510ad871dcSYegappan Lakshmanan 
2252b005cd80SBram Moolenaar     if (argvars[0].v_type == VAR_LIST)
2253b005cd80SBram Moolenaar     {
2254b005cd80SBram Moolenaar 	list = argvars[0].vval.v_list;
2255b005cd80SBram Moolenaar 	if (list == NULL)
2256b005cd80SBram Moolenaar 	    return;
22577e9f351bSBram Moolenaar 	CHECK_LIST_MATERIALIZE(list);
2258aeea7215SBram Moolenaar 	FOR_ALL_LIST_ITEMS(list, li)
2259b005cd80SBram Moolenaar 	    if (tv_get_string_chk(&li->li_tv) == NULL)
2260b005cd80SBram Moolenaar 		return;
2261b005cd80SBram Moolenaar     }
2262b005cd80SBram Moolenaar     else if (argvars[0].v_type == VAR_BLOB)
2263b005cd80SBram Moolenaar     {
2264b005cd80SBram Moolenaar 	blob = argvars[0].vval.v_blob;
2265b005cd80SBram Moolenaar 	if (blob == NULL)
2266b005cd80SBram Moolenaar 	    return;
2267b005cd80SBram Moolenaar     }
2268b005cd80SBram Moolenaar     else
2269b005cd80SBram Moolenaar     {
227018a2b87cSBram Moolenaar 	semsg(_(e_invarg2),
227118a2b87cSBram Moolenaar 		_("writefile() first argument must be a List or a Blob"));
2272b005cd80SBram Moolenaar 	return;
2273b005cd80SBram Moolenaar     }
2274b005cd80SBram Moolenaar 
2275b005cd80SBram Moolenaar     if (argvars[2].v_type != VAR_UNKNOWN)
2276b005cd80SBram Moolenaar     {
2277b005cd80SBram Moolenaar 	char_u *arg2 = tv_get_string_chk(&argvars[2]);
2278b005cd80SBram Moolenaar 
2279b005cd80SBram Moolenaar 	if (arg2 == NULL)
2280b005cd80SBram Moolenaar 	    return;
2281b005cd80SBram Moolenaar 	if (vim_strchr(arg2, 'b') != NULL)
2282b005cd80SBram Moolenaar 	    binary = TRUE;
2283b005cd80SBram Moolenaar 	if (vim_strchr(arg2, 'a') != NULL)
2284b005cd80SBram Moolenaar 	    append = TRUE;
2285b005cd80SBram Moolenaar #ifdef HAVE_FSYNC
2286b005cd80SBram Moolenaar 	if (vim_strchr(arg2, 's') != NULL)
2287b005cd80SBram Moolenaar 	    do_fsync = TRUE;
2288b005cd80SBram Moolenaar 	else if (vim_strchr(arg2, 'S') != NULL)
2289b005cd80SBram Moolenaar 	    do_fsync = FALSE;
2290b005cd80SBram Moolenaar #endif
2291b005cd80SBram Moolenaar     }
2292b005cd80SBram Moolenaar 
2293b005cd80SBram Moolenaar     fname = tv_get_string_chk(&argvars[1]);
2294b005cd80SBram Moolenaar     if (fname == NULL)
2295b005cd80SBram Moolenaar 	return;
2296b005cd80SBram Moolenaar 
229726262f87SBram Moolenaar     // Always open the file in binary mode, library functions have a mind of
229826262f87SBram Moolenaar     // their own about CR-LF conversion.
2299b005cd80SBram Moolenaar     if (*fname == NUL || (fd = mch_fopen((char *)fname,
2300b005cd80SBram Moolenaar 				      append ? APPENDBIN : WRITEBIN)) == NULL)
2301b005cd80SBram Moolenaar     {
2302b005cd80SBram Moolenaar 	semsg(_(e_notcreate), *fname == NUL ? (char_u *)_("<empty>") : fname);
2303b005cd80SBram Moolenaar 	ret = -1;
2304b005cd80SBram Moolenaar     }
2305b005cd80SBram Moolenaar     else if (blob)
2306b005cd80SBram Moolenaar     {
2307b005cd80SBram Moolenaar 	if (write_blob(fd, blob) == FAIL)
2308b005cd80SBram Moolenaar 	    ret = -1;
2309b005cd80SBram Moolenaar #ifdef HAVE_FSYNC
2310b005cd80SBram Moolenaar 	else if (do_fsync)
2311b005cd80SBram Moolenaar 	    // Ignore the error, the user wouldn't know what to do about it.
2312b005cd80SBram Moolenaar 	    // May happen for a device.
2313b005cd80SBram Moolenaar 	    vim_ignored = vim_fsync(fileno(fd));
2314b005cd80SBram Moolenaar #endif
2315b005cd80SBram Moolenaar 	fclose(fd);
2316b005cd80SBram Moolenaar     }
2317b005cd80SBram Moolenaar     else
2318b005cd80SBram Moolenaar     {
2319b005cd80SBram Moolenaar 	if (write_list(fd, list, binary) == FAIL)
2320b005cd80SBram Moolenaar 	    ret = -1;
2321b005cd80SBram Moolenaar #ifdef HAVE_FSYNC
2322b005cd80SBram Moolenaar 	else if (do_fsync)
232326262f87SBram Moolenaar 	    // Ignore the error, the user wouldn't know what to do about it.
232426262f87SBram Moolenaar 	    // May happen for a device.
2325b005cd80SBram Moolenaar 	    vim_ignored = vim_fsync(fileno(fd));
2326b005cd80SBram Moolenaar #endif
2327b005cd80SBram Moolenaar 	fclose(fd);
2328b005cd80SBram Moolenaar     }
2329b005cd80SBram Moolenaar 
2330b005cd80SBram Moolenaar     rettv->vval.v_number = ret;
2331b005cd80SBram Moolenaar }
2332b005cd80SBram Moolenaar 
2333b005cd80SBram Moolenaar #endif // FEAT_EVAL
2334b005cd80SBram Moolenaar 
2335b005cd80SBram Moolenaar #if defined(FEAT_BROWSE) || defined(PROTO)
2336b005cd80SBram Moolenaar /*
2337b005cd80SBram Moolenaar  * Generic browse function.  Calls gui_mch_browse() when possible.
2338b005cd80SBram Moolenaar  * Later this may pop-up a non-GUI file selector (external command?).
2339b005cd80SBram Moolenaar  */
2340b005cd80SBram Moolenaar     char_u *
do_browse(int flags,char_u * title,char_u * dflt,char_u * ext,char_u * initdir,char_u * filter,buf_T * buf)2341b005cd80SBram Moolenaar do_browse(
234226262f87SBram Moolenaar     int		flags,		// BROWSE_SAVE and BROWSE_DIR
234326262f87SBram Moolenaar     char_u	*title,		// title for the window
234426262f87SBram Moolenaar     char_u	*dflt,		// default file name (may include directory)
234526262f87SBram Moolenaar     char_u	*ext,		// extension added
234626262f87SBram Moolenaar     char_u	*initdir,	// initial directory, NULL for current dir or
234726262f87SBram Moolenaar 				// when using path from "dflt"
234826262f87SBram Moolenaar     char_u	*filter,	// file name filter
234926262f87SBram Moolenaar     buf_T	*buf)		// buffer to read/write for
2350b005cd80SBram Moolenaar {
2351b005cd80SBram Moolenaar     char_u		*fname;
235226262f87SBram Moolenaar     static char_u	*last_dir = NULL;    // last used directory
2353b005cd80SBram Moolenaar     char_u		*tofree = NULL;
2354e1004401SBram Moolenaar     int			save_cmod_flags = cmdmod.cmod_flags;
2355b005cd80SBram Moolenaar 
235626262f87SBram Moolenaar     // Must turn off browse to avoid that autocommands will get the
235726262f87SBram Moolenaar     // flag too!
2358e1004401SBram Moolenaar     cmdmod.cmod_flags &= ~CMOD_BROWSE;
2359b005cd80SBram Moolenaar 
2360b005cd80SBram Moolenaar     if (title == NULL || *title == NUL)
2361b005cd80SBram Moolenaar     {
2362b005cd80SBram Moolenaar 	if (flags & BROWSE_DIR)
2363b005cd80SBram Moolenaar 	    title = (char_u *)_("Select Directory dialog");
2364b005cd80SBram Moolenaar 	else if (flags & BROWSE_SAVE)
2365b005cd80SBram Moolenaar 	    title = (char_u *)_("Save File dialog");
2366b005cd80SBram Moolenaar 	else
2367b005cd80SBram Moolenaar 	    title = (char_u *)_("Open File dialog");
2368b005cd80SBram Moolenaar     }
2369b005cd80SBram Moolenaar 
237026262f87SBram Moolenaar     // When no directory specified, use default file name, default dir, buffer
237126262f87SBram Moolenaar     // dir, last dir or current dir
2372b005cd80SBram Moolenaar     if ((initdir == NULL || *initdir == NUL) && dflt != NULL && *dflt != NUL)
2373b005cd80SBram Moolenaar     {
237426262f87SBram Moolenaar 	if (mch_isdir(dflt))		// default file name is a directory
2375b005cd80SBram Moolenaar 	{
2376b005cd80SBram Moolenaar 	    initdir = dflt;
2377b005cd80SBram Moolenaar 	    dflt = NULL;
2378b005cd80SBram Moolenaar 	}
237926262f87SBram Moolenaar 	else if (gettail(dflt) != dflt)	// default file name includes a path
2380b005cd80SBram Moolenaar 	{
2381b005cd80SBram Moolenaar 	    tofree = vim_strsave(dflt);
2382b005cd80SBram Moolenaar 	    if (tofree != NULL)
2383b005cd80SBram Moolenaar 	    {
2384b005cd80SBram Moolenaar 		initdir = tofree;
2385b005cd80SBram Moolenaar 		*gettail(initdir) = NUL;
2386b005cd80SBram Moolenaar 		dflt = gettail(dflt);
2387b005cd80SBram Moolenaar 	    }
2388b005cd80SBram Moolenaar 	}
2389b005cd80SBram Moolenaar     }
2390b005cd80SBram Moolenaar 
2391b005cd80SBram Moolenaar     if (initdir == NULL || *initdir == NUL)
2392b005cd80SBram Moolenaar     {
239326262f87SBram Moolenaar 	// When 'browsedir' is a directory, use it
2394b005cd80SBram Moolenaar 	if (STRCMP(p_bsdir, "last") != 0
2395b005cd80SBram Moolenaar 		&& STRCMP(p_bsdir, "buffer") != 0
2396b005cd80SBram Moolenaar 		&& STRCMP(p_bsdir, "current") != 0
2397b005cd80SBram Moolenaar 		&& mch_isdir(p_bsdir))
2398b005cd80SBram Moolenaar 	    initdir = p_bsdir;
239926262f87SBram Moolenaar 	// When saving or 'browsedir' is "buffer", use buffer fname
2400b005cd80SBram Moolenaar 	else if (((flags & BROWSE_SAVE) || *p_bsdir == 'b')
2401b005cd80SBram Moolenaar 		&& buf != NULL && buf->b_ffname != NULL)
2402b005cd80SBram Moolenaar 	{
2403b005cd80SBram Moolenaar 	    if (dflt == NULL || *dflt == NUL)
2404b005cd80SBram Moolenaar 		dflt = gettail(curbuf->b_ffname);
2405b005cd80SBram Moolenaar 	    tofree = vim_strsave(curbuf->b_ffname);
2406b005cd80SBram Moolenaar 	    if (tofree != NULL)
2407b005cd80SBram Moolenaar 	    {
2408b005cd80SBram Moolenaar 		initdir = tofree;
2409b005cd80SBram Moolenaar 		*gettail(initdir) = NUL;
2410b005cd80SBram Moolenaar 	    }
2411b005cd80SBram Moolenaar 	}
241226262f87SBram Moolenaar 	// When 'browsedir' is "last", use dir from last browse
2413b005cd80SBram Moolenaar 	else if (*p_bsdir == 'l')
2414b005cd80SBram Moolenaar 	    initdir = last_dir;
241526262f87SBram Moolenaar 	// When 'browsedir is "current", use current directory.  This is the
241626262f87SBram Moolenaar 	// default already, leave initdir empty.
2417b005cd80SBram Moolenaar     }
2418b005cd80SBram Moolenaar 
2419b005cd80SBram Moolenaar # ifdef FEAT_GUI
242026262f87SBram Moolenaar     if (gui.in_use)		// when this changes, also adjust f_has()!
2421b005cd80SBram Moolenaar     {
2422b005cd80SBram Moolenaar 	if (filter == NULL
2423b005cd80SBram Moolenaar #  ifdef FEAT_EVAL
2424b005cd80SBram Moolenaar 		&& (filter = get_var_value((char_u *)"b:browsefilter")) == NULL
2425b005cd80SBram Moolenaar 		&& (filter = get_var_value((char_u *)"g:browsefilter")) == NULL
2426b005cd80SBram Moolenaar #  endif
2427b005cd80SBram Moolenaar 	)
2428b005cd80SBram Moolenaar 	    filter = BROWSE_FILTER_DEFAULT;
2429b005cd80SBram Moolenaar 	if (flags & BROWSE_DIR)
2430b005cd80SBram Moolenaar 	{
2431b005cd80SBram Moolenaar #  if defined(FEAT_GUI_GTK) || defined(MSWIN)
243226262f87SBram Moolenaar 	    // For systems that have a directory dialog.
2433b005cd80SBram Moolenaar 	    fname = gui_mch_browsedir(title, initdir);
2434b005cd80SBram Moolenaar #  else
243526262f87SBram Moolenaar 	    // Generic solution for selecting a directory: select a file and
243626262f87SBram Moolenaar 	    // remove the file name.
2437b005cd80SBram Moolenaar 	    fname = gui_mch_browse(0, title, dflt, ext, initdir, (char_u *)"");
2438b005cd80SBram Moolenaar #  endif
2439b005cd80SBram Moolenaar #  if !defined(FEAT_GUI_GTK)
244026262f87SBram Moolenaar 	    // Win32 adds a dummy file name, others return an arbitrary file
244126262f87SBram Moolenaar 	    // name.  GTK+ 2 returns only the directory,
2442b005cd80SBram Moolenaar 	    if (fname != NULL && *fname != NUL && !mch_isdir(fname))
2443b005cd80SBram Moolenaar 	    {
244426262f87SBram Moolenaar 		// Remove the file name.
2445b005cd80SBram Moolenaar 		char_u	    *tail = gettail_sep(fname);
2446b005cd80SBram Moolenaar 
2447b005cd80SBram Moolenaar 		if (tail == fname)
244826262f87SBram Moolenaar 		    *tail++ = '.';	// use current dir
2449b005cd80SBram Moolenaar 		*tail = NUL;
2450b005cd80SBram Moolenaar 	    }
2451b005cd80SBram Moolenaar #  endif
2452b005cd80SBram Moolenaar 	}
2453b005cd80SBram Moolenaar 	else
2454b005cd80SBram Moolenaar 	    fname = gui_mch_browse(flags & BROWSE_SAVE,
2455b005cd80SBram Moolenaar 			       title, dflt, ext, initdir, (char_u *)_(filter));
2456b005cd80SBram Moolenaar 
245726262f87SBram Moolenaar 	// We hang around in the dialog for a while, the user might do some
245826262f87SBram Moolenaar 	// things to our files.  The Win32 dialog allows deleting or renaming
245926262f87SBram Moolenaar 	// a file, check timestamps.
2460b005cd80SBram Moolenaar 	need_check_timestamps = TRUE;
2461b005cd80SBram Moolenaar 	did_check_timestamps = FALSE;
2462b005cd80SBram Moolenaar     }
2463b005cd80SBram Moolenaar     else
2464b005cd80SBram Moolenaar # endif
2465b005cd80SBram Moolenaar     {
246626262f87SBram Moolenaar 	// TODO: non-GUI file selector here
2467b005cd80SBram Moolenaar 	emsg(_("E338: Sorry, no file browser in console mode"));
2468b005cd80SBram Moolenaar 	fname = NULL;
2469b005cd80SBram Moolenaar     }
2470b005cd80SBram Moolenaar 
247126262f87SBram Moolenaar     // keep the directory for next time
2472b005cd80SBram Moolenaar     if (fname != NULL)
2473b005cd80SBram Moolenaar     {
2474b005cd80SBram Moolenaar 	vim_free(last_dir);
2475b005cd80SBram Moolenaar 	last_dir = vim_strsave(fname);
2476b005cd80SBram Moolenaar 	if (last_dir != NULL && !(flags & BROWSE_DIR))
2477b005cd80SBram Moolenaar 	{
2478b005cd80SBram Moolenaar 	    *gettail(last_dir) = NUL;
2479b005cd80SBram Moolenaar 	    if (*last_dir == NUL)
2480b005cd80SBram Moolenaar 	    {
248126262f87SBram Moolenaar 		// filename only returned, must be in current dir
2482b005cd80SBram Moolenaar 		vim_free(last_dir);
2483b005cd80SBram Moolenaar 		last_dir = alloc(MAXPATHL);
2484b005cd80SBram Moolenaar 		if (last_dir != NULL)
2485b005cd80SBram Moolenaar 		    mch_dirname(last_dir, MAXPATHL);
2486b005cd80SBram Moolenaar 	    }
2487b005cd80SBram Moolenaar 	}
2488b005cd80SBram Moolenaar     }
2489b005cd80SBram Moolenaar 
2490b005cd80SBram Moolenaar     vim_free(tofree);
2491e1004401SBram Moolenaar     cmdmod.cmod_flags = save_cmod_flags;
2492b005cd80SBram Moolenaar 
2493b005cd80SBram Moolenaar     return fname;
2494b005cd80SBram Moolenaar }
2495b005cd80SBram Moolenaar #endif
2496b005cd80SBram Moolenaar 
2497b005cd80SBram Moolenaar #if defined(FEAT_EVAL) || defined(PROTO)
2498b005cd80SBram Moolenaar 
2499b005cd80SBram Moolenaar /*
2500b005cd80SBram Moolenaar  * "browse(save, title, initdir, default)" function
2501b005cd80SBram Moolenaar  */
2502b005cd80SBram Moolenaar     void
f_browse(typval_T * argvars UNUSED,typval_T * rettv)2503b005cd80SBram Moolenaar f_browse(typval_T *argvars UNUSED, typval_T *rettv)
2504b005cd80SBram Moolenaar {
2505b005cd80SBram Moolenaar # ifdef FEAT_BROWSE
2506b005cd80SBram Moolenaar     int		save;
2507b005cd80SBram Moolenaar     char_u	*title;
2508b005cd80SBram Moolenaar     char_u	*initdir;
2509b005cd80SBram Moolenaar     char_u	*defname;
2510b005cd80SBram Moolenaar     char_u	buf[NUMBUFLEN];
2511b005cd80SBram Moolenaar     char_u	buf2[NUMBUFLEN];
2512b005cd80SBram Moolenaar     int		error = FALSE;
2513b005cd80SBram Moolenaar 
2514f28f2ac4SBram Moolenaar     if (in_vim9script()
251583494b4aSYegappan Lakshmanan 	    && (check_for_bool_arg(argvars, 0) == FAIL
251683494b4aSYegappan Lakshmanan 		|| check_for_string_arg(argvars, 1) == FAIL
251732105ae8SBram Moolenaar 		|| check_for_string_arg(argvars, 2) == FAIL
251832105ae8SBram Moolenaar 		|| check_for_string_arg(argvars, 3) == FAIL))
2519f28f2ac4SBram Moolenaar 	return;
252083494b4aSYegappan Lakshmanan 
2521b005cd80SBram Moolenaar     save = (int)tv_get_number_chk(&argvars[0], &error);
2522b005cd80SBram Moolenaar     title = tv_get_string_chk(&argvars[1]);
2523b005cd80SBram Moolenaar     initdir = tv_get_string_buf_chk(&argvars[2], buf);
2524b005cd80SBram Moolenaar     defname = tv_get_string_buf_chk(&argvars[3], buf2);
2525b005cd80SBram Moolenaar 
2526b005cd80SBram Moolenaar     if (error || title == NULL || initdir == NULL || defname == NULL)
2527b005cd80SBram Moolenaar 	rettv->vval.v_string = NULL;
2528b005cd80SBram Moolenaar     else
2529b005cd80SBram Moolenaar 	rettv->vval.v_string =
2530b005cd80SBram Moolenaar 		 do_browse(save ? BROWSE_SAVE : 0,
2531b005cd80SBram Moolenaar 				 title, defname, NULL, initdir, NULL, curbuf);
2532b005cd80SBram Moolenaar # else
2533b005cd80SBram Moolenaar     rettv->vval.v_string = NULL;
2534b005cd80SBram Moolenaar # endif
2535b005cd80SBram Moolenaar     rettv->v_type = VAR_STRING;
2536b005cd80SBram Moolenaar }
2537b005cd80SBram Moolenaar 
2538b005cd80SBram Moolenaar /*
2539b005cd80SBram Moolenaar  * "browsedir(title, initdir)" function
2540b005cd80SBram Moolenaar  */
2541b005cd80SBram Moolenaar     void
f_browsedir(typval_T * argvars UNUSED,typval_T * rettv)2542b005cd80SBram Moolenaar f_browsedir(typval_T *argvars UNUSED, typval_T *rettv)
2543b005cd80SBram Moolenaar {
2544b005cd80SBram Moolenaar # ifdef FEAT_BROWSE
2545b005cd80SBram Moolenaar     char_u	*title;
2546b005cd80SBram Moolenaar     char_u	*initdir;
2547b005cd80SBram Moolenaar     char_u	buf[NUMBUFLEN];
2548b005cd80SBram Moolenaar 
25494490ec4eSYegappan Lakshmanan     if (in_vim9script()
25504490ec4eSYegappan Lakshmanan 	    && (check_for_string_arg(argvars, 0) == FAIL
25514490ec4eSYegappan Lakshmanan 		|| check_for_string_arg(argvars, 1) == FAIL))
25524490ec4eSYegappan Lakshmanan 	return;
25534490ec4eSYegappan Lakshmanan 
2554b005cd80SBram Moolenaar     title = tv_get_string_chk(&argvars[0]);
2555b005cd80SBram Moolenaar     initdir = tv_get_string_buf_chk(&argvars[1], buf);
2556b005cd80SBram Moolenaar 
2557b005cd80SBram Moolenaar     if (title == NULL || initdir == NULL)
2558b005cd80SBram Moolenaar 	rettv->vval.v_string = NULL;
2559b005cd80SBram Moolenaar     else
2560b005cd80SBram Moolenaar 	rettv->vval.v_string = do_browse(BROWSE_DIR,
2561b005cd80SBram Moolenaar 				    title, NULL, NULL, initdir, NULL, curbuf);
2562b005cd80SBram Moolenaar # else
2563b005cd80SBram Moolenaar     rettv->vval.v_string = NULL;
2564b005cd80SBram Moolenaar # endif
2565b005cd80SBram Moolenaar     rettv->v_type = VAR_STRING;
2566b005cd80SBram Moolenaar }
2567b005cd80SBram Moolenaar 
2568b005cd80SBram Moolenaar #endif // FEAT_EVAL
256926262f87SBram Moolenaar 
257026262f87SBram Moolenaar /*
257126262f87SBram Moolenaar  * Replace home directory by "~" in each space or comma separated file name in
257226262f87SBram Moolenaar  * 'src'.
257326262f87SBram Moolenaar  * If anything fails (except when out of space) dst equals src.
257426262f87SBram Moolenaar  */
257526262f87SBram Moolenaar     void
home_replace(buf_T * buf,char_u * src,char_u * dst,int dstlen,int one)257626262f87SBram Moolenaar home_replace(
257726262f87SBram Moolenaar     buf_T	*buf,	// when not NULL, check for help files
257826262f87SBram Moolenaar     char_u	*src,	// input file name
257926262f87SBram Moolenaar     char_u	*dst,	// where to put the result
258026262f87SBram Moolenaar     int		dstlen,	// maximum length of the result
258126262f87SBram Moolenaar     int		one)	// if TRUE, only replace one file name, include
258226262f87SBram Moolenaar 			// spaces and commas in the file name.
258326262f87SBram Moolenaar {
258426262f87SBram Moolenaar     size_t	dirlen = 0, envlen = 0;
258526262f87SBram Moolenaar     size_t	len;
258626262f87SBram Moolenaar     char_u	*homedir_env, *homedir_env_orig;
258726262f87SBram Moolenaar     char_u	*p;
258826262f87SBram Moolenaar 
258926262f87SBram Moolenaar     if (src == NULL)
259026262f87SBram Moolenaar     {
259126262f87SBram Moolenaar 	*dst = NUL;
259226262f87SBram Moolenaar 	return;
259326262f87SBram Moolenaar     }
259426262f87SBram Moolenaar 
259526262f87SBram Moolenaar     /*
259626262f87SBram Moolenaar      * If the file is a help file, remove the path completely.
259726262f87SBram Moolenaar      */
259826262f87SBram Moolenaar     if (buf != NULL && buf->b_help)
259926262f87SBram Moolenaar     {
260026262f87SBram Moolenaar 	vim_snprintf((char *)dst, dstlen, "%s", gettail(src));
260126262f87SBram Moolenaar 	return;
260226262f87SBram Moolenaar     }
260326262f87SBram Moolenaar 
260426262f87SBram Moolenaar     /*
260526262f87SBram Moolenaar      * We check both the value of the $HOME environment variable and the
260626262f87SBram Moolenaar      * "real" home directory.
260726262f87SBram Moolenaar      */
260826262f87SBram Moolenaar     if (homedir != NULL)
260926262f87SBram Moolenaar 	dirlen = STRLEN(homedir);
261026262f87SBram Moolenaar 
261126262f87SBram Moolenaar #ifdef VMS
261226262f87SBram Moolenaar     homedir_env_orig = homedir_env = mch_getenv((char_u *)"SYS$LOGIN");
261326262f87SBram Moolenaar #else
261426262f87SBram Moolenaar     homedir_env_orig = homedir_env = mch_getenv((char_u *)"HOME");
261526262f87SBram Moolenaar #endif
261626262f87SBram Moolenaar #ifdef MSWIN
261726262f87SBram Moolenaar     if (homedir_env == NULL)
261826262f87SBram Moolenaar 	homedir_env_orig = homedir_env = mch_getenv((char_u *)"USERPROFILE");
261926262f87SBram Moolenaar #endif
262026262f87SBram Moolenaar     // Empty is the same as not set.
262126262f87SBram Moolenaar     if (homedir_env != NULL && *homedir_env == NUL)
262226262f87SBram Moolenaar 	homedir_env = NULL;
262326262f87SBram Moolenaar 
262426262f87SBram Moolenaar     if (homedir_env != NULL && *homedir_env == '~')
262526262f87SBram Moolenaar     {
262626262f87SBram Moolenaar 	int	usedlen = 0;
262726262f87SBram Moolenaar 	int	flen;
262826262f87SBram Moolenaar 	char_u	*fbuf = NULL;
262926262f87SBram Moolenaar 
263026262f87SBram Moolenaar 	flen = (int)STRLEN(homedir_env);
263126262f87SBram Moolenaar 	(void)modify_fname((char_u *)":p", FALSE, &usedlen,
263226262f87SBram Moolenaar 						  &homedir_env, &fbuf, &flen);
263326262f87SBram Moolenaar 	flen = (int)STRLEN(homedir_env);
263426262f87SBram Moolenaar 	if (flen > 0 && vim_ispathsep(homedir_env[flen - 1]))
263526262f87SBram Moolenaar 	    // Remove the trailing / that is added to a directory.
263626262f87SBram Moolenaar 	    homedir_env[flen - 1] = NUL;
263726262f87SBram Moolenaar     }
263826262f87SBram Moolenaar 
263926262f87SBram Moolenaar     if (homedir_env != NULL)
264026262f87SBram Moolenaar 	envlen = STRLEN(homedir_env);
264126262f87SBram Moolenaar 
264226262f87SBram Moolenaar     if (!one)
264326262f87SBram Moolenaar 	src = skipwhite(src);
264426262f87SBram Moolenaar     while (*src && dstlen > 0)
264526262f87SBram Moolenaar     {
264626262f87SBram Moolenaar 	/*
264726262f87SBram Moolenaar 	 * Here we are at the beginning of a file name.
264826262f87SBram Moolenaar 	 * First, check to see if the beginning of the file name matches
264926262f87SBram Moolenaar 	 * $HOME or the "real" home directory. Check that there is a '/'
265026262f87SBram Moolenaar 	 * after the match (so that if e.g. the file is "/home/pieter/bla",
265126262f87SBram Moolenaar 	 * and the home directory is "/home/piet", the file does not end up
265226262f87SBram Moolenaar 	 * as "~er/bla" (which would seem to indicate the file "bla" in user
265326262f87SBram Moolenaar 	 * er's home directory)).
265426262f87SBram Moolenaar 	 */
265526262f87SBram Moolenaar 	p = homedir;
265626262f87SBram Moolenaar 	len = dirlen;
265726262f87SBram Moolenaar 	for (;;)
265826262f87SBram Moolenaar 	{
265926262f87SBram Moolenaar 	    if (   len
266026262f87SBram Moolenaar 		&& fnamencmp(src, p, len) == 0
266126262f87SBram Moolenaar 		&& (vim_ispathsep(src[len])
266226262f87SBram Moolenaar 		    || (!one && (src[len] == ',' || src[len] == ' '))
266326262f87SBram Moolenaar 		    || src[len] == NUL))
266426262f87SBram Moolenaar 	    {
266526262f87SBram Moolenaar 		src += len;
266626262f87SBram Moolenaar 		if (--dstlen > 0)
266726262f87SBram Moolenaar 		    *dst++ = '~';
266826262f87SBram Moolenaar 
26690e390f40SBram Moolenaar 		// Do not add directory separator into dst, because dst is
26700e390f40SBram Moolenaar 		// expected to just return the directory name without the
26710e390f40SBram Moolenaar 		// directory separator '/'.
267226262f87SBram Moolenaar 		break;
267326262f87SBram Moolenaar 	    }
267426262f87SBram Moolenaar 	    if (p == homedir_env)
267526262f87SBram Moolenaar 		break;
267626262f87SBram Moolenaar 	    p = homedir_env;
267726262f87SBram Moolenaar 	    len = envlen;
267826262f87SBram Moolenaar 	}
267926262f87SBram Moolenaar 
268026262f87SBram Moolenaar 	// if (!one) skip to separator: space or comma
268126262f87SBram Moolenaar 	while (*src && (one || (*src != ',' && *src != ' ')) && --dstlen > 0)
268226262f87SBram Moolenaar 	    *dst++ = *src++;
268326262f87SBram Moolenaar 	// skip separator
268426262f87SBram Moolenaar 	while ((*src == ' ' || *src == ',') && --dstlen > 0)
268526262f87SBram Moolenaar 	    *dst++ = *src++;
268626262f87SBram Moolenaar     }
268726262f87SBram Moolenaar     // if (dstlen == 0) out of space, what to do???
268826262f87SBram Moolenaar 
268926262f87SBram Moolenaar     *dst = NUL;
269026262f87SBram Moolenaar 
269126262f87SBram Moolenaar     if (homedir_env != homedir_env_orig)
269226262f87SBram Moolenaar 	vim_free(homedir_env);
269326262f87SBram Moolenaar }
269426262f87SBram Moolenaar 
269526262f87SBram Moolenaar /*
269626262f87SBram Moolenaar  * Like home_replace, store the replaced string in allocated memory.
269726262f87SBram Moolenaar  * When something fails, NULL is returned.
269826262f87SBram Moolenaar  */
269926262f87SBram Moolenaar     char_u  *
home_replace_save(buf_T * buf,char_u * src)270026262f87SBram Moolenaar home_replace_save(
270126262f87SBram Moolenaar     buf_T	*buf,	// when not NULL, check for help files
270226262f87SBram Moolenaar     char_u	*src)	// input file name
270326262f87SBram Moolenaar {
270426262f87SBram Moolenaar     char_u	*dst;
270526262f87SBram Moolenaar     unsigned	len;
270626262f87SBram Moolenaar 
270726262f87SBram Moolenaar     len = 3;			// space for "~/" and trailing NUL
270826262f87SBram Moolenaar     if (src != NULL)		// just in case
270926262f87SBram Moolenaar 	len += (unsigned)STRLEN(src);
271026262f87SBram Moolenaar     dst = alloc(len);
271126262f87SBram Moolenaar     if (dst != NULL)
271226262f87SBram Moolenaar 	home_replace(buf, src, dst, len, TRUE);
271326262f87SBram Moolenaar     return dst;
271426262f87SBram Moolenaar }
271526262f87SBram Moolenaar 
271626262f87SBram Moolenaar /*
271726262f87SBram Moolenaar  * Compare two file names and return:
271826262f87SBram Moolenaar  * FPC_SAME   if they both exist and are the same file.
271926262f87SBram Moolenaar  * FPC_SAMEX  if they both don't exist and have the same file name.
272026262f87SBram Moolenaar  * FPC_DIFF   if they both exist and are different files.
272126262f87SBram Moolenaar  * FPC_NOTX   if they both don't exist.
272226262f87SBram Moolenaar  * FPC_DIFFX  if one of them doesn't exist.
272326262f87SBram Moolenaar  * For the first name environment variables are expanded if "expandenv" is
272426262f87SBram Moolenaar  * TRUE.
272526262f87SBram Moolenaar  */
272626262f87SBram Moolenaar     int
fullpathcmp(char_u * s1,char_u * s2,int checkname,int expandenv)272726262f87SBram Moolenaar fullpathcmp(
272826262f87SBram Moolenaar     char_u *s1,
272926262f87SBram Moolenaar     char_u *s2,
273026262f87SBram Moolenaar     int	    checkname,		// when both don't exist, check file names
273126262f87SBram Moolenaar     int	    expandenv)
273226262f87SBram Moolenaar {
273326262f87SBram Moolenaar #ifdef UNIX
273426262f87SBram Moolenaar     char_u	    exp1[MAXPATHL];
273526262f87SBram Moolenaar     char_u	    full1[MAXPATHL];
273626262f87SBram Moolenaar     char_u	    full2[MAXPATHL];
273726262f87SBram Moolenaar     stat_T	    st1, st2;
273826262f87SBram Moolenaar     int		    r1, r2;
273926262f87SBram Moolenaar 
274026262f87SBram Moolenaar     if (expandenv)
274126262f87SBram Moolenaar 	expand_env(s1, exp1, MAXPATHL);
274226262f87SBram Moolenaar     else
274326262f87SBram Moolenaar 	vim_strncpy(exp1, s1, MAXPATHL - 1);
274426262f87SBram Moolenaar     r1 = mch_stat((char *)exp1, &st1);
274526262f87SBram Moolenaar     r2 = mch_stat((char *)s2, &st2);
274626262f87SBram Moolenaar     if (r1 != 0 && r2 != 0)
274726262f87SBram Moolenaar     {
2748217e1b83SBram Moolenaar 	// if mch_stat() doesn't work, may compare the names
274926262f87SBram Moolenaar 	if (checkname)
275026262f87SBram Moolenaar 	{
275126262f87SBram Moolenaar 	    if (fnamecmp(exp1, s2) == 0)
275226262f87SBram Moolenaar 		return FPC_SAMEX;
275326262f87SBram Moolenaar 	    r1 = vim_FullName(exp1, full1, MAXPATHL, FALSE);
275426262f87SBram Moolenaar 	    r2 = vim_FullName(s2, full2, MAXPATHL, FALSE);
275526262f87SBram Moolenaar 	    if (r1 == OK && r2 == OK && fnamecmp(full1, full2) == 0)
275626262f87SBram Moolenaar 		return FPC_SAMEX;
275726262f87SBram Moolenaar 	}
275826262f87SBram Moolenaar 	return FPC_NOTX;
275926262f87SBram Moolenaar     }
276026262f87SBram Moolenaar     if (r1 != 0 || r2 != 0)
276126262f87SBram Moolenaar 	return FPC_DIFFX;
276226262f87SBram Moolenaar     if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
276326262f87SBram Moolenaar 	return FPC_SAME;
276426262f87SBram Moolenaar     return FPC_DIFF;
276526262f87SBram Moolenaar #else
276626262f87SBram Moolenaar     char_u  *exp1;		// expanded s1
276726262f87SBram Moolenaar     char_u  *full1;		// full path of s1
276826262f87SBram Moolenaar     char_u  *full2;		// full path of s2
276926262f87SBram Moolenaar     int	    retval = FPC_DIFF;
277026262f87SBram Moolenaar     int	    r1, r2;
277126262f87SBram Moolenaar 
277226262f87SBram Moolenaar     // allocate one buffer to store three paths (alloc()/free() is slow!)
277326262f87SBram Moolenaar     if ((exp1 = alloc(MAXPATHL * 3)) != NULL)
277426262f87SBram Moolenaar     {
277526262f87SBram Moolenaar 	full1 = exp1 + MAXPATHL;
277626262f87SBram Moolenaar 	full2 = full1 + MAXPATHL;
277726262f87SBram Moolenaar 
277826262f87SBram Moolenaar 	if (expandenv)
277926262f87SBram Moolenaar 	    expand_env(s1, exp1, MAXPATHL);
278026262f87SBram Moolenaar 	else
278126262f87SBram Moolenaar 	    vim_strncpy(exp1, s1, MAXPATHL - 1);
278226262f87SBram Moolenaar 	r1 = vim_FullName(exp1, full1, MAXPATHL, FALSE);
278326262f87SBram Moolenaar 	r2 = vim_FullName(s2, full2, MAXPATHL, FALSE);
278426262f87SBram Moolenaar 
278526262f87SBram Moolenaar 	// If vim_FullName() fails, the file probably doesn't exist.
278626262f87SBram Moolenaar 	if (r1 != OK && r2 != OK)
278726262f87SBram Moolenaar 	{
278826262f87SBram Moolenaar 	    if (checkname && fnamecmp(exp1, s2) == 0)
278926262f87SBram Moolenaar 		retval = FPC_SAMEX;
279026262f87SBram Moolenaar 	    else
279126262f87SBram Moolenaar 		retval = FPC_NOTX;
279226262f87SBram Moolenaar 	}
279326262f87SBram Moolenaar 	else if (r1 != OK || r2 != OK)
279426262f87SBram Moolenaar 	    retval = FPC_DIFFX;
279526262f87SBram Moolenaar 	else if (fnamecmp(full1, full2))
279626262f87SBram Moolenaar 	    retval = FPC_DIFF;
279726262f87SBram Moolenaar 	else
279826262f87SBram Moolenaar 	    retval = FPC_SAME;
279926262f87SBram Moolenaar 	vim_free(exp1);
280026262f87SBram Moolenaar     }
280126262f87SBram Moolenaar     return retval;
280226262f87SBram Moolenaar #endif
280326262f87SBram Moolenaar }
280426262f87SBram Moolenaar 
280526262f87SBram Moolenaar /*
280626262f87SBram Moolenaar  * Get the tail of a path: the file name.
280726262f87SBram Moolenaar  * When the path ends in a path separator the tail is the NUL after it.
280826262f87SBram Moolenaar  * Fail safe: never returns NULL.
280926262f87SBram Moolenaar  */
281026262f87SBram Moolenaar     char_u *
gettail(char_u * fname)281126262f87SBram Moolenaar gettail(char_u *fname)
281226262f87SBram Moolenaar {
281326262f87SBram Moolenaar     char_u  *p1, *p2;
281426262f87SBram Moolenaar 
281526262f87SBram Moolenaar     if (fname == NULL)
281626262f87SBram Moolenaar 	return (char_u *)"";
281726262f87SBram Moolenaar     for (p1 = p2 = get_past_head(fname); *p2; )	// find last part of path
281826262f87SBram Moolenaar     {
281926262f87SBram Moolenaar 	if (vim_ispathsep_nocolon(*p2))
282026262f87SBram Moolenaar 	    p1 = p2 + 1;
282126262f87SBram Moolenaar 	MB_PTR_ADV(p2);
282226262f87SBram Moolenaar     }
282326262f87SBram Moolenaar     return p1;
282426262f87SBram Moolenaar }
282526262f87SBram Moolenaar 
282626262f87SBram Moolenaar /*
282726262f87SBram Moolenaar  * Get pointer to tail of "fname", including path separators.  Putting a NUL
282826262f87SBram Moolenaar  * here leaves the directory name.  Takes care of "c:/" and "//".
282926262f87SBram Moolenaar  * Always returns a valid pointer.
283026262f87SBram Moolenaar  */
283126262f87SBram Moolenaar     char_u *
gettail_sep(char_u * fname)283226262f87SBram Moolenaar gettail_sep(char_u *fname)
283326262f87SBram Moolenaar {
283426262f87SBram Moolenaar     char_u	*p;
283526262f87SBram Moolenaar     char_u	*t;
283626262f87SBram Moolenaar 
283726262f87SBram Moolenaar     p = get_past_head(fname);	// don't remove the '/' from "c:/file"
283826262f87SBram Moolenaar     t = gettail(fname);
283926262f87SBram Moolenaar     while (t > p && after_pathsep(fname, t))
284026262f87SBram Moolenaar 	--t;
284126262f87SBram Moolenaar #ifdef VMS
284226262f87SBram Moolenaar     // path separator is part of the path
284326262f87SBram Moolenaar     ++t;
284426262f87SBram Moolenaar #endif
284526262f87SBram Moolenaar     return t;
284626262f87SBram Moolenaar }
284726262f87SBram Moolenaar 
284826262f87SBram Moolenaar /*
284926262f87SBram Moolenaar  * get the next path component (just after the next path separator).
285026262f87SBram Moolenaar  */
285126262f87SBram Moolenaar     char_u *
getnextcomp(char_u * fname)285226262f87SBram Moolenaar getnextcomp(char_u *fname)
285326262f87SBram Moolenaar {
285426262f87SBram Moolenaar     while (*fname && !vim_ispathsep(*fname))
285526262f87SBram Moolenaar 	MB_PTR_ADV(fname);
285626262f87SBram Moolenaar     if (*fname)
285726262f87SBram Moolenaar 	++fname;
285826262f87SBram Moolenaar     return fname;
285926262f87SBram Moolenaar }
286026262f87SBram Moolenaar 
286126262f87SBram Moolenaar /*
286226262f87SBram Moolenaar  * Get a pointer to one character past the head of a path name.
286326262f87SBram Moolenaar  * Unix: after "/"; DOS: after "c:\"; Amiga: after "disk:/"; Mac: no head.
286426262f87SBram Moolenaar  * If there is no head, path is returned.
286526262f87SBram Moolenaar  */
286626262f87SBram Moolenaar     char_u *
get_past_head(char_u * path)286726262f87SBram Moolenaar get_past_head(char_u *path)
286826262f87SBram Moolenaar {
286926262f87SBram Moolenaar     char_u  *retval;
287026262f87SBram Moolenaar 
287126262f87SBram Moolenaar #if defined(MSWIN)
287226262f87SBram Moolenaar     // may skip "c:"
287326262f87SBram Moolenaar     if (isalpha(path[0]) && path[1] == ':')
287426262f87SBram Moolenaar 	retval = path + 2;
287526262f87SBram Moolenaar     else
287626262f87SBram Moolenaar 	retval = path;
287726262f87SBram Moolenaar #else
287826262f87SBram Moolenaar # if defined(AMIGA)
287926262f87SBram Moolenaar     // may skip "label:"
288026262f87SBram Moolenaar     retval = vim_strchr(path, ':');
288126262f87SBram Moolenaar     if (retval == NULL)
288226262f87SBram Moolenaar 	retval = path;
288326262f87SBram Moolenaar # else	// Unix
288426262f87SBram Moolenaar     retval = path;
288526262f87SBram Moolenaar # endif
288626262f87SBram Moolenaar #endif
288726262f87SBram Moolenaar 
288826262f87SBram Moolenaar     while (vim_ispathsep(*retval))
288926262f87SBram Moolenaar 	++retval;
289026262f87SBram Moolenaar 
289126262f87SBram Moolenaar     return retval;
289226262f87SBram Moolenaar }
289326262f87SBram Moolenaar 
289426262f87SBram Moolenaar /*
289526262f87SBram Moolenaar  * Return TRUE if 'c' is a path separator.
289626262f87SBram Moolenaar  * Note that for MS-Windows this includes the colon.
289726262f87SBram Moolenaar  */
289826262f87SBram Moolenaar     int
vim_ispathsep(int c)289926262f87SBram Moolenaar vim_ispathsep(int c)
290026262f87SBram Moolenaar {
290126262f87SBram Moolenaar #ifdef UNIX
290226262f87SBram Moolenaar     return (c == '/');	    // UNIX has ':' inside file names
290326262f87SBram Moolenaar #else
290426262f87SBram Moolenaar # ifdef BACKSLASH_IN_FILENAME
290526262f87SBram Moolenaar     return (c == ':' || c == '/' || c == '\\');
290626262f87SBram Moolenaar # else
290726262f87SBram Moolenaar #  ifdef VMS
290826262f87SBram Moolenaar     // server"user passwd"::device:[full.path.name]fname.extension;version"
290926262f87SBram Moolenaar     return (c == ':' || c == '[' || c == ']' || c == '/'
291026262f87SBram Moolenaar 	    || c == '<' || c == '>' || c == '"' );
291126262f87SBram Moolenaar #  else
291226262f87SBram Moolenaar     return (c == ':' || c == '/');
291326262f87SBram Moolenaar #  endif // VMS
291426262f87SBram Moolenaar # endif
291526262f87SBram Moolenaar #endif
291626262f87SBram Moolenaar }
291726262f87SBram Moolenaar 
291826262f87SBram Moolenaar /*
291926262f87SBram Moolenaar  * Like vim_ispathsep(c), but exclude the colon for MS-Windows.
292026262f87SBram Moolenaar  */
292126262f87SBram Moolenaar     int
vim_ispathsep_nocolon(int c)292226262f87SBram Moolenaar vim_ispathsep_nocolon(int c)
292326262f87SBram Moolenaar {
292426262f87SBram Moolenaar     return vim_ispathsep(c)
292526262f87SBram Moolenaar #ifdef BACKSLASH_IN_FILENAME
292626262f87SBram Moolenaar 	&& c != ':'
292726262f87SBram Moolenaar #endif
292826262f87SBram Moolenaar 	;
292926262f87SBram Moolenaar }
293026262f87SBram Moolenaar 
293126262f87SBram Moolenaar /*
293226262f87SBram Moolenaar  * Return TRUE if the directory of "fname" exists, FALSE otherwise.
293326262f87SBram Moolenaar  * Also returns TRUE if there is no directory name.
293426262f87SBram Moolenaar  * "fname" must be writable!.
293526262f87SBram Moolenaar  */
293626262f87SBram Moolenaar     int
dir_of_file_exists(char_u * fname)293726262f87SBram Moolenaar dir_of_file_exists(char_u *fname)
293826262f87SBram Moolenaar {
293926262f87SBram Moolenaar     char_u	*p;
294026262f87SBram Moolenaar     int		c;
294126262f87SBram Moolenaar     int		retval;
294226262f87SBram Moolenaar 
294326262f87SBram Moolenaar     p = gettail_sep(fname);
294426262f87SBram Moolenaar     if (p == fname)
294526262f87SBram Moolenaar 	return TRUE;
294626262f87SBram Moolenaar     c = *p;
294726262f87SBram Moolenaar     *p = NUL;
294826262f87SBram Moolenaar     retval = mch_isdir(fname);
294926262f87SBram Moolenaar     *p = c;
295026262f87SBram Moolenaar     return retval;
295126262f87SBram Moolenaar }
295226262f87SBram Moolenaar 
295326262f87SBram Moolenaar /*
295426262f87SBram Moolenaar  * Versions of fnamecmp() and fnamencmp() that handle '/' and '\' equally
295526262f87SBram Moolenaar  * and deal with 'fileignorecase'.
295626262f87SBram Moolenaar  */
295726262f87SBram Moolenaar     int
vim_fnamecmp(char_u * x,char_u * y)295826262f87SBram Moolenaar vim_fnamecmp(char_u *x, char_u *y)
295926262f87SBram Moolenaar {
296026262f87SBram Moolenaar #ifdef BACKSLASH_IN_FILENAME
296126262f87SBram Moolenaar     return vim_fnamencmp(x, y, MAXPATHL);
296226262f87SBram Moolenaar #else
296326262f87SBram Moolenaar     if (p_fic)
296426262f87SBram Moolenaar 	return MB_STRICMP(x, y);
296526262f87SBram Moolenaar     return STRCMP(x, y);
296626262f87SBram Moolenaar #endif
296726262f87SBram Moolenaar }
296826262f87SBram Moolenaar 
296926262f87SBram Moolenaar     int
vim_fnamencmp(char_u * x,char_u * y,size_t len)297026262f87SBram Moolenaar vim_fnamencmp(char_u *x, char_u *y, size_t len)
297126262f87SBram Moolenaar {
297226262f87SBram Moolenaar #ifdef BACKSLASH_IN_FILENAME
297326262f87SBram Moolenaar     char_u	*px = x;
297426262f87SBram Moolenaar     char_u	*py = y;
297526262f87SBram Moolenaar     int		cx = NUL;
297626262f87SBram Moolenaar     int		cy = NUL;
297726262f87SBram Moolenaar 
297826262f87SBram Moolenaar     while (len > 0)
297926262f87SBram Moolenaar     {
298026262f87SBram Moolenaar 	cx = PTR2CHAR(px);
298126262f87SBram Moolenaar 	cy = PTR2CHAR(py);
298226262f87SBram Moolenaar 	if (cx == NUL || cy == NUL
298326262f87SBram Moolenaar 	    || ((p_fic ? MB_TOLOWER(cx) != MB_TOLOWER(cy) : cx != cy)
298426262f87SBram Moolenaar 		&& !(cx == '/' && cy == '\\')
298526262f87SBram Moolenaar 		&& !(cx == '\\' && cy == '/')))
298626262f87SBram Moolenaar 	    break;
29871614a149SBram Moolenaar 	len -= mb_ptr2len(px);
29881614a149SBram Moolenaar 	px += mb_ptr2len(px);
29891614a149SBram Moolenaar 	py += mb_ptr2len(py);
299026262f87SBram Moolenaar     }
299126262f87SBram Moolenaar     if (len == 0)
299226262f87SBram Moolenaar 	return 0;
299326262f87SBram Moolenaar     return (cx - cy);
299426262f87SBram Moolenaar #else
299526262f87SBram Moolenaar     if (p_fic)
299626262f87SBram Moolenaar 	return MB_STRNICMP(x, y, len);
299726262f87SBram Moolenaar     return STRNCMP(x, y, len);
299826262f87SBram Moolenaar #endif
299926262f87SBram Moolenaar }
300026262f87SBram Moolenaar 
300126262f87SBram Moolenaar /*
300226262f87SBram Moolenaar  * Concatenate file names fname1 and fname2 into allocated memory.
300326262f87SBram Moolenaar  * Only add a '/' or '\\' when 'sep' is TRUE and it is necessary.
300426262f87SBram Moolenaar  */
300526262f87SBram Moolenaar     char_u  *
concat_fnames(char_u * fname1,char_u * fname2,int sep)300626262f87SBram Moolenaar concat_fnames(char_u *fname1, char_u *fname2, int sep)
300726262f87SBram Moolenaar {
300826262f87SBram Moolenaar     char_u  *dest;
300926262f87SBram Moolenaar 
301026262f87SBram Moolenaar     dest = alloc(STRLEN(fname1) + STRLEN(fname2) + 3);
301126262f87SBram Moolenaar     if (dest != NULL)
301226262f87SBram Moolenaar     {
301326262f87SBram Moolenaar 	STRCPY(dest, fname1);
301426262f87SBram Moolenaar 	if (sep)
301526262f87SBram Moolenaar 	    add_pathsep(dest);
301626262f87SBram Moolenaar 	STRCAT(dest, fname2);
301726262f87SBram Moolenaar     }
301826262f87SBram Moolenaar     return dest;
301926262f87SBram Moolenaar }
302026262f87SBram Moolenaar 
302126262f87SBram Moolenaar /*
302226262f87SBram Moolenaar  * Add a path separator to a file name, unless it already ends in a path
302326262f87SBram Moolenaar  * separator.
302426262f87SBram Moolenaar  */
302526262f87SBram Moolenaar     void
add_pathsep(char_u * p)302626262f87SBram Moolenaar add_pathsep(char_u *p)
302726262f87SBram Moolenaar {
302826262f87SBram Moolenaar     if (*p != NUL && !after_pathsep(p, p + STRLEN(p)))
302926262f87SBram Moolenaar 	STRCAT(p, PATHSEPSTR);
303026262f87SBram Moolenaar }
303126262f87SBram Moolenaar 
303226262f87SBram Moolenaar /*
303326262f87SBram Moolenaar  * FullName_save - Make an allocated copy of a full file name.
303426262f87SBram Moolenaar  * Returns NULL when out of memory.
303526262f87SBram Moolenaar  */
303626262f87SBram Moolenaar     char_u  *
FullName_save(char_u * fname,int force)303726262f87SBram Moolenaar FullName_save(
303826262f87SBram Moolenaar     char_u	*fname,
303926262f87SBram Moolenaar     int		force)		// force expansion, even when it already looks
304026262f87SBram Moolenaar 				// like a full path name
304126262f87SBram Moolenaar {
304226262f87SBram Moolenaar     char_u	*buf;
304326262f87SBram Moolenaar     char_u	*new_fname = NULL;
304426262f87SBram Moolenaar 
304526262f87SBram Moolenaar     if (fname == NULL)
304626262f87SBram Moolenaar 	return NULL;
304726262f87SBram Moolenaar 
304826262f87SBram Moolenaar     buf = alloc(MAXPATHL);
304926262f87SBram Moolenaar     if (buf != NULL)
305026262f87SBram Moolenaar     {
305126262f87SBram Moolenaar 	if (vim_FullName(fname, buf, MAXPATHL, force) != FAIL)
305226262f87SBram Moolenaar 	    new_fname = vim_strsave(buf);
305326262f87SBram Moolenaar 	else
305426262f87SBram Moolenaar 	    new_fname = vim_strsave(fname);
305526262f87SBram Moolenaar 	vim_free(buf);
305626262f87SBram Moolenaar     }
305726262f87SBram Moolenaar     return new_fname;
305826262f87SBram Moolenaar }
305926262f87SBram Moolenaar 
306026262f87SBram Moolenaar /*
306126262f87SBram Moolenaar  * return TRUE if "fname" exists.
306226262f87SBram Moolenaar  */
306326262f87SBram Moolenaar     int
vim_fexists(char_u * fname)306426262f87SBram Moolenaar vim_fexists(char_u *fname)
306526262f87SBram Moolenaar {
306626262f87SBram Moolenaar     stat_T st;
306726262f87SBram Moolenaar 
306826262f87SBram Moolenaar     if (mch_stat((char *)fname, &st))
306926262f87SBram Moolenaar 	return FALSE;
307026262f87SBram Moolenaar     return TRUE;
307126262f87SBram Moolenaar }
307226262f87SBram Moolenaar 
307326262f87SBram Moolenaar /*
307426262f87SBram Moolenaar  * Invoke expand_wildcards() for one pattern.
307526262f87SBram Moolenaar  * Expand items like "%:h" before the expansion.
307626262f87SBram Moolenaar  * Returns OK or FAIL.
307726262f87SBram Moolenaar  */
307826262f87SBram Moolenaar     int
expand_wildcards_eval(char_u ** pat,int * num_file,char_u *** file,int flags)307926262f87SBram Moolenaar expand_wildcards_eval(
308026262f87SBram Moolenaar     char_u	 **pat,		// pointer to input pattern
308126262f87SBram Moolenaar     int		  *num_file,	// resulting number of files
308226262f87SBram Moolenaar     char_u	***file,	// array of resulting files
308326262f87SBram Moolenaar     int		   flags)	// EW_DIR, etc.
308426262f87SBram Moolenaar {
308526262f87SBram Moolenaar     int		ret = FAIL;
308626262f87SBram Moolenaar     char_u	*eval_pat = NULL;
308726262f87SBram Moolenaar     char_u	*exp_pat = *pat;
308826262f87SBram Moolenaar     char      *ignored_msg;
308926262f87SBram Moolenaar     int		usedlen;
309026262f87SBram Moolenaar 
309126262f87SBram Moolenaar     if (*exp_pat == '%' || *exp_pat == '#' || *exp_pat == '<')
309226262f87SBram Moolenaar     {
309326262f87SBram Moolenaar 	++emsg_off;
309426262f87SBram Moolenaar 	eval_pat = eval_vars(exp_pat, exp_pat, &usedlen,
309526262f87SBram Moolenaar 						    NULL, &ignored_msg, NULL);
309626262f87SBram Moolenaar 	--emsg_off;
309726262f87SBram Moolenaar 	if (eval_pat != NULL)
309826262f87SBram Moolenaar 	    exp_pat = concat_str(eval_pat, exp_pat + usedlen);
309926262f87SBram Moolenaar     }
310026262f87SBram Moolenaar 
310126262f87SBram Moolenaar     if (exp_pat != NULL)
310226262f87SBram Moolenaar 	ret = expand_wildcards(1, &exp_pat, num_file, file, flags);
310326262f87SBram Moolenaar 
310426262f87SBram Moolenaar     if (eval_pat != NULL)
310526262f87SBram Moolenaar     {
310626262f87SBram Moolenaar 	vim_free(exp_pat);
310726262f87SBram Moolenaar 	vim_free(eval_pat);
310826262f87SBram Moolenaar     }
310926262f87SBram Moolenaar 
311026262f87SBram Moolenaar     return ret;
311126262f87SBram Moolenaar }
311226262f87SBram Moolenaar 
311326262f87SBram Moolenaar /*
311426262f87SBram Moolenaar  * Expand wildcards.  Calls gen_expand_wildcards() and removes files matching
311526262f87SBram Moolenaar  * 'wildignore'.
311626262f87SBram Moolenaar  * Returns OK or FAIL.  When FAIL then "num_files" won't be set.
311726262f87SBram Moolenaar  */
311826262f87SBram Moolenaar     int
expand_wildcards(int num_pat,char_u ** pat,int * num_files,char_u *** files,int flags)311926262f87SBram Moolenaar expand_wildcards(
312026262f87SBram Moolenaar     int		   num_pat,	// number of input patterns
312126262f87SBram Moolenaar     char_u	 **pat,		// array of input patterns
312226262f87SBram Moolenaar     int		  *num_files,	// resulting number of files
312326262f87SBram Moolenaar     char_u	***files,	// array of resulting files
312426262f87SBram Moolenaar     int		   flags)	// EW_DIR, etc.
312526262f87SBram Moolenaar {
312626262f87SBram Moolenaar     int		retval;
312726262f87SBram Moolenaar     int		i, j;
312826262f87SBram Moolenaar     char_u	*p;
312926262f87SBram Moolenaar     int		non_suf_match;	// number without matching suffix
313026262f87SBram Moolenaar 
313126262f87SBram Moolenaar     retval = gen_expand_wildcards(num_pat, pat, num_files, files, flags);
313226262f87SBram Moolenaar 
313326262f87SBram Moolenaar     // When keeping all matches, return here
313426262f87SBram Moolenaar     if ((flags & EW_KEEPALL) || retval == FAIL)
313526262f87SBram Moolenaar 	return retval;
313626262f87SBram Moolenaar 
313726262f87SBram Moolenaar #ifdef FEAT_WILDIGN
313826262f87SBram Moolenaar     /*
313926262f87SBram Moolenaar      * Remove names that match 'wildignore'.
314026262f87SBram Moolenaar      */
314126262f87SBram Moolenaar     if (*p_wig)
314226262f87SBram Moolenaar     {
314326262f87SBram Moolenaar 	char_u	*ffname;
314426262f87SBram Moolenaar 
314526262f87SBram Moolenaar 	// check all files in (*files)[]
314626262f87SBram Moolenaar 	for (i = 0; i < *num_files; ++i)
314726262f87SBram Moolenaar 	{
314826262f87SBram Moolenaar 	    ffname = FullName_save((*files)[i], FALSE);
314926262f87SBram Moolenaar 	    if (ffname == NULL)		// out of memory
315026262f87SBram Moolenaar 		break;
315126262f87SBram Moolenaar # ifdef VMS
315226262f87SBram Moolenaar 	    vms_remove_version(ffname);
315326262f87SBram Moolenaar # endif
315426262f87SBram Moolenaar 	    if (match_file_list(p_wig, (*files)[i], ffname))
315526262f87SBram Moolenaar 	    {
315626262f87SBram Moolenaar 		// remove this matching file from the list
315726262f87SBram Moolenaar 		vim_free((*files)[i]);
315826262f87SBram Moolenaar 		for (j = i; j + 1 < *num_files; ++j)
315926262f87SBram Moolenaar 		    (*files)[j] = (*files)[j + 1];
316026262f87SBram Moolenaar 		--*num_files;
316126262f87SBram Moolenaar 		--i;
316226262f87SBram Moolenaar 	    }
316326262f87SBram Moolenaar 	    vim_free(ffname);
316426262f87SBram Moolenaar 	}
316526262f87SBram Moolenaar 
316626262f87SBram Moolenaar 	// If the number of matches is now zero, we fail.
316726262f87SBram Moolenaar 	if (*num_files == 0)
316826262f87SBram Moolenaar 	{
316926262f87SBram Moolenaar 	    VIM_CLEAR(*files);
317026262f87SBram Moolenaar 	    return FAIL;
317126262f87SBram Moolenaar 	}
317226262f87SBram Moolenaar     }
317326262f87SBram Moolenaar #endif
317426262f87SBram Moolenaar 
317526262f87SBram Moolenaar     /*
317626262f87SBram Moolenaar      * Move the names where 'suffixes' match to the end.
317726262f87SBram Moolenaar      */
317826262f87SBram Moolenaar     if (*num_files > 1)
317926262f87SBram Moolenaar     {
318026262f87SBram Moolenaar 	non_suf_match = 0;
318126262f87SBram Moolenaar 	for (i = 0; i < *num_files; ++i)
318226262f87SBram Moolenaar 	{
318326262f87SBram Moolenaar 	    if (!match_suffix((*files)[i]))
318426262f87SBram Moolenaar 	    {
318526262f87SBram Moolenaar 		/*
318626262f87SBram Moolenaar 		 * Move the name without matching suffix to the front
318726262f87SBram Moolenaar 		 * of the list.
318826262f87SBram Moolenaar 		 */
318926262f87SBram Moolenaar 		p = (*files)[i];
319026262f87SBram Moolenaar 		for (j = i; j > non_suf_match; --j)
319126262f87SBram Moolenaar 		    (*files)[j] = (*files)[j - 1];
319226262f87SBram Moolenaar 		(*files)[non_suf_match++] = p;
319326262f87SBram Moolenaar 	    }
319426262f87SBram Moolenaar 	}
319526262f87SBram Moolenaar     }
319626262f87SBram Moolenaar 
319726262f87SBram Moolenaar     return retval;
319826262f87SBram Moolenaar }
319926262f87SBram Moolenaar 
320026262f87SBram Moolenaar /*
320126262f87SBram Moolenaar  * Return TRUE if "fname" matches with an entry in 'suffixes'.
320226262f87SBram Moolenaar  */
320326262f87SBram Moolenaar     int
match_suffix(char_u * fname)320426262f87SBram Moolenaar match_suffix(char_u *fname)
320526262f87SBram Moolenaar {
320626262f87SBram Moolenaar     int		fnamelen, setsuflen;
320726262f87SBram Moolenaar     char_u	*setsuf;
320826262f87SBram Moolenaar #define MAXSUFLEN 30	    // maximum length of a file suffix
320926262f87SBram Moolenaar     char_u	suf_buf[MAXSUFLEN];
321026262f87SBram Moolenaar 
321126262f87SBram Moolenaar     fnamelen = (int)STRLEN(fname);
321226262f87SBram Moolenaar     setsuflen = 0;
321326262f87SBram Moolenaar     for (setsuf = p_su; *setsuf; )
321426262f87SBram Moolenaar     {
321526262f87SBram Moolenaar 	setsuflen = copy_option_part(&setsuf, suf_buf, MAXSUFLEN, ".,");
321626262f87SBram Moolenaar 	if (setsuflen == 0)
321726262f87SBram Moolenaar 	{
321826262f87SBram Moolenaar 	    char_u *tail = gettail(fname);
321926262f87SBram Moolenaar 
322026262f87SBram Moolenaar 	    // empty entry: match name without a '.'
322126262f87SBram Moolenaar 	    if (vim_strchr(tail, '.') == NULL)
322226262f87SBram Moolenaar 	    {
322326262f87SBram Moolenaar 		setsuflen = 1;
322426262f87SBram Moolenaar 		break;
322526262f87SBram Moolenaar 	    }
322626262f87SBram Moolenaar 	}
322726262f87SBram Moolenaar 	else
322826262f87SBram Moolenaar 	{
322926262f87SBram Moolenaar 	    if (fnamelen >= setsuflen
323026262f87SBram Moolenaar 		    && fnamencmp(suf_buf, fname + fnamelen - setsuflen,
323126262f87SBram Moolenaar 						  (size_t)setsuflen) == 0)
323226262f87SBram Moolenaar 		break;
323326262f87SBram Moolenaar 	    setsuflen = 0;
323426262f87SBram Moolenaar 	}
323526262f87SBram Moolenaar     }
323626262f87SBram Moolenaar     return (setsuflen != 0);
323726262f87SBram Moolenaar }
323826262f87SBram Moolenaar 
323926262f87SBram Moolenaar #ifdef VIM_BACKTICK
324026262f87SBram Moolenaar 
324126262f87SBram Moolenaar /*
324226262f87SBram Moolenaar  * Return TRUE if we can expand this backtick thing here.
324326262f87SBram Moolenaar  */
324426262f87SBram Moolenaar     static int
vim_backtick(char_u * p)324526262f87SBram Moolenaar vim_backtick(char_u *p)
324626262f87SBram Moolenaar {
324726262f87SBram Moolenaar     return (*p == '`' && *(p + 1) != NUL && *(p + STRLEN(p) - 1) == '`');
324826262f87SBram Moolenaar }
324926262f87SBram Moolenaar 
325026262f87SBram Moolenaar /*
325126262f87SBram Moolenaar  * Expand an item in `backticks` by executing it as a command.
325226262f87SBram Moolenaar  * Currently only works when pat[] starts and ends with a `.
325326262f87SBram Moolenaar  * Returns number of file names found, -1 if an error is encountered.
325426262f87SBram Moolenaar  */
325526262f87SBram Moolenaar     static int
expand_backtick(garray_T * gap,char_u * pat,int flags)325626262f87SBram Moolenaar expand_backtick(
325726262f87SBram Moolenaar     garray_T	*gap,
325826262f87SBram Moolenaar     char_u	*pat,
325926262f87SBram Moolenaar     int		flags)	// EW_* flags
326026262f87SBram Moolenaar {
326126262f87SBram Moolenaar     char_u	*p;
326226262f87SBram Moolenaar     char_u	*cmd;
326326262f87SBram Moolenaar     char_u	*buffer;
326426262f87SBram Moolenaar     int		cnt = 0;
326526262f87SBram Moolenaar     int		i;
326626262f87SBram Moolenaar 
326726262f87SBram Moolenaar     // Create the command: lop off the backticks.
326871ccd03eSBram Moolenaar     cmd = vim_strnsave(pat + 1, STRLEN(pat) - 2);
326926262f87SBram Moolenaar     if (cmd == NULL)
327026262f87SBram Moolenaar 	return -1;
327126262f87SBram Moolenaar 
327226262f87SBram Moolenaar #ifdef FEAT_EVAL
327326262f87SBram Moolenaar     if (*cmd == '=')	    // `={expr}`: Expand expression
3274b171fb17SBram Moolenaar 	buffer = eval_to_string(cmd + 1, TRUE);
327526262f87SBram Moolenaar     else
327626262f87SBram Moolenaar #endif
327726262f87SBram Moolenaar 	buffer = get_cmd_output(cmd, NULL,
327826262f87SBram Moolenaar 				(flags & EW_SILENT) ? SHELL_SILENT : 0, NULL);
327926262f87SBram Moolenaar     vim_free(cmd);
328026262f87SBram Moolenaar     if (buffer == NULL)
328126262f87SBram Moolenaar 	return -1;
328226262f87SBram Moolenaar 
328326262f87SBram Moolenaar     cmd = buffer;
328426262f87SBram Moolenaar     while (*cmd != NUL)
328526262f87SBram Moolenaar     {
328626262f87SBram Moolenaar 	cmd = skipwhite(cmd);		// skip over white space
328726262f87SBram Moolenaar 	p = cmd;
328826262f87SBram Moolenaar 	while (*p != NUL && *p != '\r' && *p != '\n') // skip over entry
328926262f87SBram Moolenaar 	    ++p;
329026262f87SBram Moolenaar 	// add an entry if it is not empty
329126262f87SBram Moolenaar 	if (p > cmd)
329226262f87SBram Moolenaar 	{
329326262f87SBram Moolenaar 	    i = *p;
329426262f87SBram Moolenaar 	    *p = NUL;
329526262f87SBram Moolenaar 	    addfile(gap, cmd, flags);
329626262f87SBram Moolenaar 	    *p = i;
329726262f87SBram Moolenaar 	    ++cnt;
329826262f87SBram Moolenaar 	}
329926262f87SBram Moolenaar 	cmd = p;
330026262f87SBram Moolenaar 	while (*cmd != NUL && (*cmd == '\r' || *cmd == '\n'))
330126262f87SBram Moolenaar 	    ++cmd;
330226262f87SBram Moolenaar     }
330326262f87SBram Moolenaar 
330426262f87SBram Moolenaar     vim_free(buffer);
330526262f87SBram Moolenaar     return cnt;
330626262f87SBram Moolenaar }
330726262f87SBram Moolenaar #endif // VIM_BACKTICK
330826262f87SBram Moolenaar 
330926262f87SBram Moolenaar #if defined(MSWIN)
331026262f87SBram Moolenaar /*
331126262f87SBram Moolenaar  * File name expansion code for MS-DOS, Win16 and Win32.  It's here because
331226262f87SBram Moolenaar  * it's shared between these systems.
331326262f87SBram Moolenaar  */
331426262f87SBram Moolenaar 
331526262f87SBram Moolenaar /*
331626262f87SBram Moolenaar  * comparison function for qsort in dos_expandpath()
331726262f87SBram Moolenaar  */
331826262f87SBram Moolenaar     static int
pstrcmp(const void * a,const void * b)331926262f87SBram Moolenaar pstrcmp(const void *a, const void *b)
332026262f87SBram Moolenaar {
332126262f87SBram Moolenaar     return (pathcmp(*(char **)a, *(char **)b, -1));
332226262f87SBram Moolenaar }
332326262f87SBram Moolenaar 
332426262f87SBram Moolenaar /*
332526262f87SBram Moolenaar  * Recursively expand one path component into all matching files and/or
332626262f87SBram Moolenaar  * directories.  Adds matches to "gap".  Handles "*", "?", "[a-z]", "**", etc.
332726262f87SBram Moolenaar  * Return the number of matches found.
332826262f87SBram Moolenaar  * "path" has backslashes before chars that are not to be expanded, starting
332926262f87SBram Moolenaar  * at "path[wildoff]".
333026262f87SBram Moolenaar  * Return the number of matches found.
333126262f87SBram Moolenaar  * NOTE: much of this is identical to unix_expandpath(), keep in sync!
333226262f87SBram Moolenaar  */
333326262f87SBram Moolenaar     static int
dos_expandpath(garray_T * gap,char_u * path,int wildoff,int flags,int didstar)333426262f87SBram Moolenaar dos_expandpath(
333526262f87SBram Moolenaar     garray_T	*gap,
333626262f87SBram Moolenaar     char_u	*path,
333726262f87SBram Moolenaar     int		wildoff,
333826262f87SBram Moolenaar     int		flags,		// EW_* flags
333926262f87SBram Moolenaar     int		didstar)	// expanded "**" once already
334026262f87SBram Moolenaar {
334126262f87SBram Moolenaar     char_u	*buf;
334226262f87SBram Moolenaar     char_u	*path_end;
334326262f87SBram Moolenaar     char_u	*p, *s, *e;
334426262f87SBram Moolenaar     int		start_len = gap->ga_len;
334526262f87SBram Moolenaar     char_u	*pat;
334626262f87SBram Moolenaar     regmatch_T	regmatch;
334726262f87SBram Moolenaar     int		starts_with_dot;
334826262f87SBram Moolenaar     int		matches;
334926262f87SBram Moolenaar     int		len;
335026262f87SBram Moolenaar     int		starstar = FALSE;
335126262f87SBram Moolenaar     static int	stardepth = 0;	    // depth for "**" expansion
335226262f87SBram Moolenaar     HANDLE		hFind = INVALID_HANDLE_VALUE;
335326262f87SBram Moolenaar     WIN32_FIND_DATAW    wfb;
335426262f87SBram Moolenaar     WCHAR		*wn = NULL;	// UCS-2 name, NULL when not used.
335526262f87SBram Moolenaar     char_u		*matchname;
335626262f87SBram Moolenaar     int			ok;
3357c74fbfedSBram Moolenaar     char_u		*p_alt;
335826262f87SBram Moolenaar 
335926262f87SBram Moolenaar     // Expanding "**" may take a long time, check for CTRL-C.
336026262f87SBram Moolenaar     if (stardepth > 0)
336126262f87SBram Moolenaar     {
336226262f87SBram Moolenaar 	ui_breakcheck();
336326262f87SBram Moolenaar 	if (got_int)
336426262f87SBram Moolenaar 	    return 0;
336526262f87SBram Moolenaar     }
336626262f87SBram Moolenaar 
336726262f87SBram Moolenaar     // Make room for file name.  When doing encoding conversion the actual
336826262f87SBram Moolenaar     // length may be quite a bit longer, thus use the maximum possible length.
336926262f87SBram Moolenaar     buf = alloc(MAXPATHL);
337026262f87SBram Moolenaar     if (buf == NULL)
337126262f87SBram Moolenaar 	return 0;
337226262f87SBram Moolenaar 
337326262f87SBram Moolenaar     /*
337426262f87SBram Moolenaar      * Find the first part in the path name that contains a wildcard or a ~1.
337526262f87SBram Moolenaar      * Copy it into buf, including the preceding characters.
337626262f87SBram Moolenaar      */
337726262f87SBram Moolenaar     p = buf;
337826262f87SBram Moolenaar     s = buf;
337926262f87SBram Moolenaar     e = NULL;
338026262f87SBram Moolenaar     path_end = path;
338126262f87SBram Moolenaar     while (*path_end != NUL)
338226262f87SBram Moolenaar     {
338326262f87SBram Moolenaar 	// May ignore a wildcard that has a backslash before it; it will
338426262f87SBram Moolenaar 	// be removed by rem_backslash() or file_pat_to_reg_pat() below.
338526262f87SBram Moolenaar 	if (path_end >= path + wildoff && rem_backslash(path_end))
338626262f87SBram Moolenaar 	    *p++ = *path_end++;
338726262f87SBram Moolenaar 	else if (*path_end == '\\' || *path_end == ':' || *path_end == '/')
338826262f87SBram Moolenaar 	{
338926262f87SBram Moolenaar 	    if (e != NULL)
339026262f87SBram Moolenaar 		break;
339126262f87SBram Moolenaar 	    s = p + 1;
339226262f87SBram Moolenaar 	}
339326262f87SBram Moolenaar 	else if (path_end >= path + wildoff
339426262f87SBram Moolenaar 			 && vim_strchr((char_u *)"*?[~", *path_end) != NULL)
339526262f87SBram Moolenaar 	    e = p;
339626262f87SBram Moolenaar 	if (has_mbyte)
339726262f87SBram Moolenaar 	{
339826262f87SBram Moolenaar 	    len = (*mb_ptr2len)(path_end);
339926262f87SBram Moolenaar 	    STRNCPY(p, path_end, len);
340026262f87SBram Moolenaar 	    p += len;
340126262f87SBram Moolenaar 	    path_end += len;
340226262f87SBram Moolenaar 	}
340326262f87SBram Moolenaar 	else
340426262f87SBram Moolenaar 	    *p++ = *path_end++;
340526262f87SBram Moolenaar     }
340626262f87SBram Moolenaar     e = p;
340726262f87SBram Moolenaar     *e = NUL;
340826262f87SBram Moolenaar 
340926262f87SBram Moolenaar     // now we have one wildcard component between s and e
341026262f87SBram Moolenaar     // Remove backslashes between "wildoff" and the start of the wildcard
341126262f87SBram Moolenaar     // component.
341226262f87SBram Moolenaar     for (p = buf + wildoff; p < s; ++p)
341326262f87SBram Moolenaar 	if (rem_backslash(p))
341426262f87SBram Moolenaar 	{
341526262f87SBram Moolenaar 	    STRMOVE(p, p + 1);
341626262f87SBram Moolenaar 	    --e;
341726262f87SBram Moolenaar 	    --s;
341826262f87SBram Moolenaar 	}
341926262f87SBram Moolenaar 
342026262f87SBram Moolenaar     // Check for "**" between "s" and "e".
342126262f87SBram Moolenaar     for (p = s; p < e; ++p)
342226262f87SBram Moolenaar 	if (p[0] == '*' && p[1] == '*')
342326262f87SBram Moolenaar 	    starstar = TRUE;
342426262f87SBram Moolenaar 
342526262f87SBram Moolenaar     starts_with_dot = *s == '.';
342626262f87SBram Moolenaar     pat = file_pat_to_reg_pat(s, e, NULL, FALSE);
342726262f87SBram Moolenaar     if (pat == NULL)
342826262f87SBram Moolenaar     {
342926262f87SBram Moolenaar 	vim_free(buf);
343026262f87SBram Moolenaar 	return 0;
343126262f87SBram Moolenaar     }
343226262f87SBram Moolenaar 
343326262f87SBram Moolenaar     // compile the regexp into a program
343426262f87SBram Moolenaar     if (flags & (EW_NOERROR | EW_NOTWILD))
343526262f87SBram Moolenaar 	++emsg_silent;
343626262f87SBram Moolenaar     regmatch.rm_ic = TRUE;		// Always ignore case
343726262f87SBram Moolenaar     regmatch.regprog = vim_regcomp(pat, RE_MAGIC);
343826262f87SBram Moolenaar     if (flags & (EW_NOERROR | EW_NOTWILD))
343926262f87SBram Moolenaar 	--emsg_silent;
344026262f87SBram Moolenaar     vim_free(pat);
344126262f87SBram Moolenaar 
344226262f87SBram Moolenaar     if (regmatch.regprog == NULL && (flags & EW_NOTWILD) == 0)
344326262f87SBram Moolenaar     {
344426262f87SBram Moolenaar 	vim_free(buf);
344526262f87SBram Moolenaar 	return 0;
344626262f87SBram Moolenaar     }
344726262f87SBram Moolenaar 
344826262f87SBram Moolenaar     // remember the pattern or file name being looked for
344926262f87SBram Moolenaar     matchname = vim_strsave(s);
345026262f87SBram Moolenaar 
345126262f87SBram Moolenaar     // If "**" is by itself, this is the first time we encounter it and more
345226262f87SBram Moolenaar     // is following then find matches without any directory.
345326262f87SBram Moolenaar     if (!didstar && stardepth < 100 && starstar && e - s == 2
345426262f87SBram Moolenaar 							  && *path_end == '/')
345526262f87SBram Moolenaar     {
345626262f87SBram Moolenaar 	STRCPY(s, path_end + 1);
345726262f87SBram Moolenaar 	++stardepth;
345826262f87SBram Moolenaar 	(void)dos_expandpath(gap, buf, (int)(s - buf), flags, TRUE);
345926262f87SBram Moolenaar 	--stardepth;
346026262f87SBram Moolenaar     }
346126262f87SBram Moolenaar 
346226262f87SBram Moolenaar     // Scan all files in the directory with "dir/ *.*"
346326262f87SBram Moolenaar     STRCPY(s, "*.*");
346426262f87SBram Moolenaar     wn = enc_to_utf16(buf, NULL);
346526262f87SBram Moolenaar     if (wn != NULL)
346626262f87SBram Moolenaar 	hFind = FindFirstFileW(wn, &wfb);
346726262f87SBram Moolenaar     ok = (hFind != INVALID_HANDLE_VALUE);
346826262f87SBram Moolenaar 
346926262f87SBram Moolenaar     while (ok)
347026262f87SBram Moolenaar     {
347126262f87SBram Moolenaar 	p = utf16_to_enc(wfb.cFileName, NULL);   // p is allocated here
3472c74fbfedSBram Moolenaar 
347326262f87SBram Moolenaar 	if (p == NULL)
347426262f87SBram Moolenaar 	    break;  // out of memory
347526262f87SBram Moolenaar 
34760dc5f603SBram Moolenaar 	// Do not use the alternate filename when the file name ends in '~',
34770dc5f603SBram Moolenaar 	// because it picks up backup files: short name for "foo.vim~" is
34780dc5f603SBram Moolenaar 	// "foo~1.vim", which matches "*.vim".
34790dc5f603SBram Moolenaar 	if (*wfb.cAlternateFileName == NUL || p[STRLEN(p) - 1] == '~')
348040655d50SBram Moolenaar 	    p_alt = NULL;
3481c74fbfedSBram Moolenaar 	else
3482c74fbfedSBram Moolenaar 	    p_alt = utf16_to_enc(wfb.cAlternateFileName, NULL);
3483c74fbfedSBram Moolenaar 
348426262f87SBram Moolenaar 	// Ignore entries starting with a dot, unless when asked for.  Accept
348526262f87SBram Moolenaar 	// all entries found with "matchname".
348626262f87SBram Moolenaar 	if ((p[0] != '.' || starts_with_dot
348726262f87SBram Moolenaar 			 || ((flags & EW_DODOT)
348826262f87SBram Moolenaar 			     && p[1] != NUL && (p[1] != '.' || p[2] != NUL)))
348926262f87SBram Moolenaar 		&& (matchname == NULL
349026262f87SBram Moolenaar 		  || (regmatch.regprog != NULL
3491c74fbfedSBram Moolenaar 		      && (vim_regexec(&regmatch, p, (colnr_T)0)
3492c74fbfedSBram Moolenaar 			 || (p_alt != NULL
349340655d50SBram Moolenaar 				&& vim_regexec(&regmatch, p_alt, (colnr_T)0))))
349426262f87SBram Moolenaar 		  || ((flags & EW_NOTWILD)
349526262f87SBram Moolenaar 		     && fnamencmp(path + (s - buf), p, e - s) == 0)))
349626262f87SBram Moolenaar 	{
349726262f87SBram Moolenaar 	    STRCPY(s, p);
349826262f87SBram Moolenaar 	    len = (int)STRLEN(buf);
349926262f87SBram Moolenaar 
3500c74fbfedSBram Moolenaar 	    if (starstar && stardepth < 100
3501c74fbfedSBram Moolenaar 			  && (wfb.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
350226262f87SBram Moolenaar 	    {
350326262f87SBram Moolenaar 		// For "**" in the pattern first go deeper in the tree to
350426262f87SBram Moolenaar 		// find matches.
350526262f87SBram Moolenaar 		STRCPY(buf + len, "/**");
350626262f87SBram Moolenaar 		STRCPY(buf + len + 3, path_end);
350726262f87SBram Moolenaar 		++stardepth;
350826262f87SBram Moolenaar 		(void)dos_expandpath(gap, buf, len + 1, flags, TRUE);
350926262f87SBram Moolenaar 		--stardepth;
351026262f87SBram Moolenaar 	    }
351126262f87SBram Moolenaar 
351226262f87SBram Moolenaar 	    STRCPY(buf + len, path_end);
351326262f87SBram Moolenaar 	    if (mch_has_exp_wildcard(path_end))
351426262f87SBram Moolenaar 	    {
351526262f87SBram Moolenaar 		// need to expand another component of the path
351626262f87SBram Moolenaar 		// remove backslashes for the remaining components only
351726262f87SBram Moolenaar 		(void)dos_expandpath(gap, buf, len + 1, flags, FALSE);
351826262f87SBram Moolenaar 	    }
351926262f87SBram Moolenaar 	    else
352026262f87SBram Moolenaar 	    {
352126262f87SBram Moolenaar 		// no more wildcards, check if there is a match
352226262f87SBram Moolenaar 		// remove backslashes for the remaining components only
352326262f87SBram Moolenaar 		if (*path_end != 0)
352426262f87SBram Moolenaar 		    backslash_halve(buf + len + 1);
352526262f87SBram Moolenaar 		if (mch_getperm(buf) >= 0)	// add existing file
352626262f87SBram Moolenaar 		    addfile(gap, buf, flags);
352726262f87SBram Moolenaar 	    }
352826262f87SBram Moolenaar 	}
352926262f87SBram Moolenaar 
3530c74fbfedSBram Moolenaar 	vim_free(p_alt);
353126262f87SBram Moolenaar 	vim_free(p);
353226262f87SBram Moolenaar 	ok = FindNextFileW(hFind, &wfb);
353326262f87SBram Moolenaar     }
353426262f87SBram Moolenaar 
353526262f87SBram Moolenaar     FindClose(hFind);
353626262f87SBram Moolenaar     vim_free(wn);
353726262f87SBram Moolenaar     vim_free(buf);
353826262f87SBram Moolenaar     vim_regfree(regmatch.regprog);
353926262f87SBram Moolenaar     vim_free(matchname);
354026262f87SBram Moolenaar 
354126262f87SBram Moolenaar     matches = gap->ga_len - start_len;
354226262f87SBram Moolenaar     if (matches > 0)
354326262f87SBram Moolenaar 	qsort(((char_u **)gap->ga_data) + start_len, (size_t)matches,
354426262f87SBram Moolenaar 						   sizeof(char_u *), pstrcmp);
354526262f87SBram Moolenaar     return matches;
354626262f87SBram Moolenaar }
354726262f87SBram Moolenaar 
354826262f87SBram Moolenaar     int
mch_expandpath(garray_T * gap,char_u * path,int flags)354926262f87SBram Moolenaar mch_expandpath(
355026262f87SBram Moolenaar     garray_T	*gap,
355126262f87SBram Moolenaar     char_u	*path,
355226262f87SBram Moolenaar     int		flags)		// EW_* flags
355326262f87SBram Moolenaar {
355426262f87SBram Moolenaar     return dos_expandpath(gap, path, 0, flags, FALSE);
355526262f87SBram Moolenaar }
355626262f87SBram Moolenaar #endif // MSWIN
355726262f87SBram Moolenaar 
355826262f87SBram Moolenaar #if (defined(UNIX) && !defined(VMS)) || defined(USE_UNIXFILENAME) \
355926262f87SBram Moolenaar 	|| defined(PROTO)
356026262f87SBram Moolenaar /*
356126262f87SBram Moolenaar  * Unix style wildcard expansion code.
356226262f87SBram Moolenaar  * It's here because it's used both for Unix and Mac.
356326262f87SBram Moolenaar  */
356426262f87SBram Moolenaar     static int
pstrcmp(const void * a,const void * b)356526262f87SBram Moolenaar pstrcmp(const void *a, const void *b)
356626262f87SBram Moolenaar {
356726262f87SBram Moolenaar     return (pathcmp(*(char **)a, *(char **)b, -1));
356826262f87SBram Moolenaar }
356926262f87SBram Moolenaar 
357026262f87SBram Moolenaar /*
357126262f87SBram Moolenaar  * Recursively expand one path component into all matching files and/or
357226262f87SBram Moolenaar  * directories.  Adds matches to "gap".  Handles "*", "?", "[a-z]", "**", etc.
357326262f87SBram Moolenaar  * "path" has backslashes before chars that are not to be expanded, starting
357426262f87SBram Moolenaar  * at "path + wildoff".
357526262f87SBram Moolenaar  * Return the number of matches found.
357626262f87SBram Moolenaar  * NOTE: much of this is identical to dos_expandpath(), keep in sync!
357726262f87SBram Moolenaar  */
357826262f87SBram Moolenaar     int
unix_expandpath(garray_T * gap,char_u * path,int wildoff,int flags,int didstar)357926262f87SBram Moolenaar unix_expandpath(
358026262f87SBram Moolenaar     garray_T	*gap,
358126262f87SBram Moolenaar     char_u	*path,
358226262f87SBram Moolenaar     int		wildoff,
358326262f87SBram Moolenaar     int		flags,		// EW_* flags
358426262f87SBram Moolenaar     int		didstar)	// expanded "**" once already
358526262f87SBram Moolenaar {
358626262f87SBram Moolenaar     char_u	*buf;
358726262f87SBram Moolenaar     char_u	*path_end;
358826262f87SBram Moolenaar     char_u	*p, *s, *e;
358926262f87SBram Moolenaar     int		start_len = gap->ga_len;
359026262f87SBram Moolenaar     char_u	*pat;
359126262f87SBram Moolenaar     regmatch_T	regmatch;
359226262f87SBram Moolenaar     int		starts_with_dot;
359326262f87SBram Moolenaar     int		matches;
359426262f87SBram Moolenaar     int		len;
359526262f87SBram Moolenaar     int		starstar = FALSE;
359626262f87SBram Moolenaar     static int	stardepth = 0;	    // depth for "**" expansion
359726262f87SBram Moolenaar 
359826262f87SBram Moolenaar     DIR		*dirp;
359926262f87SBram Moolenaar     struct dirent *dp;
360026262f87SBram Moolenaar 
360126262f87SBram Moolenaar     // Expanding "**" may take a long time, check for CTRL-C.
360226262f87SBram Moolenaar     if (stardepth > 0)
360326262f87SBram Moolenaar     {
360426262f87SBram Moolenaar 	ui_breakcheck();
360526262f87SBram Moolenaar 	if (got_int)
360626262f87SBram Moolenaar 	    return 0;
360726262f87SBram Moolenaar     }
360826262f87SBram Moolenaar 
360926262f87SBram Moolenaar     // make room for file name
361026262f87SBram Moolenaar     buf = alloc(STRLEN(path) + BASENAMELEN + 5);
361126262f87SBram Moolenaar     if (buf == NULL)
361226262f87SBram Moolenaar 	return 0;
361326262f87SBram Moolenaar 
361426262f87SBram Moolenaar     /*
361526262f87SBram Moolenaar      * Find the first part in the path name that contains a wildcard.
361626262f87SBram Moolenaar      * When EW_ICASE is set every letter is considered to be a wildcard.
361726262f87SBram Moolenaar      * Copy it into "buf", including the preceding characters.
361826262f87SBram Moolenaar      */
361926262f87SBram Moolenaar     p = buf;
362026262f87SBram Moolenaar     s = buf;
362126262f87SBram Moolenaar     e = NULL;
362226262f87SBram Moolenaar     path_end = path;
362326262f87SBram Moolenaar     while (*path_end != NUL)
362426262f87SBram Moolenaar     {
362526262f87SBram Moolenaar 	// May ignore a wildcard that has a backslash before it; it will
362626262f87SBram Moolenaar 	// be removed by rem_backslash() or file_pat_to_reg_pat() below.
362726262f87SBram Moolenaar 	if (path_end >= path + wildoff && rem_backslash(path_end))
362826262f87SBram Moolenaar 	    *p++ = *path_end++;
362926262f87SBram Moolenaar 	else if (*path_end == '/')
363026262f87SBram Moolenaar 	{
363126262f87SBram Moolenaar 	    if (e != NULL)
363226262f87SBram Moolenaar 		break;
363326262f87SBram Moolenaar 	    s = p + 1;
363426262f87SBram Moolenaar 	}
363526262f87SBram Moolenaar 	else if (path_end >= path + wildoff
363626262f87SBram Moolenaar 			 && (vim_strchr((char_u *)"*?[{~$", *path_end) != NULL
363726262f87SBram Moolenaar 			     || (!p_fic && (flags & EW_ICASE)
363826262f87SBram Moolenaar 					     && isalpha(PTR2CHAR(path_end)))))
363926262f87SBram Moolenaar 	    e = p;
364026262f87SBram Moolenaar 	if (has_mbyte)
364126262f87SBram Moolenaar 	{
364226262f87SBram Moolenaar 	    len = (*mb_ptr2len)(path_end);
364326262f87SBram Moolenaar 	    STRNCPY(p, path_end, len);
364426262f87SBram Moolenaar 	    p += len;
364526262f87SBram Moolenaar 	    path_end += len;
364626262f87SBram Moolenaar 	}
364726262f87SBram Moolenaar 	else
364826262f87SBram Moolenaar 	    *p++ = *path_end++;
364926262f87SBram Moolenaar     }
365026262f87SBram Moolenaar     e = p;
365126262f87SBram Moolenaar     *e = NUL;
365226262f87SBram Moolenaar 
365326262f87SBram Moolenaar     // Now we have one wildcard component between "s" and "e".
365426262f87SBram Moolenaar     // Remove backslashes between "wildoff" and the start of the wildcard
365526262f87SBram Moolenaar     // component.
365626262f87SBram Moolenaar     for (p = buf + wildoff; p < s; ++p)
365726262f87SBram Moolenaar 	if (rem_backslash(p))
365826262f87SBram Moolenaar 	{
365926262f87SBram Moolenaar 	    STRMOVE(p, p + 1);
366026262f87SBram Moolenaar 	    --e;
366126262f87SBram Moolenaar 	    --s;
366226262f87SBram Moolenaar 	}
366326262f87SBram Moolenaar 
366426262f87SBram Moolenaar     // Check for "**" between "s" and "e".
366526262f87SBram Moolenaar     for (p = s; p < e; ++p)
366626262f87SBram Moolenaar 	if (p[0] == '*' && p[1] == '*')
366726262f87SBram Moolenaar 	    starstar = TRUE;
366826262f87SBram Moolenaar 
366926262f87SBram Moolenaar     // convert the file pattern to a regexp pattern
367026262f87SBram Moolenaar     starts_with_dot = *s == '.';
367126262f87SBram Moolenaar     pat = file_pat_to_reg_pat(s, e, NULL, FALSE);
367226262f87SBram Moolenaar     if (pat == NULL)
367326262f87SBram Moolenaar     {
367426262f87SBram Moolenaar 	vim_free(buf);
367526262f87SBram Moolenaar 	return 0;
367626262f87SBram Moolenaar     }
367726262f87SBram Moolenaar 
367826262f87SBram Moolenaar     // compile the regexp into a program
367926262f87SBram Moolenaar     if (flags & EW_ICASE)
368026262f87SBram Moolenaar 	regmatch.rm_ic = TRUE;		// 'wildignorecase' set
368126262f87SBram Moolenaar     else
368226262f87SBram Moolenaar 	regmatch.rm_ic = p_fic;	// ignore case when 'fileignorecase' is set
368326262f87SBram Moolenaar     if (flags & (EW_NOERROR | EW_NOTWILD))
368426262f87SBram Moolenaar 	++emsg_silent;
368526262f87SBram Moolenaar     regmatch.regprog = vim_regcomp(pat, RE_MAGIC);
368626262f87SBram Moolenaar     if (flags & (EW_NOERROR | EW_NOTWILD))
368726262f87SBram Moolenaar 	--emsg_silent;
368826262f87SBram Moolenaar     vim_free(pat);
368926262f87SBram Moolenaar 
369026262f87SBram Moolenaar     if (regmatch.regprog == NULL && (flags & EW_NOTWILD) == 0)
369126262f87SBram Moolenaar     {
369226262f87SBram Moolenaar 	vim_free(buf);
369326262f87SBram Moolenaar 	return 0;
369426262f87SBram Moolenaar     }
369526262f87SBram Moolenaar 
369626262f87SBram Moolenaar     // If "**" is by itself, this is the first time we encounter it and more
369726262f87SBram Moolenaar     // is following then find matches without any directory.
369826262f87SBram Moolenaar     if (!didstar && stardepth < 100 && starstar && e - s == 2
369926262f87SBram Moolenaar 							  && *path_end == '/')
370026262f87SBram Moolenaar     {
370126262f87SBram Moolenaar 	STRCPY(s, path_end + 1);
370226262f87SBram Moolenaar 	++stardepth;
370326262f87SBram Moolenaar 	(void)unix_expandpath(gap, buf, (int)(s - buf), flags, TRUE);
370426262f87SBram Moolenaar 	--stardepth;
370526262f87SBram Moolenaar     }
370626262f87SBram Moolenaar 
370726262f87SBram Moolenaar     // open the directory for scanning
370826262f87SBram Moolenaar     *s = NUL;
370926262f87SBram Moolenaar     dirp = opendir(*buf == NUL ? "." : (char *)buf);
371026262f87SBram Moolenaar 
371126262f87SBram Moolenaar     // Find all matching entries
371226262f87SBram Moolenaar     if (dirp != NULL)
371326262f87SBram Moolenaar     {
371426262f87SBram Moolenaar 	for (;;)
371526262f87SBram Moolenaar 	{
371626262f87SBram Moolenaar 	    dp = readdir(dirp);
371726262f87SBram Moolenaar 	    if (dp == NULL)
371826262f87SBram Moolenaar 		break;
371926262f87SBram Moolenaar 	    if ((dp->d_name[0] != '.' || starts_with_dot
372026262f87SBram Moolenaar 			|| ((flags & EW_DODOT)
372126262f87SBram Moolenaar 			    && dp->d_name[1] != NUL
372226262f87SBram Moolenaar 			    && (dp->d_name[1] != '.' || dp->d_name[2] != NUL)))
372326262f87SBram Moolenaar 		 && ((regmatch.regprog != NULL && vim_regexec(&regmatch,
372426262f87SBram Moolenaar 					     (char_u *)dp->d_name, (colnr_T)0))
372526262f87SBram Moolenaar 		   || ((flags & EW_NOTWILD)
372626262f87SBram Moolenaar 		     && fnamencmp(path + (s - buf), dp->d_name, e - s) == 0)))
372726262f87SBram Moolenaar 	    {
372826262f87SBram Moolenaar 		STRCPY(s, dp->d_name);
372926262f87SBram Moolenaar 		len = STRLEN(buf);
373026262f87SBram Moolenaar 
373126262f87SBram Moolenaar 		if (starstar && stardepth < 100)
373226262f87SBram Moolenaar 		{
373326262f87SBram Moolenaar 		    // For "**" in the pattern first go deeper in the tree to
373426262f87SBram Moolenaar 		    // find matches.
373526262f87SBram Moolenaar 		    STRCPY(buf + len, "/**");
373626262f87SBram Moolenaar 		    STRCPY(buf + len + 3, path_end);
373726262f87SBram Moolenaar 		    ++stardepth;
373826262f87SBram Moolenaar 		    (void)unix_expandpath(gap, buf, len + 1, flags, TRUE);
373926262f87SBram Moolenaar 		    --stardepth;
374026262f87SBram Moolenaar 		}
374126262f87SBram Moolenaar 
374226262f87SBram Moolenaar 		STRCPY(buf + len, path_end);
374326262f87SBram Moolenaar 		if (mch_has_exp_wildcard(path_end)) // handle more wildcards
374426262f87SBram Moolenaar 		{
374526262f87SBram Moolenaar 		    // need to expand another component of the path
374626262f87SBram Moolenaar 		    // remove backslashes for the remaining components only
374726262f87SBram Moolenaar 		    (void)unix_expandpath(gap, buf, len + 1, flags, FALSE);
374826262f87SBram Moolenaar 		}
374926262f87SBram Moolenaar 		else
375026262f87SBram Moolenaar 		{
375126262f87SBram Moolenaar 		    stat_T  sb;
375226262f87SBram Moolenaar 
375326262f87SBram Moolenaar 		    // no more wildcards, check if there is a match
375426262f87SBram Moolenaar 		    // remove backslashes for the remaining components only
375526262f87SBram Moolenaar 		    if (*path_end != NUL)
375626262f87SBram Moolenaar 			backslash_halve(buf + len + 1);
375726262f87SBram Moolenaar 		    // add existing file or symbolic link
375826262f87SBram Moolenaar 		    if ((flags & EW_ALLLINKS) ? mch_lstat((char *)buf, &sb) >= 0
375926262f87SBram Moolenaar 						      : mch_getperm(buf) >= 0)
376026262f87SBram Moolenaar 		    {
376126262f87SBram Moolenaar #ifdef MACOS_CONVERT
376226262f87SBram Moolenaar 			size_t precomp_len = STRLEN(buf)+1;
376326262f87SBram Moolenaar 			char_u *precomp_buf =
376426262f87SBram Moolenaar 			    mac_precompose_path(buf, precomp_len, &precomp_len);
376526262f87SBram Moolenaar 
376626262f87SBram Moolenaar 			if (precomp_buf)
376726262f87SBram Moolenaar 			{
376826262f87SBram Moolenaar 			    mch_memmove(buf, precomp_buf, precomp_len);
376926262f87SBram Moolenaar 			    vim_free(precomp_buf);
377026262f87SBram Moolenaar 			}
377126262f87SBram Moolenaar #endif
377226262f87SBram Moolenaar 			addfile(gap, buf, flags);
377326262f87SBram Moolenaar 		    }
377426262f87SBram Moolenaar 		}
377526262f87SBram Moolenaar 	    }
377626262f87SBram Moolenaar 	}
377726262f87SBram Moolenaar 
377826262f87SBram Moolenaar 	closedir(dirp);
377926262f87SBram Moolenaar     }
378026262f87SBram Moolenaar 
378126262f87SBram Moolenaar     vim_free(buf);
378226262f87SBram Moolenaar     vim_regfree(regmatch.regprog);
378326262f87SBram Moolenaar 
378426262f87SBram Moolenaar     matches = gap->ga_len - start_len;
378526262f87SBram Moolenaar     if (matches > 0)
378626262f87SBram Moolenaar 	qsort(((char_u **)gap->ga_data) + start_len, matches,
378726262f87SBram Moolenaar 						   sizeof(char_u *), pstrcmp);
378826262f87SBram Moolenaar     return matches;
378926262f87SBram Moolenaar }
379026262f87SBram Moolenaar #endif
379126262f87SBram Moolenaar 
379226262f87SBram Moolenaar /*
379326262f87SBram Moolenaar  * Return TRUE if "p" contains what looks like an environment variable.
379426262f87SBram Moolenaar  * Allowing for escaping.
379526262f87SBram Moolenaar  */
379626262f87SBram Moolenaar     static int
has_env_var(char_u * p)379726262f87SBram Moolenaar has_env_var(char_u *p)
379826262f87SBram Moolenaar {
379926262f87SBram Moolenaar     for ( ; *p; MB_PTR_ADV(p))
380026262f87SBram Moolenaar     {
380126262f87SBram Moolenaar 	if (*p == '\\' && p[1] != NUL)
380226262f87SBram Moolenaar 	    ++p;
380326262f87SBram Moolenaar 	else if (vim_strchr((char_u *)
380426262f87SBram Moolenaar #if defined(MSWIN)
380526262f87SBram Moolenaar 				    "$%"
380626262f87SBram Moolenaar #else
380726262f87SBram Moolenaar 				    "$"
380826262f87SBram Moolenaar #endif
380926262f87SBram Moolenaar 					, *p) != NULL)
381026262f87SBram Moolenaar 	    return TRUE;
381126262f87SBram Moolenaar     }
381226262f87SBram Moolenaar     return FALSE;
381326262f87SBram Moolenaar }
381426262f87SBram Moolenaar 
381526262f87SBram Moolenaar #ifdef SPECIAL_WILDCHAR
381626262f87SBram Moolenaar /*
381726262f87SBram Moolenaar  * Return TRUE if "p" contains a special wildcard character, one that Vim
381826262f87SBram Moolenaar  * cannot expand, requires using a shell.
381926262f87SBram Moolenaar  */
382026262f87SBram Moolenaar     static int
has_special_wildchar(char_u * p)382126262f87SBram Moolenaar has_special_wildchar(char_u *p)
382226262f87SBram Moolenaar {
382326262f87SBram Moolenaar     for ( ; *p; MB_PTR_ADV(p))
382426262f87SBram Moolenaar     {
382526262f87SBram Moolenaar 	// Disallow line break characters.
382626262f87SBram Moolenaar 	if (*p == '\r' || *p == '\n')
382726262f87SBram Moolenaar 	    break;
382826262f87SBram Moolenaar 	// Allow for escaping.
382926262f87SBram Moolenaar 	if (*p == '\\' && p[1] != NUL && p[1] != '\r' && p[1] != '\n')
383026262f87SBram Moolenaar 	    ++p;
383126262f87SBram Moolenaar 	else if (vim_strchr((char_u *)SPECIAL_WILDCHAR, *p) != NULL)
383226262f87SBram Moolenaar 	{
383326262f87SBram Moolenaar 	    // A { must be followed by a matching }.
383426262f87SBram Moolenaar 	    if (*p == '{' && vim_strchr(p, '}') == NULL)
383526262f87SBram Moolenaar 		continue;
383626262f87SBram Moolenaar 	    // A quote and backtick must be followed by another one.
383726262f87SBram Moolenaar 	    if ((*p == '`' || *p == '\'') && vim_strchr(p, *p) == NULL)
383826262f87SBram Moolenaar 		continue;
383926262f87SBram Moolenaar 	    return TRUE;
384026262f87SBram Moolenaar 	}
384126262f87SBram Moolenaar     }
384226262f87SBram Moolenaar     return FALSE;
384326262f87SBram Moolenaar }
384426262f87SBram Moolenaar #endif
384526262f87SBram Moolenaar 
384626262f87SBram Moolenaar /*
384726262f87SBram Moolenaar  * Generic wildcard expansion code.
384826262f87SBram Moolenaar  *
384926262f87SBram Moolenaar  * Characters in "pat" that should not be expanded must be preceded with a
385026262f87SBram Moolenaar  * backslash. E.g., "/path\ with\ spaces/my\*star*"
385126262f87SBram Moolenaar  *
385226262f87SBram Moolenaar  * Return FAIL when no single file was found.  In this case "num_file" is not
385326262f87SBram Moolenaar  * set, and "file" may contain an error message.
385426262f87SBram Moolenaar  * Return OK when some files found.  "num_file" is set to the number of
385526262f87SBram Moolenaar  * matches, "file" to the array of matches.  Call FreeWild() later.
385626262f87SBram Moolenaar  */
385726262f87SBram Moolenaar     int
gen_expand_wildcards(int num_pat,char_u ** pat,int * num_file,char_u *** file,int flags)385826262f87SBram Moolenaar gen_expand_wildcards(
385926262f87SBram Moolenaar     int		num_pat,	// number of input patterns
386026262f87SBram Moolenaar     char_u	**pat,		// array of input patterns
386126262f87SBram Moolenaar     int		*num_file,	// resulting number of files
386226262f87SBram Moolenaar     char_u	***file,	// array of resulting files
386326262f87SBram Moolenaar     int		flags)		// EW_* flags
386426262f87SBram Moolenaar {
386526262f87SBram Moolenaar     int			i;
386626262f87SBram Moolenaar     garray_T		ga;
386726262f87SBram Moolenaar     char_u		*p;
386826262f87SBram Moolenaar     static int		recursive = FALSE;
386926262f87SBram Moolenaar     int			add_pat;
387026262f87SBram Moolenaar     int			retval = OK;
387126262f87SBram Moolenaar #if defined(FEAT_SEARCHPATH)
387226262f87SBram Moolenaar     int			did_expand_in_path = FALSE;
387326262f87SBram Moolenaar #endif
387426262f87SBram Moolenaar 
387526262f87SBram Moolenaar     /*
387626262f87SBram Moolenaar      * expand_env() is called to expand things like "~user".  If this fails,
387726262f87SBram Moolenaar      * it calls ExpandOne(), which brings us back here.  In this case, always
387826262f87SBram Moolenaar      * call the machine specific expansion function, if possible.  Otherwise,
387926262f87SBram Moolenaar      * return FAIL.
388026262f87SBram Moolenaar      */
388126262f87SBram Moolenaar     if (recursive)
388226262f87SBram Moolenaar #ifdef SPECIAL_WILDCHAR
388326262f87SBram Moolenaar 	return mch_expand_wildcards(num_pat, pat, num_file, file, flags);
388426262f87SBram Moolenaar #else
388526262f87SBram Moolenaar 	return FAIL;
388626262f87SBram Moolenaar #endif
388726262f87SBram Moolenaar 
388826262f87SBram Moolenaar #ifdef SPECIAL_WILDCHAR
388926262f87SBram Moolenaar     /*
389026262f87SBram Moolenaar      * If there are any special wildcard characters which we cannot handle
389126262f87SBram Moolenaar      * here, call machine specific function for all the expansion.  This
389226262f87SBram Moolenaar      * avoids starting the shell for each argument separately.
389326262f87SBram Moolenaar      * For `=expr` do use the internal function.
389426262f87SBram Moolenaar      */
389526262f87SBram Moolenaar     for (i = 0; i < num_pat; i++)
389626262f87SBram Moolenaar     {
389726262f87SBram Moolenaar 	if (has_special_wildchar(pat[i])
389826262f87SBram Moolenaar # ifdef VIM_BACKTICK
389926262f87SBram Moolenaar 		&& !(vim_backtick(pat[i]) && pat[i][1] == '=')
390026262f87SBram Moolenaar # endif
390126262f87SBram Moolenaar 	   )
390226262f87SBram Moolenaar 	    return mch_expand_wildcards(num_pat, pat, num_file, file, flags);
390326262f87SBram Moolenaar     }
390426262f87SBram Moolenaar #endif
390526262f87SBram Moolenaar 
390626262f87SBram Moolenaar     recursive = TRUE;
390726262f87SBram Moolenaar 
390826262f87SBram Moolenaar     /*
390926262f87SBram Moolenaar      * The matching file names are stored in a growarray.  Init it empty.
391026262f87SBram Moolenaar      */
391126262f87SBram Moolenaar     ga_init2(&ga, (int)sizeof(char_u *), 30);
391226262f87SBram Moolenaar 
391326262f87SBram Moolenaar     for (i = 0; i < num_pat; ++i)
391426262f87SBram Moolenaar     {
391526262f87SBram Moolenaar 	add_pat = -1;
391626262f87SBram Moolenaar 	p = pat[i];
391726262f87SBram Moolenaar 
391826262f87SBram Moolenaar #ifdef VIM_BACKTICK
391926262f87SBram Moolenaar 	if (vim_backtick(p))
392026262f87SBram Moolenaar 	{
392126262f87SBram Moolenaar 	    add_pat = expand_backtick(&ga, p, flags);
392226262f87SBram Moolenaar 	    if (add_pat == -1)
392326262f87SBram Moolenaar 		retval = FAIL;
392426262f87SBram Moolenaar 	}
392526262f87SBram Moolenaar 	else
392626262f87SBram Moolenaar #endif
392726262f87SBram Moolenaar 	{
392826262f87SBram Moolenaar 	    /*
392926262f87SBram Moolenaar 	     * First expand environment variables, "~/" and "~user/".
393026262f87SBram Moolenaar 	     */
393126262f87SBram Moolenaar 	    if ((has_env_var(p) && !(flags & EW_NOTENV)) || *p == '~')
393226262f87SBram Moolenaar 	    {
393326262f87SBram Moolenaar 		p = expand_env_save_opt(p, TRUE);
393426262f87SBram Moolenaar 		if (p == NULL)
393526262f87SBram Moolenaar 		    p = pat[i];
393626262f87SBram Moolenaar #ifdef UNIX
393726262f87SBram Moolenaar 		/*
393826262f87SBram Moolenaar 		 * On Unix, if expand_env() can't expand an environment
393926262f87SBram Moolenaar 		 * variable, use the shell to do that.  Discard previously
394026262f87SBram Moolenaar 		 * found file names and start all over again.
394126262f87SBram Moolenaar 		 */
394226262f87SBram Moolenaar 		else if (has_env_var(p) || *p == '~')
394326262f87SBram Moolenaar 		{
394426262f87SBram Moolenaar 		    vim_free(p);
394526262f87SBram Moolenaar 		    ga_clear_strings(&ga);
394626262f87SBram Moolenaar 		    i = mch_expand_wildcards(num_pat, pat, num_file, file,
394726262f87SBram Moolenaar 							 flags|EW_KEEPDOLLAR);
394826262f87SBram Moolenaar 		    recursive = FALSE;
394926262f87SBram Moolenaar 		    return i;
395026262f87SBram Moolenaar 		}
395126262f87SBram Moolenaar #endif
395226262f87SBram Moolenaar 	    }
395326262f87SBram Moolenaar 
395426262f87SBram Moolenaar 	    /*
395526262f87SBram Moolenaar 	     * If there are wildcards: Expand file names and add each match to
395626262f87SBram Moolenaar 	     * the list.  If there is no match, and EW_NOTFOUND is given, add
395726262f87SBram Moolenaar 	     * the pattern.
395826262f87SBram Moolenaar 	     * If there are no wildcards: Add the file name if it exists or
395926262f87SBram Moolenaar 	     * when EW_NOTFOUND is given.
396026262f87SBram Moolenaar 	     */
396126262f87SBram Moolenaar 	    if (mch_has_exp_wildcard(p))
396226262f87SBram Moolenaar 	    {
396326262f87SBram Moolenaar #if defined(FEAT_SEARCHPATH)
396426262f87SBram Moolenaar 		if ((flags & EW_PATH)
396526262f87SBram Moolenaar 			&& !mch_isFullName(p)
396626262f87SBram Moolenaar 			&& !(p[0] == '.'
396726262f87SBram Moolenaar 			    && (vim_ispathsep(p[1])
396826262f87SBram Moolenaar 				|| (p[1] == '.' && vim_ispathsep(p[2]))))
396926262f87SBram Moolenaar 		   )
397026262f87SBram Moolenaar 		{
397126262f87SBram Moolenaar 		    // :find completion where 'path' is used.
397226262f87SBram Moolenaar 		    // Recursiveness is OK here.
397326262f87SBram Moolenaar 		    recursive = FALSE;
397426262f87SBram Moolenaar 		    add_pat = expand_in_path(&ga, p, flags);
397526262f87SBram Moolenaar 		    recursive = TRUE;
397626262f87SBram Moolenaar 		    did_expand_in_path = TRUE;
397726262f87SBram Moolenaar 		}
397826262f87SBram Moolenaar 		else
397926262f87SBram Moolenaar #endif
398026262f87SBram Moolenaar 		    add_pat = mch_expandpath(&ga, p, flags);
398126262f87SBram Moolenaar 	    }
398226262f87SBram Moolenaar 	}
398326262f87SBram Moolenaar 
398426262f87SBram Moolenaar 	if (add_pat == -1 || (add_pat == 0 && (flags & EW_NOTFOUND)))
398526262f87SBram Moolenaar 	{
398626262f87SBram Moolenaar 	    char_u	*t = backslash_halve_save(p);
398726262f87SBram Moolenaar 
398826262f87SBram Moolenaar 	    // When EW_NOTFOUND is used, always add files and dirs.  Makes
398926262f87SBram Moolenaar 	    // "vim c:/" work.
399026262f87SBram Moolenaar 	    if (flags & EW_NOTFOUND)
399126262f87SBram Moolenaar 		addfile(&ga, t, flags | EW_DIR | EW_FILE);
399226262f87SBram Moolenaar 	    else
399326262f87SBram Moolenaar 		addfile(&ga, t, flags);
399426262f87SBram Moolenaar 
399526262f87SBram Moolenaar 	    if (t != p)
399626262f87SBram Moolenaar 		vim_free(t);
399726262f87SBram Moolenaar 	}
399826262f87SBram Moolenaar 
399926262f87SBram Moolenaar #if defined(FEAT_SEARCHPATH)
400026262f87SBram Moolenaar 	if (did_expand_in_path && ga.ga_len > 0 && (flags & EW_PATH))
400126262f87SBram Moolenaar 	    uniquefy_paths(&ga, p);
400226262f87SBram Moolenaar #endif
400326262f87SBram Moolenaar 	if (p != pat[i])
400426262f87SBram Moolenaar 	    vim_free(p);
400526262f87SBram Moolenaar     }
400626262f87SBram Moolenaar 
4007566cc8c7SBram Moolenaar     // When returning FAIL the array must be freed here.
4008566cc8c7SBram Moolenaar     if (retval == FAIL)
4009566cc8c7SBram Moolenaar 	ga_clear(&ga);
4010566cc8c7SBram Moolenaar 
401126262f87SBram Moolenaar     *num_file = ga.ga_len;
4012566cc8c7SBram Moolenaar     *file = (ga.ga_data != NULL) ? (char_u **)ga.ga_data
4013566cc8c7SBram Moolenaar 						  : (char_u **)_("no matches");
401426262f87SBram Moolenaar 
401526262f87SBram Moolenaar     recursive = FALSE;
401626262f87SBram Moolenaar 
401726262f87SBram Moolenaar     return ((flags & EW_EMPTYOK) || ga.ga_data != NULL) ? retval : FAIL;
401826262f87SBram Moolenaar }
401926262f87SBram Moolenaar 
402026262f87SBram Moolenaar /*
402126262f87SBram Moolenaar  * Add a file to a file list.  Accepted flags:
402226262f87SBram Moolenaar  * EW_DIR	add directories
402326262f87SBram Moolenaar  * EW_FILE	add files
402426262f87SBram Moolenaar  * EW_EXEC	add executable files
402526262f87SBram Moolenaar  * EW_NOTFOUND	add even when it doesn't exist
402626262f87SBram Moolenaar  * EW_ADDSLASH	add slash after directory name
402726262f87SBram Moolenaar  * EW_ALLLINKS	add symlink also when the referred file does not exist
402826262f87SBram Moolenaar  */
402926262f87SBram Moolenaar     void
addfile(garray_T * gap,char_u * f,int flags)403026262f87SBram Moolenaar addfile(
403126262f87SBram Moolenaar     garray_T	*gap,
4032217e1b83SBram Moolenaar     char_u	*f,	// filename
403326262f87SBram Moolenaar     int		flags)
403426262f87SBram Moolenaar {
403526262f87SBram Moolenaar     char_u	*p;
403626262f87SBram Moolenaar     int		isdir;
403726262f87SBram Moolenaar     stat_T	sb;
403826262f87SBram Moolenaar 
403926262f87SBram Moolenaar     // if the file/dir/link doesn't exist, may not add it
404026262f87SBram Moolenaar     if (!(flags & EW_NOTFOUND) && ((flags & EW_ALLLINKS)
404126262f87SBram Moolenaar 			? mch_lstat((char *)f, &sb) < 0 : mch_getperm(f) < 0))
404226262f87SBram Moolenaar 	return;
404326262f87SBram Moolenaar 
404426262f87SBram Moolenaar #ifdef FNAME_ILLEGAL
404526262f87SBram Moolenaar     // if the file/dir contains illegal characters, don't add it
404626262f87SBram Moolenaar     if (vim_strpbrk(f, (char_u *)FNAME_ILLEGAL) != NULL)
404726262f87SBram Moolenaar 	return;
404826262f87SBram Moolenaar #endif
404926262f87SBram Moolenaar 
405026262f87SBram Moolenaar     isdir = mch_isdir(f);
405126262f87SBram Moolenaar     if ((isdir && !(flags & EW_DIR)) || (!isdir && !(flags & EW_FILE)))
405226262f87SBram Moolenaar 	return;
405326262f87SBram Moolenaar 
405426262f87SBram Moolenaar     // If the file isn't executable, may not add it.  Do accept directories.
405526262f87SBram Moolenaar     // When invoked from expand_shellcmd() do not use $PATH.
405626262f87SBram Moolenaar     if (!isdir && (flags & EW_EXEC)
405726262f87SBram Moolenaar 			     && !mch_can_exe(f, NULL, !(flags & EW_SHELLCMD)))
405826262f87SBram Moolenaar 	return;
405926262f87SBram Moolenaar 
406026262f87SBram Moolenaar     // Make room for another item in the file list.
406126262f87SBram Moolenaar     if (ga_grow(gap, 1) == FAIL)
406226262f87SBram Moolenaar 	return;
406326262f87SBram Moolenaar 
406426262f87SBram Moolenaar     p = alloc(STRLEN(f) + 1 + isdir);
406526262f87SBram Moolenaar     if (p == NULL)
406626262f87SBram Moolenaar 	return;
406726262f87SBram Moolenaar 
406826262f87SBram Moolenaar     STRCPY(p, f);
406926262f87SBram Moolenaar #ifdef BACKSLASH_IN_FILENAME
407026262f87SBram Moolenaar     slash_adjust(p);
407126262f87SBram Moolenaar #endif
407226262f87SBram Moolenaar     /*
407326262f87SBram Moolenaar      * Append a slash or backslash after directory names if none is present.
407426262f87SBram Moolenaar      */
407526262f87SBram Moolenaar #ifndef DONT_ADD_PATHSEP_TO_DIR
407626262f87SBram Moolenaar     if (isdir && (flags & EW_ADDSLASH))
407726262f87SBram Moolenaar 	add_pathsep(p);
407826262f87SBram Moolenaar #endif
407926262f87SBram Moolenaar     ((char_u **)gap->ga_data)[gap->ga_len++] = p;
408026262f87SBram Moolenaar }
408126262f87SBram Moolenaar 
408226262f87SBram Moolenaar /*
408326262f87SBram Moolenaar  * Free the list of files returned by expand_wildcards() or other expansion
408426262f87SBram Moolenaar  * functions.
408526262f87SBram Moolenaar  */
408626262f87SBram Moolenaar     void
FreeWild(int count,char_u ** files)408726262f87SBram Moolenaar FreeWild(int count, char_u **files)
408826262f87SBram Moolenaar {
408926262f87SBram Moolenaar     if (count <= 0 || files == NULL)
409026262f87SBram Moolenaar 	return;
409126262f87SBram Moolenaar     while (count--)
409226262f87SBram Moolenaar 	vim_free(files[count]);
409326262f87SBram Moolenaar     vim_free(files);
409426262f87SBram Moolenaar }
409526262f87SBram Moolenaar 
409626262f87SBram Moolenaar /*
409726262f87SBram Moolenaar  * Compare path "p[]" to "q[]".
409826262f87SBram Moolenaar  * If "maxlen" >= 0 compare "p[maxlen]" to "q[maxlen]"
409926262f87SBram Moolenaar  * Return value like strcmp(p, q), but consider path separators.
410026262f87SBram Moolenaar  */
410126262f87SBram Moolenaar     int
pathcmp(const char * p,const char * q,int maxlen)410226262f87SBram Moolenaar pathcmp(const char *p, const char *q, int maxlen)
410326262f87SBram Moolenaar {
410426262f87SBram Moolenaar     int		i, j;
410526262f87SBram Moolenaar     int		c1, c2;
410626262f87SBram Moolenaar     const char	*s = NULL;
410726262f87SBram Moolenaar 
410826262f87SBram Moolenaar     for (i = 0, j = 0; maxlen < 0 || (i < maxlen && j < maxlen);)
410926262f87SBram Moolenaar     {
411026262f87SBram Moolenaar 	c1 = PTR2CHAR((char_u *)p + i);
411126262f87SBram Moolenaar 	c2 = PTR2CHAR((char_u *)q + j);
411226262f87SBram Moolenaar 
411326262f87SBram Moolenaar 	// End of "p": check if "q" also ends or just has a slash.
411426262f87SBram Moolenaar 	if (c1 == NUL)
411526262f87SBram Moolenaar 	{
411626262f87SBram Moolenaar 	    if (c2 == NUL)  // full match
411726262f87SBram Moolenaar 		return 0;
411826262f87SBram Moolenaar 	    s = q;
411926262f87SBram Moolenaar 	    i = j;
412026262f87SBram Moolenaar 	    break;
412126262f87SBram Moolenaar 	}
412226262f87SBram Moolenaar 
412326262f87SBram Moolenaar 	// End of "q": check if "p" just has a slash.
412426262f87SBram Moolenaar 	if (c2 == NUL)
412526262f87SBram Moolenaar 	{
412626262f87SBram Moolenaar 	    s = p;
412726262f87SBram Moolenaar 	    break;
412826262f87SBram Moolenaar 	}
412926262f87SBram Moolenaar 
413026262f87SBram Moolenaar 	if ((p_fic ? MB_TOUPPER(c1) != MB_TOUPPER(c2) : c1 != c2)
413126262f87SBram Moolenaar #ifdef BACKSLASH_IN_FILENAME
413226262f87SBram Moolenaar 		// consider '/' and '\\' to be equal
413326262f87SBram Moolenaar 		&& !((c1 == '/' && c2 == '\\')
413426262f87SBram Moolenaar 		    || (c1 == '\\' && c2 == '/'))
413526262f87SBram Moolenaar #endif
413626262f87SBram Moolenaar 		)
413726262f87SBram Moolenaar 	{
413826262f87SBram Moolenaar 	    if (vim_ispathsep(c1))
413926262f87SBram Moolenaar 		return -1;
414026262f87SBram Moolenaar 	    if (vim_ispathsep(c2))
414126262f87SBram Moolenaar 		return 1;
414226262f87SBram Moolenaar 	    return p_fic ? MB_TOUPPER(c1) - MB_TOUPPER(c2)
414326262f87SBram Moolenaar 		    : c1 - c2;  // no match
414426262f87SBram Moolenaar 	}
414526262f87SBram Moolenaar 
41461614a149SBram Moolenaar 	i += mb_ptr2len((char_u *)p + i);
41471614a149SBram Moolenaar 	j += mb_ptr2len((char_u *)q + j);
414826262f87SBram Moolenaar     }
414926262f87SBram Moolenaar     if (s == NULL)	// "i" or "j" ran into "maxlen"
415026262f87SBram Moolenaar 	return 0;
415126262f87SBram Moolenaar 
415226262f87SBram Moolenaar     c1 = PTR2CHAR((char_u *)s + i);
41531614a149SBram Moolenaar     c2 = PTR2CHAR((char_u *)s + i + mb_ptr2len((char_u *)s + i));
415426262f87SBram Moolenaar     // ignore a trailing slash, but not "//" or ":/"
415526262f87SBram Moolenaar     if (c2 == NUL
415626262f87SBram Moolenaar 	    && i > 0
415726262f87SBram Moolenaar 	    && !after_pathsep((char_u *)s, (char_u *)s + i)
415826262f87SBram Moolenaar #ifdef BACKSLASH_IN_FILENAME
415926262f87SBram Moolenaar 	    && (c1 == '/' || c1 == '\\')
416026262f87SBram Moolenaar #else
416126262f87SBram Moolenaar 	    && c1 == '/'
416226262f87SBram Moolenaar #endif
416326262f87SBram Moolenaar        )
416426262f87SBram Moolenaar 	return 0;   // match with trailing slash
416526262f87SBram Moolenaar     if (s == q)
416626262f87SBram Moolenaar 	return -1;	    // no match
416726262f87SBram Moolenaar     return 1;
416826262f87SBram Moolenaar }
416926262f87SBram Moolenaar 
417026262f87SBram Moolenaar /*
417126262f87SBram Moolenaar  * Return TRUE if "name" is a full (absolute) path name or URL.
417226262f87SBram Moolenaar  */
417326262f87SBram Moolenaar     int
vim_isAbsName(char_u * name)417426262f87SBram Moolenaar vim_isAbsName(char_u *name)
417526262f87SBram Moolenaar {
417626262f87SBram Moolenaar     return (path_with_url(name) != 0 || mch_isFullName(name));
417726262f87SBram Moolenaar }
417826262f87SBram Moolenaar 
417926262f87SBram Moolenaar /*
418026262f87SBram Moolenaar  * Get absolute file name into buffer "buf[len]".
418126262f87SBram Moolenaar  *
418226262f87SBram Moolenaar  * return FAIL for failure, OK otherwise
418326262f87SBram Moolenaar  */
418426262f87SBram Moolenaar     int
vim_FullName(char_u * fname,char_u * buf,int len,int force)418526262f87SBram Moolenaar vim_FullName(
418626262f87SBram Moolenaar     char_u	*fname,
418726262f87SBram Moolenaar     char_u	*buf,
418826262f87SBram Moolenaar     int		len,
418926262f87SBram Moolenaar     int		force)	    // force expansion even when already absolute
419026262f87SBram Moolenaar {
419126262f87SBram Moolenaar     int		retval = OK;
419226262f87SBram Moolenaar     int		url;
419326262f87SBram Moolenaar 
419426262f87SBram Moolenaar     *buf = NUL;
419526262f87SBram Moolenaar     if (fname == NULL)
419626262f87SBram Moolenaar 	return FAIL;
419726262f87SBram Moolenaar 
419826262f87SBram Moolenaar     url = path_with_url(fname);
419926262f87SBram Moolenaar     if (!url)
420026262f87SBram Moolenaar 	retval = mch_FullName(fname, buf, len, force);
420126262f87SBram Moolenaar     if (url || retval == FAIL)
420226262f87SBram Moolenaar     {
420326262f87SBram Moolenaar 	// something failed; use the file name (truncate when too long)
420426262f87SBram Moolenaar 	vim_strncpy(buf, fname, len - 1);
420526262f87SBram Moolenaar     }
420626262f87SBram Moolenaar #if defined(MSWIN)
420726262f87SBram Moolenaar     slash_adjust(buf);
420826262f87SBram Moolenaar #endif
420926262f87SBram Moolenaar     return retval;
421026262f87SBram Moolenaar }
4211