Home | History | Annotate | Line # | Download | only in isccc
      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