xref: /csrg-svn/lib/libc/gen/getcwd.c (revision 46483)
121345Sdist /*
2*46483Sbostic  * Copyright (c) 1989, 1991 The Regents of the University of California.
337209Sbostic  * All rights reserved.
437209Sbostic  *
542625Sbostic  * %sccs.include.redist.c%
621345Sdist  */
79986Ssam 
826565Sdonn #if defined(LIBC_SCCS) && !defined(lint)
9*46483Sbostic static char sccsid[] = "@(#)getcwd.c	5.9 (Berkeley) 02/20/91";
1037209Sbostic #endif /* LIBC_SCCS and not lint */
1121345Sdist 
1210088Ssam #include <sys/param.h>
1310088Ssam #include <sys/stat.h>
14*46483Sbostic #include <errno.h>
1537209Sbostic #include <dirent.h>
16*46483Sbostic #include <stdio.h>
17*46483Sbostic #include <stdlib.h>
1842396Sbostic #include <string.h>
199986Ssam 
2042396Sbostic #define	ISDOT(dp) \
2142396Sbostic 	(dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
2242396Sbostic 	    dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
2342396Sbostic 
249986Ssam char *
25*46483Sbostic getcwd(pt, size)
26*46483Sbostic 	char *pt;
27*46483Sbostic 	size_t size;
289986Ssam {
2942396Sbostic 	register struct dirent *dp;
3037209Sbostic 	register DIR *dir;
31*46483Sbostic 	register dev_t dev;
3242396Sbostic 	register ino_t ino;
3337209Sbostic 	register int first;
34*46483Sbostic 	register char *bpt, *bup;
3542396Sbostic 	struct stat s;
36*46483Sbostic 	dev_t root_dev;
3737209Sbostic 	ino_t root_ino;
38*46483Sbostic 	size_t ptsize, upsize;
39*46483Sbostic 	int save_errno;
40*46483Sbostic 	char *ept, *eup, *up;
419986Ssam 
42*46483Sbostic 	/*
43*46483Sbostic 	 * If no buffer specified by the user, allocate one as necessary.
44*46483Sbostic 	 * If a buffer is specified, the size has to be non-zero.  The path
45*46483Sbostic 	 * is built from the end of the buffer backwards.
46*46483Sbostic 	 */
47*46483Sbostic 	if (pt) {
48*46483Sbostic 		ptsize = 0;
49*46483Sbostic 		if (!size) {
50*46483Sbostic 			errno = EINVAL;
51*46483Sbostic 			return((char *)NULL);
52*46483Sbostic 		}
53*46483Sbostic 		ept = pt + size;
54*46483Sbostic 	} else {
55*46483Sbostic 		if (!(pt = (char *)malloc(ptsize = 1024 - 4)))
56*46483Sbostic 			return((char *)NULL);
57*46483Sbostic 		ept = pt + ptsize;
58*46483Sbostic 	}
59*46483Sbostic 	bpt = ept - 1;
60*46483Sbostic 	*ept = '\0';
61*46483Sbostic 
62*46483Sbostic 	/*
63*46483Sbostic 	 * Allocate bytes (1024 - malloc space) for the string of "../"'s.
64*46483Sbostic 	 * Should always be enough (it's 340 levels).  If it's not, allocate
65*46483Sbostic 	 * as necessary.  Special * case the first stat, it's ".", not "..".
66*46483Sbostic 	 */
67*46483Sbostic 	if (!(up = (char *)malloc(upsize = 1024 - 4)))
6837209Sbostic 		goto err;
69*46483Sbostic 	eup = up + MAXPATHLEN;
70*46483Sbostic 	bup = up;
71*46483Sbostic 	up[0] = '.';
72*46483Sbostic 	up[1] = '\0';
73*46483Sbostic 
74*46483Sbostic 	/* Save root values, so know when to stop. */
75*46483Sbostic 	if (stat("/", &s))
76*46483Sbostic 		goto err;
7737209Sbostic 	root_dev = s.st_dev;
7837209Sbostic 	root_ino = s.st_ino;
7942396Sbostic 
80*46483Sbostic 	errno = 0;			/* XXX readdir has no error return. */
8142396Sbostic 
82*46483Sbostic 	for (first = 1;; first = 0) {
83*46483Sbostic 		/* Stat the current level. */
84*46483Sbostic 		if (lstat(up, &s))
8542396Sbostic 			goto err;
8642396Sbostic 
87*46483Sbostic 		/* Save current node values. */
8842396Sbostic 		ino = s.st_ino;
8942396Sbostic 		dev = s.st_dev;
9042396Sbostic 
91*46483Sbostic 		/* Check for reaching root. */
9242396Sbostic 		if (root_dev == dev && root_ino == ino) {
93*46483Sbostic 			*--bpt = '/';
94*46483Sbostic 			/*
95*46483Sbostic 			 * It's unclear that it's a requirement to copy the
96*46483Sbostic 			 * path to the beginning of the buffer, but it's always
97*46483Sbostic 			 * been that way and stuff would probably break.
98*46483Sbostic 			 */
99*46483Sbostic 			(void)bcopy(bpt, pt, ept - bpt);
100*46483Sbostic 			free(up);
101*46483Sbostic 			return(pt);
10210155Ssam 		}
10342396Sbostic 
104*46483Sbostic 		/*
105*46483Sbostic 		 * Build pointer to the parent directory, allocating memory
106*46483Sbostic 		 * as necessary.  Max length is 3 for "../", the largest
107*46483Sbostic 		 * possible component name, plus a trailing NULL.
108*46483Sbostic 		 */
109*46483Sbostic 		if (bup + 3  + MAXNAMLEN + 1 >= eup) {
110*46483Sbostic 			if (!(up = (char *)realloc(up, upsize *= 2)))
111*46483Sbostic 				goto err;
112*46483Sbostic 			eup = up + upsize;
113*46483Sbostic 		}
114*46483Sbostic 		*bup++ = '.';
115*46483Sbostic 		*bup++ = '.';
116*46483Sbostic 		*bup = '\0';
11742396Sbostic 
118*46483Sbostic 		/* Open and stat parent directory. */
119*46483Sbostic 		if (!(dir = opendir(up)) || fstat(dirfd(dir), &s))
12040556Skarels 			goto err;
12142396Sbostic 
122*46483Sbostic 		/* Add trailing slash for next directory. */
123*46483Sbostic 		*bup++ = '/';
12442396Sbostic 
12542396Sbostic 		/*
126*46483Sbostic 		 * If it's a mount point, have to stat each element because
12742396Sbostic 		 * the inode number in the directory is for the entry in the
12842396Sbostic 		 * parent directory, not the inode number of the mounted file.
12942396Sbostic 		 */
130*46483Sbostic 		save_errno = 0;
13142396Sbostic 		if (s.st_dev == dev) {
132*46483Sbostic 			for (;;) {
133*46483Sbostic 				if (!(dp = readdir(dir)))
134*46483Sbostic 					goto notfound;
13542396Sbostic 				if (dp->d_fileno == ino)
136*46483Sbostic 					break;
137*46483Sbostic 			}
138*46483Sbostic 		} else
139*46483Sbostic 			for (;;) {
140*46483Sbostic 				if (!(dp = readdir(dir)))
141*46483Sbostic 					goto notfound;
14242396Sbostic 				if (ISDOT(dp))
14342396Sbostic 					continue;
144*46483Sbostic 				bcopy(dp->d_name, bup, dp->d_namlen + 1);
145*46483Sbostic 
146*46483Sbostic 				/* Save the first error for later. */
14742396Sbostic 				if (lstat(up, &s)) {
148*46483Sbostic 					if (!save_errno)
149*46483Sbostic 						save_errno = errno;
15042396Sbostic 					errno = 0;
15142396Sbostic 					continue;
15242396Sbostic 				}
153*46483Sbostic 				if (s.st_dev == dev && s.st_ino == ino)
15442396Sbostic 					break;
15542396Sbostic 			}
15642396Sbostic 
157*46483Sbostic 		/*
158*46483Sbostic 		 * Check for length of the current name, preceding slash,
159*46483Sbostic 		 * leading slash.
160*46483Sbostic 		 */
161*46483Sbostic 		if (bpt - pt <= dp->d_namlen + (first ? 1 : 2)) {
162*46483Sbostic 			size_t len, off;
16342396Sbostic 
164*46483Sbostic 			if (!ptsize) {
165*46483Sbostic 				errno = ERANGE;
16640556Skarels 				goto err;
16740556Skarels 			}
168*46483Sbostic 			off = bpt - pt;
169*46483Sbostic 			len = ept - bpt;
170*46483Sbostic 			if (!(pt = (char *)realloc(pt, ptsize *= 2)))
171*46483Sbostic 				goto err;
172*46483Sbostic 			bpt = pt + off;
173*46483Sbostic 			ept = pt + ptsize;
174*46483Sbostic 			(void)bcopy(bpt, ept - len, len);
175*46483Sbostic 			bpt = ept - len;
17640556Skarels 		}
177*46483Sbostic 		if (!first)
178*46483Sbostic 			*--bpt = '/';
179*46483Sbostic 		bpt -= dp->d_namlen;
180*46483Sbostic 		bcopy(dp->d_name, bpt, dp->d_namlen);
181*46483Sbostic 		(void)closedir(dir);
182*46483Sbostic 
183*46483Sbostic 		/* Truncate any file name. */
184*46483Sbostic 		*bup = '\0';
1859986Ssam 	}
186*46483Sbostic 
187*46483Sbostic notfound:
188*46483Sbostic 	/*
189*46483Sbostic 	 * If readdir set errno, use it, not any saved error; otherwise,
190*46483Sbostic 	 * didn't find the current directory in its parent directory, set
191*46483Sbostic 	 * errno to ENOENT.
192*46483Sbostic 	 */
193*46483Sbostic 	if (!errno)
194*46483Sbostic 		errno = save_errno ? save_errno : ENOENT;
195*46483Sbostic 	/* FALLTHROUGH */
19640556Skarels err:
197*46483Sbostic 	if (ptsize)
198*46483Sbostic 		free(pt);
199*46483Sbostic 	free(up);
200*46483Sbostic 	return((char *)NULL);
2019986Ssam }
202