%{ /* This file is part of fileserv. Copyright (C) 2017, 2018 Sergey Poznyakoff Fileserv 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. Fileserv 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 fileserv. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include "mtint.h" #include "grammar.h" static void yyprint (FILE *output, unsigned short toknum, YYSTYPE val) { switch (toknum) { case TYPE: case IDENT: case STRING: fprintf (output, "[%lu] %s", (unsigned long) val.string.len, val.string.ptr); break; case EOL: fprintf (output, "\\n"); break; default: if (isprint (toknum)) fprintf (output, "'%c'", toknum); else fprintf (output, "tok(%d)", toknum); break; } } #define YYPRINT yyprint extern int yyerror (char *s); extern int yylex (void); static void arg_list_append (struct arg_list *al, struct mimetypes_string const *str) { struct arg_elt *elt; elt = malloc (sizeof *elt); if (!elt) mimetypes_nomem (); elt->string.ptr = str->ptr; elt->string.len = str->len; LLE_APPEND (al, elt, link); } static void arg_list_destroy (struct arg_list *al) { struct arg_elt *elt; while ((elt = LL_FIRST (al)) != NULL) { /* FIXME: free? */ LLE_UNLINK (al, elt, link); } } //FIXME struct rule_list rule_list = LL_HEAD_INITIALIZER; static struct node *make_node (enum node_type type, struct locus_range const *loc); static struct node *make_binary_node (int op, struct node *left, struct node *rigth, struct locus_range const *loc); static struct node *make_negation_node (struct node *p, struct locus_range const *loc); static struct node *make_suffix_node (struct mimetypes_string *suffix, struct locus_range const *loc); static struct node *make_functional_node (char *ident, struct arg_list *list, struct locus_range const *loc); static size_t errors; %} %locations %expect 15 %token TYPE IDENT %token STRING %token EOL BOGUS PRIORITY %left ',' %left '+' %type arg %type arglist %type function stmt rule maybe_rule %type priority maybe_priority %union { struct mimetypes_string string; char *s; struct arg_list list; int result; struct node *node; } %% input : list ; list : rule_line | list EOL rule_line ; rule_line: /* empty */ | TYPE maybe_rule maybe_priority { struct rule *p = malloc (sizeof (*p)); if (!p) mimetypes_nomem (); LLE_APPEND (&rule_list, p, link); locus_range_init (&p->loc); p->type = $1.ptr; p->node = $2; p->priority = $3; locus_point_copy (&p->loc.beg, &@1.beg); locus_point_copy (&p->loc.end, &@3.end); #if 0 YY_LOCATION_PRINT (stderr, p->loc); fprintf (stderr, ": rule %s\n", p->type); #endif } | BOGUS { YYERROR; } | error { errors++; // arg_list_destroy (); //FIXME lex_next_rule (); yyerrok; yyclearin; } ; maybe_rule: /* empty */ { $$ = make_node (true_node, &yylloc); } | rule ; rule : stmt | rule rule %prec ',' { struct locus_range lr; lr.beg = @1.beg; lr.end = @2.end; $$ = make_binary_node (L_OR, $1, $2, &lr); } | rule ',' rule { struct locus_range lr; lr.beg = @1.beg; lr.end = @3.end; $$ = make_binary_node (L_OR, $1, $3, &lr); } | rule '+' rule { struct locus_range lr; lr.beg = @1.beg; lr.end = @3.end; $$ = make_binary_node (L_AND, $1, $3, &lr); } ; stmt : '!' stmt { $$ = make_negation_node ($2, &@2); } | '(' rule ')' { $$ = $2; } | STRING { $$ = make_suffix_node (&$1, &@1); } | function | BOGUS { YYERROR; } ; priority : PRIORITY '(' arglist ')' { if (LL_COUNT (&$3) != 1) { mimetypes_error_at (&@1, "priority takes single numberic argument"); YYERROR; } $$ = atoi (LL_FIRST (&$3)->string.ptr); arg_list_destroy (&$3); } ; maybe_priority: /* empty */ { $$ = 100; } | priority ; function : IDENT '(' arglist ')' { struct locus_range lr; lr.beg = @1.beg; lr.end = @4.end; $$ = make_functional_node ($1.ptr, &$3, &lr); if (!$$) YYERROR; } ; arglist : arg { LL_HEAD_INIT (&$$); arg_list_append (&$$, &$1); } | arglist ',' arg { arg_list_append (&$1, &$3); $$ = $1; } ; arg : STRING | BOGUS { YYERROR; } ; %% int mimetypes_parse (const char *name) { int rc; char *p; if (mimetypes_open (name)) return 1; p = getenv ("MIMETYPES_DEBUG_GRAM"); yydebug = p ? (*p - '0') : 0; rc = yyparse (); mimetypes_close (); return rc || errors; } static struct node * make_node (enum node_type type, struct locus_range const *loc) { struct node *p = malloc (sizeof *p); if (!p) mimetypes_nomem (); p->type = type; locus_range_init (&p->loc); locus_range_copy (&p->loc, loc); return p; } static struct node * make_binary_node (int op, struct node *left, struct node *right, struct locus_range const *loc) { struct node *node = make_node (binary_node, loc); node->v.bin.op = op; node->v.bin.arg1 = left; node->v.bin.arg2 = right; return node; } struct node * make_negation_node (struct node *p, struct locus_range const *loc) { struct node *node = make_node (negation_node, loc); node->v.arg = p; return node; } struct node * make_suffix_node (struct mimetypes_string *suffix, struct locus_range const *loc) { struct node *node = make_node (suffix_node, loc); node->v.suffix = *suffix; return node; } struct node * make_functional_node (char *ident, struct arg_list *list, struct locus_range const *loc) { size_t count, i; struct builtin_tab const *p; struct node *node; union argument *args; struct arg_elt *elt; p = find_builtin (ident); if (!p) { mimetypes_error_at (loc, "unknown builtin: %s", ident); return NULL; } count = LL_COUNT (list); i = strlen (p->args); if (count < i) { mimetypes_error_at (loc, "too few arguments in call to %s", ident); return NULL; } else if (count > i) { mimetypes_error_at (loc, "too many arguments in call to %s", ident); return NULL; } args = calloc (count, sizeof *args); if (!args) mimetypes_nomem (); i = 0; LL_FOREACH (list, elt, link) { char *tmp; switch (p->args[i]) { case 'd': args[i].number = strtoul (elt->string.ptr, &tmp, 0); if (*tmp) goto err; break; case 's': args[i].string = elt->string; break; case 'x': { int rc = regcomp (&args[i].rx, elt->string.ptr, REG_EXTENDED|REG_NOSUB); if (rc) { char errbuf[512]; regerror (rc, &args[i].rx, errbuf, sizeof errbuf); mimetypes_error_at (loc, "%s", errbuf); return NULL; } } break; case 'c': args[i].c = strtoul (elt->string.ptr, &tmp, 0); if (*tmp) goto err; break; default: abort (); } i++; } node = make_node (functional_node, loc); node->v.function.fun = p->handler; node->v.function.args = args; return node; err: mimetypes_error_at (loc, "argument %lu has wrong type in call to %s", (unsigned long) i, ident); return NULL; }