neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

path.c (72880B)


      1 #include <assert.h>
      2 #include <limits.h>
      3 #include <stdbool.h>
      4 #include <stddef.h>
      5 #include <stdint.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 #ifdef MSWIN
      9 # include <direct.h>
     10 #endif
     11 
     12 #include "auto/config.h"
     13 #include "nvim/ascii_defs.h"
     14 #include "nvim/buffer_defs.h"
     15 #include "nvim/charset.h"
     16 #include "nvim/cmdexpand.h"
     17 #include "nvim/eval.h"
     18 #include "nvim/ex_docmd.h"
     19 #include "nvim/fileio.h"
     20 #include "nvim/garray.h"
     21 #include "nvim/globals.h"
     22 #include "nvim/macros_defs.h"
     23 #include "nvim/mbyte.h"
     24 #include "nvim/memory.h"
     25 #include "nvim/option.h"
     26 #include "nvim/option_vars.h"
     27 #include "nvim/os/fs.h"
     28 #include "nvim/os/fs_defs.h"
     29 #include "nvim/os/input.h"
     30 #include "nvim/os/os.h"
     31 #include "nvim/os/os_defs.h"
     32 #include "nvim/os/shell.h"
     33 #include "nvim/path.h"
     34 #include "nvim/regexp.h"
     35 #include "nvim/regexp_defs.h"
     36 #include "nvim/strings.h"
     37 #include "nvim/vim_defs.h"
     38 
     39 enum {
     40  URL_SLASH = 1,      // path_is_url() has found ":/"
     41  URL_BACKSLASH = 2,  // path_is_url() has found ":\\"
     42 };
     43 
     44 #ifdef gen_expand_wildcards
     45 # undef gen_expand_wildcards
     46 #endif
     47 
     48 #include "path.c.generated.h"
     49 
     50 /// Compare two file names.
     51 ///
     52 /// @param s1 First file name. Environment variables in this name will be expanded.
     53 /// @param s2 Second file name.
     54 /// @param checkname When both files don't exist, only compare their names.
     55 /// @param expandenv Whether to expand environment variables in file names.
     56 /// @return Enum of type FileComparison. @see FileComparison.
     57 FileComparison path_full_compare(char *const s1, char *const s2, const bool checkname,
     58                                 const bool expandenv)
     59  FUNC_ATTR_NONNULL_ALL
     60 {
     61  char expanded1[MAXPATHL];
     62  char full1[MAXPATHL];
     63  char full2[MAXPATHL];
     64  FileID file_id_1, file_id_2;
     65 
     66  if (expandenv) {
     67    expand_env(s1, expanded1, MAXPATHL);
     68  } else {
     69    xstrlcpy(expanded1, s1, MAXPATHL);
     70  }
     71  bool id_ok_1 = os_fileid(expanded1, &file_id_1);
     72  bool id_ok_2 = os_fileid(s2, &file_id_2);
     73  if (!id_ok_1 && !id_ok_2) {
     74    // If os_fileid() doesn't work, may compare the names.
     75    if (checkname) {
     76      vim_FullName(expanded1, full1, MAXPATHL, false);
     77      vim_FullName(s2, full2, MAXPATHL, false);
     78      if (path_fnamecmp(full1, full2) == 0) {
     79        return kEqualFileNames;
     80      }
     81    }
     82    return kBothFilesMissing;
     83  }
     84  if (!id_ok_1 || !id_ok_2) {
     85    return kOneFileMissing;
     86  }
     87  if (os_fileid_equal(&file_id_1, &file_id_2)) {
     88    return kEqualFiles;
     89  }
     90  return kDifferentFiles;
     91 }
     92 
     93 /// Gets the tail (filename segment) of path `fname`.
     94 ///
     95 /// Examples:
     96 /// - "dir/file.txt" => "file.txt"
     97 /// - "file.txt" => "file.txt"
     98 /// - "dir/" => ""
     99 ///
    100 /// @return pointer just past the last path separator (empty string, if fname
    101 ///         ends in a slash), or empty string if fname is NULL.
    102 char *path_tail(const char *fname)
    103  FUNC_ATTR_NONNULL_RET
    104 {
    105  if (fname == NULL) {
    106    return "";
    107  }
    108 
    109  const char *tail = get_past_head(fname);
    110  const char *p = tail;
    111  // Find last part of path.
    112  while (*p != NUL) {
    113    if (vim_ispathsep_nocolon(*p)) {
    114      tail = p + 1;
    115    }
    116    MB_PTR_ADV(p);
    117  }
    118  return (char *)tail;
    119 }
    120 
    121 /// Get pointer to tail of "fname", including path separators.
    122 ///
    123 /// Takes care of "c:/" and "//". That means `path_tail_with_sep("dir///file.txt")`
    124 /// will return a pointer to `"///file.txt"`.
    125 /// @param fname A file path. (Must be != NULL.)
    126 /// @return
    127 ///   - Pointer to the last path separator of `fname`, if there is any.
    128 ///   - `fname` if it contains no path separator.
    129 ///   - Never NULL.
    130 char *path_tail_with_sep(char *fname)
    131  FUNC_ATTR_NONNULL_ALL
    132 {
    133  // Don't remove the '/' from "c:/file".
    134  char *past_head = get_past_head(fname);
    135  char *tail = path_tail(fname);
    136  while (tail > past_head && after_pathsep(fname, tail)) {
    137    tail--;
    138  }
    139  return tail;
    140 }
    141 
    142 /// Finds the path tail (or executable) in an invocation.
    143 ///
    144 /// @param[in]  invocation A program invocation in the form:
    145 ///                        "path/to/exe [args]".
    146 /// @param[out] len Stores the length of the executable name.
    147 ///
    148 /// @post if `len` is not null, stores the length of the executable name.
    149 ///
    150 /// @return The position of the last path separator + 1.
    151 const char *invocation_path_tail(const char *invocation, size_t *len)
    152  FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ARG(1)
    153 {
    154  const char *tail = get_past_head(invocation);
    155  const char *p = tail;
    156  while (*p != NUL && *p != ' ') {
    157    bool was_sep = vim_ispathsep_nocolon(*p);
    158    MB_PTR_ADV(p);
    159    if (was_sep) {
    160      tail = p;  // Now tail points one past the separator.
    161    }
    162  }
    163 
    164  if (len != NULL) {
    165    *len = (size_t)(p - tail);
    166  }
    167 
    168  return tail;
    169 }
    170 
    171 /// Get the next path component of a path name.
    172 ///
    173 /// @param fname A file path. (Must be != NULL.)
    174 /// @return Pointer to first found path separator + 1.
    175 /// An empty string, if `fname` doesn't contain a path separator,
    176 const char *path_next_component(const char *fname)
    177  FUNC_ATTR_NONNULL_ALL
    178 {
    179  while (*fname != NUL && !vim_ispathsep(*fname)) {
    180    MB_PTR_ADV(fname);
    181  }
    182  if (*fname != NUL) {
    183    fname++;
    184  }
    185  return fname;
    186 }
    187 
    188 /// Returns the length of the path head on the current platform.
    189 /// @return
    190 ///   - 3 on windows
    191 ///   - 1 otherwise
    192 int path_head_length(void)
    193 {
    194 #ifdef MSWIN
    195  return 3;
    196 #else
    197  return 1;
    198 #endif
    199 }
    200 
    201 /// Returns true if path begins with characters denoting the head of a path
    202 /// (e.g. '/' on linux and 'D:' on windows).
    203 /// @param path The path to be checked.
    204 /// @return
    205 ///   - True if path begins with a path head
    206 ///   - False otherwise
    207 bool is_path_head(const char *path)
    208  FUNC_ATTR_NONNULL_ALL
    209 {
    210 #ifdef MSWIN
    211  return isalpha((uint8_t)path[0]) && path[1] == ':';
    212 #else
    213  return vim_ispathsep(*path);
    214 #endif
    215 }
    216 
    217 /// Get a pointer to one character past the head of a path name.
    218 /// Unix: after "/"; Win: after "c:\"
    219 /// If there is no head, path is returned.
    220 char *get_past_head(const char *path)
    221  FUNC_ATTR_NONNULL_ALL
    222 {
    223  const char *retval = path;
    224 
    225 #ifdef MSWIN
    226  // May skip "c:"
    227  if (is_path_head(path)) {
    228    retval = path + 2;
    229  }
    230 #endif
    231 
    232  while (vim_ispathsep(*retval)) {
    233    retval++;
    234  }
    235 
    236  return (char *)retval;
    237 }
    238 
    239 /// @return true if 'c' is a path separator.
    240 /// Note that for MS-Windows this includes the colon.
    241 bool vim_ispathsep(int c)
    242 {
    243 #ifdef UNIX
    244  return c == '/';          // Unix has ':' inside file names
    245 #else
    246 # ifdef BACKSLASH_IN_FILENAME
    247  return c == ':' || c == '/' || c == '\\';
    248 # else
    249  return c == ':' || c == '/';
    250 # endif
    251 #endif
    252 }
    253 
    254 // Like vim_ispathsep(c), but exclude the colon for MS-Windows.
    255 bool vim_ispathsep_nocolon(int c)
    256 {
    257  return vim_ispathsep(c)
    258 #ifdef BACKSLASH_IN_FILENAME
    259         && c != ':'
    260 #endif
    261  ;
    262 }
    263 
    264 /// @return true if 'c' is a path list separator.
    265 bool vim_ispathlistsep(int c)
    266 {
    267 #ifdef UNIX
    268  return c == ':';
    269 #else
    270  return c == ';';      // might not be right for every system...
    271 #endif
    272 }
    273 
    274 /// Shorten the path of a file from "~/foo/../.bar/fname" to "~/f/../.b/fname"
    275 /// "trim_len" specifies how many characters to keep for each directory.
    276 /// Must be 1 or more.
    277 /// It's done in-place.
    278 void shorten_dir_len(char *str, int trim_len)
    279  FUNC_ATTR_NONNULL_ALL
    280 {
    281  char *tail = path_tail(str);
    282  char *d = str;
    283  bool skip = false;
    284  int dirchunk_len = 0;
    285  for (char *s = str;; s++) {
    286    if (s >= tail) {                // copy the whole tail
    287      *d++ = *s;
    288      if (*s == NUL) {
    289        break;
    290      }
    291    } else if (vim_ispathsep(*s)) {       // copy '/' and next char
    292      *d++ = *s;
    293      skip = false;
    294      dirchunk_len = 0;
    295    } else if (!skip) {
    296      *d++ = *s;                     // copy next char
    297      if (*s != '~' && *s != '.') {  // and leading "~" and "."
    298        dirchunk_len++;  // only count word chars for the size
    299        // keep copying chars until we have our preferred length (or
    300        // until the above if/else branches move us along)
    301        if (dirchunk_len >= trim_len) {
    302          skip = true;
    303        }
    304      }
    305      int l = utfc_ptr2len(s);
    306      while (--l > 0) {
    307        *d++ = *++s;
    308      }
    309    }
    310  }
    311 }
    312 
    313 /// Shorten the path of a file from "~/foo/../.bar/fname" to "~/f/../.b/fname"
    314 /// It's done in-place.
    315 void shorten_dir(char *str)
    316  FUNC_ATTR_NONNULL_ALL
    317 {
    318  shorten_dir_len(str, 1);
    319 }
    320 
    321 /// Return true if the directory of "fname" exists, false otherwise.
    322 /// Also returns true if there is no directory name.
    323 /// "fname" must be writable!.
    324 bool dir_of_file_exists(char *fname)
    325  FUNC_ATTR_NONNULL_ALL
    326 {
    327  char *p = path_tail_with_sep(fname);
    328  if (p == fname) {
    329    return true;
    330  }
    331  char c = *p;
    332  *p = NUL;
    333  bool retval = os_isdir(fname);
    334  *p = c;
    335  return retval;
    336 }
    337 
    338 /// Compare two file names
    339 ///
    340 /// On some systems case in a file name does not matter, on others it does.
    341 ///
    342 /// @note Does not account for maximum name lengths and things like "../dir",
    343 ///       thus it is not 100% accurate. OS may also use different algorithm for
    344 ///       case-insensitive comparison.
    345 ///
    346 /// Handles '/' and '\\' correctly and deals with &fileignorecase option.
    347 ///
    348 /// @param[in]  fname1  First file name.
    349 /// @param[in]  fname2  Second file name.
    350 ///
    351 /// @return 0 if they are equal, non-zero otherwise.
    352 int path_fnamecmp(const char *fname1, const char *fname2)
    353  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
    354 {
    355 #ifdef BACKSLASH_IN_FILENAME
    356  const size_t len1 = strlen(fname1);
    357  const size_t len2 = strlen(fname2);
    358  return path_fnamencmp(fname1, fname2, MAX(len1, len2));
    359 #else
    360  return mb_strcmp_ic((bool)p_fic, fname1, fname2);
    361 #endif
    362 }
    363 
    364 /// Compare two file names
    365 ///
    366 /// Handles '/' and '\\' correctly and deals with &fileignorecase option.
    367 ///
    368 /// @param[in]  fname1  First file name.
    369 /// @param[in]  fname2  Second file name.
    370 /// @param[in]  len  Compare at most len bytes.
    371 ///
    372 /// @return 0 if they are equal, non-zero otherwise.
    373 int path_fnamencmp(const char *const fname1, const char *const fname2, size_t len)
    374  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
    375 {
    376 #ifdef BACKSLASH_IN_FILENAME
    377  int c1 = NUL;
    378  int c2 = NUL;
    379 
    380  const char *p1 = fname1;
    381  const char *p2 = fname2;
    382 
    383 # ifdef MSWIN
    384  // To allow proper comparison of absolute paths:
    385  //   - one with explicit drive letter C:\xxx
    386  //   - another with implicit drive letter \xxx
    387  // advance the pointer, of the explicit one, to skip the drive
    388  for (int swap = 0, drive = NUL; swap < 2; swap++) {
    389    // Handle absolute paths with implicit drive letter
    390    c1 = utf_ptr2char(p1);
    391    c2 = utf_ptr2char(p2);
    392 
    393    if ((c1 == '/' || c1 == '\\') && ASCII_ISALPHA(c2)) {
    394      drive = mb_toupper(c2) - 'A' + 1;
    395 
    396      // Check for the colon
    397      p2 += utfc_ptr2len(p2);
    398      c2 = utf_ptr2char(p2);
    399      if (c2 == ':' && drive == _getdrive()) {  // skip the drive for comparison
    400        p2 += utfc_ptr2len(p2);
    401        break;
    402      } else {  // ignore
    403        p2 -= utfc_ptr2len(p2);
    404      }
    405    }
    406 
    407    // swap pointers
    408    const char *tmp = p1;
    409    p1 = p2;
    410    p2 = tmp;
    411  }
    412 # endif
    413 
    414  while (len > 0) {
    415    c1 = utf_ptr2char(p1);
    416    c2 = utf_ptr2char(p2);
    417    if ((c1 == NUL || c2 == NUL
    418         || (!((c1 == '/' || c1 == '\\') && (c2 == '\\' || c2 == '/'))))
    419        && (p_fic ? (c1 != c2 && utf_fold(c1) != utf_fold(c2)) : c1 != c2)) {
    420      break;
    421    }
    422    len -= (size_t)utfc_ptr2len(p1);
    423    p1 += utfc_ptr2len(p1);
    424    p2 += utfc_ptr2len(p2);
    425  }
    426  return p_fic ? utf_fold(c1) - utf_fold(c2) : c1 - c2;
    427 #else
    428  if (p_fic) {
    429    return mb_strnicmp(fname1, fname2, len);
    430  }
    431  return strncmp(fname1, fname2, len);
    432 #endif
    433 }
    434 
    435 /// Append fname2 to fname1
    436 ///
    437 /// @param[in]  fname1  First fname to append to.
    438 /// @param[in]  len1    Length of fname1.
    439 /// @param[in]  fname2  Second part of the file name.
    440 /// @param[in]  len2    Length of fname2.
    441 /// @param[in]  sep     If true and fname1 does not end with a path separator,
    442 ///                     add a path separator before fname2.
    443 ///
    444 /// @return fname1
    445 static inline char *do_concat_fnames(char *fname1, const size_t len1, const char *fname2,
    446                                     const size_t len2, const bool sep)
    447  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
    448 {
    449  if (sep && *fname1 && !after_pathsep(fname1, fname1 + len1)) {
    450    fname1[len1] = PATHSEP;
    451    memmove(fname1 + len1 + 1, fname2, len2 + 1);
    452  } else {
    453    memmove(fname1 + len1, fname2, len2 + 1);
    454  }
    455 
    456  return fname1;
    457 }
    458 
    459 /// Concatenate file names fname1 and fname2 into allocated memory.
    460 ///
    461 /// Only add a '/' or '\\' when 'sep' is true and it is necessary.
    462 ///
    463 /// @param fname1 is the first part of the path or filename
    464 /// @param fname2 is the second half of the path or filename
    465 /// @param sep    is a flag to indicate a path separator should be added
    466 ///               if necessary
    467 /// @return [allocated] Concatenation of fname1 and fname2.
    468 char *concat_fnames(const char *fname1, const char *fname2, bool sep)
    469  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
    470 {
    471  const size_t len1 = strlen(fname1);
    472  const size_t len2 = strlen(fname2);
    473  char *dest = xmalloc(len1 + len2 + 3);
    474  memmove(dest, fname1, len1 + 1);
    475  return do_concat_fnames(dest, len1, fname2, len2, sep);
    476 }
    477 
    478 /// Concatenate file names fname1 and fname2
    479 ///
    480 /// Like concat_fnames(), but in place of allocating new memory it reallocates
    481 /// fname1. For this reason fname1 must be allocated with xmalloc, and can no
    482 /// longer be used after running concat_fnames_realloc.
    483 ///
    484 /// @param fname1 is the first part of the path or filename
    485 /// @param fname2 is the second half of the path or filename
    486 /// @param sep    is a flag to indicate a path separator should be added
    487 ///               if necessary
    488 /// @return [allocated] Concatenation of fname1 and fname2.
    489 char *concat_fnames_realloc(char *fname1, const char *fname2, bool sep)
    490  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
    491 {
    492  const size_t len1 = strlen(fname1);
    493  const size_t len2 = strlen(fname2);
    494  return do_concat_fnames(xrealloc(fname1, len1 + len2 + 3), len1,
    495                          fname2, len2, sep);
    496 }
    497 
    498 /// Adds a path separator to a filename, unless it already ends in one.
    499 ///
    500 /// @return `true` if the path separator was added or already existed.
    501 ///         `false` if the filename is too long.
    502 bool add_pathsep(char *p)
    503  FUNC_ATTR_NONNULL_ALL
    504 {
    505  const size_t len = strlen(p);
    506  if (*p != NUL && !after_pathsep(p, p + len)) {
    507    const size_t pathsep_len = sizeof(PATHSEPSTR);
    508    if (len > MAXPATHL - pathsep_len) {
    509      return false;
    510    }
    511    memcpy(p + len, PATHSEPSTR, pathsep_len);
    512  }
    513  return true;
    514 }
    515 
    516 /// Get an allocated copy of the full path to a file.
    517 ///
    518 /// @param fname is the filename to save
    519 /// @param force is a flag to expand `fname` even if it looks absolute
    520 ///
    521 /// @return [allocated] Copy of absolute path to `fname` or NULL when
    522 ///                     `fname` is NULL.
    523 char *FullName_save(const char *fname, bool force)
    524  FUNC_ATTR_MALLOC
    525 {
    526  if (fname == NULL) {
    527    return NULL;
    528  }
    529 
    530  char *buf = xmalloc(MAXPATHL);
    531  if (vim_FullName(fname, buf, MAXPATHL, force) == FAIL) {
    532    xfree(buf);
    533    return xstrdup(fname);
    534  }
    535  return buf;
    536 }
    537 
    538 /// Saves the absolute path.
    539 /// @param name An absolute or relative path.
    540 /// @return The absolute path of `name`.
    541 char *save_abs_path(const char *name)
    542  FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
    543 {
    544  if (!path_is_absolute(name)) {
    545    return FullName_save(name, true);
    546  }
    547  return xstrdup(name);
    548 }
    549 
    550 /// Checks if a path has a wildcard character including '~', unless at the end.
    551 /// @param p  The path to expand.
    552 /// @returns Unix: True if it contains one of "?[{`'$".
    553 /// @returns Windows: True if it contains one of "*?$[".
    554 bool path_has_wildcard(const char *p)
    555  FUNC_ATTR_NONNULL_ALL
    556 {
    557  for (; *p; MB_PTR_ADV(p)) {
    558 #if defined(UNIX)
    559    if (p[0] == '\\' && p[1] != NUL) {
    560      p++;
    561      continue;
    562    }
    563 
    564    const char *wildcards = "*?[{`'$";
    565 #else
    566    // Windows:
    567    const char *wildcards = "?*$[`";
    568 #endif
    569    if (vim_strchr(wildcards, (uint8_t)(*p)) != NULL
    570        || (p[0] == '~' && p[1] != NUL)) {
    571      return true;
    572    }
    573  }
    574  return false;
    575 }
    576 
    577 static int pstrcmp(const void *a, const void *b)
    578 {
    579  return pathcmp(*(char **)a, *(char **)b, -1);
    580 }
    581 
    582 /// Checks if a path has a character path_expand can expand.
    583 /// @param p  The path to expand.
    584 /// @returns Unix: True if it contains one of *?[{.
    585 /// @returns Windows: True if it contains one of *?[.
    586 bool path_has_exp_wildcard(const char *p)
    587  FUNC_ATTR_NONNULL_ALL
    588 {
    589  for (; *p != NUL; MB_PTR_ADV(p)) {
    590 #if defined(UNIX)
    591    if (p[0] == '\\' && p[1] != NUL) {
    592      p++;
    593      continue;
    594    }
    595 
    596    const char *wildcards = "*?[{";
    597 #else
    598    const char *wildcards = "*?[";  // Windows.
    599 #endif
    600    if (vim_strchr(wildcards, (uint8_t)(*p)) != NULL) {
    601      return true;
    602    }
    603  }
    604  return false;
    605 }
    606 
    607 /// Recursively expands one path component into all matching files and/or
    608 /// directories. Handles "*", "?", "[a-z]", "**", etc.
    609 /// @remark "**" in `path` requests recursive expansion.
    610 ///
    611 /// @param[out] gap  The matches found.
    612 /// @param path     The path to search.
    613 /// @param flags    Flags for regexp expansion.
    614 ///   - EW_ICASE: Ignore case.
    615 ///   - EW_NOERROR: Silence error messages.
    616 ///   - EW_NOTWILD: Add matches literally.
    617 /// @returns the number of matches found.
    618 static size_t path_expand(garray_T *gap, const char *path, int flags)
    619  FUNC_ATTR_NONNULL_ALL
    620 {
    621  return do_path_expand(gap, path, 0, flags, false);
    622 }
    623 
    624 static const char *scandir_next_with_dots(Directory *dir)
    625 {
    626  static int count = 0;
    627  if (dir == NULL) {  // initialize
    628    count = 0;
    629    return NULL;
    630  }
    631 
    632  count += 1;
    633  if (count == 1 || count == 2) {
    634    return (count == 1) ? "." : "..";
    635  }
    636  return os_scandir_next(dir);
    637 }
    638 
    639 /// Implementation of path_expand().
    640 ///
    641 /// Chars before `path + wildoff` do not get expanded.
    642 static size_t do_path_expand(garray_T *gap, const char *path, size_t wildoff, int flags,
    643                             bool didstar)
    644  FUNC_ATTR_NONNULL_ALL
    645 {
    646  int start_len = gap->ga_len;
    647  bool starstar = false;
    648  static int stardepth = 0;  // depth for "**" expansion
    649 
    650  // Expanding "**" may take a long time, check for CTRL-C.
    651  if (stardepth > 0 && !(flags & EW_NOBREAK)) {
    652    os_breakcheck();
    653    if (got_int) {
    654      return 0;
    655    }
    656  }
    657 
    658  // Make room for file name (a bit too much to stay on the safe side).
    659  const size_t buflen = strlen(path) + MAXPATHL;
    660  char *buf = xmalloc(buflen);
    661 
    662  // Find the first part in the path name that contains a wildcard.
    663  // When EW_ICASE is set every letter is considered to be a wildcard.
    664  // Copy it into "buf", including the preceding characters.
    665  char *p = buf;
    666  char *s = buf;
    667  char *e = NULL;
    668  const char *path_end = path;
    669  while (*path_end != NUL) {
    670    // May ignore a wildcard that has a backslash before it; it will
    671    // be removed by rem_backslash() or file_pat_to_reg_pat() below.
    672    if (path_end >= path + wildoff && rem_backslash(path_end)) {
    673      *p++ = *path_end++;
    674    } else if (vim_ispathsep_nocolon(*path_end)) {
    675      if (e != NULL) {
    676        break;
    677      }
    678      s = p + 1;
    679    } else if (path_end >= path + wildoff
    680 #ifdef MSWIN
    681               && vim_strchr("*?[~", (uint8_t)(*path_end)) != NULL
    682 #else
    683               && (vim_strchr("*?[{~$", (uint8_t)(*path_end)) != NULL
    684                   || (!p_fic && (flags & EW_ICASE) && mb_isalpha(utf_ptr2char(path_end))))
    685 #endif
    686               ) {
    687      e = p;
    688    }
    689    int charlen = utfc_ptr2len(path_end);
    690    memcpy(p, path_end, (size_t)charlen);
    691    p += charlen;
    692    path_end += charlen;
    693  }
    694  e = p;
    695  *e = NUL;
    696 
    697  // Now we have one wildcard component between "s" and "e".
    698  // Remove backslashes between "wildoff" and the start of the wildcard
    699  // component.
    700  for (p = buf + wildoff; p < s; p++) {
    701    if (rem_backslash(p)) {
    702      STRMOVE(p, p + 1);
    703      e--;
    704      s--;
    705    }
    706  }
    707 
    708  // Check for "**" between "s" and "e".
    709  for (p = s; p < e; p++) {
    710    if (p[0] == '*' && p[1] == '*') {
    711      starstar = true;
    712    }
    713  }
    714 
    715  // convert the file pattern to a regexp pattern
    716  int starts_with_dot = *s == '.';
    717  char *pat = file_pat_to_reg_pat(s, e, NULL, false);
    718  if (pat == NULL) {
    719    xfree(buf);
    720    return 0;
    721  }
    722 
    723  // compile the regexp into a program
    724  regmatch_T regmatch;
    725 #if defined(UNIX)
    726  // Ignore case if given 'wildignorecase', else respect 'fileignorecase'.
    727  regmatch.rm_ic = (flags & EW_ICASE) || p_fic;
    728 #else
    729  regmatch.rm_ic = true;  // Always ignore case on Windows.
    730 #endif
    731  if (flags & (EW_NOERROR | EW_NOTWILD)) {
    732    emsg_silent++;
    733  }
    734  bool nobreak = (flags & EW_NOBREAK);
    735  regmatch.regprog = vim_regcomp(pat, RE_MAGIC | (nobreak ? RE_NOBREAK : 0));
    736  if (flags & (EW_NOERROR | EW_NOTWILD)) {
    737    emsg_silent--;
    738  }
    739  xfree(pat);
    740 
    741  if (regmatch.regprog == NULL && (flags & EW_NOTWILD) == 0) {
    742    xfree(buf);
    743    return 0;
    744  }
    745 
    746  size_t len = (size_t)(s - buf);
    747  // If "**" is by itself, this is the first time we encounter it and more
    748  // is following then find matches without any directory.
    749  if (!didstar && stardepth < 100 && starstar && e - s == 2
    750      && *path_end == '/') {
    751    vim_snprintf(s, buflen - len, "%s", path_end + 1);
    752    stardepth++;
    753    do_path_expand(gap, buf, len, flags, true);
    754    stardepth--;
    755  }
    756  *s = NUL;
    757 
    758  Directory dir;
    759  char *dirpath = (*buf == NUL ? "." : buf);
    760  if (os_file_is_readable(dirpath) && os_scandir(&dir, dirpath)) {
    761    // Find all matching entries.
    762    const char *name;
    763    scandir_next_with_dots(NULL);  // initialize
    764    while (!got_int && (name = scandir_next_with_dots(&dir)) != NULL) {
    765      len = (size_t)(s - buf);
    766      if ((name[0] != '.'
    767           || starts_with_dot
    768           || ((flags & EW_DODOT)
    769               && name[1] != NUL
    770               && (name[1] != '.' || name[2] != NUL)))
    771          && ((regmatch.regprog != NULL && vim_regexec(&regmatch, name, 0))
    772              || ((flags & EW_NOTWILD)
    773                  && path_fnamencmp(path + len, name, (size_t)(e - s)) == 0))) {
    774        len += (size_t)vim_snprintf(s, buflen - len, "%s", name);
    775        if (len + 1 >= buflen) {
    776          continue;
    777        }
    778 
    779        if (starstar && stardepth < 100) {
    780          // For "**" in the pattern first go deeper in the tree to
    781          // find matches.
    782          vim_snprintf(buf + len, buflen - len, "/**%s", path_end);  // NOLINT
    783          stardepth++;
    784          do_path_expand(gap, buf, len + 1, flags, true);
    785          stardepth--;
    786        }
    787 
    788        vim_snprintf(buf + len, buflen - len, "%s", path_end);
    789        if (path_has_exp_wildcard(path_end)) {      // handle more wildcards
    790          if (stardepth < 100) {
    791            stardepth++;
    792            // need to expand another component of the path
    793            // remove backslashes for the remaining components only
    794            do_path_expand(gap, buf, len + 1, flags, false);
    795            stardepth--;
    796          }
    797        } else {
    798          FileInfo file_info;
    799 
    800          // no more wildcards, check if there is a match
    801          // remove backslashes for the remaining components only
    802          if (*path_end != NUL) {
    803            backslash_halve(buf + len + 1);
    804          }
    805          // add existing file or symbolic link
    806          if ((flags & EW_ALLLINKS)
    807              ? os_fileinfo_link(buf, &file_info)
    808              : os_path_exists(buf)) {
    809            addfile(gap, buf, flags);
    810          }
    811        }
    812      }
    813    }
    814    os_closedir(&dir);
    815  }
    816 
    817  xfree(buf);
    818  vim_regfree(regmatch.regprog);
    819 
    820  // When interrupted the matches probably won't be used and sorting can be
    821  // slow, thus skip it.
    822  size_t matches = (size_t)(gap->ga_len - start_len);
    823  if (matches > 0 && !got_int) {
    824    qsort(((char **)gap->ga_data) + start_len, matches,
    825          sizeof(char *), pstrcmp);
    826  }
    827  return matches;
    828 }
    829 
    830 // Moves "*psep" back to the previous path separator in "path".
    831 // Returns FAIL is "*psep" ends up at the beginning of "path".
    832 static int find_previous_pathsep(char *path, char **psep)
    833  FUNC_ATTR_NONNULL_ALL
    834 {
    835  // skip the current separator
    836  if (*psep > path && vim_ispathsep(**psep)) {
    837    (*psep)--;
    838  }
    839 
    840  // find the previous separator
    841  while (*psep > path) {
    842    if (vim_ispathsep(**psep)) {
    843      return OK;
    844    }
    845    MB_PTR_BACK(path, *psep);
    846  }
    847 
    848  return FAIL;
    849 }
    850 
    851 /// Returns true if "maybe_unique" is unique wrt other_paths in "gap".
    852 /// "maybe_unique" is the end portion of "((char **)gap->ga_data)[i]".
    853 static bool is_unique(char *maybe_unique, garray_T *gap, int i)
    854  FUNC_ATTR_NONNULL_ALL
    855 {
    856  size_t candidate_len = strlen(maybe_unique);
    857  char **other_paths = gap->ga_data;
    858 
    859  for (int j = 0; j < gap->ga_len; j++) {
    860    if (j == i) {
    861      continue;  // don't compare it with itself
    862    }
    863    size_t other_path_len = strlen(other_paths[j]);
    864    if (other_path_len < candidate_len) {
    865      continue;  // it's different when it's shorter
    866    }
    867    char *rival = other_paths[j] + other_path_len - candidate_len;
    868    if (path_fnamecmp(maybe_unique, rival) == 0
    869        && (rival == other_paths[j] || vim_ispathsep(*(rival - 1)))) {
    870      return false;  // match
    871    }
    872  }
    873  return true;  // no match found
    874 }
    875 
    876 /// Split the 'path' option into an array of strings in garray_T.  Relative
    877 /// paths are expanded to their equivalent fullpath.  This includes the "."
    878 /// (relative to current buffer directory) and empty path (relative to current
    879 /// directory) notations.
    880 ///
    881 /// @param path_option  p_path or p_cdpath
    882 ///
    883 /// TODO(vim): handle upward search (;) and path limiter (**N) notations by
    884 /// expanding each into their equivalent path(s).
    885 static void expand_path_option(char *curdir, char *path_option, garray_T *gap)
    886  FUNC_ATTR_NONNULL_ALL
    887 {
    888  char *buf = xmalloc(MAXPATHL);
    889  size_t curdirlen = 0;
    890 
    891  while (*path_option != NUL) {
    892    size_t buflen = copy_option_part(&path_option, buf, MAXPATHL, " ,");
    893 
    894    if (buf[0] == '.' && (buf[1] == NUL || vim_ispathsep(buf[1]))) {
    895      // Relative to current buffer:
    896      // "/path/file" + "." -> "/path/"
    897      // "/path/file"  + "./subdir" -> "/path/subdir"
    898      if (curbuf->b_ffname == NULL) {
    899        continue;
    900      }
    901      char *p = path_tail(curbuf->b_ffname);
    902      size_t plen = (size_t)(p - curbuf->b_ffname);
    903      if (plen + strlen(buf) >= MAXPATHL) {
    904        continue;
    905      }
    906      if (buf[1] == NUL) {
    907        buf[plen] = NUL;
    908      } else {
    909        memmove(buf + plen, buf + 2, (buflen - 2) + 1);  // +1 for NUL
    910      }
    911      memmove(buf, curbuf->b_ffname, plen);
    912      buflen = simplify_filename(buf);
    913    } else if (buf[0] == NUL) {
    914      STRCPY(buf, curdir);  // relative to current directory
    915      if (curdirlen == 0) {
    916        curdirlen = strlen(curdir);
    917      }
    918      buflen = curdirlen;
    919    } else if (path_with_url(buf)) {
    920      continue;  // URL can't be used here
    921    } else if (!path_is_absolute(buf)) {
    922      // Expand relative path to their full path equivalent
    923      if (curdirlen == 0) {
    924        curdirlen = strlen(curdir);
    925      }
    926      if (curdirlen + buflen + 3 > MAXPATHL) {
    927        continue;
    928      }
    929      memmove(buf + curdirlen + 1, buf, buflen + 1);  // +1 for NUL
    930      STRCPY(buf, curdir);
    931      buf[curdirlen] = PATHSEP;
    932      buflen = simplify_filename(buf);
    933    }
    934 
    935    GA_APPEND(char *, gap, xmemdupz(buf, buflen));
    936  }
    937 
    938  xfree(buf);
    939 }
    940 
    941 // Returns a pointer to the file or directory name in "fname" that matches the
    942 // longest path in "ga"p, or NULL if there is no match. For example:
    943 //
    944 //    path: /foo/bar/baz
    945 //   fname: /foo/bar/baz/quux.txt
    946 // returns:              ^this
    947 static char *get_path_cutoff(char *fname, garray_T *gap)
    948  FUNC_ATTR_NONNULL_ALL
    949 {
    950  int maxlen = 0;
    951  char **path_part = gap->ga_data;
    952  char *cutoff = NULL;
    953 
    954  for (int i = 0; i < gap->ga_len; i++) {
    955    int j = 0;
    956 
    957    while ((fname[j] == path_part[i][j]
    958 #ifdef MSWIN
    959            || (vim_ispathsep(fname[j]) && vim_ispathsep(path_part[i][j]))
    960 #endif
    961            )
    962           && fname[j] != NUL && path_part[i][j] != NUL) {
    963      j++;
    964    }
    965    if (j > maxlen) {
    966      maxlen = j;
    967      cutoff = &fname[j];
    968    }
    969  }
    970 
    971  // skip to the file or directory name
    972  if (cutoff != NULL) {
    973    while (vim_ispathsep(*cutoff)) {
    974      MB_PTR_ADV(cutoff);
    975    }
    976  }
    977 
    978  return cutoff;
    979 }
    980 
    981 /// Sorts, removes duplicates and modifies all the fullpath names in "gap" so
    982 /// that they are unique with respect to each other while conserving the part
    983 /// that matches the pattern. Beware, this is at least O(n^2) wrt "gap->ga_len".
    984 ///
    985 /// @param path_option  p_path or p_cdpath
    986 static void uniquefy_paths(garray_T *gap, char *pattern, char *path_option)
    987  FUNC_ATTR_NONNULL_ALL
    988 {
    989  char **fnames = gap->ga_data;
    990  bool sort_again = false;
    991  regmatch_T regmatch;
    992  garray_T path_ga;
    993  char **in_curdir = NULL;
    994  char *short_name;
    995 
    996  ga_remove_duplicate_strings(gap);
    997  ga_init(&path_ga, (int)sizeof(char *), 1);
    998 
    999  // We need to prepend a '*' at the beginning of file_pattern so that the
   1000  // regex matches anywhere in the path. FIXME: is this valid for all
   1001  // possible patterns?
   1002  size_t len = strlen(pattern);
   1003  char *file_pattern = xmalloc(len + 2);
   1004  file_pattern[0] = '*';
   1005  file_pattern[1] = NUL;
   1006  STRCPY(file_pattern + 1, pattern);
   1007  char *pat = file_pat_to_reg_pat(file_pattern, NULL, NULL, false);
   1008  xfree(file_pattern);
   1009  if (pat == NULL) {
   1010    return;
   1011  }
   1012 
   1013  regmatch.rm_ic = true;                // always ignore case
   1014  regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
   1015  xfree(pat);
   1016  if (regmatch.regprog == NULL) {
   1017    return;
   1018  }
   1019 
   1020  char *curdir = xmalloc(MAXPATHL);
   1021  os_dirname(curdir, MAXPATHL);
   1022  expand_path_option(curdir, path_option, &path_ga);
   1023 
   1024  in_curdir = xcalloc((size_t)gap->ga_len, sizeof(char *));
   1025 
   1026  for (int i = 0; i < gap->ga_len && !got_int; i++) {
   1027    char *path = fnames[i];
   1028    const char *dir_end = gettail_dir(path);
   1029 
   1030    len = strlen(path);
   1031    bool is_in_curdir = path_fnamencmp(curdir, path, (size_t)(dir_end - path)) == 0
   1032                        && curdir[dir_end - path] == NUL;
   1033    if (is_in_curdir) {
   1034      in_curdir[i] = xmemdupz(path, len);
   1035    }
   1036 
   1037    // Shorten the filename while maintaining its uniqueness
   1038    char *path_cutoff = get_path_cutoff(path, &path_ga);
   1039 
   1040    // Don't assume all files can be reached without path when search
   1041    // pattern starts with **/, so only remove path_cutoff
   1042    // when possible.
   1043    if (pattern[0] == '*' && pattern[1] == '*'
   1044        && vim_ispathsep_nocolon(pattern[2])
   1045        && path_cutoff != NULL
   1046        && vim_regexec(&regmatch, path_cutoff, 0)
   1047        && is_unique(path_cutoff, gap, i)) {
   1048      sort_again = true;
   1049      memmove(path, path_cutoff, strlen(path_cutoff) + 1);
   1050    } else {
   1051      // Here all files can be reached without path, so get shortest
   1052      // unique path.  We start at the end of the path.
   1053      char *pathsep_p = path + len - 1;
   1054      while (find_previous_pathsep(path, &pathsep_p)) {
   1055        if (vim_regexec(&regmatch, pathsep_p + 1, 0)
   1056            && is_unique(pathsep_p + 1, gap, i)
   1057            && path_cutoff != NULL && pathsep_p + 1 >= path_cutoff) {
   1058          sort_again = true;
   1059          memmove(path, pathsep_p + 1,
   1060                  (size_t)((path + len) - (pathsep_p + 1)) + 1);  // +1 for NUL
   1061          break;
   1062        }
   1063      }
   1064    }
   1065 
   1066    if (path_is_absolute(path)) {
   1067      // Last resort: shorten relative to curdir if possible.
   1068      // 'possible' means:
   1069      // 1. It is under the current directory.
   1070      // 2. The result is actually shorter than the original.
   1071      //
   1072      //     Before                curdir        After
   1073      //     /foo/bar/file.txt     /foo/bar      ./file.txt
   1074      //     c:\foo\bar\file.txt   c:\foo\bar    .\file.txt
   1075      //     /file.txt             /             /file.txt
   1076      //     c:\file.txt           c:\           .\file.txt
   1077      short_name = path_shorten_fname(path, curdir);
   1078      if (short_name != NULL && short_name > path + 1) {
   1079        vim_snprintf(path, MAXPATHL, ".%s%s", PATHSEPSTR, short_name);
   1080      }
   1081    }
   1082    os_breakcheck();
   1083  }
   1084 
   1085  // Shorten filenames in /in/current/directory/{filename}
   1086  for (int i = 0; i < gap->ga_len && !got_int; i++) {
   1087    char *path = in_curdir[i];
   1088 
   1089    if (path == NULL) {
   1090      continue;
   1091    }
   1092 
   1093    // If the {filename} is not unique, change it to ./{filename}.
   1094    // Else reduce it to {filename}
   1095    short_name = path_shorten_fname(path, curdir);
   1096    if (short_name == NULL) {
   1097      short_name = path;
   1098    }
   1099    if (is_unique(short_name, gap, i)) {
   1100      STRCPY(fnames[i], short_name);
   1101      continue;
   1102    }
   1103 
   1104    size_t rel_pathsize = 1 + STRLEN_LITERAL(PATHSEPSTR) + strlen(short_name) + 1;
   1105    char *rel_path = xmalloc(rel_pathsize);
   1106 
   1107    vim_snprintf(rel_path, rel_pathsize, ".%s%s", PATHSEPSTR, short_name);
   1108 
   1109    xfree(fnames[i]);
   1110    fnames[i] = rel_path;
   1111    sort_again = true;
   1112    os_breakcheck();
   1113  }
   1114 
   1115  xfree(curdir);
   1116  for (int i = 0; i < gap->ga_len; i++) {
   1117    xfree(in_curdir[i]);
   1118  }
   1119  xfree(in_curdir);
   1120  ga_clear_strings(&path_ga);
   1121  vim_regfree(regmatch.regprog);
   1122 
   1123  if (sort_again) {
   1124    ga_remove_duplicate_strings(gap);
   1125  }
   1126 }
   1127 
   1128 /// Find end of the directory name
   1129 ///
   1130 /// @param[in]  fname  File name to process.
   1131 ///
   1132 /// @return end of the directory name, on the first path separator:
   1133 ///
   1134 ///            "/path/file", "/path/dir/", "/path//dir", "/file"
   1135 ///                  ^             ^             ^        ^
   1136 const char *gettail_dir(const char *const fname)
   1137  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
   1138 {
   1139  const char *dir_end = fname;
   1140  const char *next_dir_end = fname;
   1141  bool look_for_sep = true;
   1142 
   1143  for (const char *p = fname; *p != NUL;) {
   1144    if (vim_ispathsep(*p)) {
   1145      if (look_for_sep) {
   1146        next_dir_end = p;
   1147        look_for_sep = false;
   1148      }
   1149    } else {
   1150      if (!look_for_sep) {
   1151        dir_end = next_dir_end;
   1152      }
   1153      look_for_sep = true;
   1154    }
   1155    MB_PTR_ADV(p);
   1156  }
   1157  return dir_end;
   1158 }
   1159 
   1160 /// Calls globpath() with 'path' values for the given pattern and stores the
   1161 /// result in "gap".
   1162 /// Returns the total number of matches.
   1163 ///
   1164 /// @param flags  EW_* flags
   1165 static int expand_in_path(garray_T *const gap, char *const pattern, const int flags)
   1166  FUNC_ATTR_NONNULL_ALL
   1167 {
   1168  garray_T path_ga;
   1169  char *path_option = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path;
   1170 
   1171  char *const curdir = xmalloc(MAXPATHL);
   1172  os_dirname(curdir, MAXPATHL);
   1173 
   1174  ga_init(&path_ga, (int)sizeof(char *), 1);
   1175  if (flags & EW_CDPATH) {
   1176    expand_path_option(curdir, p_cdpath, &path_ga);
   1177  } else {
   1178    expand_path_option(curdir, path_option, &path_ga);
   1179  }
   1180  xfree(curdir);
   1181  if (GA_EMPTY(&path_ga)) {
   1182    return 0;
   1183  }
   1184 
   1185  char *const paths = ga_concat_strings(&path_ga, ",");
   1186  ga_clear_strings(&path_ga);
   1187 
   1188  int glob_flags = 0;
   1189  if (flags & EW_ICASE) {
   1190    glob_flags |= WILD_ICASE;
   1191  }
   1192  if (flags & EW_ADDSLASH) {
   1193    glob_flags |= WILD_ADD_SLASH;
   1194  }
   1195  globpath(paths, pattern, gap, glob_flags, !!(flags & EW_CDPATH));
   1196  xfree(paths);
   1197 
   1198  return gap->ga_len;
   1199 }
   1200 
   1201 /// Return true if "p" contains what looks like an environment variable.
   1202 /// Allowing for escaping.
   1203 static bool has_env_var(char *p)
   1204  FUNC_ATTR_NONNULL_ALL
   1205 {
   1206  for (; *p; MB_PTR_ADV(p)) {
   1207    if (*p == '\\' && p[1] != NUL) {
   1208      p++;
   1209    } else if (vim_strchr("$", (uint8_t)(*p)) != NULL) {
   1210      return true;
   1211    }
   1212  }
   1213  return false;
   1214 }
   1215 
   1216 #ifdef SPECIAL_WILDCHAR
   1217 
   1218 // Return true if "p" contains a special wildcard character, one that Vim
   1219 // cannot expand, requires using a shell.
   1220 static bool has_special_wildchar(char *p, int flags)
   1221  FUNC_ATTR_NONNULL_ALL
   1222 {
   1223  for (; *p; MB_PTR_ADV(p)) {
   1224    // Disallow line break characters.
   1225    if (*p == '\r' || *p == '\n') {
   1226      break;
   1227    }
   1228    // Allow for escaping.
   1229    if (*p == '\\' && p[1] != NUL && p[1] != '\r' && p[1] != '\n') {
   1230      p++;
   1231    } else if (vim_strchr(SPECIAL_WILDCHAR, (uint8_t)(*p)) != NULL) {
   1232      // Need a shell for curly braces only when including non-existing files.
   1233      if (*p == '{' && !(flags & EW_NOTFOUND)) {
   1234        continue;
   1235      }
   1236      // A { must be followed by a matching }.
   1237      if (*p == '{' && vim_strchr(p, '}') == NULL) {
   1238        continue;
   1239      }
   1240      // A quote and backtick must be followed by another one.
   1241      if ((*p == '`' || *p == '\'') && vim_strchr(p, (uint8_t)(*p)) == NULL) {
   1242        continue;
   1243      }
   1244      return true;
   1245    }
   1246  }
   1247  return false;
   1248 }
   1249 #endif
   1250 
   1251 /// Generic wildcard expansion code.
   1252 ///
   1253 /// Characters in pat that should not be expanded must be preceded with a
   1254 /// backslash. E.g., "/path\ with\ spaces/my\*star*".
   1255 ///
   1256 /// @param      num_pat  is number of input patterns.
   1257 /// @param      pat      is an array of pointers to input patterns.
   1258 /// @param[out] num_file is pointer to number of matched file names.
   1259 /// @param[out] file     is pointer to array of pointers to matched file names.
   1260 /// @param      flags    is a combination of EW_* flags used in
   1261 ///                      expand_wildcards().
   1262 ///
   1263 /// @returns             OK when some files were found. *num_file is set to the
   1264 ///                      number of matches, *file to the allocated array of
   1265 ///                      matches. Call FreeWild() later.
   1266 ///                      If FAIL is returned, *num_file and *file are either
   1267 ///                      unchanged or *num_file is set to 0 and *file is set
   1268 ///                      to NULL or points to "".
   1269 int gen_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, int flags)
   1270 {
   1271  garray_T ga;
   1272  char *p;
   1273  static bool recursive = false;
   1274  int add_pat;
   1275  bool did_expand_in_path = false;
   1276  char *path_option = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path;
   1277 
   1278  // expand_env() is called to expand things like "~user".  If this fails,
   1279  // it calls ExpandOne(), which brings us back here.  In this case, always
   1280  // call the machine specific expansion function, if possible.  Otherwise,
   1281  // return FAIL.
   1282  if (recursive) {
   1283 #ifdef SPECIAL_WILDCHAR
   1284    return os_expand_wildcards(num_pat, pat, num_file, file, flags);
   1285 #else
   1286    return FAIL;
   1287 #endif
   1288  }
   1289 
   1290 #ifdef SPECIAL_WILDCHAR
   1291  // If there are any special wildcard characters which we cannot handle
   1292  // here, call machine specific function for all the expansion.  This
   1293  // avoids starting the shell for each argument separately.
   1294  // For `=expr` do use the internal function.
   1295  for (int i = 0; i < num_pat; i++) {
   1296    if (has_special_wildchar(pat[i], flags)
   1297        && !(vim_backtick(pat[i]) && pat[i][1] == '=')) {
   1298      return os_expand_wildcards(num_pat, pat, num_file, file, flags);
   1299    }
   1300  }
   1301 #endif
   1302 
   1303  recursive = true;
   1304 
   1305  // The matching file names are stored in a growarray.  Init it empty.
   1306  ga_init(&ga, (int)sizeof(char *), 30);
   1307 
   1308  for (int i = 0; i < num_pat && !got_int; i++) {
   1309    add_pat = -1;
   1310    p = pat[i];
   1311 
   1312    if (vim_backtick(p)) {
   1313      add_pat = expand_backtick(&ga, p, flags);
   1314      if (add_pat == -1) {
   1315        recursive = false;
   1316        ga_clear_strings(&ga);
   1317        *num_file = 0;
   1318        *file = NULL;
   1319        return FAIL;
   1320      }
   1321    } else {
   1322      // First expand environment variables, "~/" and "~user/".
   1323      if ((has_env_var(p) && !(flags & EW_NOTENV)) || *p == '~') {
   1324        p = expand_env_save_opt(p, true);
   1325        if (p == NULL) {
   1326          p = pat[i];
   1327        } else {
   1328 #ifdef UNIX
   1329          // On Unix, if expand_env() can't expand an environment
   1330          // variable, use the shell to do that.  Discard previously
   1331          // found file names and start all over again.
   1332          if (has_env_var(p) || *p == '~') {
   1333            xfree(p);
   1334            ga_clear_strings(&ga);
   1335            i = os_expand_wildcards(num_pat, pat, num_file, file,
   1336                                    flags | EW_KEEPDOLLAR);
   1337            recursive = false;
   1338            return i;
   1339          }
   1340 #endif
   1341        }
   1342      }
   1343 
   1344      // If there are wildcards or case-insensitive expansion is
   1345      // required: Expand file names and add each match to the list.  If
   1346      // there is no match, and EW_NOTFOUND is given, add the pattern.
   1347      // Otherwise: Add the file name if it exists or when EW_NOTFOUND is
   1348      // given.
   1349      if (path_has_exp_wildcard(p) || (flags & EW_ICASE)) {
   1350        if ((flags & (EW_PATH | EW_CDPATH))
   1351            && !path_is_absolute(p)
   1352            && !(p[0] == '.'
   1353                 && (vim_ispathsep(p[1])
   1354                     || (p[1] == '.'
   1355                         && vim_ispathsep(p[2]))))) {
   1356          // :find completion where 'path' is used.
   1357          // Recursiveness is OK here.
   1358          recursive = false;
   1359          add_pat = expand_in_path(&ga, p, flags);
   1360          recursive = true;
   1361          did_expand_in_path = true;
   1362        } else {
   1363          // Recursive gen_expand_wildcards() can only happen here when called from an
   1364          // event handler in os_breakcheck(), in which case it should be allowed.
   1365          recursive = false;
   1366          size_t tmp_add_pat = path_expand(&ga, p, flags);
   1367          recursive = true;
   1368          assert(tmp_add_pat <= INT_MAX);
   1369          add_pat = (int)tmp_add_pat;
   1370        }
   1371      }
   1372    }
   1373 
   1374    if (add_pat == -1 || (add_pat == 0 && (flags & EW_NOTFOUND))) {
   1375      char *t = backslash_halve_save(p);
   1376 
   1377      // When EW_NOTFOUND is used, always add files and dirs.  Makes
   1378      // "vim c:/" work.
   1379      if (flags & EW_NOTFOUND) {
   1380        addfile(&ga, t, flags | EW_DIR | EW_FILE);
   1381      } else {
   1382        addfile(&ga, t, flags);
   1383      }
   1384 
   1385      if (t != p) {
   1386        xfree(t);
   1387      }
   1388    }
   1389 
   1390    if (did_expand_in_path && !GA_EMPTY(&ga) && (flags & (EW_PATH | EW_CDPATH))) {
   1391      // Recursive gen_expand_wildcards() can only happen here when called from an
   1392      // event handler in os_breakcheck(), in which case it should be allowed.
   1393      recursive = false;
   1394      uniquefy_paths(&ga, p, path_option);
   1395      recursive = true;
   1396    }
   1397    if (p != pat[i]) {
   1398      xfree(p);
   1399    }
   1400  }
   1401 
   1402  *num_file = ga.ga_len;
   1403  *file = (ga.ga_data != NULL) ? ga.ga_data : NULL;
   1404 
   1405  recursive = false;
   1406 
   1407  return ((flags & EW_EMPTYOK) || ga.ga_data != NULL) ? OK : FAIL;
   1408 }
   1409 
   1410 /// Free the list of files returned by expand_wildcards() or other expansion functions.
   1411 void FreeWild(int count, char **files)
   1412 {
   1413  if (count <= 0 || files == NULL) {
   1414    return;
   1415  }
   1416  while (count--) {
   1417    xfree(files[count]);
   1418  }
   1419  xfree(files);
   1420 }
   1421 
   1422 /// @return  true if we can expand this backtick thing here.
   1423 static bool vim_backtick(char *p)
   1424  FUNC_ATTR_NONNULL_ALL
   1425 {
   1426  return *p == '`' && *(p + 1) != NUL && *(p + strlen(p) - 1) == '`';
   1427 }
   1428 
   1429 /// Expand an item in `backticks` by executing it as a command.
   1430 /// Currently only works when pat[] starts and ends with a `.
   1431 /// Returns number of file names found, -1 if an error is encountered.
   1432 ///
   1433 /// @param flags  EW_* flags
   1434 static int expand_backtick(garray_T *gap, char *pat, int flags)
   1435  FUNC_ATTR_NONNULL_ALL
   1436 {
   1437  char *p;
   1438  char *buffer;
   1439  int cnt = 0;
   1440 
   1441  // Create the command: lop off the backticks.
   1442  char *cmd = xmemdupz(pat + 1, strlen(pat) - 2);
   1443 
   1444  if (*cmd == '=') {          // `={expr}`: Expand expression
   1445    buffer = eval_to_string(cmd + 1, true, false);
   1446  } else {
   1447    buffer = get_cmd_output(cmd, NULL, (flags & EW_SILENT) ? kShellOptSilent : 0, NULL);
   1448  }
   1449  xfree(cmd);
   1450  if (buffer == NULL) {
   1451    return -1;
   1452  }
   1453 
   1454  cmd = buffer;
   1455  while (*cmd != NUL) {
   1456    cmd = skipwhite(cmd);               // skip over white space
   1457    p = cmd;
   1458    while (*p != NUL && *p != '\r' && *p != '\n') {  // skip over entry
   1459      p++;
   1460    }
   1461    // add an entry if it is not empty
   1462    if (p > cmd) {
   1463      char i = *p;
   1464      *p = NUL;
   1465      addfile(gap, cmd, flags);
   1466      *p = i;
   1467      cnt++;
   1468    }
   1469    cmd = p;
   1470    while (*cmd != NUL && (*cmd == '\r' || *cmd == '\n')) {
   1471      cmd++;
   1472    }
   1473  }
   1474 
   1475  xfree(buffer);
   1476  return cnt;
   1477 }
   1478 
   1479 #ifdef BACKSLASH_IN_FILENAME
   1480 /// Replace all slashes by backslashes.
   1481 /// This used to be the other way around, but MS-DOS sometimes has problems
   1482 /// with slashes (e.g. in a command name).  We can't have mixed slashes and
   1483 /// backslashes, because comparing file names will not work correctly.  The
   1484 /// commands that use a file name should try to avoid the need to type a
   1485 /// backslash twice.
   1486 /// When 'shellslash' set do it the other way around.
   1487 /// When the path looks like a URL leave it unmodified.
   1488 void slash_adjust(char *p)
   1489  FUNC_ATTR_NONNULL_ALL
   1490 {
   1491  if (path_with_url(p)) {
   1492    return;
   1493  }
   1494 
   1495  if (*p == '`') {
   1496    // don't replace backslash in backtick quoted strings
   1497    const size_t len = strlen(p);
   1498    if (len > 2 && *(p + len - 1) == '`') {
   1499      return;
   1500    }
   1501  }
   1502 
   1503  while (*p) {
   1504    if (*p == psepcN) {
   1505      *p = psepc;
   1506    }
   1507    MB_PTR_ADV(p);
   1508  }
   1509 }
   1510 #endif
   1511 
   1512 /// Add a file to a file list.  Accepted flags:
   1513 /// EW_DIR      add directories
   1514 /// EW_FILE     add files
   1515 /// EW_EXEC     add executable files
   1516 /// EW_NOTFOUND add even when it doesn't exist
   1517 /// EW_ADDSLASH add slash after directory name
   1518 /// EW_ALLLINKS add symlink also when the referred file does not exist
   1519 ///
   1520 /// @param f  filename
   1521 void addfile(garray_T *gap, char *f, int flags)
   1522  FUNC_ATTR_NONNULL_ALL
   1523 {
   1524  bool isdir;
   1525  FileInfo file_info;
   1526 
   1527  // if the file/dir/link doesn't exist, may not add it
   1528  if (!(flags & EW_NOTFOUND)
   1529      && ((flags & EW_ALLLINKS)
   1530          ? !os_fileinfo_link(f, &file_info)
   1531          : !os_path_exists(f))) {
   1532    return;
   1533  }
   1534 
   1535 #ifdef FNAME_ILLEGAL
   1536  // if the file/dir contains illegal characters, don't add it
   1537  if (strpbrk(f, FNAME_ILLEGAL) != NULL) {
   1538    return;
   1539  }
   1540 #endif
   1541 
   1542  isdir = os_isdir(f);
   1543  if ((isdir && !(flags & EW_DIR)) || (!isdir && !(flags & EW_FILE))) {
   1544    return;
   1545  }
   1546 
   1547  // If the file isn't executable, may not add it.  Do accept directories.
   1548  // When invoked from expand_shellcmd() do not use $PATH.
   1549  if (!isdir && (flags & EW_EXEC)
   1550      && !os_can_exe(f, NULL, !(flags & EW_SHELLCMD))) {
   1551    return;
   1552  }
   1553 
   1554  char *p = xmalloc(strlen(f) + 1 + isdir);
   1555 
   1556  STRCPY(p, f);
   1557 #ifdef BACKSLASH_IN_FILENAME
   1558  slash_adjust(p);
   1559 #endif
   1560  // Append a slash or backslash after directory names if none is present.
   1561  if (isdir && (flags & EW_ADDSLASH)) {
   1562    add_pathsep(p);
   1563  }
   1564  GA_APPEND(char *, gap, p);
   1565 }
   1566 
   1567 // Converts a file name into a canonical form. It simplifies a file name into
   1568 // its simplest form by stripping out unneeded components, if any.  The
   1569 // resulting file name is simplified in place and will either be the same
   1570 // length as that supplied, or shorter.
   1571 size_t simplify_filename(char *filename)
   1572  FUNC_ATTR_NONNULL_ALL
   1573 {
   1574  int components = 0;
   1575  bool stripping_disabled = false;
   1576  bool relative = true;
   1577 
   1578  char *p = filename;
   1579 #ifdef BACKSLASH_IN_FILENAME
   1580  if (p[0] != NUL && p[1] == ':') {        // skip "x:"
   1581    p += 2;
   1582  }
   1583 #endif
   1584 
   1585  if (vim_ispathsep(*p)) {
   1586    relative = false;
   1587    do {
   1588      p++;
   1589    } while (vim_ispathsep(*p));
   1590  }
   1591  char *start = p;        // remember start after "c:/" or "/" or "///"
   1592  char *p_end = p + strlen(p);  // point to NUL at end of string "p"
   1593 #ifdef UNIX
   1594  // Posix says that "//path" is unchanged but "///path" is "/path".
   1595  if (start > filename + 2) {
   1596    memmove(filename + 1, p, (size_t)(p_end - p) + 1);  // +1 for NUL
   1597    p_end -= (size_t)(p - (filename + 1));
   1598    start = p = filename + 1;
   1599  }
   1600 #endif
   1601 
   1602  do {
   1603    // At this point "p" is pointing to the char following a single "/"
   1604    // or "p" is at the "start" of the (absolute or relative) path name.
   1605    if (vim_ispathsep(*p)) {
   1606      memmove(p, p + 1, (size_t)(p_end - (p + 1)) + 1);  // remove duplicate "/"
   1607      p_end--;
   1608    } else if (p[0] == '.'
   1609               && (vim_ispathsep(p[1]) || p[1] == NUL)) {
   1610      if (p == start && relative) {
   1611        p += 1 + (p[1] != NUL);         // keep single "." or leading "./"
   1612      } else {
   1613        // Strip "./" or ".///".  If we are at the end of the file name
   1614        // and there is no trailing path separator, either strip "/." if
   1615        // we are after "start", or strip "." if we are at the beginning
   1616        // of an absolute path name.
   1617        char *tail = p + 1;
   1618        if (p[1] != NUL) {
   1619          while (vim_ispathsep(*tail)) {
   1620            MB_PTR_ADV(tail);
   1621          }
   1622        } else if (p > start) {
   1623          p--;                          // strip preceding path separator
   1624        }
   1625        memmove(p, tail, (size_t)(p_end - tail) + 1);
   1626        p_end -= (size_t)(tail - p);
   1627      }
   1628    } else if (p[0] == '.' && p[1] == '.'
   1629               && (vim_ispathsep(p[2]) || p[2] == NUL)) {
   1630      // Skip to after ".." or "../" or "..///".
   1631      char *tail = p + 2;
   1632      while (vim_ispathsep(*tail)) {
   1633        MB_PTR_ADV(tail);
   1634      }
   1635 
   1636      if (components > 0) {             // strip one preceding component
   1637        bool do_strip = false;
   1638 
   1639        // Don't strip for an erroneous file name.
   1640        if (!stripping_disabled) {
   1641          // If the preceding component does not exist in the file
   1642          // system, we strip it.  On Unix, we don't accept a symbolic
   1643          // link that refers to a non-existent file.
   1644          char saved_char = p[-1];
   1645          p[-1] = NUL;
   1646          FileInfo file_info;
   1647          if (!os_fileinfo_link(filename, &file_info)) {
   1648            do_strip = true;
   1649          }
   1650          p[-1] = saved_char;
   1651 
   1652          p--;
   1653          // Skip back to after previous '/'.
   1654          while (p > start && !after_pathsep(start, p)) {
   1655            MB_PTR_BACK(start, p);
   1656          }
   1657 
   1658          if (!do_strip) {
   1659            // If the component exists in the file system, check
   1660            // that stripping it won't change the meaning of the
   1661            // file name.  First get information about the
   1662            // unstripped file name.  This may fail if the component
   1663            // to strip is not a searchable directory (but a regular
   1664            // file, for instance), since the trailing "/.." cannot
   1665            // be applied then.  We don't strip it then since we
   1666            // don't want to replace an erroneous file name by
   1667            // a valid one, and we disable stripping of later
   1668            // components.
   1669            saved_char = *tail;
   1670            *tail = NUL;
   1671            if (os_fileinfo(filename, &file_info)) {
   1672              do_strip = true;
   1673            } else {
   1674              stripping_disabled = true;
   1675            }
   1676            *tail = saved_char;
   1677            if (do_strip) {
   1678              // The check for the unstripped file name
   1679              // above works also for a symbolic link pointing to
   1680              // a searchable directory.  But then the parent of
   1681              // the directory pointed to by the link must be the
   1682              // same as the stripped file name.  (The latter
   1683              // exists in the file system since it is the
   1684              // component's parent directory.)
   1685              FileInfo new_file_info;
   1686              if (p == start && relative) {
   1687                os_fileinfo(".", &new_file_info);
   1688              } else {
   1689                saved_char = *p;
   1690                *p = NUL;
   1691                os_fileinfo(filename, &new_file_info);
   1692                *p = saved_char;
   1693              }
   1694 
   1695              if (!os_fileinfo_id_equal(&file_info, &new_file_info)) {
   1696                do_strip = false;
   1697                // We don't disable stripping of later
   1698                // components since the unstripped path name is
   1699                // still valid.
   1700              }
   1701            }
   1702          }
   1703        }
   1704 
   1705        if (!do_strip) {
   1706          // Skip the ".." or "../" and reset the counter for the
   1707          // components that might be stripped later on.
   1708          p = tail;
   1709          components = 0;
   1710        } else {
   1711          // Strip previous component.  If the result would get empty
   1712          // and there is no trailing path separator, leave a single
   1713          // "." instead.  If we are at the end of the file name and
   1714          // there is no trailing path separator and a preceding
   1715          // component is left after stripping, strip its trailing
   1716          // path separator as well.
   1717          if (p == start && relative && tail[-1] == '.') {
   1718            *p++ = '.';
   1719            *p = NUL;
   1720          } else {
   1721            if (p > start && tail[-1] == '.') {
   1722              p--;
   1723            }
   1724            memmove(p, tail, (size_t)(p_end - tail) + 1);  // strip previous component
   1725            p_end -= (size_t)(tail - p);
   1726          }
   1727 
   1728          components--;
   1729        }
   1730      } else if (p == start && !relative) {            // leading "/.." or "/../"
   1731        memmove(p, tail, (size_t)(p_end - tail) + 1);  // strip ".." or "../"
   1732        p_end -= (size_t)(tail - p);
   1733      } else {
   1734        if (p == start + 2 && p[-2] == '.') {          // leading "./../"
   1735          memmove(p - 2, p, (size_t)(p_end - p) + 1);  // strip leading "./"
   1736          p_end -= 2;
   1737          tail -= 2;
   1738        }
   1739        p = tail;                       // skip to char after ".." or "../"
   1740      }
   1741    } else {
   1742      components++;  // Simple path component.
   1743      p = (char *)path_next_component(p);
   1744    }
   1745  } while (*p != NUL);
   1746 
   1747  return (size_t)(p_end - filename);
   1748 }
   1749 
   1750 /// Checks for a Windows drive letter ("C:/") at the start of the path.
   1751 ///
   1752 /// @see https://url.spec.whatwg.org/#start-with-a-windows-drive-letter
   1753 bool path_has_drive_letter(const char *p, size_t path_len)
   1754  FUNC_ATTR_NONNULL_ALL
   1755 {
   1756  return path_len >= 2
   1757         && ASCII_ISALPHA(p[0])
   1758         && (p[1] == ':' || p[1] == '|')
   1759         && (path_len == 2 || ((p[2] == '/') | (p[2] == '\\') | (p[2] == '?') | (p[2] == '#')));
   1760 }
   1761 
   1762 // Check if the ":/" of a URL is at the pointer, return URL_SLASH.
   1763 // Also check for ":\\", which MS Internet Explorer accepts, return
   1764 // URL_BACKSLASH.
   1765 int path_is_url(const char *p)
   1766  FUNC_ATTR_NONNULL_ALL
   1767 {
   1768  // In the spec ':' is enough to recognize a scheme
   1769  // https://url.spec.whatwg.org/#scheme-state
   1770  if (strncmp(p, ":/", 2) == 0) {
   1771    return URL_SLASH;
   1772  } else if (strncmp(p, ":\\\\", 3) == 0) {
   1773    return URL_BACKSLASH;
   1774  }
   1775  return 0;
   1776 }
   1777 
   1778 /// Check if "fname" starts with "name://" or "name:\\".
   1779 ///
   1780 /// @param  fname         is the filename to test
   1781 /// @return URL_SLASH for "name://", URL_BACKSLASH for "name:\\", zero otherwise.
   1782 int path_with_url(const char *fname)
   1783  FUNC_ATTR_NONNULL_ALL
   1784 {
   1785  const char *p;
   1786 
   1787  // first character must be alpha
   1788  if (!ASCII_ISALPHA(*fname)) {
   1789    return 0;
   1790  }
   1791 
   1792  if (path_has_drive_letter(fname, strlen(fname))) {
   1793    return 0;
   1794  }
   1795 
   1796  // check body: (alpha, digit, '+', '-', '.') following RFC3986
   1797  for (p = fname + 1; (ASCII_ISALNUM(*p) || (*p == '+') || (*p == '-') || (*p == '.')); p++) {}
   1798 
   1799  // check last char is not '+', '-', or '.'
   1800  if ((p[-1] == '+') || (p[-1] == '-') || (p[-1] == '.')) {
   1801    return 0;
   1802  }
   1803 
   1804  // ":/" or ":\\" must follow
   1805  return path_is_url(p);
   1806 }
   1807 
   1808 bool path_with_extension(const char *path, const char *extension)
   1809  FUNC_ATTR_NONNULL_ALL
   1810 {
   1811  const char *last_dot = strrchr(path, '.');
   1812  if (!last_dot) {
   1813    return false;
   1814  }
   1815  return mb_strcmp_ic((bool)p_fic, last_dot + 1, extension) == 0;
   1816 }
   1817 
   1818 /// Return true if "name" is a full (absolute) path name or URL.
   1819 bool vim_isAbsName(const char *name)
   1820  FUNC_ATTR_NONNULL_ALL
   1821 {
   1822  return path_with_url(name) != 0 || path_is_absolute(name);
   1823 }
   1824 
   1825 /// Save absolute file name to "buf[len]".
   1826 ///
   1827 /// @param      fname filename to evaluate
   1828 /// @param[out] buf   contains `fname` absolute path, or:
   1829 ///                   - truncated `fname` if longer than `len`
   1830 ///                   - unmodified `fname` if absolute path fails or is a URL
   1831 /// @param      len   length of `buf`
   1832 /// @param      force flag to force expanding even if the path is absolute
   1833 ///
   1834 /// @return           FAIL for failure, OK otherwise
   1835 int vim_FullName(const char *fname, char *buf, size_t len, bool force)
   1836  FUNC_ATTR_NONNULL_ARG(2)
   1837 {
   1838  *buf = NUL;
   1839  if (fname == NULL) {
   1840    return FAIL;
   1841  }
   1842 
   1843  if (strlen(fname) > (len - 1)) {
   1844    xstrlcpy(buf, fname, len);  // truncate
   1845 #ifdef MSWIN
   1846    slash_adjust(buf);
   1847 #endif
   1848    return FAIL;
   1849  }
   1850 
   1851  if (path_with_url(fname)) {
   1852    xstrlcpy(buf, fname, len);
   1853    return OK;
   1854  }
   1855 
   1856  int rv = path_to_absolute(fname, buf, len, force);
   1857  if (rv == FAIL) {
   1858    xstrlcpy(buf, fname, len);  // something failed; use the filename
   1859  }
   1860 #ifdef MSWIN
   1861  slash_adjust(buf);
   1862 #endif
   1863  return rv;
   1864 }
   1865 
   1866 /// Get the full resolved path for `fname`
   1867 ///
   1868 /// Even filenames that appear to be absolute based on starting from
   1869 /// the root may have relative paths (like dir/../subdir) or symlinks
   1870 /// embedded, or even extra separators (//).  This function addresses
   1871 /// those possibilities, returning a resolved absolute path.
   1872 /// For MS-Windows, this also provides drive letter for all absolute paths.
   1873 ///
   1874 /// @param fname is the filename to expand
   1875 /// @return [allocated] Full path (NULL for failure).
   1876 char *fix_fname(const char *fname)
   1877 {
   1878 #ifdef UNIX
   1879  return FullName_save(fname, true);
   1880 #else
   1881  if (!vim_isAbsName(fname)
   1882      || strstr(fname, "..") != NULL
   1883      || strstr(fname, "//") != NULL
   1884 # ifdef BACKSLASH_IN_FILENAME
   1885      || strstr(fname, "\\\\") != NULL
   1886 # endif
   1887 # ifdef MSWIN
   1888      || fname[0] == '/'
   1889      || fname[0] == '\\'
   1890 # endif
   1891      ) {
   1892    return FullName_save(fname, false);
   1893  }
   1894 
   1895  fname = xstrdup(fname);
   1896 
   1897 # ifdef CASE_INSENSITIVE_FILENAME
   1898  path_fix_case((char *)fname);  // set correct case for file name
   1899 # endif
   1900 
   1901  return (char *)fname;
   1902 #endif
   1903 }
   1904 
   1905 /// Set the case of the file name, if it already exists.  This will cause the
   1906 /// file name to remain exactly the same.
   1907 /// Only required for file systems where case is ignored and preserved.
   1908 // TODO(SplinterOfChaos): Could also be used when mounting case-insensitive
   1909 // file systems.
   1910 void path_fix_case(char *name)
   1911  FUNC_ATTR_NONNULL_ALL
   1912 {
   1913  FileInfo file_info;
   1914  if (!os_fileinfo_link(name, &file_info)) {
   1915    return;
   1916  }
   1917 
   1918  // Open the directory where the file is located.
   1919  char *slash = strrchr(name, '/');
   1920  char *tail;
   1921  Directory dir;
   1922  bool ok;
   1923  if (slash == NULL) {
   1924    ok = os_scandir(&dir, ".");
   1925    tail = name;
   1926  } else {
   1927    *slash = NUL;
   1928    ok = os_scandir(&dir, name);
   1929    *slash = '/';
   1930    tail = slash + 1;
   1931  }
   1932 
   1933  if (!ok) {
   1934    return;
   1935  }
   1936 
   1937  size_t taillen = strlen(tail);
   1938  const char *entry;
   1939  while ((entry = os_scandir_next(&dir))) {
   1940    // Only accept names that differ in case and are the same byte
   1941    // length. TODO: accept different length name.
   1942    if (STRICMP(tail, entry) == 0 && taillen == strlen(entry)) {
   1943      char newname[MAXPATHL + 1];
   1944 
   1945      // Verify the inode is equal.
   1946      xstrlcpy(newname, name, MAXPATHL + 1);
   1947      xstrlcpy(newname + (tail - name), entry,
   1948               (size_t)(MAXPATHL - (tail - name) + 1));
   1949      FileInfo file_info_new;
   1950      if (os_fileinfo_link(newname, &file_info_new)
   1951          && os_fileinfo_id_equal(&file_info, &file_info_new)) {
   1952        STRCPY(tail, entry);
   1953        break;
   1954      }
   1955    }
   1956  }
   1957 
   1958  os_closedir(&dir);
   1959 }
   1960 
   1961 /// Return true if "p" points to just after a path separator.
   1962 /// Takes care of multi-byte characters.
   1963 /// "b" must point to the start of the file name
   1964 int after_pathsep(const char *b, const char *p)
   1965  FUNC_ATTR_NONNULL_ALL
   1966 {
   1967  return p > b && vim_ispathsep(p[-1])
   1968         && utf_head_off(b, p - 1) == 0;
   1969 }
   1970 
   1971 /// Return true if file names "f1" and "f2" are in the same directory.
   1972 /// "f1" may be a short name, "f2" must be a full path.
   1973 bool same_directory(char *f1, char *f2)
   1974 {
   1975  char ffname[MAXPATHL];
   1976  char *t1;
   1977  char *t2;
   1978 
   1979  // safety check
   1980  if (f1 == NULL || f2 == NULL) {
   1981    return false;
   1982  }
   1983 
   1984  vim_FullName(f1, ffname, MAXPATHL, false);
   1985  t1 = path_tail_with_sep(ffname);
   1986  t2 = path_tail_with_sep(f2);
   1987  return t1 - ffname == t2 - f2
   1988         && pathcmp(ffname, f2, (int)(t1 - ffname)) == 0;
   1989 }
   1990 
   1991 // Compare path "p[]" to "q[]".
   1992 // If `maxlen` >= 0 compare `p[maxlen]` to `q[maxlen]`
   1993 // Return value like strcmp(p, q), but consider path separators.
   1994 //
   1995 // See also `path_full_compare`.
   1996 int pathcmp(const char *p, const char *q, int maxlen)
   1997 {
   1998  int i, j;
   1999  const char *s = NULL;
   2000 
   2001  for (i = 0, j = 0; maxlen < 0 || (i < maxlen && j < maxlen);) {
   2002    int c1 = utf_ptr2char(p + i);
   2003    int c2 = utf_ptr2char(q + j);
   2004 
   2005    // End of "p": check if "q" also ends or just has a slash.
   2006    if (c1 == NUL) {
   2007      if (c2 == NUL) {      // full match
   2008        return 0;
   2009      }
   2010      s = q;
   2011      i = j;
   2012      break;
   2013    }
   2014 
   2015    // End of "q": check if "p" just has a slash.
   2016    if (c2 == NUL) {
   2017      s = p;
   2018      break;
   2019    }
   2020 
   2021    if ((p_fic ? mb_toupper(c1) != mb_toupper(c2) : c1 != c2)
   2022 #ifdef BACKSLASH_IN_FILENAME
   2023        // consider '/' and '\\' to be equal
   2024        && !((c1 == '/' && c2 == '\\')
   2025             || (c1 == '\\' && c2 == '/'))
   2026 #endif
   2027        ) {
   2028      if (vim_ispathsep(c1)) {
   2029        return -1;
   2030      }
   2031      if (vim_ispathsep(c2)) {
   2032        return 1;
   2033      }
   2034      return p_fic ? mb_toupper(c1) - mb_toupper(c2)
   2035                   : c1 - c2;  // no match
   2036    }
   2037 
   2038    i += utfc_ptr2len(p + i);
   2039    j += utfc_ptr2len(q + j);
   2040  }
   2041  if (s == NULL) {  // "i" or "j" ran into "maxlen"
   2042    return 0;
   2043  }
   2044 
   2045  int c1 = utf_ptr2char(s + i);
   2046  int c2 = utf_ptr2char(s + i + utfc_ptr2len(s + i));
   2047  // ignore a trailing slash, but not "//" or ":/"
   2048  if (c2 == NUL
   2049      && i > 0
   2050      && !after_pathsep(s, s + i)
   2051 #ifdef BACKSLASH_IN_FILENAME
   2052      && (c1 == '/' || c1 == '\\')
   2053 #else
   2054      && c1 == '/'
   2055 #endif
   2056      ) {
   2057    return 0;       // match with trailing slash
   2058  }
   2059  if (s == q) {
   2060    return -1;      // no match
   2061  }
   2062  return 1;
   2063 }
   2064 
   2065 /// Try to find a shortname by comparing the fullname with the current
   2066 /// directory.
   2067 ///
   2068 /// @param full_path The full path of the file.
   2069 /// @return
   2070 ///   - Pointer into `full_path` if shortened.
   2071 ///   - `full_path` unchanged if no shorter name is possible.
   2072 ///   - NULL if `full_path` is NULL.
   2073 char *path_try_shorten_fname(char *full_path)
   2074 {
   2075  char *dirname = xmalloc(MAXPATHL);
   2076  char *p = full_path;
   2077 
   2078  if (os_dirname(dirname, MAXPATHL) == OK) {
   2079    p = path_shorten_fname(full_path, dirname);
   2080    if (p == NULL || *p == NUL) {
   2081      p = full_path;
   2082    }
   2083  }
   2084  xfree(dirname);
   2085  return p;
   2086 }
   2087 
   2088 /// Try to find a shortname by comparing the fullname with `dir_name`.
   2089 ///
   2090 /// @param full_path The full path of the file.
   2091 /// @param dir_name The directory to shorten relative to.
   2092 /// @return
   2093 ///   - Pointer into `full_path` if shortened.
   2094 ///   - NULL if no shorter name is possible.
   2095 char *path_shorten_fname(char *full_path, char *dir_name)
   2096 {
   2097  if (full_path == NULL) {
   2098    return NULL;
   2099  }
   2100 
   2101  assert(dir_name != NULL);
   2102  size_t len = strlen(dir_name);
   2103 
   2104  // If full_path and dir_name do not match, it's impossible to make one
   2105  // relative to the other.
   2106  if (path_fnamencmp(dir_name, full_path, len) != 0) {
   2107    return NULL;
   2108  }
   2109 
   2110  // If dir_name is a path head, full_path can always be made relative.
   2111  if (len == (size_t)path_head_length() && is_path_head(dir_name)) {
   2112    return full_path + len;
   2113  }
   2114 
   2115  char *p = full_path + len;
   2116 
   2117  // If *p is not pointing to a path separator, this means that full_path's
   2118  // last directory name is longer than *dir_name's last directory, so they
   2119  // don't actually match.
   2120  if (!vim_ispathsep(*p)) {
   2121    return NULL;
   2122  }
   2123 
   2124  do {
   2125    p++;
   2126  } while (vim_ispathsep_nocolon(*p));
   2127  return p;
   2128 }
   2129 
   2130 /// Invoke expand_wildcards() for one pattern
   2131 ///
   2132 /// One should expand items like "%:h" before the expansion.
   2133 ///
   2134 /// @param[in]   pat       Pointer to the input pattern.
   2135 /// @param[out]  num_file  Resulting number of files.
   2136 /// @param[out]  file      Array of resulting files.
   2137 /// @param[in]   flags     Flags passed to expand_wildcards().
   2138 ///
   2139 /// @returns               OK when *file is set to allocated array of matches
   2140 ///                        and *num_file (can be zero) to the number of matches.
   2141 ///                        If FAIL is returned, *num_file and *file are either
   2142 ///                        unchanged or *num_file is set to 0 and *file is set
   2143 ///                        to NULL or points to "".
   2144 int expand_wildcards_eval(char **pat, int *num_file, char ***file, int flags)
   2145 {
   2146  int ret = FAIL;
   2147  char *eval_pat = NULL;
   2148  char *exp_pat = *pat;
   2149  const char *ignored_msg;
   2150  size_t usedlen;
   2151  const bool is_cur_alt_file = *exp_pat == '%' || *exp_pat == '#';
   2152  bool star_follows = false;
   2153 
   2154  if (is_cur_alt_file || *exp_pat == '<') {
   2155    emsg_off++;
   2156    eval_pat = eval_vars(exp_pat, exp_pat, &usedlen, NULL, &ignored_msg,
   2157                         NULL,
   2158                         true);
   2159    emsg_off--;
   2160    if (eval_pat != NULL) {
   2161      star_follows = strcmp(exp_pat + usedlen, "*") == 0;
   2162      exp_pat = concat_str(eval_pat, exp_pat + usedlen);
   2163    }
   2164  }
   2165 
   2166  if (exp_pat != NULL) {
   2167    ret = expand_wildcards(1, &exp_pat, num_file, file, flags);
   2168  }
   2169 
   2170  if (eval_pat != NULL) {
   2171    if (*num_file == 0 && is_cur_alt_file && star_follows) {
   2172      // Expanding "%" or "#" and the file does not exist: Add the
   2173      // pattern anyway (without the star) so that this works for remote
   2174      // files and non-file buffer names.
   2175      *file = xmalloc(sizeof(char *));
   2176      **file = eval_pat;
   2177      eval_pat = NULL;
   2178      *num_file = 1;
   2179      ret = OK;
   2180    }
   2181    xfree(exp_pat);
   2182    xfree(eval_pat);
   2183  }
   2184 
   2185  return ret;
   2186 }
   2187 
   2188 /// Expand wildcards. Calls gen_expand_wildcards() and removes files matching
   2189 /// 'wildignore'.
   2190 ///
   2191 /// @param      num_pat  is number of input patterns.
   2192 /// @param      pat      is an array of pointers to input patterns.
   2193 /// @param[out] num_file is pointer to number of matched file names.
   2194 /// @param[out] file     is pointer to array of pointers to matched file names.
   2195 /// @param      flags    is a combination of EW_* flags.
   2196 ///
   2197 /// @returns             OK when *file is set to allocated array of matches
   2198 ///                      and *num_file (can be zero) to the number of matches.
   2199 ///                      If FAIL is returned, *num_file and *file are either
   2200 ///                      unchanged or *num_file is set to 0 and *file is set to
   2201 ///                      NULL or points to "".
   2202 int expand_wildcards(int num_pat, char **pat, int *num_files, char ***files, int flags)
   2203 {
   2204  int retval = gen_expand_wildcards(num_pat, pat, num_files, files, flags);
   2205 
   2206  // When keeping all matches, return here
   2207  if ((flags & EW_KEEPALL) || retval == FAIL) {
   2208    return retval;
   2209  }
   2210 
   2211  // Remove names that match 'wildignore'.
   2212  if (*p_wig) {
   2213    // check all files in (*files)[]
   2214    assert(*num_files == 0 || *files != NULL);
   2215    for (int i = 0; i < *num_files; i++) {
   2216      char *ffname = FullName_save((*files)[i], false);
   2217      assert((*files)[i] != NULL);
   2218      assert(ffname != NULL);
   2219      if (match_file_list(p_wig, (*files)[i], ffname)) {
   2220        // remove this matching file from the list
   2221        xfree((*files)[i]);
   2222        for (int j = i; j + 1 < *num_files; j++) {
   2223          (*files)[j] = (*files)[j + 1];
   2224        }
   2225        (*num_files)--;
   2226        i--;
   2227      }
   2228      xfree(ffname);
   2229    }
   2230  }
   2231 
   2232  // Move the names where 'suffixes' match to the end.
   2233  // Skip when interrupted, the result probably won't be used.
   2234  assert(*num_files == 0 || *files != NULL);
   2235  if (*num_files > 1 && !got_int) {
   2236    int non_suf_match = 0;            // number without matching suffix
   2237    for (int i = 0; i < *num_files; i++) {
   2238      if (!match_suffix((*files)[i])) {
   2239        // Move the name without matching suffix to the front of the list.
   2240        char *p = (*files)[i];
   2241        for (int j = i; j > non_suf_match; j--) {
   2242          (*files)[j] = (*files)[j - 1];
   2243        }
   2244        (*files)[non_suf_match++] = p;
   2245      }
   2246    }
   2247  }
   2248 
   2249  // Free empty array of matches
   2250  if (*num_files == 0) {
   2251    XFREE_CLEAR(*files);
   2252    return FAIL;
   2253  }
   2254 
   2255  return retval;
   2256 }
   2257 
   2258 /// @return  true if "fname" matches with an entry in 'suffixes'.
   2259 bool match_suffix(char *fname)
   2260  FUNC_ATTR_NONNULL_ALL
   2261 {
   2262 #define MAXSUFLEN 30  // maximum length of a file suffix
   2263  char suf_buf[MAXSUFLEN];
   2264 
   2265  size_t fnamelen = strlen(fname);
   2266  size_t setsuflen = 0;
   2267  for (char *setsuf = p_su; *setsuf;) {
   2268    setsuflen = copy_option_part(&setsuf, suf_buf, MAXSUFLEN, ".,");
   2269    if (setsuflen == 0) {
   2270      char *tail = path_tail(fname);
   2271 
   2272      // empty entry: match name without a '.'
   2273      if (vim_strchr(tail, '.') == NULL) {
   2274        setsuflen = 1;
   2275        break;
   2276      }
   2277    } else {
   2278      if (fnamelen >= setsuflen
   2279          && path_fnamencmp(suf_buf, fname + fnamelen - setsuflen, setsuflen) == 0) {
   2280        break;
   2281      }
   2282      setsuflen = 0;
   2283    }
   2284  }
   2285  return setsuflen != 0;
   2286 }
   2287 
   2288 /// Get the absolute name of the given relative directory.
   2289 ///
   2290 /// @param directory Directory name, relative to current directory.
   2291 /// @return `FAIL` for failure, `OK` for success.
   2292 int path_full_dir_name(char *directory, char *buffer, size_t len)
   2293  FUNC_ATTR_NONNULL_ALL
   2294 {
   2295  if (strlen(directory) == 0) {
   2296    return os_dirname(buffer, len);
   2297  }
   2298 
   2299  if (os_realpath(directory, buffer, len) != NULL) {
   2300    return OK;
   2301  }
   2302 
   2303  // Path does not exist (yet).  For a full path fail, will use the path as-is.
   2304  if (path_is_absolute(directory)) {
   2305    return FAIL;
   2306  }
   2307  // For a relative path use the current directory and append the file name.
   2308 
   2309  char old_dir[MAXPATHL];
   2310 
   2311  // Get current directory name.
   2312  if (os_dirname(old_dir, MAXPATHL) == FAIL) {
   2313    return FAIL;
   2314  }
   2315 
   2316  xstrlcpy(buffer, old_dir, len);
   2317  if (append_path(buffer, directory, len) == FAIL) {
   2318    return FAIL;
   2319  }
   2320 
   2321  return OK;
   2322 }
   2323 
   2324 // Append to_append to path with a slash in between.
   2325 int append_path(char *path, const char *to_append, size_t max_len)
   2326  FUNC_ATTR_NONNULL_ALL
   2327 {
   2328  size_t current_length = strlen(path);
   2329  size_t to_append_length = strlen(to_append);
   2330 
   2331  // Do not append empty string or a dot.
   2332  if (to_append_length == 0 || strcmp(to_append, ".") == 0) {
   2333    return OK;
   2334  }
   2335 
   2336  // Combine the path segments, separated by a slash.
   2337  if (current_length > 0 && !vim_ispathsep_nocolon(path[current_length - 1])) {
   2338    // +1 for the NUL at the end.
   2339    if (current_length + STRLEN_LITERAL(PATHSEPSTR) + 1 > max_len) {
   2340      return FAIL;  // No space for trailing slash.
   2341    }
   2342    xstrlcpy(path + current_length, PATHSEPSTR, max_len - current_length);
   2343    current_length += STRLEN_LITERAL(PATHSEPSTR);
   2344  }
   2345 
   2346  // +1 for the NUL at the end.
   2347  if (current_length + to_append_length + 1 > max_len) {
   2348    return FAIL;
   2349  }
   2350 
   2351  xstrlcpy(path + current_length, to_append, max_len - current_length);
   2352  return OK;
   2353 }
   2354 
   2355 /// Used by `vim_FullName` and `fix_fname` to expand a filename to its full path.
   2356 ///
   2357 /// @param  fname  Filename to expand.
   2358 /// @param  buf    Where to store the absolute path of "fname".
   2359 /// @param  len    Length of `buf`.
   2360 /// @param  force  Also expand when `fname` is already absolute.
   2361 ///
   2362 /// @return FAIL for failure, OK for success.
   2363 static int path_to_absolute(const char *fname, char *buf, size_t len, int force)
   2364  FUNC_ATTR_NONNULL_ALL
   2365 {
   2366  const char *p;
   2367  *buf = NUL;
   2368 
   2369  char *relative_directory = xmalloc(len);
   2370  const char *end_of_path = fname;
   2371 
   2372  // expand it if forced or not an absolute path
   2373  if (force || !path_is_absolute(fname)
   2374 #ifdef MSWIN  // enforce drive letter on Windows paths
   2375      || fname[0] == '/' || fname[0] == '\\'
   2376 #endif
   2377      ) {
   2378    p = strrchr(fname, '/');
   2379 #ifdef MSWIN
   2380    if (p == NULL) {
   2381      p = strrchr(fname, '\\');
   2382    }
   2383 #endif
   2384    if (p == NULL && strcmp(fname, "..") == 0) {
   2385      // Handle ".." without path separators.
   2386      p = fname + 2;
   2387    }
   2388    if (p != NULL) {
   2389      if (vim_ispathsep_nocolon(*p) && strcmp(p + 1, "..") == 0) {
   2390        // For "/path/dir/.." include the "/..".
   2391        p += 3;
   2392      }
   2393      assert(p >= fname);
   2394      memcpy(relative_directory, fname, (size_t)(p - fname + 1));
   2395      relative_directory[p - fname + 1] = NUL;
   2396      end_of_path = (vim_ispathsep_nocolon(*p) ? p + 1 : p);
   2397    } else {
   2398      relative_directory[0] = NUL;
   2399    }
   2400 
   2401    if (FAIL == path_full_dir_name(relative_directory, buf, len)) {
   2402      xfree(relative_directory);
   2403      return FAIL;
   2404    }
   2405  }
   2406  xfree(relative_directory);
   2407  return append_path(buf, end_of_path, len);
   2408 }
   2409 
   2410 /// Check if file `fname` is a full (absolute) path.
   2411 ///
   2412 /// @return `true` if "fname" is absolute.
   2413 bool path_is_absolute(const char *fname)
   2414  FUNC_ATTR_NONNULL_ALL
   2415 {
   2416 #ifdef MSWIN
   2417  if (*fname == NUL) {
   2418    return false;
   2419  }
   2420  // A name like "d:/foo" and "//server/share" is absolute
   2421  // /foo and \foo are absolute too because Windows keeps a current drive.
   2422  return ((ASCII_ISALPHA(fname[0]) && fname[1] == ':' && vim_ispathsep_nocolon(fname[2]))
   2423          || vim_ispathsep_nocolon(fname[0]));
   2424 #else
   2425  // UNIX: This just checks if the file name starts with '/' or '~'.
   2426  return *fname == '/' || *fname == '~';
   2427 #endif
   2428 }
   2429 
   2430 /// Builds a full path from an invocation name `argv0`, based on heuristics.
   2431 ///
   2432 /// @param[in]  argv0     Name by which Nvim was invoked.
   2433 /// @param[out] buf       Guessed full path to `argv0`.
   2434 /// @param[in]  bufsize   Size of `buf`.
   2435 ///
   2436 /// @see os_exepath
   2437 void path_guess_exepath(const char *argv0, char *buf, size_t bufsize)
   2438  FUNC_ATTR_NONNULL_ALL
   2439 {
   2440  char *path = os_getenv("PATH");
   2441 
   2442  if (path == NULL || path_is_absolute(argv0)) {
   2443    xstrlcpy(buf, argv0, bufsize);
   2444  } else if (argv0[0] == '.' || strchr(argv0, PATHSEP)) {
   2445    // Relative to CWD.
   2446    if (os_dirname(buf, MAXPATHL) != OK) {
   2447      buf[0] = NUL;
   2448    }
   2449    xstrlcat(buf, PATHSEPSTR, bufsize);
   2450    xstrlcat(buf, argv0, bufsize);
   2451  } else {
   2452    // Search $PATH for plausible location.
   2453    const void *iter = NULL;
   2454    do {
   2455      const char *dir;
   2456      size_t dir_len;
   2457      iter = vim_env_iter(ENV_SEPCHAR, path, iter, &dir, &dir_len);
   2458      if (dir == NULL || dir_len == 0) {
   2459        break;
   2460      }
   2461      if (dir_len + 1 > sizeof(NameBuff)) {
   2462        continue;
   2463      }
   2464      xmemcpyz(NameBuff, dir, dir_len);
   2465      xstrlcat(NameBuff, PATHSEPSTR, sizeof(NameBuff));
   2466      xstrlcat(NameBuff, argv0, sizeof(NameBuff));
   2467      if (os_can_exe(NameBuff, NULL, false)) {
   2468        xstrlcpy(buf, NameBuff, bufsize);
   2469        return;
   2470      }
   2471    } while (iter != NULL);
   2472    // Not found in $PATH, fall back to argv0.
   2473    xstrlcpy(buf, argv0, bufsize);
   2474  }
   2475  xfree(path);
   2476 }