loongarch64: add new syscall numbers
[musl.git] / src / misc / realpath.c
blobdb8b74dc8dbbcce1b77885bcc446d603db22f284
1 #include <stdlib.h>
2 #include <limits.h>
3 #include <errno.h>
4 #include <unistd.h>
5 #include <string.h>
7 static size_t slash_len(const char *s)
9 const char *s0 = s;
10 while (*s == '/') s++;
11 return s-s0;
14 char *realpath(const char *restrict filename, char *restrict resolved)
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;
21 if (!filename) {
22 errno = EINVAL;
23 return 0;
25 l = strnlen(filename, sizeof stack);
26 if (!l) {
27 errno = ENOENT;
28 return 0;
30 if (l >= PATH_MAX) goto toolong;
31 p = sizeof stack - l - 1;
32 q = 0;
33 memcpy(stack+p, filename, l+1);
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;
55 char *z = __strchrnul(stack+p, '/');
56 l0 = l = z-(stack+p);
58 if (!l && !check_dir) break;
60 /* Skip any . component but preserve check_dir status. */
61 if (l==1 && stack[p]=='.') {
62 p += l;
63 continue;
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++;
74 if (q+l >= PATH_MAX) goto toolong;
75 memcpy(output+q, stack+p, l);
76 output[q+l] = 0;
77 p += l;
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;
91 /* When previous components are already known to be
92 * directories, processing .. can skip readlink. */
93 if (!check_dir) goto skip_readlink;
95 ssize_t k = readlink(output, stack, p);
96 if (k==p) goto toolong;
97 if (!k) {
98 errno = ENOENT;
99 return 0;
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;
110 if (l0) q += l;
111 check_dir = stack[p];
112 continue;
114 if (++cnt == SYMLOOP_MAX) {
115 errno = ELOOP;
116 return 0;
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);
125 /* Skip the stack advancement in case we have a new
126 * absolute base path. */
127 goto restart;
130 output[q] = 0;
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++;
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;
150 if (resolved) return memcpy(resolved, output, q+1);
151 else return strdup(output);
153 toolong:
154 errno = ENAMETOOLONG;
155 return 0;