diff options
Diffstat (limited to 'src/nvim/path.c')
-rw-r--r-- | src/nvim/path.c | 57 |
1 files changed, 56 insertions, 1 deletions
diff --git a/src/nvim/path.c b/src/nvim/path.c index 25ed2e3fd8..e83b4e44ed 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -1617,13 +1617,68 @@ char_u *fix_fname(char_u *fname) fname = vim_strsave(fname); # ifdef USE_FNAME_CASE - fname_case(fname, 0); // set correct case for file name + path_fix_case(fname); // set correct case for file name # endif return fname; #endif } +/// Set the case of the file name, if it already exists. This will cause the +/// file name to remain exactly the same. +/// Only required for file systems where case is ignored and preserved. +// TODO(SplinterOfChaos): Could also be used when mounting case-insensitive +// file systems. +void path_fix_case(char_u *name) + FUNC_ATTR_NONNULL_ALL +{ + FileInfo file_info; + if (!os_fileinfo_link((char *)name, &file_info)) { + return; + } + + // Open the directory where the file is located. + char_u *slash = vim_strrchr(name, '/'); + char_u *tail; + Directory dir; + bool ok; + if (slash == NULL) { + ok = os_scandir(&dir, "."); + tail = name; + } else { + *slash = NUL; + ok = os_scandir(&dir, (char *) name); + *slash = '/'; + tail = slash + 1; + } + + if (!ok) { + return; + } + + char_u *entry; + while ((entry = (char_u *) os_scandir_next(&dir))) { + // Only accept names that differ in case and are the same byte + // length. TODO: accept different length name. + if (STRICMP(tail, entry) == 0 && STRLEN(tail) == STRLEN(entry)) { + char_u newname[MAXPATHL + 1]; + + // Verify the inode is equal. + STRLCPY(newname, name, MAXPATHL + 1); + STRLCPY(newname + (tail - name), entry, + MAXPATHL - (tail - name) + 1); + FileInfo file_info_new; + if (os_fileinfo_link((char *)newname, &file_info_new) + && os_fileinfo_id_equal(&file_info, &file_info_new)) { + STRCPY(tail, entry); + break; + } + } + } + + os_closedir(&dir); +} + /* * Return TRUE if "p" points to just after a path separator. * Takes care of multi-byte characters. |