diff --git a/src/parse/parse.c b/src/parse/parse.c new file mode 100644 index 0000000..ef66d68 --- /dev/null +++ b/src/parse/parse.c @@ -0,0 +1,409 @@ +/* + * Copyright (c) 2010 Philippe Pepiot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define _BSD_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include "../wmfs.h" + + +static void pop_keyword(void); +static void pop_stack(void); +static struct conf_sec *get_section(void); +static struct conf_opt *get_option(void); +static struct opt_type string_to_opt(char *); +#ifdef DEBUG +static void print_kw_tree(void); +static char * get_kw_name(enum conf_type); +#endif + +static TAILQ_HEAD(, conf_keyword) keywords; +static TAILQ_HEAD(, conf_stack) stack; +static SLIST_HEAD(, conf_sec) config; +static struct conf_keyword *curk; /* current keyword */ +static struct conf_stack *curw; /* current word */ +static const struct opt_type opt_type_null = { 0, 0, False, NULL }; + +static struct { + char *name; + size_t line; +} file = { NULL, 1 }; + +void +get_keyword(const char *buf, size_t n) +{ + struct conf_keyword *kw; + size_t j, i; + struct conf_state s = { False, False, '\0' }; + struct conf_stack *e; + + TAILQ_INIT(&stack); + TAILQ_INIT(&keywords); + kw = malloc(sizeof(*kw)); + e = malloc(sizeof(*e)); + + for(i = 0, j = 0; i < n; i++) + { + if (buf[i] == '\n' && s.comment == True) { + s.comment = False; + continue; + } + + if (buf[i] == '#' && s.quote == False) { + s.comment = True; + continue; + } + + if (s.comment == True) + continue; + + if (buf[i] == s.quote_char && s.quote == True) { + NEW_WORD(); + s.quote = False; + continue; + } + + if ((buf[i] == '"' || buf[i] == '\'') && + s.quote == False) + { + s.quote_char = buf[i]; + s.quote = True; + continue; + } + + if (buf[i] == '[' && s.quote == False) { + NEW_WORD(); + TOKEN((buf[i+1] == '/') ? SEC_END : SEC_START); + if (buf[i+1] == '/') + i++; + continue; + } + + if (buf[i] == ']' && s.quote == False) { + NEW_WORD(); + continue; + } + + if (buf[i] == '{' && s.quote == False) { + NEW_WORD(); + TOKEN(LIST_START); + continue; + } + + if (buf[i] == '}' && s.quote == False) { + NEW_WORD(); + TOKEN(LIST_END); + continue; + } + + if (buf[i] == ',' && s.quote == False) { + NEW_WORD(); + continue; + } + + if (buf[i] == '=' && s.quote == False) { + NEW_WORD(); + TOKEN(EQUAL); + continue; + } + + if (strchr("\t\n ", buf[i]) && s.quote == False) { + if (buf[i] == '\n') + file.line++; + NEW_WORD(); + continue; + } + + e->name[j++] = buf[i]; + } +} + +#ifdef DEBUG +static void +print_kw_tree(void) +{ + struct conf_keyword *k; + struct conf_stack *s; + + s = TAILQ_FIRST(&stack); + + TAILQ_FOREACH(k, &keywords, entry) + printf("%s ", get_kw_name(k->type)); + printf("\n"); +} + +static char * +get_kw_name(enum conf_type type) +{ + switch (type) { + case SEC_START: + return ("SEC_START"); + break; + case SEC_END: + return ("SEC_END"); + break; + case WORD: + return ("WORD"); + break; + case LIST_START: + return ("LIST_START "); + break; + case LIST_END: + return ("LIST_END "); + break; + case EQUAL: + return ("EQUAL "); + break; + default: + return ("NONE "); + break; + } +} +#endif + +void +get_conf(void) +{ + struct conf_sec *s; + + curk = TAILQ_FIRST(&keywords); + curw = TAILQ_FIRST(&stack); + + SLIST_INIT(&config); + + while (!TAILQ_EMPTY(&keywords)) { + switch (curk->type) { + case SEC_START: + s = get_section(); + SLIST_INSERT_HEAD(&config, s, entry); + break; + default: + errx(1, "%s:%d: near '%s', config out of any section", + file.name, curk->line, curw->name); + break; + } + } +} + +static struct conf_sec * +get_section(void) +{ + struct conf_sec *s; + struct conf_opt *o; + struct conf_sec *sub; + + s = calloc(1, sizeof(*s)); + s->name = strdup(curw->name); + pop_stack(); + pop_keyword(); + + if (curk->type != WORD) + errx(1, "%s:%d: near '%s', missing section name", + file.name, curk->line, curw->name); + pop_keyword(); + + while (curk->type != SEC_END) { + switch (curk->type) { + case WORD: + o = get_option(); + SLIST_INSERT_HEAD(&s->optlist, o, entry); + s->nopt++; + break; + case SEC_START: + sub = get_section(); + SLIST_INSERT_HEAD(&s->sub, sub, entry); + s->nsub++; + case SEC_END: + break; + default: + errx(1, "%s:%d: near '%s', syntax error", + file.name, curk->line, curw->name); + break; + } + } + pop_keyword(); + + if (curk->type != WORD) + errx(1, "%s:%d: near '%s', missing end-section name", + file.name, curk->line, curw->name); + + if (strcmp(curw->name, s->name)) + errx(1, "%s:%d: near '%s', non-closed section '%s'", + file.name, curk->line, curw->name, s->name); + + pop_stack(); + pop_keyword(); + return s; +} + + +static struct conf_opt * +get_option(void) +{ + struct conf_opt *o; + size_t j = 0; + + o = calloc(1, sizeof(*o)); + o->name = strdup(curw->name); + pop_stack(); + pop_keyword(); + + if (curk->type != EQUAL) + errx(1, "%s:%d: near '%s', missing '=' here", + file.name, curk->line, curw->name); + + pop_keyword(); + + switch (curk->type) { + case WORD: + o->val[0] = strdup(curw->name); + o->val[1] = NULL; + pop_stack(); + break; + case LIST_START: + pop_keyword(); + while (curk->type != LIST_END) { + if (curk->type != WORD) + errx(1, "%s:%d: near '%s', declaration into a list", + file.name, curk->line, curw->name); + o->val[j++] = strdup(curw->name); + pop_stack(); + pop_keyword(); + } + o->val[j] = NULL; + break; + default: + errx(1, "%s:%d: near '%s', syntax error", + file.name, curk->line, curw->name); + break; + } + pop_keyword(); + return o; +} + + +static void +pop_keyword(void) +{ + TAILQ_REMOVE(&keywords, curk, entry); +#ifdef DEBUG + warnx("%s", get_kw_name(curk->type)); +#endif + free(curk); + + curk = TAILQ_FIRST(&keywords); +} + +static void +pop_stack(void) +{ + TAILQ_REMOVE(&stack, curw, entry); +#ifdef DEBUG + warnx("%s", curw->name); +#endif + free(curw); + + curw = TAILQ_FIRST(&stack); +} + + +struct conf_sec ** +fetch_section(struct conf_sec *s, char *name) +{ + struct conf_sec **ret; + struct conf_sec *sec; + size_t i = 0; + + if (!name) + return NULL; + + if (!s) { + ret = malloc(2 * sizeof(struct conf_sec *)); + SLIST_FOREACH(sec, &config, entry) + if (!strcmp(sec->name, name)) { + ret[0] = sec; + ret[1] = NULL; + } + } + else { + ret = calloc(s->nsub, sizeof(struct conf_sec *)); + SLIST_FOREACH(sec, &s->sub, entry) { + if (!strcmp(sec->name, name) && i < s->nsub) + ret[i++] = sec; + } + ret[i] = NULL; + } + return ret; +} + +struct opt_type * +fetch_opt(struct conf_sec *s, char *dfl, char *name) +{ + struct conf_opt *o; + struct opt_type *ret; + size_t i = 0; + + if (!name) + return NULL; + + ret = calloc(10, sizeof(struct opt_type)); + + if (s) { + SLIST_FOREACH(o, &s->optlist, entry) + if (!strcmp(o->name, name)) { + while (o->val[i]) { + ret[i] = string_to_opt(o->val[i]); + i++; + } + ret[i] = opt_type_null; + return ret; + } + } + + ret[0] = string_to_opt(dfl); + ret[1] = opt_type_null; + + return ret; +} + +static struct opt_type +string_to_opt(char *s) +{ + struct opt_type ret = opt_type_null; + + if (!s || !strlen(s)) + return ret; + + ret.num = atoi(s); + sscanf(s, "%f", &ret.fnum); + + if (!strcmp(s, "true") || !strcmp(s, "True") || + !strcmp(s, "TRUE") || !strcmp(s, "1")) + ret.bool = True; + else + ret.bool = False; + + ret.str = strdup(s); + + return ret; +} + diff --git a/src/parse/parse.h b/src/parse/parse.h new file mode 100644 index 0000000..72e7b40 --- /dev/null +++ b/src/parse/parse.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2010 Philippe Pepiot + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef PARSE_H +#define PARSE_H + +#include + +#if !defined(WMFS_H) +typedef enum { False, True } Bool; +#endif + +enum conf_type { SEC_START, SEC_END, WORD, EQUAL, LIST_START, LIST_END, NONE }; + +#define TOKEN(t) \ + do { \ + kw->type = (t); \ + kw->line = file.line; \ + TAILQ_INSERT_TAIL(&keywords, kw, entry); \ + kw = malloc(sizeof(*kw)); \ + } while (0) + +#define NEW_WORD() \ + do { \ + if (j > 0) { \ + e->name[j] = '\0'; \ + TAILQ_INSERT_TAIL(&stack, e, entry); \ + e = malloc(sizeof(*e)); \ + j = 0; \ + TOKEN(WORD); \ + } \ + } while (0) + +struct conf_keyword { + enum conf_type type; + size_t line; + TAILQ_ENTRY(conf_keyword) entry; +}; + +struct conf_stack { + char name[BUFSIZ]; + TAILQ_ENTRY(conf_stack) entry; +}; + +struct conf_state { + Bool quote; + Bool comment; + char quote_char; +}; + +struct conf_opt { + char *name; + char *val[10]; + SLIST_ENTRY(conf_opt) entry; + size_t nval; +}; + +struct conf_sec { + char *name; + SLIST_HEAD(, conf_opt) optlist; + SLIST_HEAD(, conf_sec) sub; + SLIST_ENTRY(conf_sec) entry; + size_t nopt; + size_t nsub; +}; + +struct opt_type { + long int num; + float fnum; + Bool bool; + char *str; +}; + +void get_keyword(const char *, size_t); +void get_conf(void); +struct conf_sec **fetch_section(struct conf_sec *, char *); +struct opt_type *fetch_opt(struct conf_sec *, char *, char *); + +#endif /* PARSE_H */