1 /*
2  * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7  *
8  * See the COPYRIGHT file distributed with this work for additional
9  * information regarding copyright ownership.
10  *
11  * Portions Copyright (C) 2001 Nominum, Inc.
12  *
13  * Permission to use, copy, modify, and/or distribute this software for any
14  * purpose with or without fee is hereby granted, provided that the above
15  * copyright notice and this permission notice appear in all copies.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NOMINUM DISCLAIMS ALL
18  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY
20  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
21  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
22  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
23  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24  */
25 
26 
27 /*! \file */
28 
29 #include <config.h>
30 
31 #include <stdbool.h>
32 #include <stdlib.h>
33 #include <string.h>
34 
35 #include <isccc/alist.h>
36 #include <isc/assertions.h>
37 #include <isc/print.h>
38 #include <isccc/result.h>
39 #include <isccc/sexpr.h>
40 #include <isccc/util.h>
41 
42 #define CAR(s)			(s)->value.as_dottedpair.car
43 #define CDR(s)			(s)->value.as_dottedpair.cdr
44 
45 #define ALIST_TAG		"*alist*"
46 #define MAX_INDENT		64
47 
48 static char spaces[MAX_INDENT + 1] =
49 	"                                                                ";
50 
51 isccc_sexpr_t *
isccc_alist_create(void)52 isccc_alist_create(void)
53 {
54 	isccc_sexpr_t *alist, *tag;
55 
56 	tag = isccc_sexpr_fromstring(ALIST_TAG);
57 	if (tag == NULL)
58 		return (NULL);
59 	alist = isccc_sexpr_cons(tag, NULL);
60 	if (alist == NULL) {
61 		isccc_sexpr_free(&tag);
62 		return (NULL);
63 	}
64 
65 	return (alist);
66 }
67 
68 bool
isccc_alist_alistp(isccc_sexpr_t * alist)69 isccc_alist_alistp(isccc_sexpr_t *alist)
70 {
71 	isccc_sexpr_t *car;
72 
73 	if (alist == NULL || alist->type != ISCCC_SEXPRTYPE_DOTTEDPAIR)
74 		return (false);
75 	car = CAR(alist);
76 	if (car == NULL || car->type != ISCCC_SEXPRTYPE_STRING)
77 		return (false);
78 	if (strcmp(car->value.as_string, ALIST_TAG) != 0)
79 		return (false);
80 	return (true);
81 }
82 
83 bool
isccc_alist_emptyp(isccc_sexpr_t * alist)84 isccc_alist_emptyp(isccc_sexpr_t *alist)
85 {
86 	REQUIRE(isccc_alist_alistp(alist));
87 
88 	if (CDR(alist) == NULL)
89 		return (true);
90 	return (false);
91 }
92 
93 isccc_sexpr_t *
isccc_alist_first(isccc_sexpr_t * alist)94 isccc_alist_first(isccc_sexpr_t *alist)
95 {
96 	REQUIRE(isccc_alist_alistp(alist));
97 
98 	return (CDR(alist));
99 }
100 
101 isccc_sexpr_t *
isccc_alist_assq(isccc_sexpr_t * alist,const char * key)102 isccc_alist_assq(isccc_sexpr_t *alist, const char *key)
103 {
104 	isccc_sexpr_t *car, *caar;
105 
106 	REQUIRE(isccc_alist_alistp(alist));
107 
108 	/*
109 	 * Skip alist type tag.
110 	 */
111 	alist = CDR(alist);
112 
113 	while (alist != NULL) {
114 		INSIST(alist->type == ISCCC_SEXPRTYPE_DOTTEDPAIR);
115 		car = CAR(alist);
116 		INSIST(car->type == ISCCC_SEXPRTYPE_DOTTEDPAIR);
117 		caar = CAR(car);
118 		if (caar->type == ISCCC_SEXPRTYPE_STRING &&
119 		    strcmp(caar->value.as_string, key) == 0)
120 			return (car);
121 		alist = CDR(alist);
122 	}
123 
124 	return (NULL);
125 }
126 
127 void
isccc_alist_delete(isccc_sexpr_t * alist,const char * key)128 isccc_alist_delete(isccc_sexpr_t *alist, const char *key)
129 {
130 	isccc_sexpr_t *car, *caar, *rest, *prev;
131 
132 	REQUIRE(isccc_alist_alistp(alist));
133 
134 	prev = alist;
135 	rest = CDR(alist);
136 	while (rest != NULL) {
137 		INSIST(rest->type == ISCCC_SEXPRTYPE_DOTTEDPAIR);
138 		car = CAR(rest);
139 		INSIST(car != NULL && car->type == ISCCC_SEXPRTYPE_DOTTEDPAIR);
140 		/* cppcheck-suppress nullPointerRedundantCheck */
141 		caar = CAR(car);
142 		/* cppcheck-suppress nullPointerRedundantCheck */
143 		if (caar->type == ISCCC_SEXPRTYPE_STRING &&
144 		    strcmp(caar->value.as_string, key) == 0) {
145 			CDR(prev) = CDR(rest);
146 			CDR(rest) = NULL;
147 			isccc_sexpr_free(&rest);
148 			break;
149 		}
150 		prev = rest;
151 		rest = CDR(rest);
152 	}
153 }
154 
155 isccc_sexpr_t *
isccc_alist_define(isccc_sexpr_t * alist,const char * key,isccc_sexpr_t * value)156 isccc_alist_define(isccc_sexpr_t *alist, const char *key, isccc_sexpr_t *value)
157 {
158 	isccc_sexpr_t *kv, *k, *elt;
159 
160 	kv = isccc_alist_assq(alist, key);
161 	if (kv == NULL) {
162 		/*
163 		 * New association.
164 		 */
165 		k = isccc_sexpr_fromstring(key);
166 		if (k == NULL)
167 			return (NULL);
168 		kv = isccc_sexpr_cons(k, value);
169 		if (kv == NULL) {
170 			isccc_sexpr_free(&kv);
171 			return (NULL);
172 		}
173 		elt = isccc_sexpr_addtolist(&alist, kv);
174 		if (elt == NULL) {
175 			isccc_sexpr_free(&kv);
176 			return (NULL);
177 		}
178 	} else {
179 		/*
180 		 * We've already got an entry for this key.  Replace it.
181 		 */
182 		isccc_sexpr_free(&CDR(kv));
183 		CDR(kv) = value;
184 	}
185 
186 	return (kv);
187 }
188 
189 isccc_sexpr_t *
isccc_alist_definestring(isccc_sexpr_t * alist,const char * key,const char * str)190 isccc_alist_definestring(isccc_sexpr_t *alist, const char *key, const char *str)
191 {
192 	isccc_sexpr_t *v, *kv;
193 
194 	v = isccc_sexpr_fromstring(str);
195 	if (v == NULL)
196 		return (NULL);
197 	kv = isccc_alist_define(alist, key, v);
198 	if (kv == NULL)
199 		isccc_sexpr_free(&v);
200 
201 	return (kv);
202 }
203 
204 isccc_sexpr_t *
isccc_alist_definebinary(isccc_sexpr_t * alist,const char * key,isccc_region_t * r)205 isccc_alist_definebinary(isccc_sexpr_t *alist, const char *key, isccc_region_t *r)
206 {
207 	isccc_sexpr_t *v, *kv;
208 
209 	v = isccc_sexpr_frombinary(r);
210 	if (v == NULL)
211 		return (NULL);
212 	kv = isccc_alist_define(alist, key, v);
213 	if (kv == NULL)
214 		isccc_sexpr_free(&v);
215 
216 	return (kv);
217 }
218 
219 isccc_sexpr_t *
isccc_alist_lookup(isccc_sexpr_t * alist,const char * key)220 isccc_alist_lookup(isccc_sexpr_t *alist, const char *key)
221 {
222 	isccc_sexpr_t *kv;
223 
224 	kv = isccc_alist_assq(alist, key);
225 	if (kv != NULL)
226 		return (CDR(kv));
227 	return (NULL);
228 }
229 
230 isc_result_t
isccc_alist_lookupstring(isccc_sexpr_t * alist,const char * key,char ** strp)231 isccc_alist_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp)
232 {
233 	isccc_sexpr_t *kv, *v;
234 
235 	kv = isccc_alist_assq(alist, key);
236 	if (kv != NULL) {
237 		v = CDR(kv);
238 		if (isccc_sexpr_stringp(v)) {
239 			if (strp != NULL)
240 				*strp = isccc_sexpr_tostring(v);
241 			return (ISC_R_SUCCESS);
242 		} else
243 			return (ISC_R_EXISTS);
244 	}
245 
246 	return (ISC_R_NOTFOUND);
247 }
248 
249 isc_result_t
isccc_alist_lookupbinary(isccc_sexpr_t * alist,const char * key,isccc_region_t ** r)250 isccc_alist_lookupbinary(isccc_sexpr_t *alist, const char *key, isccc_region_t **r)
251 {
252 	isccc_sexpr_t *kv, *v;
253 
254 	kv = isccc_alist_assq(alist, key);
255 	if (kv != NULL) {
256 		v = CDR(kv);
257 		if (isccc_sexpr_binaryp(v)) {
258 			if (r != NULL)
259 				*r = isccc_sexpr_tobinary(v);
260 			return (ISC_R_SUCCESS);
261 		} else
262 			return (ISC_R_EXISTS);
263 	}
264 
265 	return (ISC_R_NOTFOUND);
266 }
267 
268 void
isccc_alist_prettyprint(isccc_sexpr_t * sexpr,unsigned int indent,FILE * stream)269 isccc_alist_prettyprint(isccc_sexpr_t *sexpr, unsigned int indent, FILE *stream)
270 {
271 	isccc_sexpr_t *elt, *kv, *k, *v;
272 
273 	if (isccc_alist_alistp(sexpr)) {
274 		fprintf(stream, "{\n");
275 		indent += 4;
276 		for (elt = isccc_alist_first(sexpr);
277 		     elt != NULL;
278 		     elt = CDR(elt)) {
279 			kv = CAR(elt);
280 			INSIST(isccc_sexpr_listp(kv));
281 			k = CAR(kv);
282 			v = CDR(kv);
283 			INSIST(isccc_sexpr_stringp(k));
284 			fprintf(stream, "%.*s%s => ", (int)indent, spaces,
285 				isccc_sexpr_tostring(k));
286 			isccc_alist_prettyprint(v, indent, stream);
287 			if (CDR(elt) != NULL)
288 				fprintf(stream, ",");
289 			fprintf(stream, "\n");
290 		}
291 		indent -= 4;
292 		fprintf(stream, "%.*s}", (int)indent, spaces);
293 	} else if (isccc_sexpr_listp(sexpr)) {
294 		fprintf(stream, "(\n");
295 		indent += 4;
296 		for (elt = sexpr;
297 		     elt != NULL;
298 		     elt = CDR(elt)) {
299 			fprintf(stream, "%.*s", (int)indent, spaces);
300 			isccc_alist_prettyprint(CAR(elt), indent, stream);
301 			if (CDR(elt) != NULL)
302 				fprintf(stream, ",");
303 			fprintf(stream, "\n");
304 		}
305 		indent -= 4;
306 		fprintf(stream, "%.*s)", (int)indent, spaces);
307 	} else
308 		isccc_sexpr_print(sexpr, stream);
309 }
310