1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2011 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
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 "FEATURE/syscall"
39 
40 #if defined(SYSGETCWD)
41 
42 #include <error.h>
43 
44 #define ERROR(e)	{ errno = e; return 0; }
45 
46 char*
47 getcwd(char* buf, size_t len)
48 {
49 	size_t		n;
50 	size_t		r;
51 	int		oerrno;
52 
53 	if (buf)
54 		return SYSGETCWD(buf, len) < 0 ? 0 : buf;
55 	oerrno = errno;
56 	n = PATH_MAX;
57 	for (;;)
58 	{
59 		if (!(buf = newof(buf, char, n, 0)))
60 			ERROR(ENOMEM);
61 		if (SYSGETCWD(buf, n) >= 0)
62 		{
63 			if ((r = strlen(buf) + len + 1) != n && !(buf = newof(buf, char, r, 0)))
64 				ERROR(ENOMEM);
65 			break;
66 		}
67 		if (errno != ERANGE)
68 		{
69 			free(buf);
70 			return 0;
71 		}
72 		n += PATH_MAX / 4;
73 	}
74 	errno = oerrno;
75 	return buf;
76 }
77 
78 #else
79 
80 #include <ast_dir.h>
81 #include <error.h>
82 #include <fs3d.h>
83 
84 #ifndef ERANGE
85 #define ERANGE		E2BIG
86 #endif
87 
88 #define ERROR(e)	{ errno = e; goto error; }
89 
90 struct dirlist				/* long path chdir(2) component	*/
91 {
92 	struct dirlist*	next;		/* next component		*/
93 	int		index;		/* index from end of buf	*/
94 };
95 
96 /*
97  * pop long dir component chdir stack
98  */
99 
100 static int
101 popdir(register struct dirlist* d, register char* end)
102 {
103 	register struct dirlist*	dp;
104 	int				v;
105 
106 	v = 0;
107 	while (dp = d)
108 	{
109 		d = d->next;
110 		if (!v)
111 		{
112 			if (d) *(end - d->index - 1) = 0;
113 			v = chdir(end - dp->index);
114 			if (d) *(end - d->index - 1) = '/';
115 		}
116 		free(dp);
117 	}
118 	return v;
119 }
120 
121 /*
122  * push long dir component onto stack
123  */
124 
125 static struct dirlist*
126 pushdir(register struct dirlist* d, char* dots, char* path, char* end)
127 {
128 	register struct dirlist*	p;
129 
130 	if (!(p = newof(0, struct dirlist, 1, 0)) || chdir(dots))
131 	{
132 		if (p) free(p);
133 		if (d) popdir(d, end);
134 		return 0;
135 	}
136 	p->index = end - path;
137 	p->next = d;
138 	return p;
139 }
140 
141 /*
142  * return a pointer to the absolute path name of .
143  * this path name may be longer than PATH_MAX
144  *
145  * a few environment variables are checked before the search algorithm
146  * return value is placed in buf of len chars
147  * if buf is 0 then space is allocated via malloc() with
148  * len extra chars after the path name
149  * 0 is returned on error with errno set as appropriate
150  */
151 
152 char*
153 getcwd(char* buf, size_t len)
154 {
155 	register char*	d;
156 	register char*	p;
157 	register char*	s;
158 	DIR*		dirp = 0;
159 	int		n;
160 	int		x;
161 	size_t		namlen;
162 	ssize_t		extra = -1;
163 	struct dirent*	entry;
164 	struct dirlist*	dirstk = 0;
165 	struct stat*	cur;
166 	struct stat*	par;
167 	struct stat*	tmp;
168 	struct stat	curst;
169 	struct stat	parst;
170 	struct stat	tstst;
171 	char		dots[PATH_MAX];
172 
173 	static struct
174 	{
175 		char*	name;
176 		char*	path;
177 		dev_t	dev;
178 		ino_t	ino;
179 	}		env[] =
180 	{
181 		{ /*previous*/0	},
182 		{ "PWD"		},
183 		{ "HOME"	},
184 	};
185 
186 	if (buf && !len) ERROR(EINVAL);
187 	if (fs3d(FS3D_TEST) && (namlen = mount(".", dots, FS3D_GET|FS3D_VIEW|FS3D_SIZE(sizeof(dots)), NiL)) > 1 && namlen < sizeof(dots))
188 	{
189 		p = dots;
190 	easy:
191 		namlen++;
192 		if (buf)
193 		{
194 			if (len < namlen) ERROR(ERANGE);
195 		}
196 		else if (!(buf = newof(0, char, namlen, len))) ERROR(ENOMEM);
197 		return (char*)memcpy(buf, p, namlen);
198 	}
199 	cur = &curst;
200 	par = &parst;
201 	if (stat(".", par)) ERROR(errno);
202 	for (n = 0; n < elementsof(env); n++)
203 	{
204 		if ((env[n].name && (p = getenv(env[n].name)) || (p = env[n].path)) && *p == '/' && !stat(p, cur))
205 		{
206 			env[n].path = p;
207 			env[n].dev = cur->st_dev;
208 			env[n].ino = cur->st_ino;
209 			if (cur->st_ino == par->st_ino && cur->st_dev == par->st_dev)
210 			{
211 				namlen = strlen(p);
212 				goto easy;
213 			}
214 		}
215 	}
216 	if (!buf)
217 	{
218 		extra = len;
219 		len = PATH_MAX;
220 		if (!(buf = newof(0, char, len, extra))) ERROR(ENOMEM);
221 	}
222 	d = dots;
223 	p = buf + len - 1;
224 	*p = 0;
225 	n = elementsof(env);
226 	for (;;)
227 	{
228 		tmp = cur;
229 		cur = par;
230 		par = tmp;
231 		if ((d - dots) > (PATH_MAX - 4))
232 		{
233 			if (!(dirstk = pushdir(dirstk, dots, p, buf + len - 1))) ERROR(ERANGE);
234 			d = dots;
235 		}
236 		*d++ = '.';
237 		*d++ = '.';
238 		*d = 0;
239 		if (!(dirp = opendir(dots))) ERROR(errno);
240 #if !_dir_ok || _mem_dd_fd_DIR
241 		if (fstat(dirp->dd_fd, par)) ERROR(errno);
242 #else
243 		if (stat(dots, par)) ERROR(errno);
244 #endif
245 		*d++ = '/';
246 		if (par->st_dev == cur->st_dev)
247 		{
248 			if (par->st_ino == cur->st_ino)
249 			{
250 				closedir(dirp);
251 				*--p = '/';
252 			pop:
253 				if (p != buf)
254 				{
255 					d = buf;
256 					while (*d++ = *p++);
257 					len = d - buf;
258 					if (extra >= 0 && !(buf = newof(buf, char, len, extra))) ERROR(ENOMEM);
259 				}
260 				if (dirstk && popdir(dirstk, buf + len - 1))
261 				{
262 					dirstk = 0;
263 					ERROR(errno);
264 				}
265 				if (env[0].path)
266 					free(env[0].path);
267 				env[0].path = strdup(buf);
268 				return buf;
269 			}
270 #ifdef D_FILENO
271 			while (entry = readdir(dirp))
272 				if (D_FILENO(entry) == cur->st_ino)
273 				{
274 					namlen = D_NAMLEN(entry);
275 					goto found;
276 				}
277 #endif
278 
279 			/*
280 			 * this fallthrough handles logical naming
281 			 */
282 
283 			rewinddir(dirp);
284 		}
285 		do
286 		{
287 			if (!(entry = readdir(dirp))) ERROR(ENOENT);
288 			namlen = D_NAMLEN(entry);
289 			if ((d - dots) > (PATH_MAX - 1 - namlen))
290 			{
291 				*d = 0;
292 				if (namlen >= PATH_MAX || !(dirstk = pushdir(dirstk, dots + 3, p, buf + len - 1))) ERROR(ERANGE);
293 				d = dots + 3;
294 			}
295 			memcpy(d, entry->d_name, namlen + 1);
296 		} while (stat(dots, &tstst) || tstst.st_ino != cur->st_ino || tstst.st_dev != cur->st_dev);
297 	found:
298 		if (*p) *--p = '/';
299 		while ((p -= namlen) <= (buf + 1))
300 		{
301 			x = (buf + len - 1) - (p += namlen);
302 			s = buf + len;
303 			if (extra < 0 || !(buf = newof(buf, char, len += PATH_MAX, extra))) ERROR(ERANGE);
304 			p = buf + len;
305 			while (p > buf + len - 1 - x) *--p = *--s;
306 		}
307 		if (n < elementsof(env))
308 		{
309 			memcpy(p, env[n].path, namlen);
310 			goto pop;
311 		}
312 		memcpy(p, entry->d_name, namlen);
313 		closedir(dirp);
314 		dirp = 0;
315 		for (n = 0; n < elementsof(env); n++)
316 			if (env[n].ino == par->st_ino && env[n].dev == par->st_dev)
317 			{
318 				namlen = strlen(env[n].path);
319 				goto found;
320 			}
321 	}
322  error:
323 	if (buf)
324 	{
325 		if (dirstk) popdir(dirstk, buf + len - 1);
326 		if (extra >= 0) free(buf);
327 	}
328 	if (dirp) closedir(dirp);
329 	return 0;
330 }
331 
332 #endif
333 
334 #endif
335