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 and 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 WCHAR *newbuf; 34 WCHAR *wfname; 35 36 len = MAXPATHL; 37 newbuf = malloc(len * sizeof(*newbuf)); 38 if (newbuf == NULL) 39 return FAIL; 40 41 wfname = enc_to_utf16(*fnamep, NULL); 42 if (wfname == NULL) 43 { 44 vim_free(newbuf); 45 return FAIL; 46 } 47 48 l = GetShortPathNameW(wfname, newbuf, len); 49 if (l > len - 1) 50 { 51 // If that doesn't work (not enough space), then save the string 52 // and try again with a new buffer big enough. 53 WCHAR *newbuf_t = newbuf; 54 newbuf = vim_realloc(newbuf, (l + 1) * sizeof(*newbuf)); 55 if (newbuf == NULL) 56 { 57 vim_free(wfname); 58 vim_free(newbuf_t); 59 return FAIL; 60 } 61 // Really should always succeed, as the buffer is big enough. 62 l = GetShortPathNameW(wfname, newbuf, l+1); 63 } 64 if (l != 0) 65 { 66 char_u *p = utf16_to_enc(newbuf, NULL); 67 68 if (p != NULL) 69 { 70 vim_free(*bufp); 71 *fnamep = *bufp = p; 72 } 73 else 74 { 75 vim_free(wfname); 76 vim_free(newbuf); 77 return FAIL; 78 } 79 } 80 vim_free(wfname); 81 vim_free(newbuf); 82 83 *fnamelen = l == 0 ? l : (int)STRLEN(*bufp); 84 return OK; 85 } 86 87 /* 88 * Get the short path (8.3) for the filename in "fname". The converted 89 * path is returned in "bufp". 90 * 91 * Some of the directories specified in "fname" may not exist. This function 92 * will shorten the existing directories at the beginning of the path and then 93 * append the remaining non-existing path. 94 * 95 * fname - Pointer to the filename to shorten. On return, contains the 96 * pointer to the shortened pathname 97 * bufp - Pointer to an allocated buffer for the filename. 98 * fnamelen - Length of the filename pointed to by fname 99 * 100 * Returns OK on success (or nothing done) and FAIL on failure (out of memory). 101 */ 102 static int 103 shortpath_for_invalid_fname( 104 char_u **fname, 105 char_u **bufp, 106 int *fnamelen) 107 { 108 char_u *short_fname, *save_fname, *pbuf_unused; 109 char_u *endp, *save_endp; 110 char_u ch; 111 int old_len, len; 112 int new_len, sfx_len; 113 int retval = OK; 114 115 // Make a copy 116 old_len = *fnamelen; 117 save_fname = vim_strnsave(*fname, old_len); 118 pbuf_unused = NULL; 119 short_fname = NULL; 120 121 endp = save_fname + old_len - 1; // Find the end of the copy 122 save_endp = endp; 123 124 /* 125 * Try shortening the supplied path till it succeeds by removing one 126 * directory at a time from the tail of the path. 127 */ 128 len = 0; 129 for (;;) 130 { 131 // go back one path-separator 132 while (endp > save_fname && !after_pathsep(save_fname, endp + 1)) 133 --endp; 134 if (endp <= save_fname) 135 break; // processed the complete path 136 137 /* 138 * Replace the path separator with a NUL and try to shorten the 139 * resulting path. 140 */ 141 ch = *endp; 142 *endp = 0; 143 short_fname = save_fname; 144 len = (int)STRLEN(short_fname) + 1; 145 if (get_short_pathname(&short_fname, &pbuf_unused, &len) == FAIL) 146 { 147 retval = FAIL; 148 goto theend; 149 } 150 *endp = ch; // preserve the string 151 152 if (len > 0) 153 break; // successfully shortened the path 154 155 // failed to shorten the path. Skip the path separator 156 --endp; 157 } 158 159 if (len > 0) 160 { 161 /* 162 * Succeeded in shortening the path. Now concatenate the shortened 163 * path with the remaining path at the tail. 164 */ 165 166 // Compute the length of the new path. 167 sfx_len = (int)(save_endp - endp) + 1; 168 new_len = len + sfx_len; 169 170 *fnamelen = new_len; 171 vim_free(*bufp); 172 if (new_len > old_len) 173 { 174 // There is not enough space in the currently allocated string, 175 // copy it to a buffer big enough. 176 *fname = *bufp = vim_strnsave(short_fname, new_len); 177 if (*fname == NULL) 178 { 179 retval = FAIL; 180 goto theend; 181 } 182 } 183 else 184 { 185 // Transfer short_fname to the main buffer (it's big enough), 186 // unless get_short_pathname() did its work in-place. 187 *fname = *bufp = save_fname; 188 if (short_fname != save_fname) 189 vim_strncpy(save_fname, short_fname, len); 190 save_fname = NULL; 191 } 192 193 // concat the not-shortened part of the path 194 vim_strncpy(*fname + len, endp, sfx_len); 195 (*fname)[new_len] = NUL; 196 } 197 198 theend: 199 vim_free(pbuf_unused); 200 vim_free(save_fname); 201 202 return retval; 203 } 204 205 /* 206 * Get a pathname for a partial path. 207 * Returns OK for success, FAIL for failure. 208 */ 209 static int 210 shortpath_for_partial( 211 char_u **fnamep, 212 char_u **bufp, 213 int *fnamelen) 214 { 215 int sepcount, len, tflen; 216 char_u *p; 217 char_u *pbuf, *tfname; 218 int hasTilde; 219 220 // Count up the path separators from the RHS.. so we know which part 221 // of the path to return. 222 sepcount = 0; 223 for (p = *fnamep; p < *fnamep + *fnamelen; MB_PTR_ADV(p)) 224 if (vim_ispathsep(*p)) 225 ++sepcount; 226 227 // Need full path first (use expand_env() to remove a "~/") 228 hasTilde = (**fnamep == '~'); 229 if (hasTilde) 230 pbuf = tfname = expand_env_save(*fnamep); 231 else 232 pbuf = tfname = FullName_save(*fnamep, FALSE); 233 234 len = tflen = (int)STRLEN(tfname); 235 236 if (get_short_pathname(&tfname, &pbuf, &len) == FAIL) 237 return FAIL; 238 239 if (len == 0) 240 { 241 // Don't have a valid filename, so shorten the rest of the 242 // path if we can. This CAN give us invalid 8.3 filenames, but 243 // there's not a lot of point in guessing what it might be. 244 len = tflen; 245 if (shortpath_for_invalid_fname(&tfname, &pbuf, &len) == FAIL) 246 return FAIL; 247 } 248 249 // Count the paths backward to find the beginning of the desired string. 250 for (p = tfname + len - 1; p >= tfname; --p) 251 { 252 if (has_mbyte) 253 p -= mb_head_off(tfname, p); 254 if (vim_ispathsep(*p)) 255 { 256 if (sepcount == 0 || (hasTilde && sepcount == 1)) 257 break; 258 else 259 sepcount --; 260 } 261 } 262 if (hasTilde) 263 { 264 --p; 265 if (p >= tfname) 266 *p = '~'; 267 else 268 return FAIL; 269 } 270 else 271 ++p; 272 273 // Copy in the string - p indexes into tfname - allocated at pbuf 274 vim_free(*bufp); 275 *fnamelen = (int)STRLEN(p); 276 *bufp = pbuf; 277 *fnamep = p; 278 279 return OK; 280 } 281 #endif // MSWIN 282 283 /* 284 * Adjust a filename, according to a string of modifiers. 285 * *fnamep must be NUL terminated when called. When returning, the length is 286 * determined by *fnamelen. 287 * Returns VALID_ flags or -1 for failure. 288 * When there is an error, *fnamep is set to NULL. 289 */ 290 int 291 modify_fname( 292 char_u *src, // string with modifiers 293 int tilde_file, // "~" is a file name, not $HOME 294 int *usedlen, // characters after src that are used 295 char_u **fnamep, // file name so far 296 char_u **bufp, // buffer for allocated file name or NULL 297 int *fnamelen) // length of fnamep 298 { 299 int valid = 0; 300 char_u *tail; 301 char_u *s, *p, *pbuf; 302 char_u dirname[MAXPATHL]; 303 int c; 304 int has_fullname = 0; 305 int has_homerelative = 0; 306 #ifdef MSWIN 307 char_u *fname_start = *fnamep; 308 int has_shortname = 0; 309 #endif 310 311 repeat: 312 // ":p" - full path/file_name 313 if (src[*usedlen] == ':' && src[*usedlen + 1] == 'p') 314 { 315 has_fullname = 1; 316 317 valid |= VALID_PATH; 318 *usedlen += 2; 319 320 // Expand "~/path" for all systems and "~user/path" for Unix and VMS 321 if ((*fnamep)[0] == '~' 322 #if !defined(UNIX) && !(defined(VMS) && defined(USER_HOME)) 323 && ((*fnamep)[1] == '/' 324 # ifdef BACKSLASH_IN_FILENAME 325 || (*fnamep)[1] == '\\' 326 # endif 327 || (*fnamep)[1] == NUL) 328 #endif 329 && !(tilde_file && (*fnamep)[1] == NUL) 330 ) 331 { 332 *fnamep = expand_env_save(*fnamep); 333 vim_free(*bufp); // free any allocated file name 334 *bufp = *fnamep; 335 if (*fnamep == NULL) 336 return -1; 337 } 338 339 // When "/." or "/.." is used: force expansion to get rid of it. 340 for (p = *fnamep; *p != NUL; MB_PTR_ADV(p)) 341 { 342 if (vim_ispathsep(*p) 343 && p[1] == '.' 344 && (p[2] == NUL 345 || vim_ispathsep(p[2]) 346 || (p[2] == '.' 347 && (p[3] == NUL || vim_ispathsep(p[3]))))) 348 break; 349 } 350 351 // FullName_save() is slow, don't use it when not needed. 352 if (*p != NUL || !vim_isAbsName(*fnamep)) 353 { 354 *fnamep = FullName_save(*fnamep, *p != NUL); 355 vim_free(*bufp); // free any allocated file name 356 *bufp = *fnamep; 357 if (*fnamep == NULL) 358 return -1; 359 } 360 361 #ifdef MSWIN 362 # if _WIN32_WINNT >= 0x0500 363 if (vim_strchr(*fnamep, '~') != NULL) 364 { 365 // Expand 8.3 filename to full path. Needed to make sure the same 366 // file does not have two different names. 367 // Note: problem does not occur if _WIN32_WINNT < 0x0500. 368 WCHAR *wfname = enc_to_utf16(*fnamep, NULL); 369 WCHAR buf[_MAX_PATH]; 370 371 if (wfname != NULL) 372 { 373 if (GetLongPathNameW(wfname, buf, _MAX_PATH)) 374 { 375 char_u *p = utf16_to_enc(buf, NULL); 376 377 if (p != NULL) 378 { 379 vim_free(*bufp); // free any allocated file name 380 *bufp = *fnamep = p; 381 } 382 } 383 vim_free(wfname); 384 } 385 } 386 # endif 387 #endif 388 // Append a path separator to a directory. 389 if (mch_isdir(*fnamep)) 390 { 391 // Make room for one or two extra characters. 392 *fnamep = vim_strnsave(*fnamep, STRLEN(*fnamep) + 2); 393 vim_free(*bufp); // free any allocated file name 394 *bufp = *fnamep; 395 if (*fnamep == NULL) 396 return -1; 397 add_pathsep(*fnamep); 398 } 399 } 400 401 // ":." - path relative to the current directory 402 // ":~" - path relative to the home directory 403 // ":8" - shortname path - postponed till after 404 while (src[*usedlen] == ':' 405 && ((c = src[*usedlen + 1]) == '.' || c == '~' || c == '8')) 406 { 407 *usedlen += 2; 408 if (c == '8') 409 { 410 #ifdef MSWIN 411 has_shortname = 1; // Postpone this. 412 #endif 413 continue; 414 } 415 pbuf = NULL; 416 // Need full path first (use expand_env() to remove a "~/") 417 if (!has_fullname && !has_homerelative) 418 { 419 if ((c == '.' || c == '~') && **fnamep == '~') 420 p = pbuf = expand_env_save(*fnamep); 421 else 422 p = pbuf = FullName_save(*fnamep, FALSE); 423 } 424 else 425 p = *fnamep; 426 427 has_fullname = 0; 428 429 if (p != NULL) 430 { 431 if (c == '.') 432 { 433 size_t namelen; 434 435 mch_dirname(dirname, MAXPATHL); 436 if (has_homerelative) 437 { 438 s = vim_strsave(dirname); 439 if (s != NULL) 440 { 441 home_replace(NULL, s, dirname, MAXPATHL, TRUE); 442 vim_free(s); 443 } 444 } 445 namelen = STRLEN(dirname); 446 447 // Do not call shorten_fname() here since it removes the prefix 448 // even though the path does not have a prefix. 449 if (fnamencmp(p, dirname, namelen) == 0) 450 { 451 p += namelen; 452 if (vim_ispathsep(*p)) 453 { 454 while (*p && vim_ispathsep(*p)) 455 ++p; 456 *fnamep = p; 457 if (pbuf != NULL) 458 { 459 // free any allocated file name 460 vim_free(*bufp); 461 *bufp = pbuf; 462 pbuf = NULL; 463 } 464 } 465 } 466 } 467 else 468 { 469 home_replace(NULL, p, dirname, MAXPATHL, TRUE); 470 // Only replace it when it starts with '~' 471 if (*dirname == '~') 472 { 473 s = vim_strsave(dirname); 474 if (s != NULL) 475 { 476 *fnamep = s; 477 vim_free(*bufp); 478 *bufp = s; 479 has_homerelative = TRUE; 480 } 481 } 482 } 483 vim_free(pbuf); 484 } 485 } 486 487 tail = gettail(*fnamep); 488 *fnamelen = (int)STRLEN(*fnamep); 489 490 // ":h" - head, remove "/file_name", can be repeated 491 // Don't remove the first "/" or "c:\" 492 while (src[*usedlen] == ':' && src[*usedlen + 1] == 'h') 493 { 494 valid |= VALID_HEAD; 495 *usedlen += 2; 496 s = get_past_head(*fnamep); 497 while (tail > s && after_pathsep(s, tail)) 498 MB_PTR_BACK(*fnamep, tail); 499 *fnamelen = (int)(tail - *fnamep); 500 #ifdef VMS 501 if (*fnamelen > 0) 502 *fnamelen += 1; // the path separator is part of the path 503 #endif 504 if (*fnamelen == 0) 505 { 506 // Result is empty. Turn it into "." to make ":cd %:h" work. 507 p = vim_strsave((char_u *)"."); 508 if (p == NULL) 509 return -1; 510 vim_free(*bufp); 511 *bufp = *fnamep = tail = p; 512 *fnamelen = 1; 513 } 514 else 515 { 516 while (tail > s && !after_pathsep(s, tail)) 517 MB_PTR_BACK(*fnamep, tail); 518 } 519 } 520 521 // ":8" - shortname 522 if (src[*usedlen] == ':' && src[*usedlen + 1] == '8') 523 { 524 *usedlen += 2; 525 #ifdef MSWIN 526 has_shortname = 1; 527 #endif 528 } 529 530 #ifdef MSWIN 531 /* 532 * Handle ":8" after we have done 'heads' and before we do 'tails'. 533 */ 534 if (has_shortname) 535 { 536 // Copy the string if it is shortened by :h and when it wasn't copied 537 // yet, because we are going to change it in place. Avoids changing 538 // the buffer name for "%:8". 539 if (*fnamelen < (int)STRLEN(*fnamep) || *fnamep == fname_start) 540 { 541 p = vim_strnsave(*fnamep, *fnamelen); 542 if (p == NULL) 543 return -1; 544 vim_free(*bufp); 545 *bufp = *fnamep = p; 546 } 547 548 // Split into two implementations - makes it easier. First is where 549 // there isn't a full name already, second is where there is. 550 if (!has_fullname && !vim_isAbsName(*fnamep)) 551 { 552 if (shortpath_for_partial(fnamep, bufp, fnamelen) == FAIL) 553 return -1; 554 } 555 else 556 { 557 int l = *fnamelen; 558 559 // Simple case, already have the full-name. 560 // Nearly always shorter, so try first time. 561 if (get_short_pathname(fnamep, bufp, &l) == FAIL) 562 return -1; 563 564 if (l == 0) 565 { 566 // Couldn't find the filename, search the paths. 567 l = *fnamelen; 568 if (shortpath_for_invalid_fname(fnamep, bufp, &l) == FAIL) 569 return -1; 570 } 571 *fnamelen = l; 572 } 573 } 574 #endif // MSWIN 575 576 // ":t" - tail, just the basename 577 if (src[*usedlen] == ':' && src[*usedlen + 1] == 't') 578 { 579 *usedlen += 2; 580 *fnamelen -= (int)(tail - *fnamep); 581 *fnamep = tail; 582 } 583 584 // ":e" - extension, can be repeated 585 // ":r" - root, without extension, can be repeated 586 while (src[*usedlen] == ':' 587 && (src[*usedlen + 1] == 'e' || src[*usedlen + 1] == 'r')) 588 { 589 // find a '.' in the tail: 590 // - for second :e: before the current fname 591 // - otherwise: The last '.' 592 if (src[*usedlen + 1] == 'e' && *fnamep > tail) 593 s = *fnamep - 2; 594 else 595 s = *fnamep + *fnamelen - 1; 596 for ( ; s > tail; --s) 597 if (s[0] == '.') 598 break; 599 if (src[*usedlen + 1] == 'e') // :e 600 { 601 if (s > tail) 602 { 603 *fnamelen += (int)(*fnamep - (s + 1)); 604 *fnamep = s + 1; 605 #ifdef VMS 606 // cut version from the extension 607 s = *fnamep + *fnamelen - 1; 608 for ( ; s > *fnamep; --s) 609 if (s[0] == ';') 610 break; 611 if (s > *fnamep) 612 *fnamelen = s - *fnamep; 613 #endif 614 } 615 else if (*fnamep <= tail) 616 *fnamelen = 0; 617 } 618 else // :r 619 { 620 char_u *limit = *fnamep; 621 622 if (limit < tail) 623 limit = tail; 624 if (s > limit) // remove one extension 625 *fnamelen = (int)(s - *fnamep); 626 } 627 *usedlen += 2; 628 } 629 630 // ":s?pat?foo?" - substitute 631 // ":gs?pat?foo?" - global substitute 632 if (src[*usedlen] == ':' 633 && (src[*usedlen + 1] == 's' 634 || (src[*usedlen + 1] == 'g' && src[*usedlen + 2] == 's'))) 635 { 636 char_u *str; 637 char_u *pat; 638 char_u *sub; 639 int sep; 640 char_u *flags; 641 int didit = FALSE; 642 643 flags = (char_u *)""; 644 s = src + *usedlen + 2; 645 if (src[*usedlen + 1] == 'g') 646 { 647 flags = (char_u *)"g"; 648 ++s; 649 } 650 651 sep = *s++; 652 if (sep) 653 { 654 // find end of pattern 655 p = vim_strchr(s, sep); 656 if (p != NULL) 657 { 658 pat = vim_strnsave(s, p - s); 659 if (pat != NULL) 660 { 661 s = p + 1; 662 // find end of substitution 663 p = vim_strchr(s, sep); 664 if (p != NULL) 665 { 666 sub = vim_strnsave(s, p - s); 667 str = vim_strnsave(*fnamep, *fnamelen); 668 if (sub != NULL && str != NULL) 669 { 670 *usedlen = (int)(p + 1 - src); 671 s = do_string_sub(str, pat, sub, NULL, flags); 672 if (s != NULL) 673 { 674 *fnamep = s; 675 *fnamelen = (int)STRLEN(s); 676 vim_free(*bufp); 677 *bufp = s; 678 didit = TRUE; 679 } 680 } 681 vim_free(sub); 682 vim_free(str); 683 } 684 vim_free(pat); 685 } 686 } 687 // after using ":s", repeat all the modifiers 688 if (didit) 689 goto repeat; 690 } 691 } 692 693 if (src[*usedlen] == ':' && src[*usedlen + 1] == 'S') 694 { 695 // vim_strsave_shellescape() needs a NUL terminated string. 696 c = (*fnamep)[*fnamelen]; 697 if (c != NUL) 698 (*fnamep)[*fnamelen] = NUL; 699 p = vim_strsave_shellescape(*fnamep, FALSE, FALSE); 700 if (c != NUL) 701 (*fnamep)[*fnamelen] = c; 702 if (p == NULL) 703 return -1; 704 vim_free(*bufp); 705 *bufp = *fnamep = p; 706 *fnamelen = (int)STRLEN(p); 707 *usedlen += 2; 708 } 709 710 return valid; 711 } 712 713 /* 714 * Shorten the path of a file from "~/foo/../.bar/fname" to "~/f/../.b/fname" 715 * "trim_len" specifies how many characters to keep for each directory. 716 * Must be 1 or more. 717 * It's done in-place. 718 */ 719 static void 720 shorten_dir_len(char_u *str, int trim_len) 721 { 722 char_u *tail, *s, *d; 723 int skip = FALSE; 724 int dirchunk_len = 0; 725 726 tail = gettail(str); 727 d = str; 728 for (s = str; ; ++s) 729 { 730 if (s >= tail) // copy the whole tail 731 { 732 *d++ = *s; 733 if (*s == NUL) 734 break; 735 } 736 else if (vim_ispathsep(*s)) // copy '/' and next char 737 { 738 *d++ = *s; 739 skip = FALSE; 740 dirchunk_len = 0; 741 } 742 else if (!skip) 743 { 744 *d++ = *s; // copy next char 745 if (*s != '~' && *s != '.') // and leading "~" and "." 746 { 747 ++dirchunk_len; // only count word chars for the size 748 749 // keep copying chars until we have our preferred length (or 750 // until the above if/else branches move us along) 751 if (dirchunk_len >= trim_len) 752 skip = TRUE; 753 } 754 755 if (has_mbyte) 756 { 757 int l = mb_ptr2len(s); 758 759 while (--l > 0) 760 *d++ = *++s; 761 } 762 } 763 } 764 } 765 766 /* 767 * Shorten the path of a file from "~/foo/../.bar/fname" to "~/f/../.b/fname" 768 * It's done in-place. 769 */ 770 void 771 shorten_dir(char_u *str) 772 { 773 shorten_dir_len(str, 1); 774 } 775 776 #if defined(FEAT_EVAL) || defined(PROTO) 777 778 /* 779 * "chdir(dir)" function 780 */ 781 void 782 f_chdir(typval_T *argvars, typval_T *rettv) 783 { 784 char_u *cwd; 785 cdscope_T scope = CDSCOPE_GLOBAL; 786 787 rettv->v_type = VAR_STRING; 788 rettv->vval.v_string = NULL; 789 790 if (argvars[0].v_type != VAR_STRING) 791 { 792 // Returning an empty string means it failed. 793 // No error message, for historic reasons. 794 if (in_vim9script()) 795 (void) check_for_string_arg(argvars, 0); 796 return; 797 } 798 799 // Return the current directory 800 cwd = alloc(MAXPATHL); 801 if (cwd != NULL) 802 { 803 if (mch_dirname(cwd, MAXPATHL) != FAIL) 804 { 805 #ifdef BACKSLASH_IN_FILENAME 806 slash_adjust(cwd); 807 #endif 808 rettv->vval.v_string = vim_strsave(cwd); 809 } 810 vim_free(cwd); 811 } 812 813 if (curwin->w_localdir != NULL) 814 scope = CDSCOPE_WINDOW; 815 else if (curtab->tp_localdir != NULL) 816 scope = CDSCOPE_TABPAGE; 817 818 if (!changedir_func(argvars[0].vval.v_string, TRUE, scope)) 819 // Directory change failed 820 VIM_CLEAR(rettv->vval.v_string); 821 } 822 823 /* 824 * "delete()" function 825 */ 826 void 827 f_delete(typval_T *argvars, typval_T *rettv) 828 { 829 char_u nbuf[NUMBUFLEN]; 830 char_u *name; 831 char_u *flags; 832 833 rettv->vval.v_number = -1; 834 if (check_restricted() || check_secure()) 835 return; 836 837 name = tv_get_string(&argvars[0]); 838 if (name == NULL || *name == NUL) 839 { 840 emsg(_(e_invarg)); 841 return; 842 } 843 844 if (argvars[1].v_type != VAR_UNKNOWN) 845 flags = tv_get_string_buf(&argvars[1], nbuf); 846 else 847 flags = (char_u *)""; 848 849 if (*flags == NUL) 850 // delete a file 851 rettv->vval.v_number = mch_remove(name) == 0 ? 0 : -1; 852 else if (STRCMP(flags, "d") == 0) 853 // delete an empty directory 854 rettv->vval.v_number = mch_rmdir(name) == 0 ? 0 : -1; 855 else if (STRCMP(flags, "rf") == 0) 856 // delete a directory recursively 857 rettv->vval.v_number = delete_recursive(name); 858 else 859 semsg(_(e_invalid_expression_str), flags); 860 } 861 862 /* 863 * "executable()" function 864 */ 865 void 866 f_executable(typval_T *argvars, typval_T *rettv) 867 { 868 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) 869 return; 870 871 // Check in $PATH and also check directly if there is a directory name. 872 rettv->vval.v_number = mch_can_exe(tv_get_string(&argvars[0]), NULL, TRUE); 873 } 874 875 /* 876 * "exepath()" function 877 */ 878 void 879 f_exepath(typval_T *argvars, typval_T *rettv) 880 { 881 char_u *p = NULL; 882 883 if (in_vim9script() && check_for_nonempty_string_arg(argvars, 0) == FAIL) 884 return; 885 (void)mch_can_exe(tv_get_string(&argvars[0]), &p, TRUE); 886 rettv->v_type = VAR_STRING; 887 rettv->vval.v_string = p; 888 } 889 890 /* 891 * "filereadable()" function 892 */ 893 void 894 f_filereadable(typval_T *argvars, typval_T *rettv) 895 { 896 int fd; 897 char_u *p; 898 int n; 899 900 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) 901 return; 902 #ifndef O_NONBLOCK 903 # define O_NONBLOCK 0 904 #endif 905 p = tv_get_string(&argvars[0]); 906 if (*p && !mch_isdir(p) && (fd = mch_open((char *)p, 907 O_RDONLY | O_NONBLOCK, 0)) >= 0) 908 { 909 n = TRUE; 910 close(fd); 911 } 912 else 913 n = FALSE; 914 915 rettv->vval.v_number = n; 916 } 917 918 /* 919 * Return 0 for not writable, 1 for writable file, 2 for a dir which we have 920 * rights to write into. 921 */ 922 void 923 f_filewritable(typval_T *argvars, typval_T *rettv) 924 { 925 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) 926 return; 927 rettv->vval.v_number = filewritable(tv_get_string(&argvars[0])); 928 } 929 930 static void 931 findfilendir( 932 typval_T *argvars UNUSED, 933 typval_T *rettv, 934 int find_what UNUSED) 935 { 936 #ifdef FEAT_SEARCHPATH 937 char_u *fname; 938 char_u *fresult = NULL; 939 char_u *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; 940 char_u *p; 941 char_u pathbuf[NUMBUFLEN]; 942 int count = 1; 943 int first = TRUE; 944 int error = FALSE; 945 #endif 946 947 rettv->vval.v_string = NULL; 948 rettv->v_type = VAR_STRING; 949 if (in_vim9script() && check_for_nonempty_string_arg(argvars, 0) == FAIL) 950 return; 951 952 #ifdef FEAT_SEARCHPATH 953 fname = tv_get_string(&argvars[0]); 954 955 if (argvars[1].v_type != VAR_UNKNOWN) 956 { 957 p = tv_get_string_buf_chk(&argvars[1], pathbuf); 958 if (p == NULL) 959 error = TRUE; 960 else 961 { 962 if (*p != NUL) 963 path = p; 964 965 if (argvars[2].v_type != VAR_UNKNOWN) 966 count = (int)tv_get_number_chk(&argvars[2], &error); 967 } 968 } 969 970 if (count < 0 && rettv_list_alloc(rettv) == FAIL) 971 error = TRUE; 972 973 if (*fname != NUL && !error) 974 { 975 do 976 { 977 if (rettv->v_type == VAR_STRING || rettv->v_type == VAR_LIST) 978 vim_free(fresult); 979 fresult = find_file_in_path_option(first ? fname : NULL, 980 first ? (int)STRLEN(fname) : 0, 981 0, first, path, 982 find_what, 983 curbuf->b_ffname, 984 find_what == FINDFILE_DIR 985 ? (char_u *)"" : curbuf->b_p_sua); 986 first = FALSE; 987 988 if (fresult != NULL && rettv->v_type == VAR_LIST) 989 list_append_string(rettv->vval.v_list, fresult, -1); 990 991 } while ((rettv->v_type == VAR_LIST || --count > 0) && fresult != NULL); 992 } 993 994 if (rettv->v_type == VAR_STRING) 995 rettv->vval.v_string = fresult; 996 #endif 997 } 998 999 /* 1000 * "finddir({fname}[, {path}[, {count}]])" function 1001 */ 1002 void 1003 f_finddir(typval_T *argvars, typval_T *rettv) 1004 { 1005 findfilendir(argvars, rettv, FINDFILE_DIR); 1006 } 1007 1008 /* 1009 * "findfile({fname}[, {path}[, {count}]])" function 1010 */ 1011 void 1012 f_findfile(typval_T *argvars, typval_T *rettv) 1013 { 1014 findfilendir(argvars, rettv, FINDFILE_FILE); 1015 } 1016 1017 /* 1018 * "fnamemodify({fname}, {mods})" function 1019 */ 1020 void 1021 f_fnamemodify(typval_T *argvars, typval_T *rettv) 1022 { 1023 char_u *fname; 1024 char_u *mods; 1025 int usedlen = 0; 1026 int len = 0; 1027 char_u *fbuf = NULL; 1028 char_u buf[NUMBUFLEN]; 1029 1030 if (in_vim9script() && (check_for_string_arg(argvars, 0) == FAIL 1031 || check_for_string_arg(argvars, 1) == FAIL)) 1032 return; 1033 fname = tv_get_string_chk(&argvars[0]); 1034 mods = tv_get_string_buf_chk(&argvars[1], buf); 1035 if (mods == NULL || fname == NULL) 1036 fname = NULL; 1037 else 1038 { 1039 len = (int)STRLEN(fname); 1040 if (mods != NULL && *mods != NUL) 1041 (void)modify_fname(mods, FALSE, &usedlen, &fname, &fbuf, &len); 1042 } 1043 1044 rettv->v_type = VAR_STRING; 1045 if (fname == NULL) 1046 rettv->vval.v_string = NULL; 1047 else 1048 rettv->vval.v_string = vim_strnsave(fname, len); 1049 vim_free(fbuf); 1050 } 1051 1052 /* 1053 * "getcwd()" function 1054 * 1055 * Return the current working directory of a window in a tab page. 1056 * First optional argument 'winnr' is the window number or -1 and the second 1057 * optional argument 'tabnr' is the tab page number. 1058 * 1059 * If no arguments are supplied, then return the directory of the current 1060 * window. 1061 * If only 'winnr' is specified and is not -1 or 0 then return the directory of 1062 * the specified window. 1063 * If 'winnr' is 0 then return the directory of the current window. 1064 * If both 'winnr and 'tabnr' are specified and 'winnr' is -1 then return the 1065 * directory of the specified tab page. Otherwise return the directory of the 1066 * specified window in the specified tab page. 1067 * If the window or the tab page doesn't exist then return NULL. 1068 */ 1069 void 1070 f_getcwd(typval_T *argvars, typval_T *rettv) 1071 { 1072 win_T *wp = NULL; 1073 tabpage_T *tp = NULL; 1074 char_u *cwd; 1075 int global = FALSE; 1076 1077 rettv->v_type = VAR_STRING; 1078 rettv->vval.v_string = NULL; 1079 1080 if (argvars[0].v_type == VAR_NUMBER 1081 && argvars[0].vval.v_number == -1 1082 && argvars[1].v_type == VAR_UNKNOWN) 1083 global = TRUE; 1084 else 1085 wp = find_tabwin(&argvars[0], &argvars[1], &tp); 1086 1087 if (wp != NULL && wp->w_localdir != NULL) 1088 rettv->vval.v_string = vim_strsave(wp->w_localdir); 1089 else if (tp != NULL && tp->tp_localdir != NULL) 1090 rettv->vval.v_string = vim_strsave(tp->tp_localdir); 1091 else if (wp != NULL || tp != NULL || global) 1092 { 1093 if (globaldir != NULL) 1094 rettv->vval.v_string = vim_strsave(globaldir); 1095 else 1096 { 1097 cwd = alloc(MAXPATHL); 1098 if (cwd != NULL) 1099 { 1100 if (mch_dirname(cwd, MAXPATHL) != FAIL) 1101 rettv->vval.v_string = vim_strsave(cwd); 1102 vim_free(cwd); 1103 } 1104 } 1105 } 1106 #ifdef BACKSLASH_IN_FILENAME 1107 if (rettv->vval.v_string != NULL) 1108 slash_adjust(rettv->vval.v_string); 1109 #endif 1110 } 1111 1112 /* 1113 * Convert "st" to file permission string. 1114 */ 1115 char_u * 1116 getfpermst(stat_T *st, char_u *perm) 1117 { 1118 char_u flags[] = "rwx"; 1119 int i; 1120 1121 for (i = 0; i < 9; i++) 1122 { 1123 if (st->st_mode & (1 << (8 - i))) 1124 perm[i] = flags[i % 3]; 1125 else 1126 perm[i] = '-'; 1127 } 1128 return perm; 1129 } 1130 1131 /* 1132 * "getfperm({fname})" function 1133 */ 1134 void 1135 f_getfperm(typval_T *argvars, typval_T *rettv) 1136 { 1137 char_u *fname; 1138 stat_T st; 1139 char_u *perm = NULL; 1140 char_u permbuf[] = "---------"; 1141 1142 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) 1143 return; 1144 fname = tv_get_string(&argvars[0]); 1145 1146 rettv->v_type = VAR_STRING; 1147 if (mch_stat((char *)fname, &st) >= 0) 1148 perm = vim_strsave(getfpermst(&st, permbuf)); 1149 rettv->vval.v_string = perm; 1150 } 1151 1152 /* 1153 * "getfsize({fname})" function 1154 */ 1155 void 1156 f_getfsize(typval_T *argvars, typval_T *rettv) 1157 { 1158 char_u *fname; 1159 stat_T st; 1160 1161 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) 1162 return; 1163 1164 fname = tv_get_string(&argvars[0]); 1165 if (mch_stat((char *)fname, &st) >= 0) 1166 { 1167 if (mch_isdir(fname)) 1168 rettv->vval.v_number = 0; 1169 else 1170 { 1171 rettv->vval.v_number = (varnumber_T)st.st_size; 1172 1173 // non-perfect check for overflow 1174 if ((off_T)rettv->vval.v_number != (off_T)st.st_size) 1175 rettv->vval.v_number = -2; 1176 } 1177 } 1178 else 1179 rettv->vval.v_number = -1; 1180 } 1181 1182 /* 1183 * "getftime({fname})" function 1184 */ 1185 void 1186 f_getftime(typval_T *argvars, typval_T *rettv) 1187 { 1188 char_u *fname; 1189 stat_T st; 1190 1191 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) 1192 return; 1193 fname = tv_get_string(&argvars[0]); 1194 if (mch_stat((char *)fname, &st) >= 0) 1195 rettv->vval.v_number = (varnumber_T)st.st_mtime; 1196 else 1197 rettv->vval.v_number = -1; 1198 } 1199 1200 /* 1201 * Convert "st" to file type string. 1202 */ 1203 char_u * 1204 getftypest(stat_T *st) 1205 { 1206 char *t; 1207 1208 if (S_ISREG(st->st_mode)) 1209 t = "file"; 1210 else if (S_ISDIR(st->st_mode)) 1211 t = "dir"; 1212 else if (S_ISLNK(st->st_mode)) 1213 t = "link"; 1214 else if (S_ISBLK(st->st_mode)) 1215 t = "bdev"; 1216 else if (S_ISCHR(st->st_mode)) 1217 t = "cdev"; 1218 else if (S_ISFIFO(st->st_mode)) 1219 t = "fifo"; 1220 else if (S_ISSOCK(st->st_mode)) 1221 t = "socket"; 1222 else 1223 t = "other"; 1224 return (char_u*)t; 1225 } 1226 1227 /* 1228 * "getftype({fname})" function 1229 */ 1230 void 1231 f_getftype(typval_T *argvars, typval_T *rettv) 1232 { 1233 char_u *fname; 1234 stat_T st; 1235 char_u *type = NULL; 1236 1237 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) 1238 return; 1239 fname = tv_get_string(&argvars[0]); 1240 1241 rettv->v_type = VAR_STRING; 1242 if (mch_lstat((char *)fname, &st) >= 0) 1243 type = vim_strsave(getftypest(&st)); 1244 rettv->vval.v_string = type; 1245 } 1246 1247 /* 1248 * "glob()" function 1249 */ 1250 void 1251 f_glob(typval_T *argvars, typval_T *rettv) 1252 { 1253 int options = WILD_SILENT|WILD_USE_NL; 1254 expand_T xpc; 1255 int error = FALSE; 1256 1257 if (in_vim9script() 1258 && (check_for_string_arg(argvars, 0) == FAIL 1259 || check_for_opt_bool_arg(argvars, 1) == FAIL 1260 || (argvars[1].v_type != VAR_UNKNOWN 1261 && (check_for_opt_bool_arg(argvars, 2) == FAIL 1262 || (argvars[2].v_type != VAR_UNKNOWN 1263 && check_for_opt_bool_arg(argvars, 3) == FAIL))))) 1264 return; 1265 1266 // When the optional second argument is non-zero, don't remove matches 1267 // for 'wildignore' and don't put matches for 'suffixes' at the end. 1268 rettv->v_type = VAR_STRING; 1269 if (argvars[1].v_type != VAR_UNKNOWN) 1270 { 1271 if (tv_get_bool_chk(&argvars[1], &error)) 1272 options |= WILD_KEEP_ALL; 1273 if (argvars[2].v_type != VAR_UNKNOWN) 1274 { 1275 if (tv_get_bool_chk(&argvars[2], &error)) 1276 rettv_list_set(rettv, NULL); 1277 if (argvars[3].v_type != VAR_UNKNOWN 1278 && tv_get_bool_chk(&argvars[3], &error)) 1279 options |= WILD_ALLLINKS; 1280 } 1281 } 1282 if (!error) 1283 { 1284 ExpandInit(&xpc); 1285 xpc.xp_context = EXPAND_FILES; 1286 if (p_wic) 1287 options += WILD_ICASE; 1288 if (rettv->v_type == VAR_STRING) 1289 rettv->vval.v_string = ExpandOne(&xpc, tv_get_string(&argvars[0]), 1290 NULL, options, WILD_ALL); 1291 else if (rettv_list_alloc(rettv) != FAIL) 1292 { 1293 int i; 1294 1295 ExpandOne(&xpc, tv_get_string(&argvars[0]), 1296 NULL, options, WILD_ALL_KEEP); 1297 for (i = 0; i < xpc.xp_numfiles; i++) 1298 list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1); 1299 1300 ExpandCleanup(&xpc); 1301 } 1302 } 1303 else 1304 rettv->vval.v_string = NULL; 1305 } 1306 1307 /* 1308 * "glob2regpat()" function 1309 */ 1310 void 1311 f_glob2regpat(typval_T *argvars, typval_T *rettv) 1312 { 1313 char_u buf[NUMBUFLEN]; 1314 char_u *pat = tv_get_string_buf_chk_strict(&argvars[0], buf, 1315 in_vim9script()); 1316 1317 rettv->v_type = VAR_STRING; 1318 rettv->vval.v_string = (pat == NULL) 1319 ? NULL : file_pat_to_reg_pat(pat, NULL, NULL, FALSE); 1320 } 1321 1322 /* 1323 * "globpath()" function 1324 */ 1325 void 1326 f_globpath(typval_T *argvars, typval_T *rettv) 1327 { 1328 int flags = WILD_IGNORE_COMPLETESLASH; 1329 char_u buf1[NUMBUFLEN]; 1330 char_u *file; 1331 int error = FALSE; 1332 garray_T ga; 1333 int i; 1334 1335 if (in_vim9script() 1336 && (check_for_string_arg(argvars, 0) == FAIL 1337 || check_for_string_arg(argvars, 1) == FAIL 1338 || check_for_opt_bool_arg(argvars, 2) == FAIL 1339 || (argvars[2].v_type != VAR_UNKNOWN 1340 && (check_for_opt_bool_arg(argvars, 3) == FAIL 1341 || (argvars[3].v_type != VAR_UNKNOWN 1342 && check_for_opt_bool_arg(argvars, 4) == FAIL))))) 1343 return; 1344 1345 file = tv_get_string_buf_chk(&argvars[1], buf1); 1346 1347 // When the optional second argument is non-zero, don't remove matches 1348 // for 'wildignore' and don't put matches for 'suffixes' at the end. 1349 rettv->v_type = VAR_STRING; 1350 if (argvars[2].v_type != VAR_UNKNOWN) 1351 { 1352 if (tv_get_bool_chk(&argvars[2], &error)) 1353 flags |= WILD_KEEP_ALL; 1354 if (argvars[3].v_type != VAR_UNKNOWN) 1355 { 1356 if (tv_get_bool_chk(&argvars[3], &error)) 1357 rettv_list_set(rettv, NULL); 1358 if (argvars[4].v_type != VAR_UNKNOWN 1359 && tv_get_bool_chk(&argvars[4], &error)) 1360 flags |= WILD_ALLLINKS; 1361 } 1362 } 1363 if (file != NULL && !error) 1364 { 1365 ga_init2(&ga, (int)sizeof(char_u *), 10); 1366 globpath(tv_get_string(&argvars[0]), file, &ga, flags); 1367 if (rettv->v_type == VAR_STRING) 1368 rettv->vval.v_string = ga_concat_strings(&ga, "\n"); 1369 else if (rettv_list_alloc(rettv) != FAIL) 1370 for (i = 0; i < ga.ga_len; ++i) 1371 list_append_string(rettv->vval.v_list, 1372 ((char_u **)(ga.ga_data))[i], -1); 1373 ga_clear_strings(&ga); 1374 } 1375 else 1376 rettv->vval.v_string = NULL; 1377 } 1378 1379 /* 1380 * "isdirectory()" function 1381 */ 1382 void 1383 f_isdirectory(typval_T *argvars, typval_T *rettv) 1384 { 1385 rettv->vval.v_number = mch_isdir(tv_get_string(&argvars[0])); 1386 } 1387 1388 /* 1389 * Create the directory in which "dir" is located, and higher levels when 1390 * needed. 1391 * Return OK or FAIL. 1392 */ 1393 static int 1394 mkdir_recurse(char_u *dir, int prot) 1395 { 1396 char_u *p; 1397 char_u *updir; 1398 int r = FAIL; 1399 1400 // Get end of directory name in "dir". 1401 // We're done when it's "/" or "c:/". 1402 p = gettail_sep(dir); 1403 if (p <= get_past_head(dir)) 1404 return OK; 1405 1406 // If the directory exists we're done. Otherwise: create it. 1407 updir = vim_strnsave(dir, p - dir); 1408 if (updir == NULL) 1409 return FAIL; 1410 if (mch_isdir(updir)) 1411 r = OK; 1412 else if (mkdir_recurse(updir, prot) == OK) 1413 r = vim_mkdir_emsg(updir, prot); 1414 vim_free(updir); 1415 return r; 1416 } 1417 1418 /* 1419 * "mkdir()" function 1420 */ 1421 void 1422 f_mkdir(typval_T *argvars, typval_T *rettv) 1423 { 1424 char_u *dir; 1425 char_u buf[NUMBUFLEN]; 1426 int prot = 0755; 1427 1428 rettv->vval.v_number = FAIL; 1429 if (check_restricted() || check_secure()) 1430 return; 1431 1432 dir = tv_get_string_buf(&argvars[0], buf); 1433 if (*dir == NUL) 1434 return; 1435 1436 if (*gettail(dir) == NUL) 1437 // remove trailing slashes 1438 *gettail_sep(dir) = NUL; 1439 1440 if (argvars[1].v_type != VAR_UNKNOWN) 1441 { 1442 if (argvars[2].v_type != VAR_UNKNOWN) 1443 { 1444 prot = (int)tv_get_number_chk(&argvars[2], NULL); 1445 if (prot == -1) 1446 return; 1447 } 1448 if (STRCMP(tv_get_string(&argvars[1]), "p") == 0) 1449 { 1450 if (mch_isdir(dir)) 1451 { 1452 // With the "p" flag it's OK if the dir already exists. 1453 rettv->vval.v_number = OK; 1454 return; 1455 } 1456 mkdir_recurse(dir, prot); 1457 } 1458 } 1459 rettv->vval.v_number = vim_mkdir_emsg(dir, prot); 1460 } 1461 1462 /* 1463 * "pathshorten()" function 1464 */ 1465 void 1466 f_pathshorten(typval_T *argvars, typval_T *rettv) 1467 { 1468 char_u *p; 1469 int trim_len = 1; 1470 1471 if (in_vim9script() 1472 && (check_for_string_arg(argvars, 0) == FAIL 1473 || check_for_opt_number_arg(argvars, 1) == FAIL)) 1474 return; 1475 1476 if (argvars[1].v_type != VAR_UNKNOWN) 1477 { 1478 trim_len = (int)tv_get_number(&argvars[1]); 1479 if (trim_len < 1) 1480 trim_len = 1; 1481 } 1482 1483 rettv->v_type = VAR_STRING; 1484 p = tv_get_string_chk(&argvars[0]); 1485 1486 if (p == NULL) 1487 rettv->vval.v_string = NULL; 1488 else 1489 { 1490 p = vim_strsave(p); 1491 rettv->vval.v_string = p; 1492 if (p != NULL) 1493 shorten_dir_len(p, trim_len); 1494 } 1495 } 1496 1497 /* 1498 * Common code for readdir_checkitem() and readdirex_checkitem(). 1499 * Either "name" or "dict" is NULL. 1500 */ 1501 static int 1502 checkitem_common(void *context, char_u *name, dict_T *dict) 1503 { 1504 typval_T *expr = (typval_T *)context; 1505 typval_T save_val; 1506 typval_T rettv; 1507 typval_T argv[2]; 1508 int retval = 0; 1509 int error = FALSE; 1510 1511 prepare_vimvar(VV_VAL, &save_val); 1512 if (name != NULL) 1513 { 1514 set_vim_var_string(VV_VAL, name, -1); 1515 argv[0].v_type = VAR_STRING; 1516 argv[0].vval.v_string = name; 1517 } 1518 else 1519 { 1520 set_vim_var_dict(VV_VAL, dict); 1521 argv[0].v_type = VAR_DICT; 1522 argv[0].vval.v_dict = dict; 1523 } 1524 1525 if (eval_expr_typval(expr, argv, 1, &rettv) == FAIL) 1526 goto theend; 1527 1528 // We want to use -1, but also true/false should be allowed. 1529 if (rettv.v_type == VAR_SPECIAL || rettv.v_type == VAR_BOOL) 1530 { 1531 rettv.v_type = VAR_NUMBER; 1532 rettv.vval.v_number = rettv.vval.v_number == VVAL_TRUE; 1533 } 1534 retval = tv_get_number_chk(&rettv, &error); 1535 if (error) 1536 retval = -1; 1537 clear_tv(&rettv); 1538 1539 theend: 1540 if (name != NULL) 1541 set_vim_var_string(VV_VAL, NULL, 0); 1542 else 1543 set_vim_var_dict(VV_VAL, NULL); 1544 restore_vimvar(VV_VAL, &save_val); 1545 return retval; 1546 } 1547 1548 /* 1549 * Evaluate "expr" (= "context") for readdir(). 1550 */ 1551 static int 1552 readdir_checkitem(void *context, void *item) 1553 { 1554 char_u *name = (char_u *)item; 1555 1556 return checkitem_common(context, name, NULL); 1557 } 1558 1559 static int 1560 readdirex_dict_arg(typval_T *tv, int *cmp) 1561 { 1562 char_u *compare; 1563 1564 if (tv->v_type != VAR_DICT) 1565 { 1566 emsg(_(e_dictreq)); 1567 return FAIL; 1568 } 1569 1570 if (dict_find(tv->vval.v_dict, (char_u *)"sort", -1) != NULL) 1571 compare = dict_get_string(tv->vval.v_dict, (char_u *)"sort", FALSE); 1572 else 1573 { 1574 semsg(_(e_no_dict_key), "sort"); 1575 return FAIL; 1576 } 1577 1578 if (STRCMP(compare, (char_u *) "none") == 0) 1579 *cmp = READDIR_SORT_NONE; 1580 else if (STRCMP(compare, (char_u *) "case") == 0) 1581 *cmp = READDIR_SORT_BYTE; 1582 else if (STRCMP(compare, (char_u *) "icase") == 0) 1583 *cmp = READDIR_SORT_IC; 1584 else if (STRCMP(compare, (char_u *) "collate") == 0) 1585 *cmp = READDIR_SORT_COLLATE; 1586 return OK; 1587 } 1588 1589 /* 1590 * "readdir()" function 1591 */ 1592 void 1593 f_readdir(typval_T *argvars, typval_T *rettv) 1594 { 1595 typval_T *expr; 1596 int ret; 1597 char_u *path; 1598 char_u *p; 1599 garray_T ga; 1600 int i; 1601 int sort = READDIR_SORT_BYTE; 1602 1603 if (rettv_list_alloc(rettv) == FAIL) 1604 return; 1605 1606 if (in_vim9script() 1607 && (check_for_string_arg(argvars, 0) == FAIL 1608 || (argvars[1].v_type != VAR_UNKNOWN 1609 && check_for_opt_dict_arg(argvars, 2) == FAIL))) 1610 return; 1611 1612 path = tv_get_string(&argvars[0]); 1613 expr = &argvars[1]; 1614 1615 if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN && 1616 readdirex_dict_arg(&argvars[2], &sort) == FAIL) 1617 return; 1618 1619 ret = readdir_core(&ga, path, FALSE, (void *)expr, 1620 (expr->v_type == VAR_UNKNOWN) ? NULL : readdir_checkitem, sort); 1621 if (ret == OK) 1622 { 1623 for (i = 0; i < ga.ga_len; i++) 1624 { 1625 p = ((char_u **)ga.ga_data)[i]; 1626 list_append_string(rettv->vval.v_list, p, -1); 1627 } 1628 } 1629 ga_clear_strings(&ga); 1630 } 1631 1632 /* 1633 * Evaluate "expr" (= "context") for readdirex(). 1634 */ 1635 static int 1636 readdirex_checkitem(void *context, void *item) 1637 { 1638 dict_T *dict = (dict_T*)item; 1639 1640 return checkitem_common(context, NULL, dict); 1641 } 1642 1643 /* 1644 * "readdirex()" function 1645 */ 1646 void 1647 f_readdirex(typval_T *argvars, typval_T *rettv) 1648 { 1649 typval_T *expr; 1650 int ret; 1651 char_u *path; 1652 garray_T ga; 1653 int i; 1654 int sort = READDIR_SORT_BYTE; 1655 1656 if (rettv_list_alloc(rettv) == FAIL) 1657 return; 1658 1659 if (in_vim9script() 1660 && (check_for_string_arg(argvars, 0) == FAIL 1661 || (argvars[1].v_type != VAR_UNKNOWN 1662 && check_for_opt_dict_arg(argvars, 2) == FAIL))) 1663 return; 1664 1665 path = tv_get_string(&argvars[0]); 1666 expr = &argvars[1]; 1667 1668 if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN && 1669 readdirex_dict_arg(&argvars[2], &sort) == FAIL) 1670 return; 1671 1672 ret = readdir_core(&ga, path, TRUE, (void *)expr, 1673 (expr->v_type == VAR_UNKNOWN) ? NULL : readdirex_checkitem, sort); 1674 if (ret == OK) 1675 { 1676 for (i = 0; i < ga.ga_len; i++) 1677 { 1678 dict_T *dict = ((dict_T**)ga.ga_data)[i]; 1679 list_append_dict(rettv->vval.v_list, dict); 1680 dict_unref(dict); 1681 } 1682 } 1683 ga_clear(&ga); 1684 } 1685 1686 /* 1687 * "readfile()" function 1688 */ 1689 static void 1690 read_file_or_blob(typval_T *argvars, typval_T *rettv, int always_blob) 1691 { 1692 int binary = FALSE; 1693 int blob = always_blob; 1694 int failed = FALSE; 1695 char_u *fname; 1696 FILE *fd; 1697 char_u buf[(IOSIZE/256)*256]; // rounded to avoid odd + 1 1698 int io_size = sizeof(buf); 1699 int readlen; // size of last fread() 1700 char_u *prev = NULL; // previously read bytes, if any 1701 long prevlen = 0; // length of data in prev 1702 long prevsize = 0; // size of prev buffer 1703 long maxline = MAXLNUM; 1704 long cnt = 0; 1705 char_u *p; // position in buf 1706 char_u *start; // start of current line 1707 1708 if (argvars[1].v_type != VAR_UNKNOWN) 1709 { 1710 if (STRCMP(tv_get_string(&argvars[1]), "b") == 0) 1711 binary = TRUE; 1712 if (STRCMP(tv_get_string(&argvars[1]), "B") == 0) 1713 blob = TRUE; 1714 1715 if (argvars[2].v_type != VAR_UNKNOWN) 1716 maxline = (long)tv_get_number(&argvars[2]); 1717 } 1718 1719 if ((blob ? rettv_blob_alloc(rettv) : rettv_list_alloc(rettv)) == FAIL) 1720 return; 1721 1722 // Always open the file in binary mode, library functions have a mind of 1723 // their own about CR-LF conversion. 1724 fname = tv_get_string(&argvars[0]); 1725 1726 if (mch_isdir(fname)) 1727 { 1728 semsg(_(e_src_is_directory), fname); 1729 return; 1730 } 1731 if (*fname == NUL || (fd = mch_fopen((char *)fname, READBIN)) == NULL) 1732 { 1733 semsg(_(e_notopen), *fname == NUL ? (char_u *)_("<empty>") : fname); 1734 return; 1735 } 1736 1737 if (blob) 1738 { 1739 if (read_blob(fd, rettv->vval.v_blob) == FAIL) 1740 { 1741 semsg(_(e_notread), fname); 1742 // An empty blob is returned on error. 1743 blob_free(rettv->vval.v_blob); 1744 rettv->vval.v_blob = NULL; 1745 } 1746 fclose(fd); 1747 return; 1748 } 1749 1750 while (cnt < maxline || maxline < 0) 1751 { 1752 readlen = (int)fread(buf, 1, io_size, fd); 1753 1754 // This for loop processes what was read, but is also entered at end 1755 // of file so that either: 1756 // - an incomplete line gets written 1757 // - a "binary" file gets an empty line at the end if it ends in a 1758 // newline. 1759 for (p = buf, start = buf; 1760 p < buf + readlen || (readlen <= 0 && (prevlen > 0 || binary)); 1761 ++p) 1762 { 1763 if (*p == '\n' || readlen <= 0) 1764 { 1765 listitem_T *li; 1766 char_u *s = NULL; 1767 long_u len = p - start; 1768 1769 // Finished a line. Remove CRs before NL. 1770 if (readlen > 0 && !binary) 1771 { 1772 while (len > 0 && start[len - 1] == '\r') 1773 --len; 1774 // removal may cross back to the "prev" string 1775 if (len == 0) 1776 while (prevlen > 0 && prev[prevlen - 1] == '\r') 1777 --prevlen; 1778 } 1779 if (prevlen == 0) 1780 s = vim_strnsave(start, len); 1781 else 1782 { 1783 // Change "prev" buffer to be the right size. This way 1784 // the bytes are only copied once, and very long lines are 1785 // allocated only once. 1786 if ((s = vim_realloc(prev, prevlen + len + 1)) != NULL) 1787 { 1788 mch_memmove(s + prevlen, start, len); 1789 s[prevlen + len] = NUL; 1790 prev = NULL; // the list will own the string 1791 prevlen = prevsize = 0; 1792 } 1793 } 1794 if (s == NULL) 1795 { 1796 do_outofmem_msg((long_u) prevlen + len + 1); 1797 failed = TRUE; 1798 break; 1799 } 1800 1801 if ((li = listitem_alloc()) == NULL) 1802 { 1803 vim_free(s); 1804 failed = TRUE; 1805 break; 1806 } 1807 li->li_tv.v_type = VAR_STRING; 1808 li->li_tv.v_lock = 0; 1809 li->li_tv.vval.v_string = s; 1810 list_append(rettv->vval.v_list, li); 1811 1812 start = p + 1; // step over newline 1813 if ((++cnt >= maxline && maxline >= 0) || readlen <= 0) 1814 break; 1815 } 1816 else if (*p == NUL) 1817 *p = '\n'; 1818 // Check for utf8 "bom"; U+FEFF is encoded as EF BB BF. Do this 1819 // when finding the BF and check the previous two bytes. 1820 else if (*p == 0xbf && enc_utf8 && !binary) 1821 { 1822 // Find the two bytes before the 0xbf. If p is at buf, or buf 1823 // + 1, these may be in the "prev" string. 1824 char_u back1 = p >= buf + 1 ? p[-1] 1825 : prevlen >= 1 ? prev[prevlen - 1] : NUL; 1826 char_u back2 = p >= buf + 2 ? p[-2] 1827 : p == buf + 1 && prevlen >= 1 ? prev[prevlen - 1] 1828 : prevlen >= 2 ? prev[prevlen - 2] : NUL; 1829 1830 if (back2 == 0xef && back1 == 0xbb) 1831 { 1832 char_u *dest = p - 2; 1833 1834 // Usually a BOM is at the beginning of a file, and so at 1835 // the beginning of a line; then we can just step over it. 1836 if (start == dest) 1837 start = p + 1; 1838 else 1839 { 1840 // have to shuffle buf to close gap 1841 int adjust_prevlen = 0; 1842 1843 if (dest < buf) 1844 { 1845 // must be 1 or 2 1846 adjust_prevlen = (int)(buf - dest); 1847 dest = buf; 1848 } 1849 if (readlen > p - buf + 1) 1850 mch_memmove(dest, p + 1, readlen - (p - buf) - 1); 1851 readlen -= 3 - adjust_prevlen; 1852 prevlen -= adjust_prevlen; 1853 p = dest - 1; 1854 } 1855 } 1856 } 1857 } // for 1858 1859 if (failed || (cnt >= maxline && maxline >= 0) || readlen <= 0) 1860 break; 1861 if (start < p) 1862 { 1863 // There's part of a line in buf, store it in "prev". 1864 if (p - start + prevlen >= prevsize) 1865 { 1866 // need bigger "prev" buffer 1867 char_u *newprev; 1868 1869 // A common use case is ordinary text files and "prev" gets a 1870 // fragment of a line, so the first allocation is made 1871 // small, to avoid repeatedly 'allocing' large and 1872 // 'reallocing' small. 1873 if (prevsize == 0) 1874 prevsize = (long)(p - start); 1875 else 1876 { 1877 long grow50pc = (prevsize * 3) / 2; 1878 long growmin = (long)((p - start) * 2 + prevlen); 1879 prevsize = grow50pc > growmin ? grow50pc : growmin; 1880 } 1881 newprev = vim_realloc(prev, prevsize); 1882 if (newprev == NULL) 1883 { 1884 do_outofmem_msg((long_u)prevsize); 1885 failed = TRUE; 1886 break; 1887 } 1888 prev = newprev; 1889 } 1890 // Add the line part to end of "prev". 1891 mch_memmove(prev + prevlen, start, p - start); 1892 prevlen += (long)(p - start); 1893 } 1894 } // while 1895 1896 // For a negative line count use only the lines at the end of the file, 1897 // free the rest. 1898 if (!failed && maxline < 0) 1899 while (cnt > -maxline) 1900 { 1901 listitem_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first); 1902 --cnt; 1903 } 1904 1905 if (failed) 1906 { 1907 // an empty list is returned on error 1908 list_free(rettv->vval.v_list); 1909 rettv_list_alloc(rettv); 1910 } 1911 1912 vim_free(prev); 1913 fclose(fd); 1914 } 1915 1916 /* 1917 * "readblob()" function 1918 */ 1919 void 1920 f_readblob(typval_T *argvars, typval_T *rettv) 1921 { 1922 read_file_or_blob(argvars, rettv, TRUE); 1923 } 1924 1925 /* 1926 * "readfile()" function 1927 */ 1928 void 1929 f_readfile(typval_T *argvars, typval_T *rettv) 1930 { 1931 read_file_or_blob(argvars, rettv, FALSE); 1932 } 1933 1934 /* 1935 * "resolve()" function 1936 */ 1937 void 1938 f_resolve(typval_T *argvars, typval_T *rettv) 1939 { 1940 char_u *p; 1941 #ifdef HAVE_READLINK 1942 char_u *buf = NULL; 1943 #endif 1944 1945 p = tv_get_string(&argvars[0]); 1946 #ifdef FEAT_SHORTCUT 1947 { 1948 char_u *v = NULL; 1949 1950 v = mch_resolve_path(p, TRUE); 1951 if (v != NULL) 1952 rettv->vval.v_string = v; 1953 else 1954 rettv->vval.v_string = vim_strsave(p); 1955 } 1956 #else 1957 # ifdef HAVE_READLINK 1958 { 1959 char_u *cpy; 1960 int len; 1961 char_u *remain = NULL; 1962 char_u *q; 1963 int is_relative_to_current = FALSE; 1964 int has_trailing_pathsep = FALSE; 1965 int limit = 100; 1966 1967 p = vim_strsave(p); 1968 if (p == NULL) 1969 goto fail; 1970 if (p[0] == '.' && (vim_ispathsep(p[1]) 1971 || (p[1] == '.' && (vim_ispathsep(p[2]))))) 1972 is_relative_to_current = TRUE; 1973 1974 len = STRLEN(p); 1975 if (len > 1 && after_pathsep(p, p + len)) 1976 { 1977 has_trailing_pathsep = TRUE; 1978 p[len - 1] = NUL; // the trailing slash breaks readlink() 1979 } 1980 1981 q = getnextcomp(p); 1982 if (*q != NUL) 1983 { 1984 // Separate the first path component in "p", and keep the 1985 // remainder (beginning with the path separator). 1986 remain = vim_strsave(q - 1); 1987 q[-1] = NUL; 1988 } 1989 1990 buf = alloc(MAXPATHL + 1); 1991 if (buf == NULL) 1992 { 1993 vim_free(p); 1994 goto fail; 1995 } 1996 1997 for (;;) 1998 { 1999 for (;;) 2000 { 2001 len = readlink((char *)p, (char *)buf, MAXPATHL); 2002 if (len <= 0) 2003 break; 2004 buf[len] = NUL; 2005 2006 if (limit-- == 0) 2007 { 2008 vim_free(p); 2009 vim_free(remain); 2010 emsg(_("E655: Too many symbolic links (cycle?)")); 2011 rettv->vval.v_string = NULL; 2012 goto fail; 2013 } 2014 2015 // Ensure that the result will have a trailing path separator 2016 // if the argument has one. 2017 if (remain == NULL && has_trailing_pathsep) 2018 add_pathsep(buf); 2019 2020 // Separate the first path component in the link value and 2021 // concatenate the remainders. 2022 q = getnextcomp(vim_ispathsep(*buf) ? buf + 1 : buf); 2023 if (*q != NUL) 2024 { 2025 if (remain == NULL) 2026 remain = vim_strsave(q - 1); 2027 else 2028 { 2029 cpy = concat_str(q - 1, remain); 2030 if (cpy != NULL) 2031 { 2032 vim_free(remain); 2033 remain = cpy; 2034 } 2035 } 2036 q[-1] = NUL; 2037 } 2038 2039 q = gettail(p); 2040 if (q > p && *q == NUL) 2041 { 2042 // Ignore trailing path separator. 2043 q[-1] = NUL; 2044 q = gettail(p); 2045 } 2046 if (q > p && !mch_isFullName(buf)) 2047 { 2048 // symlink is relative to directory of argument 2049 cpy = alloc(STRLEN(p) + STRLEN(buf) + 1); 2050 if (cpy != NULL) 2051 { 2052 STRCPY(cpy, p); 2053 STRCPY(gettail(cpy), buf); 2054 vim_free(p); 2055 p = cpy; 2056 } 2057 } 2058 else 2059 { 2060 vim_free(p); 2061 p = vim_strsave(buf); 2062 } 2063 } 2064 2065 if (remain == NULL) 2066 break; 2067 2068 // Append the first path component of "remain" to "p". 2069 q = getnextcomp(remain + 1); 2070 len = q - remain - (*q != NUL); 2071 cpy = vim_strnsave(p, STRLEN(p) + len); 2072 if (cpy != NULL) 2073 { 2074 STRNCAT(cpy, remain, len); 2075 vim_free(p); 2076 p = cpy; 2077 } 2078 // Shorten "remain". 2079 if (*q != NUL) 2080 STRMOVE(remain, q - 1); 2081 else 2082 VIM_CLEAR(remain); 2083 } 2084 2085 // If the result is a relative path name, make it explicitly relative to 2086 // the current directory if and only if the argument had this form. 2087 if (!vim_ispathsep(*p)) 2088 { 2089 if (is_relative_to_current 2090 && *p != NUL 2091 && !(p[0] == '.' 2092 && (p[1] == NUL 2093 || vim_ispathsep(p[1]) 2094 || (p[1] == '.' 2095 && (p[2] == NUL 2096 || vim_ispathsep(p[2])))))) 2097 { 2098 // Prepend "./". 2099 cpy = concat_str((char_u *)"./", p); 2100 if (cpy != NULL) 2101 { 2102 vim_free(p); 2103 p = cpy; 2104 } 2105 } 2106 else if (!is_relative_to_current) 2107 { 2108 // Strip leading "./". 2109 q = p; 2110 while (q[0] == '.' && vim_ispathsep(q[1])) 2111 q += 2; 2112 if (q > p) 2113 STRMOVE(p, p + 2); 2114 } 2115 } 2116 2117 // Ensure that the result will have no trailing path separator 2118 // if the argument had none. But keep "/" or "//". 2119 if (!has_trailing_pathsep) 2120 { 2121 q = p + STRLEN(p); 2122 if (after_pathsep(p, q)) 2123 *gettail_sep(p) = NUL; 2124 } 2125 2126 rettv->vval.v_string = p; 2127 } 2128 # else 2129 rettv->vval.v_string = vim_strsave(p); 2130 # endif 2131 #endif 2132 2133 simplify_filename(rettv->vval.v_string); 2134 2135 #ifdef HAVE_READLINK 2136 fail: 2137 vim_free(buf); 2138 #endif 2139 rettv->v_type = VAR_STRING; 2140 } 2141 2142 /* 2143 * "tempname()" function 2144 */ 2145 void 2146 f_tempname(typval_T *argvars UNUSED, typval_T *rettv) 2147 { 2148 static int x = 'A'; 2149 2150 rettv->v_type = VAR_STRING; 2151 rettv->vval.v_string = vim_tempname(x, FALSE); 2152 2153 // Advance 'x' to use A-Z and 0-9, so that there are at least 34 different 2154 // names. Skip 'I' and 'O', they are used for shell redirection. 2155 do 2156 { 2157 if (x == 'Z') 2158 x = '0'; 2159 else if (x == '9') 2160 x = 'A'; 2161 else 2162 { 2163 #ifdef EBCDIC 2164 if (x == 'I') 2165 x = 'J'; 2166 else if (x == 'R') 2167 x = 'S'; 2168 else 2169 #endif 2170 ++x; 2171 } 2172 } while (x == 'I' || x == 'O'); 2173 } 2174 2175 /* 2176 * "writefile()" function 2177 */ 2178 void 2179 f_writefile(typval_T *argvars, typval_T *rettv) 2180 { 2181 int binary = FALSE; 2182 int append = FALSE; 2183 #ifdef HAVE_FSYNC 2184 int do_fsync = p_fs; 2185 #endif 2186 char_u *fname; 2187 FILE *fd; 2188 int ret = 0; 2189 listitem_T *li; 2190 list_T *list = NULL; 2191 blob_T *blob = NULL; 2192 2193 rettv->vval.v_number = -1; 2194 if (check_secure()) 2195 return; 2196 2197 if (in_vim9script() 2198 && (check_for_list_or_blob_arg(argvars, 0) == FAIL 2199 || check_for_string_arg(argvars, 1) == FAIL 2200 || check_for_opt_string_arg(argvars, 2) == FAIL)) 2201 return; 2202 2203 if (argvars[0].v_type == VAR_LIST) 2204 { 2205 list = argvars[0].vval.v_list; 2206 if (list == NULL) 2207 return; 2208 CHECK_LIST_MATERIALIZE(list); 2209 FOR_ALL_LIST_ITEMS(list, li) 2210 if (tv_get_string_chk(&li->li_tv) == NULL) 2211 return; 2212 } 2213 else if (argvars[0].v_type == VAR_BLOB) 2214 { 2215 blob = argvars[0].vval.v_blob; 2216 if (blob == NULL) 2217 return; 2218 } 2219 else 2220 { 2221 semsg(_(e_invarg2), 2222 _("writefile() first argument must be a List or a Blob")); 2223 return; 2224 } 2225 2226 if (argvars[2].v_type != VAR_UNKNOWN) 2227 { 2228 char_u *arg2 = tv_get_string_chk(&argvars[2]); 2229 2230 if (arg2 == NULL) 2231 return; 2232 if (vim_strchr(arg2, 'b') != NULL) 2233 binary = TRUE; 2234 if (vim_strchr(arg2, 'a') != NULL) 2235 append = TRUE; 2236 #ifdef HAVE_FSYNC 2237 if (vim_strchr(arg2, 's') != NULL) 2238 do_fsync = TRUE; 2239 else if (vim_strchr(arg2, 'S') != NULL) 2240 do_fsync = FALSE; 2241 #endif 2242 } 2243 2244 fname = tv_get_string_chk(&argvars[1]); 2245 if (fname == NULL) 2246 return; 2247 2248 // Always open the file in binary mode, library functions have a mind of 2249 // their own about CR-LF conversion. 2250 if (*fname == NUL || (fd = mch_fopen((char *)fname, 2251 append ? APPENDBIN : WRITEBIN)) == NULL) 2252 { 2253 semsg(_(e_notcreate), *fname == NUL ? (char_u *)_("<empty>") : fname); 2254 ret = -1; 2255 } 2256 else if (blob) 2257 { 2258 if (write_blob(fd, blob) == FAIL) 2259 ret = -1; 2260 #ifdef HAVE_FSYNC 2261 else if (do_fsync) 2262 // Ignore the error, the user wouldn't know what to do about it. 2263 // May happen for a device. 2264 vim_ignored = vim_fsync(fileno(fd)); 2265 #endif 2266 fclose(fd); 2267 } 2268 else 2269 { 2270 if (write_list(fd, list, binary) == FAIL) 2271 ret = -1; 2272 #ifdef HAVE_FSYNC 2273 else if (do_fsync) 2274 // Ignore the error, the user wouldn't know what to do about it. 2275 // May happen for a device. 2276 vim_ignored = vim_fsync(fileno(fd)); 2277 #endif 2278 fclose(fd); 2279 } 2280 2281 rettv->vval.v_number = ret; 2282 } 2283 2284 #endif // FEAT_EVAL 2285 2286 #if defined(FEAT_BROWSE) || defined(PROTO) 2287 /* 2288 * Generic browse function. Calls gui_mch_browse() when possible. 2289 * Later this may pop-up a non-GUI file selector (external command?). 2290 */ 2291 char_u * 2292 do_browse( 2293 int flags, // BROWSE_SAVE and BROWSE_DIR 2294 char_u *title, // title for the window 2295 char_u *dflt, // default file name (may include directory) 2296 char_u *ext, // extension added 2297 char_u *initdir, // initial directory, NULL for current dir or 2298 // when using path from "dflt" 2299 char_u *filter, // file name filter 2300 buf_T *buf) // buffer to read/write for 2301 { 2302 char_u *fname; 2303 static char_u *last_dir = NULL; // last used directory 2304 char_u *tofree = NULL; 2305 int save_cmod_flags = cmdmod.cmod_flags; 2306 2307 // Must turn off browse to avoid that autocommands will get the 2308 // flag too! 2309 cmdmod.cmod_flags &= ~CMOD_BROWSE; 2310 2311 if (title == NULL || *title == NUL) 2312 { 2313 if (flags & BROWSE_DIR) 2314 title = (char_u *)_("Select Directory dialog"); 2315 else if (flags & BROWSE_SAVE) 2316 title = (char_u *)_("Save File dialog"); 2317 else 2318 title = (char_u *)_("Open File dialog"); 2319 } 2320 2321 // When no directory specified, use default file name, default dir, buffer 2322 // dir, last dir or current dir 2323 if ((initdir == NULL || *initdir == NUL) && dflt != NULL && *dflt != NUL) 2324 { 2325 if (mch_isdir(dflt)) // default file name is a directory 2326 { 2327 initdir = dflt; 2328 dflt = NULL; 2329 } 2330 else if (gettail(dflt) != dflt) // default file name includes a path 2331 { 2332 tofree = vim_strsave(dflt); 2333 if (tofree != NULL) 2334 { 2335 initdir = tofree; 2336 *gettail(initdir) = NUL; 2337 dflt = gettail(dflt); 2338 } 2339 } 2340 } 2341 2342 if (initdir == NULL || *initdir == NUL) 2343 { 2344 // When 'browsedir' is a directory, use it 2345 if (STRCMP(p_bsdir, "last") != 0 2346 && STRCMP(p_bsdir, "buffer") != 0 2347 && STRCMP(p_bsdir, "current") != 0 2348 && mch_isdir(p_bsdir)) 2349 initdir = p_bsdir; 2350 // When saving or 'browsedir' is "buffer", use buffer fname 2351 else if (((flags & BROWSE_SAVE) || *p_bsdir == 'b') 2352 && buf != NULL && buf->b_ffname != NULL) 2353 { 2354 if (dflt == NULL || *dflt == NUL) 2355 dflt = gettail(curbuf->b_ffname); 2356 tofree = vim_strsave(curbuf->b_ffname); 2357 if (tofree != NULL) 2358 { 2359 initdir = tofree; 2360 *gettail(initdir) = NUL; 2361 } 2362 } 2363 // When 'browsedir' is "last", use dir from last browse 2364 else if (*p_bsdir == 'l') 2365 initdir = last_dir; 2366 // When 'browsedir is "current", use current directory. This is the 2367 // default already, leave initdir empty. 2368 } 2369 2370 # ifdef FEAT_GUI 2371 if (gui.in_use) // when this changes, also adjust f_has()! 2372 { 2373 if (filter == NULL 2374 # ifdef FEAT_EVAL 2375 && (filter = get_var_value((char_u *)"b:browsefilter")) == NULL 2376 && (filter = get_var_value((char_u *)"g:browsefilter")) == NULL 2377 # endif 2378 ) 2379 filter = BROWSE_FILTER_DEFAULT; 2380 if (flags & BROWSE_DIR) 2381 { 2382 # if defined(FEAT_GUI_GTK) || defined(MSWIN) 2383 // For systems that have a directory dialog. 2384 fname = gui_mch_browsedir(title, initdir); 2385 # else 2386 // Generic solution for selecting a directory: select a file and 2387 // remove the file name. 2388 fname = gui_mch_browse(0, title, dflt, ext, initdir, (char_u *)""); 2389 # endif 2390 # if !defined(FEAT_GUI_GTK) 2391 // Win32 adds a dummy file name, others return an arbitrary file 2392 // name. GTK+ 2 returns only the directory, 2393 if (fname != NULL && *fname != NUL && !mch_isdir(fname)) 2394 { 2395 // Remove the file name. 2396 char_u *tail = gettail_sep(fname); 2397 2398 if (tail == fname) 2399 *tail++ = '.'; // use current dir 2400 *tail = NUL; 2401 } 2402 # endif 2403 } 2404 else 2405 fname = gui_mch_browse(flags & BROWSE_SAVE, 2406 title, dflt, ext, initdir, (char_u *)_(filter)); 2407 2408 // We hang around in the dialog for a while, the user might do some 2409 // things to our files. The Win32 dialog allows deleting or renaming 2410 // a file, check timestamps. 2411 need_check_timestamps = TRUE; 2412 did_check_timestamps = FALSE; 2413 } 2414 else 2415 # endif 2416 { 2417 // TODO: non-GUI file selector here 2418 emsg(_("E338: Sorry, no file browser in console mode")); 2419 fname = NULL; 2420 } 2421 2422 // keep the directory for next time 2423 if (fname != NULL) 2424 { 2425 vim_free(last_dir); 2426 last_dir = vim_strsave(fname); 2427 if (last_dir != NULL && !(flags & BROWSE_DIR)) 2428 { 2429 *gettail(last_dir) = NUL; 2430 if (*last_dir == NUL) 2431 { 2432 // filename only returned, must be in current dir 2433 vim_free(last_dir); 2434 last_dir = alloc(MAXPATHL); 2435 if (last_dir != NULL) 2436 mch_dirname(last_dir, MAXPATHL); 2437 } 2438 } 2439 } 2440 2441 vim_free(tofree); 2442 cmdmod.cmod_flags = save_cmod_flags; 2443 2444 return fname; 2445 } 2446 #endif 2447 2448 #if defined(FEAT_EVAL) || defined(PROTO) 2449 2450 /* 2451 * "browse(save, title, initdir, default)" function 2452 */ 2453 void 2454 f_browse(typval_T *argvars UNUSED, typval_T *rettv) 2455 { 2456 # ifdef FEAT_BROWSE 2457 int save; 2458 char_u *title; 2459 char_u *initdir; 2460 char_u *defname; 2461 char_u buf[NUMBUFLEN]; 2462 char_u buf2[NUMBUFLEN]; 2463 int error = FALSE; 2464 2465 if (in_vim9script() 2466 && (check_for_bool_arg(argvars, 0) == FAIL 2467 || check_for_string_arg(argvars, 1) == FAIL 2468 || check_for_string_arg(argvars, 2) == FAIL 2469 || check_for_string_arg(argvars, 3) == FAIL)) 2470 return; 2471 2472 save = (int)tv_get_number_chk(&argvars[0], &error); 2473 title = tv_get_string_chk(&argvars[1]); 2474 initdir = tv_get_string_buf_chk(&argvars[2], buf); 2475 defname = tv_get_string_buf_chk(&argvars[3], buf2); 2476 2477 if (error || title == NULL || initdir == NULL || defname == NULL) 2478 rettv->vval.v_string = NULL; 2479 else 2480 rettv->vval.v_string = 2481 do_browse(save ? BROWSE_SAVE : 0, 2482 title, defname, NULL, initdir, NULL, curbuf); 2483 # else 2484 rettv->vval.v_string = NULL; 2485 # endif 2486 rettv->v_type = VAR_STRING; 2487 } 2488 2489 /* 2490 * "browsedir(title, initdir)" function 2491 */ 2492 void 2493 f_browsedir(typval_T *argvars UNUSED, typval_T *rettv) 2494 { 2495 # ifdef FEAT_BROWSE 2496 char_u *title; 2497 char_u *initdir; 2498 char_u buf[NUMBUFLEN]; 2499 2500 title = tv_get_string_chk(&argvars[0]); 2501 initdir = tv_get_string_buf_chk(&argvars[1], buf); 2502 2503 if (title == NULL || initdir == NULL) 2504 rettv->vval.v_string = NULL; 2505 else 2506 rettv->vval.v_string = do_browse(BROWSE_DIR, 2507 title, NULL, NULL, initdir, NULL, curbuf); 2508 # else 2509 rettv->vval.v_string = NULL; 2510 # endif 2511 rettv->v_type = VAR_STRING; 2512 } 2513 2514 #endif // FEAT_EVAL 2515 2516 /* 2517 * Replace home directory by "~" in each space or comma separated file name in 2518 * 'src'. 2519 * If anything fails (except when out of space) dst equals src. 2520 */ 2521 void 2522 home_replace( 2523 buf_T *buf, // when not NULL, check for help files 2524 char_u *src, // input file name 2525 char_u *dst, // where to put the result 2526 int dstlen, // maximum length of the result 2527 int one) // if TRUE, only replace one file name, include 2528 // spaces and commas in the file name. 2529 { 2530 size_t dirlen = 0, envlen = 0; 2531 size_t len; 2532 char_u *homedir_env, *homedir_env_orig; 2533 char_u *p; 2534 2535 if (src == NULL) 2536 { 2537 *dst = NUL; 2538 return; 2539 } 2540 2541 /* 2542 * If the file is a help file, remove the path completely. 2543 */ 2544 if (buf != NULL && buf->b_help) 2545 { 2546 vim_snprintf((char *)dst, dstlen, "%s", gettail(src)); 2547 return; 2548 } 2549 2550 /* 2551 * We check both the value of the $HOME environment variable and the 2552 * "real" home directory. 2553 */ 2554 if (homedir != NULL) 2555 dirlen = STRLEN(homedir); 2556 2557 #ifdef VMS 2558 homedir_env_orig = homedir_env = mch_getenv((char_u *)"SYS$LOGIN"); 2559 #else 2560 homedir_env_orig = homedir_env = mch_getenv((char_u *)"HOME"); 2561 #endif 2562 #ifdef MSWIN 2563 if (homedir_env == NULL) 2564 homedir_env_orig = homedir_env = mch_getenv((char_u *)"USERPROFILE"); 2565 #endif 2566 // Empty is the same as not set. 2567 if (homedir_env != NULL && *homedir_env == NUL) 2568 homedir_env = NULL; 2569 2570 if (homedir_env != NULL && *homedir_env == '~') 2571 { 2572 int usedlen = 0; 2573 int flen; 2574 char_u *fbuf = NULL; 2575 2576 flen = (int)STRLEN(homedir_env); 2577 (void)modify_fname((char_u *)":p", FALSE, &usedlen, 2578 &homedir_env, &fbuf, &flen); 2579 flen = (int)STRLEN(homedir_env); 2580 if (flen > 0 && vim_ispathsep(homedir_env[flen - 1])) 2581 // Remove the trailing / that is added to a directory. 2582 homedir_env[flen - 1] = NUL; 2583 } 2584 2585 if (homedir_env != NULL) 2586 envlen = STRLEN(homedir_env); 2587 2588 if (!one) 2589 src = skipwhite(src); 2590 while (*src && dstlen > 0) 2591 { 2592 /* 2593 * Here we are at the beginning of a file name. 2594 * First, check to see if the beginning of the file name matches 2595 * $HOME or the "real" home directory. Check that there is a '/' 2596 * after the match (so that if e.g. the file is "/home/pieter/bla", 2597 * and the home directory is "/home/piet", the file does not end up 2598 * as "~er/bla" (which would seem to indicate the file "bla" in user 2599 * er's home directory)). 2600 */ 2601 p = homedir; 2602 len = dirlen; 2603 for (;;) 2604 { 2605 if ( len 2606 && fnamencmp(src, p, len) == 0 2607 && (vim_ispathsep(src[len]) 2608 || (!one && (src[len] == ',' || src[len] == ' ')) 2609 || src[len] == NUL)) 2610 { 2611 src += len; 2612 if (--dstlen > 0) 2613 *dst++ = '~'; 2614 2615 // Do not add directory separator into dst, because dst is 2616 // expected to just return the directory name without the 2617 // directory separator '/'. 2618 break; 2619 } 2620 if (p == homedir_env) 2621 break; 2622 p = homedir_env; 2623 len = envlen; 2624 } 2625 2626 // if (!one) skip to separator: space or comma 2627 while (*src && (one || (*src != ',' && *src != ' ')) && --dstlen > 0) 2628 *dst++ = *src++; 2629 // skip separator 2630 while ((*src == ' ' || *src == ',') && --dstlen > 0) 2631 *dst++ = *src++; 2632 } 2633 // if (dstlen == 0) out of space, what to do??? 2634 2635 *dst = NUL; 2636 2637 if (homedir_env != homedir_env_orig) 2638 vim_free(homedir_env); 2639 } 2640 2641 /* 2642 * Like home_replace, store the replaced string in allocated memory. 2643 * When something fails, NULL is returned. 2644 */ 2645 char_u * 2646 home_replace_save( 2647 buf_T *buf, // when not NULL, check for help files 2648 char_u *src) // input file name 2649 { 2650 char_u *dst; 2651 unsigned len; 2652 2653 len = 3; // space for "~/" and trailing NUL 2654 if (src != NULL) // just in case 2655 len += (unsigned)STRLEN(src); 2656 dst = alloc(len); 2657 if (dst != NULL) 2658 home_replace(buf, src, dst, len, TRUE); 2659 return dst; 2660 } 2661 2662 /* 2663 * Compare two file names and return: 2664 * FPC_SAME if they both exist and are the same file. 2665 * FPC_SAMEX if they both don't exist and have the same file name. 2666 * FPC_DIFF if they both exist and are different files. 2667 * FPC_NOTX if they both don't exist. 2668 * FPC_DIFFX if one of them doesn't exist. 2669 * For the first name environment variables are expanded if "expandenv" is 2670 * TRUE. 2671 */ 2672 int 2673 fullpathcmp( 2674 char_u *s1, 2675 char_u *s2, 2676 int checkname, // when both don't exist, check file names 2677 int expandenv) 2678 { 2679 #ifdef UNIX 2680 char_u exp1[MAXPATHL]; 2681 char_u full1[MAXPATHL]; 2682 char_u full2[MAXPATHL]; 2683 stat_T st1, st2; 2684 int r1, r2; 2685 2686 if (expandenv) 2687 expand_env(s1, exp1, MAXPATHL); 2688 else 2689 vim_strncpy(exp1, s1, MAXPATHL - 1); 2690 r1 = mch_stat((char *)exp1, &st1); 2691 r2 = mch_stat((char *)s2, &st2); 2692 if (r1 != 0 && r2 != 0) 2693 { 2694 // if mch_stat() doesn't work, may compare the names 2695 if (checkname) 2696 { 2697 if (fnamecmp(exp1, s2) == 0) 2698 return FPC_SAMEX; 2699 r1 = vim_FullName(exp1, full1, MAXPATHL, FALSE); 2700 r2 = vim_FullName(s2, full2, MAXPATHL, FALSE); 2701 if (r1 == OK && r2 == OK && fnamecmp(full1, full2) == 0) 2702 return FPC_SAMEX; 2703 } 2704 return FPC_NOTX; 2705 } 2706 if (r1 != 0 || r2 != 0) 2707 return FPC_DIFFX; 2708 if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino) 2709 return FPC_SAME; 2710 return FPC_DIFF; 2711 #else 2712 char_u *exp1; // expanded s1 2713 char_u *full1; // full path of s1 2714 char_u *full2; // full path of s2 2715 int retval = FPC_DIFF; 2716 int r1, r2; 2717 2718 // allocate one buffer to store three paths (alloc()/free() is slow!) 2719 if ((exp1 = alloc(MAXPATHL * 3)) != NULL) 2720 { 2721 full1 = exp1 + MAXPATHL; 2722 full2 = full1 + MAXPATHL; 2723 2724 if (expandenv) 2725 expand_env(s1, exp1, MAXPATHL); 2726 else 2727 vim_strncpy(exp1, s1, MAXPATHL - 1); 2728 r1 = vim_FullName(exp1, full1, MAXPATHL, FALSE); 2729 r2 = vim_FullName(s2, full2, MAXPATHL, FALSE); 2730 2731 // If vim_FullName() fails, the file probably doesn't exist. 2732 if (r1 != OK && r2 != OK) 2733 { 2734 if (checkname && fnamecmp(exp1, s2) == 0) 2735 retval = FPC_SAMEX; 2736 else 2737 retval = FPC_NOTX; 2738 } 2739 else if (r1 != OK || r2 != OK) 2740 retval = FPC_DIFFX; 2741 else if (fnamecmp(full1, full2)) 2742 retval = FPC_DIFF; 2743 else 2744 retval = FPC_SAME; 2745 vim_free(exp1); 2746 } 2747 return retval; 2748 #endif 2749 } 2750 2751 /* 2752 * Get the tail of a path: the file name. 2753 * When the path ends in a path separator the tail is the NUL after it. 2754 * Fail safe: never returns NULL. 2755 */ 2756 char_u * 2757 gettail(char_u *fname) 2758 { 2759 char_u *p1, *p2; 2760 2761 if (fname == NULL) 2762 return (char_u *)""; 2763 for (p1 = p2 = get_past_head(fname); *p2; ) // find last part of path 2764 { 2765 if (vim_ispathsep_nocolon(*p2)) 2766 p1 = p2 + 1; 2767 MB_PTR_ADV(p2); 2768 } 2769 return p1; 2770 } 2771 2772 /* 2773 * Get pointer to tail of "fname", including path separators. Putting a NUL 2774 * here leaves the directory name. Takes care of "c:/" and "//". 2775 * Always returns a valid pointer. 2776 */ 2777 char_u * 2778 gettail_sep(char_u *fname) 2779 { 2780 char_u *p; 2781 char_u *t; 2782 2783 p = get_past_head(fname); // don't remove the '/' from "c:/file" 2784 t = gettail(fname); 2785 while (t > p && after_pathsep(fname, t)) 2786 --t; 2787 #ifdef VMS 2788 // path separator is part of the path 2789 ++t; 2790 #endif 2791 return t; 2792 } 2793 2794 /* 2795 * get the next path component (just after the next path separator). 2796 */ 2797 char_u * 2798 getnextcomp(char_u *fname) 2799 { 2800 while (*fname && !vim_ispathsep(*fname)) 2801 MB_PTR_ADV(fname); 2802 if (*fname) 2803 ++fname; 2804 return fname; 2805 } 2806 2807 /* 2808 * Get a pointer to one character past the head of a path name. 2809 * Unix: after "/"; DOS: after "c:\"; Amiga: after "disk:/"; Mac: no head. 2810 * If there is no head, path is returned. 2811 */ 2812 char_u * 2813 get_past_head(char_u *path) 2814 { 2815 char_u *retval; 2816 2817 #if defined(MSWIN) 2818 // may skip "c:" 2819 if (isalpha(path[0]) && path[1] == ':') 2820 retval = path + 2; 2821 else 2822 retval = path; 2823 #else 2824 # if defined(AMIGA) 2825 // may skip "label:" 2826 retval = vim_strchr(path, ':'); 2827 if (retval == NULL) 2828 retval = path; 2829 # else // Unix 2830 retval = path; 2831 # endif 2832 #endif 2833 2834 while (vim_ispathsep(*retval)) 2835 ++retval; 2836 2837 return retval; 2838 } 2839 2840 /* 2841 * Return TRUE if 'c' is a path separator. 2842 * Note that for MS-Windows this includes the colon. 2843 */ 2844 int 2845 vim_ispathsep(int c) 2846 { 2847 #ifdef UNIX 2848 return (c == '/'); // UNIX has ':' inside file names 2849 #else 2850 # ifdef BACKSLASH_IN_FILENAME 2851 return (c == ':' || c == '/' || c == '\\'); 2852 # else 2853 # ifdef VMS 2854 // server"user passwd"::device:[full.path.name]fname.extension;version" 2855 return (c == ':' || c == '[' || c == ']' || c == '/' 2856 || c == '<' || c == '>' || c == '"' ); 2857 # else 2858 return (c == ':' || c == '/'); 2859 # endif // VMS 2860 # endif 2861 #endif 2862 } 2863 2864 /* 2865 * Like vim_ispathsep(c), but exclude the colon for MS-Windows. 2866 */ 2867 int 2868 vim_ispathsep_nocolon(int c) 2869 { 2870 return vim_ispathsep(c) 2871 #ifdef BACKSLASH_IN_FILENAME 2872 && c != ':' 2873 #endif 2874 ; 2875 } 2876 2877 /* 2878 * Return TRUE if the directory of "fname" exists, FALSE otherwise. 2879 * Also returns TRUE if there is no directory name. 2880 * "fname" must be writable!. 2881 */ 2882 int 2883 dir_of_file_exists(char_u *fname) 2884 { 2885 char_u *p; 2886 int c; 2887 int retval; 2888 2889 p = gettail_sep(fname); 2890 if (p == fname) 2891 return TRUE; 2892 c = *p; 2893 *p = NUL; 2894 retval = mch_isdir(fname); 2895 *p = c; 2896 return retval; 2897 } 2898 2899 /* 2900 * Versions of fnamecmp() and fnamencmp() that handle '/' and '\' equally 2901 * and deal with 'fileignorecase'. 2902 */ 2903 int 2904 vim_fnamecmp(char_u *x, char_u *y) 2905 { 2906 #ifdef BACKSLASH_IN_FILENAME 2907 return vim_fnamencmp(x, y, MAXPATHL); 2908 #else 2909 if (p_fic) 2910 return MB_STRICMP(x, y); 2911 return STRCMP(x, y); 2912 #endif 2913 } 2914 2915 int 2916 vim_fnamencmp(char_u *x, char_u *y, size_t len) 2917 { 2918 #ifdef BACKSLASH_IN_FILENAME 2919 char_u *px = x; 2920 char_u *py = y; 2921 int cx = NUL; 2922 int cy = NUL; 2923 2924 while (len > 0) 2925 { 2926 cx = PTR2CHAR(px); 2927 cy = PTR2CHAR(py); 2928 if (cx == NUL || cy == NUL 2929 || ((p_fic ? MB_TOLOWER(cx) != MB_TOLOWER(cy) : cx != cy) 2930 && !(cx == '/' && cy == '\\') 2931 && !(cx == '\\' && cy == '/'))) 2932 break; 2933 len -= mb_ptr2len(px); 2934 px += mb_ptr2len(px); 2935 py += mb_ptr2len(py); 2936 } 2937 if (len == 0) 2938 return 0; 2939 return (cx - cy); 2940 #else 2941 if (p_fic) 2942 return MB_STRNICMP(x, y, len); 2943 return STRNCMP(x, y, len); 2944 #endif 2945 } 2946 2947 /* 2948 * Concatenate file names fname1 and fname2 into allocated memory. 2949 * Only add a '/' or '\\' when 'sep' is TRUE and it is necessary. 2950 */ 2951 char_u * 2952 concat_fnames(char_u *fname1, char_u *fname2, int sep) 2953 { 2954 char_u *dest; 2955 2956 dest = alloc(STRLEN(fname1) + STRLEN(fname2) + 3); 2957 if (dest != NULL) 2958 { 2959 STRCPY(dest, fname1); 2960 if (sep) 2961 add_pathsep(dest); 2962 STRCAT(dest, fname2); 2963 } 2964 return dest; 2965 } 2966 2967 /* 2968 * Add a path separator to a file name, unless it already ends in a path 2969 * separator. 2970 */ 2971 void 2972 add_pathsep(char_u *p) 2973 { 2974 if (*p != NUL && !after_pathsep(p, p + STRLEN(p))) 2975 STRCAT(p, PATHSEPSTR); 2976 } 2977 2978 /* 2979 * FullName_save - Make an allocated copy of a full file name. 2980 * Returns NULL when out of memory. 2981 */ 2982 char_u * 2983 FullName_save( 2984 char_u *fname, 2985 int force) // force expansion, even when it already looks 2986 // like a full path name 2987 { 2988 char_u *buf; 2989 char_u *new_fname = NULL; 2990 2991 if (fname == NULL) 2992 return NULL; 2993 2994 buf = alloc(MAXPATHL); 2995 if (buf != NULL) 2996 { 2997 if (vim_FullName(fname, buf, MAXPATHL, force) != FAIL) 2998 new_fname = vim_strsave(buf); 2999 else 3000 new_fname = vim_strsave(fname); 3001 vim_free(buf); 3002 } 3003 return new_fname; 3004 } 3005 3006 /* 3007 * return TRUE if "fname" exists. 3008 */ 3009 int 3010 vim_fexists(char_u *fname) 3011 { 3012 stat_T st; 3013 3014 if (mch_stat((char *)fname, &st)) 3015 return FALSE; 3016 return TRUE; 3017 } 3018 3019 /* 3020 * Invoke expand_wildcards() for one pattern. 3021 * Expand items like "%:h" before the expansion. 3022 * Returns OK or FAIL. 3023 */ 3024 int 3025 expand_wildcards_eval( 3026 char_u **pat, // pointer to input pattern 3027 int *num_file, // resulting number of files 3028 char_u ***file, // array of resulting files 3029 int flags) // EW_DIR, etc. 3030 { 3031 int ret = FAIL; 3032 char_u *eval_pat = NULL; 3033 char_u *exp_pat = *pat; 3034 char *ignored_msg; 3035 int usedlen; 3036 3037 if (*exp_pat == '%' || *exp_pat == '#' || *exp_pat == '<') 3038 { 3039 ++emsg_off; 3040 eval_pat = eval_vars(exp_pat, exp_pat, &usedlen, 3041 NULL, &ignored_msg, NULL); 3042 --emsg_off; 3043 if (eval_pat != NULL) 3044 exp_pat = concat_str(eval_pat, exp_pat + usedlen); 3045 } 3046 3047 if (exp_pat != NULL) 3048 ret = expand_wildcards(1, &exp_pat, num_file, file, flags); 3049 3050 if (eval_pat != NULL) 3051 { 3052 vim_free(exp_pat); 3053 vim_free(eval_pat); 3054 } 3055 3056 return ret; 3057 } 3058 3059 /* 3060 * Expand wildcards. Calls gen_expand_wildcards() and removes files matching 3061 * 'wildignore'. 3062 * Returns OK or FAIL. When FAIL then "num_files" won't be set. 3063 */ 3064 int 3065 expand_wildcards( 3066 int num_pat, // number of input patterns 3067 char_u **pat, // array of input patterns 3068 int *num_files, // resulting number of files 3069 char_u ***files, // array of resulting files 3070 int flags) // EW_DIR, etc. 3071 { 3072 int retval; 3073 int i, j; 3074 char_u *p; 3075 int non_suf_match; // number without matching suffix 3076 3077 retval = gen_expand_wildcards(num_pat, pat, num_files, files, flags); 3078 3079 // When keeping all matches, return here 3080 if ((flags & EW_KEEPALL) || retval == FAIL) 3081 return retval; 3082 3083 #ifdef FEAT_WILDIGN 3084 /* 3085 * Remove names that match 'wildignore'. 3086 */ 3087 if (*p_wig) 3088 { 3089 char_u *ffname; 3090 3091 // check all files in (*files)[] 3092 for (i = 0; i < *num_files; ++i) 3093 { 3094 ffname = FullName_save((*files)[i], FALSE); 3095 if (ffname == NULL) // out of memory 3096 break; 3097 # ifdef VMS 3098 vms_remove_version(ffname); 3099 # endif 3100 if (match_file_list(p_wig, (*files)[i], ffname)) 3101 { 3102 // remove this matching file from the list 3103 vim_free((*files)[i]); 3104 for (j = i; j + 1 < *num_files; ++j) 3105 (*files)[j] = (*files)[j + 1]; 3106 --*num_files; 3107 --i; 3108 } 3109 vim_free(ffname); 3110 } 3111 3112 // If the number of matches is now zero, we fail. 3113 if (*num_files == 0) 3114 { 3115 VIM_CLEAR(*files); 3116 return FAIL; 3117 } 3118 } 3119 #endif 3120 3121 /* 3122 * Move the names where 'suffixes' match to the end. 3123 */ 3124 if (*num_files > 1) 3125 { 3126 non_suf_match = 0; 3127 for (i = 0; i < *num_files; ++i) 3128 { 3129 if (!match_suffix((*files)[i])) 3130 { 3131 /* 3132 * Move the name without matching suffix to the front 3133 * of the list. 3134 */ 3135 p = (*files)[i]; 3136 for (j = i; j > non_suf_match; --j) 3137 (*files)[j] = (*files)[j - 1]; 3138 (*files)[non_suf_match++] = p; 3139 } 3140 } 3141 } 3142 3143 return retval; 3144 } 3145 3146 /* 3147 * Return TRUE if "fname" matches with an entry in 'suffixes'. 3148 */ 3149 int 3150 match_suffix(char_u *fname) 3151 { 3152 int fnamelen, setsuflen; 3153 char_u *setsuf; 3154 #define MAXSUFLEN 30 // maximum length of a file suffix 3155 char_u suf_buf[MAXSUFLEN]; 3156 3157 fnamelen = (int)STRLEN(fname); 3158 setsuflen = 0; 3159 for (setsuf = p_su; *setsuf; ) 3160 { 3161 setsuflen = copy_option_part(&setsuf, suf_buf, MAXSUFLEN, ".,"); 3162 if (setsuflen == 0) 3163 { 3164 char_u *tail = gettail(fname); 3165 3166 // empty entry: match name without a '.' 3167 if (vim_strchr(tail, '.') == NULL) 3168 { 3169 setsuflen = 1; 3170 break; 3171 } 3172 } 3173 else 3174 { 3175 if (fnamelen >= setsuflen 3176 && fnamencmp(suf_buf, fname + fnamelen - setsuflen, 3177 (size_t)setsuflen) == 0) 3178 break; 3179 setsuflen = 0; 3180 } 3181 } 3182 return (setsuflen != 0); 3183 } 3184 3185 #ifdef VIM_BACKTICK 3186 3187 /* 3188 * Return TRUE if we can expand this backtick thing here. 3189 */ 3190 static int 3191 vim_backtick(char_u *p) 3192 { 3193 return (*p == '`' && *(p + 1) != NUL && *(p + STRLEN(p) - 1) == '`'); 3194 } 3195 3196 /* 3197 * Expand an item in `backticks` by executing it as a command. 3198 * Currently only works when pat[] starts and ends with a `. 3199 * Returns number of file names found, -1 if an error is encountered. 3200 */ 3201 static int 3202 expand_backtick( 3203 garray_T *gap, 3204 char_u *pat, 3205 int flags) // EW_* flags 3206 { 3207 char_u *p; 3208 char_u *cmd; 3209 char_u *buffer; 3210 int cnt = 0; 3211 int i; 3212 3213 // Create the command: lop off the backticks. 3214 cmd = vim_strnsave(pat + 1, STRLEN(pat) - 2); 3215 if (cmd == NULL) 3216 return -1; 3217 3218 #ifdef FEAT_EVAL 3219 if (*cmd == '=') // `={expr}`: Expand expression 3220 buffer = eval_to_string(cmd + 1, TRUE); 3221 else 3222 #endif 3223 buffer = get_cmd_output(cmd, NULL, 3224 (flags & EW_SILENT) ? SHELL_SILENT : 0, NULL); 3225 vim_free(cmd); 3226 if (buffer == NULL) 3227 return -1; 3228 3229 cmd = buffer; 3230 while (*cmd != NUL) 3231 { 3232 cmd = skipwhite(cmd); // skip over white space 3233 p = cmd; 3234 while (*p != NUL && *p != '\r' && *p != '\n') // skip over entry 3235 ++p; 3236 // add an entry if it is not empty 3237 if (p > cmd) 3238 { 3239 i = *p; 3240 *p = NUL; 3241 addfile(gap, cmd, flags); 3242 *p = i; 3243 ++cnt; 3244 } 3245 cmd = p; 3246 while (*cmd != NUL && (*cmd == '\r' || *cmd == '\n')) 3247 ++cmd; 3248 } 3249 3250 vim_free(buffer); 3251 return cnt; 3252 } 3253 #endif // VIM_BACKTICK 3254 3255 #if defined(MSWIN) 3256 /* 3257 * File name expansion code for MS-DOS, Win16 and Win32. It's here because 3258 * it's shared between these systems. 3259 */ 3260 3261 /* 3262 * comparison function for qsort in dos_expandpath() 3263 */ 3264 static int 3265 pstrcmp(const void *a, const void *b) 3266 { 3267 return (pathcmp(*(char **)a, *(char **)b, -1)); 3268 } 3269 3270 /* 3271 * Recursively expand one path component into all matching files and/or 3272 * directories. Adds matches to "gap". Handles "*", "?", "[a-z]", "**", etc. 3273 * Return the number of matches found. 3274 * "path" has backslashes before chars that are not to be expanded, starting 3275 * at "path[wildoff]". 3276 * Return the number of matches found. 3277 * NOTE: much of this is identical to unix_expandpath(), keep in sync! 3278 */ 3279 static int 3280 dos_expandpath( 3281 garray_T *gap, 3282 char_u *path, 3283 int wildoff, 3284 int flags, // EW_* flags 3285 int didstar) // expanded "**" once already 3286 { 3287 char_u *buf; 3288 char_u *path_end; 3289 char_u *p, *s, *e; 3290 int start_len = gap->ga_len; 3291 char_u *pat; 3292 regmatch_T regmatch; 3293 int starts_with_dot; 3294 int matches; 3295 int len; 3296 int starstar = FALSE; 3297 static int stardepth = 0; // depth for "**" expansion 3298 HANDLE hFind = INVALID_HANDLE_VALUE; 3299 WIN32_FIND_DATAW wfb; 3300 WCHAR *wn = NULL; // UCS-2 name, NULL when not used. 3301 char_u *matchname; 3302 int ok; 3303 char_u *p_alt; 3304 3305 // Expanding "**" may take a long time, check for CTRL-C. 3306 if (stardepth > 0) 3307 { 3308 ui_breakcheck(); 3309 if (got_int) 3310 return 0; 3311 } 3312 3313 // Make room for file name. When doing encoding conversion the actual 3314 // length may be quite a bit longer, thus use the maximum possible length. 3315 buf = alloc(MAXPATHL); 3316 if (buf == NULL) 3317 return 0; 3318 3319 /* 3320 * Find the first part in the path name that contains a wildcard or a ~1. 3321 * Copy it into buf, including the preceding characters. 3322 */ 3323 p = buf; 3324 s = buf; 3325 e = NULL; 3326 path_end = path; 3327 while (*path_end != NUL) 3328 { 3329 // May ignore a wildcard that has a backslash before it; it will 3330 // be removed by rem_backslash() or file_pat_to_reg_pat() below. 3331 if (path_end >= path + wildoff && rem_backslash(path_end)) 3332 *p++ = *path_end++; 3333 else if (*path_end == '\\' || *path_end == ':' || *path_end == '/') 3334 { 3335 if (e != NULL) 3336 break; 3337 s = p + 1; 3338 } 3339 else if (path_end >= path + wildoff 3340 && vim_strchr((char_u *)"*?[~", *path_end) != NULL) 3341 e = p; 3342 if (has_mbyte) 3343 { 3344 len = (*mb_ptr2len)(path_end); 3345 STRNCPY(p, path_end, len); 3346 p += len; 3347 path_end += len; 3348 } 3349 else 3350 *p++ = *path_end++; 3351 } 3352 e = p; 3353 *e = NUL; 3354 3355 // now we have one wildcard component between s and e 3356 // Remove backslashes between "wildoff" and the start of the wildcard 3357 // component. 3358 for (p = buf + wildoff; p < s; ++p) 3359 if (rem_backslash(p)) 3360 { 3361 STRMOVE(p, p + 1); 3362 --e; 3363 --s; 3364 } 3365 3366 // Check for "**" between "s" and "e". 3367 for (p = s; p < e; ++p) 3368 if (p[0] == '*' && p[1] == '*') 3369 starstar = TRUE; 3370 3371 starts_with_dot = *s == '.'; 3372 pat = file_pat_to_reg_pat(s, e, NULL, FALSE); 3373 if (pat == NULL) 3374 { 3375 vim_free(buf); 3376 return 0; 3377 } 3378 3379 // compile the regexp into a program 3380 if (flags & (EW_NOERROR | EW_NOTWILD)) 3381 ++emsg_silent; 3382 regmatch.rm_ic = TRUE; // Always ignore case 3383 regmatch.regprog = vim_regcomp(pat, RE_MAGIC); 3384 if (flags & (EW_NOERROR | EW_NOTWILD)) 3385 --emsg_silent; 3386 vim_free(pat); 3387 3388 if (regmatch.regprog == NULL && (flags & EW_NOTWILD) == 0) 3389 { 3390 vim_free(buf); 3391 return 0; 3392 } 3393 3394 // remember the pattern or file name being looked for 3395 matchname = vim_strsave(s); 3396 3397 // If "**" is by itself, this is the first time we encounter it and more 3398 // is following then find matches without any directory. 3399 if (!didstar && stardepth < 100 && starstar && e - s == 2 3400 && *path_end == '/') 3401 { 3402 STRCPY(s, path_end + 1); 3403 ++stardepth; 3404 (void)dos_expandpath(gap, buf, (int)(s - buf), flags, TRUE); 3405 --stardepth; 3406 } 3407 3408 // Scan all files in the directory with "dir/ *.*" 3409 STRCPY(s, "*.*"); 3410 wn = enc_to_utf16(buf, NULL); 3411 if (wn != NULL) 3412 hFind = FindFirstFileW(wn, &wfb); 3413 ok = (hFind != INVALID_HANDLE_VALUE); 3414 3415 while (ok) 3416 { 3417 p = utf16_to_enc(wfb.cFileName, NULL); // p is allocated here 3418 3419 if (p == NULL) 3420 break; // out of memory 3421 3422 // Do not use the alternate filename when the file name ends in '~', 3423 // because it picks up backup files: short name for "foo.vim~" is 3424 // "foo~1.vim", which matches "*.vim". 3425 if (*wfb.cAlternateFileName == NUL || p[STRLEN(p) - 1] == '~') 3426 p_alt = NULL; 3427 else 3428 p_alt = utf16_to_enc(wfb.cAlternateFileName, NULL); 3429 3430 // Ignore entries starting with a dot, unless when asked for. Accept 3431 // all entries found with "matchname". 3432 if ((p[0] != '.' || starts_with_dot 3433 || ((flags & EW_DODOT) 3434 && p[1] != NUL && (p[1] != '.' || p[2] != NUL))) 3435 && (matchname == NULL 3436 || (regmatch.regprog != NULL 3437 && (vim_regexec(®match, p, (colnr_T)0) 3438 || (p_alt != NULL 3439 && vim_regexec(®match, p_alt, (colnr_T)0)))) 3440 || ((flags & EW_NOTWILD) 3441 && fnamencmp(path + (s - buf), p, e - s) == 0))) 3442 { 3443 STRCPY(s, p); 3444 len = (int)STRLEN(buf); 3445 3446 if (starstar && stardepth < 100 3447 && (wfb.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) 3448 { 3449 // For "**" in the pattern first go deeper in the tree to 3450 // find matches. 3451 STRCPY(buf + len, "/**"); 3452 STRCPY(buf + len + 3, path_end); 3453 ++stardepth; 3454 (void)dos_expandpath(gap, buf, len + 1, flags, TRUE); 3455 --stardepth; 3456 } 3457 3458 STRCPY(buf + len, path_end); 3459 if (mch_has_exp_wildcard(path_end)) 3460 { 3461 // need to expand another component of the path 3462 // remove backslashes for the remaining components only 3463 (void)dos_expandpath(gap, buf, len + 1, flags, FALSE); 3464 } 3465 else 3466 { 3467 // no more wildcards, check if there is a match 3468 // remove backslashes for the remaining components only 3469 if (*path_end != 0) 3470 backslash_halve(buf + len + 1); 3471 if (mch_getperm(buf) >= 0) // add existing file 3472 addfile(gap, buf, flags); 3473 } 3474 } 3475 3476 vim_free(p_alt); 3477 vim_free(p); 3478 ok = FindNextFileW(hFind, &wfb); 3479 } 3480 3481 FindClose(hFind); 3482 vim_free(wn); 3483 vim_free(buf); 3484 vim_regfree(regmatch.regprog); 3485 vim_free(matchname); 3486 3487 matches = gap->ga_len - start_len; 3488 if (matches > 0) 3489 qsort(((char_u **)gap->ga_data) + start_len, (size_t)matches, 3490 sizeof(char_u *), pstrcmp); 3491 return matches; 3492 } 3493 3494 int 3495 mch_expandpath( 3496 garray_T *gap, 3497 char_u *path, 3498 int flags) // EW_* flags 3499 { 3500 return dos_expandpath(gap, path, 0, flags, FALSE); 3501 } 3502 #endif // MSWIN 3503 3504 #if (defined(UNIX) && !defined(VMS)) || defined(USE_UNIXFILENAME) \ 3505 || defined(PROTO) 3506 /* 3507 * Unix style wildcard expansion code. 3508 * It's here because it's used both for Unix and Mac. 3509 */ 3510 static int 3511 pstrcmp(const void *a, const void *b) 3512 { 3513 return (pathcmp(*(char **)a, *(char **)b, -1)); 3514 } 3515 3516 /* 3517 * Recursively expand one path component into all matching files and/or 3518 * directories. Adds matches to "gap". Handles "*", "?", "[a-z]", "**", etc. 3519 * "path" has backslashes before chars that are not to be expanded, starting 3520 * at "path + wildoff". 3521 * Return the number of matches found. 3522 * NOTE: much of this is identical to dos_expandpath(), keep in sync! 3523 */ 3524 int 3525 unix_expandpath( 3526 garray_T *gap, 3527 char_u *path, 3528 int wildoff, 3529 int flags, // EW_* flags 3530 int didstar) // expanded "**" once already 3531 { 3532 char_u *buf; 3533 char_u *path_end; 3534 char_u *p, *s, *e; 3535 int start_len = gap->ga_len; 3536 char_u *pat; 3537 regmatch_T regmatch; 3538 int starts_with_dot; 3539 int matches; 3540 int len; 3541 int starstar = FALSE; 3542 static int stardepth = 0; // depth for "**" expansion 3543 3544 DIR *dirp; 3545 struct dirent *dp; 3546 3547 // Expanding "**" may take a long time, check for CTRL-C. 3548 if (stardepth > 0) 3549 { 3550 ui_breakcheck(); 3551 if (got_int) 3552 return 0; 3553 } 3554 3555 // make room for file name 3556 buf = alloc(STRLEN(path) + BASENAMELEN + 5); 3557 if (buf == NULL) 3558 return 0; 3559 3560 /* 3561 * Find the first part in the path name that contains a wildcard. 3562 * When EW_ICASE is set every letter is considered to be a wildcard. 3563 * Copy it into "buf", including the preceding characters. 3564 */ 3565 p = buf; 3566 s = buf; 3567 e = NULL; 3568 path_end = path; 3569 while (*path_end != NUL) 3570 { 3571 // May ignore a wildcard that has a backslash before it; it will 3572 // be removed by rem_backslash() or file_pat_to_reg_pat() below. 3573 if (path_end >= path + wildoff && rem_backslash(path_end)) 3574 *p++ = *path_end++; 3575 else if (*path_end == '/') 3576 { 3577 if (e != NULL) 3578 break; 3579 s = p + 1; 3580 } 3581 else if (path_end >= path + wildoff 3582 && (vim_strchr((char_u *)"*?[{~$", *path_end) != NULL 3583 || (!p_fic && (flags & EW_ICASE) 3584 && isalpha(PTR2CHAR(path_end))))) 3585 e = p; 3586 if (has_mbyte) 3587 { 3588 len = (*mb_ptr2len)(path_end); 3589 STRNCPY(p, path_end, len); 3590 p += len; 3591 path_end += len; 3592 } 3593 else 3594 *p++ = *path_end++; 3595 } 3596 e = p; 3597 *e = NUL; 3598 3599 // Now we have one wildcard component between "s" and "e". 3600 // Remove backslashes between "wildoff" and the start of the wildcard 3601 // component. 3602 for (p = buf + wildoff; p < s; ++p) 3603 if (rem_backslash(p)) 3604 { 3605 STRMOVE(p, p + 1); 3606 --e; 3607 --s; 3608 } 3609 3610 // Check for "**" between "s" and "e". 3611 for (p = s; p < e; ++p) 3612 if (p[0] == '*' && p[1] == '*') 3613 starstar = TRUE; 3614 3615 // convert the file pattern to a regexp pattern 3616 starts_with_dot = *s == '.'; 3617 pat = file_pat_to_reg_pat(s, e, NULL, FALSE); 3618 if (pat == NULL) 3619 { 3620 vim_free(buf); 3621 return 0; 3622 } 3623 3624 // compile the regexp into a program 3625 if (flags & EW_ICASE) 3626 regmatch.rm_ic = TRUE; // 'wildignorecase' set 3627 else 3628 regmatch.rm_ic = p_fic; // ignore case when 'fileignorecase' is set 3629 if (flags & (EW_NOERROR | EW_NOTWILD)) 3630 ++emsg_silent; 3631 regmatch.regprog = vim_regcomp(pat, RE_MAGIC); 3632 if (flags & (EW_NOERROR | EW_NOTWILD)) 3633 --emsg_silent; 3634 vim_free(pat); 3635 3636 if (regmatch.regprog == NULL && (flags & EW_NOTWILD) == 0) 3637 { 3638 vim_free(buf); 3639 return 0; 3640 } 3641 3642 // If "**" is by itself, this is the first time we encounter it and more 3643 // is following then find matches without any directory. 3644 if (!didstar && stardepth < 100 && starstar && e - s == 2 3645 && *path_end == '/') 3646 { 3647 STRCPY(s, path_end + 1); 3648 ++stardepth; 3649 (void)unix_expandpath(gap, buf, (int)(s - buf), flags, TRUE); 3650 --stardepth; 3651 } 3652 3653 // open the directory for scanning 3654 *s = NUL; 3655 dirp = opendir(*buf == NUL ? "." : (char *)buf); 3656 3657 // Find all matching entries 3658 if (dirp != NULL) 3659 { 3660 for (;;) 3661 { 3662 dp = readdir(dirp); 3663 if (dp == NULL) 3664 break; 3665 if ((dp->d_name[0] != '.' || starts_with_dot 3666 || ((flags & EW_DODOT) 3667 && dp->d_name[1] != NUL 3668 && (dp->d_name[1] != '.' || dp->d_name[2] != NUL))) 3669 && ((regmatch.regprog != NULL && vim_regexec(®match, 3670 (char_u *)dp->d_name, (colnr_T)0)) 3671 || ((flags & EW_NOTWILD) 3672 && fnamencmp(path + (s - buf), dp->d_name, e - s) == 0))) 3673 { 3674 STRCPY(s, dp->d_name); 3675 len = STRLEN(buf); 3676 3677 if (starstar && stardepth < 100) 3678 { 3679 // For "**" in the pattern first go deeper in the tree to 3680 // find matches. 3681 STRCPY(buf + len, "/**"); 3682 STRCPY(buf + len + 3, path_end); 3683 ++stardepth; 3684 (void)unix_expandpath(gap, buf, len + 1, flags, TRUE); 3685 --stardepth; 3686 } 3687 3688 STRCPY(buf + len, path_end); 3689 if (mch_has_exp_wildcard(path_end)) // handle more wildcards 3690 { 3691 // need to expand another component of the path 3692 // remove backslashes for the remaining components only 3693 (void)unix_expandpath(gap, buf, len + 1, flags, FALSE); 3694 } 3695 else 3696 { 3697 stat_T sb; 3698 3699 // no more wildcards, check if there is a match 3700 // remove backslashes for the remaining components only 3701 if (*path_end != NUL) 3702 backslash_halve(buf + len + 1); 3703 // add existing file or symbolic link 3704 if ((flags & EW_ALLLINKS) ? mch_lstat((char *)buf, &sb) >= 0 3705 : mch_getperm(buf) >= 0) 3706 { 3707 #ifdef MACOS_CONVERT 3708 size_t precomp_len = STRLEN(buf)+1; 3709 char_u *precomp_buf = 3710 mac_precompose_path(buf, precomp_len, &precomp_len); 3711 3712 if (precomp_buf) 3713 { 3714 mch_memmove(buf, precomp_buf, precomp_len); 3715 vim_free(precomp_buf); 3716 } 3717 #endif 3718 addfile(gap, buf, flags); 3719 } 3720 } 3721 } 3722 } 3723 3724 closedir(dirp); 3725 } 3726 3727 vim_free(buf); 3728 vim_regfree(regmatch.regprog); 3729 3730 matches = gap->ga_len - start_len; 3731 if (matches > 0) 3732 qsort(((char_u **)gap->ga_data) + start_len, matches, 3733 sizeof(char_u *), pstrcmp); 3734 return matches; 3735 } 3736 #endif 3737 3738 /* 3739 * Return TRUE if "p" contains what looks like an environment variable. 3740 * Allowing for escaping. 3741 */ 3742 static int 3743 has_env_var(char_u *p) 3744 { 3745 for ( ; *p; MB_PTR_ADV(p)) 3746 { 3747 if (*p == '\\' && p[1] != NUL) 3748 ++p; 3749 else if (vim_strchr((char_u *) 3750 #if defined(MSWIN) 3751 "$%" 3752 #else 3753 "$" 3754 #endif 3755 , *p) != NULL) 3756 return TRUE; 3757 } 3758 return FALSE; 3759 } 3760 3761 #ifdef SPECIAL_WILDCHAR 3762 /* 3763 * Return TRUE if "p" contains a special wildcard character, one that Vim 3764 * cannot expand, requires using a shell. 3765 */ 3766 static int 3767 has_special_wildchar(char_u *p) 3768 { 3769 for ( ; *p; MB_PTR_ADV(p)) 3770 { 3771 // Disallow line break characters. 3772 if (*p == '\r' || *p == '\n') 3773 break; 3774 // Allow for escaping. 3775 if (*p == '\\' && p[1] != NUL && p[1] != '\r' && p[1] != '\n') 3776 ++p; 3777 else if (vim_strchr((char_u *)SPECIAL_WILDCHAR, *p) != NULL) 3778 { 3779 // A { must be followed by a matching }. 3780 if (*p == '{' && vim_strchr(p, '}') == NULL) 3781 continue; 3782 // A quote and backtick must be followed by another one. 3783 if ((*p == '`' || *p == '\'') && vim_strchr(p, *p) == NULL) 3784 continue; 3785 return TRUE; 3786 } 3787 } 3788 return FALSE; 3789 } 3790 #endif 3791 3792 /* 3793 * Generic wildcard expansion code. 3794 * 3795 * Characters in "pat" that should not be expanded must be preceded with a 3796 * backslash. E.g., "/path\ with\ spaces/my\*star*" 3797 * 3798 * Return FAIL when no single file was found. In this case "num_file" is not 3799 * set, and "file" may contain an error message. 3800 * Return OK when some files found. "num_file" is set to the number of 3801 * matches, "file" to the array of matches. Call FreeWild() later. 3802 */ 3803 int 3804 gen_expand_wildcards( 3805 int num_pat, // number of input patterns 3806 char_u **pat, // array of input patterns 3807 int *num_file, // resulting number of files 3808 char_u ***file, // array of resulting files 3809 int flags) // EW_* flags 3810 { 3811 int i; 3812 garray_T ga; 3813 char_u *p; 3814 static int recursive = FALSE; 3815 int add_pat; 3816 int retval = OK; 3817 #if defined(FEAT_SEARCHPATH) 3818 int did_expand_in_path = FALSE; 3819 #endif 3820 3821 /* 3822 * expand_env() is called to expand things like "~user". If this fails, 3823 * it calls ExpandOne(), which brings us back here. In this case, always 3824 * call the machine specific expansion function, if possible. Otherwise, 3825 * return FAIL. 3826 */ 3827 if (recursive) 3828 #ifdef SPECIAL_WILDCHAR 3829 return mch_expand_wildcards(num_pat, pat, num_file, file, flags); 3830 #else 3831 return FAIL; 3832 #endif 3833 3834 #ifdef SPECIAL_WILDCHAR 3835 /* 3836 * If there are any special wildcard characters which we cannot handle 3837 * here, call machine specific function for all the expansion. This 3838 * avoids starting the shell for each argument separately. 3839 * For `=expr` do use the internal function. 3840 */ 3841 for (i = 0; i < num_pat; i++) 3842 { 3843 if (has_special_wildchar(pat[i]) 3844 # ifdef VIM_BACKTICK 3845 && !(vim_backtick(pat[i]) && pat[i][1] == '=') 3846 # endif 3847 ) 3848 return mch_expand_wildcards(num_pat, pat, num_file, file, flags); 3849 } 3850 #endif 3851 3852 recursive = TRUE; 3853 3854 /* 3855 * The matching file names are stored in a growarray. Init it empty. 3856 */ 3857 ga_init2(&ga, (int)sizeof(char_u *), 30); 3858 3859 for (i = 0; i < num_pat; ++i) 3860 { 3861 add_pat = -1; 3862 p = pat[i]; 3863 3864 #ifdef VIM_BACKTICK 3865 if (vim_backtick(p)) 3866 { 3867 add_pat = expand_backtick(&ga, p, flags); 3868 if (add_pat == -1) 3869 retval = FAIL; 3870 } 3871 else 3872 #endif 3873 { 3874 /* 3875 * First expand environment variables, "~/" and "~user/". 3876 */ 3877 if ((has_env_var(p) && !(flags & EW_NOTENV)) || *p == '~') 3878 { 3879 p = expand_env_save_opt(p, TRUE); 3880 if (p == NULL) 3881 p = pat[i]; 3882 #ifdef UNIX 3883 /* 3884 * On Unix, if expand_env() can't expand an environment 3885 * variable, use the shell to do that. Discard previously 3886 * found file names and start all over again. 3887 */ 3888 else if (has_env_var(p) || *p == '~') 3889 { 3890 vim_free(p); 3891 ga_clear_strings(&ga); 3892 i = mch_expand_wildcards(num_pat, pat, num_file, file, 3893 flags|EW_KEEPDOLLAR); 3894 recursive = FALSE; 3895 return i; 3896 } 3897 #endif 3898 } 3899 3900 /* 3901 * If there are wildcards: Expand file names and add each match to 3902 * the list. If there is no match, and EW_NOTFOUND is given, add 3903 * the pattern. 3904 * If there are no wildcards: Add the file name if it exists or 3905 * when EW_NOTFOUND is given. 3906 */ 3907 if (mch_has_exp_wildcard(p)) 3908 { 3909 #if defined(FEAT_SEARCHPATH) 3910 if ((flags & EW_PATH) 3911 && !mch_isFullName(p) 3912 && !(p[0] == '.' 3913 && (vim_ispathsep(p[1]) 3914 || (p[1] == '.' && vim_ispathsep(p[2])))) 3915 ) 3916 { 3917 // :find completion where 'path' is used. 3918 // Recursiveness is OK here. 3919 recursive = FALSE; 3920 add_pat = expand_in_path(&ga, p, flags); 3921 recursive = TRUE; 3922 did_expand_in_path = TRUE; 3923 } 3924 else 3925 #endif 3926 add_pat = mch_expandpath(&ga, p, flags); 3927 } 3928 } 3929 3930 if (add_pat == -1 || (add_pat == 0 && (flags & EW_NOTFOUND))) 3931 { 3932 char_u *t = backslash_halve_save(p); 3933 3934 // When EW_NOTFOUND is used, always add files and dirs. Makes 3935 // "vim c:/" work. 3936 if (flags & EW_NOTFOUND) 3937 addfile(&ga, t, flags | EW_DIR | EW_FILE); 3938 else 3939 addfile(&ga, t, flags); 3940 3941 if (t != p) 3942 vim_free(t); 3943 } 3944 3945 #if defined(FEAT_SEARCHPATH) 3946 if (did_expand_in_path && ga.ga_len > 0 && (flags & EW_PATH)) 3947 uniquefy_paths(&ga, p); 3948 #endif 3949 if (p != pat[i]) 3950 vim_free(p); 3951 } 3952 3953 // When returning FAIL the array must be freed here. 3954 if (retval == FAIL) 3955 ga_clear(&ga); 3956 3957 *num_file = ga.ga_len; 3958 *file = (ga.ga_data != NULL) ? (char_u **)ga.ga_data 3959 : (char_u **)_("no matches"); 3960 3961 recursive = FALSE; 3962 3963 return ((flags & EW_EMPTYOK) || ga.ga_data != NULL) ? retval : FAIL; 3964 } 3965 3966 /* 3967 * Add a file to a file list. Accepted flags: 3968 * EW_DIR add directories 3969 * EW_FILE add files 3970 * EW_EXEC add executable files 3971 * EW_NOTFOUND add even when it doesn't exist 3972 * EW_ADDSLASH add slash after directory name 3973 * EW_ALLLINKS add symlink also when the referred file does not exist 3974 */ 3975 void 3976 addfile( 3977 garray_T *gap, 3978 char_u *f, // filename 3979 int flags) 3980 { 3981 char_u *p; 3982 int isdir; 3983 stat_T sb; 3984 3985 // if the file/dir/link doesn't exist, may not add it 3986 if (!(flags & EW_NOTFOUND) && ((flags & EW_ALLLINKS) 3987 ? mch_lstat((char *)f, &sb) < 0 : mch_getperm(f) < 0)) 3988 return; 3989 3990 #ifdef FNAME_ILLEGAL 3991 // if the file/dir contains illegal characters, don't add it 3992 if (vim_strpbrk(f, (char_u *)FNAME_ILLEGAL) != NULL) 3993 return; 3994 #endif 3995 3996 isdir = mch_isdir(f); 3997 if ((isdir && !(flags & EW_DIR)) || (!isdir && !(flags & EW_FILE))) 3998 return; 3999 4000 // If the file isn't executable, may not add it. Do accept directories. 4001 // When invoked from expand_shellcmd() do not use $PATH. 4002 if (!isdir && (flags & EW_EXEC) 4003 && !mch_can_exe(f, NULL, !(flags & EW_SHELLCMD))) 4004 return; 4005 4006 // Make room for another item in the file list. 4007 if (ga_grow(gap, 1) == FAIL) 4008 return; 4009 4010 p = alloc(STRLEN(f) + 1 + isdir); 4011 if (p == NULL) 4012 return; 4013 4014 STRCPY(p, f); 4015 #ifdef BACKSLASH_IN_FILENAME 4016 slash_adjust(p); 4017 #endif 4018 /* 4019 * Append a slash or backslash after directory names if none is present. 4020 */ 4021 #ifndef DONT_ADD_PATHSEP_TO_DIR 4022 if (isdir && (flags & EW_ADDSLASH)) 4023 add_pathsep(p); 4024 #endif 4025 ((char_u **)gap->ga_data)[gap->ga_len++] = p; 4026 } 4027 4028 /* 4029 * Free the list of files returned by expand_wildcards() or other expansion 4030 * functions. 4031 */ 4032 void 4033 FreeWild(int count, char_u **files) 4034 { 4035 if (count <= 0 || files == NULL) 4036 return; 4037 while (count--) 4038 vim_free(files[count]); 4039 vim_free(files); 4040 } 4041 4042 /* 4043 * Compare path "p[]" to "q[]". 4044 * If "maxlen" >= 0 compare "p[maxlen]" to "q[maxlen]" 4045 * Return value like strcmp(p, q), but consider path separators. 4046 */ 4047 int 4048 pathcmp(const char *p, const char *q, int maxlen) 4049 { 4050 int i, j; 4051 int c1, c2; 4052 const char *s = NULL; 4053 4054 for (i = 0, j = 0; maxlen < 0 || (i < maxlen && j < maxlen);) 4055 { 4056 c1 = PTR2CHAR((char_u *)p + i); 4057 c2 = PTR2CHAR((char_u *)q + j); 4058 4059 // End of "p": check if "q" also ends or just has a slash. 4060 if (c1 == NUL) 4061 { 4062 if (c2 == NUL) // full match 4063 return 0; 4064 s = q; 4065 i = j; 4066 break; 4067 } 4068 4069 // End of "q": check if "p" just has a slash. 4070 if (c2 == NUL) 4071 { 4072 s = p; 4073 break; 4074 } 4075 4076 if ((p_fic ? MB_TOUPPER(c1) != MB_TOUPPER(c2) : c1 != c2) 4077 #ifdef BACKSLASH_IN_FILENAME 4078 // consider '/' and '\\' to be equal 4079 && !((c1 == '/' && c2 == '\\') 4080 || (c1 == '\\' && c2 == '/')) 4081 #endif 4082 ) 4083 { 4084 if (vim_ispathsep(c1)) 4085 return -1; 4086 if (vim_ispathsep(c2)) 4087 return 1; 4088 return p_fic ? MB_TOUPPER(c1) - MB_TOUPPER(c2) 4089 : c1 - c2; // no match 4090 } 4091 4092 i += mb_ptr2len((char_u *)p + i); 4093 j += mb_ptr2len((char_u *)q + j); 4094 } 4095 if (s == NULL) // "i" or "j" ran into "maxlen" 4096 return 0; 4097 4098 c1 = PTR2CHAR((char_u *)s + i); 4099 c2 = PTR2CHAR((char_u *)s + i + mb_ptr2len((char_u *)s + i)); 4100 // ignore a trailing slash, but not "//" or ":/" 4101 if (c2 == NUL 4102 && i > 0 4103 && !after_pathsep((char_u *)s, (char_u *)s + i) 4104 #ifdef BACKSLASH_IN_FILENAME 4105 && (c1 == '/' || c1 == '\\') 4106 #else 4107 && c1 == '/' 4108 #endif 4109 ) 4110 return 0; // match with trailing slash 4111 if (s == q) 4112 return -1; // no match 4113 return 1; 4114 } 4115 4116 /* 4117 * Return TRUE if "name" is a full (absolute) path name or URL. 4118 */ 4119 int 4120 vim_isAbsName(char_u *name) 4121 { 4122 return (path_with_url(name) != 0 || mch_isFullName(name)); 4123 } 4124 4125 /* 4126 * Get absolute file name into buffer "buf[len]". 4127 * 4128 * return FAIL for failure, OK otherwise 4129 */ 4130 int 4131 vim_FullName( 4132 char_u *fname, 4133 char_u *buf, 4134 int len, 4135 int force) // force expansion even when already absolute 4136 { 4137 int retval = OK; 4138 int url; 4139 4140 *buf = NUL; 4141 if (fname == NULL) 4142 return FAIL; 4143 4144 url = path_with_url(fname); 4145 if (!url) 4146 retval = mch_FullName(fname, buf, len, force); 4147 if (url || retval == FAIL) 4148 { 4149 // something failed; use the file name (truncate when too long) 4150 vim_strncpy(buf, fname, len - 1); 4151 } 4152 #if defined(MSWIN) 4153 slash_adjust(buf); 4154 #endif 4155 return retval; 4156 } 4157