aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias Andrée <maandree@operamail.com>2015-11-13 12:52:31 +0100
committerMattias Andrée <maandree@operamail.com>2015-11-13 12:52:31 +0100
commit66dbf9a6cf3d6d390ce17ed6d7f8789811a160ef (patch)
tree464f1bc39cb98591569caa85d79755958ef7c74e
parentdoc (diff)
downloadslibc-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.h21
-rw-r--r--src/libgen.c81
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;
+}
+