diff options
-rw-r--r-- | src/buffer.c | 2 | ||||
-rw-r--r-- | src/eval.c | 2 | ||||
-rw-r--r-- | src/ex_getln.c | 2 | ||||
-rw-r--r-- | src/fileio.c | 2 | ||||
-rw-r--r-- | src/memline.c | 2 | ||||
-rw-r--r-- | src/misc1.c | 6 | ||||
-rw-r--r-- | src/os/fs.c | 163 | ||||
-rw-r--r-- | src/os/os.h | 4 | ||||
-rw-r--r-- | src/os_unix.c | 2 | ||||
-rw-r--r-- | src/syntax.c | 2 | ||||
-rw-r--r-- | src/window.c | 4 | ||||
-rw-r--r-- | test/unit/os/fs.moon | 198 |
12 files changed, 265 insertions, 124 deletions
diff --git a/src/buffer.c b/src/buffer.c index 1c1c0dc6d9..4a13e4bcc9 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -2418,7 +2418,7 @@ void buflist_altfpos(win_T *win) /* * Return TRUE if 'ffname' is not the same file as current file. - * Fname must have a full path (expanded by mch_FullName()). + * Fname must have a full path (expanded by mch_full_name()). */ int otherfile(char_u *ffname) { diff --git a/src/eval.c b/src/eval.c index 08496e05bd..4eee644536 100644 --- a/src/eval.c +++ b/src/eval.c @@ -12467,7 +12467,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv) q[-1] = NUL; q = gettail(p); } - if (q > p && !mch_isFullName(buf)) { + if (q > p && !mch_is_full_name(buf)) { /* symlink is relative to directory of argument */ cpy = alloc((unsigned)(STRLEN(p) + STRLEN(buf) + 1)); if (cpy != NULL) { diff --git a/src/ex_getln.c b/src/ex_getln.c index dabe2e49e2..53f30471f5 100644 --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -3944,7 +3944,7 @@ expand_shellcmd ( flags |= EW_FILE | EW_EXEC; /* For an absolute name we don't use $PATH. */ - if (mch_isFullName(pat)) + if (mch_is_full_name(pat)) path = (char_u *)" "; else if ((pat[0] == '.' && (vim_ispathsep(pat[1]) || (pat[1] == '.' && vim_ispathsep(pat[2]))))) diff --git a/src/fileio.c b/src/fileio.c index 54631ad34a..4613c7cf1a 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -4769,7 +4769,7 @@ void shorten_fnames(int force) && !path_with_url(buf->b_fname) && (force || buf->b_sfname == NULL - || mch_isFullName(buf->b_sfname))) { + || mch_is_full_name(buf->b_sfname))) { vim_free(buf->b_sfname); buf->b_sfname = NULL; p = shorten_fname(buf->b_ffname, dirname); diff --git a/src/memline.c b/src/memline.c index 7ccd39b9fc..6fb29c2ddb 100644 --- a/src/memline.c +++ b/src/memline.c @@ -3381,7 +3381,7 @@ int resolve_symlink(char_u *fname, char_u *buf) * portion of the filename (if any) and the path the symlink * points to. */ - if (mch_isFullName(buf)) + if (mch_is_full_name(buf)) STRCPY(tmp, buf); else { char_u *tail; diff --git a/src/misc1.c b/src/misc1.c index 0fca08188b..770e870069 100644 --- a/src/misc1.c +++ b/src/misc1.c @@ -7945,7 +7945,7 @@ static void expand_path_option(char_u *curdir, garray_T *gap) else if (path_with_url(buf)) /* URL can't be used here */ continue; - else if (!mch_isFullName(buf)) { + else if (!mch_is_full_name(buf)) { /* Expand relative path to their full path equivalent */ len = (int)STRLEN(curdir); if (len + (int)STRLEN(buf) + 3 > MAXPATHL) @@ -8086,7 +8086,7 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern) break; } - if (mch_isFullName(path)) { + if (mch_is_full_name(path)) { /* * Last resort: shorten relative to curdir if possible. * 'possible' means: @@ -8375,7 +8375,7 @@ gen_expand_wildcards ( */ if (mch_has_exp_wildcard(p)) { if ((flags & EW_PATH) - && !mch_isFullName(p) + && !mch_is_full_name(p) && !(p[0] == '.' && (vim_ispathsep(p[1]) || (p[1] == '.' && vim_ispathsep(p[2])))) diff --git a/src/os/fs.c b/src/os/fs.c index bc9a495984..0b83ca319e 100644 --- a/src/os/fs.c +++ b/src/os/fs.c @@ -1,4 +1,4 @@ -/* vi:set ts=8 sts=4 sw=4: +/* vi:set ts=2 sts=2 sw=2: * * VIM - Vi IMproved by Bram Moolenaar * @@ -33,96 +33,129 @@ int mch_chdir(char *path) { int mch_dirname(char_u *buf, int len) { int errno; - if ((errno = uv_cwd((char *)buf, len)) != 0) { + if ((errno = uv_cwd((char *) buf, len)) != 0) { STRCPY(buf, uv_strerror(errno)); return FAIL; } return OK; } -/* - * Get absolute file name into "buf[len]". +/* + * Get the absolute name of the given relative directory. * + * parameter directory: Directory name, relative to current directory. * return FAIL for failure, OK for success */ -int mch_FullName( - char_u *fname, - char_u *buf, - int len, - int force /* also expand when already absolute path */ - ) +int mch_full_dir_name(char *directory, char *buffer, int len) { - int l; - char_u olddir[MAXPATHL]; - char_u *p; int retval = OK; + if(0 == STRLEN(directory)) { + return mch_dirname((char_u *) buffer, len); + } + char old_dir[MAXPATHL]; - /* expand it if forced or not an absolute path */ - if (force || !mch_isFullName(fname)) { - /* - * If the file name has a path, change to that directory for a moment, - * and then do the getwd() (and get back to where we were). - * This will get the correct path name with "../" things. - */ - if ((p = vim_strrchr(fname, '/')) != NULL) { + /* Get current directory name. */ + if (FAIL == mch_dirname((char_u *) old_dir, MAXPATHL)) { + return FAIL; + } - /* Only change directory when we are sure we can return to where - * we are now. After doing "su" chdir(".") might not work. */ - if ((mch_dirname(olddir, MAXPATHL) == FAIL - || mch_chdir((char *)olddir) != 0)) { - p = NULL; /* can't get current dir: don't chdir */ - retval = FAIL; - } else { - /* The directory is copied into buf[], to be able to remove - * the file name without changing it (could be a string in - * read-only memory) */ - if (p - fname >= len) - retval = FAIL; - else { - vim_strncpy(buf, fname, p - fname); - if (mch_chdir((char *)buf)) - retval = FAIL; - else - fname = p + 1; - *buf = NUL; - } - } - } - if (mch_dirname(buf, len) == FAIL) { - retval = FAIL; - *buf = NUL; - } - if (p != NULL) { - l = mch_chdir((char *)olddir); - if (l != 0) - EMSG(_(e_prev_dir)); - } + /* We have to get back to the current dir at the end, check if that works. */ + if (0 != mch_chdir(old_dir)) { + return FAIL; + } - l = STRLEN(buf); - if (l >= len - 1) - retval = FAIL; /* no space for trailing "/" */ - else if (l > 0 && buf[l - 1] != '/' && *fname != NUL - && STRCMP(fname, ".") != 0) - STRCAT(buf, "/"); + if (0 != mch_chdir(directory)) { + retval = FAIL; } - /* Catch file names which are too long. */ - if (retval == FAIL || (int)(STRLEN(buf) + STRLEN(fname)) >= len) - return FAIL; + if ((FAIL == retval) || (FAIL == mch_dirname((char_u *) buffer, len))) { + retval = FAIL; + } + + if (0 != mch_chdir(old_dir)) { + /* That shouldn't happen, since we've tested if it works. */ + retval = FAIL; + EMSG(_(e_prev_dir)); + } + return retval; +} + +/* + * Append to_append to path with a slash in between. + */ +int append_path(char *path, char *to_append, int max_len) +{ + int current_length = STRLEN(path); + int to_append_length = STRLEN(to_append); + + /* Do not append empty strings. */ + if (0 == to_append_length) + return OK; + + /* Do not append a dot. */ + if (STRCMP(to_append, ".") == 0) + return OK; - /* Do not append ".", "/dir/." is equal to "/dir". */ - if (STRCMP(fname, ".") != 0) - STRCAT(buf, fname); + /* Glue both paths with a slash. */ + if (current_length > 0 && path[current_length-1] != '/') { + current_length += 1; /* Count the trailing slash. */ + if (current_length > max_len) + return FAIL; + + STRCAT(path, "/"); + } + + /* +1 for the NUL at the end. */ + if (current_length + to_append_length +1 > max_len) { + return FAIL; + } + + STRCAT(path, to_append); return OK; } /* + * Get absolute file name into "buf[len]". + * + * parameter force: Also expand when the given path in fname is already + * absolute. + * + * return FAIL for failure, OK for success + */ +int mch_full_name(char_u *fname, char_u *buf, int len, int force) +{ + char_u *p; + *buf = NUL; + + char relative_directory[len]; + char *end_of_path = (char *) fname; + + /* expand it if forced or not an absolute path */ + if (force || !mch_is_full_name(fname)) { + if ((p = vim_strrchr(fname, '/')) != NULL) { + + STRNCPY(relative_directory, fname, p-fname); + relative_directory[p-fname] = NUL; + end_of_path = (char *) (p + 1); + } else { + relative_directory[0] = NUL; + end_of_path = (char *) fname; + } + + if (FAIL == mch_full_dir_name(relative_directory, (char *) buf, len)) { + return FAIL; + } + } + return append_path((char *) buf, (char *) end_of_path, len); +} + +/* * Return TRUE if "fname" does not depend on the current directory. */ -int mch_isFullName(char_u *fname) +int mch_is_full_name(char_u *fname) { return *fname == '/' || *fname == '~'; } diff --git a/src/os/os.h b/src/os/os.h index 9cf2116091..1ef311b32c 100644 --- a/src/os/os.h +++ b/src/os/os.h @@ -6,7 +6,7 @@ long_u mch_total_mem(int special); int mch_chdir(char *path); int mch_dirname(char_u *buf, int len); -int mch_FullName (char_u *fname, char_u *buf, int len, int force); -int mch_isFullName (char_u *fname); +int mch_full_name (char_u *fname, char_u *buf, int len, int force); +int mch_is_full_name (char_u *fname); #endif diff --git a/src/os_unix.c b/src/os_unix.c index 80832024c9..003e45c2a0 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -1364,7 +1364,7 @@ int mch_can_exe(char_u *name) int retval; /* If it's an absolute or relative path don't need to use $PATH. */ - if (mch_isFullName(name) || (name[0] == '.' && (name[1] == '/' + if (mch_is_full_name(name) || (name[0] == '.' && (name[1] == '/' || (name[1] == '.' && name[2] == '/')))) return executable_file(name); diff --git a/src/syntax.c b/src/syntax.c index fb3285729f..ba33f16e23 100644 --- a/src/syntax.c +++ b/src/syntax.c @@ -4187,7 +4187,7 @@ static void syn_cmd_include(exarg_T *eap, int syncing) */ eap->argt |= (XFILE | NOSPC); separate_nextcmd(eap); - if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg)) { + if (*eap->arg == '<' || *eap->arg == '$' || mch_is_full_name(eap->arg)) { /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the * file. Need to expand the file name first. In other cases * ":runtime!" is used. */ diff --git a/src/window.c b/src/window.c index cd96365902..16acf013ab 100644 --- a/src/window.c +++ b/src/window.c @@ -5065,7 +5065,7 @@ int path_with_url(char_u *fname) */ int vim_isAbsName(char_u *name) { - return path_with_url(name) != 0 || mch_isFullName(name); + return path_with_url(name) != 0 || mch_is_full_name(name); } /* @@ -5090,7 +5090,7 @@ vim_FullName ( url = path_with_url(fname); if (!url) - retval = mch_FullName(fname, buf, len, force); + retval = mch_full_name(fname, buf, len, force); if (url || retval == FAIL) { /* something failed; use the file name (truncate when too long) */ vim_strncpy(buf, fname, len - 1); diff --git a/test/unit/os/fs.moon b/test/unit/os/fs.moon index cb4abdba7e..8c1b9618db 100644 --- a/test/unit/os/fs.moon +++ b/test/unit/os/fs.moon @@ -5,87 +5,195 @@ fs = cimport './src/fs.h' cstr = ffi.typeof 'char[?]' describe 'fs function', -> + + export OK = 1 + export FAIL = 0 + describe 'mch_dirname', -> ffi.cdef 'int mch_dirname(char *buf, int len);' mch_dirname = (buf, len) -> fs.mch_dirname buf, len - OK = 1 - FAIL = 0 - before_each -> - export len = string.len(lfs.currentdir()) + 1 + export len = (string.len lfs.currentdir!) + 1 + export buf = cstr len, '' - it 'returns OK and writes current directory into the buffer if it is buffer - is large enough', -> - buf = ffi.new 'char[?]', len + it 'returns OK and writes current directory into the buffer if it is large + enough', -> eq OK, (mch_dirname buf, len) - eq lfs.currentdir(), ffi.string(buf) + eq lfs.currentdir!, (ffi.string buf) -- What kind of other failing cases are possible? it 'returns FAIL if the buffer is too small', -> - buf = ffi.new 'char[?]', 0 - eq FAIL, (mch_dirname buf, 0) + buf = cstr (len-1), '' + eq FAIL, (mch_dirname buf, (len-1)) + + describe 'mch_full_dir_name', -> + ffi.cdef 'int mch_full_dir_name(char *directory, char *buffer, int len);' - describe 'mch_FullName', -> - ffi.cdef 'int mch_FullName(char *fname, char *buf, int len, int force);' + mch_full_dir_name = (directory, buffer, len) -> + directory = cstr (string.len directory), directory + fs.mch_full_dir_name(directory, buffer, len) + + before_each -> + -- Create empty string buffer which will contain the resulting path. + export len = (string.len lfs.currentdir!) + 22 + export buffer = cstr len, '' + + it 'returns the absolute directory name of a given relative one', -> + result = mch_full_dir_name '..', buffer, len + eq OK, result + old_dir = lfs.currentdir! + lfs.chdir '..' + expected = lfs.currentdir! + lfs.chdir old_dir + eq expected, (ffi.string buffer) + + it 'returns the current directory name if the given string is empty', -> + eq OK, (mch_full_dir_name '', buffer, len) + eq lfs.currentdir!, (ffi.string buffer) + + it 'fails if the given directory does not exist', -> + eq FAIL, mch_full_dir_name('does_not_exist', buffer, len) + + it 'works with a normal relative dir', -> + lfs.mkdir('empty-test-directory') + result = mch_full_dir_name('empty-test-directory', buffer, len) + lfs.rmdir('empty-test-directory') + eq lfs.currentdir! .. '/empty-test-directory', ffi.string(buffer) + eq OK, result - mch_FullName = (filename, buffer, length, force) -> - filename = cstr(string.len(filename) + 1, filename) - fs.mch_FullName(filename, buffer, length, force) + describe 'mch_full_name', -> + ffi.cdef 'int mch_full_name(char *fname, char *buf, int len, int force);' - OK = 1 - FAIL = 0 + mch_full_name = (filename, buffer, length, force) -> + filename = cstr (string.len filename) + 1, filename + fs.mch_full_name filename, buffer, length, force before_each -> - -- Create empty string buffer which will contain the resulting path - export len = string.len(lfs.currentdir()) + 11 - export buffer = cstr(len, '') + -- Create empty string buffer which will contain the resulting path. + export len = (string.len lfs.currentdir!) + 33 + export buffer = cstr len, '' + + -- Create a directory and an empty file inside in order to know some + -- existing relative path. + lfs.mkdir('empty-test-directory') + lfs.touch('empty-test-directory/empty.file') - it 'failes if given filename contains non-existing directory', -> - result = mch_FullName('nonexistingdir/test.file', buffer, len, 1) + after_each -> + lfs.rmdir('empty-test-directory') + + it 'fails if given filename contains non-existing directory', -> + force_expansion = 1 + result = mch_full_name 'non_existing_dir/test.file', buffer, len, force_expansion eq FAIL, result it 'concatenates given filename if it does not contain a slash', -> - result = mch_FullName('test.file', buffer, len, 1) + force_expansion = 1 + result = mch_full_name 'test.file', buffer, len, force_expansion + expected = lfs.currentdir! .. '/test.file' + eq expected, (ffi.string buffer) eq OK, result - expected = lfs.currentdir() .. '/test.file' - eq expected, ffi.string(buffer) it 'concatenates given filename if it is a directory but does not contain a slash', -> - result = mch_FullName('..', buffer, len, 1) + force_expansion = 1 + result = mch_full_name '..', buffer, len, force_expansion + expected = lfs.currentdir! .. '/..' + eq expected, (ffi.string buffer) eq OK, result - expected = lfs.currentdir() .. '/..' - eq expected, ffi.string(buffer) -- Is it possible for every developer to enter '..' directory while running -- the unit tests? Which other directory would be better? - it 'enters given directory if possible and if path contains a slash', -> - result = mch_FullName('../test.file', buffer, 200, 1) + it 'enters given directory (instead of just concatenating the strings) if + possible and if path contains a slash', -> + force_expansion = 1 + result = mch_full_name '../test.file', buffer, len, force_expansion + old_dir = lfs.currentdir! + lfs.chdir '..' + expected = lfs.currentdir! .. '/test.file' + lfs.chdir old_dir + eq expected, (ffi.string buffer) + eq OK, result + + it 'just copies the path if it is already absolute and force=0', -> + force_expansion = 0 + absolute_path = '/absolute/path' + result = mch_full_name absolute_path, buffer, len, force_expansion + eq absolute_path, (ffi.string buffer) eq OK, result - old_dir = lfs.currentdir() - lfs.chdir('..') - expected = lfs.currentdir() .. '/test.file' - lfs.chdir(old_dir) - eq expected, ffi.string(buffer) - describe 'mch_isFullName', -> - ffi.cdef 'int mch_isFullName(char *fname);' + it 'fails when the path is relative to HOME', -> + force_expansion = 1 + absolute_path = '~/home.file' + result = mch_full_name absolute_path, buffer, len, force_expansion + eq FAIL, result - mch_isFullName = (filename) -> - filename = cstr(string.len(filename) + 1, filename) - fs.mch_isFullName(filename) + it 'works with some "normal" relative path with directories', -> + force_expansion = 1 + result = mch_full_name 'empty-test-directory/empty.file', buffer, len, force_expansion + eq OK, result + eq lfs.currentdir! .. '/empty-test-directory/empty.file', (ffi.string buffer) + + it 'does not modify the given filename', -> + force_expansion = 1 + filename = cstr 100, 'empty-test-directory/empty.file' + result = fs.mch_full_name filename, buffer, len, force_expansion + eq lfs.currentdir! .. '/empty-test-directory/empty.file', (ffi.string buffer) + eq 'empty-test-directory/empty.file', (ffi.string filename) + eq OK, result - TRUE = 1 - FALSE = 0 + describe 'append_path', -> + ffi.cdef 'int append_path(char *path, char *to_append, int max_len);' + + it 'joins given paths with a slash', -> + path = cstr 100, 'path1' + to_append = cstr 6, 'path2' + eq OK, (fs.append_path path, to_append, 100) + eq "path1/path2", (ffi.string path) + + it 'joins given paths without adding an unnecessary slash', -> + path = cstr 100, 'path1/' + to_append = cstr 6, 'path2' + eq OK, fs.append_path path, to_append, 100 + eq "path1/path2", (ffi.string path) + + it 'fails if there is not enough space left for to_append', -> + path = cstr 11, 'path1/' + to_append = cstr 6, 'path2' + eq FAIL, (fs.append_path path, to_append, 11) + + it 'does not append a slash if to_append is empty', -> + path = cstr 6, 'path1' + to_append = cstr 1, '' + eq OK, (fs.append_path path, to_append, 6) + eq 'path1', (ffi.string path) + + it 'does not append unnecessary dots', -> + path = cstr 6, 'path1' + to_append = cstr 2, '.' + eq OK, (fs.append_path path, to_append, 6) + eq 'path1', (ffi.string path) + + it 'copies to_append to path, if path is empty', -> + path = cstr 7, '' + to_append = cstr 7, '/path2' + eq OK, (fs.append_path path, to_append, 7) + eq '/path2', (ffi.string path) + + describe 'mch_is_full_name', -> + ffi.cdef 'int mch_is_full_name(char *fname);' + + mch_is_full_name = (filename) -> + filename = cstr (string.len filename) + 1, filename + fs.mch_is_full_name filename it 'returns true if filename starts with a slash', -> - eq TRUE, mch_isFullName('/some/directory/') + eq OK, mch_is_full_name '/some/directory/' it 'returns true if filename starts with a tilde', -> - eq TRUE, mch_isFullName('~/in/my/home~/directory') + eq OK, mch_is_full_name '~/in/my/home~/directory' it 'returns false if filename starts not with slash nor tilde', -> - eq FALSE, mch_isFullName('not/in/my/home~/directory') + eq FAIL, mch_is_full_name 'not/in/my/home~/directory' |