X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/mLib/blobdiff_plain/081e681557eb1b6223fe8f4e2551c506cd0d2d3c..4aee0550325e9d8d20666454bc638755b3604f24:/str.c diff --git a/str.c b/str.c index 51a9805..9b5fc63 100644 --- a/str.c +++ b/str.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: str.c,v 1.1 1999/05/17 20:37:01 mdw Exp $ + * $Id: str.c,v 1.5 2000/10/08 09:50:11 mdw Exp $ * * Functions for hacking with strings * @@ -30,6 +30,18 @@ /*----- Revision history --------------------------------------------------* * * $Log: str.c,v $ + * Revision 1.5 2000/10/08 09:50:11 mdw + * (str_qword): Remove redundant definition of @STRF_QUOTE@. + * + * Revision 1.4 2000/10/08 09:43:34 mdw + * New quoted string handling and simple pattern matching. + * + * Revision 1.3 1999/12/22 15:41:14 mdw + * Skip past trailing whitespace in str_getword. + * + * Revision 1.2 1999/05/26 20:52:57 mdw + * Add new `rest' argument for `str_split'. + * * Revision 1.1 1999/05/17 20:37:01 mdw * Some trivial string hacks. * @@ -46,43 +58,89 @@ /*----- Main code ---------------------------------------------------------*/ -/* --- @str_getword@ --- * +/* --- @str_qword@ --- * * * Arguments: @char **pp@ = address of pointer into string + * @unsigned f@ = various flags * - * Returns: Pointer to the next space-separated word from the string, - * or null. + * Returns: Pointer to the next space-separated possibly-quoted word from + * the string, or null. * - * Use: Parses off space-separated words from a string. + * Use: Fetches the next word from a string. If the flag + * @STRF_QUOTE@ is set, the `\' character acts as an escape, and + * single and double quotes protect whitespace. */ -char *str_getword(char **pp) +char *str_qword(char **pp, unsigned f) { - char *p = *pp, *q; + char *p = *pp, *q, *qq; + int st = 0, pst = 0; + + /* --- Preliminaries --- */ if (!p) return (0); - while (isspace((unsigned char)*p)) p++; + if (!*p) { + *pp = 0; + return (0); + } + + /* --- Main work --- */ - for (q = p; *q; q++) { - if (isspace((unsigned char)*q)) { - *q = 0; - *pp = q + 1; - return (p); + for (q = qq = p; *q; q++) { + switch (st) { + case '\\': + *qq++ = *q; + st = pst; + break; + case '\'': + case '\"': + if (*q == st) + st = pst = 0; + else if (*q == '\\') + st = '\\'; + else + *qq++ = *q; + break; + default: + if (isspace((unsigned char)*q)) { + do q++; while (*q && isspace((unsigned char)*q)); + goto done; + } else if (!(f & STRF_QUOTE)) + goto stdchar; + switch (*q) { + case '\\': + st = '\\'; + break; + case '\'': + case '\"': + st = pst = *q; + break; + default: + stdchar: + *qq++ = *q; + break; + } } } - *pp = 0; + /* --- Finished --- */ + +done: + *pp = *q ? q : 0; + *qq++ = 0; return (p); } -/* --- @str_split@ --- * +/* --- @str_qsplit@ --- * * * Arguments: @char *p@ = pointer to string * @char *v[]@ = pointer to array to fill in * @size_t c@ = count of strings to fill in + * @char **rest@ = where to store the remainder of the string + * @unsigned f@ = flags for @str_qword@ * * Returns: Number of strings filled in. * @@ -92,27 +150,158 @@ char *str_getword(char **pp) * and trailing space stripped off. No more than @c@ words * are read; the actual number is returned as the value of the * function. Unused slots in the array are populated with - * null bytes. + * null bytes. If there's any string left, the address of the + * remainder is stored in @rest@ (if it's non-null); otherwise + * @rest@ is set to a null pointer. */ -size_t str_split(char *p, char *v[], size_t c) +size_t str_qsplit(char *p, char *v[], size_t c, char **rest, unsigned f) { size_t n = 0; char *q; - while (c && (q = str_getword(&p)) != 0) { + while (c && (q = str_qword(&p, f)) != 0) { *v++ = q; c--; n++; } - while (c) { *v++ = 0; c--; } + if (rest) + *rest = p; return (n); } +/* --- @str_getword@ --- * + * + * Arguments: @char **pp@ = address of pointer into string + * + * Returns: Pointer to the next space-separated word from the string, + * or null. + * + * Use: Parses off space-separated words from a string. This is a + * compatibility veneer over @str_qword@. + */ + +char *str_getword(char **pp) +{ + return (str_qword(pp, 0)); +} + +/* --- @str_split@ --- * + * + * Arguments: @char *p@ = pointer to string + * @char *v[]@ = pointer to array to fill in + * @size_t c@ = count of strings to fill in + * @char **rest@ = where to store the remainder of the string + * + * Returns: Number of strings filled in. + * + * Use: Fills an array with pointers to the individual words of a + * string. This is a compatibility veneer over @str_qsplit@. + */ + +size_t str_split(char *p, char *v[], size_t c, char **rest) +{ + return (str_qsplit(p, v, c, rest, 0)); +} + +/* --- @str_match@ --- * + * + * Arguments: @const char *p@ = pointer to pattern string + * @const char *s@ = string to compare with + * + * Returns: Nonzero if the pattern matches the string. + * + * Use: Does simple wildcard matching. This is quite nasty and more + * than a little slow. Supports metacharacters `*', `?' and + * '['. + */ + +int str_match(const char *p, const char *s) +{ + for (;;) { + char pch = *p++, pche, sch; + int sense; + + switch (pch) { + case '?': + if (!*s) + return (0); + s++; + break; + case '*': + if (!*p) + return (1); + while (*s) { + if (str_match(p, s)) + return (1); + s++; + } + return (0); + case '[': + if (!*s) + return (0); + sch = *s++; + pch = *p++; + sense = 1; + if (pch == '^' || pch == '!') { + sense = !sense; + pch = *p++; + } + if (pch == ']') { + if (*p == '-' && p[1] && p[1] != ']') { + pche = p[1]; + p += 2; + if (pch <= sch && sch <= pche) + goto class_match; + } else if (pch == sch) + goto class_match; + pch = *p++; + } + for (;; pch = *p++) { + if (!pch || pch == ']') + goto class_nomatch; + if (*p == '-' && p[1] && p[1] != ']') { + pche = p[1]; + p += 2; + if (pch <= sch && sch <= pche) + goto class_match; + } else if (pch == sch) + goto class_match; + } + class_match: + if (!sense) + return (0); + for (;;) { + pch = *p++; + if (!pch) + return (0); + if (pch == ']') + break; + if (*p == '-' && p[1] && p[1] != ']') + p += 2; + } + break; + class_nomatch: + if (sense) + return (0); + break; + case '\\': + pch = *p++; + default: + if (pch != *s) + return (0); + if (!pch) + return (1); + s++; + break; + } + } +} + /* --- @str_sanitize@ --- * * * Arguments: @char *d@ = destination buffer