version 1.3, 2022/03/20 18:47:16 |
version 1.10, 2022/03/25 22:43:43 |
|
|
%{ |
%{ |
#include <sys/queue.h> |
#include <sys/queue.h> |
|
|
#include <stdio.h> |
#include <stdio.h> |
#include <stdarg.h> |
#include <stdarg.h> |
|
#include <ctype.h> |
|
|
#include "blind.h" |
#include "blind.h" |
|
#include "config.h" |
#include "log.h" |
#include "log.h" |
|
|
TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); |
//TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); |
static struct file { |
static struct file { |
TAILQ_ENTRY(file) entry; |
// TAILQ_ENTRY(file) entry; |
FILE *stream; |
FILE *stream; |
char *name; |
char *name; |
size_t ungetpos; |
size_t ungetpos; |
Line 34 static struct file { |
|
Line 36 static struct file { |
|
int eof_reached; |
int eof_reached; |
int lineno; |
int lineno; |
int errors; |
int errors; |
} *file, *top; |
} *file; |
|
//, *top; |
|
|
int yylex(void); |
int lookup(char *); |
int yyerror(const char *, ...); |
int igetc(void); |
int config_load(struct blind *); |
int lgetc(int); |
struct file *config_push(const char *); |
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))); |
|
|
struct blind *env = NULL; |
int config_load(struct blind *); |
static int errors = 0; |
struct file *config_push(const char *); |
|
int config_pop(void); |
|
|
|
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 { |
typedef struct { |
union { |
union { |
int64_t number; |
int64_t number; |
char *string; |
char *string; |
} v; |
} v; |
int lineno; |
int lineno; |
} YYSTYPE; |
} YYSTYPE; |
|
|
%} |
%} |
|
|
%token EXPIRE |
%token ACTION ARROW DISABLE ENABLE EXPIRE ERROR SET |
|
|
%token <v.number> NUMBER |
%token <v.number> NUMBER |
%token <v.string> STRING |
%token <v.string> STRING |
|
|
|
|
grammar : |
grammar : |
| grammar '\n' |
| grammar '\n' |
| grammar expire '\n' |
| grammar set '\n' |
|
| grammar error '\n' { file->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 |
int |
yyerror(const char *fmt, ...) |
yyerror(const char *fmt, ...) |
{ |
{ |
|
va_list ap; |
|
char *msg; |
|
|
|
file->errors++; |
|
va_start(ap, fmt); |
|
if (vasprintf(&msg, fmt, ap) == -1) |
|
log_fatal("yyerror vasprintf"); |
|
va_end(ap); |
|
log_info("%s:%d: %s", file->name, yylval.lineno, msg); |
|
free(msg); |
return (0); |
return (0); |
} |
} |
|
|
int |
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); |
|
} |
|
|
|
#define START_EXPAND 1 |
|
#define DONE_EXPAND 2 |
|
|
|
static int expanding; |
|
|
|
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 (file->ungetpos > 0) |
|
c = file->ungetbuf[--file->ungetpos]; |
|
else |
|
c = getc(file->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 (file == top || config_pop() == EOF) |
|
if (config_pop() == EOF) |
|
return (EOF); |
|
return (quotec); |
|
} |
|
return (c); |
|
} |
|
|
|
while ((c = igetc()) == '\\') { |
|
next = igetc(); |
|
if (next != '\n') { |
|
c = next; |
|
break; |
|
} |
|
yylval.lineno = file->lineno; |
|
file->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 (file->eof_reached == 0) { |
|
file->eof_reached = 1; |
|
return ('\n'); |
|
} |
|
while (c == EOF) { |
|
//if (file == top || config_pop() == EOF) |
|
if (config_pop() == EOF) |
|
return (EOF); |
|
c = igetc(); |
|
} |
|
} |
|
return (c); |
|
} |
|
|
|
void |
|
lungetc(int c) |
|
{ |
|
if (c == EOF) |
|
return; |
|
|
|
if (file->ungetpos >= file->ungetsize) { |
|
void *p = reallocarray(file->ungetbuf, file->ungetsize, 2); |
|
if (p == NULL) |
|
log_fatal("cannot reallocate memory"); |
|
file->ungetbuf = p; |
|
file->ungetsize *= 2; |
|
} |
|
file->ungetbuf[file->ungetpos++] = c; |
|
} |
|
|
|
int |
|
findeol(void) |
|
{ |
|
int c; |
|
|
|
/* skip to either EOF or the first real EOL */ |
|
while (1) { |
|
c = lgetc(0); |
|
if (c == '\n') { |
|
file->lineno++; |
|
break; |
|
} |
|
if (c == EOF) |
|
break; |
|
} |
|
return (ERROR); |
|
} |
|
|
|
int |
yylex(void) |
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 = file->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') { |
|
file->lineno++; |
|
continue; |
|
} else if (c == '\\') { |
|
if ((next = lgetc(quotec)) == EOF) |
|
return (0); |
|
if (next == quotec || next == ' ' || |
|
next == '\t') |
|
c = next; |
|
else if (next == '\n') { |
|
file->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 = file->lineno; |
|
file->lineno++; |
|
} |
|
if (c == EOF) |
|
return (0); |
|
return (c); |
} |
} |
|
|
int |
int |
Line 93 config_load(struct blind *temp) |
|
Line 445 config_load(struct blind *temp) |
|
// config_purge(PURGE_ALL); |
// config_purge(PURGE_ALL); |
return (-1); |
return (-1); |
} |
} |
top = file; |
// top = file; |
|
|
// parse config |
yyparse(); |
|
|
|
errors = file->errors; |
|
config_pop(); |
|
|
|
// setup |
|
|
|
if (errors) { |
|
// config_purge(PURGE_ALL); |
|
return (-1); |
|
} |
|
|
return (0); |
return (0); |
} |
} |
|
|
Line 106 config_push(const char *name) |
|
Line 468 config_push(const char *name) |
|
struct file *nfile; |
struct file *nfile; |
|
|
if ((nfile = calloc(1, sizeof(struct file))) == NULL) { |
if ((nfile = calloc(1, sizeof(struct file))) == NULL) { |
log_debug("%s", __func__); |
log_debug("cannot allocate memory"); |
return (NULL); |
return (NULL); |
} |
} |
|
|
if ((nfile->name = strdup(name)) == NULL) { |
if ((nfile->name = strdup(name)) == NULL) { |
log_debug("%s", __func__); |
log_debug("cannot duplicate name"); |
free(nfile); |
free(nfile); |
return (NULL); |
return (NULL); |
} |
} |
|
|
if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { |
if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { |
log_debug("%s: %s", __func__, nfile->name); |
log_debug("cannot open config file"); |
free(nfile->name); |
free(nfile->name); |
free(nfile); |
free(nfile); |
return (NULL); |
return (NULL); |
} |
} |
|
if (config_perm(fileno(nfile->stream), nfile->name)) { |
|
fclose(nfile->stream); |
|
free(nfile->name); |
|
free(nfile); |
|
return (NULL); |
|
} |
|
nfile->lineno = 1; // TAILQ_EMPTY(&files) ? 1 : 0; |
|
nfile->ungetsize = 16; |
|
nfile->ungetbuf = malloc(nfile->ungetsize); |
|
if (nfile->ungetbuf == NULL) { |
|
log_debug("cannot allocate buffer"); |
|
fclose(nfile->stream); |
|
free(nfile->name); |
|
free(nfile); |
|
return (NULL); |
|
} |
|
//TAILQ_INSERT_TAIL(&files, nfile, entry); |
|
return (nfile); |
|
} |
|
|
// check file permission |
int |
|
config_pop(void) |
|
{ |
|
// struct file *prv; |
|
|
// load file |
//if ((prv = TAILQ_PREV(file, files, entry)) != NULL) |
|
// prv->errors += file->errors; |
|
|
return (nfile); |
// TAILQ_REMOVE(&files, file, entry); |
|
fclose(file->stream); |
|
free(file->name); |
|
free(file->ungetbuf); |
|
// free(file); |
|
// file = prv; |
|
|
|
// return (file ? 0 : EOF); |
|
return (EOF); |
} |
} |