utils/macros.h: Add <ctype.h> and `foocmp' helper macros.
[mLib] / utils / str.c
CommitLineData
081e6815 1/* -*-c-*-
2 *
081e6815 3 * Functions for hacking with strings
4 *
5 * (c) 1999 Straylight/Edgeware
6 */
7
d4efbcd9 8/*----- Licensing notice --------------------------------------------------*
081e6815 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.
d4efbcd9 16 *
081e6815 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.
d4efbcd9 21 *
081e6815 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
081e6815 28/*----- Header files ------------------------------------------------------*/
29
30#include <ctype.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34
36188114 35#include "macros.h"
081e6815 36#include "str.h"
37
38/*----- Main code ---------------------------------------------------------*/
39
efae42a6 40/* --- @str_qword@ --- *
081e6815 41 *
42 * Arguments: @char **pp@ = address of pointer into string
efae42a6 43 * @unsigned f@ = various flags
081e6815 44 *
efae42a6 45 * Returns: Pointer to the next space-separated possibly-quoted word from
46 * the string, or null.
081e6815 47 *
efae42a6 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.
081e6815 51 */
52
efae42a6 53char *str_qword(char **pp, unsigned f)
081e6815 54{
efae42a6 55 char *p = *pp, *q, *qq;
56 int st = 0, pst = 0;
57
58 /* --- Preliminaries --- */
081e6815 59
60 if (!p)
61 return (0);
36188114 62 while (ISSPACE(*p))
081e6815 63 p++;
efae42a6 64 if (!*p) {
65 *pp = 0;
66 return (0);
67 }
68
69 /* --- Main work --- */
081e6815 70
efae42a6 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:
36188114
MW
87 if (ISSPACE(*q)) {
88 do q++; while (*q && ISSPACE(*q));
efae42a6 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 }
081e6815 105 }
106 }
107
efae42a6 108 /* --- Finished --- */
109
110done:
111 *pp = *q ? q : 0;
112 *qq++ = 0;
081e6815 113 return (p);
114}
115
efae42a6 116/* --- @str_qsplit@ --- *
081e6815 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
f3a542e8 121 * @char **rest@ = where to store the remainder of the string
efae42a6 122 * @unsigned f@ = flags for @str_qword@
081e6815 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
f3a542e8 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.
081e6815 135 */
136
efae42a6 137size_t str_qsplit(char *p, char *v[], size_t c, char **rest, unsigned f)
081e6815 138{
139 size_t n = 0;
140 char *q;
141
efae42a6 142 while (c && (q = str_qword(&p, f)) != 0) {
081e6815 143 *v++ = q;
144 c--;
145 n++;
146 }
081e6815 147 while (c) {
148 *v++ = 0;
149 c--;
150 }
48d198f1 151 if (rest)
152 *rest = p;
081e6815 153 return (n);
154}
155
efae42a6 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
d45be1af 167char *str_getword(char **pp) { return (str_qword(pp, 0)); }
efae42a6 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
182size_t str_split(char *p, char *v[], size_t c, char **rest)
d45be1af 183 { return (str_qsplit(p, v, c, rest, 0)); }
efae42a6 184
26f325c0 185/* --- @str_matchx@ --- *
efae42a6 186 *
187 * Arguments: @const char *p@ = pointer to pattern string
188 * @const char *s@ = string to compare with
26f325c0 189 * @unsigned f@ = various flags
efae42a6 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
26f325c0 198int str_matchx(const char *p, const char *s, unsigned f)
efae42a6 199{
200 for (;;) {
201 char pch = *p++, pche, sch;
202 int sense;
203
26f325c0
MW
204 if ((f & STRF_PREFIX) && !*s)
205 return (1);
efae42a6 206 switch (pch) {
207 case '?':
208 if (!*s)
209 return (0);
210 s++;
211 break;
212 case '*':
26f325c0 213 if (!*p || (f & STRF_PREFIX))
efae42a6 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
26f325c0
MW
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
293int str_match(const char *p, const char *s)
294 { return (str_matchx(p, s, 0)); }
295
081e6815 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
309void 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++;
36188114 316 if (!ISGRAPH(ch))
081e6815 317 ch = '_';
318 *d++ = ch;
319 sz--;
320 }
321 *d++ = 0;
322}
323
324/*----- That's all, folks -------------------------------------------------*/