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
34NoN(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
46char*
47getcwd(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
90struct 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
100static int
101popdir(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
125static struct dirlist*
126pushdir(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
152char*
153getcwd(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