xref: /netbsd-src/usr.bin/make/str.c (revision ce63d6c20fc4ec8ddc95c84bb229e3c4ecf82b69)
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
40 static 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  */
53 char *
54 str_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  */
101 char **
102 brk_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 	}
198 done:	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  */
213 char *
214 Str_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  */
252 Str_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);
336 thisCharOK:	++pattern;
337 		++string;
338 	}
339 }
340