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