xref: /minix3/minix/lib/libc/sys/__getcwd.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1*433d6423SLionel Sambuc /*	getcwd() - get the name of the current working directory.
2*433d6423SLionel Sambuc  *							Author: Kees J. Bot
3*433d6423SLionel Sambuc  *								30 Apr 1989
4*433d6423SLionel Sambuc  */
5*433d6423SLionel Sambuc 
6*433d6423SLionel Sambuc #include <sys/cdefs.h>
7*433d6423SLionel Sambuc #include "namespace.h"
8*433d6423SLionel Sambuc 
9*433d6423SLionel Sambuc #include <sys/types.h>
10*433d6423SLionel Sambuc #include <sys/stat.h>
11*433d6423SLionel Sambuc #include <errno.h>
12*433d6423SLionel Sambuc #include <unistd.h>
13*433d6423SLionel Sambuc #include <dirent.h>
14*433d6423SLionel Sambuc #include <limits.h>
15*433d6423SLionel Sambuc #include <string.h>
16*433d6423SLionel Sambuc 
17*433d6423SLionel Sambuc /* libc-private interface */
18*433d6423SLionel Sambuc int __getcwd(char *, size_t);
19*433d6423SLionel Sambuc 
addpath(const char * path,char ** ap,const char * entry)20*433d6423SLionel Sambuc static int addpath(const char *path, char **ap, const char *entry)
21*433d6423SLionel Sambuc /* Add the name of a directory entry at the front of the path being built.
22*433d6423SLionel Sambuc  * Note that the result always starts with a slash.
23*433d6423SLionel Sambuc  */
24*433d6423SLionel Sambuc {
25*433d6423SLionel Sambuc 	const char *e= entry;
26*433d6423SLionel Sambuc 	char *p= *ap;
27*433d6423SLionel Sambuc 
28*433d6423SLionel Sambuc 	while (*e != 0) e++;
29*433d6423SLionel Sambuc 
30*433d6423SLionel Sambuc 	while (e > entry && p > path) *--p = *--e;
31*433d6423SLionel Sambuc 
32*433d6423SLionel Sambuc 	if (p == path) return -1;
33*433d6423SLionel Sambuc 	*--p = '/';
34*433d6423SLionel Sambuc 	*ap= p;
35*433d6423SLionel Sambuc 	return 0;
36*433d6423SLionel Sambuc }
37*433d6423SLionel Sambuc 
recover(char * p)38*433d6423SLionel Sambuc static int recover(char *p)
39*433d6423SLionel Sambuc /* Undo all those chdir("..")'s that have been recorded by addpath.  This
40*433d6423SLionel Sambuc  * has to be done entry by entry, because the whole pathname may be too long.
41*433d6423SLionel Sambuc  */
42*433d6423SLionel Sambuc {
43*433d6423SLionel Sambuc 	int e= errno, slash;
44*433d6423SLionel Sambuc 	char *p0;
45*433d6423SLionel Sambuc 
46*433d6423SLionel Sambuc 	while (*p != 0) {
47*433d6423SLionel Sambuc 		p0= ++p;
48*433d6423SLionel Sambuc 
49*433d6423SLionel Sambuc 		do p++; while (*p != 0 && *p != '/');
50*433d6423SLionel Sambuc 		slash= *p; *p= 0;
51*433d6423SLionel Sambuc 
52*433d6423SLionel Sambuc 		if (chdir(p0) < 0) return -1;
53*433d6423SLionel Sambuc 		*p= slash;
54*433d6423SLionel Sambuc 	}
55*433d6423SLionel Sambuc 	errno= e;
56*433d6423SLionel Sambuc 	return 0;
57*433d6423SLionel Sambuc }
58*433d6423SLionel Sambuc 
__getcwd(char * path,size_t size)59*433d6423SLionel Sambuc int __getcwd(char *path, size_t size)
60*433d6423SLionel Sambuc {
61*433d6423SLionel Sambuc 	struct stat above, current, tmp;
62*433d6423SLionel Sambuc 	struct dirent *entry;
63*433d6423SLionel Sambuc 	DIR *d;
64*433d6423SLionel Sambuc 	char *p, *up;
65*433d6423SLionel Sambuc 	const char *dotdot = "..";
66*433d6423SLionel Sambuc 	int cycle;
67*433d6423SLionel Sambuc 
68*433d6423SLionel Sambuc 	if (path == NULL || size <= 1) { errno= EINVAL; return -1; }
69*433d6423SLionel Sambuc 
70*433d6423SLionel Sambuc 	p= path + size;
71*433d6423SLionel Sambuc 	*--p = 0;
72*433d6423SLionel Sambuc 
73*433d6423SLionel Sambuc 	if (stat(".", &current) < 0) return -1;
74*433d6423SLionel Sambuc 
75*433d6423SLionel Sambuc 	while (1) {
76*433d6423SLionel Sambuc 		if (stat(dotdot, &above) < 0) { recover(p); return -1; }
77*433d6423SLionel Sambuc 
78*433d6423SLionel Sambuc 		if (above.st_dev == current.st_dev
79*433d6423SLionel Sambuc 					&& above.st_ino == current.st_ino)
80*433d6423SLionel Sambuc 			break;	/* Root dir found */
81*433d6423SLionel Sambuc 
82*433d6423SLionel Sambuc 		if ((d= opendir(dotdot)) == NULL) { recover(p); return -1; }
83*433d6423SLionel Sambuc 
84*433d6423SLionel Sambuc 		/* Cycle is 0 for a simple inode nr search, or 1 for a search
85*433d6423SLionel Sambuc 		 * for inode *and* device nr.
86*433d6423SLionel Sambuc 		 */
87*433d6423SLionel Sambuc 		cycle= above.st_dev == current.st_dev ? 0 : 1;
88*433d6423SLionel Sambuc 
89*433d6423SLionel Sambuc 		do {
90*433d6423SLionel Sambuc 			char name[3 + NAME_MAX + 1];
91*433d6423SLionel Sambuc 
92*433d6423SLionel Sambuc 			tmp.st_ino= 0;
93*433d6423SLionel Sambuc 			if ((entry= readdir(d)) == NULL) {
94*433d6423SLionel Sambuc 				switch (++cycle) {
95*433d6423SLionel Sambuc 				case 1:
96*433d6423SLionel Sambuc 					rewinddir(d);
97*433d6423SLionel Sambuc 					continue;
98*433d6423SLionel Sambuc 				case 2:
99*433d6423SLionel Sambuc 					closedir(d);
100*433d6423SLionel Sambuc 					errno= ENOENT;
101*433d6423SLionel Sambuc 					recover(p);
102*433d6423SLionel Sambuc 					return -1;
103*433d6423SLionel Sambuc 				}
104*433d6423SLionel Sambuc 			}
105*433d6423SLionel Sambuc 			if (strcmp(entry->d_name, ".") == 0) continue;
106*433d6423SLionel Sambuc 			if (strcmp(entry->d_name, "..") == 0) continue;
107*433d6423SLionel Sambuc 
108*433d6423SLionel Sambuc 			switch (cycle) {
109*433d6423SLionel Sambuc 			case 0:
110*433d6423SLionel Sambuc 				/* Simple test on inode nr. */
111*433d6423SLionel Sambuc 				if (entry->d_ino != current.st_ino) continue;
112*433d6423SLionel Sambuc 				/*FALL THROUGH*/
113*433d6423SLionel Sambuc 
114*433d6423SLionel Sambuc 			case 1:
115*433d6423SLionel Sambuc 				/* Current is mounted. */
116*433d6423SLionel Sambuc 				strcpy(name, "../");
117*433d6423SLionel Sambuc 				strcpy(name+3, entry->d_name);
118*433d6423SLionel Sambuc 				if (stat(name, &tmp) < 0) continue;
119*433d6423SLionel Sambuc 				break;
120*433d6423SLionel Sambuc 			}
121*433d6423SLionel Sambuc 		} while (tmp.st_ino != current.st_ino
122*433d6423SLionel Sambuc 					|| tmp.st_dev != current.st_dev);
123*433d6423SLionel Sambuc 
124*433d6423SLionel Sambuc 		up= p;
125*433d6423SLionel Sambuc 		if (addpath(path, &up, entry->d_name) < 0) {
126*433d6423SLionel Sambuc 			closedir(d);
127*433d6423SLionel Sambuc 			errno = ERANGE;
128*433d6423SLionel Sambuc 			recover(p);
129*433d6423SLionel Sambuc 			return -1;
130*433d6423SLionel Sambuc 		}
131*433d6423SLionel Sambuc 		closedir(d);
132*433d6423SLionel Sambuc 
133*433d6423SLionel Sambuc 		if (chdir(dotdot) < 0) { recover(p); return -1; }
134*433d6423SLionel Sambuc 		p= up;
135*433d6423SLionel Sambuc 
136*433d6423SLionel Sambuc 		current= above;
137*433d6423SLionel Sambuc 	}
138*433d6423SLionel Sambuc 	if (recover(p) < 0) return -1;	/* Undo all those chdir("..")'s. */
139*433d6423SLionel Sambuc 	if (*p == 0) *--p = '/';	/* Cwd is "/" if nothing added */
140*433d6423SLionel Sambuc 	if (p > path) strcpy(path, p);	/* Move string to start of path. */
141*433d6423SLionel Sambuc 	return 0;
142*433d6423SLionel Sambuc }
143