/* input.c */ /* nettle, low-level cryptographics library * * Copyright (C) 2002, 2003 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. */ #if HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include "input.h" void sexp_input_init(struct sexp_input *input, FILE *f) { input->f = f; input->coding = NULL; } static void sexp_get_raw_char(struct sexp_input *input) { int c = getc(input->f); if (c < 0) { if (ferror(input->f)) die("Read error: %s\n", strerror(errno)); input->ctype = SEXP_EOF_CHAR; } else { input->ctype = SEXP_NORMAL_CHAR; input->c = c; } } void sexp_get_char(struct sexp_input *input) { if (input->coding) for (;;) { unsigned done; sexp_get_raw_char(input); if (input->ctype == SEXP_EOF_CHAR) die("Unexpected end of file in coded data.\n"); if (input->c == input->terminator) { input->ctype = SEXP_END_CHAR; return; } done = 1; /* Decodes in place. Should always work, when we decode one * character at a time. */ if (!input->coding->decode_update(&input->state, &done, &input->c, 1, &input->c)) die("Invalid coded data.\n"); if (done) return; } else sexp_get_raw_char(input); } static uint8_t sexp_next_char(struct sexp_input *input) { sexp_get_char(input); if (input->ctype != SEXP_NORMAL_CHAR) die("Unexpected end of file.\n"); return input->c; } static void sexp_push_char(struct sexp_input *input, struct nettle_buffer *string) { assert(input->ctype == SEXP_NORMAL_CHAR); if (!NETTLE_BUFFER_PUTC(string, input->c)) die("Virtual memory exhasuted.\n"); } static void sexp_input_start_coding(struct sexp_input *input, const struct nettle_armor *coding, uint8_t terminator) { assert(!input->coding); input->coding = coding; input->coding->decode_init(&input->state); input->terminator = terminator; } static void sexp_input_end_coding(struct sexp_input *input) { assert(input->coding); if (!input->coding->decode_final(&input->state)) die("Invalid coded data.\n"); input->coding = NULL; } /* Return 0 at end-of-string */ static int sexp_get_quoted_char(struct sexp_input *input) { sexp_next_char(input); for (;;) switch (input->c) { default: return 1; case '\"': return 0; case '\\': sexp_next_char(input); switch (input->c) { case 'b': input->c = '\b'; return 1; case 't': input->c = '\t'; return 1; case 'n': input->c = '\n'; return 1; case 'f': input->c = '\f'; return 1; case 'r': input->c = '\r'; return 1; case '\\': input->c = '\\'; return 1; case 'o': case 'x': /* FIXME: Not implemnted */ abort(); case '\n': if (sexp_next_char(input) == '\r') sexp_next_char(input); break; case '\r': if (sexp_next_char(input) == '\n') sexp_next_char(input); break; } return 1; } } static void sexp_get_token_string(struct sexp_input *input, struct nettle_buffer *string) { assert(!input->coding); assert(input->ctype == SEXP_NORMAL_CHAR); if (!TOKEN_CHAR(input->c)) die("Invalid token.\n"); do { sexp_push_char(input, string); sexp_get_char(input); } while (input->ctype == SEXP_NORMAL_CHAR && TOKEN_CHAR(input->c)); assert (string->size); } static void sexp_get_string(struct sexp_input *input, struct nettle_buffer *string) { nettle_buffer_reset(string); input->token = SEXP_STRING; switch (input->c) { case '\"': while (sexp_get_quoted_char(input)) sexp_push_char(input, string); sexp_get_char(input); break; case '#': sexp_input_start_coding(input, &nettle_base16, '#'); goto decode; case '|': sexp_input_start_coding(input, &nettle_base64, '|'); decode: for (;;) { sexp_get_char(input); switch (input->ctype) { case SEXP_NORMAL_CHAR: sexp_push_char(input, string); break; case SEXP_EOF_CHAR: die("Unexpected end of file in coded string.\n"); case SEXP_END_CHAR: sexp_input_end_coding(input); sexp_get_char(input); return; } } break; default: sexp_get_token_string(input, string); break; } } static void sexp_get_string_length(struct sexp_input *input, enum sexp_mode mode, struct nettle_buffer *string) { unsigned length; nettle_buffer_reset(string); input->token = SEXP_STRING; length = input->c - '0'; if (!length) /* There must be no more digits */ sexp_next_char(input); else { assert(length < 10); /* Get rest of digits */ for (;;) { sexp_next_char(input); if (input->c < '0' || input->c > '9') break; /* FIXME: Check for overflow? */ length = length * 10 + input->c - '0'; } } switch(input->c) { case ':': /* Verbatim */ for (; length; length--) { sexp_next_char(input); sexp_push_char(input, string); } break; case '"': if (mode != SEXP_ADVANCED) die("Encountered quoted string in canonical mode.\n"); for (; length; length--) if (sexp_get_quoted_char(input)) sexp_push_char(input, string); else die("Unexpected end of string.\n"); if (sexp_get_quoted_char(input)) die("Quoted string longer than expected.\n"); break; case '#': sexp_input_start_coding(input, &nettle_base16, '#'); goto decode; case '|': sexp_input_start_coding(input, &nettle_base64, '|'); decode: for (; length; length--) { sexp_next_char(input); sexp_push_char(input, string); } sexp_get_char(input); if (input->ctype != SEXP_END_CHAR) die("Coded string too long.\n"); sexp_input_end_coding(input); break; default: die("Invalid string.\n"); } /* Skip the ending character. */ sexp_get_char(input); } /* When called, input->c should be the first character of the current * token. * * When returning, input->c should be the first character of the next * token. */ void sexp_get_token(struct sexp_input *input, enum sexp_mode mode, struct nettle_buffer *string) { for(;;) switch(input->ctype) { case SEXP_EOF_CHAR: input->token = SEXP_EOF; return; case SEXP_END_CHAR: input->token = SEXP_CODING_END; sexp_input_end_coding(input); sexp_get_char(input); return; case SEXP_NORMAL_CHAR: switch(input->c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': sexp_get_string_length(input, mode, string); return; case '(': input->token = SEXP_LIST_START; sexp_get_char(input); return; case ')': input->token = SEXP_LIST_END; sexp_get_char(input); return; case '[': input->token = SEXP_DISPLAY_START; sexp_get_char(input); return; case ']': input->token = SEXP_DISPLAY_END; sexp_get_char(input); return; case '{': if (mode == SEXP_CANONICAL) die("Unexpected transport data in canonical mode.\n"); sexp_input_start_coding(input, &nettle_base64, '}'); sexp_get_char(input); input->token = SEXP_TRANSPORT_START; return; case ' ': /* SPC, TAB, LF, CR */ case '\t': case '\n': case '\r': if (mode == SEXP_CANONICAL) die("Whitespace encountered in canonical mode.\n"); sexp_get_char(input); break; case ';': /* Comments */ if (mode == SEXP_CANONICAL) die("Comment encountered in canonical mode.\n"); do { sexp_get_raw_char(input); if (input->ctype != SEXP_NORMAL_CHAR) return; } while (input->c != '\n'); break; default: /* Ought to be a string */ if (mode != SEXP_ADVANCED) die("Encountered advanced string in canonical mode.\n"); sexp_get_string(input, string); return; } } }