=================================================================== RCS file: /cvs/cvs/blind/parse.y,v retrieving revision 1.2 retrieving revision 1.16 diff -u -p -r1.2 -r1.16 --- blind/parse.y 2022/03/20 17:33:18 1.2 +++ blind/parse.y 2022/04/10 20:03:38 1.16 @@ -15,28 +15,69 @@ */ %{ +#include + +#include #include - +#include + #include "blind.h" +#include "config.h" +#include "log.h" -int yylex(void); -int yyerror(const char *, ...); -int config_load(struct blind *, const char *); +static struct file { + FILE *stream; + char *name; + size_t ungetpos; + size_t ungetsize; + u_char *ungetbuf; + int eof_reached; + int lineno; +} *f; -struct blind *env = NULL; -static int errors = 0; +int lookup(char *); +int igetc(void); +int lgetc(int); +int findeol(void); +void lungetc(int); +int kw_cmp(const void *, const void *); +int yyparse(void); +int yylex(void); +int yyerror(const char *, ...) + __attribute__((__format__ (printf, 1, 2))) + __attribute__((__nonnull__ (1))); +int config_load(struct blind *); +struct file *config_push(const char *); +int config_close(void); +#define START_EXPAND 1 +#define DONE_EXPAND 2 +static int expanding; + +TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); +struct sym { + TAILQ_ENTRY(sym) entry; + int used; + int persist; + char *nam; + char *val; +}; +char *symget(const char *); + +struct blind *env = NULL; +static int errors = 0; + typedef struct { union { - int64_t number; - char *string; + int64_t number; + char *string; } v; int lineno; } YYSTYPE; %} -%token EXPIRE +%token ACTION ARROW DISABLE ENABLE EXPIRE ERROR SET %token NUMBER %token STRING @@ -44,34 +85,402 @@ typedef struct { grammar : | grammar '\n' - | grammar expire '\n' + | grammar set '\n' + | grammar error '\n' { errors++; } ; -expire : EXPIRE NUMBER { - } - ; +set : SET EXPIRE NUMBER { + env->bl_ttl = $3; +} +| SET ACTION ENABLE { + env->bl_opt |= BL_OPT_ACTION; + } +| SET ACTION DISABLE { + env->bl_opt |= !BL_OPT_ACTION; + } +; %% +struct keywords { + const char *k_name; + int k_val; +}; + int yyerror(const char *fmt, ...) { + va_list ap; + char *msg; + + errors++; + va_start(ap, fmt); + if (vasprintf(&msg, fmt, ap) == -1) + log_fatal("yyerror vasprintf"); + va_end(ap); + log_info("%s:%d: %s", f->name, yylval.lineno, msg); + free(msg); return (0); } int +kw_cmp(const void *k, const void *e) +{ + return (strcmp(k, ((const struct keywords *)e)->k_name)); +} + +int +lookup(char *s) +{ + /* this has to be sorted always */ + static const struct keywords keywords[] = { + { "action", ACTION }, + { "disable", DISABLE }, + { "enable", ENABLE }, + { "expire", EXPIRE }, + { "set", SET }, + }; + const struct keywords *p; + + p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), + sizeof(keywords[0]), kw_cmp); + + if (p) + return (p->k_val); + else + return (STRING); +} + +char * +symget(const char *nam) +{ + struct sym *sym; + + TAILQ_FOREACH(sym, &symhead, entry) { + if (strcmp(nam, sym->nam) == 0) { + sym->used = 1; + return (sym->val); + } + } + return (NULL); +} + +int +igetc(void) +{ + int c; + + while (1) { + if (f->ungetpos > 0) + c = f->ungetbuf[--f->ungetpos]; + else + c = getc(f->stream); + + if (c == START_EXPAND) + expanding = 1; + else if (c == DONE_EXPAND) + expanding = 0; + else + break; + } + return (c); +} + +int +lgetc(int quotec) +{ + int c, next; + + if (quotec) { + if ((c = igetc()) == EOF) { + yyerror("reached end of file while parsing " + "quoted string"); + if (config_close() == EOF) + return (EOF); + return (quotec); + } + return (c); + } + + while ((c = igetc()) == '\\') { + next = igetc(); + if (next != '\n') { + c = next; + break; + } + yylval.lineno = f->lineno; + f->lineno++; + } + + if (c == EOF) { + /* + * Fake EOL when hit EOF for the first time. This gets line + * count right if last line in included file is syntactically + * invalid and has no newline. + */ + if (f->eof_reached == 0) { + f->eof_reached = 1; + return ('\n'); + } + while (c == EOF) { + if (config_close() == EOF) + return (EOF); + c = igetc(); + } + } + return (c); +} + +void +lungetc(int c) +{ + if (c == EOF) + return; + + if (f->ungetpos >= f->ungetsize) { + void *p = reallocarray(f->ungetbuf, f->ungetsize, 2); + if (p == NULL) + log_fatal("cannot reallocate memory"); + f->ungetbuf = p; + f->ungetsize *= 2; + } + f->ungetbuf[f->ungetpos++] = c; +} + +int +findeol(void) +{ + int c; + + /* skip to either EOF or the first real EOL */ + while (1) { + c = lgetc(0); + if (c == '\n') { + f->lineno++; + break; + } + if (c == EOF) + break; + } + return (ERROR); +} + +int yylex(void) { - return (0); + char buf[8096]; + char *p, *val; + int quotec, next, c; + int token; + +top: + p = buf; + while ((c = lgetc(0)) == ' ' || c == '\t') + ; /* nothing */ + + yylval.lineno = f->lineno; + if (c == '#') + while ((c = lgetc(0)) != '\n' && c != EOF) + ; /* nothing */ + if (c == '$' && !expanding) { + while (1) { + if ((c = lgetc(0)) == EOF) + return (0); + + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("string too long"); + return (findeol()); + } + if (isalnum(c) || c == '_') { + *p++ = c; + continue; + } + *p = '\0'; + lungetc(c); + break; + } + val = symget(buf); + if (val == NULL) { + yyerror("macro '%s' not defined", buf); + return (findeol()); + } + p = val + strlen(val) - 1; + lungetc(DONE_EXPAND); + while (p >= val) { + lungetc((unsigned char)*p); + p--; + } + lungetc(START_EXPAND); + goto top; + } + + switch (c) { + case '\'': + case '"': + quotec = c; + while (1) { + if ((c = lgetc(quotec)) == EOF) + return (0); + if (c == '\n') { + f->lineno++; + continue; + } else if (c == '\\') { + if ((next = lgetc(quotec)) == EOF) + return (0); + if (next == quotec || next == ' ' || + next == '\t') + c = next; + else if (next == '\n') { + f->lineno++; + continue; + } else + lungetc(next); + } else if (c == quotec) { + *p = '\0'; + break; + } else if (c == '\0') { + yyerror("syntax error"); + return (findeol()); + } + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("string too long"); + return (findeol()); + } + *p++ = c; + } + yylval.v.string = strdup(buf); + if (yylval.v.string == NULL) + log_fatal("cannot duplicate buffer"); + return (STRING); + } + +#define allowed_to_end_number(x) \ + (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') + + if (c == '-' || isdigit(c)) { + do { + *p++ = c; + if ((size_t)(p-buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(0)) != EOF && isdigit(c)); + lungetc(c); + if (p == buf + 1 && buf[0] == '-') + goto nodigits; + if (c == EOF || allowed_to_end_number(c)) { + const char *errstr = NULL; + + *p = '\0'; + yylval.v.number = strtonum(buf, LLONG_MIN, + LLONG_MAX, &errstr); + if (errstr) { + yyerror("\"%s\" invalid number: %s", + buf, errstr); + return (findeol()); + } + return (NUMBER); + } else { +nodigits: + while (p > buf + 1) + lungetc((unsigned char)*--p); + c = (unsigned char)*--p; + if (c == '-') + return (c); + } + } + + if (c == '=') { + if ((c = lgetc(0)) != EOF && c == '>') + return (ARROW); + lungetc(c); + c = '='; + } + +#define allowed_in_string(x) \ + (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ + x != '{' && x != '}' && x != '<' && x != '>' && \ + x != '!' && x != '=' && x != '#' && \ + x != ',')) + + if (isalnum(c) || c == ':' || c == '_') { + do { + *p++ = c; + if ((size_t)(p-buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); + lungetc(c); + *p = '\0'; + if ((token = lookup(buf)) == STRING) + if ((yylval.v.string = strdup(buf)) == NULL) + log_fatal("cannot duplicate buffer"); + return (token); + } + if (c == '\n') { + yylval.lineno = f->lineno; + f->lineno++; + } + if (c == EOF) + return (0); + return (c); } int -config_load(struct blind *temp, const char *filename) +config_load(struct blind *temp) { - env = temp; - errors = 0; + env = temp; + errors = 0; - // + if ((f = calloc(1, sizeof(struct file))) == NULL) { + log_debug("cannot allocate memory"); + return (-1); + } + if ((f->name = strdup(env->bl_conf)) == NULL) { + log_debug("cannot duplicate name"); + free(f); + return (-1); + } + if ((f->stream = fopen(f->name, "r")) == NULL) { + log_debug("cannot open config file"); + free(f->name); + free(f); + return (-1); + } + if (config_perm(fileno(f->stream), f->name)) { + fclose(f->stream); + free(f->name); + free(f); + return (-1); + } + f->lineno = 1; + f->ungetsize = 16; + f->ungetbuf = malloc(f->ungetsize); + if (f->ungetbuf == NULL) { + log_debug("cannot allocate buffer"); + fclose(f->stream); + free(f->name); + free(f); + return (-1); + } - return (0); + yyparse(); + // setup + + if (errors) + return (-1); + + return (0); +} + +int +config_close(void) +{ + fclose(f->stream); + free(f->name); + free(f->ungetbuf); + free(f); + + return (EOF); }