1 /* $NetBSD: strlist.c,v 1.2 2021/01/23 19:41:16 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2021 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * strlist --
34 *
35 * A set of routines for interacting with IEEE 1275 (OpenFirmware)
36 * style string lists.
37 *
38 * An OpenFirmware string list is simply a buffer containing
39 * multiple NUL-terminated strings concatenated together.
40 *
41 * So, for example, the a string list consisting of the strings
42 * "foo", "bar", and "baz" would be represented in memory like:
43 *
44 * foo\0bar\0baz\0
45 */
46
47 #include <sys/types.h>
48
49 /*
50 * Memory allocation wrappers to handle different environments.
51 */
52 #if defined(_KERNEL)
53 #include <sys/kmem.h>
54 #include <sys/systm.h>
55
56 static void *
strlist_alloc(size_t const size)57 strlist_alloc(size_t const size)
58 {
59 return kmem_zalloc(size, KM_SLEEP);
60 }
61
62 static void
strlist_free(void * const v,size_t const size)63 strlist_free(void * const v, size_t const size)
64 {
65 kmem_free(v, size);
66 }
67 #elif defined(_STANDALONE)
68 #include <lib/libkern/libkern.h>
69 #include <lib/libsa/stand.h>
70
71 static void *
strlist_alloc(size_t const size)72 strlist_alloc(size_t const size)
73 {
74 char *cp = alloc(size);
75 if (cp != NULL) {
76 memset(cp, 0, size);
77 }
78 return cp;
79 }
80
81 static void
strlist_free(void * const v,size_t const size)82 strlist_free(void * const v, size_t const size)
83 {
84 dealloc(v, size);
85 }
86 #else /* user-space */
87 #include <stdlib.h>
88 #include <string.h>
89
90 extern int pmatch(const char *, const char *, const char **);
91
92 static void *
strlist_alloc(size_t const size)93 strlist_alloc(size_t const size)
94 {
95 return calloc(1, size);
96 }
97
98 static void
strlist_free(void * const v,size_t const size __unused)99 strlist_free(void * const v, size_t const size __unused)
100 {
101 free(v);
102 }
103 #endif
104
105 #include "strlist.h"
106
107 /*
108 * strlist_next --
109 *
110 * Return a pointer to the next string in the strlist,
111 * or NULL if there are no more strings.
112 */
113 const char *
strlist_next(const char * const sl,size_t const slsize,size_t * const cursorp)114 strlist_next(const char * const sl, size_t const slsize, size_t * const cursorp)
115 {
116
117 if (sl == NULL || slsize == 0 || cursorp == NULL) {
118 return NULL;
119 }
120
121 size_t cursor = *cursorp;
122
123 if (cursor >= slsize) {
124 /* No more strings in the list. */
125 return NULL;
126 }
127
128 const char *cp = sl + cursor;
129 *cursorp = cursor + strlen(cp) + 1;
130
131 return cp;
132 }
133
134 /*
135 * strlist_count --
136 *
137 * Return the number of strings in the strlist.
138 */
139 unsigned int
strlist_count(const char * sl,size_t slsize)140 strlist_count(const char *sl, size_t slsize)
141 {
142
143 if (sl == NULL || slsize == 0) {
144 return 0;
145 }
146
147 size_t cursize;
148 unsigned int count;
149
150 for (count = 0; slsize != 0;
151 count++, sl += cursize, slsize -= cursize) {
152 cursize = strlen(sl) + 1;
153 }
154 return count;
155 }
156
157 /*
158 * strlist_string --
159 *
160 * Returns the string in the strlist at the specified index.
161 * Returns NULL if the index is beyond the strlist range.
162 */
163 const char *
strlist_string(const char * sl,size_t slsize,unsigned int const idx)164 strlist_string(const char * sl, size_t slsize, unsigned int const idx)
165 {
166
167 if (sl == NULL || slsize == 0) {
168 return NULL;
169 }
170
171 size_t cursize;
172 unsigned int i;
173
174 for (i = 0; slsize != 0; i++, slsize -= cursize, sl += cursize) {
175 cursize = strlen(sl) + 1;
176 if (i == idx) {
177 return sl;
178 }
179 }
180
181 return NULL;
182 }
183
184 static bool
match_strcmp(const char * const s1,const char * const s2)185 match_strcmp(const char * const s1, const char * const s2)
186 {
187 return strcmp(s1, s2) == 0;
188 }
189
190 #if !defined(_STANDALONE)
191 static bool
match_pmatch(const char * const s1,const char * const s2)192 match_pmatch(const char * const s1, const char * const s2)
193 {
194 return pmatch(s1, s2, NULL) == 2;
195 }
196 #endif /* _STANDALONE */
197
198 static bool
strlist_match_internal(const char * const sl,size_t slsize,const char * const str,int * const indexp,unsigned int * const countp,bool (* match_fn)(const char *,const char *))199 strlist_match_internal(const char * const sl, size_t slsize,
200 const char * const str, int * const indexp, unsigned int * const countp,
201 bool (*match_fn)(const char *, const char *))
202 {
203 const char *cp;
204 size_t l;
205 int i;
206 bool rv = false;
207
208 if (sl == NULL || slsize == 0) {
209 return false;
210 }
211
212 cp = sl;
213
214 for (i = 0; slsize != 0;
215 l = strlen(cp) + 1, slsize -= l, cp += l, i++) {
216 if (rv) {
217 /*
218 * We've already matched. We must be
219 * counting to the end.
220 */
221 continue;
222 }
223 if ((*match_fn)(cp, str)) {
224 /*
225 * Matched! Get the index. If we don't
226 * also want the total count, then get
227 * out early.
228 */
229 *indexp = i;
230 rv = true;
231 if (countp == NULL) {
232 break;
233 }
234 }
235 }
236
237 if (countp != NULL) {
238 *countp = i;
239 }
240
241 return rv;
242 }
243
244 /*
245 * strlist_match --
246 *
247 * Returns a weighted match value (1 <= match <= sl->count) if the
248 * specified string appears in the strlist. A match at the
249 * beginning of the list carriest the greatest weight (i.e. sl->count)
250 * and a match at the end of the list carriest the least (i.e. 1).
251 * Returns 0 if there is no match.
252 *
253 * This routine operates independently of the cursor used to enumerate
254 * a strlist.
255 */
256 int
strlist_match(const char * const sl,size_t const slsize,const char * const str)257 strlist_match(const char * const sl, size_t const slsize,
258 const char * const str)
259 {
260 unsigned int count;
261 int idx;
262
263 if (strlist_match_internal(sl, slsize, str, &idx, &count,
264 match_strcmp)) {
265 return count - idx;
266 }
267 return 0;
268 }
269
270 #if !defined(_STANDALONE)
271 /*
272 * strlist_pmatch --
273 *
274 * Like strlist_match(), but uses pmatch(9) to match the
275 * strings.
276 */
277 int
strlist_pmatch(const char * const sl,size_t const slsize,const char * const pattern)278 strlist_pmatch(const char * const sl, size_t const slsize,
279 const char * const pattern)
280 {
281 unsigned int count;
282 int idx;
283
284 if (strlist_match_internal(sl, slsize, pattern, &idx, &count,
285 match_pmatch)) {
286 return count - idx;
287 }
288 return 0;
289 }
290 #endif /* _STANDALONE */
291
292 /*
293 * strlist_index --
294 *
295 * Returns the index of the specified string if it appears
296 * in the strlist. Returns -1 if the string is not found.
297 *
298 * This routine operates independently of the cursor used to enumerate
299 * a strlist.
300 */
301 int
strlist_index(const char * const sl,size_t const slsize,const char * const str)302 strlist_index(const char * const sl, size_t const slsize,
303 const char * const str)
304 {
305 int idx;
306
307 if (strlist_match_internal(sl, slsize, str, &idx, NULL,
308 match_strcmp)) {
309 return idx;
310 }
311 return -1;
312 }
313
314 /*
315 * strlist_append --
316 *
317 * Append the specified string to a mutable strlist. Turns
318 * true if successful, false upon failure for any reason.
319 */
320 bool
strlist_append(char ** const slp,size_t * const slsizep,const char * const str)321 strlist_append(char ** const slp, size_t * const slsizep,
322 const char * const str)
323 {
324 size_t const slsize = *slsizep;
325 char * const sl = *slp;
326
327 size_t const addsize = strlen(str) + 1;
328 size_t const newsize = slsize + addsize;
329 char * const newbuf = strlist_alloc(newsize);
330
331 if (newbuf == NULL) {
332 return false;
333 }
334
335 if (sl != NULL) {
336 memcpy(newbuf, sl, slsize);
337 }
338
339 memcpy(newbuf + slsize, str, addsize);
340
341 if (sl != NULL) {
342 strlist_free(sl, slsize);
343 }
344
345 *slp = newbuf;
346 *slsizep = newsize;
347
348 return true;
349 }
350
351 #ifdef STRLIST_TEST
352 /*
353 * To build and run the tests:
354 *
355 * % cc -DSTRLIST_TEST -Os pmatch.c strlist.c
356 * % ./a.out
357 * Testing basic properties.
358 * Testing enumeration.
359 * Testing weighted matching.
360 * Testing pattern matching.
361 * Testing index return.
362 * Testing string-at-index.
363 * Testing gross blob count.
364 * Testing gross blob indexing.
365 * Testing creating a strlist.
366 * Verifying new strlist.
367 * All tests completed successfully.
368 * %
369 */
370
371 static char nice_blob[] = "zero\0one\0two\0three\0four\0five";
372 static char gross_blob[] = "zero\0\0two\0\0four\0\0";
373
374 #include <assert.h>
375 #include <stdio.h>
376
377 int
main(int argc,char * argv[])378 main(int argc, char *argv[])
379 {
380 const char *sl;
381 size_t slsize;
382 size_t cursor;
383 const char *cp;
384 size_t size;
385
386 sl = nice_blob;
387 slsize = sizeof(nice_blob);
388
389 printf("Testing basic properties.\n");
390 assert(strlist_count(sl, slsize) == 6);
391
392 printf("Testing enumeration.\n");
393 cursor = 0;
394 assert((cp = strlist_next(sl, slsize, &cursor)) != NULL);
395 assert(strcmp(cp, "zero") == 0);
396
397 assert((cp = strlist_next(sl, slsize, &cursor)) != NULL);
398 assert(strcmp(cp, "one") == 0);
399
400 assert((cp = strlist_next(sl, slsize, &cursor)) != NULL);
401 assert(strcmp(cp, "two") == 0);
402
403 assert((cp = strlist_next(sl, slsize, &cursor)) != NULL);
404 assert(strcmp(cp, "three") == 0);
405
406 assert((cp = strlist_next(sl, slsize, &cursor)) != NULL);
407 assert(strcmp(cp, "four") == 0);
408
409 assert((cp = strlist_next(sl, slsize, &cursor)) != NULL);
410 assert(strcmp(cp, "five") == 0);
411
412 assert((cp = strlist_next(sl, slsize, &cursor)) == NULL);
413
414 printf("Testing weighted matching.\n");
415 assert(strlist_match(sl, slsize, "non-existent") == 0);
416 assert(strlist_match(sl, slsize, "zero") == 6);
417 assert(strlist_match(sl, slsize, "one") == 5);
418 assert(strlist_match(sl, slsize, "two") == 4);
419 assert(strlist_match(sl, slsize, "three") == 3);
420 assert(strlist_match(sl, slsize, "four") == 2);
421 assert(strlist_match(sl, slsize, "five") == 1);
422
423 printf("Testing pattern matching.\n");
424 assert(strlist_pmatch(sl, slsize, "t?o") == 4);
425 assert(strlist_pmatch(sl, slsize, "f[a-o][o-u][a-z]") == 2);
426
427 printf("Testing index return.\n");
428 assert(strlist_index(sl, slsize, "non-existent") == -1);
429 assert(strlist_index(sl, slsize, "zero") == 0);
430 assert(strlist_index(sl, slsize, "one") == 1);
431 assert(strlist_index(sl, slsize, "two") == 2);
432 assert(strlist_index(sl, slsize, "three") == 3);
433 assert(strlist_index(sl, slsize, "four") == 4);
434 assert(strlist_index(sl, slsize, "five") == 5);
435
436 printf("Testing string-at-index.\n");
437 assert(strcmp(strlist_string(sl, slsize, 0), "zero") == 0);
438 assert(strcmp(strlist_string(sl, slsize, 1), "one") == 0);
439 assert(strcmp(strlist_string(sl, slsize, 2), "two") == 0);
440 assert(strcmp(strlist_string(sl, slsize, 3), "three") == 0);
441 assert(strcmp(strlist_string(sl, slsize, 4), "four") == 0);
442 assert(strcmp(strlist_string(sl, slsize, 5), "five") == 0);
443 assert(strlist_string(sl, slsize, 6) == NULL);
444
445 sl = gross_blob;
446 slsize = sizeof(gross_blob);
447
448 printf("Testing gross blob count.\n");
449 assert(strlist_count(sl, slsize) == 7);
450
451 printf("Testing gross blob indexing.\n");
452 assert(strcmp(strlist_string(sl, slsize, 0), "zero") == 0);
453 assert(strcmp(strlist_string(sl, slsize, 1), "") == 0);
454 assert(strcmp(strlist_string(sl, slsize, 2), "two") == 0);
455 assert(strcmp(strlist_string(sl, slsize, 3), "") == 0);
456 assert(strcmp(strlist_string(sl, slsize, 4), "four") == 0);
457 assert(strcmp(strlist_string(sl, slsize, 5), "") == 0);
458 assert(strcmp(strlist_string(sl, slsize, 6), "") == 0);
459 assert(strlist_string(sl, slsize, 7) == NULL);
460
461
462 printf("Testing creating a strlist.\n");
463 char *newsl = NULL;
464 size_t newslsize = 0;
465 assert(strlist_append(&newsl, &newslsize, "zero"));
466 assert(strlist_append(&newsl, &newslsize, "one"));
467 assert(strlist_append(&newsl, &newslsize, "two"));
468 assert(strlist_append(&newsl, &newslsize, "three"));
469 assert(strlist_append(&newsl, &newslsize, "four"));
470 assert(strlist_append(&newsl, &newslsize, "five"));
471
472 printf("Verifying new strlist.\n");
473 assert(strlist_count(newsl, newslsize) == 6);
474 assert(strcmp(strlist_string(newsl, newslsize, 0), "zero") == 0);
475 assert(strcmp(strlist_string(newsl, newslsize, 1), "one") == 0);
476 assert(strcmp(strlist_string(newsl, newslsize, 2), "two") == 0);
477 assert(strcmp(strlist_string(newsl, newslsize, 3), "three") == 0);
478 assert(strcmp(strlist_string(newsl, newslsize, 4), "four") == 0);
479 assert(strcmp(strlist_string(newsl, newslsize, 5), "five") == 0);
480 assert(strlist_string(newsl, newslsize, 6) == NULL);
481
482 /* This should be equivalent to nice_blob. */
483 assert(newslsize == sizeof(nice_blob));
484 assert(memcmp(newsl, nice_blob, newslsize) == 0);
485
486
487 printf("All tests completed successfully.\n");
488 return 0;
489 }
490
491 #endif /* STRLIST_TEST */
492