1#include <stdlib.h> 2#include <limits.h> 3#include <errno.h> 4#include <unistd.h> 5#include <string.h> 6 7static size_t slash_len(const char *s) 8{ 9 const char *s0 = s; 10 while (*s == '/') s++; 11 return s-s0; 12} 13 14char *realpath(const char *restrict filename, char *restrict resolved) 15{ 16 char stack[PATH_MAX+1]; 17 char output[PATH_MAX]; 18 size_t p, q, l, l0, cnt=0, nup=0; 19 int check_dir=0; 20 21 if (!filename) { 22 errno = EINVAL; 23 return 0; 24 } 25 l = strnlen(filename, sizeof stack); 26 if (!l) { 27 errno = ENOENT; 28 return 0; 29 } 30 if (l >= PATH_MAX) goto toolong; 31 p = sizeof stack - l - 1; 32 q = 0; 33 memcpy(stack+p, filename, l+1); 34 35 /* Main loop. Each iteration pops the next part from stack of 36 * remaining path components and consumes any slashes that follow. 37 * If not a link, it's moved to output; if a link, contents are 38 * pushed to the stack. */ 39restart: 40 for (; ; p+=slash_len(stack+p)) { 41 /* If stack starts with /, the whole component is / or // 42 * and the output state must be reset. */ 43 if (stack[p] == '/') { 44 check_dir=0; 45 nup=0; 46 q=0; 47 output[q++] = '/'; 48 p++; 49 /* Initial // is special. */ 50 if (stack[p] == '/' && stack[p+1] != '/') 51 output[q++] = '/'; 52 continue; 53 } 54 55 char *z = __strchrnul(stack+p, '/'); 56 l0 = l = z-(stack+p); 57 58 if (!l && !check_dir) break; 59 60 /* Skip any . component but preserve check_dir status. */ 61 if (l==1 && stack[p]=='.') { 62 p += l; 63 continue; 64 } 65 66 /* Copy next component onto output at least temporarily, to 67 * call readlink, but wait to advance output position until 68 * determining it's not a link. */ 69 if (q && output[q-1] != '/') { 70 if (!p) goto toolong; 71 stack[--p] = '/'; 72 l++; 73 } 74 if (q+l >= PATH_MAX) goto toolong; 75 memcpy(output+q, stack+p, l); 76 output[q+l] = 0; 77 p += l; 78 79 int up = 0; 80 if (l0==2 && stack[p-2]=='.' && stack[p-1]=='.') { 81 up = 1; 82 /* Any non-.. path components we could cancel start 83 * after nup repetitions of the 3-byte string "../"; 84 * if there are none, accumulate .. components to 85 * later apply to cwd, if needed. */ 86 if (q <= 3*nup) { 87 nup++; 88 q += l; 89 continue; 90 } 91 /* When previous components are already known to be 92 * directories, processing .. can skip readlink. */ 93 if (!check_dir) goto skip_readlink; 94 } 95 ssize_t k = readlink(output, stack, p); 96 if (k==p) goto toolong; 97 if (!k) { 98 errno = ENOENT; 99 return 0; 100 } 101 if (k<0) { 102 if (errno != EINVAL) return 0; 103skip_readlink: 104 check_dir = 0; 105 if (up) { 106 while(q && output[q-1]!='/') q--; 107 if (q>1 && (q>2 || output[0]!='/')) q--; 108 continue; 109 } 110 if (l0) q += l; 111 check_dir = stack[p]; 112 continue; 113 } 114 if (++cnt == SYMLOOP_MAX) { 115 errno = ELOOP; 116 return 0; 117 } 118 119 /* If link contents end in /, strip any slashes already on 120 * stack to avoid /->// or //->/// or spurious toolong. */ 121 if (stack[k-1]=='/') while (stack[p]=='/') p++; 122 p -= k; 123 memmove(stack+p, stack, k); 124 125 /* Skip the stack advancement in case we have a new 126 * absolute base path. */ 127 goto restart; 128 } 129 130 output[q] = 0; 131 132 if (output[0] != '/') { 133 if (!getcwd(stack, sizeof stack)) return 0; 134 l = strlen(stack); 135 /* Cancel any initial .. components. */ 136 p = 0; 137 while (nup--) { 138 while(l>1 && stack[l-1]!='/') l--; 139 if (l>1) l--; 140 p += 2; 141 if (p<q) p++; 142 } 143 if (q-p && stack[l-1]!='/') stack[l++] = '/'; 144 if (l + (q-p) + 1 >= PATH_MAX) goto toolong; 145 memmove(output + l, output + p, q - p + 1); 146 memcpy(output, stack, l); 147 q = l + q-p; 148 } 149 150 if (resolved) return memcpy(resolved, output, q+1); 151 else return strdup(output); 152 153toolong: 154 errno = ENAMETOOLONG; 155 return 0; 156} 157