skalibs

Mirror/fork of https://skarnet.org/software/skalibs/
git clone https://ccx.te2000.cz/git/skalibs
Log | Files | Refs | README | LICENSE

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 }