Commit 169acb63 authored by Niels Möller's avatar Niels Möller
Browse files

*** empty log message ***

Rev: src/nettle/examples/sexp-conv-test:1.7(DEAD)
Rev: src/nettle/examples/sexp-conv.c:1.16(DEAD)
parent 6aab1123
#! /bin/sh
if [ -z "$srcdir" ] ; then
srcdir=`pwd`
fi
# echo -n is not portable
if [ "`echo -n ''`" = "" ]; then
n='-n'; c=''
elif [ "`echo '\c'`" = "" ]; then
n=''; c='\c'
else
echo >&2 'Neither echo -n nor echo \c seems to work.'
exit 1
fi
test_advanced () {
echo "$1" > test.in
if ./sexp-conv -s advanced <test.in >test1.out ; then
true
else
exit 1
fi
echo "$2" > test2.out
if cmp test1.out test2.out ; then
true
else
exit 1;
fi
}
test_transport () {
echo "$1" > test.in
if ./sexp-conv -s transport <test.in >test1.out ; then
true
else
exit 1
fi
echo "$2" > test2.out
if cmp test1.out test2.out ; then
true
else
exit 1;
fi
}
test_canonical () {
echo "$1" > test.in
if ./sexp-conv -s canonical <test.in >test1.out ; then
true
else
exit 1
fi
echo $n "$2$c" > test2.out
if cmp test1.out test2.out ; then
true
else
exit 1;
fi
}
test_advanced '0:' '""'
test_advanced '3:foo' 'foo'
test_advanced '12:fooooooooooo' 'fooooooooooo'
test_advanced '10:fooooooooo' 'fooooooooo'
test_advanced '4:3des' '"3des"'
test_advanced '"foo"' 'foo'
test_advanced '4:foo
' '"foo\n"'
test_advanced '2:"\' '"\"\\"'
test_advanced '()' '()'
test_advanced '(foo bar baz)' '(foo bar
baz)'
test_advanced '; comment
()' '()'
test_advanced '(foo[bar]foo)' '(foo [bar]foo)'
test_transport '0:' '{MDo=}'
test_transport '()' '{KCk=}'
test_transport 'foo' '{Mzpmb28=}'
test_transport '(foo bar baz)' '{KDM6Zm9vMzpiYXIzOmJheik=}'
test_canonical '""' '0:'
test_canonical 'foo' '3:foo'
test_canonical 'fooooooooooo' '12:fooooooooooo'
test_canonical 'fooooooooo' '10:fooooooooo'
test_canonical '(foo bar baz)' '(3:foo3:bar3:baz)'
test_canonical '{KDM6Zm9vMzpiYXIzOmJheik=}' '(3:foo3:bar3:baz)'
exit 0
/* sexp-conv.c
*
* Conversion tool for handling the different flavours of sexp
* syntax. */
#include "base16.h"
#include "base64.h"
#include "buffer.h"
#include "nettle-meta.h"
#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* For getopt */
#include <unistd.h>
void
die(const char *format, ...)
#if __GNUC___
__attribute__((__format__ (__printf__,1, 2)))
__attribute__((__noreturn__))
#endif
;
void
die(const char *format, ...)
{
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
exit(EXIT_FAILURE);
}
enum sexp_mode
{
SEXP_CANONICAL = 0,
SEXP_ADVANCED = 1,
SEXP_TRANSPORT = 2,
};
/* Special marks in the input stream */
enum sexp_char_type
{
SEXP_NORMAL_CHAR = 0,
SEXP_EOF_CHAR, SEXP_END_CHAR,
};
enum sexp_token
{
SEXP_STRING,
SEXP_DISPLAY_START,
SEXP_DISPLAY_END,
SEXP_LIST_START,
SEXP_LIST_END,
SEXP_TRANSPORT_START,
SEXP_CODING_END,
SEXP_EOF,
};
/* Input */
struct sexp_input
{
FILE *f;
/* Character stream, consisting of ordinary characters,
* SEXP_EOF_CHAR, and SEXP_END_CHAR. */
enum sexp_char_type ctype;
uint8_t c;
const struct nettle_armor *coding;
union {
struct base64_decode_ctx base64;
struct base16_decode_ctx hex;
} state;
/* Terminator for current coding */
uint8_t terminator;
/* Type of current token */
enum sexp_token token;
/* Current token */
struct nettle_buffer string;
};
static void
sexp_input_init(struct sexp_input *input, FILE *f)
{
input->f = f;
input->coding = NULL;
nettle_buffer_init(&input->string);
}
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;
}
}
static void
sexp_get_char(struct sexp_input *input)
{
if (input->coding)
for (;;)
{
int 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)
{
assert(input->ctype == SEXP_NORMAL_CHAR);
if (!NETTLE_BUFFER_PUTC(&input->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 const char
token_chars[0x80] =
{
/* 0, ... 0x1f */
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
/* SPC ! " # $ % & ' ( ) * + , - . / */
0,0,0,0,0,0,0,0, 0,0,1,1,0,1,1,1,
/* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
1,1,1,1,1,1,1,1, 1,1,1,0,0,1,0,0,
/* @ A ... O */
0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
/* P ... Z [ \] ^ _ */
1,1,1,1,1,1,1,1, 1,1,1,0,0,0,0,1,
/* ` a, ... o */
0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
/* p ... z { | } ~ DEL */
1,1,1,1,1,1,1,1, 1,1,1,0,0,0,0,0,
};
#define TOKEN_CHAR(c) ((c) < 0x80 && token_chars[(c)])
static void
sexp_get_token_string(struct sexp_input *input)
{
assert(!input->coding);
assert(input->ctype == SEXP_NORMAL_CHAR);
if (!TOKEN_CHAR(input->c))
die("Invalid token.\n");
do
{
sexp_push_char(input);
sexp_get_char(input);
}
while (TOKEN_CHAR(input->c));
assert (input->string.size);
}
static void
sexp_get_string(struct sexp_input *input)
{
input->string.size = 0;
input->token = SEXP_STRING;
switch (input->c)
{
case '\"':
while (sexp_get_quoted_char(input))
sexp_push_char(input);
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);
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);
break;
}
}
static void
sexp_get_string_length(struct sexp_input *input, enum sexp_mode mode)
{
unsigned length;
input->string.size = 0;
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);
}
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);
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);
}
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. */
static void
sexp_get_token(struct sexp_input *input, enum sexp_mode mode)
{
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_next_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);
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);
return;
}
}
}
/* Output routines */
struct sexp_output
{
FILE *f;
const struct nettle_armor *coding;
unsigned coding_indent;
union {
struct base64_decode_ctx base64;
/* NOTE: There's no context for hex encoding */
} state;
unsigned pos;
};
static void
sexp_output_init(struct sexp_output *output, FILE *f)
{
output->f = f;
output->coding = NULL;
output->pos = 0;
}
#define LINE_WIDTH 60