xref: /titanic_44/usr/src/lib/libast/common/misc/getcwd.c (revision 67e3a03ed4a2813074d36330f062ed6e593a4937)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *           Copyright (c) 1985-2007 AT&T Knowledge Ventures            *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                      by AT&T Knowledge Ventures                      *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                 Glenn Fowler <gsf@research.att.com>                  *
18 *                  David Korn <dgk@research.att.com>                   *
19 *                   Phong Vo <kpv@research.att.com>                    *
20 *                                                                      *
21 ***********************************************************************/
22 #pragma prototyped
23 /*
24  * Glenn Fowler
25  * AT&T Research
26  *
27  * pwd library support
28  */
29 
30 #include <ast.h>
31 
32 #if _WINIX
33 
34 NoN(getcwd)
35 
36 #else
37 
38 #include <ast_dir.h>
39 #include <error.h>
40 #include <fs3d.h>
41 
42 #ifndef ERANGE
43 #define ERANGE		E2BIG
44 #endif
45 
46 #define ERROR(e)	{ errno = e; goto error; }
47 
48 struct dirlist				/* long path chdir(2) component	*/
49 {
50 	struct dirlist*	next;		/* next component		*/
51 	int		index;		/* index from end of buf	*/
52 };
53 
54 /*
55  * pop long dir component chdir stack
56  */
57 
58 static int
59 popdir(register struct dirlist* d, register char* end)
60 {
61 	register struct dirlist*	dp;
62 	int				v;
63 
64 	v = 0;
65 	while (dp = d)
66 	{
67 		d = d->next;
68 		if (!v)
69 		{
70 			if (d) *(end - d->index - 1) = 0;
71 			v = chdir(end - dp->index);
72 			if (d) *(end - d->index - 1) = '/';
73 		}
74 		free(dp);
75 	}
76 	return v;
77 }
78 
79 /*
80  * push long dir component onto stack
81  */
82 
83 static struct dirlist*
84 pushdir(register struct dirlist* d, char* dots, char* path, char* end)
85 {
86 	register struct dirlist*	p;
87 
88 	if (!(p = newof(0, struct dirlist, 1, 0)) || chdir(dots))
89 	{
90 		if (p) free(p);
91 		if (d) popdir(d, end);
92 		return 0;
93 	}
94 	p->index = end - path;
95 	p->next = d;
96 	return p;
97 }
98 
99 /*
100  * return a pointer to the absolute path name of .
101  * this path name may be longer than PATH_MAX
102  *
103  * a few environment variables are checked before the search algorithm
104  * return value is placed in buf of len chars
105  * if buf is 0 then space is allocated via malloc() with
106  * len extra chars after the path name
107  * 0 is returned on error with errno set as appropriate
108  */
109 
110 char*
111 getcwd(char* buf, size_t len)
112 {
113 	register char*	d;
114 	register char*	p;
115 	register char*	s;
116 	DIR*		dirp = 0;
117 	int		n;
118 	int		x;
119 	size_t		namlen;
120 	ssize_t		extra = -1;
121 	struct dirent*	entry;
122 	struct dirlist*	dirstk = 0;
123 	struct stat*	cur;
124 	struct stat*	par;
125 	struct stat*	tmp;
126 	struct stat	curst;
127 	struct stat	parst;
128 	struct stat	tstst;
129 	char		dots[PATH_MAX];
130 
131 	static struct
132 	{
133 		char*	name;
134 		char*	path;
135 		dev_t	dev;
136 		ino_t	ino;
137 	}		env[] =
138 	{
139 		{ /*previous*/0	},
140 		{ "PWD"		},
141 		{ "HOME"	},
142 	};
143 
144 	if (buf && !len) ERROR(EINVAL);
145 	if (fs3d(FS3D_TEST) && (namlen = mount(".", dots, FS3D_GET|FS3D_VIEW|FS3D_SIZE(sizeof(dots)), NiL)) > 1 && namlen < sizeof(dots))
146 	{
147 		p = dots;
148 	easy:
149 		namlen++;
150 		if (buf)
151 		{
152 			if (len < namlen) ERROR(ERANGE);
153 		}
154 		else if (!(buf = newof(0, char, namlen, len))) ERROR(ENOMEM);
155 		return (char*)memcpy(buf, p, namlen);
156 	}
157 	cur = &curst;
158 	par = &parst;
159 	if (stat(".", par)) ERROR(errno);
160 	for (n = 0; n < elementsof(env); n++)
161 	{
162 		if ((env[n].name && (p = getenv(env[n].name)) || (p = env[n].path)) && *p == '/' && !stat(p, cur))
163 		{
164 			env[n].path = p;
165 			env[n].dev = cur->st_dev;
166 			env[n].ino = cur->st_ino;
167 			if (cur->st_ino == par->st_ino && cur->st_dev == par->st_dev)
168 			{
169 				namlen = strlen(p);
170 				goto easy;
171 			}
172 		}
173 	}
174 	if (!buf)
175 	{
176 		extra = len;
177 		len = PATH_MAX;
178 		if (!(buf = newof(0, char, len, extra))) ERROR(ENOMEM);
179 	}
180 	d = dots;
181 	p = buf + len - 1;
182 	*p = 0;
183 	n = elementsof(env);
184 	for (;;)
185 	{
186 		tmp = cur;
187 		cur = par;
188 		par = tmp;
189 		if ((d - dots) > (PATH_MAX - 4))
190 		{
191 			if (!(dirstk = pushdir(dirstk, dots, p, buf + len - 1))) ERROR(ERANGE);
192 			d = dots;
193 		}
194 		*d++ = '.';
195 		*d++ = '.';
196 		*d = 0;
197 		if (!(dirp = opendir(dots))) ERROR(errno);
198 #if !_dir_ok || _mem_dd_fd_DIR
199 		if (fstat(dirp->dd_fd, par)) ERROR(errno);
200 #else
201 		if (stat(dots, par)) ERROR(errno);
202 #endif
203 		*d++ = '/';
204 		if (par->st_dev == cur->st_dev)
205 		{
206 			if (par->st_ino == cur->st_ino)
207 			{
208 				closedir(dirp);
209 				*--p = '/';
210 			pop:
211 				if (p != buf)
212 				{
213 					d = buf;
214 					while (*d++ = *p++);
215 					len = d - buf;
216 					if (extra >= 0 && !(buf = newof(buf, char, len, extra))) ERROR(ENOMEM);
217 				}
218 				if (dirstk && popdir(dirstk, buf + len - 1))
219 				{
220 					dirstk = 0;
221 					ERROR(errno);
222 				}
223 				if (env[0].path)
224 					free(env[0].path);
225 				env[0].path = strdup(buf);
226 				return buf;
227 			}
228 #ifdef D_FILENO
229 			while (entry = readdir(dirp))
230 				if (D_FILENO(entry) == cur->st_ino)
231 				{
232 					namlen = D_NAMLEN(entry);
233 					goto found;
234 				}
235 #endif
236 
237 			/*
238 			 * this fallthrough handles logical naming
239 			 */
240 
241 			rewinddir(dirp);
242 		}
243 		do
244 		{
245 			if (!(entry = readdir(dirp))) ERROR(ENOENT);
246 			namlen = D_NAMLEN(entry);
247 			if ((d - dots) > (PATH_MAX - 1 - namlen))
248 			{
249 				*d = 0;
250 				if (namlen >= PATH_MAX || !(dirstk = pushdir(dirstk, dots + 3, p, buf + len - 1))) ERROR(ERANGE);
251 				d = dots + 3;
252 			}
253 			memcpy(d, entry->d_name, namlen + 1);
254 		} while (stat(dots, &tstst) || tstst.st_ino != cur->st_ino || tstst.st_dev != cur->st_dev);
255 	found:
256 		if (*p) *--p = '/';
257 		while ((p -= namlen) <= (buf + 1))
258 		{
259 			x = (buf + len - 1) - (p += namlen);
260 			s = buf + len;
261 			if (extra < 0 || !(buf = newof(buf, char, len += PATH_MAX, extra))) ERROR(ERANGE);
262 			p = buf + len;
263 			while (p > buf + len - 1 - x) *--p = *--s;
264 		}
265 		if (n < elementsof(env))
266 		{
267 			memcpy(p, env[n].path, namlen);
268 			goto pop;
269 		}
270 		memcpy(p, entry->d_name, namlen);
271 		closedir(dirp);
272 		dirp = 0;
273 		for (n = 0; n < elementsof(env); n++)
274 			if (env[n].ino == par->st_ino && env[n].dev == par->st_dev)
275 			{
276 				namlen = strlen(env[n].path);
277 				goto found;
278 			}
279 	}
280  error:
281 	if (buf)
282 	{
283 		if (dirstk) popdir(dirstk, buf + len - 1);
284 		if (extra >= 0) free(buf);
285 	}
286 	if (dirp) closedir(dirp);
287 	return 0;
288 }
289 
290 #endif
291