7 static size_t slash_len(const char *s
)
10 while (*s
== '/') s
++;
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;
25 l
= strnlen(filename
, sizeof stack
);
30 if (l
>= PATH_MAX
) goto toolong
;
31 p
= sizeof stack
- l
- 1;
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. */
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
] == '/') {
49 /* Initial // is special. */
50 if (stack
[p
] == '/' && stack
[p
+1] != '/')
55 char *z
= __strchrnul(stack
+p
, '/');
58 if (!l
&& !check_dir
) break;
60 /* Skip any . component but preserve check_dir status. */
61 if (l
==1 && stack
[p
]=='.') {
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] != '/') {
74 if (q
+l
>= PATH_MAX
) goto toolong
;
75 memcpy(output
+q
, stack
+p
, l
);
80 if (l0
==2 && stack
[p
-2]=='.' && stack
[p
-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. */
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
;
102 if (errno
!= EINVAL
) return 0;
106 while(q
&& output
[q
-1]!='/') q
--;
107 if (q
>1 && (q
>2 || output
[0]!='/')) q
--;
111 check_dir
= stack
[p
];
114 if (++cnt
== SYMLOOP_MAX
) {
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
++;
123 memmove(stack
+p
, stack
, k
);
125 /* Skip the stack advancement in case we have a new
126 * absolute base path. */
132 if (output
[0] != '/') {
133 if (!getcwd(stack
, sizeof stack
)) return 0;
135 /* Cancel any initial .. components. */
138 while(l
>1 && stack
[l
-1]!='/') l
--;
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
);
150 if (resolved
) return memcpy(resolved
, output
, q
+1);
151 else return strdup(output
);
154 errno
= ENAMETOOLONG
;