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