Infrastructure: Split the files into subdirectories.
[mLib] / utils / str.c
diff --git a/utils/str.c b/utils/str.c
new file mode 100644 (file)
index 0000000..51ca323
--- /dev/null
@@ -0,0 +1,323 @@
+/* -*-c-*-
+ *
+ * Functions for hacking with strings
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with mLib; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "str.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @str_qword@ --- *
+ *
+ * Arguments:  @char **pp@ = address of pointer into string
+ *             @unsigned f@ = various flags
+ *
+ * Returns:    Pointer to the next space-separated possibly-quoted word from
+ *             the string, or null.
+ *
+ * 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_qword(char **pp, unsigned f)
+{
+  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 = 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;
+       }
+    }
+  }
+
+  /* --- Finished --- */
+
+done:
+  *pp = *q ? q : 0;
+  *qq++ = 0;
+  return (p);
+}
+
+/* --- @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.
+ *
+ * Use:                Fills an array with pointers to the individual words of a
+ *             string.  The string is modified in place to contain zero
+ *             bytes at the word boundaries, and the words have leading
+ *             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.  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_qsplit(char *p, char *v[], size_t c, char **rest, unsigned f)
+{
+  size_t n = 0;
+  char *q;
+
+  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_matchx@ --- *
+ *
+ * Arguments:  @const char *p@ = pointer to pattern string
+ *             @const char *s@ = string to compare with
+ *             @unsigned f@ = various flags
+ *
+ * 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_matchx(const char *p, const char *s, unsigned f)
+{
+  for (;;) {
+    char pch = *p++, pche, sch;
+    int sense;
+
+    if ((f & STRF_PREFIX) && !*s)
+      return (1);
+    switch (pch) {
+      case '?':
+       if (!*s)
+         return (0);
+       s++;
+       break;
+      case '*':
+       if (!*p || (f & STRF_PREFIX))
+         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_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.  Equivalent to @str_matchx@
+ *             with zero flags word.
+ */
+
+int str_match(const char *p, const char *s)
+  { return (str_matchx(p, s, 0)); }
+
+/* --- @str_sanitize@ --- *
+ *
+ * Arguments:  @char *d@ = destination buffer
+ *             @const char *p@ = pointer to source string
+ *             @size_t sz@ = size of destination buffer
+ *
+ * Returns:    ---
+ *
+ * Use:                Writes a string into a buffer, being careful not to overflow
+ *             the buffer, to null terminate the result, and to prevent
+ *             nasty nonprintable characters ending up in the buffer.
+ */
+
+void str_sanitize(char *d, const char *p, size_t sz)
+{
+  if (!sz)
+    return;
+  sz--;
+  while (*p && sz) {
+    int ch = *p++;
+    if (!isgraph((unsigned char)ch))
+      ch = '_';
+    *d++ = ch;
+    sz--;
+  }
+  *d++ = 0;
+}
+
+/*----- That's all, folks -------------------------------------------------*/