summaryrefslogtreecommitdiff
path: root/src/libstddjb
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstddjb')
-rw-r--r--src/libstddjb/path_canonicalize.c69
1 files changed, 69 insertions, 0 deletions
diff --git a/src/libstddjb/path_canonicalize.c b/src/libstddjb/path_canonicalize.c
new file mode 100644
index 0000000..c30f9f4
--- /dev/null
+++ b/src/libstddjb/path_canonicalize.c
@@ -0,0 +1,69 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <skalibs/djbunix.h>
+
+static unsigned char cclass (char c)
+{
+ switch (c)
+ {
+ case 0 : return 0 ;
+ case '/' : return 1 ;
+ case '.' : return 2 ;
+ default : return 3 ;
+ }
+}
+
+ /* out must be at least strlen(in) + 2 bytes */
+
+size_t path_canonicalize (char *out, char const *in, int check)
+{
+ static unsigned char const table[4][4] =
+ {
+ { 0x04, 0x00, 0x12, 0x11 },
+ { 0x04, 0x50, 0x11, 0x11 },
+ { 0x24, 0x20, 0x13, 0x11 },
+ { 0xa4, 0xa0, 0x11, 0x11 }
+ } ;
+ int isabsolute = in[0] == '/' ;
+ size_t j = 0 ;
+ unsigned int depth = 0 ;
+ unsigned char state = 0 ;
+
+ if (isabsolute) *out++ = *in++ ;
+ while (state < 4)
+ {
+ char c = *in++ ;
+ unsigned char what = table[state][cclass(c)] ;
+ state = what & 0x07 ;
+ if (what & 0x80)
+ {
+ if (depth)
+ {
+ depth-- ;
+ j -= 3 ;
+ if (check)
+ {
+ struct stat st ;
+ out[j] = 0 ;
+ if (stat(out - isabsolute, &st) < 0) return 0 ;
+ if (!S_ISDIR(st.st_mode)) return (errno = ENOTDIR, 0) ;
+ }
+ }
+ else if (!isabsolute)
+ {
+ out[j++] = '/' ;
+ out[j++] = '.' ;
+ }
+ }
+ if (what & 0x40) depth++ ;
+ if (what & 0x20) while (j && out[j-1] != '/') j-- ;
+ if (what & 0x10) out[j++] = c ;
+ }
+ if (j && out[j-1] == '/') j-- ;
+ if (!j && !isabsolute) out[j++] = '.' ;
+ out[j] = 0 ;
+ return j ;
+}