1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28/*	  All Rights Reserved  	*/
29
30
31#pragma ident	"%Z%%M%	%I%	%E% SMI"
32/*
33 *	UNIX shell
34 */
35
36#include	"mac.h"
37#include	<errno.h>
38#include	<sys/types.h>
39#include	<sys/stat.h>
40#include	<limits.h>
41#include	"defs.h"
42
43#define	DOT		'.'
44#define	NULL	0
45#define	SLASH	'/'
46#define PARTLY	2
47
48static void rmslash();
49#ifdef __STDC__
50extern const char	longpwd[];
51#else
52extern char	longpwd[];
53#endif
54extern char *getcwd();
55
56unsigned char cwdname[PATH_MAX+1];
57
58static int 	didpwd = FALSE;
59
60void
61cwd(unsigned char *dir)
62{
63	unsigned char *pcwd;
64	unsigned char *pdir;
65
66	/* First remove extra /'s */
67
68	rmslash(dir);
69
70	/* Now remove any .'s */
71
72	pdir = dir;
73	if(*dir == SLASH)
74		pdir++;
75	while(*pdir) 			/* remove /./ by itself */
76	{
77		if((*pdir==DOT) && (*(pdir+1)==SLASH))
78		{
79			movstr(pdir+2, pdir);
80			continue;
81		}
82		pdir++;
83		while ((*pdir) && (*pdir != SLASH))
84			pdir++;
85		if (*pdir)
86			pdir++;
87	}
88	/* take care of trailing /. */
89	if(*(--pdir)==DOT && pdir > dir && *(--pdir)==SLASH) {
90		if(pdir > dir) {
91			*pdir = NULL;
92		} else {
93			*(pdir+1) = NULL;
94		}
95
96	}
97
98	/* Remove extra /'s */
99
100	rmslash(dir);
101
102	/* Now that the dir is canonicalized, process it */
103
104	if(*dir==DOT && *(dir+1)==NULL)
105	{
106		return;
107	}
108
109
110	if(*dir==SLASH)
111	{
112		/* Absolute path */
113
114		pcwd = cwdname;
115		*pcwd++ = *dir++;
116		didpwd = PARTLY;
117	}
118	else
119	{
120		/* Relative path */
121
122		if (didpwd == FALSE)
123			return;
124		didpwd = PARTLY;
125		pcwd = cwdname + length(cwdname) - 1;
126		if(pcwd != cwdname+1)
127			*pcwd++ = SLASH;
128	}
129	while(*dir)
130	{
131		if(*dir==DOT &&
132		   *(dir+1)==DOT &&
133		   (*(dir+2)==SLASH || *(dir+2)==NULL))
134		{
135			/* Parent directory, so backup one */
136
137			if( pcwd > cwdname+2 )
138				--pcwd;
139			while(*(--pcwd) != SLASH)
140				;
141			pcwd++;
142			dir += 2;
143			if(*dir==SLASH)
144			{
145				dir++;
146			}
147			continue;
148		}
149	 	if (pcwd >= &cwdname[PATH_MAX+1])
150		{
151			didpwd=FALSE;
152			return;
153		}
154		*pcwd++ = *dir++;
155		while((*dir) && (*dir != SLASH))
156		{
157	 		if (pcwd >= &cwdname[PATH_MAX+1])
158			{
159				didpwd=FALSE;
160				return;
161			}
162			*pcwd++ = *dir++;
163		}
164		if (*dir)
165		{
166	 		if (pcwd >= &cwdname[PATH_MAX+1])
167			{
168				didpwd=FALSE;
169				return;
170			}
171			*pcwd++ = *dir++;
172		}
173	}
174	if (pcwd >= &cwdname[PATH_MAX+1])
175	{
176		didpwd=FALSE;
177		return;
178	}
179	*pcwd = NULL;
180
181	--pcwd;
182	if(pcwd>cwdname && *pcwd==SLASH)
183	{
184		/* Remove trailing / */
185
186		*pcwd = NULL;
187	}
188	return;
189}
190
191void
192cwd2()
193{
194	struct stat stat1, stat2;
195	unsigned char *pcwd;
196	/* check if there are any symbolic links in pathname */
197
198	if(didpwd == FALSE)
199		return;
200	pcwd = cwdname + 1;
201	if(didpwd == PARTLY) {
202		while (*pcwd)
203		{
204			char c;
205			while((c = *pcwd++) != SLASH && c != '\0');
206			*--pcwd = '\0';
207			if (lstat((char *)cwdname, &stat1) == -1
208		    	|| (stat1.st_mode & S_IFMT) == S_IFLNK) {
209				didpwd = FALSE;
210				*pcwd = c;
211				return;
212			}
213			*pcwd = c;
214			if(c)
215				pcwd++;
216		}
217		didpwd = TRUE;
218	} else
219		if (stat((char *)cwdname, &stat1) == -1) {
220			didpwd = FALSE;
221			return;
222		}
223	/*
224	 * check if ino's and dev's match; pathname could
225	 * consist of symbolic links with ".."
226	 */
227
228	if (stat(".", &stat2) == -1
229	    || stat1.st_dev != stat2.st_dev
230	    || stat1.st_ino != stat2.st_ino)
231		didpwd = FALSE;
232	return;
233}
234
235unsigned char *
236cwdget()
237{
238	cwd2();
239	if (didpwd == FALSE) {
240		if (getcwd((char *)cwdname, PATH_MAX+1) == NULL)
241			*cwdname = 0;
242		didpwd = TRUE;
243	}
244	return (cwdname);
245}
246
247/*
248 *	Print the current working directory.
249 */
250
251void
252cwdprint(void)
253{
254	unsigned char *cp;
255
256	cwd2();
257	if (didpwd == FALSE) {
258		if (getcwd((char *)cwdname, PATH_MAX+1) == NULL) {
259			if (errno && errno != ERANGE)
260				error(badpwd);
261			else
262				error(longpwd);
263		}
264		didpwd = TRUE;
265	}
266
267	for (cp = cwdname; *cp; cp++) {
268	  prc_buff(*cp);
269	}
270
271	prc_buff(NL);
272	return;
273}
274
275/*
276 *	This routine will remove repeated slashes from string.
277 */
278
279static void
280rmslash(string)
281	unsigned char *string;
282{
283	unsigned char *pstring;
284
285	pstring = string;
286	while(*pstring)
287	{
288		if(*pstring==SLASH && *(pstring+1)==SLASH)
289		{
290			/* Remove repeated SLASH's */
291
292			movstr(pstring+1, pstring);
293			continue;
294		}
295		pstring++;
296	}
297
298	--pstring;
299	if(pstring>string && *pstring==SLASH)
300	{
301		/* Remove trailing / */
302
303		*pstring = NULL;
304	}
305	return;
306}
307