* syntax. */
#include "base64.h"
+#include "buffer.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
enum sexp_mode
{
SEXP_CANONICAL = 0,
SEXP_ADVANCED = 1,
/* OR:ed with SEXP_CANONICAL or SEXP_ADVANCED when reading
- * transport data. */
+ * transport data */
SEXP_TRANSPORT = 2,
};
enum sexp_token
{
- SEXP_LIST,
SEXP_STRING,
SEXP_DISPLAY_START,
SEXP_DISPLAY_END,
unsigned level;
};
+static void
+sexp_input_init(struct sexp_input *input, FILE *f, enum sexp_mode mode)
+{
+ input->f = f;
+ input->mode = mode;
+ input->level = 0;
+}
+
struct sexp_output
{
FILE *f;
enum sexp_mode mode;
+ struct base64_encode_ctx base64;
- unsigned indent;
+ /* Items at the head of the list */
+ unsigned items;
+
unsigned pos;
};
-
+static void
+sexp_output_init(struct sexp_output *output, FILE *f, enum sexp_mode mode)
+{
+ output->f = f;
+ output->mode = mode;
+ output->pos = 0;
+}
+
+\f
+/* Input */
+
/* Returns 1 on success. On failure, return -1. For special tokens,
* return 0 and set input->token accordingly. */
static int
if (c == '}')
{
- if (base64_decode_status(&input->ctx))
+ if (base64_decode_status(&input->base64))
{
input->token = SEXP_TRANSPORT_END;
return 0;
return -1;
}
- done = base64_decode_single(&input->ctx, out, c);
+ done = base64_decode_single(&input->base64, out, c);
if (done)
return 1;
}
if (c >= 0 && TOKEN_CHAR(c))
return c;
- ungetc(input->f, c);
+ ungetc(c, input->f);
return 0;
}
assert(input->mode == SEXP_ADVANCED);
ungetc(c, input->f);
}
-#endif
static int
sexp_get_string_char(struct sexp_input *input)
{
assert(input->mode == SEXP_ADVANCED);
-
}
+#endif
static int
sexp_get_quoted_char(struct sexp_input *input, uint8_t *c)
}
static int
-sexp_get_atom_string(struct sexp_input *input, uint8_t c)
+sexp_get_token_string(struct sexp_input *input, uint8_t c)
{
assert(input->mode == SEXP_ADVANCED);
if (sexp_get_char(input, &c) <= 0)
return 0;
- if (c < 0 || < > 9)
+ if (c < '0' || c > '9')
break;
/* FIXME: Check for overflow? */
return sexp_get_string_length(input, c - '0');
case '(':
- input->token = SEXP_START_LIST;
+ input->token = SEXP_LIST_START;
return 1;
case ')':
- input->token = SEXP_END_LIST;
+ input->token = SEXP_LIST_END;
+
+ if (!input->level)
+ return 0;
+
+ input->level--;
return 1;
case '[':
return (input->mode == SEXP_ADVANCED)
&& sexp_get_string(input, c);
}
- }
+ }
+ abort();
}
+
+\f
+/* Output routines */
+
+#define LINE_WIDTH 60
+
static int
-sexp_put_newline(struct sexp_output *output)
+sexp_put_newline(struct sexp_output *output,
+ unsigned indent)
{
unsigned i;
if (putc('\n', output->f) < 0)
return 0;
- for(i = 0; i<output->indent)
+ for(i = 0; i < indent; i++)
if (putc(' ', output->f) < 0)
return 0;
- output->pos = output->indent;
+ output->pos = indent;
return 1;
}
static int
-sexp_put_char(struct sexp_output *output,
+sexp_put_char(struct sexp_output *output, unsigned indent,
uint8_t c)
{
- output->pos++;
- return fputc(c, output->file) >= 0;
-}
-
-static int
-sexp_put_data(struct sexp_output *output,
- unsigned length, uint8_t data)
-{
- if (fwrite(data, 1, length, output->f)
- == length)
+ if (output->mode & SEXP_TRANSPORT)
{
- output->pos += length;
+ uint8_t encoded[2];
+ unsigned done;
+ unsigned i;
+
+ done = base64_encode_single(&output->base64, encoded, c);
+
+ assert(done <= sizeof(encoded));
+
+ for (i = 0; i<done; i++)
+ {
+ if (indent &&
+ output->pos > LINE_WIDTH
+ && output->pos > (indent + 10))
+ if (!sexp_put_newline(output, indent))
+ return 0;
+
+ if (putc(encoded[i], output->f) < 0)
+ return 0;
+
+ output->pos++;
+ }
return 1;
}
else
+ {
+ output->pos++;
+ return putc(c, output->f) >= 0;
+ }
+}
+
+static int
+sexp_put_data(struct sexp_output *output, unsigned indent,
+ unsigned length, const uint8_t *data)
+{
+ unsigned i;
+
+ for (i = 0; i<length; i++)
+ if (!sexp_put_char(output, indent, data[i]))
+ return 0;
+
+ return 1;
+}
+
+static int
+sexp_puts(struct sexp_output *output, unsigned indent,
+ const uint8_t *s)
+{
+ while (*s)
+ if (!sexp_put_char(output, indent, *s++))
+ return 0;
+
+ return 1;
+}
+
+static int
+sexp_put_length(struct sexp_output *output, unsigned indent,
+ unsigned length)
+{
+ unsigned digit = 1;
+
+ while (digit < length)
+ digit *= 10;
+
+ for (; digit; length %= digit, digit /= 10)
+ if (!sexp_put_char(output, indent, '0' + length / digit))
+ return 0;
+
+ return 1;
+}
+
+static int
+sexp_put_base64_start(struct sexp_output *output, uint8_t c)
+{
+ assert(! (output->mode & SEXP_TRANSPORT));
+
+ if (!sexp_put_char(output, 0, c))
return 0;
+
+ base64_encode_init(&output->base64);
+ output->mode |= SEXP_TRANSPORT;
+
+ return 1;
}
+static int
+sexp_put_base64_end(struct sexp_output *output, uint8_t c)
+{
+ uint8_t encoded[BASE64_ENCODE_FINAL_LENGTH];
+ unsigned done;
+
+ assert(output->mode & SEXP_TRANSPORT);
+
+ done = base64_encode_final(&output->base64, encoded);
+
+ assert(done < sizeof(encoded));
+
+ output->mode &= ~ SEXP_TRANSPORT;
+
+ return sexp_put_data(output, 0, done, encoded)
+ && sexp_put_char(output, 0, c);
+}
static int
-sexp_put_string(struct sexp_output *output,
+sexp_put_string(struct sexp_output *output, unsigned indent,
struct nettle_buffer *string)
{
if (!string->size)
- {
- const char *s = (output->mode == SEXP_ADVANCED) ? "\"\"": "0:";
- output->pos += 2;
-
- return 2 == fputs(s , output->f);
- }
+ return sexp_puts(output, indent,
+ (output->mode == SEXP_ADVANCED) ? "\"\"": "0:");
if (output->mode == SEXP_ADVANCED)
{
unsigned i;
- int token = (string.buffer[0] < '0' || string.buffer[0] > '9');
+ int token = (string->contents[0] < '0' || string->contents[0] > '9');
int quote_friendly = 1;
- for (i = 0; i<string.size; i++)
+ for (i = 0; i<string->size; i++)
{
- uint8_t c = string.buffer[i];
+ uint8_t c = string->contents[i];
if (token & !TOKEN_CHAR(c))
token = 0;
}
if (token)
- return sexp_put_buffer(output, string.size, string.buffer);
+ return sexp_put_data(output, indent, string->size, string->contents);
else if (quote_friendly)
{
- output->pos += 2;
-
- return sexp_put_char(output, '"')
- && sexp_put_buffer(output, string.size, string.buffer)
- && sexp_put_char(output, '"');
+ return sexp_put_char(output, indent, '"')
+ && sexp_put_data(output, indent, string->size, string->contents)
+ && sexp_put_char(output, indent, '"');
}
else
- {
-#define DATA_PER_LINE 40
- uint8_t line[BASE64_ENCODE_LENGTH(DATA_PER_LINE)
- + BASE64_ENCODE_FINAL_LENGTH];
- struct base64_encode_ctx ctx;
+ return (sexp_put_base64_start(output, '|')
+ && sexp_put_data(output, output->pos,
+ string->size, string->contents)
+ && sexp_put_base64_end(output, '|'));
+ }
+ else
+ return sexp_put_length(output, indent, string->size)
+ && sexp_put_char(output, indent, ':')
+ && sexp_put_data(output, indent, string->size, string->contents);
+}
- unsigned old_indent = output->indent;
- unsigned i;
-
- output->indent = output->pos + 1;
- if (!sexp_put_char(output, '|'))
- return 0;
-
- base64_encode_init(&ctx);
- for (i = 0; i + DATA_PER_LINE < string.size)
+static int
+sexp_put_list_start(struct sexp_output *output, unsigned indent)
+{
+ if (!sexp_put_char(output, indent, '('))
+ return 0;
+
+ output->items = 0;
+
+ return 1;
+}
+
+static int
+sexp_put_list_end(struct sexp_output *output, unsigned indent)
+{
+ return sexp_put_char(output, indent, ')') ;
+}
+
+static int
+sexp_put_display_start(struct sexp_output *output, unsigned indent)
+{
+ return sexp_put_char(output, indent, '[');
+}
+
+static int
+sexp_put_display_end(struct sexp_output *output, unsigned indent)
+{
+ return sexp_put_char(output, indent, ']') ;
+}
+
+static int
+sexp_convert_string(struct sexp_input *input, struct sexp_output *output,
+ unsigned indent)
+{
+ return (sexp_get_token(input)
+ && input->token == SEXP_STRING
+ && sexp_put_string(output, indent, &input->string));
+}
+
+static int
+sexp_convert_item(struct sexp_input *input, struct sexp_output *output,
+ unsigned indent);
+
+static int
+sexp_convert_list(struct sexp_input *input, struct sexp_output *output,
+ unsigned indent)
+{
+ if (!sexp_get_token(input))
+ return 0;
+
+ switch (sexp_convert_item(input, output, indent))
+ {
+ case 0:
+ return 1;
+ case -1:
+ return 0;
+ case 1:
+ break;
+ }
+
+ indent = output->pos;
+
+ for (;;)
+ {
+ if (!sexp_get_token(input))
+ return 0;
+
+ if (input->token == SEXP_LIST_END
+ || input->token == SEXP_EOF
+ || input->token == SEXP_TRANSPORT_END)
+ return 1;
+
+ sexp_put_newline(output, indent);
+ sexp_convert_item(input, output, indent);
+ }
+}
+
+/* Returns 1 on success, -1 on error, and 0 at end of list/file.
+ *
+ * Should be called after getting the first token. */
+static int
+sexp_convert_item(struct sexp_input *input, struct sexp_output *output,
+ unsigned indent)
+{
+ switch(input->token)
+ {
+ case SEXP_LIST_START:
+ input->level++;
+
+ if (sexp_put_list_start(output, indent)
+ && sexp_convert_list(input, output, indent)
+ && sexp_put_list_end(output, indent))
+ {
+ if (input->level)
{
- unsigned done = base64_encode_update(&ctx,
- line,
- DATA_PER_LINE,
- string->buffer + i);
-
- assert(done <= BASE64_ENCODE_LENGTH(DATA_PER_LINE));
-
- sexp_put_data(output, done, line);
- sexp_put_newline(output);
+ input->level--;
+ if (input->token == SEXP_LIST_END)
+ return 1;
}
+ else if (input->token == SEXP_EOF)
+ return 1;
+ }
+ return -1;
+
+ case SEXP_LIST_END:
+ if (!input->level)
+ return -1;
+
+ input->level--;
+ return 1;
- output->indent = old_indent;
-
- done = base64_encode_update(&ctx, line,
- string.size - i, string->buffer + i);
- done += base64_encode_final(&ctx, line + done);
- assert(done <= sizeof(line));
+ case SEXP_EOF:
+ return input->level ? -1 : 1;
+
+ case SEXP_STRING:
+ return sexp_put_string(output, indent, &input->string) ? 1 : -1;
+
+ case SEXP_DISPLAY_START:
+ return (sexp_put_display_start(output, indent)
+ && sexp_convert_string(input, output, indent)
+ && sexp_put_display_end(output, indent)
+ && sexp_convert_string(input, output, indent)) ? 1 : -1;
- return sexp_put_data(output, done, line)
- && sexp_put_char(output, '}');
+ case SEXP_TRANSPORT_START:
+ if (input->mode != SEXP_ADVANCED)
+ return -1;
+ else
+ {
+ unsigned old_level = input->level;
+ input->mode = SEXP_TRANSPORT;
+ input->level = 0;
+
+ base64_decode_init(&input->base64);
+
+ if (!sexp_convert_list(input, output, indent))
+ return -1;
+
+ input->mode = SEXP_ADVANCED;
+ input->level = old_level;
+ return 1;
}
+ case SEXP_TRANSPORT_END:
+ if (input->mode != SEXP_TRANSPORT
+ || input->level || !base64_decode_status(&input->base64))
+ return -1;
+
+ return 0;
+ default:
+ return -1;
}
- else
- /* FIXME: Support transport mode */
- return fprintf(output->f, "%d:", string.length) > 0
- && sexp_put_string(output, string.size, string.buffer);
+ abort();
}
-static void
-sexp_put_start_list(struct sexp_output *output)
+int
+main(int argc, char **argv)
{
-}
+ struct sexp_input input;
+ struct sexp_output output;
+ sexp_input_init(&input, stdin, SEXP_ADVANCED);
+ sexp_output_init(&output, stdout, SEXP_ADVANCED);
+ return sexp_convert_list(&input, &output, 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}