aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/buffer.c2
-rw-r--r--src/eval.c2
-rw-r--r--src/ex_getln.c2
-rw-r--r--src/fileio.c2
-rw-r--r--src/memline.c2
-rw-r--r--src/misc1.c6
-rw-r--r--src/os/fs.c163
-rw-r--r--src/os/os.h4
-rw-r--r--src/os_unix.c2
-rw-r--r--src/syntax.c2
-rw-r--r--src/window.c4
-rw-r--r--test/unit/os/fs.moon198
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'