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