xref: /netbsd-src/crypto/external/bsd/libsaslc/dist/src/list.c (revision daf6c4152fcddc27c445489775ed1f66ab4ea9a9)
1 /* $NetBSD: list.c,v 1.2 2011/02/12 23:21:32 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.2 2011/02/12 23:21:32 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
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 *
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 *
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 *
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 *
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
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 p string to parse
168  * @return allocated list.
169  */
170 list_t *
171 saslc__list_parse(const char *p)
172 {
173 	const char *e, *n;
174 	list_t *l, *t, **tp;
175 
176 	l = NULL;
177 	tp = NULL;
178 	n = p;
179 	for (;;) {
180 		p = n;
181 		p = skip_LWS(p);
182 		if (*p == '\0')
183 			break;
184 		n = next_element(p);
185 		e = n > p && n[-1] == ',' ? n - 1 : n;
186 		e = strip_LWS(e - 1, p);
187 		if (e <= p)
188 			continue;
189 		t = alloc_list(p, (size_t)(e - p));
190 		if (t == NULL) {
191 			saslc__list_free(l);
192 			return NULL;
193 		}
194 		if (tp != NULL)
195 			*tp = t;
196 		else
197 			l = t;
198 		tp = &t->next;
199 	}
200 	return l;
201 }
202 
203 /**
204  * @brief allocate a new list node for a string and append it to a
205  * list
206  * @param l the list to append
207  * @param p the string
208  */
209 int
210 saslc__list_append(list_t **l, const char *p)
211 {
212 	list_t *n, *e;
213 
214 	e = NULL;
215 	for (n = *l; n != NULL; n = n->next)
216 		e = n;
217 
218 	n = alloc_list(p, strlen(p));
219 	if (n == NULL)
220 		return -1;
221 
222 	if (e == NULL)
223 		*l = n;
224 	else
225 		e->next = n;
226 
227 	return 0;
228 }
229 
230 /**
231  * @brief get the flags corresponding to a given name.
232  * @param key the key to find the flags for.
233  * @param tbl the NULL terminated named_flag_t table to use for the
234  * lookup.
235  * @return the flags if found or zero if not.
236  */
237 static unsigned int
238 get_named_flag(const char *key, const named_flag_t *tbl)
239 {
240 	const named_flag_t *p;
241 
242 	for (p = tbl; p->name != NULL; p++) {
243 		if (strcasecmp(key, p->name) == 0)
244 			return p->flag;
245 	}
246 	return 0;
247 }
248 
249 /**
250  * @brief collect all the flags from a list of flag names.
251  * @param list the list
252  * @param tbl the NULL terminated named_flag_t table to use for the
253  * lookups
254  * @return the or-ed flags of all the matches
255  */
256 uint32_t
257 saslc__list_flags(list_t *list, const named_flag_t *tbl)
258 {
259 	list_t *l;
260 	uint32_t flags;
261 
262 	flags = 0;
263 	for (l = list; l != NULL; l = l->next)
264 		flags |= get_named_flag(l->value, tbl);
265 
266 	return flags;
267 }
268 
269 /**
270  * @brief print all the values in a list if debugging is enabled
271  * @param list the list
272  * @param str a string to print before the results.
273  *
274  * XXX: move this to msg.c?
275  */
276 void
277 saslc__list_log(list_t *list, const char *str)
278 {
279 	list_t *l;
280 
281 	if (!saslc_debug)
282 		return;
283 
284 	saslc__msg_dbg("%s", str);
285 	for (l = list; l != NULL; l = l->next)
286 		saslc__msg_dbg("  value: '%s'\n",
287 		    l->value ? l->value : "<null>");
288 }
289