diff options
-rw-r--r-- | CMakeLists.txt | 3 | ||||
-rw-r--r-- | config/config.h.in | 2 | ||||
-rw-r--r-- | src/nvim/buffer.c | 2 | ||||
-rw-r--r-- | src/nvim/charset.c | 5 | ||||
-rw-r--r-- | src/nvim/eval.c | 2 | ||||
-rw-r--r-- | src/nvim/ex_cmds.c | 2 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 4 | ||||
-rw-r--r-- | src/nvim/main.c | 4 | ||||
-rw-r--r-- | src/nvim/os/fs.c | 33 | ||||
-rw-r--r-- | src/nvim/os/fs_defs.h | 5 | ||||
-rw-r--r-- | src/nvim/os_unix.c | 93 | ||||
-rw-r--r-- | src/nvim/os_unix_defs.h | 19 | ||||
-rw-r--r-- | src/nvim/path.c | 221 | ||||
-rw-r--r-- | src/nvim/tag.c | 2 | ||||
-rw-r--r-- | test/unit/path_spec.lua | 24 |
15 files changed, 234 insertions, 187 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index c56e883f24..a026af7a1a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,9 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-isystem ") endif() + + # Enable fixing case-insensitive filenames for Mac. + set(USE_FNAME_CASE TRUE) endif() # Set available build types for CMake GUIs. diff --git a/config/config.h.in b/config/config.h.in index 8f3d154553..382b5c653d 100644 --- a/config/config.h.in +++ b/config/config.h.in @@ -15,7 +15,6 @@ #cmakedefine HAVE__NSGETENVIRON #cmakedefine HAVE_CRT_EXTERNS_H -#cmakedefine HAVE_DIRENT_H #cmakedefine HAVE_FCNTL_H #cmakedefine HAVE_FD_CLOEXEC #cmakedefine HAVE_FSEEKO @@ -60,6 +59,7 @@ #define SIGRETURN return #define TIME_WITH_SYS_TIME 1 #cmakedefine UNIX +#cmakedefine USE_FNAME_CASE #define USEMAN_S 1 #define FEAT_BROWSE diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index dd20d61f75..11cb7bdeac 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -2253,7 +2253,7 @@ setfname ( } sfname = vim_strsave(sfname); #ifdef USE_FNAME_CASE - fname_case(sfname, 0); /* set correct case for short file name */ + path_fix_case(sfname); /* set correct case for short file name */ #endif free(buf->b_ffname); free(buf->b_sfname); diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 5e11e202a3..4633bacb78 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -24,6 +24,7 @@ #include "nvim/move.h" #include "nvim/os_unix.h" #include "nvim/strings.h" +#include "nvim/path.h" #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -871,7 +872,7 @@ int vim_isfilec(int c) /// return TRUE if 'c' is a valid file-name character or a wildcard character /// Assume characters above 0x100 are valid (multi-byte). -/// Explicitly interpret ']' as a wildcard character as mch_has_wildcard("]") +/// Explicitly interpret ']' as a wildcard character as path_has_wildcard("]") /// returns false. /// /// @param c @@ -882,7 +883,7 @@ int vim_isfilec_or_wc(int c) char_u buf[2]; buf[0] = (char_u)c; buf[1] = NUL; - return vim_isfilec(c) || c == ']' || mch_has_wildcard(buf); + return vim_isfilec(c) || c == ']' || path_has_wildcard(buf); } /// return TRUE if 'c' is a printable character diff --git a/src/nvim/eval.c b/src/nvim/eval.c index deeca10310..9d8421ef04 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -9895,9 +9895,7 @@ static void f_has(typval_T *argvars, typval_T *rettv) #if defined(WIN64) || defined(_WIN64) "win64", #endif -#ifndef CASE_INSENSITIVE_FILENAME "fname_case", -#endif #ifdef HAVE_ACL "acl", #endif diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 81c7ecab5f..e687eab3c4 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -2563,7 +2563,7 @@ do_ecmd ( sfname = ffname; #ifdef USE_FNAME_CASE if (sfname != NULL) - fname_case(sfname, 0); /* set correct case for sfname */ + path_fix_case(sfname); // set correct case for sfname #endif if ((flags & ECMD_ADDBUF) && (ffname == NULL || *ffname == NUL)) diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 776ed844e9..db4f3a54f1 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -3432,7 +3432,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp) * the file name contains a wildcard it should not cause expanding. * (it will be expanded anyway if there is a wildcard before replacing). */ - has_wildcards = mch_has_wildcard(p); + has_wildcards = path_has_wildcard(p); while (*p != NUL) { /* Skip over `=expr`, wildcards in it are not expanded. */ if (p[0] == '`' && p[1] == '=') { @@ -3543,7 +3543,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp) || vim_strchr(eap->arg, '~') != NULL) { expand_env_esc(eap->arg, NameBuff, MAXPATHL, TRUE, TRUE, NULL); - has_wildcards = mch_has_wildcard(NameBuff); + has_wildcards = path_has_wildcard(NameBuff); p = NameBuff; } else p = NULL; diff --git a/src/nvim/main.c b/src/nvim/main.c index 55935564b8..a03fd2e8a9 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1290,8 +1290,8 @@ scripterror: } #ifdef USE_FNAME_CASE - /* Make the case of the file name match the actual file. */ - fname_case(p, 0); + // Make the case of the file name match the actual file. + path_fix_case(p); #endif alist_add(&global_alist, p, diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 6200685076..d583323b1f 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -348,6 +348,39 @@ int os_rmdir(const char *path) return result; } +/// Opens a directory. +/// @param[out] dir The Directory object. +/// @param path Path to the directory. +/// @returns true if dir contains one or more items, false if not or an error +/// occurred. +bool os_scandir(Directory *dir, const char *path) + FUNC_ATTR_NONNULL_ALL +{ + int r = uv_fs_scandir(uv_default_loop(), &dir->request, path, 0, NULL); + if (r <= 0) { + os_closedir(dir); + } + return r > 0; +} + +/// Increments the directory pointer. +/// @param dir The Directory object. +/// @returns a pointer to the next path in `dir` or `NULL`. +const char *os_scandir_next(Directory *dir) + FUNC_ATTR_NONNULL_ALL +{ + int err = uv_fs_scandir_next(&dir->request, &dir->ent); + return err != UV_EOF ? dir->ent.name : NULL; +} + +/// Frees memory associated with `os_scandir()`. +/// @param dir The directory. +void os_closedir(Directory *dir) + FUNC_ATTR_NONNULL_ALL +{ + uv_fs_req_cleanup(&dir->request); +} + /// Remove a file. /// /// @return `0` for success, non-zero for failure. diff --git a/src/nvim/os/fs_defs.h b/src/nvim/os/fs_defs.h index e4d79aab66..ddd382a3cb 100644 --- a/src/nvim/os/fs_defs.h +++ b/src/nvim/os/fs_defs.h @@ -16,4 +16,9 @@ typedef struct { #define FILE_ID_EMPTY (FileID) {.inode = 0, .device_id = 0} +typedef struct { + uv_fs_t request; ///< @private The request to uv for the directory. + uv_dirent_t ent; ///< @private The entry information. +} Directory; + #endif // NVIM_OS_FS_DEFS_H diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index 8e4569033a..70cafc62ca 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -69,62 +69,6 @@ static int selinux_enabled = -1; # include "os_unix.c.generated.h" #endif -#if defined(USE_FNAME_CASE) -/* - * 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. - */ -void fname_case( -char_u *name, -int len /* buffer size, only used when name gets longer */ -) -{ - char_u *slash, *tail; - DIR *dirp; - struct dirent *dp; - - FileInfo file_info; - if (os_fileinfo_link((char *)name, &file_info)) { - /* Open the directory where the file is located. */ - slash = vim_strrchr(name, '/'); - if (slash == NULL) { - dirp = opendir("."); - tail = name; - } else { - *slash = NUL; - dirp = opendir((char *)name); - *slash = '/'; - tail = slash + 1; - } - - if (dirp != NULL) { - while ((dp = readdir(dirp)) != NULL) { - /* Only accept names that differ in case and are the same byte - * length. TODO: accept different length name. */ - if (STRICMP(tail, dp->d_name) == 0 - && STRLEN(tail) == STRLEN(dp->d_name)) { - char_u newname[MAXPATHL + 1]; - - /* Verify the inode is equal. */ - STRLCPY(newname, name, MAXPATHL + 1); - STRLCPY(newname + (tail - name), dp->d_name, - 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, dp->d_name); - break; - } - } - } - - closedir(dirp); - } - } -} -#endif - #if defined(HAVE_ACL) # ifdef HAVE_SYS_ACL_H # include <sys/acl.h> @@ -719,47 +663,12 @@ static void save_patterns(int num_pat, char_u **pat, int *num_file, *num_file = num_pat; } -/* - * Return TRUE if the string "p" contains a wildcard that mch_expandpath() can - * expand. - */ -int mch_has_exp_wildcard(char_u *p) -{ - for (; *p; mb_ptr_adv(p)) { - if (*p == '\\' && p[1] != NUL) - ++p; - else if (vim_strchr((char_u *) - "*?[{'" - , *p) != NULL) - return TRUE; - } - return FALSE; -} - -/* - * Return TRUE if the string "p" contains a wildcard. - * Don't recognize '~' at the end as a wildcard. - */ -int mch_has_wildcard(char_u *p) -{ - for (; *p; mb_ptr_adv(p)) { - if (*p == '\\' && p[1] != NUL) - ++p; - else if (vim_strchr((char_u *) - "*?[{`'$" - , *p) != NULL - || (*p == '~' && p[1] != NUL)) - return TRUE; - } - return FALSE; -} - static int have_wildcard(int num, char_u **file) { int i; for (i = 0; i < num; i++) - if (mch_has_wildcard(file[i])) + if (path_has_wildcard(file[i])) return 1; return 0; } diff --git a/src/nvim/os_unix_defs.h b/src/nvim/os_unix_defs.h index 4ffd23aa25..24b069b090 100644 --- a/src/nvim/os_unix_defs.h +++ b/src/nvim/os_unix_defs.h @@ -44,25 +44,6 @@ # define SIGDUMMYARG #endif -#ifdef HAVE_DIRENT_H -# include <dirent.h> -# ifndef NAMLEN -# define NAMLEN(dirent) strlen((dirent)->d_name) -# endif -#else -# define dirent direct -# define NAMLEN(dirent) (dirent)->d_namlen -# if HAVE_SYS_NDIR_H -# include <sys/ndir.h> -# endif -# if HAVE_SYS_DIR_H -# include <sys/dir.h> -# endif -# if HAVE_NDIR_H -# include <ndir.h> -# endif -#endif - #if !defined(HAVE_SYS_TIME_H) || defined(TIME_WITH_SYS_TIME) # include <time.h> /* on some systems time.h should not be included together with sys/time.h */ diff --git a/src/nvim/path.c b/src/nvim/path.c index 346be9108d..e83b4e44ed 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -400,6 +400,32 @@ char_u *save_absolute_path(const char_u *name) return vim_strsave((char_u *) name); } +/// Checks if a path has a wildcard character including '~', unless at the end. +/// @param p The path to expand. +/// @returns Unix: True if it contains one of "?[{`'$". +/// @returns Windows: True if it contains one of "*?$[". +bool path_has_wildcard(const char_u *p) + FUNC_ATTR_NONNULL_ALL +{ + for (; *p; mb_ptr_adv(p)) { +#if defined(UNIX) + if (p[0] == '\\' && p[1] != NUL) { + p++; + continue; + } + + const char *wildcards = "*?[{`'$"; +#else + // Windows: + const char *wildcards = "?*$[`"; +#endif + if (vim_strchr((char_u *)wildcards, *p) != NULL + || (p[0] == '~' && p[1] != NUL)) { + return true; + } + } + return false; +} #if defined(UNIX) /* @@ -410,39 +436,67 @@ static int pstrcmp(const void *a, const void *b) { return pathcmp(*(char **)a, *(char **)b, -1); } +#endif -/* - * Recursively expand one path component into all matching files and/or - * directories. Adds matches to "gap". Handles "*", "?", "[a-z]", "**", etc. - * "path" has backslashes before chars that are not to be expanded, starting - * at "path + wildoff". - * Return the number of matches found. - * NOTE: much of this is identical to dos_expandpath(), keep in sync! - */ -int -unix_expandpath ( - garray_T *gap, - char_u *path, - int wildoff, - int flags, /* EW_* flags */ - int didstar /* expanded "**" once already */ -) +/// Checks if a path has a character path_expand can expand. +/// @param p The path to expand. +/// @returns Unix: True if it contains one of *?[{. +/// @returns Windows: True if it contains one of *?[. +bool path_has_exp_wildcard(const char_u *p) + FUNC_ATTR_NONNULL_ALL +{ + for (; *p != NUL; mb_ptr_adv(p)) { +#if defined(UNIX) + if (p[0] == '\\' && p[1] != NUL) { + p++; + continue; + } + + const char *wildcards = "*?[{"; +#else + const char *wildcards = "*?["; // Windows. +#endif + if (vim_strchr((char_u *) wildcards, *p) != NULL) { + return true; + } + } + return false; +} + +/// Recursively expands one path component into all matching files and/or +/// directories. Handles "*", "?", "[a-z]", "**", etc. +/// @remark "**" in `path` requests recursive expansion. +/// +/// @param[out] gap The matches found. +/// @param path The path to search. +/// @param flags Flags for regexp expansion. +/// - EW_ICASE: Ignore case. +/// - EW_NOERROR: Silence error messeges. +/// - EW_NOTWILD: Add matches literally. +/// @returns the number of matches found. +static size_t path_expand(garray_T *gap, const char_u *path, int flags) + FUNC_ATTR_NONNULL_ALL +{ + return do_path_expand(gap, path, 0, flags, false); +} + +/// Implementation of path_expand(). +/// +/// Chars before `path + wildoff` do not get expanded. +static size_t do_path_expand(garray_T *gap, const char_u *path, + size_t wildoff, int flags, bool didstar) + FUNC_ATTR_NONNULL_ALL { char_u *buf; - char_u *path_end; char_u *p, *s, *e; int start_len = gap->ga_len; char_u *pat; - regmatch_T regmatch; int starts_with_dot; int matches; int len; int starstar = FALSE; static int stardepth = 0; /* depth for "**" expansion */ - DIR *dirp; - struct dirent *dp; - /* Expanding "**" may take a long time, check for CTRL-C. */ if (stardepth > 0) { os_breakcheck(); @@ -461,7 +515,7 @@ unix_expandpath ( p = buf; s = buf; e = NULL; - path_end = path; + const char_u *path_end = path; while (*path_end != NUL) { /* May ignore a wildcard that has a backslash before it; it will * be removed by rem_backslash() or file_pat_to_reg_pat() below. */ @@ -510,11 +564,14 @@ unix_expandpath ( return 0; } - /* compile the regexp into a program */ - if (flags & EW_ICASE) - regmatch.rm_ic = TRUE; /* 'wildignorecase' set */ - else - regmatch.rm_ic = p_fic; /* ignore case when 'fileignorecase' is set */ + // compile the regexp into a program + regmatch_T regmatch; +#if defined(UNIX) + // Ignore case if given 'wildignorecase', else respect 'fileignorecase'. + regmatch.rm_ic = (flags & EW_ICASE) || p_fic; +#else + regmatch.rm_ic = true; // Always ignore case on Windows. +#endif if (flags & (EW_NOERROR | EW_NOTWILD)) ++emsg_silent; regmatch.regprog = vim_regcomp(pat, RE_MAGIC); @@ -533,26 +590,22 @@ unix_expandpath ( && *path_end == '/') { STRCPY(s, path_end + 1); ++stardepth; - (void)unix_expandpath(gap, buf, (int)(s - buf), flags, TRUE); + (void)do_path_expand(gap, buf, (int)(s - buf), flags, TRUE); --stardepth; } - - /* open the directory for scanning */ *s = NUL; - dirp = opendir(*buf == NUL ? "." : (char *)buf); - /* Find all matching entries */ - if (dirp != NULL) { - for (;; ) { - dp = readdir(dirp); - if (dp == NULL) - break; - if ((dp->d_name[0] != '.' || starts_with_dot) - && ((regmatch.regprog != NULL && vim_regexec(®match, - (char_u *)dp->d_name, (colnr_T)0)) + Directory dir; + // open the directory for scanning + if (os_scandir(&dir, *buf == NUL ? "." : (char *)buf)) { + // Find all matching entries. + char_u *name; + while((name = (char_u *) os_scandir_next(&dir))) { + if ((name[0] != '.' || starts_with_dot) + && ((regmatch.regprog != NULL && vim_regexec(®match, name, 0)) || ((flags & EW_NOTWILD) - && fnamencmp(path + (s - buf), dp->d_name, e - s) == 0))) { - STRCPY(s, dp->d_name); + && fnamencmp(path + (s - buf), name, e - s) == 0))) { + STRCPY(s, name); len = STRLEN(buf); if (starstar && stardepth < 100) { @@ -561,15 +614,15 @@ unix_expandpath ( STRCPY(buf + len, "/**"); STRCPY(buf + len + 3, path_end); ++stardepth; - (void)unix_expandpath(gap, buf, len + 1, flags, TRUE); + (void)do_path_expand(gap, buf, len + 1, flags, true); --stardepth; } STRCPY(buf + len, path_end); - if (mch_has_exp_wildcard(path_end)) { /* handle more wildcards */ + if (path_has_exp_wildcard(path_end)) { /* handle more wildcards */ /* need to expand another component of the path */ /* remove backslashes for the remaining components only */ - (void)unix_expandpath(gap, buf, len + 1, flags, FALSE); + (void)do_path_expand(gap, buf, len + 1, flags, false); } else { /* no more wildcards, check if there is a match */ /* remove backslashes for the remaining components only */ @@ -581,8 +634,7 @@ unix_expandpath ( } } } - - closedir(dirp); + os_closedir(&dir); } free(buf); @@ -594,7 +646,6 @@ unix_expandpath ( sizeof(char_u *), pstrcmp); return matches; } -#endif /* * Moves "*psep" back to the previous path separator in "path". @@ -1078,7 +1129,7 @@ gen_expand_wildcards ( * If there are no wildcards: Add the file name if it exists or * when EW_NOTFOUND is given. */ - if (mch_has_exp_wildcard(p)) { + if (path_has_exp_wildcard(p)) { if ((flags & EW_PATH) && !path_is_absolute_path(p) && !(p[0] == '.' @@ -1092,7 +1143,7 @@ gen_expand_wildcards ( recursive = TRUE; did_expand_in_path = TRUE; } else - add_pat = mch_expandpath(&ga, p, flags); + add_pat = path_expand(&ga, p, flags); } } @@ -1566,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. @@ -1670,19 +1776,6 @@ int pathcmp(const char *p, const char *q, int maxlen) return 1; } -/* - * Expand a path into all matching files and/or directories. Handles "*", - * "?", "[a-z]", "**", etc. - * "path" has backslashes before chars that are not to be expanded. - * Returns the number of matches found. - * - * Uses EW_* flags - */ -int mch_expandpath(garray_T *gap, char_u *path, int flags) -{ - return unix_expandpath(gap, path, 0, flags, FALSE); -} - /// Try to find a shortname by comparing the fullname with the current /// directory. /// diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 361afb7e07..1f6b42d9cf 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -2587,7 +2587,7 @@ static char_u *expand_tag_fname(char_u *fname, char_u *tag_fname, int expand) /* * Expand file name (for environment variables) when needed. */ - if (expand && mch_has_wildcard(fname)) { + if (expand && path_has_wildcard(fname)) { ExpandInit(&xpc); xpc.xp_context = EXPAND_FILES; expanded_fname = ExpandOne(&xpc, fname, NULL, diff --git a/test/unit/path_spec.lua b/test/unit/path_spec.lua index 1824eaeccc..13546a6183 100644 --- a/test/unit/path_spec.lua +++ b/test/unit/path_spec.lua @@ -418,6 +418,30 @@ describe('more path function', function() end) end) + describe('path_fix_case', function() + function fix_case(file) + c_file = to_cstr(file) + path.path_fix_case(c_file) + return ffi.string(c_file) + end + + if ffi.os == 'Windows' or ffi.os == 'OSX' then + it('Corrects the case of file names in Mac and Windows', function() + lfs.mkdir('CamelCase') + eq('CamelCase', fix_case('camelcase')) + eq('CamelCase', fix_case('cAMELcASE')) + lfs.rmdir('CamelCase') + end) + else + it('does nothing on Linux', function() + lfs.mkdir('CamelCase') + eq('camelcase', fix_case('camelcase')) + eq('cAMELcASE', fix_case('cAMELcASE')) + lfs.mkdir('CamelCase') + end) + end + end) + describe('append_path', function() it('joins given paths with a slash', function() local path1 = cstr(100, 'path1') |