xref: /original-bsd/usr.bin/make/str.c (revision 0842ddeb)
1 /*-
2  * Copyright (c) 1988, 1989, 1990, 1993
3  *	The Regents of the University of California.  All rights reserved.
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  * %sccs.include.redist.c%
11  */
12 
13 #ifndef lint
14 static char     sccsid[] = "@(#)str.c	8.6 (Berkeley) 04/28/95";
15 #endif /* not lint */
16 
17 #include "make.h"
18 
19 static char **argv, *buffer;
20 static int argmax, curlen;
21 
22 /*
23  * str_init --
24  *	Initialize the strings package
25  *
26  */
27 void
28 str_init()
29 {
30     char *p1;
31     argv = (char **)emalloc((argmax = 50) * sizeof(char *));
32     argv[0] = Var_Value(".MAKE", VAR_GLOBAL, &p1);
33 }
34 
35 
36 /*
37  * str_end --
38  *	Cleanup the strings package
39  *
40  */
41 void
42 str_end()
43 {
44     if (argv[0]) {
45 	free(argv[0]);
46 	free((Address) argv);
47     }
48     if (buffer)
49 	free(buffer);
50 }
51 
52 
53 /*-
54  * str_concat --
55  *	concatenate the two strings, inserting a space or slash between them,
56  *	freeing them if requested.
57  *
58  * returns --
59  *	the resulting string in allocated space.
60  */
61 char *
62 str_concat(s1, s2, flags)
63 	char *s1, *s2;
64 	int flags;
65 {
66 	register int len1, len2;
67 	register char *result;
68 
69 	/* get the length of both strings */
70 	len1 = strlen(s1);
71 	len2 = strlen(s2);
72 
73 	/* allocate length plus separator plus EOS */
74 	result = emalloc((u_int)(len1 + len2 + 2));
75 
76 	/* copy first string into place */
77 	memcpy(result, s1, len1);
78 
79 	/* add separator character */
80 	if (flags & STR_ADDSPACE) {
81 		result[len1] = ' ';
82 		++len1;
83 	} else if (flags & STR_ADDSLASH) {
84 		result[len1] = '/';
85 		++len1;
86 	}
87 
88 	/* copy second string plus EOS into place */
89 	memcpy(result + len1, s2, len2 + 1);
90 
91 	/* free original strings */
92 	if (flags & STR_DOFREE) {
93 		(void)free(s1);
94 		(void)free(s2);
95 	}
96 	return(result);
97 }
98 
99 /*-
100  * brk_string --
101  *	Fracture a string into an array of words (as delineated by tabs or
102  *	spaces) taking quotation marks into account.  Leading tabs/spaces
103  *	are ignored.
104  *
105  * returns --
106  *	Pointer to the array of pointers to the words.  To make life easier,
107  *	the first word is always the value of the .MAKE variable.
108  */
109 char **
110 brk_string(str, store_argc, expand)
111 	register char *str;
112 	int *store_argc;
113 	Boolean expand;
114 {
115 	register int argc, ch;
116 	register char inquote, *p, *start, *t;
117 	int len;
118 
119 	/* skip leading space chars. */
120 	for (; *str == ' ' || *str == '\t'; ++str)
121 		continue;
122 
123 	/* allocate room for a copy of the string */
124 	if ((len = strlen(str) + 1) > curlen) {
125 		if (buffer)
126 		    free(buffer);
127 		buffer = emalloc(curlen = len);
128 	}
129 
130 	/*
131 	 * copy the string; at the same time, parse backslashes,
132 	 * quotes and build the argument list.
133 	 */
134 	argc = 1;
135 	inquote = '\0';
136 	for (p = str, start = t = buffer;; ++p) {
137 		switch(ch = *p) {
138 		case '"':
139 		case '\'':
140 			if (inquote)
141 				if (inquote == ch)
142 					inquote = '\0';
143 				else
144 					break;
145 			else {
146 				inquote = (char) ch;
147 				/* Don't miss "" or '' */
148 				if (start == NULL && p[1] == inquote) {
149 					start = t + 1;
150 					break;
151 				}
152 			}
153 			if (!expand) {
154 				if (!start)
155 					start = t;
156 				*t++ = ch;
157 			}
158 			continue;
159 		case ' ':
160 		case '\t':
161 		case '\n':
162 			if (inquote)
163 				break;
164 			if (!start)
165 				continue;
166 			/* FALLTHROUGH */
167 		case '\0':
168 			/*
169 			 * end of a token -- make sure there's enough argv
170 			 * space and save off a pointer.
171 			 */
172 			if (!start)
173 			    goto done;
174 
175 			*t++ = '\0';
176 			if (argc == argmax) {
177 				argmax *= 2;		/* ramp up fast */
178 				if (!(argv = (char **)realloc(argv,
179 				    argmax * sizeof(char *))))
180 				enomem();
181 			}
182 			argv[argc++] = start;
183 			start = (char *)NULL;
184 			if (ch == '\n' || ch == '\0')
185 				goto done;
186 			continue;
187 		case '\\':
188 			if (!expand) {
189 				if (!start)
190 					start = t;
191 				*t++ = '\\';
192 				ch = *++p;
193 				break;
194 			}
195 
196 			switch (ch = *++p) {
197 			case '\0':
198 			case '\n':
199 				/* hmmm; fix it up as best we can */
200 				ch = '\\';
201 				--p;
202 				break;
203 			case 'b':
204 				ch = '\b';
205 				break;
206 			case 'f':
207 				ch = '\f';
208 				break;
209 			case 'n':
210 				ch = '\n';
211 				break;
212 			case 'r':
213 				ch = '\r';
214 				break;
215 			case 't':
216 				ch = '\t';
217 				break;
218 			}
219 			break;
220 		}
221 		if (!start)
222 			start = t;
223 		*t++ = (char) ch;
224 	}
225 done:	argv[argc] = (char *)NULL;
226 	*store_argc = argc;
227 	return(argv);
228 }
229 
230 /*
231  * Str_FindSubstring -- See if a string contains a particular substring.
232  *
233  * Results: If string contains substring, the return value is the location of
234  * the first matching instance of substring in string.  If string doesn't
235  * contain substring, the return value is NULL.  Matching is done on an exact
236  * character-for-character basis with no wildcards or special characters.
237  *
238  * Side effects: None.
239  */
240 char *
241 Str_FindSubstring(string, substring)
242 	register char *string;		/* String to search. */
243 	char *substring;		/* Substring to find in string */
244 {
245 	register char *a, *b;
246 
247 	/*
248 	 * First scan quickly through the two strings looking for a single-
249 	 * character match.  When it's found, then compare the rest of the
250 	 * substring.
251 	 */
252 
253 	for (b = substring; *string != 0; string += 1) {
254 		if (*string != *b)
255 			continue;
256 		a = string;
257 		for (;;) {
258 			if (*b == 0)
259 				return(string);
260 			if (*a++ != *b++)
261 				break;
262 		}
263 		b = substring;
264 	}
265 	return((char *) NULL);
266 }
267 
268 /*
269  * Str_Match --
270  *
271  * See if a particular string matches a particular pattern.
272  *
273  * Results: Non-zero is returned if string matches pattern, 0 otherwise. The
274  * matching operation permits the following special characters in the
275  * pattern: *?\[] (see the man page for details on what these mean).
276  *
277  * Side effects: None.
278  */
279 int
280 Str_Match(string, pattern)
281 	register char *string;		/* String */
282 	register char *pattern;		/* Pattern */
283 {
284 	char c2;
285 
286 	for (;;) {
287 		/*
288 		 * See if we're at the end of both the pattern and the
289 		 * string. If, we succeeded.  If we're at the end of the
290 		 * pattern but not at the end of the string, we failed.
291 		 */
292 		if (*pattern == 0)
293 			return(!*string);
294 		if (*string == 0 && *pattern != '*')
295 			return(0);
296 		/*
297 		 * Check for a "*" as the next pattern character.  It matches
298 		 * any substring.  We handle this by calling ourselves
299 		 * recursively for each postfix of string, until either we
300 		 * match or we reach the end of the string.
301 		 */
302 		if (*pattern == '*') {
303 			pattern += 1;
304 			if (*pattern == 0)
305 				return(1);
306 			while (*string != 0) {
307 				if (Str_Match(string, pattern))
308 					return(1);
309 				++string;
310 			}
311 			return(0);
312 		}
313 		/*
314 		 * Check for a "?" as the next pattern character.  It matches
315 		 * any single character.
316 		 */
317 		if (*pattern == '?')
318 			goto thisCharOK;
319 		/*
320 		 * Check for a "[" as the next pattern character.  It is
321 		 * followed by a list of characters that are acceptable, or
322 		 * by a range (two characters separated by "-").
323 		 */
324 		if (*pattern == '[') {
325 			++pattern;
326 			for (;;) {
327 				if ((*pattern == ']') || (*pattern == 0))
328 					return(0);
329 				if (*pattern == *string)
330 					break;
331 				if (pattern[1] == '-') {
332 					c2 = pattern[2];
333 					if (c2 == 0)
334 						return(0);
335 					if ((*pattern <= *string) &&
336 					    (c2 >= *string))
337 						break;
338 					if ((*pattern >= *string) &&
339 					    (c2 <= *string))
340 						break;
341 					pattern += 2;
342 				}
343 				++pattern;
344 			}
345 			while ((*pattern != ']') && (*pattern != 0))
346 				++pattern;
347 			goto thisCharOK;
348 		}
349 		/*
350 		 * If the next pattern character is '/', just strip off the
351 		 * '/' so we do exact matching on the character that follows.
352 		 */
353 		if (*pattern == '\\') {
354 			++pattern;
355 			if (*pattern == 0)
356 				return(0);
357 		}
358 		/*
359 		 * There's no special character.  Just make sure that the
360 		 * next characters of each string match.
361 		 */
362 		if (*pattern != *string)
363 			return(0);
364 thisCharOK:	++pattern;
365 		++string;
366 	}
367 }
368 
369 
370 /*-
371  *-----------------------------------------------------------------------
372  * Str_SYSVMatch --
373  *	Check word against pattern for a match (% is wild),
374  *
375  * Results:
376  *	Returns the beginning position of a match or null. The number
377  *	of characters matched is returned in len.
378  *
379  * Side Effects:
380  *	None
381  *
382  *-----------------------------------------------------------------------
383  */
384 char *
385 Str_SYSVMatch(word, pattern, len)
386     char	*word;		/* Word to examine */
387     char	*pattern;	/* Pattern to examine against */
388     int		*len;		/* Number of characters to substitute */
389 {
390     char *p = pattern;
391     char *w = word;
392     char *m;
393 
394     if (*p == '\0') {
395 	/* Null pattern is the whole string */
396 	*len = strlen(w);
397 	return w;
398     }
399 
400     if ((m = strchr(p, '%')) != NULL) {
401 	/* check that the prefix matches */
402 	for (; p != m && *w && *w == *p; w++, p++)
403 	     continue;
404 
405 	if (p != m)
406 	    return NULL;	/* No match */
407 
408 	if (*++p == '\0') {
409 	    /* No more pattern, return the rest of the string */
410 	    *len = strlen(w);
411 	    return w;
412 	}
413     }
414 
415     m = w;
416 
417     /* Find a matching tail */
418     do
419 	if (strcmp(p, w) == 0) {
420 	    *len = w - m;
421 	    return m;
422 	}
423     while (*w++ != '\0');
424 
425     return NULL;
426 }
427 
428 
429 /*-
430  *-----------------------------------------------------------------------
431  * Str_SYSVSubst --
432  *	Substitute '%' on the pattern with len characters from src.
433  *	If the pattern does not contain a '%' prepend len characters
434  *	from src.
435  *
436  * Results:
437  *	None
438  *
439  * Side Effects:
440  *	Places result on buf
441  *
442  *-----------------------------------------------------------------------
443  */
444 void
445 Str_SYSVSubst(buf, pat, src, len)
446     Buffer buf;
447     char *pat;
448     char *src;
449     int   len;
450 {
451     char *m;
452 
453     if ((m = strchr(pat, '%')) != NULL) {
454 	/* Copy the prefix */
455 	Buf_AddBytes(buf, m - pat, (Byte *) pat);
456 	/* skip the % */
457 	pat = m + 1;
458     }
459 
460     /* Copy the pattern */
461     Buf_AddBytes(buf, len, (Byte *) src);
462 
463     /* append the rest */
464     Buf_AddBytes(buf, strlen(pat), (Byte *) pat);
465 }
466