version 1.16, 2000/09/14 13:35:38 |
version 1.17, 2001/05/03 13:41:11 |
|
|
|
/* $OpenPackages$ */ |
/* $OpenBSD$ */ |
/* $OpenBSD$ */ |
/* $NetBSD: str.c,v 1.13 1996/11/06 17:59:23 christos Exp $ */ |
/* $NetBSD: str.c,v 1.13 1996/11/06 17:59:23 christos Exp $ */ |
|
|
|
|
|
|
#ifndef lint |
#ifndef lint |
#if 0 |
#if 0 |
static char sccsid[] = "@(#)str.c 5.8 (Berkeley) 6/1/90"; |
static char sccsid[] = "@(#)str.c 5.8 (Berkeley) 6/1/90"; |
#else |
#else |
UNUSED |
UNUSED |
static char rcsid[] = "$OpenBSD$"; |
static char rcsid[] = "$OpenBSD$"; |
|
|
#endif /* not lint */ |
#endif /* not lint */ |
|
|
/*- |
/*- |
* str_concat -- |
* str_concati -- |
* concatenate the two strings, possibly inserting a separator |
* concatenate the two strings, possibly inserting a separator |
* |
* |
* returns -- |
* returns -- |
* the resulting string in allocated space. |
* the resulting string in allocated space. |
*/ |
*/ |
char * |
char * |
str_concat(s1, s2, sep) |
str_concati(s1, s2, e2, sep) |
const char *s1, *s2; |
const char *s1, *s2, *e2; |
char sep; |
int sep; |
{ |
{ |
size_t len1, len2; |
size_t len1, len2; |
char *result; |
char *result; |
|
|
/* get the length of both strings */ |
/* get the length of both strings */ |
len1 = strlen(s1); |
len1 = strlen(s1); |
len2 = strlen(s2); |
len2 = e2 - s2; |
|
|
/* space for separator */ |
/* space for separator */ |
if (sep) |
if (sep) |
|
|
memcpy(result, s1, len1); |
memcpy(result, s1, len1); |
|
|
/* add separator character */ |
/* add separator character */ |
if (sep) |
if (sep) |
result[len1-1] = sep; |
result[len1-1] = sep; |
|
|
/* copy second string plus EOS into place */ |
/* copy second string plus EOS into place */ |
|
|
* are ignored. |
* are ignored. |
* |
* |
* returns -- |
* returns -- |
* Pointer to the array of pointers to the words. To make life easier, |
* Pointer to the array of pointers to the words. To make life easier, |
* the first word is always the value of the .MAKE variable. |
* the first word is always the value of the .MAKE variable. |
*/ |
*/ |
char ** |
char ** |
brk_string(str, store_argc, expand, buffer) |
brk_string(str, store_argc, buffer) |
const char *str; |
const char *str; |
int *store_argc; |
int *store_argc; |
Boolean expand; |
char **buffer; |
char **buffer; |
|
{ |
{ |
register int argc, ch; |
int argc; |
char inquote; |
char ch; |
const char *p; |
char inquote; |
char *start, *t; |
const char *p; |
size_t len; |
char *start, *t; |
int argmax = 50; |
size_t len; |
size_t curlen = 0; |
int argmax = 50; |
char **argv = (char **)emalloc((argmax + 1) * sizeof(char *)); |
size_t curlen = 0; |
|
char **argv = emalloc((argmax + 1) * sizeof(char *)); |
|
|
/* skip leading space chars. */ |
/* skip leading space chars. */ |
for (; *str == ' ' || *str == '\t'; ++str) |
for (; *str == ' ' || *str == '\t'; ++str) |
continue; |
continue; |
|
|
/* allocate room for a copy of the string */ |
/* allocate room for a copy of the string */ |
if ((len = strlen(str) + 1) > curlen) |
if ((len = strlen(str) + 1) > curlen) |
*buffer = emalloc(curlen = len); |
*buffer = emalloc(curlen = len); |
|
|
/* |
/* |
* copy the string; at the same time, parse backslashes, |
* copy the string; at the same time, parse backslashes, |
* quotes and build the argument list. |
* quotes and build the argument list. |
*/ |
*/ |
argc = 0; |
argc = 0; |
inquote = '\0'; |
inquote = '\0'; |
for (p = str, start = t = *buffer;; ++p) { |
for (p = str, start = t = *buffer;; ++p) { |
switch(ch = *p) { |
switch (ch = *p) { |
case '"': |
case '"': |
case '\'': |
case '\'': |
if (inquote) { |
if (inquote) { |
if (inquote == ch) |
if (inquote == ch) |
inquote = '\0'; |
inquote = '\0'; |
else |
else |
break; |
break; |
} else { |
} else { |
inquote = (char) ch; |
inquote = ch; |
/* Don't miss "" or '' */ |
/* Don't miss "" or '' */ |
if (start == NULL && p[1] == inquote) { |
if (start == NULL && p[1] == inquote) { |
start = t + 1; |
start = t + 1; |
break; |
break; |
} |
|
} |
|
if (!expand) { |
|
if (!start) |
|
start = t; |
|
*t++ = ch; |
|
} |
|
continue; |
|
case ' ': |
|
case '\t': |
|
case '\n': |
|
if (inquote) |
|
break; |
|
if (!start) |
|
continue; |
|
/* FALLTHROUGH */ |
|
case '\0': |
|
/* |
|
* end of a token -- make sure there's enough argv |
|
* space and save off a pointer. |
|
*/ |
|
if (!start) |
|
goto done; |
|
|
|
*t++ = '\0'; |
|
if (argc == argmax) { |
|
argmax *= 2; /* ramp up fast */ |
|
argv = (char **)erealloc(argv, |
|
(argmax + 1) * sizeof(char *)); |
|
} |
|
argv[argc++] = start; |
|
start = (char *)NULL; |
|
if (ch == '\n' || ch == '\0') |
|
goto done; |
|
continue; |
|
case '\\': |
|
if (!expand) { |
|
if (!start) |
|
start = t; |
|
*t++ = '\\'; |
|
ch = *++p; |
|
break; |
|
} |
|
|
|
switch (ch = *++p) { |
|
case '\0': |
|
case '\n': |
|
/* hmmm; fix it up as best we can */ |
|
ch = '\\'; |
|
--p; |
|
break; |
|
case 'b': |
|
ch = '\b'; |
|
break; |
|
case 'f': |
|
ch = '\f'; |
|
break; |
|
case 'n': |
|
ch = '\n'; |
|
break; |
|
case 'r': |
|
ch = '\r'; |
|
break; |
|
case 't': |
|
ch = '\t'; |
|
break; |
|
} |
|
break; |
|
} |
} |
if (!start) |
} |
start = t; |
continue; |
*t++ = (char) ch; |
case ' ': |
|
case '\t': |
|
case '\n': |
|
if (inquote) |
|
break; |
|
if (!start) |
|
continue; |
|
/* FALLTHROUGH */ |
|
case '\0': |
|
/* |
|
* end of a token -- make sure there's enough argv |
|
* space and save off a pointer. |
|
*/ |
|
if (!start) |
|
goto done; |
|
|
|
*t++ = '\0'; |
|
if (argc == argmax) { |
|
argmax *= 2; /* ramp up fast */ |
|
argv = erealloc(argv, (argmax + 1) * sizeof(char *)); |
|
} |
|
argv[argc++] = start; |
|
start = NULL; |
|
if (ch == '\n' || ch == '\0') |
|
goto done; |
|
continue; |
|
case '\\': |
|
switch (ch = *++p) { |
|
case '\0': |
|
case '\n': |
|
/* hmmm; fix it up as best we can */ |
|
ch = '\\'; |
|
--p; |
|
break; |
|
case 'b': |
|
ch = '\b'; |
|
break; |
|
case 'f': |
|
ch = '\f'; |
|
break; |
|
case 'n': |
|
ch = '\n'; |
|
break; |
|
case 'r': |
|
ch = '\r'; |
|
break; |
|
case 't': |
|
ch = '\t'; |
|
break; |
|
} |
|
break; |
} |
} |
done: argv[argc] = (char *)NULL; |
if (!start) |
*store_argc = argc; |
start = t; |
return(argv); |
*t++ = ch; |
|
} |
|
done: |
|
argv[argc] = NULL; |
|
*store_argc = argc; |
|
return argv; |
} |
} |
|
|
/* Iterate through a string word by word, |
/* Iterate through a string word by word, |
|
|
*/ |
*/ |
const char * |
const char * |
iterate_words(end) |
iterate_words(end) |
const char **end; |
const char **end; |
{ |
{ |
const char *start, *p; |
const char *start, *p; |
char state = 0; |
char state = 0; |
start = *end; |
start = *end; |
|
|
while (isspace(*start)) |
while (isspace(*start)) |
start++; |
start++; |
if (*start == '\0') |
if (*start == '\0') |
return NULL; |
return NULL; |
|
|
for (p = start;; p++) |
for (p = start;; p++) |
switch(*p) { |
switch(*p) { |
case '\\': |
case '\\': |
if (p[1] != '\0') |
if (p[1] != '\0') |
p++; |
p++; |
break; |
break; |
case '\'': |
case '\'': |
case '"': |
case '"': |
if (state == *p) |
if (state == *p) |
state = 0; |
state = 0; |
else if (state == 0) |
else if (state == 0) |
state = *p; |
state = *p; |
break; |
break; |
case ' ': |
case ' ': |
case '\t': |
case '\t': |
if (state != 0) |
if (state != 0) |
break; |
break; |
/* FALLTHROUGH */ |
/* FALLTHROUGH */ |
case '\0': |
case '\0': |
*end = p; |
*end = p; |
return start; |
return start; |
default: |
default: |
break; |
break; |
} |
} |
} |
} |
|
|
/* |
/* |
* Str_Match -- |
* Str_Matchi -- |
* |
* |
* See if a particular string matches a particular pattern. |
* See if a particular string matches a particular pattern. |
* |
* |
|
|
* pattern: *?\[] (see the man page for details on what these mean). |
* pattern: *?\[] (see the man page for details on what these mean). |
*/ |
*/ |
Boolean |
Boolean |
Str_Match(string, pattern) |
Str_Matchi(string, pattern, end) |
const char *string; /* String */ |
const char *string; /* String */ |
const char *pattern; /* Pattern */ |
const char *pattern; /* Pattern */ |
|
const char *end; /* End of Pattern */ |
{ |
{ |
while (*pattern != '\0') { |
while (pattern != end) { |
/* Check for a "*" as the next pattern character. It matches |
/* Check for a "*" as the next pattern character. It matches |
* any substring. We handle this by calling ourselves |
* any substring. We handle this by calling ourselves |
* recursively for each postfix of string, until either we |
* recursively for each postfix of string, until either we |
|
|
pattern++; |
pattern++; |
/* Skip over contiguous sequences of `?*', so that recursive |
/* Skip over contiguous sequences of `?*', so that recursive |
* calls only occur on `real' characters. */ |
* calls only occur on `real' characters. */ |
while (*pattern == '?' || *pattern == '*') { |
while (pattern != end && (*pattern == '?' || *pattern == '*')) { |
if (*pattern == '?') { |
if (*pattern == '?') { |
if (*string == '\0') |
if (*string == '\0') |
return FALSE; |
return FALSE; |
|
|
} |
} |
pattern++; |
pattern++; |
} |
} |
if (*pattern == '\0') |
if (pattern == end) |
return TRUE; |
return TRUE; |
for (; *string != '\0'; string++) |
for (; *string != '\0'; string++) |
if (Str_Match(string, pattern)) |
if (Str_Matchi(string, pattern, end)) |
return TRUE; |
return TRUE; |
return FALSE; |
return FALSE; |
} else if (*string == '\0') |
} else if (*string == '\0') |
return FALSE; |
return FALSE; |
/* Check for a "[" as the next pattern character. It is |
/* Check for a "[" as the next pattern character. It is |
* followed by a list of characters that are acceptable, or |
* followed by a list of characters that are acceptable, or |
* by a range (two characters separated by "-"). */ |
* by a range (two characters separated by "-"). */ |
else if (*pattern == '[') { |
else if (*pattern == '[') { |
pattern++; |
pattern++; |
if (*pattern == '\0') |
if (pattern == end) |
return FALSE; |
return FALSE; |
if (*pattern == '!' || *pattern == '^') { |
if (*pattern == '!' || *pattern == '^') { |
pattern++; |
pattern++; |
if (*pattern == '\0') |
if (pattern == end) |
return FALSE; |
return FALSE; |
/* Negative match */ |
/* Negative match */ |
for (;;) { |
for (;;) { |
if (*pattern == '\\') { |
if (*pattern == '\\') { |
if (*++pattern == '\0') |
if (++pattern == end) |
return FALSE; |
return FALSE; |
} |
} |
if (*pattern == *string) |
if (*pattern == *string) |
return FALSE; |
return FALSE; |
if (pattern[1] == '-') { |
if (pattern[1] == '-') { |
if (pattern[2] == '\0') |
if (pattern + 2 == end) |
return FALSE; |
return FALSE; |
if (*pattern < *string && *string <= pattern[2]) |
if (*pattern < *string && *string <= pattern[2]) |
return FALSE; |
return FALSE; |
|
|
pattern += 3; |
pattern += 3; |
} else |
} else |
pattern++; |
pattern++; |
if (*pattern == '\0') |
if (pattern == end) |
return FALSE; |
return FALSE; |
/* The test for ']' is done at the end so that ']' |
/* The test for ']' is done at the end so that ']' |
* can be used at the start of the range without '\' */ |
* can be used at the start of the range without '\' */ |
if (*pattern == ']') |
if (*pattern == ']') |
break; |
break; |
} |
} |
} else { |
} else { |
for (;;) { |
for (;;) { |
if (*pattern == '\\') { |
if (*pattern == '\\') { |
if (*++pattern == '\0') |
if (++pattern == end) |
return FALSE; |
return FALSE; |
} |
} |
if (*pattern == *string) |
if (*pattern == *string) |
break; |
break; |
if (pattern[1] == '-') { |
if (pattern[1] == '-') { |
if (pattern[2] == '\0') |
if (pattern + 2 == end) |
return FALSE; |
return FALSE; |
if (*pattern < *string && *string <= pattern[2]) |
if (*pattern < *string && *string <= pattern[2]) |
break; |
break; |
|
|
pattern++; |
pattern++; |
/* The test for ']' is done at the end so that ']' |
/* The test for ']' is done at the end so that ']' |
* can be used at the start of the range without '\' */ |
* can be used at the start of the range without '\' */ |
if (*pattern == '\0' || *pattern == ']') |
if (pattern == end || *pattern == ']') |
return FALSE; |
return FALSE; |
} |
} |
/* Found matching character, skip over rest of class. */ |
/* Found matching character, skip over rest of class. */ |
while (*pattern != ']') { |
while (*pattern != ']') { |
if (*pattern == '\\') |
if (*pattern == '\\') |
pattern++; |
pattern++; |
/* A non-terminated character class is ok. */ |
/* A non-terminated character class is ok. */ |
if (*pattern == '\0') |
if (pattern == end) |
break; |
break; |
pattern++; |
pattern++; |
} |
} |
|
|
/* If the next pattern character is '\', just strip off the |
/* If the next pattern character is '\', just strip off the |
* '\' so we do exact matching on the character that follows. */ |
* '\' so we do exact matching on the character that follows. */ |
if (*pattern == '\\') { |
if (*pattern == '\\') { |
if (*++pattern == '\0') |
if (++pattern == end) |
return FALSE; |
return FALSE; |
} |
} |
/* There's no special character. Just make sure that |
/* There's no special character. Just make sure that |
* the next characters of each string match. */ |
* the next characters of each string match. */ |
if (*pattern != *string) |
if (*pattern != *string) |
return FALSE; |
return FALSE; |
|
|
return FALSE; |
return FALSE; |
} |
} |
|
|
|
|
/*- |
/*- |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
* Str_SYSVMatch -- |
* Str_SYSVMatch -- |
|
|
const char *m; |
const char *m; |
|
|
if (*p == '\0') { |
if (*p == '\0') { |
/* Null pattern is the whole string */ |
/* Null pattern is the whole string. */ |
*len = strlen(w); |
*len = strlen(w); |
return w; |
return w; |
} |
} |
|
|
if ((m = strchr(p, '%')) != NULL) { |
if ((m = strchr(p, '%')) != NULL) { |
/* check that the prefix matches */ |
/* Check that the prefix matches. */ |
for (; p != m && *w && *w == *p; w++, p++) |
for (; p != m && *w && *w == *p; w++, p++) |
continue; |
continue; |
|
|
if (p != m) |
if (p != m) |
return NULL; /* No match */ |
return NULL; /* No match. */ |
|
|
if (*++p == '\0') { |
if (*++p == '\0') { |
/* No more pattern, return the rest of the string */ |
/* No more pattern, return the rest of the string. */ |
*len = strlen(w); |
*len = strlen(w); |
return w; |
return w; |
} |
} |
|
|
|
|
m = w; |
m = w; |
|
|
/* Find a matching tail */ |
/* Find a matching tail. */ |
do |
do { |
if (strcmp(p, w) == 0) { |
if (strcmp(p, w) == 0) { |
*len = w - m; |
*len = w - m; |
return m; |
return m; |
} |
} |
while (*w++ != '\0'); |
} while (*w++ != '\0'); |
|
|
|
|
return NULL; |
return NULL; |
} |
} |
|
|
|
|
const char *m; |
const char *m; |
|
|
if ((m = strchr(pat, '%')) != NULL) { |
if ((m = strchr(pat, '%')) != NULL) { |
/* Copy the prefix */ |
/* Copy the prefix. */ |
Buf_AddInterval(buf, pat, m); |
Buf_AddInterval(buf, pat, m); |
/* skip the % */ |
/* Skip the %. */ |
pat = m + 1; |
pat = m + 1; |
} |
} |
|
|
/* Copy the pattern */ |
/* Copy the pattern. */ |
Buf_AddChars(buf, len, src); |
Buf_AddChars(buf, len, src); |
|
|
/* append the rest */ |
/* Append the rest. */ |
Buf_AddString(buf, pat); |
Buf_AddString(buf, pat); |
} |
} |
|
|
|
|
|
|
t = s = emalloc(end - begin + 1); |
t = s = emalloc(end - begin + 1); |
while (begin != end) { |
while (begin != end) { |
if (*begin == '\\') { |
if (*begin == '\\') { |
begin++; |
begin++; |
if (begin == end) { |
if (begin == end) { |
*t++ = '\\'; |
*t++ = '\\'; |
break; |
break; |
} |
} |
if (strchr(set, *begin) == NULL) |
if (strchr(set, *begin) == NULL) |
*t++ = '\\'; |
*t++ = '\\'; |
} |
} |
*t++ = *begin++; |
*t++ = *begin++; |
} |
} |
|
|
return s; |
return s; |
} |
} |
|
|
|
char * |
|
lastchar(s, e, c) |
|
const char *s; |
|
const char *e; |
|
int c; |
|
{ |
|
if (s != e) |
|
do { |
|
if (*--e == c) |
|
return (char *)e; |
|
} while (e != s); |
|
return NULL; |
|
} |