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 http://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 /*! \file */
27 
28 #include <stdbool.h>
29 #include <stdlib.h>
30 #include <string.h>
31 
32 #include <isc/assertions.h>
33 #include <isc/print.h>
34 
35 #include <isccc/alist.h>
36 #include <isccc/result.h>
37 #include <isccc/sexpr.h>
38 #include <isccc/util.h>
39 
40 #define CAR(s) (s)->value.as_dottedpair.car
41 #define CDR(s) (s)->value.as_dottedpair.cdr
42 
43 #define ALIST_TAG  "*alist*"
44 #define MAX_INDENT 64
45 
46 static char spaces[MAX_INDENT + 1] = "                                         "
47 				     "                       ";
48 
49 isccc_sexpr_t *
isccc_alist_create(void)50 isccc_alist_create(void) {
51 	isccc_sexpr_t *alist, *tag;
52 
53 	tag = isccc_sexpr_fromstring(ALIST_TAG);
54 	if (tag == NULL) {
55 		return (NULL);
56 	}
57 	alist = isccc_sexpr_cons(tag, NULL);
58 	if (alist == NULL) {
59 		isccc_sexpr_free(&tag);
60 		return (NULL);
61 	}
62 
63 	return (alist);
64 }
65 
66 bool
isccc_alist_alistp(isccc_sexpr_t * alist)67 isccc_alist_alistp(isccc_sexpr_t *alist) {
68 	isccc_sexpr_t *car;
69 
70 	if (alist == NULL || alist->type != ISCCC_SEXPRTYPE_DOTTEDPAIR) {
71 		return (false);
72 	}
73 	car = CAR(alist);
74 	if (car == NULL || car->type != ISCCC_SEXPRTYPE_STRING) {
75 		return (false);
76 	}
77 	if (strcmp(car->value.as_string, ALIST_TAG) != 0) {
78 		return (false);
79 	}
80 	return (true);
81 }
82 
83 bool
isccc_alist_emptyp(isccc_sexpr_t * alist)84 isccc_alist_emptyp(isccc_sexpr_t *alist) {
85 	REQUIRE(isccc_alist_alistp(alist));
86 
87 	if (CDR(alist) == NULL) {
88 		return (true);
89 	}
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 	REQUIRE(isccc_alist_alistp(alist));
96 
97 	return (CDR(alist));
98 }
99 
100 isccc_sexpr_t *
isccc_alist_assq(isccc_sexpr_t * alist,const char * key)101 isccc_alist_assq(isccc_sexpr_t *alist, const char *key) {
102 	isccc_sexpr_t *car, *caar;
103 
104 	REQUIRE(isccc_alist_alistp(alist));
105 
106 	/*
107 	 * Skip alist type tag.
108 	 */
109 	alist = CDR(alist);
110 
111 	while (alist != NULL) {
112 		INSIST(alist->type == ISCCC_SEXPRTYPE_DOTTEDPAIR);
113 		car = CAR(alist);
114 		INSIST(car->type == ISCCC_SEXPRTYPE_DOTTEDPAIR);
115 		caar = CAR(car);
116 		if (caar->type == ISCCC_SEXPRTYPE_STRING &&
117 		    strcmp(caar->value.as_string, key) == 0)
118 		{
119 			return (car);
120 		}
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 	isccc_sexpr_t *car, *caar, *rest, *prev;
130 
131 	REQUIRE(isccc_alist_alistp(alist));
132 
133 	prev = alist;
134 	rest = CDR(alist);
135 	while (rest != NULL) {
136 		INSIST(rest->type == ISCCC_SEXPRTYPE_DOTTEDPAIR);
137 		car = CAR(rest);
138 		INSIST(car != NULL && car->type == ISCCC_SEXPRTYPE_DOTTEDPAIR);
139 		caar = CAR(car);
140 		if (caar->type == ISCCC_SEXPRTYPE_STRING &&
141 		    strcmp(caar->value.as_string, key) == 0)
142 		{
143 			CDR(prev) = CDR(rest);
144 			CDR(rest) = NULL;
145 			isccc_sexpr_free(&rest);
146 			break;
147 		}
148 		prev = rest;
149 		rest = CDR(rest);
150 	}
151 }
152 
153 isccc_sexpr_t *
isccc_alist_define(isccc_sexpr_t * alist,const char * key,isccc_sexpr_t * value)154 isccc_alist_define(isccc_sexpr_t *alist, const char *key,
155 		   isccc_sexpr_t *value) {
156 	isccc_sexpr_t *kv, *k, *elt;
157 
158 	kv = isccc_alist_assq(alist, key);
159 	if (kv == NULL) {
160 		/*
161 		 * New association.
162 		 */
163 		k = isccc_sexpr_fromstring(key);
164 		if (k == NULL) {
165 			return (NULL);
166 		}
167 		kv = isccc_sexpr_cons(k, value);
168 		if (kv == NULL) {
169 			isccc_sexpr_free(&kv);
170 			return (NULL);
171 		}
172 		elt = isccc_sexpr_addtolist(&alist, kv);
173 		if (elt == NULL) {
174 			isccc_sexpr_free(&kv);
175 			return (NULL);
176 		}
177 	} else {
178 		/*
179 		 * We've already got an entry for this key.  Replace it.
180 		 */
181 		isccc_sexpr_free(&CDR(kv));
182 		CDR(kv) = value;
183 	}
184 
185 	return (kv);
186 }
187 
188 isccc_sexpr_t *
isccc_alist_definestring(isccc_sexpr_t * alist,const char * key,const char * str)189 isccc_alist_definestring(isccc_sexpr_t *alist, const char *key,
190 			 const char *str) {
191 	isccc_sexpr_t *v, *kv;
192 
193 	v = isccc_sexpr_fromstring(str);
194 	if (v == NULL) {
195 		return (NULL);
196 	}
197 	kv = isccc_alist_define(alist, key, v);
198 	if (kv == NULL) {
199 		isccc_sexpr_free(&v);
200 	}
201 
202 	return (kv);
203 }
204 
205 isccc_sexpr_t *
isccc_alist_definebinary(isccc_sexpr_t * alist,const char * key,isccc_region_t * r)206 isccc_alist_definebinary(isccc_sexpr_t *alist, const char *key,
207 			 isccc_region_t *r) {
208 	isccc_sexpr_t *v, *kv;
209 
210 	v = isccc_sexpr_frombinary(r);
211 	if (v == NULL) {
212 		return (NULL);
213 	}
214 	kv = isccc_alist_define(alist, key, v);
215 	if (kv == NULL) {
216 		isccc_sexpr_free(&v);
217 	}
218 
219 	return (kv);
220 }
221 
222 isccc_sexpr_t *
isccc_alist_lookup(isccc_sexpr_t * alist,const char * key)223 isccc_alist_lookup(isccc_sexpr_t *alist, const char *key) {
224 	isccc_sexpr_t *kv;
225 
226 	kv = isccc_alist_assq(alist, key);
227 	if (kv != NULL) {
228 		return (CDR(kv));
229 	}
230 	return (NULL);
231 }
232 
233 isc_result_t
isccc_alist_lookupstring(isccc_sexpr_t * alist,const char * key,char ** strp)234 isccc_alist_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp) {
235 	isccc_sexpr_t *kv, *v;
236 
237 	kv = isccc_alist_assq(alist, key);
238 	if (kv != NULL) {
239 		v = CDR(kv);
240 		if (isccc_sexpr_stringp(v)) {
241 			if (strp != NULL) {
242 				*strp = isccc_sexpr_tostring(v);
243 			}
244 			return (ISC_R_SUCCESS);
245 		} else {
246 			return (ISC_R_EXISTS);
247 		}
248 	}
249 
250 	return (ISC_R_NOTFOUND);
251 }
252 
253 isc_result_t
isccc_alist_lookupbinary(isccc_sexpr_t * alist,const char * key,isccc_region_t ** r)254 isccc_alist_lookupbinary(isccc_sexpr_t *alist, const char *key,
255 			 isccc_region_t **r) {
256 	isccc_sexpr_t *kv, *v;
257 
258 	kv = isccc_alist_assq(alist, key);
259 	if (kv != NULL) {
260 		v = CDR(kv);
261 		if (isccc_sexpr_binaryp(v)) {
262 			if (r != NULL) {
263 				*r = isccc_sexpr_tobinary(v);
264 			}
265 			return (ISC_R_SUCCESS);
266 		} else {
267 			return (ISC_R_EXISTS);
268 		}
269 	}
270 
271 	return (ISC_R_NOTFOUND);
272 }
273 
274 void
isccc_alist_prettyprint(isccc_sexpr_t * sexpr,unsigned int indent,FILE * stream)275 isccc_alist_prettyprint(isccc_sexpr_t *sexpr, unsigned int indent,
276 			FILE *stream) {
277 	isccc_sexpr_t *elt, *kv, *k, *v;
278 
279 	if (isccc_alist_alistp(sexpr)) {
280 		fprintf(stream, "{\n");
281 		indent += 4;
282 		for (elt = isccc_alist_first(sexpr); elt != NULL;
283 		     elt = CDR(elt)) {
284 			kv = CAR(elt);
285 			INSIST(isccc_sexpr_listp(kv));
286 			k = CAR(kv);
287 			v = CDR(kv);
288 			INSIST(isccc_sexpr_stringp(k));
289 			fprintf(stream, "%.*s%s => ", (int)indent, spaces,
290 				isccc_sexpr_tostring(k));
291 			isccc_alist_prettyprint(v, indent, stream);
292 			if (CDR(elt) != NULL) {
293 				fprintf(stream, ",");
294 			}
295 			fprintf(stream, "\n");
296 		}
297 		indent -= 4;
298 		fprintf(stream, "%.*s}", (int)indent, spaces);
299 	} else if (isccc_sexpr_listp(sexpr)) {
300 		fprintf(stream, "(\n");
301 		indent += 4;
302 		for (elt = sexpr; elt != NULL; elt = CDR(elt)) {
303 			fprintf(stream, "%.*s", (int)indent, spaces);
304 			isccc_alist_prettyprint(CAR(elt), indent, stream);
305 			if (CDR(elt) != NULL) {
306 				fprintf(stream, ",");
307 			}
308 			fprintf(stream, "\n");
309 		}
310 		indent -= 4;
311 		fprintf(stream, "%.*s)", (int)indent, spaces);
312 	} else {
313 		isccc_sexpr_print(sexpr, stream);
314 	}
315 }
316