chiark / gitweb /
(crc-mktab): now requires str.c.
[mLib] / str.c
diff --git a/str.c b/str.c
index 51a980501800948a85abf8c0da22cb473244d2af..9b5fc63cde88641de2b238f099fd3603a678ce28 100644 (file)
--- 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
  *
 /*----- 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.
  *
 
 /*----- 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