diff options
author | Mattias Andrée <maandree@operamail.com> | 2015-11-13 12:52:31 +0100 |
---|---|---|
committer | Mattias Andrée <maandree@operamail.com> | 2015-11-13 12:52:31 +0100 |
commit | 66dbf9a6cf3d6d390ce17ed6d7f8789811a160ef (patch) | |
tree | 464f1bc39cb98591569caa85d79755958ef7c74e | |
parent | doc (diff) | |
download | slibc-66dbf9a6cf3d6d390ce17ed6d7f8789811a160ef.tar.gz slibc-66dbf9a6cf3d6d390ce17ed6d7f8789811a160ef.tar.bz2 slibc-66dbf9a6cf3d6d390ce17ed6d7f8789811a160ef.tar.xz |
add cleanname
Signed-off-by: Mattias Andrée <maandree@operamail.com>
-rw-r--r-- | include/libgen.h | 21 | ||||
-rw-r--r-- | src/libgen.c | 81 |
2 files changed, 102 insertions, 0 deletions
diff --git a/include/libgen.h b/include/libgen.h index 653b1c0..d417739 100644 --- a/include/libgen.h +++ b/include/libgen.h @@ -55,6 +55,27 @@ char* __xpg_basename(char*) char* dirname(char*) __GCC_ONLY(__attribute__((__warn_unused_result__))); +#if defined(__SLIBC_SOURCE) +/** + * Removes all trailing slashes (that is not the first character + * in the filename,) all '.' directory components, and when possible + * using only lexical analysis, resolves '..' directory components. + * + * '..' directory components that should resolve up beyond '/', + * are removed. Note that this can in fact mean that the resulting + * path is not the same file if the processes is `chroot`:ed. + * + * This is a slibc extension. + * + * @param filename The filename, may be edited by this function. + * @return The dirname, it is either `filename` or, + * if `filename` is `NULL` or does no contain a + * non-trailing slash, a statically allocationed + * string, so it must not freed or edited. + */ +char* cleanname(char*) +#endif + #endif diff --git a/src/libgen.c b/src/libgen.c index 8ab303e..9f24da6 100644 --- a/src/libgen.c +++ b/src/libgen.c @@ -103,3 +103,84 @@ char* dirname(char* filename) return *last_slash = 0, filename; } + +/** + * Removes all trailing slashes (that is not the first character + * in the filename,), all duplicate slashes, all '.' directory + * components, and when possible using only lexical analysis, + * resolves '..' directory components. + * + * '..' directory components that should resolve up beyond '/', + * are removed. Note that this can in fact mean that the resulting + * path is not the same file if the processes is `chroot`:ed. + * + * This is a slibc extension. + * + * @param filename The filename, may be edited by this function. + * @return The dirname, it is either `filename` or, + * if `filename` is `NULL` or does no contain a + * non-trailing slash, a statically allocationed + * string, so it must not freed or edited. + */ +char* cleanname(char* filename) +{ + size_t parts = 0; + int start; + char* w; + char* r; + + if ((filename == NULL) || (!*filename)) + return "."; + + /* Remove unnecessary slashes and '.' directory components. */ + for (w = r = filename, start = 1; *r; r++) + if (*r == '/') + { + if ((w == filename) || (w[-1] != '/')) + *w++ = '/'; + start = 1; + } + else if (start && (r[0] == '.') && ((r[1] == '/') || (r[1] == 0))) + r += (r[1] == '/'); + else + *w++ = *r, start = 0; + if ((w > filename + 1) && (w[-1] == '/')) + *--w = 0; + if (w == filename) + *w++ = '.'; + *w = 0; + + /* Resolve '..' directory components. */ + for (w = r = filename, start = 1; *r; r++) + if (*r == '/') + *w++ = '/', start = 1, parts++; + else if (start && (r[0] == '.') && (r[1] == '.') && ((r[2] == '/') || (r[2] == 0))) + { + if (parts == 0) + { + *w++ = '.', *w++ = '.'; + if (r[2]) + *w++ = '/'; + r += 1 + (r[2] == '/'); + continue; + } + r += 1 + (r[2] == '/'); + parts -= *filename != '/'; + if (w > filename + 1) + { + *--w = 0; + while (w[-1] != '/') + *--w = 0; + } + } + else + *w++ = *r, start = 0; + if ((w > filename + 1) && (w[-1] == '/')) + *--w = 0; + if (!*filename) + w = filename, *w++ = '/'; + *w = 0; + + return filename; +} + |