X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;ds=sidebyside;f=ucw%2Fconf-input.c;h=ddfef9bd621fa954e6e41e88454b7b8682a84d3f;hb=6efdc514c193f18c9ef840096750c37e78a01bf6;hp=fed6a8282b45feb649ed7c2708289ac1f23beed0;hpb=c990a38bac73f158715c17d49544bc93d94c3ea6;p=libucw.git diff --git a/ucw/conf-input.c b/ucw/conf-input.c index fed6a828..ddfef9bd 100644 --- a/ucw/conf-input.c +++ b/ucw/conf-input.c @@ -2,22 +2,21 @@ * UCW Library -- Configuration files: parsing input streams * * (c) 2001--2006 Robert Spalek - * (c) 2003--2009 Martin Mares + * (c) 2003--2012 Martin Mares * * This software may be freely distributed and used according to the terms * of the GNU Lesser General Public License. */ -#include "ucw/lib.h" -#include "ucw/conf.h" -#include "ucw/getopt.h" -#include "ucw/conf-internal.h" -#include "ucw/clists.h" -#include "ucw/mempool.h" -#include "ucw/fastbuf.h" -#include "ucw/chartype.h" -#include "ucw/string.h" -#include "ucw/stkstring.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -25,72 +24,77 @@ /* Text file parser */ -static const char *name_parse_fb; -static struct fastbuf *parse_fb; -static uns line_num; - #define MAX_LINE 4096 -static char line_buf[MAX_LINE]; -static char *line = line_buf; -#include "ucw/bbuf.h" -static bb_t copy_buf; -static uns copied; +#include #define GBUF_TYPE uns #define GBUF_PREFIX(x) split_##x -#include "ucw/gbuf.h" -static split_t word_buf; -static uns words; -static uns ends_by_brace; // the line is ended by "{" +#include + +struct cf_parser_state { + const char *name_parse_fb; + struct fastbuf *parse_fb; + uns line_num; + char *line; + split_t word_buf; + uns words; + uns ends_by_brace; // the line is ended by "{" + bb_t copy_buf; + uns copied; + char line_buf[]; +}; static int -get_line(char **msg) +get_line(struct cf_parser_state *p, char **msg) { - int err = bgets_nodie(parse_fb, line_buf, MAX_LINE); - line_num++; + int err = bgets_nodie(p->parse_fb, p->line_buf, MAX_LINE); + p->line_num++; if (err <= 0) { *msg = err < 0 ? "Line too long" : NULL; return 0; } - line = line_buf; - while (Cblank(*line)) - line++; + p->line = p->line_buf; + while (Cblank(*p->line)) + p->line++; return 1; } static void -append(char *start, char *end) +append(struct cf_parser_state *p, char *start, char *end) { uns len = end - start; - bb_grow(©_buf, copied + len + 1); - memcpy(copy_buf.ptr + copied, start, len); - copied += len + 1; - copy_buf.ptr[copied-1] = 0; + bb_grow(&p->copy_buf, p->copied + len + 1); + memcpy(p->copy_buf.ptr + p->copied, start, len); + p->copied += len + 1; + p->copy_buf.ptr[p->copied-1] = 0; } static char * -get_word(uns is_command_name) +get_word(struct cf_parser_state *p, uns is_command_name) { char *msg; + char *line = p->line; + if (*line == '\'') { line++; while (1) { char *start = line; while (*line && *line != '\'') line++; - append(start, line); + append(p, start, line); if (*line) break; - copy_buf.ptr[copied-1] = '\n'; - if (!get_line(&msg)) + p->copy_buf.ptr[p->copied-1] = '\n'; + if (!get_line(p, &msg)) return msg ? : "Unterminated apostrophe word at the end"; + line = p->line; } line++; } else if (*line == '"') { line++; - uns start_copy = copied; + uns start_copy = p->copied; while (1) { char *start = line; uns escape = 0; @@ -103,23 +107,24 @@ get_word(uns is_command_name) escape = 0; line++; } - append(start, line); + append(p, start, line); if (*line) break; if (!escape) - copy_buf.ptr[copied-1] = '\n'; + p->copy_buf.ptr[p->copied-1] = '\n'; else // merge two lines - copied -= 2; - if (!get_line(&msg)) + p->copied -= 2; + if (!get_line(p, &msg)) return msg ? : "Unterminated quoted word at the end"; + line = p->line; } line++; - char *tmp = stk_str_unesc(copy_buf.ptr + start_copy); + char *tmp = stk_str_unesc(p->copy_buf.ptr + start_copy); uns l = strlen(tmp); - bb_grow(©_buf, start_copy + l + 1); - strcpy(copy_buf.ptr + start_copy, tmp); - copied = start_copy + l + 1; + bb_grow(&p->copy_buf, start_copy + l + 1); + strcpy(p->copy_buf.ptr + start_copy, tmp); + p->copied = start_copy + l + 1; } else { // promised that *line is non-null and non-blank @@ -135,59 +140,60 @@ get_word(uns is_command_name) } if (line == start) // already the first char is control line++; - append(start, line); + append(p, start, line); } while (Cblank(*line)) line++; + p->line = line; return NULL; } static char * -get_token(uns is_command_name, char **err) +get_token(struct cf_parser_state *p, uns is_command_name, char **err) { *err = NULL; while (1) { - if (!*line || *line == '#') { - if (!is_command_name || !get_line(err)) + if (!*p->line || *p->line == '#') { + if (!is_command_name || !get_line(p, err)) return NULL; - } else if (*line == ';') { - *err = get_word(0); + } else if (*p->line == ';') { + *err = get_word(p, 0); if (!is_command_name || *err) return NULL; - } else if (*line == '\\' && !line[1]) { - if (!get_line(err)) { + } else if (*p->line == '\\' && !p->line[1]) { + if (!get_line(p, err)) { if (!*err) *err = "Last line ends by a backslash"; return NULL; } - if (!*line || *line == '#') - msg(L_WARN, "The line %s:%d following a backslash is empty", name_parse_fb ? : "", line_num); + if (!*p->line || *p->line == '#') + msg(L_WARN, "The line %s:%d following a backslash is empty", p->name_parse_fb ? : "", p->line_num); } else { - split_grow(&word_buf, words+1); - uns start = copied; - word_buf.ptr[words++] = copied; - *err = get_word(is_command_name); - return *err ? NULL : copy_buf.ptr + start; + split_grow(&p->word_buf, p->words+1); + uns start = p->copied; + p->word_buf.ptr[p->words++] = p->copied; + *err = get_word(p, is_command_name); + return *err ? NULL : p->copy_buf.ptr + start; } } } static char * -split_command(void) +split_command(struct cf_parser_state *p) { - words = copied = ends_by_brace = 0; + p->words = p->copied = p->ends_by_brace = 0; char *msg, *start_word; - if (!(start_word = get_token(1, &msg))) + if (!(start_word = get_token(p, 1, &msg))) return msg; if (*start_word == '{') // only one opening brace return "Unexpected opening brace"; - while (*line != '}') // stays for the next time + while (*p->line != '}') // stays for the next time { - if (!(start_word = get_token(0, &msg))) + if (!(start_word = get_token(p, 0, &msg))) return msg; if (*start_word == '{') { - words--; // discard the brace - ends_by_brace = 1; + p->words--; // discard the brace + p->ends_by_brace = 1; break; } } @@ -196,34 +202,51 @@ split_command(void) /* Parsing multiple files */ +static int +maybe_commit(struct cf_context *cc) +{ + if (cf_commit_all(cc->postpone_commit ? CF_NO_COMMIT : cc->everything_committed ? CF_COMMIT : CF_COMMIT_ALL)) + return 1; + if (!cc->postpone_commit) + cc->everything_committed = 1; + return 0; +} + static char * -parse_fastbuf(const char *name_fb, struct fastbuf *fb, uns depth) +parse_fastbuf(struct cf_context *cc, const char *name_fb, struct fastbuf *fb, uns depth) { - char *err; - name_parse_fb = name_fb; - parse_fb = fb; - line_num = 0; - line = line_buf; - *line = 0; + struct cf_parser_state *p = cc->parser; + if (!p) + p = cc->parser = xmalloc_zero(sizeof(*p) + MAX_LINE); + p->name_parse_fb = name_fb; + p->parse_fb = fb; + p->line_num = 0; + p->line = p->line_buf; + *p->line = 0; + + if (!depth) + cf_init_stack(cc); + + char *err = NULL; while (1) { - err = split_command(); + err = split_command(p); if (err) goto error; - if (!words) - return NULL; - char *name = copy_buf.ptr + word_buf.ptr[0]; - char *pars[words-1]; - for (uns i=1; iwords) + break; + char *name = p->copy_buf.ptr + p->word_buf.ptr[0]; + char *pars[p->words-1]; + for (uns i=1; iwords; i++) + pars[i-1] = p->copy_buf.ptr + p->word_buf.ptr[i]; if (!strcasecmp(name, "include")) { - if (words != 2) + if (p->words != 2) err = "Expecting one filename"; else if (depth > 8) err = "Too many nested files"; - else if (*line && *line != '#') // because the contents of line_buf is not re-entrant and will be cleared - err = "The input command must be the last one on a line"; + else if (*p->line && *p->line != '#') // because the contents of line_buf is not re-entrant and will be cleared + err = "The include command must be the last one on a line"; if (err) goto error; struct fastbuf *new_fb = bopen_try(pars[0], O_RDONLY, 1<<14); @@ -231,13 +254,13 @@ parse_fastbuf(const char *name_fb, struct fastbuf *fb, uns depth) err = cf_printf("Cannot open file %s: %m", pars[0]); goto error; } - uns ll = line_num; - err = parse_fastbuf(stk_strdup(pars[0]), new_fb, depth+1); - line_num = ll; + uns ll = p->line_num; + err = parse_fastbuf(cc, stk_strdup(pars[0]), new_fb, depth+1); + p->line_num = ll; bclose(new_fb); if (err) goto error; - parse_fb = fb; + p->parse_fb = fb; continue; } enum cf_operation op; @@ -265,70 +288,53 @@ parse_fastbuf(const char *name_fb, struct fastbuf *fb, uns depth) goto error; } } - if (ends_by_brace) + if (p->ends_by_brace) op |= OP_OPEN; - err = cf_interpret_line(name, op, words-1, pars); + err = cf_interpret_line(cc, name, op, p->words-1, pars); if (err) goto error; } + + if (!depth) + { + if (cf_done_stack(cc)) + err = "Unterminated block"; + else if (maybe_commit(cc)) + err = "Commit failed"; + } + if (!err) + return NULL; + error: if (name_fb) - msg(L_ERROR, "File %s, line %d: %s", name_fb, line_num, err); - else if (line_num == 1) + msg(L_ERROR, "File %s, line %d: %s", name_fb, p->line_num, err); + else if (p->line_num == 1) msg(L_ERROR, "Manual setting of configuration: %s", err); else - msg(L_ERROR, "Manual setting of configuration, line %d: %s", line_num, err); + msg(L_ERROR, "Manual setting of configuration, line %d: %s", p->line_num, err); return "included from here"; } -#ifndef DEFAULT_CONFIG -#define DEFAULT_CONFIG NULL -#endif -char *cf_def_file = DEFAULT_CONFIG; -static int cf_def_loaded; - -#ifndef ENV_VAR_CONFIG -#define ENV_VAR_CONFIG NULL -#endif -char *cf_env_file = ENV_VAR_CONFIG; - -static uns postpone_commit; // only for cf_getopt() -static uns everything_committed; // after the 1st load, this flag is set on - -static int -done_stack(void) -{ - if (cf_check_stack()) - return 1; - if (cf_commit_all(postpone_commit ? CF_NO_COMMIT : everything_committed ? CF_COMMIT : CF_COMMIT_ALL)) - return 1; - if (!postpone_commit) - everything_committed = 1; - return 0; -} - static int -load_file(const char *file) +load_file(struct cf_context *cc, const char *file) { - cf_init_stack(); struct fastbuf *fb = bopen_try(file, O_RDONLY, 1<<14); if (!fb) { - msg(L_ERROR, "Cannot open %s: %m", file); + msg(L_ERROR, "Cannot open configuration file %s: %m", file); return 1; } - char *err_msg = parse_fastbuf(file, fb, 0); + char *err_msg = parse_fastbuf(cc, file, fb, 0); bclose(fb); - return !!err_msg || done_stack(); + return !!err_msg; } static int -load_string(const char *string) +load_string(struct cf_context *cc, const char *string) { - cf_init_stack(); struct fastbuf fb; fbbuf_init_read(&fb, (byte *)string, strlen(string), 0); - char *msg = parse_fastbuf(NULL, &fb, 0); - return !!msg || done_stack(); + char *msg = parse_fastbuf(cc, NULL, &fb, 0); + return !!msg; } /* Safe loading and reloading */ @@ -342,61 +348,55 @@ struct conf_entry { /* We remember a list of actions to apply upon reload */ char *arg; }; -static clist conf_entries; - static void -cf_remember_entry(uns type, const char *arg) +cf_remember_entry(struct cf_context *cc, uns type, const char *arg) { - if (!cf_need_journal) - return; - if (!postpone_commit) + if (!cc->enable_journal) return; struct conf_entry *ce = cf_malloc(sizeof(*ce)); ce->type = type; ce->arg = cf_strdup(arg); - clist_add_tail(&conf_entries, &ce->n); + clist_add_tail(&cc->conf_entries, &ce->n); } int cf_reload(const char *file) { + struct cf_context *cc = cf_get_context(); + ASSERT(cc->enable_journal); cf_journal_swap(); struct cf_journal_item *oldj = cf_journal_new_transaction(1); - uns ec = everything_committed; - everything_committed = 0; + uns ec = cc->everything_committed; + cc->everything_committed = 0; - if (!conf_entries.head.next) - clist_init(&conf_entries); clist old_entries; - clist_move(&old_entries, &conf_entries); - postpone_commit = 1; + clist_move(&old_entries, &cc->conf_entries); + cf_open_group(); int err = 0; if (file) - err = load_file(file); + err = load_file(cc, file); else CLIST_FOR_EACH(struct conf_entry *, ce, old_entries) { if (ce->type == CE_FILE) - err |= load_file(ce->arg); + err |= load_file(cc, ce->arg); else - err |= load_string(ce->arg); + err |= load_string(cc, ce->arg); if (err) break; - cf_remember_entry(ce->type, ce->arg); + cf_remember_entry(cc, ce->type, ce->arg); } - postpone_commit = 0; - if (!err) - err |= done_stack(); + err |= cf_close_group(); if (!err) { cf_journal_delete(); cf_journal_commit_transaction(1, NULL); } else { - everything_committed = ec; + cc->everything_committed = ec; cf_journal_rollback_transaction(1, oldj); cf_journal_swap(); - clist_move(&conf_entries, &old_entries); + clist_move(&cc->conf_entries, &old_entries); } return err; } @@ -404,12 +404,13 @@ cf_reload(const char *file) int cf_load(const char *file) { + struct cf_context *cc = cf_get_context(); struct cf_journal_item *oldj = cf_journal_new_transaction(1); - int err = load_file(file); + int err = load_file(cc, file); if (!err) { cf_journal_commit_transaction(1, oldj); - cf_remember_entry(CE_FILE, file); - cf_def_loaded = 1; + cf_remember_entry(cc, CE_FILE, file); + cc->config_loaded = 1; } else cf_journal_rollback_transaction(1, oldj); return err; @@ -418,93 +419,38 @@ cf_load(const char *file) int cf_set(const char *string) { + struct cf_context *cc = cf_get_context(); struct cf_journal_item *oldj = cf_journal_new_transaction(0); - int err = load_string(string); + int err = load_string(cc, string); if (!err) { cf_journal_commit_transaction(0, oldj); - cf_remember_entry(CE_STRING, string); + cf_remember_entry(cc, CE_STRING, string); } else cf_journal_rollback_transaction(0, oldj); return err; } -/* Command-line parser */ - -static void -load_default(void) +void +cf_revert(void) { - if (cf_def_loaded++) - return; - if (cf_def_file) - { - char *env; - if (cf_env_file && (env = getenv(cf_env_file))) - { - if (cf_load(env)) - die("Cannot load config file %s", env); - } - else if (cf_load(cf_def_file)) - die("Cannot load default config %s", cf_def_file); - } - else - { - // We need to create an empty pool and initialize all configuration items - struct cf_journal_item *oldj = cf_journal_new_transaction(1); - cf_init_stack(); - done_stack(); - cf_journal_commit_transaction(1, oldj); - } + cf_journal_swap(); + cf_journal_delete(); } -static void -final_commit(void) +void +cf_open_group(void) { - if (postpone_commit) { - postpone_commit = 0; - if (done_stack()) - die("Cannot commit after the initialization"); - } + struct cf_context *cc = cf_get_context(); + cc->postpone_commit++; } int -cf_getopt(int argc, char * const argv[], const char *short_opts, const struct option *long_opts, int *long_index) +cf_close_group(void) { - clist_init(&conf_entries); - postpone_commit = 1; - - static int other_options = 0; - while (1) { - int res = getopt_long (argc, argv, short_opts, long_opts, long_index); - if (res == 'S' || res == 'C' || res == 0x64436667) - { - if (other_options) - die("The -S and -C options must precede all other arguments"); - if (res == 'S') { - load_default(); - if (cf_set(optarg)) - die("Cannot set %s", optarg); - } else if (res == 'C') { - if (cf_load(optarg)) - die("Cannot load config file %s", optarg); - } -#ifdef CONFIG_DEBUG - else { /* --dumpconfig */ - load_default(); - final_commit(); - struct fastbuf *b = bfdopen(1, 4096); - cf_dump_sections(b); - bclose(b); - exit(0); - } -#endif - } else { - /* unhandled option or end of options */ - if (res != ':' && res != '?') { - load_default(); - final_commit(); - } - other_options++; - return res; - } - } + struct cf_context *cc = cf_get_context(); + ASSERT(cc->postpone_commit); + if (!--cc->postpone_commit) + return maybe_commit(cc); + else + return 0; }