xref: /netbsd-src/crypto/external/bsd/libsaslc/dist/src/list.c (revision 09484ebb416ab960674c4302bf9f696a73c945f4)
1 /* $NetBSD: list.c,v 1.3 2011/02/20 01:59:46 christos Exp $ */
2 
3 /* Copyright (c) 2010 The NetBSD Foundation, Inc.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *        This product includes software developed by the NetBSD
17  *        Foundation, Inc. and its contributors.
18  * 4. Neither the name of The NetBSD Foundation nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25  * PURPOSE ARE DISCLAIMED.	IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 #include <sys/cdefs.h>
35 __RCSID("$NetBSD: list.c,v 1.3 2011/02/20 01:59:46 christos Exp $");
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 
41 #include "list.h"
42 #include "msg.h"
43 
44 /**
45  * @brief test for linear white space
46  * @param ch character to test
47  * @return true if LWS, false otherwise.
48  */
49 static int
is_LWS(const char ch)50 is_LWS(const char ch)
51 {
52 
53 	return ch == '\n' || ch == ' ' || ch == '\t';
54 }
55 
56 /**
57  * @brief skip leading whitespace (does not modify string)
58  * @param p start of string
59  * @return pointer to string after leading whitespace
60  */
61 static const char *
skip_LWS(const char * p)62 skip_LWS(const char *p)
63 {
64 
65 	while (*p != '\0' && is_LWS((unsigned char)*p))
66 		p++;
67 	return p;
68 }
69 
70 /**
71  * @brief remove trailing whitespace from a string (does not modify
72  * string)
73  * @param p pointer to start of trailing whitespace
74  * @param s pointer to start of string
75  * @return pointer to new end of string
76  */
77 static const char *
strip_LWS(const char * p,const char * s)78 strip_LWS(const char *p, const char *s)
79 {
80 
81 	if (p <= s)
82 		return s;
83 	while (p >= s && is_LWS((unsigned char)*p))
84 		p--;
85 	return p + 1;
86 }
87 
88 /**
89  * @brief find the next element in a comma delimited list
90  * @param p pointer to list string
91  * @return pointer to start of next element in list
92  */
93 static const char *
next_element(const char * p)94 next_element(const char *p)
95 {
96 	int quoting;
97 	int escaped;
98 
99 	quoting = 0;
100 	escaped = 0;
101 	for (; *p != '\0'; p++) {
102 		switch (*p) {
103 		case ',':
104 			if (!quoting && !escaped)
105 				return p + 1;
106 			escaped = 0;
107 			break;
108 		case '"':
109 			if (!escaped)
110 				quoting = !quoting;
111 			escaped = 0;
112 			break;
113 		case '\\':
114 			escaped = !escaped;
115 			break;
116 		default:
117 			escaped = 0;
118 			break;
119 		}
120 	}
121 	return p;
122 }
123 
124 /**
125  * @brief allocate a list_t node, the memory for its value, and save
126  * the value.
127  * @param b buffer containing data to save in node value
128  * @param len length of data to save
129  * @return the list_t node, or NULL on failure.
130  */
131 static list_t *
alloc_list(const char * b,size_t len)132 alloc_list(const char *b, size_t len)
133 {
134 	list_t *l;
135 
136 	if ((l = malloc(sizeof(*l))) == NULL)
137 		return NULL;
138 	if ((l->value = malloc(len + 1)) == NULL) {
139 		free(l);
140 		return NULL;
141 	}
142 	memcpy(l->value, b, len);
143 	l->value[len]  = '\0';
144 	l->next = NULL;
145 	return l;
146 }
147 
148 /**
149  * @brief free an allocated list
150  * @param l the list
151  */
152 void
saslc__list_free(list_t * l)153 saslc__list_free(list_t *l)
154 {
155 	list_t *n;
156 
157 	for (/*EMPTY*/; l != NULL; l = n) {
158 		n = l->next;
159 		free(l->value);
160 		free(l);
161 	}
162 }
163 
164 /**
165  * @brief Parse a list of the following format:
166  *   ( *LWS element *( *LWS "," *LWS element ))
167  * @param lp pointer to list_t type for returned list.  Cannot be NULL.
168  * @param p string to parse
169  * @return 0 on success, -1 on error (no memory).
170  *
171  * Note: the list is allocated.  Use saslc__list_free() to free it.
172  */
173 int
saslc__list_parse(list_t ** lp,const char * p)174 saslc__list_parse(list_t **lp, const char *p)
175 {
176 	const char *e, *n;
177 	list_t *l, *t, **tp;
178 
179 	l = NULL;
180 	tp = NULL;
181 	n = p;
182 	for (;;) {
183 		p = n;
184 		p = skip_LWS(p);
185 		if (*p == '\0')
186 			break;
187 		n = next_element(p);
188 		e = n > p && n[-1] == ',' ? n - 1 : n;
189 		e = strip_LWS(e - 1, p);
190 		if (e <= p)
191 			continue;
192 		t = alloc_list(p, (size_t)(e - p));
193 		if (t == NULL) {
194 			saslc__list_free(l);
195 			return -1;
196 		}
197 		if (tp != NULL)
198 			*tp = t;
199 		else
200 			l = t;
201 		tp = &t->next;
202 	}
203 	*lp = l;
204 	return 0;
205 }
206 
207 /**
208  * @brief allocate a new list node for a string and append it to a
209  * list
210  * @param l the list to append
211  * @param p the string
212  */
213 int
saslc__list_append(list_t ** l,const char * p)214 saslc__list_append(list_t **l, const char *p)
215 {
216 	list_t *n, *e;
217 
218 	e = NULL;
219 	for (n = *l; n != NULL; n = n->next)
220 		e = n;
221 
222 	n = alloc_list(p, strlen(p));
223 	if (n == NULL)
224 		return -1;
225 
226 	if (e == NULL)
227 		*l = n;
228 	else
229 		e->next = n;
230 
231 	return 0;
232 }
233 
234 /**
235  * @brief get the flags corresponding to a given name.
236  * @param key the key to find the flags for.
237  * @param tbl the NULL terminated named_flag_t table to use for the
238  * lookup.
239  * @return the flags if found or zero if not.
240  */
241 static unsigned int
get_named_flag(const char * key,const named_flag_t * tbl)242 get_named_flag(const char *key, const named_flag_t *tbl)
243 {
244 	const named_flag_t *p;
245 
246 	for (p = tbl; p->name != NULL; p++) {
247 		if (strcasecmp(key, p->name) == 0)
248 			return p->flag;
249 	}
250 	return 0;
251 }
252 
253 /**
254  * @brief collect all the flags from a list of flag names.
255  * @param list the list
256  * @param tbl the NULL terminated named_flag_t table to use for the
257  * lookups
258  * @return the or-ed flags of all the matches
259  */
260 uint32_t
saslc__list_flags(list_t * list,const named_flag_t * tbl)261 saslc__list_flags(list_t *list, const named_flag_t *tbl)
262 {
263 	list_t *l;
264 	uint32_t flags;
265 
266 	flags = 0;
267 	for (l = list; l != NULL; l = l->next)
268 		flags |= get_named_flag(l->value, tbl);
269 
270 	return flags;
271 }
272 
273 /**
274  * @brief print all the values in a list if debugging is enabled
275  * @param list the list
276  * @param str a string to print before the results.
277  *
278  * XXX: move this to msg.c?
279  */
280 void
saslc__list_log(list_t * list,const char * str)281 saslc__list_log(list_t *list, const char *str)
282 {
283 	list_t *l;
284 
285 	if (!saslc_debug)
286 		return;
287 
288 	saslc__msg_dbg("%s", str);
289 	for (l = list; l != NULL; l = l->next)
290 		saslc__msg_dbg("  value: '%s'\n",
291 		    l->value ? l->value : "<null>");
292 }
293