Parse: when syntax error, fallback to default config, fixes #45
This commit is contained in:
@@ -181,7 +181,8 @@ void
|
||||
conf_bar_section(void)
|
||||
{
|
||||
struct conf_sec *bar, **mouse, *selbar, *systray;
|
||||
char *barbg, sc = screen_count();
|
||||
char *barbg;
|
||||
int sc = screen_count();
|
||||
|
||||
bar = fetch_section_first(NULL, "bar");
|
||||
|
||||
@@ -230,7 +231,6 @@ conf_bar_section(void)
|
||||
}
|
||||
|
||||
free(mouse);
|
||||
free(barbg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -71,45 +71,6 @@ print_unused(struct conf_sec *sec)
|
||||
print_unused(s);
|
||||
}
|
||||
|
||||
void
|
||||
free_conf(struct conf_sec *sec)
|
||||
{
|
||||
struct conf_sec *s;
|
||||
struct conf_opt *o;
|
||||
size_t n;
|
||||
|
||||
if (!sec)
|
||||
{
|
||||
TAILQ_FOREACH(s, &config, entry)
|
||||
{
|
||||
free(s->name);
|
||||
free_conf(s);
|
||||
free(s);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
while (!SLIST_EMPTY(&sec->optlist))
|
||||
{
|
||||
o = SLIST_FIRST(&sec->optlist);
|
||||
SLIST_REMOVE_HEAD(&sec->optlist, entry);
|
||||
free(o->name);
|
||||
|
||||
for (n = 0; o->val[n]; n++)
|
||||
free(o->val[n]);
|
||||
|
||||
free(o);
|
||||
}
|
||||
|
||||
while (!TAILQ_EMPTY(&sec->sub))
|
||||
{
|
||||
s = TAILQ_FIRST(&sec->sub);
|
||||
TAILQ_REMOVE(&sec->sub, s, entry);
|
||||
free_conf(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct conf_sec **
|
||||
fetch_section(struct conf_sec *s, char *name)
|
||||
{
|
||||
|
||||
@@ -122,8 +122,12 @@ syntax(struct keyword *kw, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
fprintf(stderr, "%s: %s:%d", __progname, kw->file->name, kw->line);
|
||||
if (kw->name)
|
||||
fprintf(stderr, "%s:", __progname);
|
||||
|
||||
if (kw && kw->file && kw->file->name)
|
||||
fprintf(stderr, "%s:%d", kw->file->name, kw->line);
|
||||
|
||||
if (kw && kw->name)
|
||||
fprintf(stderr, ", near '%s'", kw->name);
|
||||
fprintf(stderr, ": ");
|
||||
|
||||
@@ -132,8 +136,6 @@ syntax(struct keyword *kw, const char *fmt, ...)
|
||||
va_end(args);
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
@@ -154,6 +156,7 @@ parse_keywords(const char *filename)
|
||||
char path[PATH_MAX];
|
||||
size_t i, j;
|
||||
int line;
|
||||
bool_t error = False;
|
||||
|
||||
|
||||
if ((fd = open(filename, O_RDONLY)) == -1 || stat(filename, &st) == -1) {
|
||||
@@ -161,28 +164,27 @@ parse_keywords(const char *filename)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf = (char *)mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, SEEK_SET);
|
||||
|
||||
if (buf == (char*)MAP_FAILED) {
|
||||
warn("%s", filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!realpath(filename, path)) {
|
||||
warn("%s", filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
file = xcalloc(1, sizeof(*file));
|
||||
bufname = xcalloc(1, sizeof(*bufname) * BUFSIZ);
|
||||
file->name = strdup(path);
|
||||
file->parent = NULL;
|
||||
|
||||
bufname = xcalloc(1, sizeof(*bufname) * BUFSIZ);
|
||||
buf = (char *)mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, SEEK_SET);
|
||||
|
||||
if (buf == (char*)MAP_FAILED) {
|
||||
warn("%s", filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
for(i = 0, j = 0, line = 1; i < (size_t)st.st_size; i++) {
|
||||
|
||||
if (tail && !head)
|
||||
if (!head && tail)
|
||||
head = tail;
|
||||
|
||||
if (buf[i] == '\n' && s.comment == True) {
|
||||
@@ -199,94 +201,106 @@ parse_keywords(const char *filename)
|
||||
if (s.comment == True)
|
||||
continue;
|
||||
|
||||
if (buf[i] == s.quote_char && s.quote == True) {
|
||||
if (s.quote == True && buf[i] == s.quote_char) {
|
||||
/* end of quotted string */
|
||||
PUSH_KEYWORD(WORD);
|
||||
s.quote = False;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((buf[i] == '"' || buf[i] == '\'') &&
|
||||
s.quote == False)
|
||||
{
|
||||
PUSH_KEYWORD(WORD);
|
||||
/* begin quotted string */
|
||||
s.quote_char = buf[i];
|
||||
s.quote = True;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (buf[i] == '[' && s.quote == False) {
|
||||
PUSH_KEYWORD(WORD);
|
||||
if (buf[i+1] == '/') {
|
||||
i +=2;
|
||||
type = SEC_END;
|
||||
}
|
||||
else {
|
||||
i++;
|
||||
type = SEC_START;
|
||||
if (s.quote == False) {
|
||||
if ((buf[i] == '"' || buf[i] == '\'')) {
|
||||
PUSH_KEYWORD(WORD);
|
||||
/* begin quotted string */
|
||||
s.quote_char = buf[i];
|
||||
s.quote = True;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* get section name */
|
||||
while (buf[i] != ']') {
|
||||
|
||||
if (i >= ((size_t)st.st_size-1) || j >= (BUFSIZ-1)) {
|
||||
bufname[j] = '\0';
|
||||
syntax(NULL, "word too long in %s:%d near '%s'",
|
||||
file->name, line, bufname);
|
||||
if (buf[i] == '[') {
|
||||
PUSH_KEYWORD(WORD);
|
||||
if (buf[i+1] == '/') {
|
||||
i +=2;
|
||||
type = SEC_END;
|
||||
}
|
||||
else {
|
||||
i++;
|
||||
type = SEC_START;
|
||||
}
|
||||
|
||||
bufname[j++] = buf[i++];
|
||||
/* get section name */
|
||||
while (buf[i] != ']') {
|
||||
|
||||
if (i >= ((size_t)st.st_size-1) || j >= (BUFSIZ-1)) {
|
||||
bufname[j] = '\0';
|
||||
syntax(NULL, "word too long in %s:%d near '%s'",
|
||||
file->name, line, bufname);
|
||||
error = True;
|
||||
break;
|
||||
}
|
||||
|
||||
bufname[j++] = buf[i++];
|
||||
}
|
||||
PUSH_KEYWORD(type);
|
||||
continue;
|
||||
}
|
||||
PUSH_KEYWORD(type);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (buf[i] == '{' && s.quote == False) {
|
||||
PUSH_KEYWORD(WORD);
|
||||
PUSH_KEYWORD(LIST_START);
|
||||
continue;
|
||||
}
|
||||
if (buf[i] == '{') {
|
||||
PUSH_KEYWORD(WORD);
|
||||
PUSH_KEYWORD(LIST_START);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (buf[i] == '}' && s.quote == False) {
|
||||
PUSH_KEYWORD(WORD);
|
||||
PUSH_KEYWORD(LIST_END);
|
||||
continue;
|
||||
}
|
||||
if (buf[i] == '}') {
|
||||
PUSH_KEYWORD(WORD);
|
||||
PUSH_KEYWORD(LIST_END);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (buf[i] == ',' && s.quote == False) {
|
||||
PUSH_KEYWORD(WORD);
|
||||
continue;
|
||||
}
|
||||
if (buf[i] == ',') {
|
||||
PUSH_KEYWORD(WORD);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (buf[i] == '=' && s.quote == False) {
|
||||
PUSH_KEYWORD(WORD);
|
||||
PUSH_KEYWORD(EQUAL);
|
||||
continue;
|
||||
}
|
||||
if (buf[i] == '=') {
|
||||
PUSH_KEYWORD(WORD);
|
||||
PUSH_KEYWORD(EQUAL);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strchr("\t\n ", buf[i]) && s.quote == False) {
|
||||
PUSH_KEYWORD(WORD);
|
||||
if (strchr("\t\n ", buf[i])) {
|
||||
PUSH_KEYWORD(WORD);
|
||||
|
||||
if (buf[i] == '\n')
|
||||
line++;
|
||||
if (buf[i] == '\n')
|
||||
line++;
|
||||
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} /* s.quote == False */
|
||||
|
||||
if (j >= (BUFSIZ - 1)) {
|
||||
bufname[j] = '\0';
|
||||
syntax(NULL, "word too long in %s:%d near '%s'",
|
||||
file->name, line, bufname);
|
||||
error = True;
|
||||
break;
|
||||
}
|
||||
|
||||
bufname[j++] = buf[i];
|
||||
}
|
||||
|
||||
munmap(buf, st.st_size);
|
||||
free(bufname);
|
||||
warnx("%s read", file->name);
|
||||
return head;
|
||||
|
||||
return (error ? NULL: head);
|
||||
}
|
||||
|
||||
/*
|
||||
* return NULL on failure and head->next if
|
||||
* no config found (of file doesn't exist)
|
||||
* NOTE to devs: head->name is the file to include
|
||||
*/
|
||||
static struct keyword *
|
||||
include(struct keyword *head)
|
||||
{
|
||||
@@ -299,8 +313,10 @@ include(struct keyword *head)
|
||||
|
||||
head = head->next;
|
||||
|
||||
if (!head || head->type != WORD)
|
||||
if (!head || head->type != WORD) {
|
||||
syntax(head, "missing filename to include");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* replace ~ by user directory */
|
||||
if (head->name && head->name[0] == '~') {
|
||||
@@ -322,19 +338,29 @@ include(struct keyword *head)
|
||||
|
||||
if (!(kw = parse_keywords(filename))) {
|
||||
warnx("no config fond in include file %s", head->name);
|
||||
return head->next;
|
||||
|
||||
if (filename != head->name)
|
||||
free(filename);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
kw->file->parent = head->file;
|
||||
head = head->next;
|
||||
|
||||
/* detect circular include */
|
||||
for (file = kw->file->parent; file != NULL; file = file->parent)
|
||||
{
|
||||
if (!strcmp(file->name, kw->file->name))
|
||||
for (file = kw->file->parent; file != NULL; file = file->parent) {
|
||||
if (!strcmp(file->name, kw->file->name)) {
|
||||
syntax(kw, "circular include of %s", kw->file->name);
|
||||
|
||||
if (filename != head->name)
|
||||
free(filename);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
head = head->next;
|
||||
|
||||
if (kw) {
|
||||
for (tail = kw; tail->next; tail = tail->next);
|
||||
tail->next = head;
|
||||
@@ -343,6 +369,20 @@ include(struct keyword *head)
|
||||
return kw;
|
||||
}
|
||||
|
||||
static void *
|
||||
free_opt(struct conf_opt *o)
|
||||
{
|
||||
int i;
|
||||
if (o) {
|
||||
if (o->name)
|
||||
free(o->name);
|
||||
for (i = 0; o->val[i]; i++)
|
||||
free(o->val[i]);
|
||||
free(o);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct conf_opt *
|
||||
get_option(struct keyword **head)
|
||||
{
|
||||
@@ -358,18 +398,23 @@ get_option(struct keyword **head)
|
||||
|
||||
kw = kw->next;
|
||||
|
||||
if (kw->type != EQUAL)
|
||||
if (kw->type != EQUAL) {
|
||||
syntax(kw, "missing '=' here");
|
||||
return free_opt(o);
|
||||
}
|
||||
|
||||
kw = kw->next;
|
||||
|
||||
if (!kw)
|
||||
if (!kw) {
|
||||
syntax(kw, "missing value");
|
||||
return free_opt(o);
|
||||
}
|
||||
|
||||
|
||||
switch (kw->type) {
|
||||
case INCLUDE:
|
||||
kw = include(kw);
|
||||
if (!(kw = include(kw)))
|
||||
return free_opt(o);
|
||||
break;
|
||||
case WORD:
|
||||
o->val[0] = kw->name;
|
||||
@@ -381,27 +426,34 @@ get_option(struct keyword **head)
|
||||
while (kw && kw->type != LIST_END) {
|
||||
switch (kw->type) {
|
||||
case WORD:
|
||||
if (j > 9)
|
||||
if (j > (PARSE_MAX_LIST - 1)) {
|
||||
syntax(kw, "too much values in list");
|
||||
return free_opt(o);
|
||||
}
|
||||
o->val[j++] = kw->name;
|
||||
kw = kw->next;
|
||||
break;
|
||||
case INCLUDE:
|
||||
kw = include(kw);
|
||||
if (!(kw = include(kw)))
|
||||
return free_opt(o);
|
||||
break;
|
||||
default:
|
||||
syntax(kw, "declaration into a list");
|
||||
return free_opt(o);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!kw)
|
||||
if (!kw) {
|
||||
syntax(kw, "list unclosed");
|
||||
return free_opt(o);
|
||||
}
|
||||
|
||||
kw = kw->next;
|
||||
break;
|
||||
default:
|
||||
syntax(kw, "missing value");
|
||||
return free_opt(o);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -409,6 +461,29 @@ get_option(struct keyword **head)
|
||||
return o;
|
||||
}
|
||||
|
||||
static void *
|
||||
free_sec(struct conf_sec *sec)
|
||||
{
|
||||
struct conf_opt *o;
|
||||
struct conf_sec *s;
|
||||
|
||||
if (sec) {
|
||||
while (!SLIST_EMPTY(&sec->optlist)) {
|
||||
o = SLIST_FIRST(&sec->optlist);
|
||||
SLIST_REMOVE_HEAD(&sec->optlist, entry);
|
||||
free_opt(o);
|
||||
}
|
||||
while (!TAILQ_EMPTY(&sec->sub)) {
|
||||
s = TAILQ_FIRST(&sec->sub);
|
||||
TAILQ_REMOVE(&sec->sub, s, entry);
|
||||
free_sec(s);
|
||||
}
|
||||
free(sec->name);
|
||||
free(sec);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct conf_sec *
|
||||
get_section(struct keyword **head)
|
||||
{
|
||||
@@ -427,26 +502,32 @@ get_section(struct keyword **head)
|
||||
while (kw && kw->type != SEC_END) {
|
||||
switch (kw->type) {
|
||||
case INCLUDE:
|
||||
kw = include(kw);
|
||||
if (!(kw = include(kw)))
|
||||
return free_sec(s);
|
||||
break;
|
||||
case SEC_START:
|
||||
sub = get_section(&kw);
|
||||
if (!(sub = get_section(&kw)))
|
||||
return free_sec(s);
|
||||
TAILQ_INSERT_TAIL(&s->sub, sub, entry);
|
||||
s->nsub++;
|
||||
break;
|
||||
case WORD:
|
||||
o = get_option(&kw);
|
||||
if (!(o = get_option(&kw)))
|
||||
return free_sec(s);
|
||||
SLIST_INSERT_HEAD(&s->optlist, o, entry);
|
||||
s->nopt++;
|
||||
break;
|
||||
default:
|
||||
syntax(kw, "syntax error");
|
||||
return free_sec(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!kw || strcmp(kw->name, s->name))
|
||||
if (!kw || strcmp(kw->name, s->name)) {
|
||||
syntax(kw, "missing end section %s", s->name);
|
||||
return free_sec(s);
|
||||
}
|
||||
|
||||
kw = kw->next;
|
||||
*head = kw;
|
||||
@@ -454,6 +535,20 @@ get_section(struct keyword **head)
|
||||
return s;
|
||||
}
|
||||
|
||||
int
|
||||
free_conf(void)
|
||||
{
|
||||
struct conf_sec *s;
|
||||
struct keyword *kw = NULL;
|
||||
|
||||
while (!TAILQ_EMPTY(&config)) {
|
||||
s = TAILQ_FIRST(&config);
|
||||
TAILQ_REMOVE(&config, s, entry);
|
||||
free_sec(s);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
get_conf(const char *filename)
|
||||
{
|
||||
@@ -461,6 +556,7 @@ get_conf(const char *filename)
|
||||
struct keyword *head, *kw;
|
||||
|
||||
kw = head = parse_keywords(filename);
|
||||
|
||||
if (!head)
|
||||
return -1; /* TODO ERREUR */
|
||||
|
||||
@@ -469,14 +565,17 @@ get_conf(const char *filename)
|
||||
while (kw) {
|
||||
switch (kw->type) {
|
||||
case INCLUDE:
|
||||
kw = include(kw);
|
||||
if (!(kw = include(kw)))
|
||||
return free_conf();
|
||||
break;
|
||||
case SEC_START:
|
||||
s = get_section(&kw);
|
||||
if (!(s = get_section(&kw)))
|
||||
return free_conf();
|
||||
TAILQ_INSERT_TAIL(&config, s, entry);
|
||||
break;
|
||||
default:
|
||||
syntax(kw, "out of any section");
|
||||
return free_conf();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <sys/queue.h>
|
||||
|
||||
#define INCLUDE_CMD "@include"
|
||||
#define PARSE_MAX_LIST 10
|
||||
|
||||
#if defined(Bool)
|
||||
#define bool_t Bool
|
||||
@@ -29,7 +30,7 @@ typedef enum { False, True } bool_t;
|
||||
|
||||
struct conf_opt {
|
||||
char *name;
|
||||
char *val[10];
|
||||
char *val[PARSE_MAX_LIST];
|
||||
size_t nval;
|
||||
bool_t used;
|
||||
int line;
|
||||
@@ -70,7 +71,7 @@ void print_unused(struct conf_sec *s);
|
||||
* WARNING: This make all string
|
||||
* returned by fetch_(opt|section)(_first) unusable.
|
||||
*/
|
||||
void free_conf(struct conf_sec *s);
|
||||
int free_conf(void);
|
||||
|
||||
/*
|
||||
* Get all subsection matching the given name on the given
|
||||
|
||||
@@ -126,7 +126,7 @@ quit(void)
|
||||
IFREE(conf.client.mouse);
|
||||
IFREE(conf.root.mouse);
|
||||
|
||||
free_conf(NULL);
|
||||
free_conf();
|
||||
|
||||
XSync(dpy, False);
|
||||
XCloseDisplay(dpy);
|
||||
|
||||
Reference in New Issue
Block a user