diff options
Diffstat (limited to 'usr.bin/make/str.c')
-rw-r--r-- | usr.bin/make/str.c | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/usr.bin/make/str.c b/usr.bin/make/str.c new file mode 100644 index 0000000..ce4ac05 --- /dev/null +++ b/usr.bin/make/str.c @@ -0,0 +1,559 @@ +/*- + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)str.c 5.8 (Berkeley) 6/1/90 + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdlib.h> +#include <string.h> + +#include "buf.h" +#include "str.h" +#include "util.h" + +/** + * Initialize the argument array object. The array is initially + * eight positions, and will be expanded as necessary. The first + * position is set to NULL since everything ignores it. We allocate + * (size + 1) since we need space for the terminating NULL. The + * buffer is set to NULL, since no common buffer is allocated yet. + */ +void +ArgArray_Init(ArgArray *aa) +{ + + aa->size = 8; + aa->argv = emalloc((aa->size + 1) * sizeof(char *)); + aa->argc = 0; + aa->argv[aa->argc++] = NULL; + aa->len = 0; + aa->buffer = NULL; +} + +/** + * Cleanup the memory allocated for in the argument array object. + */ +void +ArgArray_Done(ArgArray *aa) +{ + + if (aa->buffer == NULL) { + int i; + /* args are individually allocated */ + for (i = 0; i < aa->argc; ++i) { + if (aa->argv[i]) { + free(aa->argv[i]); + aa->argv[i] = NULL; + } + } + } else { + /* args are part of a single allocation */ + free(aa->buffer); + aa->buffer = NULL; + } + free(aa->argv); + aa->argv = NULL; + aa->argc = 0; + aa->size = 0; +} + +/*- + * str_concat -- + * concatenate the two strings, inserting a space or slash between them. + * + * returns -- + * the resulting string in allocated space. + */ +char * +str_concat(const char *s1, const char *s2, int flags) +{ + int len1, len2; + char *result; + + /* get the length of both strings */ + len1 = strlen(s1); + len2 = strlen(s2); + + /* allocate length plus separator plus EOS */ + result = emalloc(len1 + len2 + 2); + + /* copy first string into place */ + memcpy(result, s1, len1); + + /* add separator character */ + if (flags & STR_ADDSPACE) { + result[len1] = ' '; + ++len1; + } else if (flags & STR_ADDSLASH) { + result[len1] = '/'; + ++len1; + } + + /* copy second string plus EOS into place */ + memcpy(result + len1, s2, len2 + 1); + + return (result); +} + +/** + * Fracture a string into an array of words (as delineated by tabs or + * spaces) taking quotation marks into account. Leading tabs/spaces + * are ignored. + */ +void +brk_string(ArgArray *aa, const char str[], Boolean expand) +{ + char inquote; + char *start; + char *arg; + + /* skip leading space chars. */ + for (; *str == ' ' || *str == '\t'; ++str) + continue; + + ArgArray_Init(aa); + + aa->buffer = estrdup(str); + + arg = aa->buffer; + start = arg; + inquote = '\0'; + + /* + * copy the string; at the same time, parse backslashes, + * quotes and build the argument list. + */ + for (;;) { + switch (str[0]) { + case '"': + case '\'': + if (inquote == '\0') { + inquote = str[0]; + if (expand) + break; + if (start == NULL) + start = arg; + } else if (inquote == str[0]) { + inquote = '\0'; + /* Don't miss "" or '' */ + if (start == NULL) + start = arg; + if (expand) + break; + } else { + /* other type of quote found */ + if (start == NULL) + start = arg; + } + *arg++ = str[0]; + break; + case ' ': + case '\t': + case '\n': + if (inquote) { + if (start == NULL) + start = arg; + *arg++ = str[0]; + break; + } + if (start == NULL) + break; + /* FALLTHROUGH */ + case '\0': + /* + * end of a token -- make sure there's enough argv + * space and save off a pointer. + */ + if (aa->argc == aa->size) { + aa->size *= 2; /* ramp up fast */ + aa->argv = erealloc(aa->argv, + (aa->size + 1) * sizeof(char *)); + } + + *arg++ = '\0'; + if (start == NULL) { + aa->argv[aa->argc] = start; + return; + } + if (str[0] == '\n' || str[0] == '\0') { + aa->argv[aa->argc++] = start; + aa->argv[aa->argc] = NULL; + return; + } else { + aa->argv[aa->argc++] = start; + start = NULL; + break; + } + case '\\': + if (start == NULL) + start = arg; + if (expand) { + switch (str[1]) { + case '\0': + case '\n': + /* hmmm; fix it up as best we can */ + *arg++ = '\\'; + break; + case 'b': + *arg++ = '\b'; + ++str; + break; + case 'f': + *arg++ = '\f'; + ++str; + break; + case 'n': + *arg++ = '\n'; + ++str; + break; + case 'r': + *arg++ = '\r'; + ++str; + break; + case 't': + *arg++ = '\t'; + ++str; + break; + default: + *arg++ = str[1]; + ++str; + break; + } + } else { + *arg++ = str[0]; + if (str[1] != '\0') { + ++str; + *arg++ = str[0]; + } + } + break; + default: + if (start == NULL) + start = arg; + *arg++ = str[0]; + break; + } + ++str; + } +} + +/* + * Quote a string for appending it to MAKEFLAGS. According to Posix the + * kind of quoting here is implementation-defined. This quoting must ensure + * that the parsing of MAKEFLAGS's contents in a sub-shell yields the same + * options, option arguments and macro definitions as in the calling make. + * We simply quote all blanks, which according to Posix are space and tab + * in the POSIX locale. Don't use isblank because in that case makes with + * different locale settings could not communicate. We must also quote + * backslashes obviously. + */ +char * +MAKEFLAGS_quote(const char *str) +{ + char *ret, *q; + const char *p; + + /* assume worst case - everything has to be quoted */ + ret = emalloc(strlen(str) * 2 + 1); + + p = str; + q = ret; + while (*p != '\0') { + switch (*p) { + + case ' ': + case '\t': + *q++ = '\\'; + break; + + default: + break; + } + *q++ = *p++; + } + *q++ = '\0'; + return (ret); +} + +void +MAKEFLAGS_break(ArgArray *aa, const char str[]) +{ + char *arg; + char *start; + + ArgArray_Init(aa); + + aa->buffer = strdup(str); + + arg = aa->buffer; + start = NULL; + + for (;;) { + switch (str[0]) { + case ' ': + case '\t': + /* word separator */ + if (start == NULL) { + /* not in a word */ + str++; + continue; + } + /* FALLTHRU */ + case '\0': + if (aa->argc == aa->size) { + aa->size *= 2; + aa->argv = erealloc(aa->argv, + (aa->size + 1) * sizeof(char *)); + } + + *arg++ = '\0'; + if (start == NULL) { + aa->argv[aa->argc] = start; + return; + } + if (str[0] == '\0') { + aa->argv[aa->argc++] = start; + aa->argv[aa->argc] = NULL; + return; + } else { + aa->argv[aa->argc++] = start; + start = NULL; + str++; + continue; + } + + case '\\': + if (str[1] == ' ' || str[1] == '\t') + str++; + break; + + default: + break; + } + if (start == NULL) + start = arg; + *arg++ = *str++; + } +} + +/* + * Str_Match -- + * + * See if a particular string matches a particular pattern. + * + * Results: Non-zero is returned if string matches pattern, 0 otherwise. The + * matching operation permits the following special characters in the + * pattern: *?\[] (see the man page for details on what these mean). + * + * Side effects: None. + */ +int +Str_Match(const char *string, const char *pattern) +{ + char c2; + + for (;;) { + /* + * See if we're at the end of both the pattern and the + * string. If, we succeeded. If we're at the end of the + * pattern but not at the end of the string, we failed. + */ + if (*pattern == 0) + return (!*string); + if (*string == 0 && *pattern != '*') + return (0); + /* + * Check for a "*" as the next pattern character. It matches + * any substring. We handle this by calling ourselves + * recursively for each postfix of string, until either we + * match or we reach the end of the string. + */ + if (*pattern == '*') { + pattern += 1; + if (*pattern == 0) + return (1); + while (*string != 0) { + if (Str_Match(string, pattern)) + return (1); + ++string; + } + return (0); + } + /* + * Check for a "?" as the next pattern character. It matches + * any single character. + */ + if (*pattern == '?') + goto thisCharOK; + /* + * Check for a "[" as the next pattern character. It is + * followed by a list of characters that are acceptable, or + * by a range (two characters separated by "-"). + */ + if (*pattern == '[') { + ++pattern; + for (;;) { + if ((*pattern == ']') || (*pattern == 0)) + return (0); + if (*pattern == *string) + break; + if (pattern[1] == '-') { + c2 = pattern[2]; + if (c2 == 0) + return (0); + if ((*pattern <= *string) && + (c2 >= *string)) + break; + if ((*pattern >= *string) && + (c2 <= *string)) + break; + pattern += 2; + } + ++pattern; + } + while ((*pattern != ']') && (*pattern != 0)) + ++pattern; + goto thisCharOK; + } + /* + * If the next pattern character is '/', just strip off the + * '/' so we do exact matching on the character that follows. + */ + if (*pattern == '\\') { + ++pattern; + if (*pattern == 0) + return (0); + } + /* + * There's no special character. Just make sure that the + * next characters of each string match. + */ + if (*pattern != *string) + return (0); +thisCharOK: ++pattern; + ++string; + } +} + + +/** + * Str_SYSVMatch + * Check word against pattern for a match (% is wild), + * + * Results: + * Returns the beginning position of a match or null. The number + * of characters matched is returned in len. + */ +const char * +Str_SYSVMatch(const char *word, const char *pattern, int *len) +{ + const char *m, *p, *w; + + p = pattern; + w = word; + + if (*w == '\0') { + /* Zero-length word cannot be matched against */ + *len = 0; + return (NULL); + } + + if (*p == '\0') { + /* Null pattern is the whole string */ + *len = strlen(w); + return (w); + } + + if ((m = strchr(p, '%')) != NULL) { + /* check that the prefix matches */ + for (; p != m && *w && *w == *p; w++, p++) + continue; + + if (p != m) + return (NULL); /* No match */ + + if (*++p == '\0') { + /* No more pattern, return the rest of the string */ + *len = strlen(w); + return (w); + } + } + + m = w; + + /* Find a matching tail */ + do + if (strcmp(p, w) == 0) { + *len = w - m; + return (m); + } + while (*w++ != '\0'); + + return (NULL); +} + + +/** + * Str_SYSVSubst + * Substitute '%' on the pattern with len characters from src. + * If the pattern does not contain a '%' prepend len characters + * from src. + * + * Side Effects: + * Places result on buf + */ +void +Str_SYSVSubst(Buffer *buf, const char *pat, const char *src, int len) +{ + const char *m; + + if ((m = strchr(pat, '%')) != NULL) { + /* Copy the prefix */ + Buf_AppendRange(buf, pat, m); + /* skip the % */ + pat = m + 1; + } + + /* Copy the pattern */ + Buf_AddBytes(buf, len, (const Byte *)src); + + /* append the rest */ + Buf_Append(buf, pat); +} |