386BSD 0.1 development
[unix-history] / usr / src / usr.bin / make / str.c
CommitLineData
e72c71e5
WJ
1/*-
2 * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
3 * Copyright (c) 1988, 1989 by Adam de Boor
4 * Copyright (c) 1989 by Berkeley Softworks
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Adam de Boor.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39#ifndef lint
40static char sccsid[] = "@(#)str.c 5.8 (Berkeley) 6/1/90";
41#endif /* not lint */
42
43#include "make.h"
44
45/*-
46 * str_concat --
47 * concatenate the two strings, inserting a space or slash between them,
48 * freeing them if requested.
49 *
50 * returns --
51 * the resulting string in allocated space.
52 */
53char *
54str_concat(s1, s2, flags)
55 char *s1, *s2;
56 int flags;
57{
58 register int len1, len2;
59 register char *result;
60
61 /* get the length of both strings */
62 len1 = strlen(s1);
63 len2 = strlen(s2);
64
65 /* allocate length plus separator plus EOS */
66 result = emalloc((u_int)(len1 + len2 + 2));
67
68 /* copy first string into place */
69 bcopy(s1, result, len1);
70
71 /* add separator character */
72 if (flags & STR_ADDSPACE) {
73 result[len1] = ' ';
74 ++len1;
75 } else if (flags & STR_ADDSLASH) {
76 result[len1] = '/';
77 ++len1;
78 }
79
80 /* copy second string plus EOS into place */
81 bcopy(s2, result + len1, len2 + 1);
82
83 /* free original strings */
84 if (flags & STR_DOFREE) {
85 (void)free(s1);
86 (void)free(s2);
87 }
88 return(result);
89}
90
91/*-
92 * brk_string --
93 * Fracture a string into an array of words (as delineated by tabs or
94 * spaces) taking quotation marks into account. Leading tabs/spaces
95 * are ignored.
96 *
97 * returns --
98 * Pointer to the array of pointers to the words. To make life easier,
99 * the first word is always the value of the .MAKE variable.
100 */
101char **
102brk_string(str, store_argc)
103 register char *str;
104 int *store_argc;
105{
106 static int argmax, curlen;
107 static char **argv, *buf;
108 register int argc, ch;
109 register char inquote, *p, *start, *t;
110 int len;
111
112 /* save off pmake variable */
113 if (!argv) {
114 argv = (char **)emalloc((argmax = 50) * sizeof(char *));
115 argv[0] = Var_Value(".MAKE", VAR_GLOBAL);
116 }
117
118 /* skip leading space chars.
119 for (; *str == ' ' || *str == '\t'; ++str);
120
121 /* allocate room for a copy of the string */
122 if ((len = strlen(str) + 1) > curlen)
123 buf = emalloc(curlen = len);
124
125 /*
126 * copy the string; at the same time, parse backslashes,
127 * quotes and build the argument list.
128 */
129 argc = 1;
130 inquote = '\0';
131 for (p = str, start = t = buf;; ++p) {
132 switch(ch = *p) {
133 case '"':
134 case '\'':
135 if (inquote)
136 if (inquote == ch)
137 inquote = NULL;
138 else
139 break;
140 else
141 inquote = ch;
142 continue;
143 case ' ':
144 case '\t':
145 if (inquote)
146 break;
147 if (!start)
148 continue;
149 /* FALLTHROUGH */
150 case '\n':
151 case '\0':
152 /*
153 * end of a token -- make sure there's enough argv
154 * space and save off a pointer.
155 */
156 *t++ = '\0';
157 if (argc == argmax) {
158 argmax *= 2; /* ramp up fast */
159 if (!(argv = (char **)realloc(argv,
160 argmax * sizeof(char *))))
161 enomem();
162 }
163 argv[argc++] = start;
164 start = (char *)NULL;
165 if (ch == '\n' || ch == '\0')
166 goto done;
167 continue;
168 case '\\':
169 switch (ch = *++p) {
170 case '\0':
171 case '\n':
172 /* hmmm; fix it up as best we can */
173 ch = '\\';
174 --p;
175 break;
176 case 'b':
177 ch = '\b';
178 break;
179 case 'f':
180 ch = '\f';
181 break;
182 case 'n':
183 ch = '\n';
184 break;
185 case 'r':
186 ch = '\r';
187 break;
188 case 't':
189 ch = '\t';
190 break;
191 }
192 break;
193 }
194 if (!start)
195 start = t;
196 *t++ = ch;
197 }
198done: argv[argc] = (char *)NULL;
199 *store_argc = argc;
200 return(argv);
201}
202
203/*
204 * Str_FindSubstring -- See if a string contains a particular substring.
205 *
206 * Results: If string contains substring, the return value is the location of
207 * the first matching instance of substring in string. If string doesn't
208 * contain substring, the return value is NULL. Matching is done on an exact
209 * character-for-character basis with no wildcards or special characters.
210 *
211 * Side effects: None.
212 */
213char *
214Str_FindSubstring(string, substring)
215 register char *string; /* String to search. */
216 char *substring; /* Substring to find in string */
217{
218 register char *a, *b;
219
220 /*
221 * First scan quickly through the two strings looking for a single-
222 * character match. When it's found, then compare the rest of the
223 * substring.
224 */
225
226 for (b = substring; *string != 0; string += 1) {
227 if (*string != *b)
228 continue;
229 a = string;
230 for (;;) {
231 if (*b == 0)
232 return(string);
233 if (*a++ != *b++)
234 break;
235 }
236 b = substring;
237 }
238 return((char *) NULL);
239}
240
241/*
242 * Str_Match --
243 *
244 * See if a particular string matches a particular pattern.
245 *
246 * Results: Non-zero is returned if string matches pattern, 0 otherwise. The
247 * matching operation permits the following special characters in the
248 * pattern: *?\[] (see the man page for details on what these mean).
249 *
250 * Side effects: None.
251 */
252Str_Match(string, pattern)
253 register char *string; /* String */
254 register char *pattern; /* Pattern */
255{
256 char c2;
257
258 for (;;) {
259 /*
260 * See if we're at the end of both the pattern and the
261 * string. If, we succeeded. If we're at the end of the
262 * pattern but not at the end of the string, we failed.
263 */
264 if (*pattern == 0)
265 return(!*string);
266 if (*string == 0 && *pattern != '*')
267 return(0);
268 /*
269 * Check for a "*" as the next pattern character. It matches
270 * any substring. We handle this by calling ourselves
271 * recursively for each postfix of string, until either we
272 * match or we reach the end of the string.
273 */
274 if (*pattern == '*') {
275 pattern += 1;
276 if (*pattern == 0)
277 return(1);
278 while (*string != 0) {
279 if (Str_Match(string, pattern))
280 return(1);
281 ++string;
282 }
283 return(0);
284 }
285 /*
286 * Check for a "?" as the next pattern character. It matches
287 * any single character.
288 */
289 if (*pattern == '?')
290 goto thisCharOK;
291 /*
292 * Check for a "[" as the next pattern character. It is
293 * followed by a list of characters that are acceptable, or
294 * by a range (two characters separated by "-").
295 */
296 if (*pattern == '[') {
297 ++pattern;
298 for (;;) {
299 if ((*pattern == ']') || (*pattern == 0))
300 return(0);
301 if (*pattern == *string)
302 break;
303 if (pattern[1] == '-') {
304 c2 = pattern[2];
305 if (c2 == 0)
306 return(0);
307 if ((*pattern <= *string) &&
308 (c2 >= *string))
309 break;
310 if ((*pattern >= *string) &&
311 (c2 <= *string))
312 break;
313 pattern += 2;
314 }
315 ++pattern;
316 }
317 while ((*pattern != ']') && (*pattern != 0))
318 ++pattern;
319 goto thisCharOK;
320 }
321 /*
322 * If the next pattern character is '/', just strip off the
323 * '/' so we do exact matching on the character that follows.
324 */
325 if (*pattern == '\\') {
326 ++pattern;
327 if (*pattern == 0)
328 return(0);
329 }
330 /*
331 * There's no special character. Just make sure that the
332 * next characters of each string match.
333 */
334 if (*pattern != *string)
335 return(0);
336thisCharOK: ++pattern;
337 ++string;
338 }
339}