Mercurial > vim
comparison src/filepath.c @ 17966:46f95606b9ec v8.1.1979
patch 8.1.1979: code for handling file names is spread out
Commit: https://github.com/vim/vim/commit/b005cd80cfda591be95146024d9b97eef383500f
Author: Bram Moolenaar <Bram@vim.org>
Date: Wed Sep 4 15:54:55 2019 +0200
patch 8.1.1979: code for handling file names is spread out
Problem: Code for handling file names is spread out.
Solution: Move code to new filepath.c file. Graduate FEAT_MODIFY_FNAME.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Wed, 04 Sep 2019 16:00:04 +0200 |
parents | |
children | 8f4cc259ed7a |
comparison
equal
deleted
inserted
replaced
17965:621590abaf54 | 17966:46f95606b9ec |
---|---|
1 /* vi:set ts=8 sts=4 sw=4 noet: | |
2 * | |
3 * VIM - Vi IMproved by Bram Moolenaar | |
4 * | |
5 * Do ":help uganda" in Vim to read copying and usage conditions. | |
6 * Do ":help credits" in Vim to see a list of people who contributed. | |
7 * See README.txt for an overview of the Vim source code. | |
8 */ | |
9 | |
10 /* | |
11 * filepath.c: dealing with file names ant paths. | |
12 */ | |
13 | |
14 #include "vim.h" | |
15 | |
16 #ifdef MSWIN | |
17 /* | |
18 * Functions for ":8" filename modifier: get 8.3 version of a filename. | |
19 */ | |
20 | |
21 /* | |
22 * Get the short path (8.3) for the filename in "fnamep". | |
23 * Only works for a valid file name. | |
24 * When the path gets longer "fnamep" is changed and the allocated buffer | |
25 * is put in "bufp". | |
26 * *fnamelen is the length of "fnamep" and set to 0 for a nonexistent path. | |
27 * Returns OK on success, FAIL on failure. | |
28 */ | |
29 static int | |
30 get_short_pathname(char_u **fnamep, char_u **bufp, int *fnamelen) | |
31 { | |
32 int l, len; | |
33 char_u *newbuf; | |
34 | |
35 len = *fnamelen; | |
36 l = GetShortPathName((LPSTR)*fnamep, (LPSTR)*fnamep, len); | |
37 if (l > len - 1) | |
38 { | |
39 /* If that doesn't work (not enough space), then save the string | |
40 * and try again with a new buffer big enough. */ | |
41 newbuf = vim_strnsave(*fnamep, l); | |
42 if (newbuf == NULL) | |
43 return FAIL; | |
44 | |
45 vim_free(*bufp); | |
46 *fnamep = *bufp = newbuf; | |
47 | |
48 /* Really should always succeed, as the buffer is big enough. */ | |
49 l = GetShortPathName((LPSTR)*fnamep, (LPSTR)*fnamep, l+1); | |
50 } | |
51 | |
52 *fnamelen = l; | |
53 return OK; | |
54 } | |
55 | |
56 /* | |
57 * Get the short path (8.3) for the filename in "fname". The converted | |
58 * path is returned in "bufp". | |
59 * | |
60 * Some of the directories specified in "fname" may not exist. This function | |
61 * will shorten the existing directories at the beginning of the path and then | |
62 * append the remaining non-existing path. | |
63 * | |
64 * fname - Pointer to the filename to shorten. On return, contains the | |
65 * pointer to the shortened pathname | |
66 * bufp - Pointer to an allocated buffer for the filename. | |
67 * fnamelen - Length of the filename pointed to by fname | |
68 * | |
69 * Returns OK on success (or nothing done) and FAIL on failure (out of memory). | |
70 */ | |
71 static int | |
72 shortpath_for_invalid_fname( | |
73 char_u **fname, | |
74 char_u **bufp, | |
75 int *fnamelen) | |
76 { | |
77 char_u *short_fname, *save_fname, *pbuf_unused; | |
78 char_u *endp, *save_endp; | |
79 char_u ch; | |
80 int old_len, len; | |
81 int new_len, sfx_len; | |
82 int retval = OK; | |
83 | |
84 /* Make a copy */ | |
85 old_len = *fnamelen; | |
86 save_fname = vim_strnsave(*fname, old_len); | |
87 pbuf_unused = NULL; | |
88 short_fname = NULL; | |
89 | |
90 endp = save_fname + old_len - 1; /* Find the end of the copy */ | |
91 save_endp = endp; | |
92 | |
93 /* | |
94 * Try shortening the supplied path till it succeeds by removing one | |
95 * directory at a time from the tail of the path. | |
96 */ | |
97 len = 0; | |
98 for (;;) | |
99 { | |
100 /* go back one path-separator */ | |
101 while (endp > save_fname && !after_pathsep(save_fname, endp + 1)) | |
102 --endp; | |
103 if (endp <= save_fname) | |
104 break; /* processed the complete path */ | |
105 | |
106 /* | |
107 * Replace the path separator with a NUL and try to shorten the | |
108 * resulting path. | |
109 */ | |
110 ch = *endp; | |
111 *endp = 0; | |
112 short_fname = save_fname; | |
113 len = (int)STRLEN(short_fname) + 1; | |
114 if (get_short_pathname(&short_fname, &pbuf_unused, &len) == FAIL) | |
115 { | |
116 retval = FAIL; | |
117 goto theend; | |
118 } | |
119 *endp = ch; /* preserve the string */ | |
120 | |
121 if (len > 0) | |
122 break; /* successfully shortened the path */ | |
123 | |
124 /* failed to shorten the path. Skip the path separator */ | |
125 --endp; | |
126 } | |
127 | |
128 if (len > 0) | |
129 { | |
130 /* | |
131 * Succeeded in shortening the path. Now concatenate the shortened | |
132 * path with the remaining path at the tail. | |
133 */ | |
134 | |
135 /* Compute the length of the new path. */ | |
136 sfx_len = (int)(save_endp - endp) + 1; | |
137 new_len = len + sfx_len; | |
138 | |
139 *fnamelen = new_len; | |
140 vim_free(*bufp); | |
141 if (new_len > old_len) | |
142 { | |
143 /* There is not enough space in the currently allocated string, | |
144 * copy it to a buffer big enough. */ | |
145 *fname = *bufp = vim_strnsave(short_fname, new_len); | |
146 if (*fname == NULL) | |
147 { | |
148 retval = FAIL; | |
149 goto theend; | |
150 } | |
151 } | |
152 else | |
153 { | |
154 /* Transfer short_fname to the main buffer (it's big enough), | |
155 * unless get_short_pathname() did its work in-place. */ | |
156 *fname = *bufp = save_fname; | |
157 if (short_fname != save_fname) | |
158 vim_strncpy(save_fname, short_fname, len); | |
159 save_fname = NULL; | |
160 } | |
161 | |
162 /* concat the not-shortened part of the path */ | |
163 vim_strncpy(*fname + len, endp, sfx_len); | |
164 (*fname)[new_len] = NUL; | |
165 } | |
166 | |
167 theend: | |
168 vim_free(pbuf_unused); | |
169 vim_free(save_fname); | |
170 | |
171 return retval; | |
172 } | |
173 | |
174 /* | |
175 * Get a pathname for a partial path. | |
176 * Returns OK for success, FAIL for failure. | |
177 */ | |
178 static int | |
179 shortpath_for_partial( | |
180 char_u **fnamep, | |
181 char_u **bufp, | |
182 int *fnamelen) | |
183 { | |
184 int sepcount, len, tflen; | |
185 char_u *p; | |
186 char_u *pbuf, *tfname; | |
187 int hasTilde; | |
188 | |
189 /* Count up the path separators from the RHS.. so we know which part | |
190 * of the path to return. */ | |
191 sepcount = 0; | |
192 for (p = *fnamep; p < *fnamep + *fnamelen; MB_PTR_ADV(p)) | |
193 if (vim_ispathsep(*p)) | |
194 ++sepcount; | |
195 | |
196 /* Need full path first (use expand_env() to remove a "~/") */ | |
197 hasTilde = (**fnamep == '~'); | |
198 if (hasTilde) | |
199 pbuf = tfname = expand_env_save(*fnamep); | |
200 else | |
201 pbuf = tfname = FullName_save(*fnamep, FALSE); | |
202 | |
203 len = tflen = (int)STRLEN(tfname); | |
204 | |
205 if (get_short_pathname(&tfname, &pbuf, &len) == FAIL) | |
206 return FAIL; | |
207 | |
208 if (len == 0) | |
209 { | |
210 /* Don't have a valid filename, so shorten the rest of the | |
211 * path if we can. This CAN give us invalid 8.3 filenames, but | |
212 * there's not a lot of point in guessing what it might be. | |
213 */ | |
214 len = tflen; | |
215 if (shortpath_for_invalid_fname(&tfname, &pbuf, &len) == FAIL) | |
216 return FAIL; | |
217 } | |
218 | |
219 /* Count the paths backward to find the beginning of the desired string. */ | |
220 for (p = tfname + len - 1; p >= tfname; --p) | |
221 { | |
222 if (has_mbyte) | |
223 p -= mb_head_off(tfname, p); | |
224 if (vim_ispathsep(*p)) | |
225 { | |
226 if (sepcount == 0 || (hasTilde && sepcount == 1)) | |
227 break; | |
228 else | |
229 sepcount --; | |
230 } | |
231 } | |
232 if (hasTilde) | |
233 { | |
234 --p; | |
235 if (p >= tfname) | |
236 *p = '~'; | |
237 else | |
238 return FAIL; | |
239 } | |
240 else | |
241 ++p; | |
242 | |
243 /* Copy in the string - p indexes into tfname - allocated at pbuf */ | |
244 vim_free(*bufp); | |
245 *fnamelen = (int)STRLEN(p); | |
246 *bufp = pbuf; | |
247 *fnamep = p; | |
248 | |
249 return OK; | |
250 } | |
251 #endif // MSWIN | |
252 | |
253 /* | |
254 * Adjust a filename, according to a string of modifiers. | |
255 * *fnamep must be NUL terminated when called. When returning, the length is | |
256 * determined by *fnamelen. | |
257 * Returns VALID_ flags or -1 for failure. | |
258 * When there is an error, *fnamep is set to NULL. | |
259 */ | |
260 int | |
261 modify_fname( | |
262 char_u *src, // string with modifiers | |
263 int tilde_file, // "~" is a file name, not $HOME | |
264 int *usedlen, // characters after src that are used | |
265 char_u **fnamep, // file name so far | |
266 char_u **bufp, // buffer for allocated file name or NULL | |
267 int *fnamelen) // length of fnamep | |
268 { | |
269 int valid = 0; | |
270 char_u *tail; | |
271 char_u *s, *p, *pbuf; | |
272 char_u dirname[MAXPATHL]; | |
273 int c; | |
274 int has_fullname = 0; | |
275 #ifdef MSWIN | |
276 char_u *fname_start = *fnamep; | |
277 int has_shortname = 0; | |
278 #endif | |
279 | |
280 repeat: | |
281 /* ":p" - full path/file_name */ | |
282 if (src[*usedlen] == ':' && src[*usedlen + 1] == 'p') | |
283 { | |
284 has_fullname = 1; | |
285 | |
286 valid |= VALID_PATH; | |
287 *usedlen += 2; | |
288 | |
289 /* Expand "~/path" for all systems and "~user/path" for Unix and VMS */ | |
290 if ((*fnamep)[0] == '~' | |
291 #if !defined(UNIX) && !(defined(VMS) && defined(USER_HOME)) | |
292 && ((*fnamep)[1] == '/' | |
293 # ifdef BACKSLASH_IN_FILENAME | |
294 || (*fnamep)[1] == '\\' | |
295 # endif | |
296 || (*fnamep)[1] == NUL) | |
297 #endif | |
298 && !(tilde_file && (*fnamep)[1] == NUL) | |
299 ) | |
300 { | |
301 *fnamep = expand_env_save(*fnamep); | |
302 vim_free(*bufp); /* free any allocated file name */ | |
303 *bufp = *fnamep; | |
304 if (*fnamep == NULL) | |
305 return -1; | |
306 } | |
307 | |
308 /* When "/." or "/.." is used: force expansion to get rid of it. */ | |
309 for (p = *fnamep; *p != NUL; MB_PTR_ADV(p)) | |
310 { | |
311 if (vim_ispathsep(*p) | |
312 && p[1] == '.' | |
313 && (p[2] == NUL | |
314 || vim_ispathsep(p[2]) | |
315 || (p[2] == '.' | |
316 && (p[3] == NUL || vim_ispathsep(p[3]))))) | |
317 break; | |
318 } | |
319 | |
320 /* FullName_save() is slow, don't use it when not needed. */ | |
321 if (*p != NUL || !vim_isAbsName(*fnamep)) | |
322 { | |
323 *fnamep = FullName_save(*fnamep, *p != NUL); | |
324 vim_free(*bufp); /* free any allocated file name */ | |
325 *bufp = *fnamep; | |
326 if (*fnamep == NULL) | |
327 return -1; | |
328 } | |
329 | |
330 #ifdef MSWIN | |
331 # if _WIN32_WINNT >= 0x0500 | |
332 if (vim_strchr(*fnamep, '~') != NULL) | |
333 { | |
334 // Expand 8.3 filename to full path. Needed to make sure the same | |
335 // file does not have two different names. | |
336 // Note: problem does not occur if _WIN32_WINNT < 0x0500. | |
337 WCHAR *wfname = enc_to_utf16(*fnamep, NULL); | |
338 WCHAR buf[_MAX_PATH]; | |
339 | |
340 if (wfname != NULL) | |
341 { | |
342 if (GetLongPathNameW(wfname, buf, _MAX_PATH)) | |
343 { | |
344 char_u *p = utf16_to_enc(buf, NULL); | |
345 | |
346 if (p != NULL) | |
347 { | |
348 vim_free(*bufp); // free any allocated file name | |
349 *bufp = *fnamep = p; | |
350 } | |
351 } | |
352 vim_free(wfname); | |
353 } | |
354 } | |
355 # endif | |
356 #endif | |
357 /* Append a path separator to a directory. */ | |
358 if (mch_isdir(*fnamep)) | |
359 { | |
360 /* Make room for one or two extra characters. */ | |
361 *fnamep = vim_strnsave(*fnamep, (int)STRLEN(*fnamep) + 2); | |
362 vim_free(*bufp); /* free any allocated file name */ | |
363 *bufp = *fnamep; | |
364 if (*fnamep == NULL) | |
365 return -1; | |
366 add_pathsep(*fnamep); | |
367 } | |
368 } | |
369 | |
370 /* ":." - path relative to the current directory */ | |
371 /* ":~" - path relative to the home directory */ | |
372 /* ":8" - shortname path - postponed till after */ | |
373 while (src[*usedlen] == ':' | |
374 && ((c = src[*usedlen + 1]) == '.' || c == '~' || c == '8')) | |
375 { | |
376 *usedlen += 2; | |
377 if (c == '8') | |
378 { | |
379 #ifdef MSWIN | |
380 has_shortname = 1; /* Postpone this. */ | |
381 #endif | |
382 continue; | |
383 } | |
384 pbuf = NULL; | |
385 /* Need full path first (use expand_env() to remove a "~/") */ | |
386 if (!has_fullname) | |
387 { | |
388 if (c == '.' && **fnamep == '~') | |
389 p = pbuf = expand_env_save(*fnamep); | |
390 else | |
391 p = pbuf = FullName_save(*fnamep, FALSE); | |
392 } | |
393 else | |
394 p = *fnamep; | |
395 | |
396 has_fullname = 0; | |
397 | |
398 if (p != NULL) | |
399 { | |
400 if (c == '.') | |
401 { | |
402 mch_dirname(dirname, MAXPATHL); | |
403 s = shorten_fname(p, dirname); | |
404 if (s != NULL) | |
405 { | |
406 *fnamep = s; | |
407 if (pbuf != NULL) | |
408 { | |
409 vim_free(*bufp); /* free any allocated file name */ | |
410 *bufp = pbuf; | |
411 pbuf = NULL; | |
412 } | |
413 } | |
414 } | |
415 else | |
416 { | |
417 home_replace(NULL, p, dirname, MAXPATHL, TRUE); | |
418 /* Only replace it when it starts with '~' */ | |
419 if (*dirname == '~') | |
420 { | |
421 s = vim_strsave(dirname); | |
422 if (s != NULL) | |
423 { | |
424 *fnamep = s; | |
425 vim_free(*bufp); | |
426 *bufp = s; | |
427 } | |
428 } | |
429 } | |
430 vim_free(pbuf); | |
431 } | |
432 } | |
433 | |
434 tail = gettail(*fnamep); | |
435 *fnamelen = (int)STRLEN(*fnamep); | |
436 | |
437 /* ":h" - head, remove "/file_name", can be repeated */ | |
438 /* Don't remove the first "/" or "c:\" */ | |
439 while (src[*usedlen] == ':' && src[*usedlen + 1] == 'h') | |
440 { | |
441 valid |= VALID_HEAD; | |
442 *usedlen += 2; | |
443 s = get_past_head(*fnamep); | |
444 while (tail > s && after_pathsep(s, tail)) | |
445 MB_PTR_BACK(*fnamep, tail); | |
446 *fnamelen = (int)(tail - *fnamep); | |
447 #ifdef VMS | |
448 if (*fnamelen > 0) | |
449 *fnamelen += 1; /* the path separator is part of the path */ | |
450 #endif | |
451 if (*fnamelen == 0) | |
452 { | |
453 /* Result is empty. Turn it into "." to make ":cd %:h" work. */ | |
454 p = vim_strsave((char_u *)"."); | |
455 if (p == NULL) | |
456 return -1; | |
457 vim_free(*bufp); | |
458 *bufp = *fnamep = tail = p; | |
459 *fnamelen = 1; | |
460 } | |
461 else | |
462 { | |
463 while (tail > s && !after_pathsep(s, tail)) | |
464 MB_PTR_BACK(*fnamep, tail); | |
465 } | |
466 } | |
467 | |
468 /* ":8" - shortname */ | |
469 if (src[*usedlen] == ':' && src[*usedlen + 1] == '8') | |
470 { | |
471 *usedlen += 2; | |
472 #ifdef MSWIN | |
473 has_shortname = 1; | |
474 #endif | |
475 } | |
476 | |
477 #ifdef MSWIN | |
478 /* | |
479 * Handle ":8" after we have done 'heads' and before we do 'tails'. | |
480 */ | |
481 if (has_shortname) | |
482 { | |
483 /* Copy the string if it is shortened by :h and when it wasn't copied | |
484 * yet, because we are going to change it in place. Avoids changing | |
485 * the buffer name for "%:8". */ | |
486 if (*fnamelen < (int)STRLEN(*fnamep) || *fnamep == fname_start) | |
487 { | |
488 p = vim_strnsave(*fnamep, *fnamelen); | |
489 if (p == NULL) | |
490 return -1; | |
491 vim_free(*bufp); | |
492 *bufp = *fnamep = p; | |
493 } | |
494 | |
495 /* Split into two implementations - makes it easier. First is where | |
496 * there isn't a full name already, second is where there is. */ | |
497 if (!has_fullname && !vim_isAbsName(*fnamep)) | |
498 { | |
499 if (shortpath_for_partial(fnamep, bufp, fnamelen) == FAIL) | |
500 return -1; | |
501 } | |
502 else | |
503 { | |
504 int l = *fnamelen; | |
505 | |
506 /* Simple case, already have the full-name. | |
507 * Nearly always shorter, so try first time. */ | |
508 if (get_short_pathname(fnamep, bufp, &l) == FAIL) | |
509 return -1; | |
510 | |
511 if (l == 0) | |
512 { | |
513 /* Couldn't find the filename, search the paths. */ | |
514 l = *fnamelen; | |
515 if (shortpath_for_invalid_fname(fnamep, bufp, &l) == FAIL) | |
516 return -1; | |
517 } | |
518 *fnamelen = l; | |
519 } | |
520 } | |
521 #endif // MSWIN | |
522 | |
523 /* ":t" - tail, just the basename */ | |
524 if (src[*usedlen] == ':' && src[*usedlen + 1] == 't') | |
525 { | |
526 *usedlen += 2; | |
527 *fnamelen -= (int)(tail - *fnamep); | |
528 *fnamep = tail; | |
529 } | |
530 | |
531 /* ":e" - extension, can be repeated */ | |
532 /* ":r" - root, without extension, can be repeated */ | |
533 while (src[*usedlen] == ':' | |
534 && (src[*usedlen + 1] == 'e' || src[*usedlen + 1] == 'r')) | |
535 { | |
536 /* find a '.' in the tail: | |
537 * - for second :e: before the current fname | |
538 * - otherwise: The last '.' | |
539 */ | |
540 if (src[*usedlen + 1] == 'e' && *fnamep > tail) | |
541 s = *fnamep - 2; | |
542 else | |
543 s = *fnamep + *fnamelen - 1; | |
544 for ( ; s > tail; --s) | |
545 if (s[0] == '.') | |
546 break; | |
547 if (src[*usedlen + 1] == 'e') /* :e */ | |
548 { | |
549 if (s > tail) | |
550 { | |
551 *fnamelen += (int)(*fnamep - (s + 1)); | |
552 *fnamep = s + 1; | |
553 #ifdef VMS | |
554 /* cut version from the extension */ | |
555 s = *fnamep + *fnamelen - 1; | |
556 for ( ; s > *fnamep; --s) | |
557 if (s[0] == ';') | |
558 break; | |
559 if (s > *fnamep) | |
560 *fnamelen = s - *fnamep; | |
561 #endif | |
562 } | |
563 else if (*fnamep <= tail) | |
564 *fnamelen = 0; | |
565 } | |
566 else /* :r */ | |
567 { | |
568 if (s > tail) /* remove one extension */ | |
569 *fnamelen = (int)(s - *fnamep); | |
570 } | |
571 *usedlen += 2; | |
572 } | |
573 | |
574 /* ":s?pat?foo?" - substitute */ | |
575 /* ":gs?pat?foo?" - global substitute */ | |
576 if (src[*usedlen] == ':' | |
577 && (src[*usedlen + 1] == 's' | |
578 || (src[*usedlen + 1] == 'g' && src[*usedlen + 2] == 's'))) | |
579 { | |
580 char_u *str; | |
581 char_u *pat; | |
582 char_u *sub; | |
583 int sep; | |
584 char_u *flags; | |
585 int didit = FALSE; | |
586 | |
587 flags = (char_u *)""; | |
588 s = src + *usedlen + 2; | |
589 if (src[*usedlen + 1] == 'g') | |
590 { | |
591 flags = (char_u *)"g"; | |
592 ++s; | |
593 } | |
594 | |
595 sep = *s++; | |
596 if (sep) | |
597 { | |
598 /* find end of pattern */ | |
599 p = vim_strchr(s, sep); | |
600 if (p != NULL) | |
601 { | |
602 pat = vim_strnsave(s, (int)(p - s)); | |
603 if (pat != NULL) | |
604 { | |
605 s = p + 1; | |
606 /* find end of substitution */ | |
607 p = vim_strchr(s, sep); | |
608 if (p != NULL) | |
609 { | |
610 sub = vim_strnsave(s, (int)(p - s)); | |
611 str = vim_strnsave(*fnamep, *fnamelen); | |
612 if (sub != NULL && str != NULL) | |
613 { | |
614 *usedlen = (int)(p + 1 - src); | |
615 s = do_string_sub(str, pat, sub, NULL, flags); | |
616 if (s != NULL) | |
617 { | |
618 *fnamep = s; | |
619 *fnamelen = (int)STRLEN(s); | |
620 vim_free(*bufp); | |
621 *bufp = s; | |
622 didit = TRUE; | |
623 } | |
624 } | |
625 vim_free(sub); | |
626 vim_free(str); | |
627 } | |
628 vim_free(pat); | |
629 } | |
630 } | |
631 /* after using ":s", repeat all the modifiers */ | |
632 if (didit) | |
633 goto repeat; | |
634 } | |
635 } | |
636 | |
637 if (src[*usedlen] == ':' && src[*usedlen + 1] == 'S') | |
638 { | |
639 /* vim_strsave_shellescape() needs a NUL terminated string. */ | |
640 c = (*fnamep)[*fnamelen]; | |
641 if (c != NUL) | |
642 (*fnamep)[*fnamelen] = NUL; | |
643 p = vim_strsave_shellescape(*fnamep, FALSE, FALSE); | |
644 if (c != NUL) | |
645 (*fnamep)[*fnamelen] = c; | |
646 if (p == NULL) | |
647 return -1; | |
648 vim_free(*bufp); | |
649 *bufp = *fnamep = p; | |
650 *fnamelen = (int)STRLEN(p); | |
651 *usedlen += 2; | |
652 } | |
653 | |
654 return valid; | |
655 } | |
656 | |
657 #if defined(FEAT_EVAL) || defined(PROTO) | |
658 | |
659 /* | |
660 * "chdir(dir)" function | |
661 */ | |
662 void | |
663 f_chdir(typval_T *argvars, typval_T *rettv) | |
664 { | |
665 char_u *cwd; | |
666 cdscope_T scope = CDSCOPE_GLOBAL; | |
667 | |
668 rettv->v_type = VAR_STRING; | |
669 rettv->vval.v_string = NULL; | |
670 | |
671 if (argvars[0].v_type != VAR_STRING) | |
672 return; | |
673 | |
674 // Return the current directory | |
675 cwd = alloc(MAXPATHL); | |
676 if (cwd != NULL) | |
677 { | |
678 if (mch_dirname(cwd, MAXPATHL) != FAIL) | |
679 { | |
680 #ifdef BACKSLASH_IN_FILENAME | |
681 slash_adjust(cwd); | |
682 #endif | |
683 rettv->vval.v_string = vim_strsave(cwd); | |
684 } | |
685 vim_free(cwd); | |
686 } | |
687 | |
688 if (curwin->w_localdir != NULL) | |
689 scope = CDSCOPE_WINDOW; | |
690 else if (curtab->tp_localdir != NULL) | |
691 scope = CDSCOPE_TABPAGE; | |
692 | |
693 if (!changedir_func(argvars[0].vval.v_string, TRUE, scope)) | |
694 // Directory change failed | |
695 VIM_CLEAR(rettv->vval.v_string); | |
696 } | |
697 | |
698 /* | |
699 * "delete()" function | |
700 */ | |
701 void | |
702 f_delete(typval_T *argvars, typval_T *rettv) | |
703 { | |
704 char_u nbuf[NUMBUFLEN]; | |
705 char_u *name; | |
706 char_u *flags; | |
707 | |
708 rettv->vval.v_number = -1; | |
709 if (check_restricted() || check_secure()) | |
710 return; | |
711 | |
712 name = tv_get_string(&argvars[0]); | |
713 if (name == NULL || *name == NUL) | |
714 { | |
715 emsg(_(e_invarg)); | |
716 return; | |
717 } | |
718 | |
719 if (argvars[1].v_type != VAR_UNKNOWN) | |
720 flags = tv_get_string_buf(&argvars[1], nbuf); | |
721 else | |
722 flags = (char_u *)""; | |
723 | |
724 if (*flags == NUL) | |
725 /* delete a file */ | |
726 rettv->vval.v_number = mch_remove(name) == 0 ? 0 : -1; | |
727 else if (STRCMP(flags, "d") == 0) | |
728 /* delete an empty directory */ | |
729 rettv->vval.v_number = mch_rmdir(name) == 0 ? 0 : -1; | |
730 else if (STRCMP(flags, "rf") == 0) | |
731 /* delete a directory recursively */ | |
732 rettv->vval.v_number = delete_recursive(name); | |
733 else | |
734 semsg(_(e_invexpr2), flags); | |
735 } | |
736 | |
737 /* | |
738 * "executable()" function | |
739 */ | |
740 void | |
741 f_executable(typval_T *argvars, typval_T *rettv) | |
742 { | |
743 char_u *name = tv_get_string(&argvars[0]); | |
744 | |
745 /* Check in $PATH and also check directly if there is a directory name. */ | |
746 rettv->vval.v_number = mch_can_exe(name, NULL, TRUE); | |
747 } | |
748 | |
749 /* | |
750 * "exepath()" function | |
751 */ | |
752 void | |
753 f_exepath(typval_T *argvars, typval_T *rettv) | |
754 { | |
755 char_u *p = NULL; | |
756 | |
757 (void)mch_can_exe(tv_get_string(&argvars[0]), &p, TRUE); | |
758 rettv->v_type = VAR_STRING; | |
759 rettv->vval.v_string = p; | |
760 } | |
761 | |
762 /* | |
763 * "filereadable()" function | |
764 */ | |
765 void | |
766 f_filereadable(typval_T *argvars, typval_T *rettv) | |
767 { | |
768 int fd; | |
769 char_u *p; | |
770 int n; | |
771 | |
772 #ifndef O_NONBLOCK | |
773 # define O_NONBLOCK 0 | |
774 #endif | |
775 p = tv_get_string(&argvars[0]); | |
776 if (*p && !mch_isdir(p) && (fd = mch_open((char *)p, | |
777 O_RDONLY | O_NONBLOCK, 0)) >= 0) | |
778 { | |
779 n = TRUE; | |
780 close(fd); | |
781 } | |
782 else | |
783 n = FALSE; | |
784 | |
785 rettv->vval.v_number = n; | |
786 } | |
787 | |
788 /* | |
789 * Return 0 for not writable, 1 for writable file, 2 for a dir which we have | |
790 * rights to write into. | |
791 */ | |
792 void | |
793 f_filewritable(typval_T *argvars, typval_T *rettv) | |
794 { | |
795 rettv->vval.v_number = filewritable(tv_get_string(&argvars[0])); | |
796 } | |
797 | |
798 void | |
799 findfilendir( | |
800 typval_T *argvars UNUSED, | |
801 typval_T *rettv, | |
802 int find_what UNUSED) | |
803 { | |
804 #ifdef FEAT_SEARCHPATH | |
805 char_u *fname; | |
806 char_u *fresult = NULL; | |
807 char_u *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; | |
808 char_u *p; | |
809 char_u pathbuf[NUMBUFLEN]; | |
810 int count = 1; | |
811 int first = TRUE; | |
812 int error = FALSE; | |
813 #endif | |
814 | |
815 rettv->vval.v_string = NULL; | |
816 rettv->v_type = VAR_STRING; | |
817 | |
818 #ifdef FEAT_SEARCHPATH | |
819 fname = tv_get_string(&argvars[0]); | |
820 | |
821 if (argvars[1].v_type != VAR_UNKNOWN) | |
822 { | |
823 p = tv_get_string_buf_chk(&argvars[1], pathbuf); | |
824 if (p == NULL) | |
825 error = TRUE; | |
826 else | |
827 { | |
828 if (*p != NUL) | |
829 path = p; | |
830 | |
831 if (argvars[2].v_type != VAR_UNKNOWN) | |
832 count = (int)tv_get_number_chk(&argvars[2], &error); | |
833 } | |
834 } | |
835 | |
836 if (count < 0 && rettv_list_alloc(rettv) == FAIL) | |
837 error = TRUE; | |
838 | |
839 if (*fname != NUL && !error) | |
840 { | |
841 do | |
842 { | |
843 if (rettv->v_type == VAR_STRING || rettv->v_type == VAR_LIST) | |
844 vim_free(fresult); | |
845 fresult = find_file_in_path_option(first ? fname : NULL, | |
846 first ? (int)STRLEN(fname) : 0, | |
847 0, first, path, | |
848 find_what, | |
849 curbuf->b_ffname, | |
850 find_what == FINDFILE_DIR | |
851 ? (char_u *)"" : curbuf->b_p_sua); | |
852 first = FALSE; | |
853 | |
854 if (fresult != NULL && rettv->v_type == VAR_LIST) | |
855 list_append_string(rettv->vval.v_list, fresult, -1); | |
856 | |
857 } while ((rettv->v_type == VAR_LIST || --count > 0) && fresult != NULL); | |
858 } | |
859 | |
860 if (rettv->v_type == VAR_STRING) | |
861 rettv->vval.v_string = fresult; | |
862 #endif | |
863 } | |
864 | |
865 /* | |
866 * "finddir({fname}[, {path}[, {count}]])" function | |
867 */ | |
868 void | |
869 f_finddir(typval_T *argvars, typval_T *rettv) | |
870 { | |
871 findfilendir(argvars, rettv, FINDFILE_DIR); | |
872 } | |
873 | |
874 /* | |
875 * "findfile({fname}[, {path}[, {count}]])" function | |
876 */ | |
877 void | |
878 f_findfile(typval_T *argvars, typval_T *rettv) | |
879 { | |
880 findfilendir(argvars, rettv, FINDFILE_FILE); | |
881 } | |
882 | |
883 /* | |
884 * "fnamemodify({fname}, {mods})" function | |
885 */ | |
886 void | |
887 f_fnamemodify(typval_T *argvars, typval_T *rettv) | |
888 { | |
889 char_u *fname; | |
890 char_u *mods; | |
891 int usedlen = 0; | |
892 int len; | |
893 char_u *fbuf = NULL; | |
894 char_u buf[NUMBUFLEN]; | |
895 | |
896 fname = tv_get_string_chk(&argvars[0]); | |
897 mods = tv_get_string_buf_chk(&argvars[1], buf); | |
898 if (fname == NULL || mods == NULL) | |
899 fname = NULL; | |
900 else | |
901 { | |
902 len = (int)STRLEN(fname); | |
903 (void)modify_fname(mods, FALSE, &usedlen, &fname, &fbuf, &len); | |
904 } | |
905 | |
906 rettv->v_type = VAR_STRING; | |
907 if (fname == NULL) | |
908 rettv->vval.v_string = NULL; | |
909 else | |
910 rettv->vval.v_string = vim_strnsave(fname, len); | |
911 vim_free(fbuf); | |
912 } | |
913 | |
914 /* | |
915 * "getcwd()" function | |
916 * | |
917 * Return the current working directory of a window in a tab page. | |
918 * First optional argument 'winnr' is the window number or -1 and the second | |
919 * optional argument 'tabnr' is the tab page number. | |
920 * | |
921 * If no arguments are supplied, then return the directory of the current | |
922 * window. | |
923 * If only 'winnr' is specified and is not -1 or 0 then return the directory of | |
924 * the specified window. | |
925 * If 'winnr' is 0 then return the directory of the current window. | |
926 * If both 'winnr and 'tabnr' are specified and 'winnr' is -1 then return the | |
927 * directory of the specified tab page. Otherwise return the directory of the | |
928 * specified window in the specified tab page. | |
929 * If the window or the tab page doesn't exist then return NULL. | |
930 */ | |
931 void | |
932 f_getcwd(typval_T *argvars, typval_T *rettv) | |
933 { | |
934 win_T *wp = NULL; | |
935 tabpage_T *tp = NULL; | |
936 char_u *cwd; | |
937 int global = FALSE; | |
938 | |
939 rettv->v_type = VAR_STRING; | |
940 rettv->vval.v_string = NULL; | |
941 | |
942 if (argvars[0].v_type == VAR_NUMBER | |
943 && argvars[0].vval.v_number == -1 | |
944 && argvars[1].v_type == VAR_UNKNOWN) | |
945 global = TRUE; | |
946 else | |
947 wp = find_tabwin(&argvars[0], &argvars[1], &tp); | |
948 | |
949 if (wp != NULL && wp->w_localdir != NULL) | |
950 rettv->vval.v_string = vim_strsave(wp->w_localdir); | |
951 else if (tp != NULL && tp->tp_localdir != NULL) | |
952 rettv->vval.v_string = vim_strsave(tp->tp_localdir); | |
953 else if (wp != NULL || tp != NULL || global) | |
954 { | |
955 if (globaldir != NULL) | |
956 rettv->vval.v_string = vim_strsave(globaldir); | |
957 else | |
958 { | |
959 cwd = alloc(MAXPATHL); | |
960 if (cwd != NULL) | |
961 { | |
962 if (mch_dirname(cwd, MAXPATHL) != FAIL) | |
963 rettv->vval.v_string = vim_strsave(cwd); | |
964 vim_free(cwd); | |
965 } | |
966 } | |
967 } | |
968 #ifdef BACKSLASH_IN_FILENAME | |
969 if (rettv->vval.v_string != NULL) | |
970 slash_adjust(rettv->vval.v_string); | |
971 #endif | |
972 } | |
973 | |
974 /* | |
975 * "getfperm({fname})" function | |
976 */ | |
977 void | |
978 f_getfperm(typval_T *argvars, typval_T *rettv) | |
979 { | |
980 char_u *fname; | |
981 stat_T st; | |
982 char_u *perm = NULL; | |
983 char_u flags[] = "rwx"; | |
984 int i; | |
985 | |
986 fname = tv_get_string(&argvars[0]); | |
987 | |
988 rettv->v_type = VAR_STRING; | |
989 if (mch_stat((char *)fname, &st) >= 0) | |
990 { | |
991 perm = vim_strsave((char_u *)"---------"); | |
992 if (perm != NULL) | |
993 { | |
994 for (i = 0; i < 9; i++) | |
995 { | |
996 if (st.st_mode & (1 << (8 - i))) | |
997 perm[i] = flags[i % 3]; | |
998 } | |
999 } | |
1000 } | |
1001 rettv->vval.v_string = perm; | |
1002 } | |
1003 | |
1004 /* | |
1005 * "getfsize({fname})" function | |
1006 */ | |
1007 void | |
1008 f_getfsize(typval_T *argvars, typval_T *rettv) | |
1009 { | |
1010 char_u *fname; | |
1011 stat_T st; | |
1012 | |
1013 fname = tv_get_string(&argvars[0]); | |
1014 | |
1015 rettv->v_type = VAR_NUMBER; | |
1016 | |
1017 if (mch_stat((char *)fname, &st) >= 0) | |
1018 { | |
1019 if (mch_isdir(fname)) | |
1020 rettv->vval.v_number = 0; | |
1021 else | |
1022 { | |
1023 rettv->vval.v_number = (varnumber_T)st.st_size; | |
1024 | |
1025 /* non-perfect check for overflow */ | |
1026 if ((off_T)rettv->vval.v_number != (off_T)st.st_size) | |
1027 rettv->vval.v_number = -2; | |
1028 } | |
1029 } | |
1030 else | |
1031 rettv->vval.v_number = -1; | |
1032 } | |
1033 | |
1034 /* | |
1035 * "getftime({fname})" function | |
1036 */ | |
1037 void | |
1038 f_getftime(typval_T *argvars, typval_T *rettv) | |
1039 { | |
1040 char_u *fname; | |
1041 stat_T st; | |
1042 | |
1043 fname = tv_get_string(&argvars[0]); | |
1044 | |
1045 if (mch_stat((char *)fname, &st) >= 0) | |
1046 rettv->vval.v_number = (varnumber_T)st.st_mtime; | |
1047 else | |
1048 rettv->vval.v_number = -1; | |
1049 } | |
1050 | |
1051 /* | |
1052 * "getftype({fname})" function | |
1053 */ | |
1054 void | |
1055 f_getftype(typval_T *argvars, typval_T *rettv) | |
1056 { | |
1057 char_u *fname; | |
1058 stat_T st; | |
1059 char_u *type = NULL; | |
1060 char *t; | |
1061 | |
1062 fname = tv_get_string(&argvars[0]); | |
1063 | |
1064 rettv->v_type = VAR_STRING; | |
1065 if (mch_lstat((char *)fname, &st) >= 0) | |
1066 { | |
1067 if (S_ISREG(st.st_mode)) | |
1068 t = "file"; | |
1069 else if (S_ISDIR(st.st_mode)) | |
1070 t = "dir"; | |
1071 else if (S_ISLNK(st.st_mode)) | |
1072 t = "link"; | |
1073 else if (S_ISBLK(st.st_mode)) | |
1074 t = "bdev"; | |
1075 else if (S_ISCHR(st.st_mode)) | |
1076 t = "cdev"; | |
1077 else if (S_ISFIFO(st.st_mode)) | |
1078 t = "fifo"; | |
1079 else if (S_ISSOCK(st.st_mode)) | |
1080 t = "socket"; | |
1081 else | |
1082 t = "other"; | |
1083 type = vim_strsave((char_u *)t); | |
1084 } | |
1085 rettv->vval.v_string = type; | |
1086 } | |
1087 | |
1088 /* | |
1089 * "glob()" function | |
1090 */ | |
1091 void | |
1092 f_glob(typval_T *argvars, typval_T *rettv) | |
1093 { | |
1094 int options = WILD_SILENT|WILD_USE_NL; | |
1095 expand_T xpc; | |
1096 int error = FALSE; | |
1097 | |
1098 /* When the optional second argument is non-zero, don't remove matches | |
1099 * for 'wildignore' and don't put matches for 'suffixes' at the end. */ | |
1100 rettv->v_type = VAR_STRING; | |
1101 if (argvars[1].v_type != VAR_UNKNOWN) | |
1102 { | |
1103 if (tv_get_number_chk(&argvars[1], &error)) | |
1104 options |= WILD_KEEP_ALL; | |
1105 if (argvars[2].v_type != VAR_UNKNOWN) | |
1106 { | |
1107 if (tv_get_number_chk(&argvars[2], &error)) | |
1108 rettv_list_set(rettv, NULL); | |
1109 if (argvars[3].v_type != VAR_UNKNOWN | |
1110 && tv_get_number_chk(&argvars[3], &error)) | |
1111 options |= WILD_ALLLINKS; | |
1112 } | |
1113 } | |
1114 if (!error) | |
1115 { | |
1116 ExpandInit(&xpc); | |
1117 xpc.xp_context = EXPAND_FILES; | |
1118 if (p_wic) | |
1119 options += WILD_ICASE; | |
1120 if (rettv->v_type == VAR_STRING) | |
1121 rettv->vval.v_string = ExpandOne(&xpc, tv_get_string(&argvars[0]), | |
1122 NULL, options, WILD_ALL); | |
1123 else if (rettv_list_alloc(rettv) != FAIL) | |
1124 { | |
1125 int i; | |
1126 | |
1127 ExpandOne(&xpc, tv_get_string(&argvars[0]), | |
1128 NULL, options, WILD_ALL_KEEP); | |
1129 for (i = 0; i < xpc.xp_numfiles; i++) | |
1130 list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1); | |
1131 | |
1132 ExpandCleanup(&xpc); | |
1133 } | |
1134 } | |
1135 else | |
1136 rettv->vval.v_string = NULL; | |
1137 } | |
1138 | |
1139 /* | |
1140 * "glob2regpat()" function | |
1141 */ | |
1142 void | |
1143 f_glob2regpat(typval_T *argvars, typval_T *rettv) | |
1144 { | |
1145 char_u *pat = tv_get_string_chk(&argvars[0]); | |
1146 | |
1147 rettv->v_type = VAR_STRING; | |
1148 rettv->vval.v_string = (pat == NULL) | |
1149 ? NULL : file_pat_to_reg_pat(pat, NULL, NULL, FALSE); | |
1150 } | |
1151 | |
1152 /* | |
1153 * "globpath()" function | |
1154 */ | |
1155 void | |
1156 f_globpath(typval_T *argvars, typval_T *rettv) | |
1157 { | |
1158 int flags = WILD_IGNORE_COMPLETESLASH; | |
1159 char_u buf1[NUMBUFLEN]; | |
1160 char_u *file = tv_get_string_buf_chk(&argvars[1], buf1); | |
1161 int error = FALSE; | |
1162 garray_T ga; | |
1163 int i; | |
1164 | |
1165 // When the optional second argument is non-zero, don't remove matches | |
1166 // for 'wildignore' and don't put matches for 'suffixes' at the end. | |
1167 rettv->v_type = VAR_STRING; | |
1168 if (argvars[2].v_type != VAR_UNKNOWN) | |
1169 { | |
1170 if (tv_get_number_chk(&argvars[2], &error)) | |
1171 flags |= WILD_KEEP_ALL; | |
1172 if (argvars[3].v_type != VAR_UNKNOWN) | |
1173 { | |
1174 if (tv_get_number_chk(&argvars[3], &error)) | |
1175 rettv_list_set(rettv, NULL); | |
1176 if (argvars[4].v_type != VAR_UNKNOWN | |
1177 && tv_get_number_chk(&argvars[4], &error)) | |
1178 flags |= WILD_ALLLINKS; | |
1179 } | |
1180 } | |
1181 if (file != NULL && !error) | |
1182 { | |
1183 ga_init2(&ga, (int)sizeof(char_u *), 10); | |
1184 globpath(tv_get_string(&argvars[0]), file, &ga, flags); | |
1185 if (rettv->v_type == VAR_STRING) | |
1186 rettv->vval.v_string = ga_concat_strings(&ga, "\n"); | |
1187 else if (rettv_list_alloc(rettv) != FAIL) | |
1188 for (i = 0; i < ga.ga_len; ++i) | |
1189 list_append_string(rettv->vval.v_list, | |
1190 ((char_u **)(ga.ga_data))[i], -1); | |
1191 ga_clear_strings(&ga); | |
1192 } | |
1193 else | |
1194 rettv->vval.v_string = NULL; | |
1195 } | |
1196 | |
1197 /* | |
1198 * "isdirectory()" function | |
1199 */ | |
1200 void | |
1201 f_isdirectory(typval_T *argvars, typval_T *rettv) | |
1202 { | |
1203 rettv->vval.v_number = mch_isdir(tv_get_string(&argvars[0])); | |
1204 } | |
1205 | |
1206 /* | |
1207 * Evaluate "expr" (= "context") for readdir(). | |
1208 */ | |
1209 static int | |
1210 readdir_checkitem(void *context, char_u *name) | |
1211 { | |
1212 typval_T *expr = (typval_T *)context; | |
1213 typval_T save_val; | |
1214 typval_T rettv; | |
1215 typval_T argv[2]; | |
1216 int retval = 0; | |
1217 int error = FALSE; | |
1218 | |
1219 if (expr->v_type == VAR_UNKNOWN) | |
1220 return 1; | |
1221 | |
1222 prepare_vimvar(VV_VAL, &save_val); | |
1223 set_vim_var_string(VV_VAL, name, -1); | |
1224 argv[0].v_type = VAR_STRING; | |
1225 argv[0].vval.v_string = name; | |
1226 | |
1227 if (eval_expr_typval(expr, argv, 1, &rettv) == FAIL) | |
1228 goto theend; | |
1229 | |
1230 retval = tv_get_number_chk(&rettv, &error); | |
1231 if (error) | |
1232 retval = -1; | |
1233 clear_tv(&rettv); | |
1234 | |
1235 theend: | |
1236 set_vim_var_string(VV_VAL, NULL, 0); | |
1237 restore_vimvar(VV_VAL, &save_val); | |
1238 return retval; | |
1239 } | |
1240 | |
1241 /* | |
1242 * Create the directory in which "dir" is located, and higher levels when | |
1243 * needed. | |
1244 * Return OK or FAIL. | |
1245 */ | |
1246 static int | |
1247 mkdir_recurse(char_u *dir, int prot) | |
1248 { | |
1249 char_u *p; | |
1250 char_u *updir; | |
1251 int r = FAIL; | |
1252 | |
1253 /* Get end of directory name in "dir". | |
1254 * We're done when it's "/" or "c:/". */ | |
1255 p = gettail_sep(dir); | |
1256 if (p <= get_past_head(dir)) | |
1257 return OK; | |
1258 | |
1259 /* If the directory exists we're done. Otherwise: create it.*/ | |
1260 updir = vim_strnsave(dir, (int)(p - dir)); | |
1261 if (updir == NULL) | |
1262 return FAIL; | |
1263 if (mch_isdir(updir)) | |
1264 r = OK; | |
1265 else if (mkdir_recurse(updir, prot) == OK) | |
1266 r = vim_mkdir_emsg(updir, prot); | |
1267 vim_free(updir); | |
1268 return r; | |
1269 } | |
1270 | |
1271 /* | |
1272 * "mkdir()" function | |
1273 */ | |
1274 void | |
1275 f_mkdir(typval_T *argvars, typval_T *rettv) | |
1276 { | |
1277 char_u *dir; | |
1278 char_u buf[NUMBUFLEN]; | |
1279 int prot = 0755; | |
1280 | |
1281 rettv->vval.v_number = FAIL; | |
1282 if (check_restricted() || check_secure()) | |
1283 return; | |
1284 | |
1285 dir = tv_get_string_buf(&argvars[0], buf); | |
1286 if (*dir == NUL) | |
1287 return; | |
1288 | |
1289 if (*gettail(dir) == NUL) | |
1290 /* remove trailing slashes */ | |
1291 *gettail_sep(dir) = NUL; | |
1292 | |
1293 if (argvars[1].v_type != VAR_UNKNOWN) | |
1294 { | |
1295 if (argvars[2].v_type != VAR_UNKNOWN) | |
1296 { | |
1297 prot = (int)tv_get_number_chk(&argvars[2], NULL); | |
1298 if (prot == -1) | |
1299 return; | |
1300 } | |
1301 if (STRCMP(tv_get_string(&argvars[1]), "p") == 0) | |
1302 { | |
1303 if (mch_isdir(dir)) | |
1304 { | |
1305 /* With the "p" flag it's OK if the dir already exists. */ | |
1306 rettv->vval.v_number = OK; | |
1307 return; | |
1308 } | |
1309 mkdir_recurse(dir, prot); | |
1310 } | |
1311 } | |
1312 rettv->vval.v_number = vim_mkdir_emsg(dir, prot); | |
1313 } | |
1314 | |
1315 /* | |
1316 * "readdir()" function | |
1317 */ | |
1318 void | |
1319 f_readdir(typval_T *argvars, typval_T *rettv) | |
1320 { | |
1321 typval_T *expr; | |
1322 int ret; | |
1323 char_u *path; | |
1324 char_u *p; | |
1325 garray_T ga; | |
1326 int i; | |
1327 | |
1328 if (rettv_list_alloc(rettv) == FAIL) | |
1329 return; | |
1330 path = tv_get_string(&argvars[0]); | |
1331 expr = &argvars[1]; | |
1332 | |
1333 ret = readdir_core(&ga, path, (void *)expr, readdir_checkitem); | |
1334 if (ret == OK && rettv->vval.v_list != NULL && ga.ga_len > 0) | |
1335 { | |
1336 for (i = 0; i < ga.ga_len; i++) | |
1337 { | |
1338 p = ((char_u **)ga.ga_data)[i]; | |
1339 list_append_string(rettv->vval.v_list, p, -1); | |
1340 } | |
1341 } | |
1342 ga_clear_strings(&ga); | |
1343 } | |
1344 | |
1345 /* | |
1346 * "readfile()" function | |
1347 */ | |
1348 void | |
1349 f_readfile(typval_T *argvars, typval_T *rettv) | |
1350 { | |
1351 int binary = FALSE; | |
1352 int blob = FALSE; | |
1353 int failed = FALSE; | |
1354 char_u *fname; | |
1355 FILE *fd; | |
1356 char_u buf[(IOSIZE/256)*256]; /* rounded to avoid odd + 1 */ | |
1357 int io_size = sizeof(buf); | |
1358 int readlen; /* size of last fread() */ | |
1359 char_u *prev = NULL; /* previously read bytes, if any */ | |
1360 long prevlen = 0; /* length of data in prev */ | |
1361 long prevsize = 0; /* size of prev buffer */ | |
1362 long maxline = MAXLNUM; | |
1363 long cnt = 0; | |
1364 char_u *p; /* position in buf */ | |
1365 char_u *start; /* start of current line */ | |
1366 | |
1367 if (argvars[1].v_type != VAR_UNKNOWN) | |
1368 { | |
1369 if (STRCMP(tv_get_string(&argvars[1]), "b") == 0) | |
1370 binary = TRUE; | |
1371 if (STRCMP(tv_get_string(&argvars[1]), "B") == 0) | |
1372 blob = TRUE; | |
1373 | |
1374 if (argvars[2].v_type != VAR_UNKNOWN) | |
1375 maxline = (long)tv_get_number(&argvars[2]); | |
1376 } | |
1377 | |
1378 if (blob) | |
1379 { | |
1380 if (rettv_blob_alloc(rettv) == FAIL) | |
1381 return; | |
1382 } | |
1383 else | |
1384 { | |
1385 if (rettv_list_alloc(rettv) == FAIL) | |
1386 return; | |
1387 } | |
1388 | |
1389 /* Always open the file in binary mode, library functions have a mind of | |
1390 * their own about CR-LF conversion. */ | |
1391 fname = tv_get_string(&argvars[0]); | |
1392 if (*fname == NUL || (fd = mch_fopen((char *)fname, READBIN)) == NULL) | |
1393 { | |
1394 semsg(_(e_notopen), *fname == NUL ? (char_u *)_("<empty>") : fname); | |
1395 return; | |
1396 } | |
1397 | |
1398 if (blob) | |
1399 { | |
1400 if (read_blob(fd, rettv->vval.v_blob) == FAIL) | |
1401 { | |
1402 emsg("cannot read file"); | |
1403 blob_free(rettv->vval.v_blob); | |
1404 } | |
1405 fclose(fd); | |
1406 return; | |
1407 } | |
1408 | |
1409 while (cnt < maxline || maxline < 0) | |
1410 { | |
1411 readlen = (int)fread(buf, 1, io_size, fd); | |
1412 | |
1413 /* This for loop processes what was read, but is also entered at end | |
1414 * of file so that either: | |
1415 * - an incomplete line gets written | |
1416 * - a "binary" file gets an empty line at the end if it ends in a | |
1417 * newline. */ | |
1418 for (p = buf, start = buf; | |
1419 p < buf + readlen || (readlen <= 0 && (prevlen > 0 || binary)); | |
1420 ++p) | |
1421 { | |
1422 if (*p == '\n' || readlen <= 0) | |
1423 { | |
1424 listitem_T *li; | |
1425 char_u *s = NULL; | |
1426 long_u len = p - start; | |
1427 | |
1428 /* Finished a line. Remove CRs before NL. */ | |
1429 if (readlen > 0 && !binary) | |
1430 { | |
1431 while (len > 0 && start[len - 1] == '\r') | |
1432 --len; | |
1433 /* removal may cross back to the "prev" string */ | |
1434 if (len == 0) | |
1435 while (prevlen > 0 && prev[prevlen - 1] == '\r') | |
1436 --prevlen; | |
1437 } | |
1438 if (prevlen == 0) | |
1439 s = vim_strnsave(start, (int)len); | |
1440 else | |
1441 { | |
1442 /* Change "prev" buffer to be the right size. This way | |
1443 * the bytes are only copied once, and very long lines are | |
1444 * allocated only once. */ | |
1445 if ((s = vim_realloc(prev, prevlen + len + 1)) != NULL) | |
1446 { | |
1447 mch_memmove(s + prevlen, start, len); | |
1448 s[prevlen + len] = NUL; | |
1449 prev = NULL; /* the list will own the string */ | |
1450 prevlen = prevsize = 0; | |
1451 } | |
1452 } | |
1453 if (s == NULL) | |
1454 { | |
1455 do_outofmem_msg((long_u) prevlen + len + 1); | |
1456 failed = TRUE; | |
1457 break; | |
1458 } | |
1459 | |
1460 if ((li = listitem_alloc()) == NULL) | |
1461 { | |
1462 vim_free(s); | |
1463 failed = TRUE; | |
1464 break; | |
1465 } | |
1466 li->li_tv.v_type = VAR_STRING; | |
1467 li->li_tv.v_lock = 0; | |
1468 li->li_tv.vval.v_string = s; | |
1469 list_append(rettv->vval.v_list, li); | |
1470 | |
1471 start = p + 1; /* step over newline */ | |
1472 if ((++cnt >= maxline && maxline >= 0) || readlen <= 0) | |
1473 break; | |
1474 } | |
1475 else if (*p == NUL) | |
1476 *p = '\n'; | |
1477 /* Check for utf8 "bom"; U+FEFF is encoded as EF BB BF. Do this | |
1478 * when finding the BF and check the previous two bytes. */ | |
1479 else if (*p == 0xbf && enc_utf8 && !binary) | |
1480 { | |
1481 /* Find the two bytes before the 0xbf. If p is at buf, or buf | |
1482 * + 1, these may be in the "prev" string. */ | |
1483 char_u back1 = p >= buf + 1 ? p[-1] | |
1484 : prevlen >= 1 ? prev[prevlen - 1] : NUL; | |
1485 char_u back2 = p >= buf + 2 ? p[-2] | |
1486 : p == buf + 1 && prevlen >= 1 ? prev[prevlen - 1] | |
1487 : prevlen >= 2 ? prev[prevlen - 2] : NUL; | |
1488 | |
1489 if (back2 == 0xef && back1 == 0xbb) | |
1490 { | |
1491 char_u *dest = p - 2; | |
1492 | |
1493 /* Usually a BOM is at the beginning of a file, and so at | |
1494 * the beginning of a line; then we can just step over it. | |
1495 */ | |
1496 if (start == dest) | |
1497 start = p + 1; | |
1498 else | |
1499 { | |
1500 /* have to shuffle buf to close gap */ | |
1501 int adjust_prevlen = 0; | |
1502 | |
1503 if (dest < buf) | |
1504 { | |
1505 adjust_prevlen = (int)(buf - dest); /* must be 1 or 2 */ | |
1506 dest = buf; | |
1507 } | |
1508 if (readlen > p - buf + 1) | |
1509 mch_memmove(dest, p + 1, readlen - (p - buf) - 1); | |
1510 readlen -= 3 - adjust_prevlen; | |
1511 prevlen -= adjust_prevlen; | |
1512 p = dest - 1; | |
1513 } | |
1514 } | |
1515 } | |
1516 } /* for */ | |
1517 | |
1518 if (failed || (cnt >= maxline && maxline >= 0) || readlen <= 0) | |
1519 break; | |
1520 if (start < p) | |
1521 { | |
1522 /* There's part of a line in buf, store it in "prev". */ | |
1523 if (p - start + prevlen >= prevsize) | |
1524 { | |
1525 /* need bigger "prev" buffer */ | |
1526 char_u *newprev; | |
1527 | |
1528 /* A common use case is ordinary text files and "prev" gets a | |
1529 * fragment of a line, so the first allocation is made | |
1530 * small, to avoid repeatedly 'allocing' large and | |
1531 * 'reallocing' small. */ | |
1532 if (prevsize == 0) | |
1533 prevsize = (long)(p - start); | |
1534 else | |
1535 { | |
1536 long grow50pc = (prevsize * 3) / 2; | |
1537 long growmin = (long)((p - start) * 2 + prevlen); | |
1538 prevsize = grow50pc > growmin ? grow50pc : growmin; | |
1539 } | |
1540 newprev = vim_realloc(prev, prevsize); | |
1541 if (newprev == NULL) | |
1542 { | |
1543 do_outofmem_msg((long_u)prevsize); | |
1544 failed = TRUE; | |
1545 break; | |
1546 } | |
1547 prev = newprev; | |
1548 } | |
1549 /* Add the line part to end of "prev". */ | |
1550 mch_memmove(prev + prevlen, start, p - start); | |
1551 prevlen += (long)(p - start); | |
1552 } | |
1553 } /* while */ | |
1554 | |
1555 /* | |
1556 * For a negative line count use only the lines at the end of the file, | |
1557 * free the rest. | |
1558 */ | |
1559 if (!failed && maxline < 0) | |
1560 while (cnt > -maxline) | |
1561 { | |
1562 listitem_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first); | |
1563 --cnt; | |
1564 } | |
1565 | |
1566 if (failed) | |
1567 { | |
1568 // an empty list is returned on error | |
1569 list_free(rettv->vval.v_list); | |
1570 rettv_list_alloc(rettv); | |
1571 } | |
1572 | |
1573 vim_free(prev); | |
1574 fclose(fd); | |
1575 } | |
1576 | |
1577 /* | |
1578 * "resolve()" function | |
1579 */ | |
1580 void | |
1581 f_resolve(typval_T *argvars, typval_T *rettv) | |
1582 { | |
1583 char_u *p; | |
1584 #ifdef HAVE_READLINK | |
1585 char_u *buf = NULL; | |
1586 #endif | |
1587 | |
1588 p = tv_get_string(&argvars[0]); | |
1589 #ifdef FEAT_SHORTCUT | |
1590 { | |
1591 char_u *v = NULL; | |
1592 | |
1593 v = mch_resolve_path(p, TRUE); | |
1594 if (v != NULL) | |
1595 rettv->vval.v_string = v; | |
1596 else | |
1597 rettv->vval.v_string = vim_strsave(p); | |
1598 } | |
1599 #else | |
1600 # ifdef HAVE_READLINK | |
1601 { | |
1602 char_u *cpy; | |
1603 int len; | |
1604 char_u *remain = NULL; | |
1605 char_u *q; | |
1606 int is_relative_to_current = FALSE; | |
1607 int has_trailing_pathsep = FALSE; | |
1608 int limit = 100; | |
1609 | |
1610 p = vim_strsave(p); | |
1611 | |
1612 if (p[0] == '.' && (vim_ispathsep(p[1]) | |
1613 || (p[1] == '.' && (vim_ispathsep(p[2]))))) | |
1614 is_relative_to_current = TRUE; | |
1615 | |
1616 len = STRLEN(p); | |
1617 if (len > 0 && after_pathsep(p, p + len)) | |
1618 { | |
1619 has_trailing_pathsep = TRUE; | |
1620 p[len - 1] = NUL; /* the trailing slash breaks readlink() */ | |
1621 } | |
1622 | |
1623 q = getnextcomp(p); | |
1624 if (*q != NUL) | |
1625 { | |
1626 /* Separate the first path component in "p", and keep the | |
1627 * remainder (beginning with the path separator). */ | |
1628 remain = vim_strsave(q - 1); | |
1629 q[-1] = NUL; | |
1630 } | |
1631 | |
1632 buf = alloc(MAXPATHL + 1); | |
1633 if (buf == NULL) | |
1634 goto fail; | |
1635 | |
1636 for (;;) | |
1637 { | |
1638 for (;;) | |
1639 { | |
1640 len = readlink((char *)p, (char *)buf, MAXPATHL); | |
1641 if (len <= 0) | |
1642 break; | |
1643 buf[len] = NUL; | |
1644 | |
1645 if (limit-- == 0) | |
1646 { | |
1647 vim_free(p); | |
1648 vim_free(remain); | |
1649 emsg(_("E655: Too many symbolic links (cycle?)")); | |
1650 rettv->vval.v_string = NULL; | |
1651 goto fail; | |
1652 } | |
1653 | |
1654 /* Ensure that the result will have a trailing path separator | |
1655 * if the argument has one. */ | |
1656 if (remain == NULL && has_trailing_pathsep) | |
1657 add_pathsep(buf); | |
1658 | |
1659 /* Separate the first path component in the link value and | |
1660 * concatenate the remainders. */ | |
1661 q = getnextcomp(vim_ispathsep(*buf) ? buf + 1 : buf); | |
1662 if (*q != NUL) | |
1663 { | |
1664 if (remain == NULL) | |
1665 remain = vim_strsave(q - 1); | |
1666 else | |
1667 { | |
1668 cpy = concat_str(q - 1, remain); | |
1669 if (cpy != NULL) | |
1670 { | |
1671 vim_free(remain); | |
1672 remain = cpy; | |
1673 } | |
1674 } | |
1675 q[-1] = NUL; | |
1676 } | |
1677 | |
1678 q = gettail(p); | |
1679 if (q > p && *q == NUL) | |
1680 { | |
1681 /* Ignore trailing path separator. */ | |
1682 q[-1] = NUL; | |
1683 q = gettail(p); | |
1684 } | |
1685 if (q > p && !mch_isFullName(buf)) | |
1686 { | |
1687 /* symlink is relative to directory of argument */ | |
1688 cpy = alloc(STRLEN(p) + STRLEN(buf) + 1); | |
1689 if (cpy != NULL) | |
1690 { | |
1691 STRCPY(cpy, p); | |
1692 STRCPY(gettail(cpy), buf); | |
1693 vim_free(p); | |
1694 p = cpy; | |
1695 } | |
1696 } | |
1697 else | |
1698 { | |
1699 vim_free(p); | |
1700 p = vim_strsave(buf); | |
1701 } | |
1702 } | |
1703 | |
1704 if (remain == NULL) | |
1705 break; | |
1706 | |
1707 /* Append the first path component of "remain" to "p". */ | |
1708 q = getnextcomp(remain + 1); | |
1709 len = q - remain - (*q != NUL); | |
1710 cpy = vim_strnsave(p, STRLEN(p) + len); | |
1711 if (cpy != NULL) | |
1712 { | |
1713 STRNCAT(cpy, remain, len); | |
1714 vim_free(p); | |
1715 p = cpy; | |
1716 } | |
1717 /* Shorten "remain". */ | |
1718 if (*q != NUL) | |
1719 STRMOVE(remain, q - 1); | |
1720 else | |
1721 VIM_CLEAR(remain); | |
1722 } | |
1723 | |
1724 /* If the result is a relative path name, make it explicitly relative to | |
1725 * the current directory if and only if the argument had this form. */ | |
1726 if (!vim_ispathsep(*p)) | |
1727 { | |
1728 if (is_relative_to_current | |
1729 && *p != NUL | |
1730 && !(p[0] == '.' | |
1731 && (p[1] == NUL | |
1732 || vim_ispathsep(p[1]) | |
1733 || (p[1] == '.' | |
1734 && (p[2] == NUL | |
1735 || vim_ispathsep(p[2])))))) | |
1736 { | |
1737 /* Prepend "./". */ | |
1738 cpy = concat_str((char_u *)"./", p); | |
1739 if (cpy != NULL) | |
1740 { | |
1741 vim_free(p); | |
1742 p = cpy; | |
1743 } | |
1744 } | |
1745 else if (!is_relative_to_current) | |
1746 { | |
1747 /* Strip leading "./". */ | |
1748 q = p; | |
1749 while (q[0] == '.' && vim_ispathsep(q[1])) | |
1750 q += 2; | |
1751 if (q > p) | |
1752 STRMOVE(p, p + 2); | |
1753 } | |
1754 } | |
1755 | |
1756 /* Ensure that the result will have no trailing path separator | |
1757 * if the argument had none. But keep "/" or "//". */ | |
1758 if (!has_trailing_pathsep) | |
1759 { | |
1760 q = p + STRLEN(p); | |
1761 if (after_pathsep(p, q)) | |
1762 *gettail_sep(p) = NUL; | |
1763 } | |
1764 | |
1765 rettv->vval.v_string = p; | |
1766 } | |
1767 # else | |
1768 rettv->vval.v_string = vim_strsave(p); | |
1769 # endif | |
1770 #endif | |
1771 | |
1772 simplify_filename(rettv->vval.v_string); | |
1773 | |
1774 #ifdef HAVE_READLINK | |
1775 fail: | |
1776 vim_free(buf); | |
1777 #endif | |
1778 rettv->v_type = VAR_STRING; | |
1779 } | |
1780 | |
1781 /* | |
1782 * "tempname()" function | |
1783 */ | |
1784 void | |
1785 f_tempname(typval_T *argvars UNUSED, typval_T *rettv) | |
1786 { | |
1787 static int x = 'A'; | |
1788 | |
1789 rettv->v_type = VAR_STRING; | |
1790 rettv->vval.v_string = vim_tempname(x, FALSE); | |
1791 | |
1792 /* Advance 'x' to use A-Z and 0-9, so that there are at least 34 different | |
1793 * names. Skip 'I' and 'O', they are used for shell redirection. */ | |
1794 do | |
1795 { | |
1796 if (x == 'Z') | |
1797 x = '0'; | |
1798 else if (x == '9') | |
1799 x = 'A'; | |
1800 else | |
1801 { | |
1802 #ifdef EBCDIC | |
1803 if (x == 'I') | |
1804 x = 'J'; | |
1805 else if (x == 'R') | |
1806 x = 'S'; | |
1807 else | |
1808 #endif | |
1809 ++x; | |
1810 } | |
1811 } while (x == 'I' || x == 'O'); | |
1812 } | |
1813 | |
1814 /* | |
1815 * "writefile()" function | |
1816 */ | |
1817 void | |
1818 f_writefile(typval_T *argvars, typval_T *rettv) | |
1819 { | |
1820 int binary = FALSE; | |
1821 int append = FALSE; | |
1822 #ifdef HAVE_FSYNC | |
1823 int do_fsync = p_fs; | |
1824 #endif | |
1825 char_u *fname; | |
1826 FILE *fd; | |
1827 int ret = 0; | |
1828 listitem_T *li; | |
1829 list_T *list = NULL; | |
1830 blob_T *blob = NULL; | |
1831 | |
1832 rettv->vval.v_number = -1; | |
1833 if (check_secure()) | |
1834 return; | |
1835 | |
1836 if (argvars[0].v_type == VAR_LIST) | |
1837 { | |
1838 list = argvars[0].vval.v_list; | |
1839 if (list == NULL) | |
1840 return; | |
1841 for (li = list->lv_first; li != NULL; li = li->li_next) | |
1842 if (tv_get_string_chk(&li->li_tv) == NULL) | |
1843 return; | |
1844 } | |
1845 else if (argvars[0].v_type == VAR_BLOB) | |
1846 { | |
1847 blob = argvars[0].vval.v_blob; | |
1848 if (blob == NULL) | |
1849 return; | |
1850 } | |
1851 else | |
1852 { | |
1853 semsg(_(e_invarg2), "writefile()"); | |
1854 return; | |
1855 } | |
1856 | |
1857 if (argvars[2].v_type != VAR_UNKNOWN) | |
1858 { | |
1859 char_u *arg2 = tv_get_string_chk(&argvars[2]); | |
1860 | |
1861 if (arg2 == NULL) | |
1862 return; | |
1863 if (vim_strchr(arg2, 'b') != NULL) | |
1864 binary = TRUE; | |
1865 if (vim_strchr(arg2, 'a') != NULL) | |
1866 append = TRUE; | |
1867 #ifdef HAVE_FSYNC | |
1868 if (vim_strchr(arg2, 's') != NULL) | |
1869 do_fsync = TRUE; | |
1870 else if (vim_strchr(arg2, 'S') != NULL) | |
1871 do_fsync = FALSE; | |
1872 #endif | |
1873 } | |
1874 | |
1875 fname = tv_get_string_chk(&argvars[1]); | |
1876 if (fname == NULL) | |
1877 return; | |
1878 | |
1879 /* Always open the file in binary mode, library functions have a mind of | |
1880 * their own about CR-LF conversion. */ | |
1881 if (*fname == NUL || (fd = mch_fopen((char *)fname, | |
1882 append ? APPENDBIN : WRITEBIN)) == NULL) | |
1883 { | |
1884 semsg(_(e_notcreate), *fname == NUL ? (char_u *)_("<empty>") : fname); | |
1885 ret = -1; | |
1886 } | |
1887 else if (blob) | |
1888 { | |
1889 if (write_blob(fd, blob) == FAIL) | |
1890 ret = -1; | |
1891 #ifdef HAVE_FSYNC | |
1892 else if (do_fsync) | |
1893 // Ignore the error, the user wouldn't know what to do about it. | |
1894 // May happen for a device. | |
1895 vim_ignored = vim_fsync(fileno(fd)); | |
1896 #endif | |
1897 fclose(fd); | |
1898 } | |
1899 else | |
1900 { | |
1901 if (write_list(fd, list, binary) == FAIL) | |
1902 ret = -1; | |
1903 #ifdef HAVE_FSYNC | |
1904 else if (do_fsync) | |
1905 /* Ignore the error, the user wouldn't know what to do about it. | |
1906 * May happen for a device. */ | |
1907 vim_ignored = vim_fsync(fileno(fd)); | |
1908 #endif | |
1909 fclose(fd); | |
1910 } | |
1911 | |
1912 rettv->vval.v_number = ret; | |
1913 } | |
1914 | |
1915 #endif // FEAT_EVAL | |
1916 | |
1917 #if defined(FEAT_BROWSE) || defined(PROTO) | |
1918 /* | |
1919 * Generic browse function. Calls gui_mch_browse() when possible. | |
1920 * Later this may pop-up a non-GUI file selector (external command?). | |
1921 */ | |
1922 char_u * | |
1923 do_browse( | |
1924 int flags, /* BROWSE_SAVE and BROWSE_DIR */ | |
1925 char_u *title, /* title for the window */ | |
1926 char_u *dflt, /* default file name (may include directory) */ | |
1927 char_u *ext, /* extension added */ | |
1928 char_u *initdir, /* initial directory, NULL for current dir or | |
1929 when using path from "dflt" */ | |
1930 char_u *filter, /* file name filter */ | |
1931 buf_T *buf) /* buffer to read/write for */ | |
1932 { | |
1933 char_u *fname; | |
1934 static char_u *last_dir = NULL; /* last used directory */ | |
1935 char_u *tofree = NULL; | |
1936 int save_browse = cmdmod.browse; | |
1937 | |
1938 /* Must turn off browse to avoid that autocommands will get the | |
1939 * flag too! */ | |
1940 cmdmod.browse = FALSE; | |
1941 | |
1942 if (title == NULL || *title == NUL) | |
1943 { | |
1944 if (flags & BROWSE_DIR) | |
1945 title = (char_u *)_("Select Directory dialog"); | |
1946 else if (flags & BROWSE_SAVE) | |
1947 title = (char_u *)_("Save File dialog"); | |
1948 else | |
1949 title = (char_u *)_("Open File dialog"); | |
1950 } | |
1951 | |
1952 /* When no directory specified, use default file name, default dir, buffer | |
1953 * dir, last dir or current dir */ | |
1954 if ((initdir == NULL || *initdir == NUL) && dflt != NULL && *dflt != NUL) | |
1955 { | |
1956 if (mch_isdir(dflt)) /* default file name is a directory */ | |
1957 { | |
1958 initdir = dflt; | |
1959 dflt = NULL; | |
1960 } | |
1961 else if (gettail(dflt) != dflt) /* default file name includes a path */ | |
1962 { | |
1963 tofree = vim_strsave(dflt); | |
1964 if (tofree != NULL) | |
1965 { | |
1966 initdir = tofree; | |
1967 *gettail(initdir) = NUL; | |
1968 dflt = gettail(dflt); | |
1969 } | |
1970 } | |
1971 } | |
1972 | |
1973 if (initdir == NULL || *initdir == NUL) | |
1974 { | |
1975 /* When 'browsedir' is a directory, use it */ | |
1976 if (STRCMP(p_bsdir, "last") != 0 | |
1977 && STRCMP(p_bsdir, "buffer") != 0 | |
1978 && STRCMP(p_bsdir, "current") != 0 | |
1979 && mch_isdir(p_bsdir)) | |
1980 initdir = p_bsdir; | |
1981 /* When saving or 'browsedir' is "buffer", use buffer fname */ | |
1982 else if (((flags & BROWSE_SAVE) || *p_bsdir == 'b') | |
1983 && buf != NULL && buf->b_ffname != NULL) | |
1984 { | |
1985 if (dflt == NULL || *dflt == NUL) | |
1986 dflt = gettail(curbuf->b_ffname); | |
1987 tofree = vim_strsave(curbuf->b_ffname); | |
1988 if (tofree != NULL) | |
1989 { | |
1990 initdir = tofree; | |
1991 *gettail(initdir) = NUL; | |
1992 } | |
1993 } | |
1994 /* When 'browsedir' is "last", use dir from last browse */ | |
1995 else if (*p_bsdir == 'l') | |
1996 initdir = last_dir; | |
1997 /* When 'browsedir is "current", use current directory. This is the | |
1998 * default already, leave initdir empty. */ | |
1999 } | |
2000 | |
2001 # ifdef FEAT_GUI | |
2002 if (gui.in_use) /* when this changes, also adjust f_has()! */ | |
2003 { | |
2004 if (filter == NULL | |
2005 # ifdef FEAT_EVAL | |
2006 && (filter = get_var_value((char_u *)"b:browsefilter")) == NULL | |
2007 && (filter = get_var_value((char_u *)"g:browsefilter")) == NULL | |
2008 # endif | |
2009 ) | |
2010 filter = BROWSE_FILTER_DEFAULT; | |
2011 if (flags & BROWSE_DIR) | |
2012 { | |
2013 # if defined(FEAT_GUI_GTK) || defined(MSWIN) | |
2014 /* For systems that have a directory dialog. */ | |
2015 fname = gui_mch_browsedir(title, initdir); | |
2016 # else | |
2017 /* Generic solution for selecting a directory: select a file and | |
2018 * remove the file name. */ | |
2019 fname = gui_mch_browse(0, title, dflt, ext, initdir, (char_u *)""); | |
2020 # endif | |
2021 # if !defined(FEAT_GUI_GTK) | |
2022 /* Win32 adds a dummy file name, others return an arbitrary file | |
2023 * name. GTK+ 2 returns only the directory, */ | |
2024 if (fname != NULL && *fname != NUL && !mch_isdir(fname)) | |
2025 { | |
2026 /* Remove the file name. */ | |
2027 char_u *tail = gettail_sep(fname); | |
2028 | |
2029 if (tail == fname) | |
2030 *tail++ = '.'; /* use current dir */ | |
2031 *tail = NUL; | |
2032 } | |
2033 # endif | |
2034 } | |
2035 else | |
2036 fname = gui_mch_browse(flags & BROWSE_SAVE, | |
2037 title, dflt, ext, initdir, (char_u *)_(filter)); | |
2038 | |
2039 /* We hang around in the dialog for a while, the user might do some | |
2040 * things to our files. The Win32 dialog allows deleting or renaming | |
2041 * a file, check timestamps. */ | |
2042 need_check_timestamps = TRUE; | |
2043 did_check_timestamps = FALSE; | |
2044 } | |
2045 else | |
2046 # endif | |
2047 { | |
2048 /* TODO: non-GUI file selector here */ | |
2049 emsg(_("E338: Sorry, no file browser in console mode")); | |
2050 fname = NULL; | |
2051 } | |
2052 | |
2053 /* keep the directory for next time */ | |
2054 if (fname != NULL) | |
2055 { | |
2056 vim_free(last_dir); | |
2057 last_dir = vim_strsave(fname); | |
2058 if (last_dir != NULL && !(flags & BROWSE_DIR)) | |
2059 { | |
2060 *gettail(last_dir) = NUL; | |
2061 if (*last_dir == NUL) | |
2062 { | |
2063 /* filename only returned, must be in current dir */ | |
2064 vim_free(last_dir); | |
2065 last_dir = alloc(MAXPATHL); | |
2066 if (last_dir != NULL) | |
2067 mch_dirname(last_dir, MAXPATHL); | |
2068 } | |
2069 } | |
2070 } | |
2071 | |
2072 vim_free(tofree); | |
2073 cmdmod.browse = save_browse; | |
2074 | |
2075 return fname; | |
2076 } | |
2077 #endif | |
2078 | |
2079 #if defined(FEAT_EVAL) || defined(PROTO) | |
2080 | |
2081 /* | |
2082 * "browse(save, title, initdir, default)" function | |
2083 */ | |
2084 void | |
2085 f_browse(typval_T *argvars UNUSED, typval_T *rettv) | |
2086 { | |
2087 # ifdef FEAT_BROWSE | |
2088 int save; | |
2089 char_u *title; | |
2090 char_u *initdir; | |
2091 char_u *defname; | |
2092 char_u buf[NUMBUFLEN]; | |
2093 char_u buf2[NUMBUFLEN]; | |
2094 int error = FALSE; | |
2095 | |
2096 save = (int)tv_get_number_chk(&argvars[0], &error); | |
2097 title = tv_get_string_chk(&argvars[1]); | |
2098 initdir = tv_get_string_buf_chk(&argvars[2], buf); | |
2099 defname = tv_get_string_buf_chk(&argvars[3], buf2); | |
2100 | |
2101 if (error || title == NULL || initdir == NULL || defname == NULL) | |
2102 rettv->vval.v_string = NULL; | |
2103 else | |
2104 rettv->vval.v_string = | |
2105 do_browse(save ? BROWSE_SAVE : 0, | |
2106 title, defname, NULL, initdir, NULL, curbuf); | |
2107 # else | |
2108 rettv->vval.v_string = NULL; | |
2109 # endif | |
2110 rettv->v_type = VAR_STRING; | |
2111 } | |
2112 | |
2113 /* | |
2114 * "browsedir(title, initdir)" function | |
2115 */ | |
2116 void | |
2117 f_browsedir(typval_T *argvars UNUSED, typval_T *rettv) | |
2118 { | |
2119 # ifdef FEAT_BROWSE | |
2120 char_u *title; | |
2121 char_u *initdir; | |
2122 char_u buf[NUMBUFLEN]; | |
2123 | |
2124 title = tv_get_string_chk(&argvars[0]); | |
2125 initdir = tv_get_string_buf_chk(&argvars[1], buf); | |
2126 | |
2127 if (title == NULL || initdir == NULL) | |
2128 rettv->vval.v_string = NULL; | |
2129 else | |
2130 rettv->vval.v_string = do_browse(BROWSE_DIR, | |
2131 title, NULL, NULL, initdir, NULL, curbuf); | |
2132 # else | |
2133 rettv->vval.v_string = NULL; | |
2134 # endif | |
2135 rettv->v_type = VAR_STRING; | |
2136 } | |
2137 | |
2138 #endif // FEAT_EVAL |