chiark / gitweb /
Add global unihash table; use universal hashing instead of CRC.
[mLib] / str.c
1 /* -*-c-*-
2  *
3  * $Id: str.c,v 1.5 2000/10/08 09:50:11 mdw Exp $
4  *
5  * Functions for hacking with strings
6  *
7  * (c) 1999 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of the mLib utilities library.
13  *
14  * mLib is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU Library General Public License as
16  * published by the Free Software Foundation; either version 2 of the
17  * License, or (at your option) any later version.
18  * 
19  * mLib is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU Library General Public License for more details.
23  * 
24  * You should have received a copy of the GNU Library General Public
25  * License along with mLib; if not, write to the Free
26  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27  * MA 02111-1307, USA.
28  */
29
30 /*----- Revision history --------------------------------------------------* 
31  *
32  * $Log: str.c,v $
33  * Revision 1.5  2000/10/08 09:50:11  mdw
34  * (str_qword): Remove redundant definition of @STRF_QUOTE@.
35  *
36  * Revision 1.4  2000/10/08 09:43:34  mdw
37  * New quoted string handling and simple pattern matching.
38  *
39  * Revision 1.3  1999/12/22 15:41:14  mdw
40  * Skip past trailing whitespace in str_getword.
41  *
42  * Revision 1.2  1999/05/26 20:52:57  mdw
43  * Add new `rest' argument for `str_split'.
44  *
45  * Revision 1.1  1999/05/17 20:37:01  mdw
46  * Some trivial string hacks.
47  *
48  */
49
50 /*----- Header files ------------------------------------------------------*/
51
52 #include <ctype.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56
57 #include "str.h"
58
59 /*----- Main code ---------------------------------------------------------*/
60
61 /* --- @str_qword@ --- *
62  *
63  * Arguments:   @char **pp@ = address of pointer into string
64  *              @unsigned f@ = various flags
65  *
66  * Returns:     Pointer to the next space-separated possibly-quoted word from
67  *              the string, or null.
68  *
69  * Use:         Fetches the next word from a string.  If the flag
70  *              @STRF_QUOTE@ is set, the `\' character acts as an escape, and
71  *              single and double quotes protect whitespace.
72  */
73
74 char *str_qword(char **pp, unsigned f)
75 {
76   char *p = *pp, *q, *qq;
77   int st = 0, pst = 0;
78
79   /* --- Preliminaries --- */
80
81   if (!p)
82     return (0);
83   while (isspace((unsigned char)*p))
84     p++;
85   if (!*p) {
86     *pp = 0;
87     return (0);
88   }
89
90   /* --- Main work --- */
91
92   for (q = qq = p; *q; q++) {
93     switch (st) {
94       case '\\':
95         *qq++ = *q;
96         st = pst;
97         break;
98       case '\'':
99       case '\"':
100         if (*q == st)
101           st = pst = 0;
102         else if (*q == '\\')
103           st = '\\';
104         else
105           *qq++ = *q;
106         break;
107       default:
108         if (isspace((unsigned char)*q)) {
109           do q++; while (*q && isspace((unsigned char)*q));
110           goto done;
111         } else if (!(f & STRF_QUOTE))
112           goto stdchar;
113         switch (*q) {
114           case '\\':
115             st = '\\';
116             break;
117           case '\'':
118           case '\"':
119             st = pst = *q;
120             break;
121           default:
122           stdchar:
123             *qq++ = *q;
124             break;
125         }
126     }
127   }
128
129   /* --- Finished --- */
130
131 done:
132   *pp = *q ? q : 0;
133   *qq++ = 0;
134   return (p);
135 }
136
137 /* --- @str_qsplit@ --- *
138  *
139  * Arguments:   @char *p@ = pointer to string
140  *              @char *v[]@ = pointer to array to fill in
141  *              @size_t c@ = count of strings to fill in
142  *              @char **rest@ = where to store the remainder of the string
143  *              @unsigned f@ = flags for @str_qword@
144  *
145  * Returns:     Number of strings filled in.
146  *
147  * Use:         Fills an array with pointers to the individual words of a
148  *              string.  The string is modified in place to contain zero
149  *              bytes at the word boundaries, and the words have leading
150  *              and trailing space stripped off.  No more than @c@ words
151  *              are read; the actual number is returned as the value of the
152  *              function.  Unused slots in the array are populated with
153  *              null bytes.  If there's any string left, the address of the
154  *              remainder is stored in @rest@ (if it's non-null); otherwise
155  *              @rest@ is set to a null pointer.
156  */
157
158 size_t str_qsplit(char *p, char *v[], size_t c, char **rest, unsigned f)
159 {
160   size_t n = 0;
161   char *q;
162
163   while (c && (q = str_qword(&p, f)) != 0) {
164     *v++ = q;
165     c--;
166     n++;
167   }
168   while (c) {
169     *v++ = 0;
170     c--;
171   }
172   if (rest)
173     *rest = p;
174   return (n);
175 }
176
177 /* --- @str_getword@ --- *
178  *
179  * Arguments:   @char **pp@ = address of pointer into string
180  *
181  * Returns:     Pointer to the next space-separated word from the string,
182  *              or null.
183  *
184  * Use:         Parses off space-separated words from a string.  This is a
185  *              compatibility veneer over @str_qword@.
186  */
187
188 char *str_getword(char **pp)
189 {
190   return (str_qword(pp, 0));
191 }
192
193 /* --- @str_split@ --- *
194  *
195  * Arguments:   @char *p@ = pointer to string
196  *              @char *v[]@ = pointer to array to fill in
197  *              @size_t c@ = count of strings to fill in
198  *              @char **rest@ = where to store the remainder of the string
199  *
200  * Returns:     Number of strings filled in.
201  *
202  * Use:         Fills an array with pointers to the individual words of a
203  *              string.  This is a compatibility veneer over @str_qsplit@.
204  */
205
206 size_t str_split(char *p, char *v[], size_t c, char **rest)
207 {
208   return (str_qsplit(p, v, c, rest, 0));
209 }
210
211 /* --- @str_match@ --- *
212  *
213  * Arguments:   @const char *p@ = pointer to pattern string
214  *              @const char *s@ = string to compare with
215  *
216  * Returns:     Nonzero if the pattern matches the string.
217  *
218  * Use:         Does simple wildcard matching.  This is quite nasty and more
219  *              than a little slow.  Supports metacharacters `*', `?' and
220  *              '['.
221  */
222
223 int str_match(const char *p, const char *s)
224 {
225   for (;;) {
226     char pch = *p++, pche, sch;
227     int sense;
228
229     switch (pch) {
230       case '?':
231         if (!*s)
232           return (0);
233         s++;
234         break;
235       case '*':
236         if (!*p)
237           return (1);
238         while (*s) {
239           if (str_match(p, s))
240             return (1);
241           s++;
242         }
243         return (0);
244       case '[':
245         if (!*s)
246           return (0);
247         sch = *s++;
248         pch = *p++;
249         sense = 1;
250         if (pch == '^' || pch == '!') {
251           sense = !sense;
252           pch = *p++;
253         }
254         if (pch == ']') {
255           if (*p == '-' && p[1] && p[1] != ']') {
256             pche = p[1];
257             p += 2;
258             if (pch <= sch && sch <= pche)
259               goto class_match;
260           } else if (pch == sch)
261             goto class_match;
262           pch = *p++;
263         }
264         for (;; pch = *p++) {
265           if (!pch || pch == ']')
266             goto class_nomatch;
267           if (*p == '-' && p[1] && p[1] != ']') {
268             pche = p[1];
269             p += 2;
270             if (pch <= sch && sch <= pche)
271               goto class_match;
272           } else if (pch == sch)
273             goto class_match;
274         }
275       class_match:
276         if (!sense)
277           return (0);
278         for (;;) {
279           pch = *p++;
280           if (!pch)
281             return (0);
282           if (pch == ']')
283             break;
284           if (*p == '-' && p[1] && p[1] != ']')
285             p += 2;
286         }
287         break;
288       class_nomatch:
289         if (sense)
290           return (0);
291         break;
292       case '\\':
293         pch = *p++;
294       default:
295         if (pch != *s)
296           return (0);
297         if (!pch)
298           return (1);
299         s++;
300         break;
301     }
302   }
303 }
304
305 /* --- @str_sanitize@ --- *
306  *
307  * Arguments:   @char *d@ = destination buffer
308  *              @const char *p@ = pointer to source string
309  *              @size_t sz@ = size of destination buffer
310  *
311  * Returns:     ---
312  *
313  * Use:         Writes a string into a buffer, being careful not to overflow
314  *              the buffer, to null terminate the result, and to prevent
315  *              nasty nonprintable characters ending up in the buffer.
316  */
317
318 void str_sanitize(char *d, const char *p, size_t sz)
319 {
320   if (!sz)
321     return;
322   sz--;
323   while (*p && sz) {
324     int ch = *p++;
325     if (!isgraph((unsigned char)ch))
326       ch = '_';
327     *d++ = ch;
328     sz--;
329   }
330   *d++ = 0;
331 }
332
333 /*----- That's all, folks -------------------------------------------------*/