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