diff --git a/sexp.c b/sexp.c new file mode 100644 index 0000000000000000000000000000000000000000..9869d135582fc4554597e218ef2d23c4d9e1f495 --- /dev/null +++ b/sexp.c @@ -0,0 +1,228 @@ +/* sexp.c + * + * Parsing s-expressions. + */ + +/* nettle, low-level cryptographics library + * + * Copyright (C) 2002 Niels Möller + * + * The nettle library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The nettle library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the nettle library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "sexp.h" + +#include <stdlib.h> +#include <string.h> + +void +sexp_iterator_init(struct sexp_iterator *iterator, + unsigned length, const uint8_t *input) +{ + iterator->length = length; + iterator->buffer = input; + iterator->pos = 0; + iterator->level = 0; + iterator->type = SEXP_START; + iterator->display_length = 0; + iterator->display = NULL; + iterator->atom_length = 0; + iterator->atom = NULL; + + /* FIXME: For other than canonical syntax, + * skip white space here. */ +} + +#define EMPTY(i) ((i)->pos == (i)->length) +#define NEXT(i) ((i)->buffer[(i)->pos++]) + +static int +sexp_iterator_simple(struct sexp_iterator *iterator, + unsigned *size, + const uint8_t **string) +{ + unsigned length = 0; + uint8_t c; + + if (EMPTY(iterator)) return 0; + c = NEXT(iterator); + + if (c >= '1' && c <= '9') + do + { + length = length * 10 + (c - '0'); + + if (EMPTY(iterator)) return 0; + c = NEXT(iterator); + } + while (c < '0' || c > '9'); + + else if (c != '0') + return 0; + + if (EMPTY(iterator) || NEXT(iterator) != ':') + return 0; + + if (length > (iterator->length - iterator->pos)) + return 0; + + *size = length; + *string = iterator->buffer + iterator->pos; + iterator->pos += length; + + return 1; +} + +/* All these functions return 1 on success, 0 on failure */ +int +sexp_iterator_next(struct sexp_iterator *iterator) +{ + if (iterator->type == SEXP_END) + return 1; + + if (EMPTY(iterator)) + { + if (iterator->level) + return 0; + + iterator->type = SEXP_END; + return 1; + } + switch (iterator->buffer[iterator->pos]) + { + case '(': /* A list */ + if (iterator->type == SEXP_START) + iterator->type = SEXP_LIST; + else + return sexp_iterator_enter_list(iterator) + && sexp_iterator_exit_list(iterator); + case '[': /* Atom with display type */ + iterator->pos++; + if (!sexp_iterator_simple(iterator, + &iterator->display_length, + &iterator->display)) + return 0; + if (EMPTY(iterator) || NEXT(iterator) != ']') + return 0; + + break; + + default: + iterator->display_length = 0; + iterator->display = NULL; + + break; + } + + iterator->type = SEXP_ATOM; + + return sexp_iterator_simple(iterator, + &iterator->atom_length, + &iterator->atom); +} + +/* Current element must be a list. */ +int +sexp_iterator_enter_list(struct sexp_iterator *iterator) +{ + if (iterator->type != SEXP_LIST) + return 0; + + if (EMPTY(iterator) || NEXT(iterator) != '(') + /* Internal error */ + abort(); + + iterator->level++; + return 1; +} + +/* Skips the rest of the current list */ +int +sexp_iterator_exit_list(struct sexp_iterator *iterator) +{ + if (!iterator->level) + return 0; + + while (sexp_iterator_next(iterator)) + if (iterator->type == SEXP_END) + { + if (NEXT(iterator) != ')') + return 0; + iterator->level--; + return 1; + } + return 0; +} + +int +sexp_iterator_assoc(struct sexp_iterator *iterator, + unsigned nkeys, + const struct sexp_assoc_key *keys, + struct sexp_iterator *values) +{ + if (!sexp_iterator_enter_list(iterator)) + return 0; + + for (;;) + { + if (!sexp_iterator_next(iterator)) + return 0; + + switch (iterator->type) + { + case SEXP_LIST: + + if (sexp_iterator_enter_list(iterator) + && sexp_iterator_next(iterator)) + return 0; + + if (iterator->type == SEXP_ATOM + && !iterator->display) + { + /* Compare to the given keys */ + unsigned i; + for (i = 0; i<nkeys; i++) + { + if (keys[i].length == iterator->atom_length + && !memcmp(keys[i].name, iterator->atom, + keys[i].length)) + { + /* Match found. NOTE: We allow multiple matches. */ + if (!sexp_iterator_next(iterator)) + return 0; + + /* Record this position. */ + values[i] = *iterator; + + break; + } + } + } + if (!sexp_iterator_exit_list(iterator)) + return 0; + break; + case SEXP_ATOM: + /* Just ignore */ + break; + + case SEXP_END: + return sexp_iterator_exit_list(iterator); + + default: + abort(); + } + } +} diff --git a/sexp.h b/sexp.h new file mode 100644 index 0000000000000000000000000000000000000000..b751b0c278c9b48a81f23005beef0ed63d6149e3 --- /dev/null +++ b/sexp.h @@ -0,0 +1,87 @@ +/* sexp.h + * + * Parsing s-expressions. + */ + +/* nettle, low-level cryptographics library + * + * Copyright (C) 2002 Niels Möller + * + * The nettle library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The nettle library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the nettle library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#ifndef NETTLE_SEXP_H_INCLUDED +#define NETTLE_SEXP_H_INCLUDED + +#include <inttypes.h> + +enum sexp_type + { SEXP_START, SEXP_ATOM, SEXP_LIST, SEXP_END }; + +struct sexp_iterator +{ + unsigned length; + const uint8_t *buffer; + unsigned pos; + unsigned level; + + enum sexp_type type; + + unsigned display_length; + const uint8_t *display; + + unsigned atom_length; + const uint8_t *atom; +}; + +struct sexp_assoc_key +{ + unsigned length; + const uint8_t *name; +}; + +/* Initializes the iterator. You have to call next to get to the first + * element. */ +void +sexp_iterator_init(struct sexp_iterator *iterator, + unsigned length, const uint8_t *input); + +/* All these functions return 1 on success, 0 on failure */ +int +sexp_iterator_next(struct sexp_iterator *iterator); + +/* Current element must be a list. */ +int +sexp_iterator_enter_list(struct sexp_iterator *iterator); + +/* Skips the rest of the current list */ +int +sexp_iterator_exit_list(struct sexp_iterator *iterator); + +/* Current element must be a list. Looks up element of type + * + * (key rest...) + * + * For a matching key, the corersponding iterator is initialized + * pointing at the start of REST. + */ +int +sexp_iterator_assoc(struct sexp_iterator *iterator, + unsigned nkeys, + const struct sexp_assoc_key *keys, + struct sexp_iterator *values); + +#endif /* NETTLE_SEXP_H_INCLUDED */