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, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*	Copyright (c) 1988 AT&T	*/
23/*	  All Rights Reserved  	*/
24
25
26/*
27 * Copyright (c) 1999 by Sun Microsystems, Inc.
28 * All rights reserved.
29 */
30
31#pragma ident	"%Z%%M%	%I%	%E% SMI"
32
33/*
34 *	compath(pathname)
35 *
36 *	This compresses pathnames.  All strings of multiple slashes are
37 *	changed to a single slash.  All occurrences of "./" are removed.
38 *	Whenever possible, strings of "/.." are removed together with
39 *	the directory names that they follow.
40 *
41 *	WARNING: since pathname is altered by this function, it should
42 *		 be located in a temporary buffer. This avoids the problem
43 *		 of accidently changing strings obtained from makefiles
44 *		 and stored in global structures.
45 */
46
47#include <string.h>
48
49char *
50compath(char *pathname)
51{
52	char	*nextchar;
53	char	*lastchar;
54	char	*sofar;
55	char	*pnend;
56
57	int	pnlen;
58
59		/*
60		 *	do not change the path if it has no "/"
61		 */
62
63	if (strchr(pathname, '/') == 0)
64		return (pathname);
65
66		/*
67		 *	find all strings consisting of more than one '/'
68		 */
69
70	for (lastchar = pathname + 1; *lastchar != '\0'; lastchar++)
71		if ((*lastchar == '/') && (*(lastchar - 1) == '/')) {
72
73			/*
74			 *	find the character after the last slash
75			 */
76
77			nextchar = lastchar;
78			while (*++lastchar == '/') {
79			}
80
81			/*
82			 *	eliminate the extra slashes by copying
83			 *	everything after the slashes over the slashes
84			 */
85
86			sofar = nextchar;
87			while ((*nextchar++ = *lastchar++) != '\0')
88				;
89			lastchar = sofar;
90		}
91
92		/*
93		 *	find all strings of "./"
94		 */
95
96	for (lastchar = pathname + 1; *lastchar != '\0'; lastchar++)
97		if ((*lastchar == '/') && (*(lastchar - 1) == '.') &&
98		    ((lastchar - 1 == pathname) || (*(lastchar - 2) == '/'))) {
99
100			/*
101			 *	copy everything after the "./" over the "./"
102			 */
103
104			nextchar = lastchar - 1;
105			sofar = nextchar;
106			while ((*nextchar++ = *++lastchar) != '\0')
107				;
108			lastchar = sofar;
109		}
110
111		/*
112		 *	find each occurrence of "/.."
113		 */
114
115	for (lastchar = pathname + 1; *lastchar != '\0'; lastchar++)
116		if ((lastchar != pathname) && (*lastchar == '/') &&
117		    (*(lastchar + 1) == '.') && (*(lastchar + 2) == '.') &&
118		    ((*(lastchar + 3) == '/') || (*(lastchar + 3) == '\0'))) {
119
120			/*
121			 *	find the directory name preceding the "/.."
122			 */
123
124			nextchar = lastchar - 1;
125			while ((nextchar != pathname) &&
126			    (*(nextchar - 1) != '/'))
127				--nextchar;
128
129			/*
130			 *	make sure the preceding directory's name
131			 *	is not "." or ".."
132			 */
133
134			if ((*nextchar == '.') &&
135			    (*(nextchar + 1) == '/') ||
136			    ((*(nextchar + 1) == '.') &&
137			    (*(nextchar + 2) == '/'))) {
138				/* EMPTY */;
139			} else {
140
141				/*
142				 * 	prepare to eliminate either
143				 *	"dir_name/../" or "dir_name/.."
144				 */
145
146				if (*(lastchar + 3) == '/')
147					lastchar += 4;
148				else
149					lastchar += 3;
150
151				/*
152				 *	copy everything after the "/.." to
153				 *	before the preceding directory name
154				 */
155
156				sofar = nextchar - 1;
157				while ((*nextchar++ = *lastchar++) != '\0');
158
159				lastchar = sofar;
160
161				/*
162				 *	if the character before what was taken
163				 *	out is '/', set up to check if the
164				 *	slash is part of "/.."
165				 */
166
167				if ((sofar + 1 != pathname) && (*sofar == '/'))
168					--lastchar;
169			}
170		}
171
172	/*
173	 *	if the string is more than a character long and ends
174	 *	in '/', eliminate the '/'.
175	 */
176
177	pnlen = strlen(pathname);
178	pnend = strchr(pathname, '\0') - 1;
179
180	if ((pnlen > 1) && (*pnend == '/')) {
181		*pnend-- = '\0';
182		pnlen--;
183	}
184
185	/*
186	 *	if the string has more than two characters and ends in
187	 *	"/.", remove the "/.".
188	 */
189
190	if ((pnlen > 2) && (*(pnend - 1) == '/') && (*pnend == '.'))
191		*--pnend = '\0';
192
193	/*
194	 *	if all characters were deleted, return ".";
195	 *	otherwise return pathname
196	 */
197
198	if (*pathname == '\0')
199		(void) strcpy(pathname, ".");
200
201	return (pathname);
202}
203