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