summaryrefslogtreecommitdiff
path: root/path.c
diff options
context:
space:
mode:
Diffstat (limited to 'path.c')
-rw-r--r--path.c96
1 files changed, 96 insertions, 0 deletions
diff --git a/path.c b/path.c
index 496123c..5983255 100644
--- a/path.c
+++ b/path.c
@@ -343,3 +343,99 @@ const char *make_relative_path(const char *abs, const char *base)
strcpy(buf, abs + baselen);
return buf;
}
+
+/*
+ * path = absolute path
+ * buf = buffer of at least max(2, strlen(path)+1) bytes
+ * It is okay if buf == path, but they should not overlap otherwise.
+ *
+ * Performs the following normalizations on path, storing the result in buf:
+ * - Removes trailing slashes.
+ * - Removes empty components.
+ * - Removes "." components.
+ * - Removes ".." components, and the components the precede them.
+ * "" and paths that contain only slashes are normalized to "/".
+ * Returns the length of the output.
+ *
+ * Note that this function is purely textual. It does not follow symlinks,
+ * verify the existence of the path, or make any system calls.
+ */
+int normalize_absolute_path(char *buf, const char *path)
+{
+ const char *comp_start = path, *comp_end = path;
+ char *dst = buf;
+ int comp_len;
+ assert(buf);
+ assert(path);
+
+ while (*comp_start) {
+ assert(*comp_start == '/');
+ while (*++comp_end && *comp_end != '/')
+ ; /* nothing */
+ comp_len = comp_end - comp_start;
+
+ if (!strncmp("/", comp_start, comp_len) ||
+ !strncmp("/.", comp_start, comp_len))
+ goto next;
+
+ if (!strncmp("/..", comp_start, comp_len)) {
+ while (dst > buf && *--dst != '/')
+ ; /* nothing */
+ goto next;
+ }
+
+ memcpy(dst, comp_start, comp_len);
+ dst += comp_len;
+ next:
+ comp_start = comp_end;
+ }
+
+ if (dst == buf)
+ *dst++ = '/';
+
+ *dst = '\0';
+ return dst - buf;
+}
+
+/*
+ * path = Canonical absolute path
+ * prefix_list = Colon-separated list of absolute paths
+ *
+ * Determines, for each path in parent_list, whether the "prefix" really
+ * is an ancestor directory of path. Returns the length of the longest
+ * ancestor directory, excluding any trailing slashes, or -1 if no prefix
+ * is an ancestor. (Note that this means 0 is returned if prefix_list is
+ * "/".) "/foo" is not considered an ancestor of "/foobar". Directories
+ * are not considered to be their own ancestors. path must be in a
+ * canonical form: empty components, or "." or ".." components are not
+ * allowed. prefix_list may be null, which is like "".
+ */
+int longest_ancestor_length(const char *path, const char *prefix_list)
+{
+ char buf[PATH_MAX+1];
+ const char *ceil, *colon;
+ int len, max_len = -1;
+
+ if (prefix_list == NULL || !strcmp(path, "/"))
+ return -1;
+
+ for (colon = ceil = prefix_list; *colon; ceil = colon+1) {
+ for (colon = ceil; *colon && *colon != ':'; colon++);
+ len = colon - ceil;
+ if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil))
+ continue;
+ strlcpy(buf, ceil, len+1);
+ len = normalize_absolute_path(buf, buf);
+ /* Strip "trailing slashes" from "/". */
+ if (len == 1)
+ len = 0;
+
+ if (!strncmp(path, buf, len) &&
+ path[len] == '/' &&
+ len > max_len) {
+ max_len = len;
+ }
+ }
+
+ return max_len;
+}