xref: /csrg-svn/lib/libc/gen/getcwd.c (revision 42625)
1 /*
2  * Copyright (c) 1989 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #if defined(LIBC_SCCS) && !defined(lint)
9 static char sccsid[] = "@(#)getcwd.c	5.8 (Berkeley) 06/01/90";
10 #endif /* LIBC_SCCS and not lint */
11 
12 #include <sys/param.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <string.h>
16 
17 #define	ISDOT(dp) \
18 	(dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
19 	    dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
20 
21 char *
22 getwd(store)
23 	char *store;
24 {
25 	extern int errno;
26 	register struct dirent *dp;
27 	register DIR *dir;
28 	register ino_t ino;
29 	register char *pp, *pu;
30 	register int first;
31 	struct stat s;
32 	dev_t root_dev, dev;
33 	ino_t root_ino;
34 	int save_errno, found;
35 	char path[MAXPATHLEN], up[MAXPATHLEN], *file;
36 
37 	/* save root values */
38 	if (stat("/", &s)) {
39 		file = "/";
40 		goto err;
41 	}
42 	root_dev = s.st_dev;
43 	root_ino = s.st_ino;
44 
45 	/* init path pointer; built from the end of the buffer */
46 	pp = path + sizeof(path) - 1;
47 	*pp = '\0';
48 
49 	/* special case first stat, it's ".", not ".." */
50 	up[0] = '.';
51 	up[1] = '\0';
52 
53 	for (pu = up, first = 1;; first = 0) {
54 		/* stat current level */
55 		if (lstat(up, &s)) {
56 			file = up;
57 			goto err;
58 		}
59 
60 		/* save current node values */
61 		ino = s.st_ino;
62 		dev = s.st_dev;
63 
64 		/* check for root */
65 		if (root_dev == dev && root_ino == ino) {
66 			*store = '/';
67 			(void) strcpy(store + 1, pp);
68 			return (store);
69 		}
70 
71 		*pu++ = '.';
72 		*pu++ = '.';
73 		*pu = '\0';
74 
75 		/* open and stat parent */
76 		if (!(dir = opendir(up)) || fstat(dirfd(dir), &s)) {
77 			file = up;
78 			goto err;
79 		}
80 		found = save_errno = 0;
81 
82 		*pu++ = '/';
83 
84 		/*
85 		 * if it's a mount point you have to stat each element because
86 		 * the inode number in the directory is for the entry in the
87 		 * parent directory, not the inode number of the mounted file.
88 		 */
89 		if (s.st_dev == dev) {
90 			while (dp = readdir(dir))
91 				if (dp->d_fileno == ino)
92 					goto hit;
93 		} else {
94 			while (dp = readdir(dir)) {
95 				if (ISDOT(dp))
96 					continue;
97 				bcopy(dp->d_name, pu, dp->d_namlen + 1);
98 				if (lstat(up, &s)) {
99 					file = dp->d_name;
100 					save_errno = errno;
101 					errno = 0;
102 					continue;
103 				}
104 				if (s.st_dev == dev && s.st_ino == ino) {
105 hit:					if (!first)
106 						*--pp = '/';
107 					pp -= dp->d_namlen;
108 					bcopy(dp->d_name, pp, dp->d_namlen);
109 					found = 1;
110 					break;
111 				}
112 			}
113 			if (errno) {
114 				file = up;
115 				save_errno = errno;
116 			}
117 		}
118 		(void) closedir(dir);
119 
120 		*pu = '\0';
121 
122 		if (!found) {
123 			/*
124 			 * We didn't find the current level in its parent
125 			 * directory; figure out what to complain about.
126 			 */
127 			if (save_errno) {
128 				errno = save_errno;
129 				goto err;
130 			}
131 			(void) sprintf(store, "%s not found in %s?\n",
132 				first ? "." : pp, up);
133 			return ((char *)NULL);
134 		}
135 	}
136 err:
137 	(void) sprintf(store, "getwd: %s: %s", file, strerror(errno));
138 	return ((char *)NULL);
139 }
140