/* Text file parser */
-static byte *name_parse_fb;
+static const char *name_parse_fb;
static struct fastbuf *parse_fb;
static uns line_num;
#define MAX_LINE 4096
-static byte line_buf[MAX_LINE];
-static byte *line = line_buf;
+static char line_buf[MAX_LINE];
+static char *line = line_buf;
#include "lib/bbuf.h"
static bb_t copy_buf;
static uns ends_by_brace; // the line is ended by "{"
static int
-get_line(void)
+get_line(char **msg)
{
- if (!bgets(parse_fb, line_buf, MAX_LINE))
- return 0;
+ int err = bgets_nodie(parse_fb, line_buf, MAX_LINE);
line_num++;
+ if (err <= 0) {
+ *msg = err < 0 ? "Line too long" : NULL;
+ return 0;
+ }
line = line_buf;
while (Cblank(*line))
line++;
}
static void
-append(byte *start, byte *end)
+append(char *start, char *end)
{
uns len = end - start;
bb_grow(©_buf, copied + len + 1);
copy_buf.ptr[copied-1] = 0;
}
-#define CONTROL_CHAR(x) (x == '{' || x == '}' || x == ';')
- // these characters separate words like blanks
-
-static byte *
+static char *
get_word(uns is_command_name)
{
+ char *msg;
if (*line == '\'') {
line++;
while (1) {
- byte *start = line;
+ char *start = line;
while (*line && *line != '\'')
line++;
append(start, line);
if (*line)
break;
copy_buf.ptr[copied-1] = '\n';
- if (!get_line())
- return "Unterminated apostrophe word at the end";
+ if (!get_line(&msg))
+ return msg ? : "Unterminated apostrophe word at the end";
}
line++;
line++;
uns start_copy = copied;
while (1) {
- byte *start = line;
+ char *start = line;
uns escape = 0;
while (*line) {
if (*line == '"' && !escape)
copy_buf.ptr[copied-1] = '\n';
else // merge two lines
copied -= 2;
- if (!get_line())
- return "Unterminated quoted word at the end";
+ if (!get_line(&msg))
+ return msg ? : "Unterminated quoted word at the end";
}
line++;
- byte *tmp = stk_str_unesc(copy_buf.ptr + start_copy);
+ char *tmp = stk_str_unesc(copy_buf.ptr + start_copy);
uns l = strlen(tmp);
bb_grow(©_buf, start_copy + l + 1);
strcpy(copy_buf.ptr + start_copy, tmp);
} else {
// promised that *line is non-null and non-blank
- byte *start = line;
- while (*line && !Cblank(*line) && !CONTROL_CHAR(*line)
+ char *start = line;
+ while (*line && !Cblank(*line)
+ && *line != '{' && *line != '}' && *line != ';'
&& (*line != '=' || !is_command_name))
line++;
if (*line == '=') { // nice for setting from a command-line
return NULL;
}
-static byte *
-get_token(uns is_command_name, byte **msg)
+static char *
+get_token(uns is_command_name, char **err)
{
- *msg = NULL;
+ *err = NULL;
while (1) {
if (!*line || *line == '#') {
- if (!is_command_name || !get_line())
+ if (!is_command_name || !get_line(err))
return NULL;
} else if (*line == ';') {
- *msg = get_word(0);
- if (!is_command_name || *msg)
+ *err = get_word(0);
+ if (!is_command_name || *err)
return NULL;
} else if (*line == '\\' && !line[1]) {
- if (!get_line()) {
- *msg = "Last line ends by a backslash";
+ if (!get_line(err)) {
+ if (!*err)
+ *err = "Last line ends by a backslash";
return NULL;
}
if (!*line || *line == '#')
- log(L_WARN, "The line %s:%d following a backslash is empty", name_parse_fb, line_num);
+ msg(L_WARN, "The line %s:%d following a backslash is empty", name_parse_fb ? : "", line_num);
} else {
split_grow(&word_buf, words+1);
uns start = copied;
word_buf.ptr[words++] = copied;
- *msg = get_word(is_command_name);
- return *msg ? NULL : copy_buf.ptr + start;
+ *err = get_word(is_command_name);
+ return *err ? NULL : copy_buf.ptr + start;
}
}
}
-static byte *
+static char *
split_command(void)
{
words = copied = ends_by_brace = 0;
- byte *msg, *start_word;
+ char *msg, *start_word;
if (!(start_word = get_token(1, &msg)))
return msg;
if (*start_word == '{') // only one opening brace
/* Parsing multiple files */
-static byte *
-parse_fastbuf(byte *name_fb, struct fastbuf *fb, uns depth)
+static char *
+parse_fastbuf(const char *name_fb, struct fastbuf *fb, uns depth)
{
- byte *msg;
+ char *err;
name_parse_fb = name_fb;
parse_fb = fb;
line_num = 0;
*line = 0;
while (1)
{
- msg = split_command();
- if (msg)
+ err = split_command();
+ if (err)
goto error;
if (!words)
return NULL;
- byte *name = copy_buf.ptr + word_buf.ptr[0];
- byte *pars[words-1];
+ char *name = copy_buf.ptr + word_buf.ptr[0];
+ char *pars[words-1];
for (uns i=1; i<words; i++)
pars[i-1] = copy_buf.ptr + word_buf.ptr[i];
if (!strcasecmp(name, "include"))
{
if (words != 2)
- msg = "Expecting one filename";
+ err = "Expecting one filename";
else if (depth > 8)
- msg = "Too many nested files";
+ err = "Too many nested files";
else if (*line && *line != '#') // because the contents of line_buf is not re-entrant and will be cleared
- msg = "The input command must be the last one on a line";
- if (msg)
+ err = "The input 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);
if (!new_fb) {
- msg = cf_printf("Cannot open file %s: %m", pars[0]);
+ err = cf_printf("Cannot open file %s: %m", pars[0]);
goto error;
}
uns ll = line_num;
- msg = parse_fastbuf(stk_strdup(pars[0]), new_fb, depth+1);
+ err = parse_fastbuf(stk_strdup(pars[0]), new_fb, depth+1);
line_num = ll;
bclose(new_fb);
- if (msg)
+ if (err)
goto error;
parse_fb = fb;
continue;
}
enum cf_operation op;
- byte *c = strchr(name, ':');
+ char *c = strchr(name, ':');
if (!c)
op = strcmp(name, "}") ? OP_SET : OP_CLOSE;
else {
switch (Clocase(*c)) {
case 's': op = OP_SET; break;
case 'c': op = Clocase(c[1]) == 'l' ? OP_CLEAR: OP_COPY; break;
- case 'a': op = Clocase(c[1]) == 'p' ? OP_APPEND : OP_AFTER; break;
+ case 'a': switch (Clocase(c[1])) {
+ case 'p': op = OP_APPEND; break;
+ case 'f': op = OP_AFTER; break;
+ default: op = OP_ALL;
+ }; break;
case 'p': op = OP_PREPEND; break;
case 'r': op = OP_REMOVE; break;
case 'e': op = OP_EDIT; break;
default: op = OP_SET; break;
}
if (strcasecmp(c, cf_op_names[op])) {
- msg = cf_printf("Unknown operation %s", c);
+ err = cf_printf("Unknown operation %s", c);
goto error;
}
}
if (ends_by_brace)
op |= OP_OPEN;
- msg = cf_interpret_line(name, op, words-1, pars);
- if (msg)
+ err = cf_interpret_line(name, op, words-1, pars);
+ if (err)
goto error;
}
error:
- log(L_ERROR, "File %s, line %d: %s", name_fb, line_num, msg);
+ if (name_fb)
+ msg(L_ERROR, "File %s, line %d: %s", name_fb, line_num, err);
+ else if (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);
return "included from here";
}
#ifndef DEFAULT_CONFIG
#define DEFAULT_CONFIG NULL
#endif
-byte *cf_def_file = DEFAULT_CONFIG;
+char *cf_def_file = DEFAULT_CONFIG;
+
+#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
-load_file(byte *file)
+load_file(const char *file)
{
cf_init_stack();
struct fastbuf *fb = bopen_try(file, O_RDONLY, 1<<14);
if (!fb) {
- log(L_ERROR, "Cannot open %s: %m", file);
+ msg(L_ERROR, "Cannot open %s: %m", file);
return 1;
}
- byte *msg = parse_fastbuf(file, fb, 0);
+ char *err_msg = parse_fastbuf(file, fb, 0);
bclose(fb);
- int err = !!msg || done_stack();
+ int err = !!err_msg || done_stack();
if (!err)
cf_def_file = NULL;
return err;
}
static int
-load_string(byte *string)
+load_string(const char *string)
{
cf_init_stack();
struct fastbuf fb;
- fbbuf_init_read(&fb, string, strlen(string), 0);
- byte *msg = parse_fastbuf("memory string", &fb, 0);
+ fbbuf_init_read(&fb, (byte *)string, strlen(string), 0);
+ char *msg = parse_fastbuf(NULL, &fb, 0);
return !!msg || done_stack();
}
/* Safe loading and reloading */
int
-cf_reload(byte *file)
+cf_reload(const char *file)
{
cf_journal_swap();
struct cf_journal_item *oldj = cf_journal_new_transaction(1);
}
int
-cf_load(byte *file)
+cf_load(const char *file)
{
struct cf_journal_item *oldj = cf_journal_new_transaction(1);
int err = load_file(file);
}
int
-cf_set(byte *string)
+cf_set(const char *string)
{
struct cf_journal_item *oldj = cf_journal_new_transaction(0);
int err = load_string(string);
load_default(void)
{
if (cf_def_file)
- if (cf_load(cf_def_file))
- die("Cannot load default config %s", 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);
+ }
}
static void