clean up dns_parse_callback
[musl] / src / misc / realpath.c
1 #include <stdlib.h>
2 #include <limits.h>
3 #include <errno.h>
4 #include <unistd.h>
5 #include <string.h>
6
7 static size_t slash_len(const char *s)
8 {
9         const char *s0 = s;
10         while (*s == '/') s++;
11         return s-s0;
12 }
13
14 char *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. */
39 restart:
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;
103 skip_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
153 toolong:
154         errno = ENAMETOOLONG;
155         return 0;
156 }