1 /* $NetBSD: sexpr.c,v 1.7 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 <ctype.h> 35 #include <stdbool.h> 36 #include <stdlib.h> 37 #include <string.h> 38 39 #include <isc/assertions.h> 40 41 #include <isccc/sexpr.h> 42 #include <isccc/util.h> 43 44 static isccc_sexpr_t sexpr_t = { ISCCC_SEXPRTYPE_T, { NULL } }; 45 46 #define CAR(s) (s)->value.as_dottedpair.car 47 #define CDR(s) (s)->value.as_dottedpair.cdr 48 49 isccc_sexpr_t * 50 isccc_sexpr_cons(isccc_sexpr_t *car, isccc_sexpr_t *cdr) { 51 isccc_sexpr_t *sexpr; 52 53 sexpr = malloc(sizeof(*sexpr)); 54 if (sexpr == NULL) { 55 return NULL; 56 } 57 sexpr->type = ISCCC_SEXPRTYPE_DOTTEDPAIR; 58 CAR(sexpr) = car; 59 CDR(sexpr) = cdr; 60 61 return sexpr; 62 } 63 64 isccc_sexpr_t * 65 isccc_sexpr_tconst(void) { 66 return &sexpr_t; 67 } 68 69 isccc_sexpr_t * 70 isccc_sexpr_fromstring(const char *str) { 71 isccc_sexpr_t *sexpr; 72 73 sexpr = malloc(sizeof(*sexpr)); 74 if (sexpr == NULL) { 75 return NULL; 76 } 77 sexpr->type = ISCCC_SEXPRTYPE_STRING; 78 sexpr->value.as_string = strdup(str); 79 if (sexpr->value.as_string == NULL) { 80 free(sexpr); 81 return NULL; 82 } 83 84 return sexpr; 85 } 86 87 isccc_sexpr_t * 88 isccc_sexpr_frombinary(const isccc_region_t *region) { 89 isccc_sexpr_t *sexpr; 90 unsigned int region_size; 91 92 sexpr = malloc(sizeof(*sexpr)); 93 if (sexpr == NULL) { 94 return NULL; 95 } 96 sexpr->type = ISCCC_SEXPRTYPE_BINARY; 97 region_size = REGION_SIZE(*region); 98 /* 99 * We add an extra byte when we malloc so we can NUL terminate 100 * the binary data. This allows the caller to use it as a C 101 * string. It's up to the caller to ensure this is safe. We don't 102 * add 1 to the length of the binary region, because the NUL is 103 * not part of the binary data. 104 */ 105 sexpr->value.as_region.rstart = malloc(region_size + 1); 106 if (sexpr->value.as_region.rstart == NULL) { 107 free(sexpr); 108 return NULL; 109 } 110 sexpr->value.as_region.rend = sexpr->value.as_region.rstart + 111 region_size; 112 memmove(sexpr->value.as_region.rstart, region->rstart, region_size); 113 /* 114 * NUL terminate. 115 */ 116 sexpr->value.as_region.rstart[region_size] = '\0'; 117 118 return sexpr; 119 } 120 121 void 122 isccc_sexpr_free(isccc_sexpr_t **sexprp) { 123 isccc_sexpr_t *sexpr; 124 isccc_sexpr_t *item; 125 126 sexpr = *sexprp; 127 *sexprp = NULL; 128 if (sexpr == NULL) { 129 return; 130 } 131 switch (sexpr->type) { 132 case ISCCC_SEXPRTYPE_STRING: 133 free(sexpr->value.as_string); 134 break; 135 case ISCCC_SEXPRTYPE_DOTTEDPAIR: 136 item = CAR(sexpr); 137 if (item != NULL) { 138 isccc_sexpr_free(&item); 139 } 140 item = CDR(sexpr); 141 if (item != NULL) { 142 isccc_sexpr_free(&item); 143 } 144 break; 145 case ISCCC_SEXPRTYPE_BINARY: 146 free(sexpr->value.as_region.rstart); 147 break; 148 } 149 free(sexpr); 150 } 151 152 static bool 153 printable(isccc_region_t *r) { 154 unsigned char *curr; 155 156 curr = r->rstart; 157 while (curr != r->rend) { 158 if (!isprint(*curr)) { 159 return false; 160 } 161 curr++; 162 } 163 164 return true; 165 } 166 167 void 168 isccc_sexpr_print(isccc_sexpr_t *sexpr, FILE *stream) { 169 isccc_sexpr_t *cdr; 170 unsigned int size, i; 171 unsigned char *curr; 172 173 if (sexpr == NULL) { 174 fprintf(stream, "nil"); 175 return; 176 } 177 178 switch (sexpr->type) { 179 case ISCCC_SEXPRTYPE_T: 180 fprintf(stream, "t"); 181 break; 182 case ISCCC_SEXPRTYPE_STRING: 183 fprintf(stream, "\"%s\"", sexpr->value.as_string); 184 break; 185 case ISCCC_SEXPRTYPE_DOTTEDPAIR: 186 fprintf(stream, "("); 187 do { 188 isccc_sexpr_print(CAR(sexpr), stream); 189 cdr = CDR(sexpr); 190 if (cdr != NULL) { 191 fprintf(stream, " "); 192 if (cdr->type != ISCCC_SEXPRTYPE_DOTTEDPAIR) { 193 fprintf(stream, ". "); 194 isccc_sexpr_print(cdr, stream); 195 cdr = NULL; 196 } 197 } 198 sexpr = cdr; 199 } while (sexpr != NULL); 200 fprintf(stream, ")"); 201 break; 202 case ISCCC_SEXPRTYPE_BINARY: 203 size = REGION_SIZE(sexpr->value.as_region); 204 curr = sexpr->value.as_region.rstart; 205 if (printable(&sexpr->value.as_region)) { 206 fprintf(stream, "'%.*s'", (int)size, curr); 207 } else { 208 fprintf(stream, "0x"); 209 for (i = 0; i < size; i++) { 210 fprintf(stream, "%02x", *curr++); 211 } 212 } 213 break; 214 default: 215 UNREACHABLE(); 216 } 217 } 218 219 isccc_sexpr_t * 220 isccc_sexpr_car(isccc_sexpr_t *list) { 221 REQUIRE(list->type == ISCCC_SEXPRTYPE_DOTTEDPAIR); 222 223 return CAR(list); 224 } 225 226 isccc_sexpr_t * 227 isccc_sexpr_cdr(isccc_sexpr_t *list) { 228 REQUIRE(list->type == ISCCC_SEXPRTYPE_DOTTEDPAIR); 229 230 return CDR(list); 231 } 232 233 void 234 isccc_sexpr_setcar(isccc_sexpr_t *pair, isccc_sexpr_t *car) { 235 REQUIRE(pair->type == ISCCC_SEXPRTYPE_DOTTEDPAIR); 236 237 CAR(pair) = car; 238 } 239 240 void 241 isccc_sexpr_setcdr(isccc_sexpr_t *pair, isccc_sexpr_t *cdr) { 242 REQUIRE(pair->type == ISCCC_SEXPRTYPE_DOTTEDPAIR); 243 244 CDR(pair) = cdr; 245 } 246 247 isccc_sexpr_t * 248 isccc_sexpr_addtolist(isccc_sexpr_t **l1p, isccc_sexpr_t *l2) { 249 isccc_sexpr_t *last, *elt, *l1; 250 251 REQUIRE(l1p != NULL); 252 l1 = *l1p; 253 REQUIRE(l1 == NULL || l1->type == ISCCC_SEXPRTYPE_DOTTEDPAIR); 254 255 elt = isccc_sexpr_cons(l2, NULL); 256 if (elt == NULL) { 257 return NULL; 258 } 259 if (l1 == NULL) { 260 *l1p = elt; 261 return elt; 262 } 263 for (last = l1; CDR(last) != NULL; last = CDR(last)) { 264 /* Nothing */ 265 } 266 CDR(last) = elt; 267 268 return elt; 269 } 270 271 bool 272 isccc_sexpr_listp(isccc_sexpr_t *sexpr) { 273 if (sexpr == NULL || sexpr->type == ISCCC_SEXPRTYPE_DOTTEDPAIR) { 274 return true; 275 } 276 return false; 277 } 278 279 bool 280 isccc_sexpr_emptyp(isccc_sexpr_t *sexpr) { 281 if (sexpr == NULL) { 282 return true; 283 } 284 return false; 285 } 286 287 bool 288 isccc_sexpr_stringp(isccc_sexpr_t *sexpr) { 289 if (sexpr != NULL && sexpr->type == ISCCC_SEXPRTYPE_STRING) { 290 return true; 291 } 292 return false; 293 } 294 295 bool 296 isccc_sexpr_binaryp(isccc_sexpr_t *sexpr) { 297 if (sexpr != NULL && sexpr->type == ISCCC_SEXPRTYPE_BINARY) { 298 return true; 299 } 300 return false; 301 } 302 303 char * 304 isccc_sexpr_tostring(isccc_sexpr_t *sexpr) { 305 REQUIRE(sexpr != NULL && (sexpr->type == ISCCC_SEXPRTYPE_STRING || 306 sexpr->type == ISCCC_SEXPRTYPE_BINARY)); 307 308 if (sexpr->type == ISCCC_SEXPRTYPE_BINARY) { 309 return (char *)sexpr->value.as_region.rstart; 310 } 311 return sexpr->value.as_string; 312 } 313 314 isccc_region_t * 315 isccc_sexpr_tobinary(isccc_sexpr_t *sexpr) { 316 REQUIRE(sexpr != NULL && sexpr->type == ISCCC_SEXPRTYPE_BINARY); 317 return &sexpr->value.as_region; 318 } 319