xref: /netbsd-src/crypto/external/bsd/libsaslc/dist/src/list.c (revision 09484ebb416ab960674c4302bf9f696a73c945f4)
1*09484ebbSchristos /* $NetBSD: list.c,v 1.3 2011/02/20 01:59:46 christos Exp $ */
219c14409Schristos 
319c14409Schristos /* Copyright (c) 2010 The NetBSD Foundation, Inc.
419c14409Schristos  * All rights reserved.
519c14409Schristos  *
619c14409Schristos  * Redistribution and use in source and binary forms, with or without
719c14409Schristos  * modification, are permitted provided that the following conditions
819c14409Schristos  * are met:
919c14409Schristos  * 1. Redistributions of source code must retain the above copyright
1019c14409Schristos  *    notice, this list of conditions and the following disclaimer.
1119c14409Schristos  * 2. Redistributions in binary form must reproduce the above copyright
1219c14409Schristos  *    notice, this list of conditions and the following disclaimer in the
1319c14409Schristos  *    documentation and/or other materials provided with the distribution.
1419c14409Schristos  * 3. All advertising materials mentioning features or use of this software
1519c14409Schristos  *    must display the following acknowledgement:
1619c14409Schristos  *        This product includes software developed by the NetBSD
1719c14409Schristos  *        Foundation, Inc. and its contributors.
1819c14409Schristos  * 4. Neither the name of The NetBSD Foundation nor the names of its
1919c14409Schristos  *    contributors may be used to endorse or promote products derived
2019c14409Schristos  *    from this software without specific prior written permission.
2119c14409Schristos  *
2219c14409Schristos  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2319c14409Schristos  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2419c14409Schristos  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2519c14409Schristos  * PURPOSE ARE DISCLAIMED.	IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2619c14409Schristos  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2719c14409Schristos  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2819c14409Schristos  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2919c14409Schristos  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3019c14409Schristos  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3119c14409Schristos  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3219c14409Schristos  * POSSIBILITY OF SUCH DAMAGE.
3319c14409Schristos  */
3419c14409Schristos #include <sys/cdefs.h>
35*09484ebbSchristos __RCSID("$NetBSD: list.c,v 1.3 2011/02/20 01:59:46 christos Exp $");
3619c14409Schristos 
3719c14409Schristos #include <stdio.h>
38beea8b97Schristos #include <stdlib.h>
3919c14409Schristos #include <string.h>
4019c14409Schristos 
4119c14409Schristos #include "list.h"
4219c14409Schristos #include "msg.h"
4319c14409Schristos 
4419c14409Schristos /**
4519c14409Schristos  * @brief test for linear white space
4619c14409Schristos  * @param ch character to test
4719c14409Schristos  * @return true if LWS, false otherwise.
4819c14409Schristos  */
4919c14409Schristos static int
is_LWS(const char ch)5019c14409Schristos is_LWS(const char ch)
5119c14409Schristos {
5219c14409Schristos 
5319c14409Schristos 	return ch == '\n' || ch == ' ' || ch == '\t';
5419c14409Schristos }
5519c14409Schristos 
5619c14409Schristos /**
5719c14409Schristos  * @brief skip leading whitespace (does not modify string)
5819c14409Schristos  * @param p start of string
5919c14409Schristos  * @return pointer to string after leading whitespace
6019c14409Schristos  */
6119c14409Schristos static const char *
skip_LWS(const char * p)6219c14409Schristos skip_LWS(const char *p)
6319c14409Schristos {
6419c14409Schristos 
6519c14409Schristos 	while (*p != '\0' && is_LWS((unsigned char)*p))
6619c14409Schristos 		p++;
6719c14409Schristos 	return p;
6819c14409Schristos }
6919c14409Schristos 
7019c14409Schristos /**
7119c14409Schristos  * @brief remove trailing whitespace from a string (does not modify
7219c14409Schristos  * string)
7319c14409Schristos  * @param p pointer to start of trailing whitespace
7419c14409Schristos  * @param s pointer to start of string
7519c14409Schristos  * @return pointer to new end of string
7619c14409Schristos  */
7719c14409Schristos static const char *
strip_LWS(const char * p,const char * s)7819c14409Schristos strip_LWS(const char *p, const char *s)
7919c14409Schristos {
8019c14409Schristos 
8119c14409Schristos 	if (p <= s)
8219c14409Schristos 		return s;
8319c14409Schristos 	while (p >= s && is_LWS((unsigned char)*p))
8419c14409Schristos 		p--;
8519c14409Schristos 	return p + 1;
8619c14409Schristos }
8719c14409Schristos 
8819c14409Schristos /**
8919c14409Schristos  * @brief find the next element in a comma delimited list
9019c14409Schristos  * @param p pointer to list string
9119c14409Schristos  * @return pointer to start of next element in list
9219c14409Schristos  */
9319c14409Schristos static const char *
next_element(const char * p)9419c14409Schristos next_element(const char *p)
9519c14409Schristos {
9619c14409Schristos 	int quoting;
9719c14409Schristos 	int escaped;
9819c14409Schristos 
9919c14409Schristos 	quoting = 0;
10019c14409Schristos 	escaped = 0;
10119c14409Schristos 	for (; *p != '\0'; p++) {
10219c14409Schristos 		switch (*p) {
10319c14409Schristos 		case ',':
10419c14409Schristos 			if (!quoting && !escaped)
10519c14409Schristos 				return p + 1;
10619c14409Schristos 			escaped = 0;
10719c14409Schristos 			break;
10819c14409Schristos 		case '"':
10919c14409Schristos 			if (!escaped)
11019c14409Schristos 				quoting = !quoting;
11119c14409Schristos 			escaped = 0;
11219c14409Schristos 			break;
11319c14409Schristos 		case '\\':
11419c14409Schristos 			escaped = !escaped;
11519c14409Schristos 			break;
11619c14409Schristos 		default:
11719c14409Schristos 			escaped = 0;
11819c14409Schristos 			break;
11919c14409Schristos 		}
12019c14409Schristos 	}
12119c14409Schristos 	return p;
12219c14409Schristos }
12319c14409Schristos 
12419c14409Schristos /**
12519c14409Schristos  * @brief allocate a list_t node, the memory for its value, and save
12619c14409Schristos  * the value.
12719c14409Schristos  * @param b buffer containing data to save in node value
12819c14409Schristos  * @param len length of data to save
12919c14409Schristos  * @return the list_t node, or NULL on failure.
13019c14409Schristos  */
13119c14409Schristos static list_t *
alloc_list(const char * b,size_t len)13219c14409Schristos alloc_list(const char *b, size_t len)
13319c14409Schristos {
13419c14409Schristos 	list_t *l;
13519c14409Schristos 
13619c14409Schristos 	if ((l = malloc(sizeof(*l))) == NULL)
13719c14409Schristos 		return NULL;
13819c14409Schristos 	if ((l->value = malloc(len + 1)) == NULL) {
13919c14409Schristos 		free(l);
14019c14409Schristos 		return NULL;
14119c14409Schristos 	}
14219c14409Schristos 	memcpy(l->value, b, len);
14319c14409Schristos 	l->value[len]  = '\0';
14419c14409Schristos 	l->next = NULL;
14519c14409Schristos 	return l;
14619c14409Schristos }
14719c14409Schristos 
14819c14409Schristos /**
14919c14409Schristos  * @brief free an allocated list
15019c14409Schristos  * @param l the list
15119c14409Schristos  */
15219c14409Schristos void
saslc__list_free(list_t * l)15319c14409Schristos saslc__list_free(list_t *l)
15419c14409Schristos {
15519c14409Schristos 	list_t *n;
15619c14409Schristos 
157beea8b97Schristos 	for (/*EMPTY*/; l != NULL; l = n) {
15819c14409Schristos 		n = l->next;
15919c14409Schristos 		free(l->value);
16019c14409Schristos 		free(l);
16119c14409Schristos 	}
16219c14409Schristos }
16319c14409Schristos 
16419c14409Schristos /**
16519c14409Schristos  * @brief Parse a list of the following format:
16619c14409Schristos  *   ( *LWS element *( *LWS "," *LWS element ))
167*09484ebbSchristos  * @param lp pointer to list_t type for returned list.  Cannot be NULL.
16819c14409Schristos  * @param p string to parse
169*09484ebbSchristos  * @return 0 on success, -1 on error (no memory).
170*09484ebbSchristos  *
171*09484ebbSchristos  * Note: the list is allocated.  Use saslc__list_free() to free it.
17219c14409Schristos  */
173*09484ebbSchristos int
saslc__list_parse(list_t ** lp,const char * p)174*09484ebbSchristos saslc__list_parse(list_t **lp, const char *p)
17519c14409Schristos {
17619c14409Schristos 	const char *e, *n;
17719c14409Schristos 	list_t *l, *t, **tp;
17819c14409Schristos 
17919c14409Schristos 	l = NULL;
18019c14409Schristos 	tp = NULL;
18119c14409Schristos 	n = p;
18219c14409Schristos 	for (;;) {
18319c14409Schristos 		p = n;
18419c14409Schristos 		p = skip_LWS(p);
18519c14409Schristos 		if (*p == '\0')
18619c14409Schristos 			break;
18719c14409Schristos 		n = next_element(p);
18819c14409Schristos 		e = n > p && n[-1] == ',' ? n - 1 : n;
18919c14409Schristos 		e = strip_LWS(e - 1, p);
19019c14409Schristos 		if (e <= p)
19119c14409Schristos 			continue;
19219c14409Schristos 		t = alloc_list(p, (size_t)(e - p));
19319c14409Schristos 		if (t == NULL) {
19419c14409Schristos 			saslc__list_free(l);
195*09484ebbSchristos 			return -1;
19619c14409Schristos 		}
19719c14409Schristos 		if (tp != NULL)
19819c14409Schristos 			*tp = t;
19919c14409Schristos 		else
20019c14409Schristos 			l = t;
20119c14409Schristos 		tp = &t->next;
20219c14409Schristos 	}
203*09484ebbSchristos 	*lp = l;
204*09484ebbSchristos 	return 0;
20519c14409Schristos }
20619c14409Schristos 
20719c14409Schristos /**
20819c14409Schristos  * @brief allocate a new list node for a string and append it to a
20919c14409Schristos  * list
21019c14409Schristos  * @param l the list to append
21119c14409Schristos  * @param p the string
21219c14409Schristos  */
21319c14409Schristos int
saslc__list_append(list_t ** l,const char * p)21419c14409Schristos saslc__list_append(list_t **l, const char *p)
21519c14409Schristos {
21619c14409Schristos 	list_t *n, *e;
21719c14409Schristos 
21819c14409Schristos 	e = NULL;
21919c14409Schristos 	for (n = *l; n != NULL; n = n->next)
22019c14409Schristos 		e = n;
22119c14409Schristos 
22219c14409Schristos 	n = alloc_list(p, strlen(p));
22319c14409Schristos 	if (n == NULL)
22419c14409Schristos 		return -1;
22519c14409Schristos 
22619c14409Schristos 	if (e == NULL)
22719c14409Schristos 		*l = n;
22819c14409Schristos 	else
22919c14409Schristos 		e->next = n;
23019c14409Schristos 
23119c14409Schristos 	return 0;
23219c14409Schristos }
23319c14409Schristos 
23419c14409Schristos /**
23519c14409Schristos  * @brief get the flags corresponding to a given name.
23619c14409Schristos  * @param key the key to find the flags for.
23719c14409Schristos  * @param tbl the NULL terminated named_flag_t table to use for the
23819c14409Schristos  * lookup.
23919c14409Schristos  * @return the flags if found or zero if not.
24019c14409Schristos  */
24119c14409Schristos static unsigned int
get_named_flag(const char * key,const named_flag_t * tbl)24219c14409Schristos get_named_flag(const char *key, const named_flag_t *tbl)
24319c14409Schristos {
24419c14409Schristos 	const named_flag_t *p;
24519c14409Schristos 
24619c14409Schristos 	for (p = tbl; p->name != NULL; p++) {
24719c14409Schristos 		if (strcasecmp(key, p->name) == 0)
24819c14409Schristos 			return p->flag;
24919c14409Schristos 	}
25019c14409Schristos 	return 0;
25119c14409Schristos }
25219c14409Schristos 
25319c14409Schristos /**
25419c14409Schristos  * @brief collect all the flags from a list of flag names.
25519c14409Schristos  * @param list the list
25619c14409Schristos  * @param tbl the NULL terminated named_flag_t table to use for the
25719c14409Schristos  * lookups
25819c14409Schristos  * @return the or-ed flags of all the matches
25919c14409Schristos  */
26019c14409Schristos uint32_t
saslc__list_flags(list_t * list,const named_flag_t * tbl)26119c14409Schristos saslc__list_flags(list_t *list, const named_flag_t *tbl)
26219c14409Schristos {
26319c14409Schristos 	list_t *l;
26419c14409Schristos 	uint32_t flags;
26519c14409Schristos 
26619c14409Schristos 	flags = 0;
26719c14409Schristos 	for (l = list; l != NULL; l = l->next)
26819c14409Schristos 		flags |= get_named_flag(l->value, tbl);
26919c14409Schristos 
27019c14409Schristos 	return flags;
27119c14409Schristos }
27219c14409Schristos 
27319c14409Schristos /**
27419c14409Schristos  * @brief print all the values in a list if debugging is enabled
27519c14409Schristos  * @param list the list
27619c14409Schristos  * @param str a string to print before the results.
27719c14409Schristos  *
27819c14409Schristos  * XXX: move this to msg.c?
27919c14409Schristos  */
28019c14409Schristos void
saslc__list_log(list_t * list,const char * str)28119c14409Schristos saslc__list_log(list_t *list, const char *str)
28219c14409Schristos {
28319c14409Schristos 	list_t *l;
28419c14409Schristos 
28519c14409Schristos 	if (!saslc_debug)
28619c14409Schristos 		return;
28719c14409Schristos 
28819c14409Schristos 	saslc__msg_dbg("%s", str);
28919c14409Schristos 	for (l = list; l != NULL; l = l->next)
29019c14409Schristos 		saslc__msg_dbg("  value: '%s'\n",
29119c14409Schristos 		    l->value ? l->value : "<null>");
29219c14409Schristos }
293