#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 <stdlib.h>
#include <string.h>
+#include <errno.h>
#include <fcntl.h>
/* Text file parser */
#include <ucw/bbuf.h>
-#define GBUF_TYPE uns
+#define GBUF_TYPE uint
#define GBUF_PREFIX(x) split_##x
#include <ucw/gbuf.h>
struct cf_parser_state {
const char *name_parse_fb;
struct fastbuf *parse_fb;
- uns line_num;
+ uint line_num;
char *line;
split_t word_buf;
- uns words;
- uns ends_by_brace; // the line is ended by "{"
+ uint words;
+ uint ends_by_brace; // the line is ended by "{"
bb_t copy_buf;
- uns copied;
+ uint copied;
char line_buf[];
};
static void
append(struct cf_parser_state *p, char *start, char *end)
{
- uns len = end - start;
+ uint len = end - start;
bb_grow(&p->copy_buf, p->copied + len + 1);
memcpy(p->copy_buf.ptr + p->copied, start, len);
p->copied += len + 1;
}
static char *
-get_word(struct cf_parser_state *p, uns is_command_name)
+get_word(struct cf_parser_state *p, uint is_command_name)
{
char *msg;
char *line = p->line;
} else if (*line == '"') {
line++;
- uns start_copy = p->copied;
+ uint start_copy = p->copied;
while (1) {
char *start = line;
- uns escape = 0;
+ uint escape = 0;
while (*line) {
if (*line == '"' && !escape)
break;
line++;
char *tmp = stk_str_unesc(p->copy_buf.ptr + start_copy);
- uns l = strlen(tmp);
+ uint l = strlen(tmp);
bb_grow(&p->copy_buf, start_copy + l + 1);
strcpy(p->copy_buf.ptr + start_copy, tmp);
p->copied = start_copy + l + 1;
}
static char *
-get_token(struct cf_parser_state *p, uns is_command_name, char **err)
+get_token(struct cf_parser_state *p, uint is_command_name, char **err)
{
*err = NULL;
while (1) {
msg(L_WARN, "The line %s:%d following a backslash is empty", p->name_parse_fb ? : "", p->line_num);
} else {
split_grow(&p->word_buf, p->words+1);
- uns start = p->copied;
+ uint 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;
/* 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(struct cf_context *cc, const char *name_fb, struct fastbuf *fb, uns depth)
+parse_fastbuf(struct cf_context *cc, const char *name_fb, struct fastbuf *fb, uint depth)
{
struct cf_parser_state *p = cc->parser;
if (!p)
p->line = p->line_buf;
*p->line = 0;
- char *err;
+ if (!depth)
+ cf_init_stack(cc);
+
+ char *err = NULL;
while (1)
{
err = split_command(p);
if (err)
goto error;
if (!p->words)
- return NULL;
+ break;
char *name = p->copy_buf.ptr + p->word_buf.ptr[0];
char *pars[p->words-1];
- for (uns i=1; i<p->words; i++)
+ for (uint i=1; i<p->words; i++)
pars[i-1] = p->copy_buf.ptr + p->word_buf.ptr[i];
- if (!strcasecmp(name, "include"))
+ int optional_include = !strcasecmp(name, "optionalinclude");
+ if (optional_include || !strcasecmp(name, "include"))
{
if (p->words != 2)
err = "Expecting one filename";
goto error;
struct fastbuf *new_fb = bopen_try(pars[0], O_RDONLY, 1<<14);
if (!new_fb) {
+ if (optional_include && errno == ENOENT)
+ continue;
err = cf_printf("Cannot open file %s: %m", pars[0]);
goto error;
}
- uns ll = p->line_num;
+ uint 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;
}
+
+ 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, p->line_num, err);
return "included from here";
}
-static int
-done_stack(struct cf_context *cc)
-{
- if (cf_check_stack(cc))
- return 1;
- 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 int
load_file(struct cf_context *cc, const char *file)
{
- cf_init_stack(cc);
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(cc, file, fb, 0);
bclose(fb);
- return !!err_msg || done_stack(cc);
+ return !!err_msg;
}
static int
load_string(struct cf_context *cc, const char *string)
{
- cf_init_stack(cc);
struct fastbuf fb;
fbbuf_init_read(&fb, (byte *)string, strlen(string), 0);
char *msg = parse_fastbuf(cc, NULL, &fb, 0);
- return !!msg || done_stack(cc);
+ return !!msg;
}
/* Safe loading and reloading */
};
static void
-cf_remember_entry(struct cf_context *cc, uns type, const char *arg)
+cf_remember_entry(struct cf_context *cc, uint type, const char *arg)
{
- if (!cc->need_journal)
- return;
- if (!cc->postpone_commit)
+ if (!cc->enable_journal)
return;
struct conf_entry *ce = cf_malloc(sizeof(*ce));
ce->type = type;
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 = cc->everything_committed;
+ uint ec = cc->everything_committed;
cc->everything_committed = 0;
clist old_entries;
clist_move(&old_entries, &cc->conf_entries);
- cc->postpone_commit = 1;
+ cf_open_group();
int err = 0;
if (file)
cf_remember_entry(cc, ce->type, ce->arg);
}
- cc->postpone_commit = 0;
- if (!err)
- err |= done_stack(cc);
+ err |= cf_close_group();
if (!err) {
cf_journal_delete();
if (!err) {
cf_journal_commit_transaction(1, oldj);
cf_remember_entry(cc, CE_FILE, file);
- cc->def_loaded = 1;
+ cc->config_loaded = 1;
} else
cf_journal_rollback_transaction(1, oldj);
return err;
return err;
}
-/* Command-line parser */
-
-#ifndef CONFIG_UCW_DEFAULT_CONFIG
-#define CONFIG_UCW_DEFAULT_CONFIG NULL
-#endif
-char *cf_def_file = CONFIG_UCW_DEFAULT_CONFIG;
-
-#ifndef CONFIG_UCW_ENV_VAR_CONFIG
-#define CONFIG_UCW_ENV_VAR_CONFIG NULL
-#endif
-char *cf_env_file = CONFIG_UCW_ENV_VAR_CONFIG;
-
-static void
-load_default(struct cf_context *cc)
+void
+cf_revert(void)
{
- if (cc->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(cc);
- done_stack(cc);
- cf_journal_commit_transaction(1, oldj);
- }
+ cf_journal_swap();
+ cf_journal_delete();
}
-static void
-final_commit(struct cf_context *cc)
+void
+cf_open_group(void)
{
- if (cc->postpone_commit) {
- cc->postpone_commit = 0;
- if (done_stack(cc))
- 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)
{
- struct cf_context *cc = cf_obtain_context();
- cc->postpone_commit = 1;
-
- while (1) {
- int res = getopt_long (argc, argv, short_opts, long_opts, long_index);
- if (res == 'S' || res == 'C' || res == 0x64436667)
- {
- if (cc->other_options)
- die("The -S and -C options must precede all other arguments");
- if (res == 'S') {
- load_default(cc);
- 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_UCW_DEBUG
- else { /* --dumpconfig */
- load_default(cc);
- final_commit(cc);
- 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(cc);
- final_commit(cc);
- }
- cc->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;
}