1 /*	$OpenBSD: util.c,v 1.11 2006/04/26 20:19:25 sturm Exp $	*/
2 /*
3  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *      This product includes software developed by Niels Provos.
17  * 4. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/types.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <stdio.h>
36 #include <err.h>
37 
38 #include "util.h"
39 
40 char *
strescape(char * str)41 strescape(char *str)
42 {
43 	static char escape[8192];
44 	int i, p;
45 
46 	for (p = i = 0; i < strlen(str) && p < sizeof(escape) - 1; i++) {
47 		char a = str[i];
48 		switch (a) {
49 		case '\r':
50 			a = 'r';
51 			goto doescape;
52 		case '\n':
53 			a = 'n';
54 			goto doescape;
55 		case '\\':
56 		case '\"':
57 		doescape:
58 			escape[p++] = '\\';
59 			if (p >= sizeof(escape) - 1)
60 				errx(1, "%s: string too long: %s",
61 				    __func__, str);
62 			/* FALLTHROUGH */
63 		default:
64 			escape[p++] = a;
65 		}
66 	}
67 
68 	escape[p] = '\0';
69 	return (escape);
70 }
71 
72 char *
strrpl(char * str,size_t size,char * match,char * value)73 strrpl(char *str, size_t size, char *match, char *value)
74 {
75 	char *p, *e;
76 	int len, rlen;
77 
78 	p = str;
79 	e = p + strlen(p);
80 	len = strlen(match);
81 
82 	/* Try to match against the variable */
83 	while ((p = strchr(p, match[0])) != NULL) {
84 		if (!strncmp(p, match, len) && !isalnum(p[len]))
85 			break;
86 		p += len;
87 
88 		if (p >= e)
89 			return (NULL);
90 	}
91 
92 	if (p == NULL)
93 		return (NULL);
94 
95 	rlen = strlen(value);
96 
97 	if (strlen(str) - len + rlen > size)
98 		return (NULL);
99 
100 	memmove(p + rlen, p + len, strlen(p + len) + 1);
101 	memcpy(p, value, rlen);
102 
103 	return (p);
104 }
105 
106 /* simplify_path is from pdksh and apparently in the public domain */
107 
108 /* ISABSPATH() means path is fully and completely specified,
109  * ISROOTEDPATH() means a .. as the first component is a no-op,
110  * ISRELPATH() means $PWD can be tacked on to get an absolute path.
111  *
112  * OS		Path		ISABSPATH	ISROOTEDPATH	ISRELPATH
113  * unix		/foo		yes		yes		no
114  * unix		foo		no		no		yes
115  * unix		../foo		no		no		yes
116  * os2+cyg	a:/foo		yes		yes		no
117  * os2+cyg	a:foo		no		no		no
118  * os2+cyg	/foo		no		yes		no
119  * os2+cyg	foo		no		no		yes
120  * os2+cyg	../foo		no		no		yes
121  * cyg 		//foo		yes		yes		no
122  */
123 #ifdef OS2
124 # define PATHSEP	';'
125 # define DIRSEP		'/'	/* even though \ is native */
126 # define DIRSEPSTR	"\\"
127 # define ISDIRSEP(c)    ((c) == '\\' || (c) == '/')
128 # define ISABSPATH(s)	(((s)[0] && (s)[1] == ':' && ISDIRSEP((s)[2])))
129 # define ISROOTEDPATH(s) (ISDIRSEP((s)[0]) || ISABSPATH(s))
130 # define ISRELPATH(s)	(!(s)[0] || ((s)[1] != ':' && !ISDIRSEP((s)[0])))
131 # define FILECHCONV(c)	(isascii(c) && isupper(c) ? tolower(c) : c)
132 # define FILECMP(s1, s2) stricmp(s1, s2)
133 # define FILENCMP(s1, s2, n) strnicmp(s1, s2, n)
134 extern char *ksh_strchr_dirsep(const char *path);
135 extern char *ksh_strrchr_dirsep(const char *path);
136 # define chdir		_chdir2
137 # define getcwd		_getcwd2
138 #else
139 # define PATHSEP	':'
140 # define DIRSEP		'/'
141 # define DIRSEPSTR	"/"
142 # define ISDIRSEP(c)    ((c) == '/')
143 #ifdef __CYGWIN__
144 #  define ISABSPATH(s) \
145 	(((s)[0] && (s)[1] == ':' && ISDIRSEP((s)[2])) || ISDIRSEP((s)[0]))
146 #  define ISRELPATH(s) (!(s)[0] || ((s)[1] != ':' && !ISDIRSEP((s)[0])))
147 #else /* __CYGWIN__ */
148 # define ISABSPATH(s)	ISDIRSEP((s)[0])
149 # define ISRELPATH(s)	(!ISABSPATH(s))
150 #endif /* __CYGWIN__ */
151 # define ISROOTEDPATH(s) ISABSPATH(s)
152 # define FILECHCONV(c)	c
153 # define FILECMP(s1, s2) strcmp(s1, s2)
154 # define FILENCMP(s1, s2, n) strncmp(s1, s2, n)
155 # define ksh_strchr_dirsep(p)   strchr(p, DIRSEP)
156 # define ksh_strrchr_dirsep(p)  strrchr(p, DIRSEP)
157 #endif
158 
159 /* simplify_path is from pdksh */
160 
161 /*
162  * Simplify pathnames containing "." and ".." entries.
163  * ie, simplify_path("/a/b/c/./../d/..") returns "/a/b"
164  */
165 void
simplify_path(path)166 simplify_path(path)
167 	char	*path;
168 {
169 	char	*cur;
170 	char	*t;
171 	int	isrooted;
172 	char	*very_start = path;
173 	char	*start;
174 
175 	if (!*path)
176 		return;
177 
178 	if ((isrooted = ISROOTEDPATH(path)))
179 		very_start++;
180 #if defined (OS2) || defined (__CYGWIN__)
181 	if (path[0] && path[1] == ':')	/* skip a: */
182 		very_start += 2;
183 #endif /* OS2 || __CYGWIN__ */
184 
185 	/* Before			After
186 	 *  /foo/			/foo
187 	 *  /foo/../../bar		/bar
188 	 *  /foo/./blah/..		/foo
189 	 *  .				.
190 	 *  ..				..
191 	 *  ./foo			foo
192 	 *  foo/../../../bar		../../bar
193 	 * OS2 and CYGWIN:
194 	 *  a:/foo/../..		a:/
195 	 *  a:.				a:
196 	 *  a:..			a:..
197 	 *  a:foo/../../blah		a:../blah
198 	 */
199 
200 #ifdef __CYGWIN__
201 	/* preserve leading double-slash on pathnames (for UNC paths) */
202 	if (path[0] && ISDIRSEP(path[0]) && path[1] && ISDIRSEP(path[1]))
203 		very_start++;
204 #endif /* __CYGWIN__ */
205 
206 	for (cur = t = start = very_start; ; ) {
207 		/* treat multiple '/'s as one '/' */
208 		while (ISDIRSEP(*t))
209 			t++;
210 
211 		if (*t == '\0') {
212 			if (cur == path)
213 				/* convert empty path to dot */
214 				*cur++ = '.';
215 			*cur = '\0';
216 			break;
217 		}
218 
219 		if (t[0] == '.') {
220 			if (!t[1] || ISDIRSEP(t[1])) {
221 				t += 1;
222 				continue;
223 			} else if (t[1] == '.' && (!t[2] || ISDIRSEP(t[2]))) {
224 				if (!isrooted && cur == start) {
225 					if (cur != very_start)
226 						*cur++ = DIRSEP;
227 					*cur++ = '.';
228 					*cur++ = '.';
229 					start = cur;
230 				} else if (cur != start)
231 					while (--cur > start && !ISDIRSEP(*cur))
232 						;
233 				t += 2;
234 				continue;
235 			}
236 		}
237 
238 		if (cur != very_start)
239 			*cur++ = DIRSEP;
240 
241 		/* find/copy next component of pathname */
242 		while (*t && !ISDIRSEP(*t))
243 			*cur++ = *t++;
244 	}
245 }
246