Home | History | Annotate | Line # | Download | only in src
      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