diff options
-rw-r--r-- | NEWS | 4 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | lib/.gitignore | 4 | ||||
-rw-r--r-- | lib/Makefile.am | 15 | ||||
-rw-r--r-- | lib/diag.c (renamed from src/diag.c) | 77 | ||||
-rw-r--r-- | lib/forlan.c | 258 | ||||
-rw-r--r-- | lib/forlan.h | 123 | ||||
-rw-r--r-- | lib/forlangrm.y | 237 | ||||
-rw-r--r-- | lib/forlanlex.l | 137 | ||||
-rw-r--r-- | lib/libeclat.h | 33 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/cmdline.opt | 10 | ||||
-rw-r--r-- | src/config.c | 2 | ||||
-rw-r--r-- | src/eclat.c | 73 | ||||
-rw-r--r-- | src/eclat.h | 19 | ||||
-rw-r--r-- | src/error.c | 64 | ||||
-rw-r--r-- | tests/.gitignore | 1 | ||||
-rw-r--r-- | tests/Makefile.am | 2 | ||||
-rw-r--r-- | tests/forlan01.at | 77 | ||||
-rw-r--r-- | tests/testsuite.at | 4 | ||||
-rw-r--r-- | tests/tforlan.c | 100 |
21 files changed, 1090 insertions, 153 deletions
@@ -1,11 +1,11 @@ -Eclat NEWS -- history of user-visible changes. 2012-09-19 +Eclat NEWS -- history of user-visible changes. 2012-09-22 Copyright (C) 2012 Sergey Poznyakoff See the end of file for copying conditions. Please send Eclat bug reports to <gray+eclat@gnu.org.ua> -No news is good news. +Version 0.1 - No news is good news. ========================================================================= Copyright information: diff --git a/configure.ac b/configure.ac index fd22d37..b0aba5e 100644 --- a/configure.ac +++ b/configure.ac @@ -27,6 +27,8 @@ AM_SILENT_RULES([yes]) # Checks for programs. AC_PROG_CC +AC_PROG_LEX +AC_PROG_YACC AC_PROG_RANLIB # Checks for header files. diff --git a/lib/.gitignore b/lib/.gitignore new file mode 100644 index 0000000..e0d9b22 --- /dev/null +++ b/lib/.gitignore @@ -0,0 +1,4 @@ +forlangrm.c +forlangrm.h +forlangrm.output +forlanlex.c diff --git a/lib/Makefile.am b/lib/Makefile.am index 2c1d3a8..50b28a5 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -18,6 +18,12 @@ noinst_LIBRARIES=libeclat.a libeclat_a_SOURCES=\ base64.c\ + diag.c\ + forlan.c\ + forlan.h\ + forlangrm.h\ + forlangrm.y\ + forlanlex.l\ hmac_sha1.c\ libeclat.h\ q2url.c\ @@ -33,3 +39,12 @@ libeclat_a_SOURCES=\ AM_LDFLAGS = $(CURL_LIBS) INCLUDES = -I$(top_srcdir)/grecs/src/ $(CURL_CFLAGS) + +forlanlex.c: forlangrm.h +forlangrm.c forlangrm.h: forlangrm.y + +AM_YFLAGS=-tdv +AM_LFLAGS=-dvp + + + @@ -14,12 +14,27 @@ You should have received a copy of the GNU General Public License along with Eclat. If not, see <http://www.gnu.org/licenses/>. */ -#include "eclat.h" +#include "libeclat.h" +#include <string.h> +#include <sysexits.h> const char *program_name; +struct debug_category debug_category[LIBECLAT_DBG_MAX]; +int debug_avail; void -vdiag(grecs_locus_t const *locus, const char *qual, const char *fmt, va_list ap) +set_program_name(const char *arg) +{ + program_name = strrchr(arg, '/'); + if (!program_name) + program_name = arg; + else + program_name++; +} + +void +vdiag(grecs_locus_t const *locus, const char *qual, const char *fmt, + va_list ap) { if (program_name) fprintf(stderr, "%s: ", program_name); @@ -106,3 +121,61 @@ debug_printf(const char *fmt, ...) vdiag(NULL, "debug", fmt, ap); va_end(ap); } + +static struct debug_category * +find_category(const char *arg, size_t len) +{ + struct debug_category *dp; + + for (dp = debug_category; dp < debug_category + debug_avail; dp++) + if (dp->length == len && memcmp(dp->name, arg, len) == 0) + return dp; + return NULL; +} + +int +parse_debug_level(const char *arg) +{ + unsigned long lev; + char *p; + size_t len = strcspn(arg, "."); + struct debug_category *dp; + + if (arg[len] == 0) { + lev = strtoul(arg, &p, 10); + if (*p) + return -1; + for (dp = debug_category; dp < debug_category + debug_avail; + dp++) + dp->level = lev; + return 0; + } + + dp = find_category(arg, len); + if (!dp) + return -1; + + p = (char*) arg + len; + if (*p == 0) + lev = 100; + else if (*p != '.') + return -1; + else { + lev = strtoul(p + 1, &p, 10); + if (*p) + return -1; + } + dp->level = lev; + return 0; +} + +int +debug_register(char *name) +{ + if (debug_avail >= LIBECLAT_DBG_MAX) + die(EX_SOFTWARE, "no more debug slots available"); + debug_category[debug_avail].name = grecs_strdup(name); + debug_category[debug_avail].length = strlen(name); + debug_category[debug_avail].level = 0; + return debug_avail++; +} diff --git a/lib/forlan.c b/lib/forlan.c new file mode 100644 index 0000000..0854d08 --- /dev/null +++ b/lib/forlan.c @@ -0,0 +1,258 @@ +/* This file is part of Eclat. + Copyright (C) 2012 Sergey Poznyakoff. + + Eclat is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Eclat 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Eclat. If not, see <http://www.gnu.org/licenses/>. */ + +#include "libeclat.h" +#include "grecs.h" +#include "forlan.h" + +int forlan_dbg = -1; + +void +forlan_init() +{ + forlan_dbg = debug_register("forlan"); +} + +union forlan_node * +forlan_node_create(enum forlan_type type) +{ + union forlan_node *p = grecs_zalloc(sizeof(*p)); + p->type = type; + return p; +} + +static void f_dump_node(FILE *fp, union forlan_node *p, int *num, int lev); + + +static void +free_type_null(union forlan_node *p) +{ + warn("freeing undefined forlan_node"); +} + +void +dump_null(FILE *fp, union forlan_node *p, int *num, int lev) +{ + fprintf(fp, "[undefined node]\n"); +} + +static void +free_type_comp(union forlan_node *p) +{ + forlan_node_free(p->comp.node); +} +void +dump_comp(FILE *fp, union forlan_node *p, int *num, int lev) +{ + fprintf(fp, "COMP"); + if (p->comp.abs) + fprintf(fp, " ABS"); + fputc('\n', fp); + forlan_dump_node(fp, p->comp.node, num, lev + 1); +} + +static void +free_type_test(union forlan_node *p) +{ + free(p->test.comp); + free(p->test.value); +} +void +dump_test(FILE *fp, union forlan_node *p, int *num, int lev) +{ + fprintf(fp, "TEST: %s[%s]\n", p->test.comp, p->test.value); +} + +static void +free_type_func(union forlan_node *p) +{ + grecs_list_free(p->func.args); +} +void +dump_func(FILE *fp, union forlan_node *p, int *num, int lev) +{ + struct grecs_list_entry *ep; + fprintf(fp, "CALL: %s\n", p->func.fp); + if (p->func.args) + for (ep = p->func.args->head; ep; ep = ep->next) + forlan_dump_node(fp, ep->data, num, lev + 1); +} + +static void +free_type_cond(union forlan_node *p) +{ + forlan_node_free((union forlan_node *)p->cond.expr); + forlan_node_free(p->cond.iftrue); + forlan_node_free(p->cond.iffalse); +} +void +dump_cond(FILE *fp, union forlan_node *p, int *num, int lev) +{ + int n = *num; + fprintf(fp, "COND\n"); + forlan_dump_node(fp, p->cond.expr, num, lev + 1); + fprintf(fp, "%04d: %*.*sIFTRUE %04d\n", ++*num, lev, lev, "", n); + forlan_dump_node(fp, p->cond.iftrue, num, lev + 1); + fprintf(fp, "%04d: %*.*sIFFALSE %04d\n", ++*num, lev, lev, "", n); + forlan_dump_node(fp, p->cond.iffalse, num, lev + 1); +} + +static void +free_type_stmt(union forlan_node *p) +{ + forlan_node_free(p->stmt.stmt); + forlan_node_free(p->stmt.next); +} +void +dump_stmt(FILE *fp, union forlan_node *p, int *num, int lev) +{ + f_dump_node(fp, p->stmt.stmt, num, lev); + forlan_dump_node(fp, p->stmt.next, num, lev); +} + +static void +free_type_lit(union forlan_node *p) +{ + free(p->lit.string); +} +void +dump_lit(FILE *fp, union forlan_node *p, int *num, int lev) +{ + fprintf(fp, "LIT: \"%s\"\n", p->lit.string); +} + +static void +free_type_expr(union forlan_node *p) +{ + forlan_node_free(p->expr.arg[0]); + forlan_node_free(p->expr.arg[1]); +} +void +dump_expr(FILE *fp, union forlan_node *p, int *num, int lev) +{ + static char *opstr[] = { "NODE", "AND", "OR", "NOT" }; + + fprintf(fp, "%s\n", opstr[p->expr.opcode]); + forlan_dump_node(fp, p->expr.arg[0], num, lev + 1); + if (p->expr.arg[1]) + forlan_dump_node(fp, p->expr.arg[1], num, lev + 1); +} + +static void +free_type_last(union forlan_node *p) +{ +} +void +dump_last(FILE *fp, union forlan_node *p, int *num, int lev) +{ + fprintf(fp, "LAST\n"); +} + +struct forlan_node_method { + void (*f_free)(union forlan_node *); + void (*f_dump)(FILE *fp, union forlan_node *node, int *num, int lev); +}; + +static struct forlan_node_method f_tab[] = { + free_type_null, dump_null, /* Unknown/unset type */ + free_type_comp, dump_comp, /* A path component */ + free_type_test, dump_test, /* Value test (f[X]) */ + free_type_func, dump_func, /* Function call */ + free_type_cond, dump_cond, /* Conditional */ + free_type_stmt, dump_stmt, /* Statement */ + free_type_lit, dump_lit, /* Literal */ + free_type_expr, dump_expr, /* Boolean expression */ + free_type_last, dump_last, /* "last" */ +}; + +void +forlan_node_free(union forlan_node *p) +{ + if (!p) + return; + if (p->type > sizeof(f_tab) / sizeof(f_tab[0])) + abort(); + if (f_tab[p->type].f_free) + f_tab[p->type].f_free(p); + free(p); +} + +static void +stmt_list_free_entry(void *p) +{ + forlan_node_free(p); +} + +struct grecs_list * +forlan_stmt_list() +{ + struct grecs_list *lp; + + lp = grecs_list_create(); + lp->free_entry = stmt_list_free_entry; + return lp; +} + +union forlan_node * +forlan_stmt_from_list(struct grecs_list *list) +{ + union forlan_node **tail = NULL, *ret = NULL; + struct grecs_list_entry *ep; + + for (ep = list->head; ep; ep = ep->next) { + union forlan_node *sp = forlan_node_create(forlan_type_stmt); + sp->stmt.stmt = ep->data; + if (tail) + *tail = sp; + else + ret = sp; + tail = &sp->stmt.next; + } + list->free_entry = NULL; + grecs_list_free(list); + return ret; +} + +static void +f_dump_node(FILE *fp, union forlan_node *p, int *num, int lev) +{ + if (p) { + if (p->type > sizeof(f_tab) / sizeof(f_tab[0])) + abort(); + if (f_tab[p->type].f_dump) + f_tab[p->type].f_dump(fp, p, num, lev); + else + fprintf(fp, "type %d", p->type); + } else + fprintf(fp, "NULL"); +} + +void +forlan_dump_node(FILE *fp, union forlan_node *p, int *num, int lev) +{ + if (!p) + return; + ++*num; + fprintf(fp, "%04d: %*.*s", *num, lev, lev, ""); + f_dump_node(fp, p, num, lev); +} + +void +forlan_dump_tree(FILE *fp, union forlan_node *node) +{ + int n = 0; + forlan_dump_node(fp, node, &n, 0); +} diff --git a/lib/forlan.h b/lib/forlan.h new file mode 100644 index 0000000..4dadc70 --- /dev/null +++ b/lib/forlan.h @@ -0,0 +1,123 @@ +/* This file is part of Eclat. + Copyright (C) 2012 Sergey Poznyakoff. + + Eclat is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Eclat 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Eclat. If not, see <http://www.gnu.org/licenses/>. */ + +#define FORLAN_DBG_LEX 3 +#define FORLAN_DBG_GRAM 2 +#define FORLAN_DBG_EVAL 1 + +extern int forlan_dbg; + +void forlan_init(); +void forlan_lex_begin(const char *input, size_t length, + struct grecs_locus_point *pt); +void forlan_lex_end(void); +int forlan_parse(const char *input, size_t length, + struct grecs_locus_point *pt); + +union forlan_node; /* Declared below */ + +enum forlan_type { + forlan_type_null, /* Unknown/unset type */ + forlan_type_comp, /* A path component */ + forlan_type_test, /* Value test (f[X]) */ + forlan_type_func, /* Function call */ + forlan_type_cond, /* Conditional */ + forlan_type_stmt, /* Statement */ + forlan_type_lit, /* Literal */ + forlan_type_expr, /* Boolean expression */ + forlan_type_last /* Return last evaluated grecs_node */ +}; + +/* A path component */ +struct forlan_node_comp { + enum forlan_type type; + int abs; + union forlan_node *node; +}; + +/* Path test: .path.comp[value] */ +struct forlan_node_test { + enum forlan_type type; + char *comp; + char *value; +}; + +/* Function call */ +struct forlan_node_func { + enum forlan_type type; + void *fp; /* FIXME: replace with typedef */ + struct grecs_list *args; /* Arguments are struct forlan_node * */ +}; + +/* Conditional */ +struct forlan_node_cond { + enum forlan_type type; + union forlan_node *expr; /* Controlling expression */ + union forlan_node *iftrue; /* Run this if expr yields true */ + union forlan_node *iffalse; /* Run this if expr yields false */ +}; + +/* Statement or statement list */ +struct forlan_node_stmt { + enum forlan_type type; + union forlan_node *stmt; + union forlan_node *next; +}; + +/* Literal string */ +struct forlan_node_lit { + enum forlan_type type; + char *string; +}; + +/* Boolean opcodes */ +enum forlan_opcode { + forlan_opcode_node, /* Evaluate node, set 'last' */ + forlan_opcode_and, /* Boolean AND */ + forlan_opcode_or, /* Boolean OR */ + forlan_opcode_not /* Boolean NOT */ +}; + +/* Boolean expression */ +struct forlan_node_expr { + enum forlan_type type; + enum forlan_opcode opcode; + union forlan_node *arg[2]; +}; + +/* Now get all this together */ +union forlan_node { + enum forlan_type type; + struct forlan_node_comp comp; /* A path component */ + struct forlan_node_test test; /* Value test (f[X]) */ + struct forlan_node_func func; /* Function call */ + struct forlan_node_cond cond; /* Conditional */ + struct forlan_node_stmt stmt; /* Statement */ + struct forlan_node_lit lit; /* Literal */ + struct forlan_node_expr expr; /* Boolean expression */ + /* forlan_type_last needs no additional data */ +}; + +union forlan_node *forlan_node_create(enum forlan_type type); +void forlan_node_free(union forlan_node *); +struct grecs_list *forlan_stmt_list(void); +struct grecs_list *forlan_complist(void); +union forlan_node *forlan_stmt_from_list(struct grecs_list *list); + +extern union forlan_node *forlan_parse_tree; + +void forlan_dump_node(FILE *fp, union forlan_node *p, int *num, int lev); +void forlan_dump_tree(FILE *fp, union forlan_node *node); diff --git a/lib/forlangrm.y b/lib/forlangrm.y new file mode 100644 index 0000000..1b7781d --- /dev/null +++ b/lib/forlangrm.y @@ -0,0 +1,237 @@ +%{ +/* This file is part of Eclat. + Copyright (C) 2012 Sergey Poznyakoff. + + Eclat is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Eclat 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Eclat. If not, see <http://www.gnu.org/licenses/>. */ + +#include "libeclat.h" +#include <grecs.h> +#include <grecs-locus.h> +#include "forlangrm.h" +#include "forlan.h" + +static int yyerror(char *); +union forlan_node *forlan_parse_tree; +%} +%error-verbose +%locations + +%union { + char *string; + union forlan_node *node; + struct grecs_list *list; +}; + +%token <string> STRING IDENT +%token LAST IF ELSE + +%left OR +%left AND +%left NOT + +%type <node> stmt stmt_cond stmt_expr stmt_blk cond bool node comp funcall arg +%type <list> stmtlist complist arglist +%type <string> string + +%% +input : stmtlist + { + forlan_parse_tree = forlan_stmt_from_list($1); + } + ; + +stmtlist : stmt + { + $$ = forlan_stmt_list(); + grecs_list_append($$, $1); + } + | stmtlist stmt + { + grecs_list_append($1, $2); + $$ = $1; + } + ; + +stmt : stmt_cond + | stmt_expr + | stmt_blk + ; + +stmt_blk : '{' stmtlist '}' + { + $$ = forlan_stmt_from_list($2); + } + ; + +stmt_cond : IF cond stmt + { + $$ = forlan_node_create(forlan_type_cond); + $$->cond.expr = $2; + $$->cond.iftrue = $3; + $$->cond.iffalse = NULL; + } + | IF cond stmt ELSE stmt + { + $$ = forlan_node_create(forlan_type_cond); + $$->cond.expr = $2; + $$->cond.iftrue = $3; + $$->cond.iffalse = $5; + } + ; + +cond : bool + ; + +bool : node + { + $$ = forlan_node_create(forlan_type_expr); + $$->expr.opcode = forlan_opcode_node; + $$->expr.arg[0] = $1; + } + | bool AND bool + { + $$ = forlan_node_create(forlan_type_expr); + $$->expr.opcode = forlan_opcode_and; + $$->expr.arg[0] = $1; + $$->expr.arg[1] = $3; + } + | bool OR bool + { + $$ = forlan_node_create(forlan_type_expr); + $$->expr.opcode = forlan_opcode_or; + $$->expr.arg[0] = $1; + $$->expr.arg[1] = $3; + } + | NOT bool + { + $$ = forlan_node_create(forlan_type_expr); + $$->expr.opcode = forlan_opcode_not; + $$->expr.arg[0] = $2; + } + | '(' bool ')' + { + $$ = $2; + } + ; + +node : complist + { + $$ = forlan_node_create(forlan_type_comp); + $$->comp.abs = 0; + $$->comp.node = forlan_stmt_from_list($1); + } + | '.' complist + { + $$ = forlan_node_create(forlan_type_comp); + $$->comp.abs = 1; + $$->comp.node = forlan_stmt_from_list($2); + } + | LAST + { + $$ = forlan_node_create(forlan_type_last); + } + ; + +complist : comp + { + $$ = forlan_stmt_list(); + grecs_list_append($$, $1); + } + | complist '.' comp + { + grecs_list_append($1, $3); + $$ = $1; + } + ; + +comp : IDENT + { + $$ = forlan_node_create(forlan_type_lit); + $$->lit.string = $1; + } + | IDENT '[' string ']' + { + $$ = forlan_node_create(forlan_type_test); + $$->test.comp = $1; + $$->test.value = $3; + } + | funcall + ; + +string : IDENT + | STRING + ; + +funcall : IDENT '(' ')' + { + $$ = forlan_node_create(forlan_type_func); + $$->func.fp = $1; //FIXME + $$->func.args = NULL; + } + | IDENT '(' arglist ')' + { + $$ = forlan_node_create(forlan_type_func); + $$->func.fp = $1; //FIXME + $$->func.args = $3; + } + ; + +arglist : arg + { + $$ = forlan_stmt_list(); + grecs_list_append($$, $1); + } + | arglist ',' arg + { + grecs_list_append($1, $3); + $$ = $1; + } + ; + +arg : node + | STRING + { + $$ = forlan_node_create(forlan_type_lit); + $$->lit.string = $1; + } + ; + +stmt_expr : funcall ';' + ; +%% +static int +yyerror(char *s) +{ + grecs_error(&yylloc, 0, "%s", s); + return 0; +} + +int +forlan_parser() +{ + yydebug = debug_level(forlan_dbg) >= FORLAN_DBG_GRAM; + return yyparse(); +} + +int +forlan_parse(const char *input, size_t length, struct grecs_locus_point *pt) +{ + int rc; + forlan_lex_begin(input, length, pt); + rc = forlan_parser(); + forlan_lex_end(); + return rc; +} + + diff --git a/lib/forlanlex.l b/lib/forlanlex.l new file mode 100644 index 0000000..99966ed --- /dev/null +++ b/lib/forlanlex.l @@ -0,0 +1,137 @@ +%{ +/* This file is part of Eclat. + Copyright (C) 2012 Sergey Poznyakoff. + + Eclat is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Eclat 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Eclat. If not, see <http://www.gnu.org/licenses/>. */ + +#include "libeclat.h" +#include <grecs.h> +#include <grecs-locus.h> +#include "forlangrm.h" +#include "forlan.h" + +static const char *forlan_input_base; +static size_t forlan_input_len; +static size_t forlan_input_pos; + +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + do { \ + size_t __s = forlan_input_len - forlan_input_pos; \ + if (__s > max_size) \ + __s = max_size; \ + if (__s > 0) { \ + memcpy(buf, forlan_input_base, __s); \ + forlan_input_pos += __s; \ + } \ + result = __s; \ + } while(0) + +#define YY_USER_ACTION do { \ + if (YYSTATE == 0) { \ + yylloc.beg = grecs_current_locus_point; \ + yylloc.beg.col++; \ + } \ + grecs_current_locus_point.col += yyleng; \ + yylloc.end = grecs_current_locus_point; \ + } while (0); + +static int yywrap(void); + +%} + +WS [ \t\f][ \t\f]* +IDC [a-zA-Z_0-9-] +%x COMMENT ML STR +%% + /* Comments */ +"/*" BEGIN(COMMENT); +<COMMENT>[^*\n]* /* eat anything that's not a '*' */ +<COMMENT>"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */ +<COMMENT>\n grecs_locus_point_advance_line(grecs_current_locus_point); +<COMMENT>"*"+"/" BEGIN(INITIAL); +"//".* ; + /* Keywords */ +if return IF; +else return ELSE; +last return LAST; +! return NOT; +"&&" return AND; +"||" return OR; +{IDC}{IDC}* { grecs_line_begin(); + grecs_line_add(yytext, yyleng); + yylval.string = grecs_line_finish(); + return IDENT; } + /* Quoted strings */ +\"[^\\"\n]*\" { grecs_line_begin(); + grecs_line_add(yytext + 1, yyleng - 2); + yylval.string = grecs_line_finish(); + return STRING; } +\"[^\\"\n]*\\\n { BEGIN(STR); + grecs_line_begin(); + grecs_line_acc_grow_unescape_last(yytext + 1, + yyleng - 1); + grecs_locus_point_advance_line(grecs_current_locus_point); } +\"[^\\"\n]*\\. { BEGIN(STR); + grecs_line_begin(); + grecs_line_acc_grow_unescape_last(yytext + 1, + yyleng - 1); } +<STR>\"[^\\"\n]*\\\n { grecs_line_acc_grow_unescape_last(yytext, yyleng); + grecs_locus_point_advance_line(grecs_current_locus_point); } +<STR>[^\\"\n]*\\. { grecs_line_acc_grow_unescape_last(yytext, yyleng); } +<STR>[^\\"\n]*\" { BEGIN(INITIAL); + if (yyleng > 1) + grecs_line_add(yytext, yyleng - 1); + yylval.string = grecs_line_finish(); + return STRING; } + /* Other tokens */ +{WS} ; +\n { grecs_locus_point_advance_line(grecs_current_locus_point); } +[.,;{}()\[\]] return yytext[0]; +. { if (isascii(yytext[0]) && isprint(yytext[0])) + grecs_error(&yylloc, 0, + _("stray character %c"), yytext[0]); + else + grecs_error(&yylloc, 0, + _("stray character \\%03o"), + (unsigned char) yytext[0]); } +%% + +static int +yywrap() +{ + return 1; +} + +void +forlan_lex_begin(const char *input, size_t length, + struct grecs_locus_point *pt) +{ + forlan_input_base = input; + forlan_input_len = length; + grecs_current_locus_point = *pt; + yy_flex_debug = debug_level(forlan_dbg) >= FORLAN_DBG_LEX; + grecs_line_acc_create(); +} + +void +forlan_lex_end() +{ + grecs_line_acc_free(); +} + + + + + diff --git a/lib/libeclat.h b/lib/libeclat.h index 670c51b..5f37cc9 100644 --- a/lib/libeclat.h +++ b/lib/libeclat.h @@ -18,6 +18,39 @@ #include <expat.h> #include "grecs.h" +extern const char *program_name; + +struct debug_category { + const char *name; + size_t length; + int level; +}; + +extern struct debug_category debug_category[]; +#define LIBECLAT_DBG_MAX 64 + +#define debug_level(cat) ((cat >= 0 && cat < LIBECLAT_DBG_MAX) ? \ + debug_category[cat].level : 0) +#define debug(cat, lev, s) \ + do { \ + if (debug_level(cat) >= (lev)) \ + debug_printf s; \ + } while(0) + +void set_program_name(const char *arg); + +void die(int status, const char *fmt, ...); +void vdiag(grecs_locus_t const *locus, const char *qual, const char *fmt, + va_list ap); +void diag(grecs_locus_t const *locus, const char *qual, const char *fmt, ...); +void err(const char *fmt, ...); +void warn(const char *fmt, ...); +void debug_printf(const char *fmt, ...); + +int parse_debug_level(const char *arg); +int debug_register(char *name); + + void hmac_sha1(const void *text, size_t textlen, const void *key, size_t keylen, void *digest); diff --git a/src/Makefile.am b/src/Makefile.am index b5f7912..bda0584 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,7 +21,6 @@ eclat_SOURCES=\ cmdline.h\ config.c\ descrtags.c\ - diag.c\ eclat.c\ eclat.h\ startinst.c diff --git a/src/cmdline.opt b/src/cmdline.opt index 82e0e95..1f8d56d 100644 --- a/src/cmdline.opt +++ b/src/cmdline.opt @@ -208,16 +208,6 @@ END OPTIONS_END void -set_program_name(const char *arg) -{ - program_name = strrchr(arg, '/'); - if (!program_name) - program_name = arg; - else - program_name++; -} - -void parse_options(int argc, char *argv[], int *index) { GETOPT(argc, argv, *index, exit(EX_USAGE)) diff --git a/src/config.c b/src/config.c index c40d307..b718486 100644 --- a/src/config.c +++ b/src/config.c @@ -149,7 +149,7 @@ config_finish(struct grecs_node *tree) struct grecs_node *node; grecs_tree_reduce(tree, eclat_kw, GRECS_AGGR); - if (debug_level[ECLAT_DEBCAT_CONF]) { + if (debug_level(ECLAT_DEBCAT_CONF)) { grecs_print_node(tree, GRECS_NODE_FLAG_DEFAULT, stderr); fputc('\n', stdout); } diff --git a/src/eclat.c b/src/eclat.c index 3703376..86b4627 100644 --- a/src/eclat.c +++ b/src/eclat.c @@ -18,7 +18,6 @@ char *conffile = SYSCONFDIR "/eclat.conf" ; int lint_mode; -int debug_level[ECLAT_DEBCAT_MAX]; int dry_run_mode; int preprocess_only = 0; @@ -30,59 +29,23 @@ char *region_name; enum eclat_command eclat_command; -struct debug_trans { - const char *name; - size_t length; - int cat; +static char *categories[] = { + "main", + "cfgram", + "cflex", + "conf", + "curl", }; -static struct debug_trans debug_trans[] = { -#define S(s) #s, sizeof(#s)-1 - { S(main), ECLAT_DEBCAT_MAIN }, - { S(cfgram), ECLAT_DEBCAT_CFGRAM }, - { S(cflex), ECLAT_DEBCAT_CFLEX }, - { S(conf), ECLAT_DEBCAT_CONF }, - { S(curl), ECLAT_DEBCAT_CURL }, - { NULL } -}; - -static int -parse_debug_level(const char *arg) +static void +debug_init() { - unsigned long cat, lev; - char *p; - - if (isascii(*arg) && isdigit(*arg)) { - cat = strtoul(arg, &p, 10); - if (cat > ECLAT_DEBCAT_MAX) - return -1; - } else { - size_t len = strcspn(arg, "."); - struct debug_trans *dp; - - for (dp = debug_trans; dp->name; dp++) - if (dp->length == len && - memcmp(dp->name, arg, len) == 0) - break; + int i; - if (!dp->name) - return -1; - cat = dp->cat; - p = (char*) arg + len; + for (i = 0; i < sizeof(categories)/sizeof(categories[0]); i++) + debug_register(categories[i]); } - if (*p == 0) - lev = 100; - else if (*p != '.') - return -1; - else { - lev = strtoul(p + 1, &p, 10); - if (*p) - return -1; - } - debug_level[cat] = lev; - return 0; -} static void dump(const char *text, FILE *stream, unsigned char *ptr, size_t size) @@ -90,7 +53,7 @@ dump(const char *text, FILE *stream, unsigned char *ptr, size_t size) size_t i; size_t c; unsigned int width = 0x10; - int hex = debug_level[ECLAT_DEBCAT_CURL] > 2; + int hex = debug_level(ECLAT_DEBCAT_CURL) > 2; if (!hex) /* without the hex output, we can fit more on screen */ @@ -199,7 +162,7 @@ write_callback(void *ptr, size_t size, size_t nmemb, void *data) int column = XML_GetCurrentColumnNumber(parser); /* FIXME: Debugging level. */ - if (debug_level[ECLAT_DEBCAT_MAIN] > 10) { + if (debug_level(ECLAT_DEBCAT_MAIN) > 10) { dump_text(stderr, line, column, ptr, realsize); } status = XML_Parse(parser, ptr, realsize, 0); @@ -216,7 +179,6 @@ write_callback(void *ptr, size_t size, size_t nmemb, void *data) return realsize; } - #include "cmdline.h" eclat_command_handler_t handler_tab[] = { @@ -237,14 +199,15 @@ main(int argc, char **argv) struct grecs_node *xmltree; set_program_name(argv[0]); + debug_init(); config_init(); parse_options(argc, argv, &index); argc -= index; argv += index; - grecs_gram_trace(debug_level[ECLAT_DEBCAT_CFGRAM]); - grecs_lex_trace(debug_level[ECLAT_DEBCAT_CFLEX]); + grecs_gram_trace(debug_level(ECLAT_DEBCAT_CFGRAM)); + grecs_lex_trace(debug_level(ECLAT_DEBCAT_CFLEX)); if (preprocess_only) exit(grecs_preproc_run(conffile, grecs_preprocessor) ? @@ -292,9 +255,9 @@ main(int argc, char **argv) if (!curl) die(EX_UNAVAILABLE, "curl_easy_init failed"); - if (debug_level[ECLAT_DEBCAT_CURL]) { + if (debug_level(ECLAT_DEBCAT_CURL)) { curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - if (debug_level[ECLAT_DEBCAT_CURL] > 1) + if (debug_level(ECLAT_DEBCAT_CURL) > 1) curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, eclat_trace_fun); } diff --git a/src/eclat.h b/src/eclat.h index b0d46ac..e588b57 100644 --- a/src/eclat.h +++ b/src/eclat.h @@ -33,10 +33,7 @@ #define ECLAT_DEBCAT_CFLEX 2 #define ECLAT_DEBCAT_CONF 3 #define ECLAT_DEBCAT_CURL 4 -#define ECLAT_DEBCAT_MAX 5 - -extern const char *program_name; -extern int debug_level[]; +#define ECLAT_DEBCAT_FORLAN 5 extern char *endpoint; extern int use_ssl; @@ -46,20 +43,6 @@ extern char *access_file_name; extern char *access_key; extern char *secret_key; -#define debug(cat, lev, s) \ - do { \ - if (debug_level[cat] >= (lev)) \ - debug_printf s; \ - } while(0) - -void die(int status, const char *fmt, ...); -void vdiag(grecs_locus_t const *locus, const char *qual, const char *fmt, - va_list ap); -void diag(grecs_locus_t const *locus, const char *qual, const char *fmt, ...); -void err(const char *fmt, ...); -void warn(const char *fmt, ...); -void debug_printf(const char *fmt, ...); - typedef int (*config_finish_hook_t) (void*); void add_config_finish_hook(config_finish_hook_t fun, void *data); diff --git a/src/error.c b/src/error.c deleted file mode 100644 index 6c86d34..0000000 --- a/src/error.c +++ /dev/null @@ -1,64 +0,0 @@ -/* This file is part of Eclat. - Copyright (C) 2012 Sergey Poznyakoff. - - Eclat is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3, or (at your option) - any later version. - - Eclat 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Eclat. If not, see <http://www.gnu.org/licenses/>. */ - -#include "eclat.h" -#include <stdargs.h> -#include <stdio.h> - -char *program_name; - -void -diag(const char *qual, const char *fmt, va_list ap) -{ - if (program_name) - fprintf(stderr, "%s: ", program_name); - if (qual) - fprintf(stderr, "%s: ", qual); - va_start(ap, fmt); - vfprintf(stderr, ftm, ap); - va_end(ap); - fputc('\n', stderr); -} - -void -err(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - diag(NULL, ftm, ap); - va_end(ap); -} - -void -warn(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - diag("warning", ftm, ap); - va_end(ap); -} - -void -debug_printf(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - diag("debug", ftm, ap); - va_end(ap); -} diff --git a/tests/.gitignore b/tests/.gitignore index 99d3c9c..8b76ee0 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -4,6 +4,7 @@ package.m4 testsuite testsuite.dir testsuite.log +tforlan thmac turlenc txml diff --git a/tests/Makefile.am b/tests/Makefile.am index 82cae7e..4d567b3 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -40,6 +40,7 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac ## ------------ ## TESTSUITE_AT = \ + forlan01.at\ hmac01.at\ hmac02.at\ hmac03.at\ @@ -69,6 +70,7 @@ check-local: atconfig atlocal $(TESTSUITE) # $(SHELL) $(TESTSUITE) AUTOTEST_PATH=$(exec_prefix)/bin noinst_PROGRAMS = \ + tforlan\ thmac\ turlenc\ txml diff --git a/tests/forlan01.at b/tests/forlan01.at new file mode 100644 index 0000000..cf826e9 --- /dev/null +++ b/tests/forlan01.at @@ -0,0 +1,77 @@ +# This file is part of Eclat -*- Autotest -*- +# Copyright (C) 2012 Sergey Poznyakoff +# +# Eclat is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# Eclat 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Eclat. If not, see <http://www.gnu.org/licenses/>. + +AT_SETUP([dump]) +AT_KEYWORDS([forlan forlan01]) + +AT_DATA([input],[// test format for DescribeTags +if (.DescribeTagsResponse) { + if (.DescribeTagsResponse.tagSet.item.resourceId[[i-deadbeef]] && + parent(last).key[[hostname]]) + print(parent(last).value); +} else if (.Response.Errors) + error(.Response.Errors.Error.Message); +else + dump(); +]) + +AT_CHECK([tforlan -D input], +[0], +[0001: COND +0002: NODE +0003: COMP ABS +0004: LIT: "DescribeTagsResponse" +0005: IFTRUE 0001 +0006: COND +0007: AND +0008: NODE +0009: COMP ABS +0010: LIT: "DescribeTagsResponse" +0011: LIT: "tagSet" +0012: LIT: "item" +0013: TEST: resourceId[[i-deadbeef]] +0014: NODE +0015: COMP +0016: CALL: parent +0017: LAST +0018: TEST: key[[hostname]] +0019: IFTRUE 0006 +0020: CALL: print +0021: COMP +0022: CALL: parent +0023: LAST +0024: LIT: "value" +0025: IFFALSE 0006 +0026: IFFALSE 0001 +0027: COND +0028: NODE +0029: COMP ABS +0030: LIT: "Response" +0031: LIT: "Errors" +0032: IFTRUE 0027 +0033: CALL: error +0034: COMP ABS +0035: LIT: "Response" +0036: LIT: "Errors" +0037: LIT: "Error" +0038: LIT: "Message" +0039: IFFALSE 0027 +0040: CALL: dump +]) + +AT_CLEANUP + + diff --git a/tests/testsuite.at b/tests/testsuite.at index f40b8a7..81e91f4 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -29,4 +29,8 @@ m4_include([urlenc01.at]) AT_BANNER([XML Processing]) m4_include([xml01.at]) + +AT_BANNER([Forlan]) +m4_include([forlan01.at]) + # End of testsuite.at diff --git a/tests/tforlan.c b/tests/tforlan.c new file mode 100644 index 0000000..9fc3495 --- /dev/null +++ b/tests/tforlan.c @@ -0,0 +1,100 @@ +/* This file is part of Eclat. + Copyright (C) 2012 Sergey Poznyakoff. + + Eclat is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Eclat 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Eclat. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#ifdef HAVE_GETOPT_H +# include <getopt.h> +#endif +#include <errno.h> +#include <sysexits.h> +#include <libeclat.h> +#include "forlan.h" +#include <sys/stat.h> + +void +usage() +{ + printf("usage: %s [-dD] FILE [INPUT]\n"); +} + +int +main(int argc, char **argv) +{ + FILE *fp; + char *buf; + size_t len; + struct stat st; + struct grecs_locus_point pt; + int rc; + int dump_option = 0; + + set_program_name(argv[0]); + forlan_init(); + + while ((rc = getopt(argc, argv, "Dd:h")) != EOF) + switch (rc) { + case 'D': + dump_option++; + break; + + case 'd': + if (parse_debug_level(optarg)) + die(EX_USAGE, "bad debug category or level"); + break; + + case 'h': + usage(); + return 0; + + default: + exit(EX_USAGE); + } + argc -= optind; + argv += optind; + + + if (argc == 0 || argc > 2) + die(EX_USAGE, "one or two arguments expected"); + if (stat(argv[0], &st)) + die(EX_UNAVAILABLE, "cannot stat input file \"%s\": %s", + argv[0], strerror(errno)); + len = st.st_size; + buf = grecs_malloc(len); + fp = fopen(argv[0], "r"); + if (!fp) + die(EX_UNAVAILABLE, "cannot open input file \"%s\": %s", + argv[0], strerror(errno)); + if (fread(buf, len, 1, fp) != 1) + die(EX_UNAVAILABLE, "error reading from \"%s\": %s", + argv[0], strerror(errno)); + fclose(fp); + + pt.file = argv[0]; + pt.line = 1; + pt.col = 0; + + rc = forlan_parse(buf, len, &pt); + if (rc == 0) { + if (dump_option) + forlan_dump_tree(stdout, forlan_parse_tree); + forlan_node_free(forlan_parse_tree); + } + return rc ? EX_UNAVAILABLE : 0; +} + |