realpath.c raw

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