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