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