path_canonicalize.c (1470B)
1 /* ISC license. */ 2 3 #include <errno.h> 4 #include <sys/stat.h> 5 6 #include <skalibs/djbunix.h> 7 8 static unsigned char cclass (char c) 9 { 10 switch (c) 11 { 12 case 0 : return 0 ; 13 case '/' : return 1 ; 14 case '.' : return 2 ; 15 default : return 3 ; 16 } 17 } 18 19 /* out must be at least strlen(in) + 2 bytes */ 20 21 size_t path_canonicalize (char *out, char const *in, int check) 22 { 23 static unsigned char const table[4][4] = 24 { 25 { 0x04, 0x00, 0x12, 0x11 }, 26 { 0x04, 0x50, 0x11, 0x11 }, 27 { 0x24, 0x20, 0x13, 0x11 }, 28 { 0xa4, 0xa0, 0x11, 0x11 } 29 } ; 30 int isabsolute = in[0] == '/' ; 31 size_t j = 0 ; 32 unsigned int depth = 0 ; 33 unsigned char state = 0 ; 34 35 if (isabsolute) *out++ = *in++ ; 36 while (state < 4) 37 { 38 char c = *in++ ; 39 unsigned char what = table[state][cclass(c)] ; 40 state = what & 0x07 ; 41 if (what & 0x80) 42 { 43 if (depth) 44 { 45 depth-- ; 46 j -= 3 ; 47 if (check) 48 { 49 struct stat st ; 50 out[j] = 0 ; 51 if (stat(out - isabsolute, &st) < 0) return 0 ; 52 if (!S_ISDIR(st.st_mode)) return (errno = ENOTDIR, 0) ; 53 } 54 } 55 else if (!isabsolute) 56 { 57 out[j++] = '/' ; 58 out[j++] = '.' ; 59 } 60 } 61 if (what & 0x40) depth++ ; 62 if (what & 0x20) while (j && out[j-1] != '/') j-- ; 63 if (what & 0x10) out[j++] = c ; 64 } 65 if (j && out[j-1] == '/') j-- ; 66 if (!j && !isabsolute) out[j++] = '.' ; 67 out[j] = 0 ; 68 return j + isabsolute ; 69 }