Source to ./parser.c
/*
* Cisco router simulation platform.
* Copyright (c) 2006 Christophe Fillot ([email protected])
*
* Mini-parser.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
#include <stdarg.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "utils.h"
#include "parser.h"
#define TOKEN_MAX_SIZE 512
/* Character types */
enum {
PARSER_CHAR_BLANK,
PARSER_CHAR_NEWLINE,
PARSER_CHAR_COMMENT,
PARSER_CHAR_QUOTE,
PARSER_CHAR_OTHER,
};
/* Get a description given an error code */
char *parser_strerror(parser_context_t *ctx)
{
printf("error = %d\n",ctx->error);
switch(ctx->error) {
case 0:
return "no error";
case PARSER_ERROR_NOMEM:
return "insufficient memory";
case PARSER_ERROR_UNEXP_QUOTE:
return "unexpected quote";
case PARSER_ERROR_UNEXP_EOL:
return "unexpected end of line";
default:
return "unknown error";
}
}
/* Dump a token list */
void parser_dump_tokens(parser_context_t *ctx)
{
parser_token_t *tok;
for(tok=ctx->tok_head;tok;tok=tok->next)
printf("\"%s\" ",tok->value);
}
/* Map a token list to an array */
char **parser_map_array(parser_context_t *ctx)
{
parser_token_t *tok;
char **map;
int i;
if (ctx->tok_count <= 0)
return NULL;
if (!(map = calloc(ctx->tok_count,sizeof(char **))))
return NULL;
for(i=0,tok=ctx->tok_head;(i<ctx->tok_count) && tok;i++,tok=tok->next)
map[i] = tok->value;
return map;
}
/* Add a character to temporary token (resize if necessary) */
static int tmp_token_add_char(parser_context_t *ctx,char c)
{
size_t new_size;
char *new_str;
if (!ctx->tmp_tok || (ctx->tmp_cur_len == (ctx->tmp_tot_len - 1))) {
new_size = ctx->tmp_tot_len + TOKEN_MAX_SIZE;
new_str = realloc(ctx->tmp_tok,new_size);
if (!new_str)
return(-1);
ctx->tmp_tok = new_str;
ctx->tmp_tot_len = new_size;
}
ctx->tmp_tok[ctx->tmp_cur_len++] = c;
ctx->tmp_tok[ctx->tmp_cur_len] = 0;
return(0);
}
/* Move current token to the active token list */
static int parser_move_tmp_token(parser_context_t *ctx)
{
parser_token_t *tok;
/* no token ... */
if (!ctx->tmp_tok)
return(0);
if (!(tok = malloc(sizeof(*tok))))
return(-1);
tok->value = ctx->tmp_tok;
tok->next = NULL;
/* add it to the token list */
if (ctx->tok_last != NULL)
ctx->tok_last->next = tok;
else
ctx->tok_head = tok;
ctx->tok_last = tok;
ctx->tok_count++;
/* start a new token */
ctx->tmp_tok = NULL;
ctx->tmp_tot_len = ctx->tmp_cur_len = 0;
return(0);
}
/* Initialize parser context */
void parser_context_init(parser_context_t *ctx)
{
ctx->tok_head = ctx->tok_last = NULL;
ctx->tok_count = 0;
ctx->tmp_tok = NULL;
ctx->tmp_tot_len = ctx->tmp_cur_len = 0;
ctx->state = PARSER_STATE_BLANK;
ctx->error = 0;
ctx->consumed_len = 0;
}
/* Free a token list */
void parser_free_tokens(parser_token_t *tok_list)
{
parser_token_t *t,*next;
for(t=tok_list;t;t=next) {
next = t->next;
free(t->value);
free(t);
}
}
/* Free memory used by a parser context */
void parser_context_free(parser_context_t *ctx)
{
parser_free_tokens(ctx->tok_head);
if (ctx->tmp_tok != NULL)
free(ctx->tmp_tok);
parser_context_init(ctx);
}
/* Determine the type of the input character */
static int parser_get_char_type(u_char c)
{
switch(c) {
case '\n':
case '\r':
case 0:
return(PARSER_CHAR_NEWLINE);
case '\t':
//case '\r':
case ' ':
return(PARSER_CHAR_BLANK);
case '!':
case '#':
return(PARSER_CHAR_COMMENT);
case '"':
return(PARSER_CHAR_QUOTE);
default:
return(PARSER_CHAR_OTHER);
}
}
/* Send a buffer to the tokenizer */
int parser_scan_buffer(parser_context_t *ctx,char *buf,size_t buf_size)
{
int i,type;
u_char c;
for(i=0;(i<buf_size) && (ctx->state != PARSER_STATE_DONE);i++)
{
ctx->consumed_len++;
c = buf[i];
/* Determine character type */
type = parser_get_char_type(c);
/* Basic finite state machine */
switch(ctx->state) {
case PARSER_STATE_SKIP:
if (type == PARSER_CHAR_NEWLINE)
ctx->state = PARSER_STATE_DONE;
/* Simply ignore character until we reach end of line */
break;
case PARSER_STATE_BLANK:
switch(type) {
case PARSER_CHAR_BLANK:
/* Eat space */
break;
case PARSER_CHAR_COMMENT:
ctx->state = PARSER_STATE_SKIP;
break;
case PARSER_CHAR_NEWLINE:
ctx->state = PARSER_STATE_DONE;
break;
case PARSER_CHAR_QUOTE:
ctx->state = PARSER_STATE_QUOTED_STRING;
break;
default:
/* Begin a new string */
if (!tmp_token_add_char(ctx,c)) {
ctx->state = PARSER_STATE_STRING;
} else {
ctx->state = PARSER_STATE_SKIP;
ctx->error = PARSER_ERROR_NOMEM;
}
}
break;
case PARSER_STATE_STRING:
switch(type) {
case PARSER_CHAR_BLANK:
if (!parser_move_tmp_token(ctx)) {
ctx->state = PARSER_STATE_BLANK;
} else {
ctx->state = PARSER_STATE_SKIP;
ctx->error = PARSER_ERROR_NOMEM;
}
break;
case PARSER_CHAR_NEWLINE:
if (parser_move_tmp_token(ctx) == -1)
ctx->error = PARSER_ERROR_NOMEM;
ctx->state = PARSER_STATE_DONE;
break;
case PARSER_CHAR_COMMENT:
if (parser_move_tmp_token(ctx) == -1)
ctx->error = PARSER_ERROR_NOMEM;
ctx->state = PARSER_STATE_SKIP;
break;
case PARSER_CHAR_QUOTE:
ctx->error = PARSER_ERROR_UNEXP_QUOTE;
ctx->state = PARSER_STATE_SKIP;
break;
default:
/* Add the character to the buffer */
if (tmp_token_add_char(ctx,c) == -1) {
ctx->state = PARSER_STATE_SKIP;
ctx->error = PARSER_ERROR_NOMEM;
}
}
break;
case PARSER_STATE_QUOTED_STRING:
switch(type) {
case PARSER_CHAR_NEWLINE:
/* Unterminated string! */
ctx->error = PARSER_ERROR_UNEXP_EOL;
ctx->state = PARSER_STATE_DONE;
break;
case PARSER_CHAR_QUOTE:
if (!parser_move_tmp_token(ctx)) {
ctx->state = PARSER_STATE_BLANK;
} else {
ctx->state = PARSER_STATE_SKIP;
ctx->error = PARSER_ERROR_NOMEM;
}
break;
default:
/* Add the character to the buffer */
if (tmp_token_add_char(ctx,c) == -1) {
ctx->state = PARSER_STATE_SKIP;
ctx->error = PARSER_ERROR_NOMEM;
}
}
break;
}
}
return(ctx->state == PARSER_STATE_DONE);
}
/* Parser tests */
static char *parser_test_str[] = {
"c7200 show_hardware R1",
"c7200 show_hardware \"R1\"",
" c7200 show_hardware \"R1\" ",
"\"c7200\" \"show_hardware\" \"R1\"",
"hypervisor set_working_dir \"C:\\Program Files\\Dynamips Test\"",
"hypervisor # This is a comment set_working_dir \"C:\\Program Files\"",
"\"c7200\" \"show_hardware\" \"R1",
NULL,
};
void parser_run_tests(void)
{
parser_context_t ctx;
int i,res;
for(i=0;parser_test_str[i];i++) {
parser_context_init(&ctx);
res = parser_scan_buffer(&ctx,parser_test_str[i],
strlen(parser_test_str[i])+1);
printf("\n%d: Test string: [%s] => res=%d, state=%d\n",
i,parser_test_str[i],res,ctx.state);
if ((res != 0) && (ctx.error == 0)) {
if (ctx.tok_head) {
printf("Tokens: ");
parser_dump_tokens(&ctx);
printf("\n");
}
}
parser_context_free(&ctx);
}
}