master
  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}