1 /* $NetBSD: list.c,v 1.4 2025/12/16 12:03:39 nia 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. Neither the name of The NetBSD Foundation nor the names of its 15 * contributors may be used to endorse or promote products derived 16 * from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 22 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 #include <sys/cdefs.h> 31 __RCSID("$NetBSD: list.c,v 1.4 2025/12/16 12:03:39 nia Exp $"); 32 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 37 #include "list.h" 38 #include "msg.h" 39 40 /** 41 * @brief test for linear white space 42 * @param ch character to test 43 * @return true if LWS, false otherwise. 44 */ 45 static int 46 is_LWS(const char ch) 47 { 48 49 return ch == '\n' || ch == ' ' || ch == '\t'; 50 } 51 52 /** 53 * @brief skip leading whitespace (does not modify string) 54 * @param p start of string 55 * @return pointer to string after leading whitespace 56 */ 57 static const char * 58 skip_LWS(const char *p) 59 { 60 61 while (*p != '\0' && is_LWS((unsigned char)*p)) 62 p++; 63 return p; 64 } 65 66 /** 67 * @brief remove trailing whitespace from a string (does not modify 68 * string) 69 * @param p pointer to start of trailing whitespace 70 * @param s pointer to start of string 71 * @return pointer to new end of string 72 */ 73 static const char * 74 strip_LWS(const char *p, const char *s) 75 { 76 77 if (p <= s) 78 return s; 79 while (p >= s && is_LWS((unsigned char)*p)) 80 p--; 81 return p + 1; 82 } 83 84 /** 85 * @brief find the next element in a comma delimited list 86 * @param p pointer to list string 87 * @return pointer to start of next element in list 88 */ 89 static const char * 90 next_element(const char *p) 91 { 92 int quoting; 93 int escaped; 94 95 quoting = 0; 96 escaped = 0; 97 for (; *p != '\0'; p++) { 98 switch (*p) { 99 case ',': 100 if (!quoting && !escaped) 101 return p + 1; 102 escaped = 0; 103 break; 104 case '"': 105 if (!escaped) 106 quoting = !quoting; 107 escaped = 0; 108 break; 109 case '\\': 110 escaped = !escaped; 111 break; 112 default: 113 escaped = 0; 114 break; 115 } 116 } 117 return p; 118 } 119 120 /** 121 * @brief allocate a list_t node, the memory for its value, and save 122 * the value. 123 * @param b buffer containing data to save in node value 124 * @param len length of data to save 125 * @return the list_t node, or NULL on failure. 126 */ 127 static list_t * 128 alloc_list(const char *b, size_t len) 129 { 130 list_t *l; 131 132 if ((l = malloc(sizeof(*l))) == NULL) 133 return NULL; 134 if ((l->value = malloc(len + 1)) == NULL) { 135 free(l); 136 return NULL; 137 } 138 memcpy(l->value, b, len); 139 l->value[len] = '\0'; 140 l->next = NULL; 141 return l; 142 } 143 144 /** 145 * @brief free an allocated list 146 * @param l the list 147 */ 148 void 149 saslc__list_free(list_t *l) 150 { 151 list_t *n; 152 153 for (/*EMPTY*/; l != NULL; l = n) { 154 n = l->next; 155 free(l->value); 156 free(l); 157 } 158 } 159 160 /** 161 * @brief Parse a list of the following format: 162 * ( *LWS element *( *LWS "," *LWS element )) 163 * @param lp pointer to list_t type for returned list. Cannot be NULL. 164 * @param p string to parse 165 * @return 0 on success, -1 on error (no memory). 166 * 167 * Note: the list is allocated. Use saslc__list_free() to free it. 168 */ 169 int 170 saslc__list_parse(list_t **lp, const char *p) 171 { 172 const char *e, *n; 173 list_t *l, *t, **tp; 174 175 l = NULL; 176 tp = NULL; 177 n = p; 178 for (;;) { 179 p = n; 180 p = skip_LWS(p); 181 if (*p == '\0') 182 break; 183 n = next_element(p); 184 e = n > p && n[-1] == ',' ? n - 1 : n; 185 e = strip_LWS(e - 1, p); 186 if (e <= p) 187 continue; 188 t = alloc_list(p, (size_t)(e - p)); 189 if (t == NULL) { 190 saslc__list_free(l); 191 return -1; 192 } 193 if (tp != NULL) 194 *tp = t; 195 else 196 l = t; 197 tp = &t->next; 198 } 199 *lp = l; 200 return 0; 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