blob: 18df8003b84f1044de93176aa52ff2084e2f86e2 [file] [log] [blame]
%code top {
#pragma GCC diagnostic ignored "-Wunused-value"
#pragma GCC diagnostic ignored "-Wunused-variable"
#include "config.h"
#include "config_parsing.h"
#include "analyse.h"
#include "abstract_mem.h"
#include <stdio.h>
#include "log.h"
#if HAVE_STRING_H
# include <string.h>
#endif
}
/* Options and variants */
%pure-parser
%lex-param {struct parser_state *st}
%parse-param {struct parser_state *st}
%locations
%code requires {
/* alert the parser that we have our own definition */
# define YYLTYPE_IS_DECLARED 1
}
%union {
char *token;
struct config_node *node;
}
%code provides {
typedef struct YYLTYPE {
int first_line;
int first_column;
int last_line;
int last_column;
char *filename;
} YYLTYPE;
# define YYLLOC_DEFAULT(Current, Rhs, N) \
do \
if (N) \
{ \
(Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
(Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
(Current).last_line = YYRHSLOC (Rhs, N).last_line; \
(Current).last_column = YYRHSLOC (Rhs, N).last_column; \
(Current).filename = YYRHSLOC (Rhs, 1).filename; \
} \
else \
{ /* empty RHS */ \
(Current).first_line = (Current).last_line = \
YYRHSLOC (Rhs, 0).last_line; \
(Current).first_column = (Current).last_column = \
YYRHSLOC (Rhs, 0).last_column; \
(Current).filename = NULL; /* new */ \
} \
while (0)
int ganeshun_yylex(YYSTYPE *yylval_param,
YYLTYPE *yylloc_param,
void *scanner);
int ganesha_yylex(YYSTYPE *yylval_param,
YYLTYPE *yylloc_param,
struct parser_state *st);
void config_parse_error(YYLTYPE *yylloc_param,
struct parser_state *st,
char *format, ...);
void ganesha_yyerror(YYLTYPE *yylloc_param,
void *yyscanner,
char*);
struct config_node *config_block(char *blockname,
struct config_node *list,
YYLTYPE *yylloc_param,
struct parser_state *st);
void link_node(struct config_node *node);
struct config_node *link_sibling(struct config_node *first,
struct config_node *second);
struct config_node *config_stmt(char *varname,
struct config_node *exprlist,
YYLTYPE *yylloc_param,
struct parser_state *st);
struct config_node *config_term(char *opcode,
char *varval,
enum term_type type,
YYLTYPE *yylloc_param,
struct parser_state *st);
}
%token _ERROR_
%token LCURLY_OP
%token RCURLY_OP
%token EQUAL_OP
%token COMMA_OP
%token SEMI_OP
%token <token> IDENTIFIER
%token <token> STRING
%token <token> DQUOTE
%token <token> SQUOTE
%token <token> TOKEN
%token <token> REGEX_TOKEN
%token <token> TOK_PATH
%token <token> TOK_TRUE
%token <token> TOK_FALSE
%token <token> TOK_DECNUM
%token <token> TOK_HEXNUM
%token <token> TOK_OCTNUM
%token <token> TOK_ARITH_OP
%token <token> TOK_V4_ANY
%token <token> TOK_V4ADDR
%token <token> TOK_V4CIDR
%token <token> TOK_V6ADDR
%token <token> TOK_V6CIDR
%token <token> TOK_FSID
%token <token> TOK_NETGROUP
%type <node> deflist
%type <node> definition
%type <node> block
%type <node> statement
%type <node> exprlist
%type <node> expression
%start config
%%
config:
{ /* empty */
config_parse_error(&yyloc, st, "Empty configuration file");
}
| deflist
{
if ($1 != NULL)
glist_add_tail(&($1)->node, &st->root_node->root.u.nterm.sub_nodes);
link_node(&st->root_node->root);
}
;
deflist:
{ /* empty */
$$ = NULL;
}
| definition
{
$$ = $1;
}
| deflist definition
{
$$ = link_sibling($1, $2);
}
;
/* definition: statement | block ; */
definition:
IDENTIFIER EQUAL_OP statement
{
$$ = config_stmt($1, $3, &@$, st);
}
| IDENTIFIER LCURLY_OP block
{
$$=config_block($1, $3, &@$, st);
}
;
statement:
exprlist SEMI_OP
{
$$ = $1;
}
| error SEMI_OP
{
config_parse_error(&@$, st, "Syntax error in statement");
yyerrok;
$$ = NULL;
}
;
block:
{ /* empty */
$$ = NULL;
}
| deflist RCURLY_OP
{
$$ = $1;
}
| error RCURLY_OP
{
config_parse_error(&@$, st, "Syntax error in block");
yyerrok;
$$ = NULL;
}
;
/* A statement is a comma separated sequence of option/value tokens
*/
exprlist:
{ /* empty */
$$ = NULL;
}
| expression
{
$$ = $1;
}
| exprlist COMMA_OP expression
{
$$ = link_sibling($1, $3);
}
;
expression: /* empty */ {
$$ = NULL;
}
| TOK_PATH
{
$$ = config_term(NULL, $1, TERM_PATH, &@$, st);
}
| TOKEN
{
$$ = config_term(NULL, $1, TERM_TOKEN, &@$, st);
}
| REGEX_TOKEN
{
$$ = config_term(NULL, $1, TERM_REGEX, &@$, st);
}
| STRING
{
$$ = config_term(NULL, $1, TERM_STRING, &@$, st);
}
| DQUOTE
{
$$ = config_term(NULL, $1, TERM_DQUOTE, &@$, st);
}
| SQUOTE
{
$$ = config_term(NULL, $1, TERM_SQUOTE, &@$, st);
}
| TOK_TRUE
{
$$ = config_term(NULL, $1, TERM_TRUE, &@$, st);
}
| TOK_FALSE
{
$$ = config_term(NULL, $1, TERM_FALSE, &@$, st);
}
| TOK_OCTNUM
{
$$ = config_term(NULL, $1, TERM_OCTNUM, &@$, st);
}
| TOK_HEXNUM
{
$$ = config_term(NULL, $1, TERM_HEXNUM, &@$, st);
}
| TOK_DECNUM
{
$$ = config_term(NULL, $1, TERM_DECNUM, &@$, st);
}
| TOK_ARITH_OP TOK_OCTNUM
{
$$ = config_term($1, $2, TERM_OCTNUM, &@$, st);
}
| TOK_ARITH_OP TOK_HEXNUM
{
$$ = config_term($1, $2, TERM_HEXNUM, &@$, st);
}
| TOK_ARITH_OP TOK_DECNUM
{
$$ = config_term($1, $2, TERM_DECNUM, &@$, st);
}
| TOK_V4_ANY
{
$$ = config_term(NULL, $1, TERM_V4_ANY, &@$, st);
}
| TOK_V4ADDR
{
$$ = config_term(NULL, $1, TERM_V4ADDR, &@$, st);
}
| TOK_V4CIDR
{
$$ = config_term(NULL, $1, TERM_V4CIDR, &@$, st);
}
| TOK_V6ADDR
{
$$ = config_term(NULL, $1, TERM_V6ADDR, &@$, st);
}
| TOK_V6CIDR
{
$$ = config_term(NULL, $1, TERM_V6CIDR, &@$, st);
}
| TOK_FSID
{
$$ = config_term(NULL, $1, TERM_FSID, &@$, st);
}
| TOK_NETGROUP
{
$$ = config_term(NULL, $1, TERM_NETGROUP, &@$, st);
}
;
%%
/**
* @brief Report an scanner/parser error
*
* Replacement for yyerror() to get more info.
* HACK ALERT: new_file() does not have yylloc initialized yet for
* first file so create a string and init line number for it.
*/
void config_parse_error(YYLTYPE *yylloc_param,
struct parser_state *st,
char *format, ...)
{
FILE *fp = st->err_type->fp;
va_list arguments;
char *filename = "<unknown file>";
int linenum = 0;;
if (fp == NULL)
return; /* no stream, no message */
if (yylloc_param != NULL) {
filename = yylloc_param->filename;
linenum = yylloc_param->first_line;
}
va_start(arguments, format);
config_error(fp, filename, linenum, format, arguments);
va_end(arguments);
}
/* This is here because bison wants it declared.
* We do not use it because we can't get around the API.
* Use config_parse_error() instead.
*/
void ganesha_yyerror(YYLTYPE *yylloc_param,
void *yyscanner,
char *s){
LogCrit(COMPONENT_CONFIG,
"Config file (%s:%d) error: %s\n",
yylloc_param->filename,
yylloc_param->first_line,
s);
}
/**
* @brief Notes on parse tree linkage
*
* We use glist a little differently so beware.
* Elsewhere in the server, we only glist_init() the head
* and leave the 'node' members alone (both next and prev == NULL).
* However, the parse FSM handles siblings as a list via the LR rules.
* This means that while sub_nodes is the "head" of the list, it only
* gets linked in when the rules add the already formed list is fully
* parsed. Therefore, to make this all work, each node's 'node' member
* gets a turn as the head which requires it to be glist_init()'d
* contrary to what the rest of the code does. The last node to play
* 'head' is then the 'sub_nodes' member of the parent.
*/
/**
* Create a block item with the given content
*/
struct config_node *config_block(char *blockname,
struct config_node *list,
YYLTYPE *yylloc_param,
struct parser_state *st)
{
struct config_node *node, *cnode;
node = gsh_calloc(1, sizeof(struct config_node));
if (node == NULL) {
st->err_type->resource = true;
return NULL;
}
glist_init(&node->node);
node->u.nterm.name = blockname;
node->filename = yylloc_param->filename;
node->linenumber = yylloc_param->first_line;
node->type = TYPE_BLOCK;
glist_init(&node->u.nterm.sub_nodes);
if (list != NULL) {
glist_add_tail(&list->node, &node->u.nterm.sub_nodes);
link_node(node);
}
return node;
}
/**
* @brief Walk the subnode list and update all the sub-blocks in it
* so we can find the root of the parse tree when we need it.
*/
void link_node(struct config_node *node)
{
struct config_node *subnode;
struct glist_head *ns;
assert(node->type == TYPE_BLOCK ||
node->type == TYPE_ROOT);
glist_for_each(ns, &node->u.nterm.sub_nodes) {
subnode = glist_entry(ns, struct config_node, node);
if (subnode->type == TYPE_BLOCK)
subnode->u.nterm.parent = node;
}
}
/**
* @brief Link siblings together
*
*/
struct config_node *link_sibling(struct config_node *first,
struct config_node *second)
{
if (first == NULL) {
return second;
} else {
if (second != NULL)
glist_add_tail(&first->node, &second->node);
return first;
}
}
/**
* Create a term (value)
*/
struct config_node *config_term(char *opcode,
char *varval,
enum term_type type,
YYLTYPE *yylloc_param,
struct parser_state *st)
{
struct config_node *node;
node = gsh_calloc(1, sizeof(struct config_node));
if (node == NULL) {
st->err_type->resource = true;
return NULL;
}
glist_init(&node->node);
node->filename = yylloc_param->filename;
node->linenumber = yylloc_param->first_line;
node->type = TYPE_TERM;
node->u.term.type = type;
node->u.term.op_code = opcode;
node->u.term.varvalue = varval;
return node;
}
/**
* Create a statement node (key = list of terms)
*/
struct config_node *config_stmt(char *varname,
struct config_node *exprlist,
YYLTYPE *yylloc_param,
struct parser_state *st)
{
struct config_node *node;
node = gsh_calloc(1, sizeof(struct config_node));
if (node == NULL) {
st->err_type->resource = true;
return NULL;
}
glist_init(&node->node);
glist_init(&node->u.nterm.sub_nodes);
node->filename = yylloc_param->filename;
node->linenumber = yylloc_param->first_line;
node->type = TYPE_STMT;
node->u.nterm.name = varname;
if (exprlist != NULL)
glist_add_tail(&exprlist->node, &node->u.nterm.sub_nodes);
return node;
}
int ganesha_yylex(YYSTYPE *yylval_param,
YYLTYPE *yylloc_param,
struct parser_state *st)
{
return ganeshun_yylex(yylval_param,
yylloc_param,
st->scanner);
}