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 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 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 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 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 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 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 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