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