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(®match, p, (colnr_T)0)
3492c74fbfedSBram Moolenaar || (p_alt != NULL
349340655d50SBram Moolenaar && vim_regexec(®match, 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(®match,
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