diff options
author | Omar Polo <op@omarpolo.com> | 2024-06-10 08:20:35 +0000 |
---|---|---|
committer | Omar Polo <op@omarpolo.com> | 2024-06-10 08:20:35 +0000 |
commit | 60c2f58b758643fa16f8858e18888fb3c3386cde (patch) | |
tree | b204902fb1c8df7384bd6a497d9cdbdc58f32ac3 /iri.c | |
parent | bc1190c5d4d571d86cd935c478040f7904b5af26 (diff) |
refactor path_clean()
Instead of doing multiple passes over the string use a modified
version canonpath() from kern_pledge.c that does all in a single
go.
Diffstat (limited to 'iri.c')
-rw-r--r-- | iri.c | 106 |
1 files changed, 41 insertions, 65 deletions
@@ -1,5 +1,6 @@ /* * Copyright (c) 2020, 2022, 2024 Omar Polo <op@omarpolo.com> + * Copyright (c) 2015 Theo de Raadt <deraadt@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -240,78 +241,53 @@ parse_authority(struct parser *p) } /* - * Routine for path_clean. Elide the pointed .. with the preceding - * element. Return 0 if it's not possible. incr is the length of - * the increment, 3 for ../ and 2 for .. - */ -static int -path_elide_dotdot(char *path, char *i, int incr) -{ - char *j; - - if (i == path) - return 0; - for (j = i-2; j != path && *j != '/'; j--) - /* noop */ ; - if (*j == '/') - j++; - i += incr; - memmove(j, i, strlen(i)+1); - return 1; -} - -/* - * Use an algorithm similar to the one implemented in go' path.Clean: - * - * 1. Replace multiple slashes with a single slash - * 2. Eliminate each . path name element - * 3. Eliminate each inner .. along with the non-.. element that precedes it - * 4. Eliminate trailing .. if possible or error (go would only discard) + * Use an algorithm based on canonpath() from kern_pledge.c. * - * Unlike path.Clean, this function return the empty string if the - * original path is equivalent to "/". + * It's slightly more complicated since even if your paths are + * absolutely, they don't start with '/'. q == path asserts + * that we're at the start of the path. */ static int path_clean(char *path) { - char *i; - - /* 1. replace multiple slashes with a single one */ - for (i = path; *i; ++i) { - if (*i == '/' && *(i+1) == '/') { - memmove(i, i+1, strlen(i)); /* move also the \0 */ - i--; - } - } - - /* 2. eliminate each . path name element */ - for (i = path; *i; ++i) { - if ((i == path || *i == '/') && - *i != '.' && i[1] == '.' && i[2] == '/') { - /* move also the \0 */ - memmove(i, i+2, strlen(i)-1); - i--; - } - } - if (!strcmp(path, ".") || !strcmp(path, "/.")) { - *path = '\0'; - return 1; - } - - /* 3. eliminate each inner .. along with the preceding non-.. */ - for (i = strstr(path, "../"); i != NULL; i = strstr(path, "..")) { - /* break if we've found a trailing .. */ - if (i[2] == '\0') - break; - if (!path_elide_dotdot(path, i, 3)) + char *p, *q; + + p = q = path; + while (*p) { + if (q == path && p[0] == '/') { + /* special case, if path is just "/" trim it */ + p++; + } else if (q == path && p[0] == '.' && p[1] == '.' && + (p[2] == '/' || p[2] == '\0')) { + /* ../ at the start of path */ + // p += 3; return 0; + } else if (q == path && p[0] == '.' && + (p[1] == '/' || p[1] == '\0')) { + /* ./ at the start of path */ + p += 2; + } else if (p[0] == '/' && p[1] == '/') { + /* trim double slashes */ + p++; + } else if (p[0] == '/' && p[1] == '.' && p[2] == '.' && + (p[3] == '/' || p[3] == '\0')) { + /* /../ component */ + while (q > path && *--q != '/') + continue; + p += 3; + if (q == path && *p == '/') + p++; + } else if (p[0] == '/' && p[1] == '.' && + (p[2] == '/' || p[2] == '\0')) { + /* /./ component */ + p += 2; + } else { + *q++ = *p++; + } } - - /* 4. eliminate trailing ..*/ - if ((i = strstr(path, "..")) != NULL) - if (!path_elide_dotdot(path, i, 2)) - return 0; - + if (*p != '\0') + return 0; + *q = '\0'; return 1; } |