From 1e65247eb20f9b704b4d49a7bd90add4dcff4dae Mon Sep 17 00:00:00 2001 From: Florian Forster Date: Mon, 14 Jun 2010 20:40:30 +0200 Subject: [PATCH] Import liboconfig from collectd. --- Makefile | 16 +++- aux_types.h | 18 +++++ oconfig.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ oconfig.h | 69 ++++++++++++++++++ parser.y | 239 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ scanner.l | 137 ++++++++++++++++++++++++++++++++++ 6 files changed, 695 insertions(+), 1 deletion(-) create mode 100644 aux_types.h create mode 100644 oconfig.c create mode 100644 oconfig.h create mode 100644 parser.y create mode 100644 scanner.l diff --git a/Makefile b/Makefile index c1d879f..130a98a 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,8 @@ clean: common.o: common.c common.h +graph_config.o: graph_config.c graph_config.h + graph_ident.o: graph_ident.c graph_ident.h graph_def.o: graph_def.c graph_def.h @@ -25,10 +27,22 @@ action_graph.o: action_graph.c action_graph.h action_list_graphs.o: action_list_graphs.c action_list_graphs.h +oconfig.o: oconfig.c oconfig.h + +scanner.c: scanner.l + flex --outfile=scanner.c scanner.l + +scanner.o: scanner.c parser.h + +parser.c parser.h: parser.y + bison --output=parser.c --defines=parser.h parser.y + +parser.o: parser.c + test: test.c utils_params.o test.fcgi: LDLIBS = -lfcgi -lrrd -test.fcgi: test.fcgi.c common.o graph_ident.o graph_def.o graph_list.o utils_array.o utils_params.o action_graph.o action_list_graphs.o +test.fcgi: test.fcgi.c common.o graph_config.o graph_ident.o graph_def.o graph_list.o utils_array.o utils_params.o action_graph.o action_list_graphs.o scanner.o parser.o oconfig.o .PHONY: clean diff --git a/aux_types.h b/aux_types.h new file mode 100644 index 0000000..25b81ab --- /dev/null +++ b/aux_types.h @@ -0,0 +1,18 @@ +#ifndef AUX_TYPES_H +#define AUX_TYPES_H 1 + +struct statement_list_s +{ + oconfig_item_t *statement; + int statement_num; +}; +typedef struct statement_list_s statement_list_t; + +struct argument_list_s +{ + oconfig_value_t *argument; + int argument_num; +}; +typedef struct argument_list_s argument_list_t; + +#endif /* AUX_TYPES_H */ diff --git a/oconfig.c b/oconfig.c new file mode 100644 index 0000000..629775a --- /dev/null +++ b/oconfig.c @@ -0,0 +1,217 @@ +/** + * oconfig - src/oconfig.c + * Copyright (C) 2006,2007 Florian octo Forster + * + * This program 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; only version 2 of the License is applicable. + * + * This program 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 + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +#include "oconfig.h" + +extern FILE *yyin; + +oconfig_item_t *ci_root; +const char *c_file; + +static void yyset_in (FILE *fd) +{ + yyin = fd; +} /* void yyset_in */ + +oconfig_item_t *oconfig_parse_fh (FILE *fh) +{ + int status; + oconfig_item_t *ret; + + char file[10]; + + yyset_in (fh); + + if (NULL == c_file) { + int status; + + status = snprintf (file, sizeof (file), "", fileno (fh)); + + if ((status < 0) || (status >= sizeof (file))) { + c_file = ""; + } + else { + file[sizeof (file) - 1] = '\0'; + c_file = file; + } + } + + status = yyparse (); + if (status != 0) + { + fprintf (stderr, "yyparse returned error #%i\n", status); + return (NULL); + } + + c_file = NULL; + + ret = ci_root; + ci_root = NULL; + yyset_in ((FILE *) 0); + + return (ret); +} /* oconfig_item_t *oconfig_parse_fh */ + +oconfig_item_t *oconfig_parse_file (const char *file) +{ + FILE *fh; + oconfig_item_t *ret; + + c_file = file; + + fh = fopen (file, "r"); + if (fh == NULL) + { + fprintf (stderr, "fopen (%s) failed: %s\n", file, strerror (errno)); + return (NULL); + } + + ret = oconfig_parse_fh (fh); + fclose (fh); + + c_file = NULL; + + return (ret); +} /* oconfig_item_t *oconfig_parse_file */ + +oconfig_item_t *oconfig_clone (const oconfig_item_t *ci_orig) +{ + oconfig_item_t *ci_copy; + + ci_copy = (oconfig_item_t *) malloc (sizeof (*ci_copy)); + if (ci_copy == NULL) + { + fprintf (stderr, "malloc failed.\n"); + return (NULL); + } + memset (ci_copy, 0, sizeof (*ci_copy)); + ci_copy->values = NULL; + ci_copy->parent = NULL; + ci_copy->children = NULL; + + ci_copy->key = strdup (ci_orig->key); + if (ci_copy->key == NULL) + { + fprintf (stderr, "strdup failed.\n"); + free (ci_copy); + return (NULL); + } + + if (ci_orig->values_num > 0) /* {{{ */ + { + int i; + + ci_copy->values = (oconfig_value_t *) calloc (ci_orig->values_num, + sizeof (*ci_copy->values)); + if (ci_copy->values == NULL) + { + fprintf (stderr, "calloc failed.\n"); + free (ci_copy->key); + free (ci_copy); + return (NULL); + } + ci_copy->values_num = ci_orig->values_num; + + for (i = 0; i < ci_copy->values_num; i++) + { + ci_copy->values[i].type = ci_orig->values[i].type; + if (ci_copy->values[i].type == OCONFIG_TYPE_STRING) + { + ci_copy->values[i].value.string + = strdup (ci_orig->values[i].value.string); + if (ci_copy->values[i].value.string == NULL) + { + fprintf (stderr, "strdup failed.\n"); + oconfig_free (ci_copy); + return (NULL); + } + } + else /* ci_copy->values[i].type != OCONFIG_TYPE_STRING) */ + { + ci_copy->values[i].value = ci_orig->values[i].value; + } + } + } /* }}} if (ci_orig->values_num > 0) */ + + if (ci_orig->children_num > 0) /* {{{ */ + { + int i; + + ci_copy->children = (oconfig_item_t *) calloc (ci_orig->children_num, + sizeof (*ci_copy->children)); + if (ci_copy->children == NULL) + { + fprintf (stderr, "calloc failed.\n"); + oconfig_free (ci_copy); + return (NULL); + } + ci_copy->children_num = ci_orig->children_num; + + for (i = 0; i < ci_copy->children_num; i++) + { + oconfig_item_t *child; + + child = oconfig_clone (ci_orig->children + i); + if (child == NULL) + { + oconfig_free (ci_copy); + return (NULL); + } + child->parent = ci_copy; + ci_copy->children[i] = *child; + free (child); + } /* for (i = 0; i < ci_copy->children_num; i++) */ + } /* }}} if (ci_orig->children_num > 0) */ + + return (ci_copy); +} /* oconfig_item_t *oconfig_clone */ + +void oconfig_free (oconfig_item_t *ci) +{ + int i; + + if (ci == NULL) + return; + + if (ci->key != NULL) + free (ci->key); + + for (i = 0; i < ci->values_num; i++) + if ((ci->values[i].type == OCONFIG_TYPE_STRING) + && (NULL != ci->values[i].value.string)) + free (ci->values[i].value.string); + + if (ci->values != NULL) + free (ci->values); + + for (i = 0; i < ci->children_num; i++) + oconfig_free (ci->children + i); + + if (ci->children != NULL) + free (ci->children); +} + +/* + * vim:shiftwidth=2:tabstop=8:softtabstop=2:fdm=marker + */ diff --git a/oconfig.h b/oconfig.h new file mode 100644 index 0000000..70fc623 --- /dev/null +++ b/oconfig.h @@ -0,0 +1,69 @@ +#ifndef OCONFIG_H +#define OCONFIG_H 1 + +#include + +/** + * oconfig - src/oconfig.h + * Copyright (C) 2006-2009 Florian octo Forster + * + * This program 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; only version 2 of the License is applicable. + * + * This program 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 + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * Types + */ +#define OCONFIG_TYPE_STRING 0 +#define OCONFIG_TYPE_NUMBER 1 +#define OCONFIG_TYPE_BOOLEAN 2 + +struct oconfig_value_s +{ + union + { + char *string; + double number; + int boolean; + } value; + int type; +}; +typedef struct oconfig_value_s oconfig_value_t; + +struct oconfig_item_s; +typedef struct oconfig_item_s oconfig_item_t; +struct oconfig_item_s +{ + char *key; + oconfig_value_t *values; + int values_num; + + oconfig_item_t *parent; + oconfig_item_t *children; + int children_num; +}; + +/* + * Functions + */ +oconfig_item_t *oconfig_parse_fh (FILE *fh); +oconfig_item_t *oconfig_parse_file (const char *file); + +oconfig_item_t *oconfig_clone (const oconfig_item_t *ci); + +void oconfig_free (oconfig_item_t *ci); + +/* + * vim: shiftwidth=2:tabstop=8:softtabstop=2 + */ +#endif /* OCONFIG_H */ diff --git a/parser.y b/parser.y new file mode 100644 index 0000000..5b7aa94 --- /dev/null +++ b/parser.y @@ -0,0 +1,239 @@ +/** + * oconfig - src/parser.y + * Copyright (C) 2007,2008 Florian octo Forster + * + * This program 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; only version 2 of the License is applicable. + * + * This program 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 + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +%{ +#include +#include +#include "oconfig.h" +#include "aux_types.h" + +static char *unquote (const char *orig); +static int yyerror (const char *s); + +/* Lexer variables */ +extern int yylineno; +extern char *yytext; + +extern oconfig_item_t *ci_root; +extern char *c_file; +%} + +%start entire_file + +%union { + double number; + int boolean; + char *string; + oconfig_value_t cv; + oconfig_item_t ci; + argument_list_t al; + statement_list_t sl; +} + +%token NUMBER +%token BTRUE BFALSE +%token QUOTED_STRING UNQUOTED_STRING +%token SLASH OPENBRAC CLOSEBRAC EOL + +%type string +%type identifier +/* arguments */ +%type argument +%type argument_list +/* blocks */ +%type block_begin +%type block +%type block_end +/* statements */ +%type option +%type statement +%type statement_list +%type entire_file + +/* pass an verbose, specific error message to yyerror() */ +%error-verbose + +%% +string: + QUOTED_STRING {$$ = unquote ($1);} + | UNQUOTED_STRING {$$ = strdup ($1);} + ; + +argument: + NUMBER {$$.value.number = $1; $$.type = OCONFIG_TYPE_NUMBER;} + | BTRUE {$$.value.boolean = 1; $$.type = OCONFIG_TYPE_BOOLEAN;} + | BFALSE {$$.value.boolean = 0; $$.type = OCONFIG_TYPE_BOOLEAN;} + | string {$$.value.string = $1; $$.type = OCONFIG_TYPE_STRING;} + ; + +argument_list: + argument_list argument + { + $$ = $1; + $$.argument_num++; + $$.argument = realloc ($$.argument, $$.argument_num * sizeof (oconfig_value_t)); + $$.argument[$$.argument_num-1] = $2; + } + | argument + { + $$.argument = malloc (sizeof (oconfig_value_t)); + $$.argument[0] = $1; + $$.argument_num = 1; + } + ; + +identifier: + UNQUOTED_STRING {$$ = strdup ($1);} + ; + +option: + identifier argument_list EOL + { + memset (&$$, '\0', sizeof ($$)); + $$.key = $1; + $$.values = $2.argument; + $$.values_num = $2.argument_num; + } + ; + +block_begin: + OPENBRAC identifier CLOSEBRAC EOL + { + memset (&$$, '\0', sizeof ($$)); + $$.key = $2; + } + | + OPENBRAC identifier argument_list CLOSEBRAC EOL + { + memset (&$$, '\0', sizeof ($$)); + $$.key = $2; + $$.values = $3.argument; + $$.values_num = $3.argument_num; + } + ; + +block_end: + OPENBRAC SLASH identifier CLOSEBRAC EOL + { + $$ = $3; + } + ; + +block: + block_begin statement_list block_end + { + if (strcmp ($1.key, $3) != 0) + { + printf ("block_begin = %s; block_end = %s;\n", $1.key, $3); + yyerror ("Block not closed..\n"); + exit (1); + } + free ($3); $3 = NULL; + $$ = $1; + $$.children = $2.statement; + $$.children_num = $2.statement_num; + } + ; + +statement: + option {$$ = $1;} + | block {$$ = $1;} + | EOL {$$.values_num = 0;} + ; + +statement_list: + statement_list statement + { + $$ = $1; + if (($2.values_num > 0) || ($2.children_num > 0)) + { + $$.statement_num++; + $$.statement = realloc ($$.statement, $$.statement_num * sizeof (oconfig_item_t)); + $$.statement[$$.statement_num-1] = $2; + } + } + | statement + { + if (($1.values_num > 0) || ($1.children_num > 0)) + { + $$.statement = malloc (sizeof (oconfig_item_t)); + $$.statement[0] = $1; + $$.statement_num = 1; + } + else + { + $$.statement = NULL; + $$.statement_num = 0; + } + } + ; + +entire_file: + statement_list + { + ci_root = malloc (sizeof (oconfig_item_t)); + memset (ci_root, '\0', sizeof (oconfig_item_t)); + ci_root->children = $1.statement; + ci_root->children_num = $1.statement_num; + } + ; + +%% +static int yyerror (const char *s) +{ + char *text; + + if (*yytext == '\n') + text = ""; + else + text = yytext; + + fprintf (stderr, "Parse error in file `%s', line %i near `%s': %s\n", + c_file, yylineno, text, s); + return (-1); +} /* int yyerror */ + +static char *unquote (const char *orig) +{ + char *ret = strdup (orig); + int len; + int i; + + if (ret == NULL) + return (NULL); + + len = strlen (ret); + + if ((len < 2) || (ret[0] != '"') || (ret[len - 1] != '"')) + return (ret); + + len -= 2; + memmove (ret, ret + 1, len); + ret[len] = '\0'; + + for (i = 0; i < len; i++) + { + if (ret[i] == '\\') + { + memmove (ret + i, ret + (i + 1), len - i); + len--; + } + } + + return (ret); +} /* char *unquote */ diff --git a/scanner.l b/scanner.l new file mode 100644 index 0000000..9f0cd8e --- /dev/null +++ b/scanner.l @@ -0,0 +1,137 @@ +/** + * oconfig - src/scanner.l + * Copyright (C) 2007 Florian octo Forster + * Copyright (C) 2008 Sebastian tokkee Harl + * + * This program 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; only version 2 of the License is applicable. + * + * This program 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 + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +%{ +#include +#include "oconfig.h" +#include "aux_types.h" +#include "parser.h" + +/* multiline string buffer */ +static char *ml_buffer = NULL; +static int ml_pos = 0; +static int ml_len = 0; + +#define ml_free (ml_len - ml_pos) + +static void ml_append (char *); + +#ifdef yyterminate +# undef yyterminate +#endif +#define yyterminate() \ + do { free (ml_buffer); ml_buffer = NULL; ml_pos = 0; ml_len = 0; \ + return YY_NULL; } while (0) +%} +%option yylineno +%option noyywrap +%x ML +WHITE_SPACE [\ \t\b] +NON_WHITE_SPACE [^\ \t\b] +EOL (\r\n|\n) +QUOTED_STRING ([^\\"]+|\\.)* +UNQUOTED_STRING [0-9A-Za-z_]+ +HEX_NUMBER 0[xX][0-9a-fA-F]+ +OCT_NUMBER 0[0-7]+ +DEC_NUMBER [\+\-]?[0-9]+ +FLOAT_NUMBER [\+\-]?[0-9]*\.[0-9]+([eE][\+\-][0-9]+)? +NUMBER ({FLOAT_NUMBER}|{HEX_NUMBER}|{OCT_NUMBER}|{DEC_NUMBER}) +BOOL_TRUE (true|yes|on) +BOOL_FALSE (false|no|off) +COMMENT #.* +PORT (6(5(5(3[0-5]|[0-2][0-9])|[0-4][0-9][0-9])|[0-4][0-9][0-9][0-9])|[1-5][0-9][0-9][0-9][0-9]|[1-9][0-9]?[0-9]?[0-9]?) +IP_BYTE (2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]) +IPV4_ADDR {IP_BYTE}\.{IP_BYTE}\.{IP_BYTE}\.{IP_BYTE}(:{PORT})? + +%% +{WHITE_SPACE} | +{COMMENT} {/* ignore */} + +\\{EOL} {/* continue line */} + +{EOL} {return (EOL);} +"/" {return (SLASH);} +"<" {return (OPENBRAC);} +">" {return (CLOSEBRAC);} +{BOOL_TRUE} {yylval.boolean = 1; return (BTRUE);} +{BOOL_FALSE} {yylval.boolean = 0; return (BFALSE);} + +{IPV4_ADDR} {yylval.string = yytext; return (UNQUOTED_STRING);} + +{NUMBER} {yylval.number = strtod (yytext, NULL); return (NUMBER);} + +\"{QUOTED_STRING}\" {yylval.string = yytext; return (QUOTED_STRING);} +{UNQUOTED_STRING} {yylval.string = yytext; return (UNQUOTED_STRING);} + +\"{QUOTED_STRING}\\{EOL} { + int len = strlen (yytext); + + ml_pos = 0; + + /* remove "\\" */ + if ('\r' == yytext[len - 2]) + len -= 3; + else + len -= 2; + yytext[len] = '\0'; + + ml_append (yytext); + BEGIN (ML); +} +^{WHITE_SPACE}+ {/* remove leading white-space */} +{NON_WHITE_SPACE}{QUOTED_STRING}\\{EOL} { + int len = strlen (yytext); + + /* remove "\\" */ + if ('\r' == yytext[len - 2]) + len -= 3; + else + len -= 2; + yytext[len] = '\0'; + + ml_append(yytext); +} +{NON_WHITE_SPACE}{QUOTED_STRING}\" { + ml_append(yytext); + yylval.string = ml_buffer; + + BEGIN (INITIAL); + return (QUOTED_STRING); +} +%% +static void ml_append (char *string) +{ + int len = strlen (string); + int s; + + if (ml_free <= len) { + ml_len += len - ml_free + 1; + ml_buffer = (char *)realloc (ml_buffer, ml_len); + if (NULL == ml_buffer) + YY_FATAL_ERROR ("out of dynamic memory in ml_append"); + } + + s = snprintf (ml_buffer + ml_pos, ml_free, "%s", string); + if ((0 > s) || (ml_free <= s)) + YY_FATAL_ERROR ("failed to write to multiline buffer"); + + ml_pos += s; + return; +} /* ml_append */ + -- 2.11.0