xref: /netbsd-src/usr.bin/make/str.c (revision 2a399c6883d870daece976daec6ffa7bb7f934ce)
1 /*	$NetBSD: str.c,v 1.15 1997/09/28 03:31:10 lukem Exp $	*/
2 
3 /*-
4  * Copyright (c) 1988, 1989, 1990, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  * Copyright (c) 1989 by Berkeley Softworks
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * Adam de Boor.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *	This product includes software developed by the University of
23  *	California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  */
40 
41 #ifdef MAKE_BOOTSTRAP
42 static char rcsid[] = "$NetBSD: str.c,v 1.15 1997/09/28 03:31:10 lukem Exp $";
43 #else
44 #include <sys/cdefs.h>
45 #ifndef lint
46 #if 0
47 static char     sccsid[] = "@(#)str.c	5.8 (Berkeley) 6/1/90";
48 #else
49 __RCSID("$NetBSD: str.c,v 1.15 1997/09/28 03:31:10 lukem Exp $");
50 #endif
51 #endif				/* not lint */
52 #endif
53 
54 #include "make.h"
55 
56 static char **argv, *buffer;
57 static int argmax, curlen;
58 
59 /*
60  * str_init --
61  *	Initialize the strings package
62  *
63  */
64 void
65 str_init()
66 {
67     char *p1;
68     argv = (char **)emalloc(((argmax = 50) + 1) * sizeof(char *));
69     argv[0] = Var_Value(".MAKE", VAR_GLOBAL, &p1);
70 }
71 
72 
73 /*
74  * str_end --
75  *	Cleanup the strings package
76  *
77  */
78 void
79 str_end()
80 {
81     if (argv) {
82 	if (argv[0])
83 	    free(argv[0]);
84 	free((Address) argv);
85     }
86     if (buffer)
87 	free(buffer);
88 }
89 
90 /*-
91  * str_concat --
92  *	concatenate the two strings, inserting a space or slash between them,
93  *	freeing them if requested.
94  *
95  * returns --
96  *	the resulting string in allocated space.
97  */
98 char *
99 str_concat(s1, s2, flags)
100 	char *s1, *s2;
101 	int flags;
102 {
103 	register int len1, len2;
104 	register char *result;
105 
106 	/* get the length of both strings */
107 	len1 = strlen(s1);
108 	len2 = strlen(s2);
109 
110 	/* allocate length plus separator plus EOS */
111 	result = emalloc((u_int)(len1 + len2 + 2));
112 
113 	/* copy first string into place */
114 	memcpy(result, s1, len1);
115 
116 	/* add separator character */
117 	if (flags & STR_ADDSPACE) {
118 		result[len1] = ' ';
119 		++len1;
120 	} else if (flags & STR_ADDSLASH) {
121 		result[len1] = '/';
122 		++len1;
123 	}
124 
125 	/* copy second string plus EOS into place */
126 	memcpy(result + len1, s2, len2 + 1);
127 
128 	/* free original strings */
129 	if (flags & STR_DOFREE) {
130 		(void)free(s1);
131 		(void)free(s2);
132 	}
133 	return(result);
134 }
135 
136 /*-
137  * brk_string --
138  *	Fracture a string into an array of words (as delineated by tabs or
139  *	spaces) taking quotation marks into account.  Leading tabs/spaces
140  *	are ignored.
141  *
142  * returns --
143  *	Pointer to the array of pointers to the words.  To make life easier,
144  *	the first word is always the value of the .MAKE variable.
145  */
146 char **
147 brk_string(str, store_argc, expand)
148 	register char *str;
149 	int *store_argc;
150 	Boolean expand;
151 {
152 	register int argc, ch;
153 	register char inquote, *p, *start, *t;
154 	int len;
155 
156 	/* skip leading space chars. */
157 	for (; *str == ' ' || *str == '\t'; ++str)
158 		continue;
159 
160 	/* allocate room for a copy of the string */
161 	if ((len = strlen(str) + 1) > curlen) {
162 		if (buffer)
163 		    free(buffer);
164 		buffer = emalloc(curlen = len);
165 	}
166 
167 	/*
168 	 * copy the string; at the same time, parse backslashes,
169 	 * quotes and build the argument list.
170 	 */
171 	argc = 1;
172 	inquote = '\0';
173 	for (p = str, start = t = buffer;; ++p) {
174 		switch(ch = *p) {
175 		case '"':
176 		case '\'':
177 			if (inquote)
178 				if (inquote == ch)
179 					inquote = '\0';
180 				else
181 					break;
182 			else {
183 				inquote = (char) ch;
184 				/* Don't miss "" or '' */
185 				if (start == NULL && p[1] == inquote) {
186 					start = t + 1;
187 					break;
188 				}
189 			}
190 			if (!expand) {
191 				if (!start)
192 					start = t;
193 				*t++ = ch;
194 			}
195 			continue;
196 		case ' ':
197 		case '\t':
198 		case '\n':
199 			if (inquote)
200 				break;
201 			if (!start)
202 				continue;
203 			/* FALLTHROUGH */
204 		case '\0':
205 			/*
206 			 * end of a token -- make sure there's enough argv
207 			 * space and save off a pointer.
208 			 */
209 			if (!start)
210 			    goto done;
211 
212 			*t++ = '\0';
213 			if (argc == argmax) {
214 				argmax *= 2;		/* ramp up fast */
215 				argv = (char **)erealloc(argv,
216 				    (argmax + 1) * sizeof(char *));
217 			}
218 			argv[argc++] = start;
219 			start = (char *)NULL;
220 			if (ch == '\n' || ch == '\0')
221 				goto done;
222 			continue;
223 		case '\\':
224 			if (!expand) {
225 				if (!start)
226 					start = t;
227 				*t++ = '\\';
228 				ch = *++p;
229 				break;
230 			}
231 
232 			switch (ch = *++p) {
233 			case '\0':
234 			case '\n':
235 				/* hmmm; fix it up as best we can */
236 				ch = '\\';
237 				--p;
238 				break;
239 			case 'b':
240 				ch = '\b';
241 				break;
242 			case 'f':
243 				ch = '\f';
244 				break;
245 			case 'n':
246 				ch = '\n';
247 				break;
248 			case 'r':
249 				ch = '\r';
250 				break;
251 			case 't':
252 				ch = '\t';
253 				break;
254 			}
255 			break;
256 		}
257 		if (!start)
258 			start = t;
259 		*t++ = (char) ch;
260 	}
261 done:	argv[argc] = (char *)NULL;
262 	*store_argc = argc;
263 	return(argv);
264 }
265 
266 /*
267  * Str_FindSubstring -- See if a string contains a particular substring.
268  *
269  * Results: If string contains substring, the return value is the location of
270  * the first matching instance of substring in string.  If string doesn't
271  * contain substring, the return value is NULL.  Matching is done on an exact
272  * character-for-character basis with no wildcards or special characters.
273  *
274  * Side effects: None.
275  */
276 char *
277 Str_FindSubstring(string, substring)
278 	register char *string;		/* String to search. */
279 	char *substring;		/* Substring to find in string */
280 {
281 	register char *a, *b;
282 
283 	/*
284 	 * First scan quickly through the two strings looking for a single-
285 	 * character match.  When it's found, then compare the rest of the
286 	 * substring.
287 	 */
288 
289 	for (b = substring; *string != 0; string += 1) {
290 		if (*string != *b)
291 			continue;
292 		a = string;
293 		for (;;) {
294 			if (*b == 0)
295 				return(string);
296 			if (*a++ != *b++)
297 				break;
298 		}
299 		b = substring;
300 	}
301 	return((char *) NULL);
302 }
303 
304 /*
305  * Str_Match --
306  *
307  * See if a particular string matches a particular pattern.
308  *
309  * Results: Non-zero is returned if string matches pattern, 0 otherwise. The
310  * matching operation permits the following special characters in the
311  * pattern: *?\[] (see the man page for details on what these mean).
312  *
313  * Side effects: None.
314  */
315 int
316 Str_Match(string, pattern)
317 	register char *string;		/* String */
318 	register char *pattern;		/* Pattern */
319 {
320 	char c2;
321 
322 	for (;;) {
323 		/*
324 		 * See if we're at the end of both the pattern and the
325 		 * string. If, we succeeded.  If we're at the end of the
326 		 * pattern but not at the end of the string, we failed.
327 		 */
328 		if (*pattern == 0)
329 			return(!*string);
330 		if (*string == 0 && *pattern != '*')
331 			return(0);
332 		/*
333 		 * Check for a "*" as the next pattern character.  It matches
334 		 * any substring.  We handle this by calling ourselves
335 		 * recursively for each postfix of string, until either we
336 		 * match or we reach the end of the string.
337 		 */
338 		if (*pattern == '*') {
339 			pattern += 1;
340 			if (*pattern == 0)
341 				return(1);
342 			while (*string != 0) {
343 				if (Str_Match(string, pattern))
344 					return(1);
345 				++string;
346 			}
347 			return(0);
348 		}
349 		/*
350 		 * Check for a "?" as the next pattern character.  It matches
351 		 * any single character.
352 		 */
353 		if (*pattern == '?')
354 			goto thisCharOK;
355 		/*
356 		 * Check for a "[" as the next pattern character.  It is
357 		 * followed by a list of characters that are acceptable, or
358 		 * by a range (two characters separated by "-").
359 		 */
360 		if (*pattern == '[') {
361 			++pattern;
362 			for (;;) {
363 				if ((*pattern == ']') || (*pattern == 0))
364 					return(0);
365 				if (*pattern == *string)
366 					break;
367 				if (pattern[1] == '-') {
368 					c2 = pattern[2];
369 					if (c2 == 0)
370 						return(0);
371 					if ((*pattern <= *string) &&
372 					    (c2 >= *string))
373 						break;
374 					if ((*pattern >= *string) &&
375 					    (c2 <= *string))
376 						break;
377 					pattern += 2;
378 				}
379 				++pattern;
380 			}
381 			while ((*pattern != ']') && (*pattern != 0))
382 				++pattern;
383 			goto thisCharOK;
384 		}
385 		/*
386 		 * If the next pattern character is '/', just strip off the
387 		 * '/' so we do exact matching on the character that follows.
388 		 */
389 		if (*pattern == '\\') {
390 			++pattern;
391 			if (*pattern == 0)
392 				return(0);
393 		}
394 		/*
395 		 * There's no special character.  Just make sure that the
396 		 * next characters of each string match.
397 		 */
398 		if (*pattern != *string)
399 			return(0);
400 thisCharOK:	++pattern;
401 		++string;
402 	}
403 }
404 
405 
406 /*-
407  *-----------------------------------------------------------------------
408  * Str_SYSVMatch --
409  *	Check word against pattern for a match (% is wild),
410  *
411  * Results:
412  *	Returns the beginning position of a match or null. The number
413  *	of characters matched is returned in len.
414  *
415  * Side Effects:
416  *	None
417  *
418  *-----------------------------------------------------------------------
419  */
420 char *
421 Str_SYSVMatch(word, pattern, len)
422     char	*word;		/* Word to examine */
423     char	*pattern;	/* Pattern to examine against */
424     int		*len;		/* Number of characters to substitute */
425 {
426     char *p = pattern;
427     char *w = word;
428     char *m;
429 
430     if (*p == '\0') {
431 	/* Null pattern is the whole string */
432 	*len = strlen(w);
433 	return w;
434     }
435 
436     if ((m = strchr(p, '%')) != NULL) {
437 	/* check that the prefix matches */
438 	for (; p != m && *w && *w == *p; w++, p++)
439 	     continue;
440 
441 	if (p != m)
442 	    return NULL;	/* No match */
443 
444 	if (*++p == '\0') {
445 	    /* No more pattern, return the rest of the string */
446 	    *len = strlen(w);
447 	    return w;
448 	}
449     }
450 
451     m = w;
452 
453     /* Find a matching tail */
454     do
455 	if (strcmp(p, w) == 0) {
456 	    *len = w - m;
457 	    return m;
458 	}
459     while (*w++ != '\0');
460 
461     return NULL;
462 }
463 
464 
465 /*-
466  *-----------------------------------------------------------------------
467  * Str_SYSVSubst --
468  *	Substitute '%' on the pattern with len characters from src.
469  *	If the pattern does not contain a '%' prepend len characters
470  *	from src.
471  *
472  * Results:
473  *	None
474  *
475  * Side Effects:
476  *	Places result on buf
477  *
478  *-----------------------------------------------------------------------
479  */
480 void
481 Str_SYSVSubst(buf, pat, src, len)
482     Buffer buf;
483     char *pat;
484     char *src;
485     int   len;
486 {
487     char *m;
488 
489     if ((m = strchr(pat, '%')) != NULL) {
490 	/* Copy the prefix */
491 	Buf_AddBytes(buf, m - pat, (Byte *) pat);
492 	/* skip the % */
493 	pat = m + 1;
494     }
495 
496     /* Copy the pattern */
497     Buf_AddBytes(buf, len, (Byte *) src);
498 
499     /* append the rest */
500     Buf_AddBytes(buf, strlen(pat), (Byte *) pat);
501 }
502