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