2 * UCW Library -- Configuration files: parsing input streams
4 * (c) 2001--2006 Robert Spalek <robert@ucw.cz>
5 * (c) 2003--2006 Martin Mares <mj@ucw.cz>
7 * This software may be freely distributed and used according to the terms
8 * of the GNU Lesser General Public License.
13 #include "lib/getopt.h"
14 #include "lib/conf-internal.h"
15 #include "lib/mempool.h"
16 #include "lib/fastbuf.h"
17 #include "lib/chartype.h"
18 #include "lib/stkstring.h"
24 /* Text file parser */
26 static byte *name_parse_fb;
27 static struct fastbuf *parse_fb;
31 static byte line_buf[MAX_LINE];
32 static byte *line = line_buf;
39 #define GBUF_PREFIX(x) split_##x
41 static split_t word_buf;
43 static uns ends_by_brace; // the line is ended by "{"
48 if (!bgets(parse_fb, line_buf, MAX_LINE))
58 append(byte *start, byte *end)
60 uns len = end - start;
61 bb_grow(©_buf, copied + len + 1);
62 memcpy(copy_buf.ptr + copied, start, len);
64 copy_buf.ptr[copied-1] = 0;
67 #define CONTROL_CHAR(x) (x == '{' || x == '}' || x == ';')
68 // these characters separate words like blanks
71 get_word(uns is_command_name)
77 while (*line && *line != '\'')
82 copy_buf.ptr[copied-1] = '\n';
84 return "Unterminated apostrophe word at the end";
88 } else if (*line == '"') {
90 uns start_copy = copied;
95 if (*line == '"' && !escape)
97 else if (*line == '\\')
107 copy_buf.ptr[copied-1] = '\n';
108 else // merge two lines
111 return "Unterminated quoted word at the end";
115 byte *tmp = stk_str_unesc(copy_buf.ptr + start_copy);
117 bb_grow(©_buf, start_copy + l + 1);
118 strcpy(copy_buf.ptr + start_copy, tmp);
119 copied = start_copy + l + 1;
122 // promised that *line is non-null and non-blank
124 while (*line && !Cblank(*line) && !CONTROL_CHAR(*line)
125 && (*line != '=' || !is_command_name))
127 if (*line == '=') { // nice for setting from a command-line
129 return "Assignment without a variable";
132 if (line == start) // already the first char is control
136 while (Cblank(*line))
142 get_token(uns is_command_name, byte **msg)
146 if (!*line || *line == '#') {
147 if (!is_command_name || !get_line())
149 } else if (*line == ';') {
151 if (!is_command_name || *msg)
153 } else if (*line == '\\' && !line[1]) {
155 *msg = "Last line ends by a backslash";
158 if (!*line || *line == '#')
159 log(L_WARN, "The line %s:%d following a backslash is empty", name_parse_fb, line_num);
161 split_grow(&word_buf, words+1);
163 word_buf.ptr[words++] = copied;
164 *msg = get_word(is_command_name);
165 return *msg ? NULL : copy_buf.ptr + start;
173 words = copied = ends_by_brace = 0;
174 byte *msg, *start_word;
175 if (!(start_word = get_token(1, &msg)))
177 if (*start_word == '{') // only one opening brace
178 return "Unexpected opening brace";
179 while (*line != '}') // stays for the next time
181 if (!(start_word = get_token(0, &msg)))
183 if (*start_word == '{') {
184 words--; // discard the brace
192 /* Parsing multiple files */
195 parse_fastbuf(byte *name_fb, struct fastbuf *fb, uns depth)
198 name_parse_fb = name_fb;
205 msg = split_command();
210 byte *name = copy_buf.ptr + word_buf.ptr[0];
212 for (uns i=1; i<words; i++)
213 pars[i-1] = copy_buf.ptr + word_buf.ptr[i];
214 if (!strcasecmp(name, "include"))
217 msg = "Expecting one filename";
219 msg = "Too many nested files";
220 else if (*line && *line != '#') // because the contents of line_buf is not re-entrant and will be cleared
221 msg = "The input command must be the last one on a line";
224 struct fastbuf *new_fb = bopen_try(pars[0], O_RDONLY, 1<<14);
226 msg = cf_printf("Cannot open file %s: %m", pars[0]);
230 msg = parse_fastbuf(stk_strdup(pars[0]), new_fb, depth+1);
238 enum cf_operation op;
239 byte *c = strchr(name, ':');
241 op = strcmp(name, "}") ? OP_SET : OP_CLOSE;
244 switch (Clocase(*c)) {
245 case 's': op = OP_SET; break;
246 case 'c': op = Clocase(c[1]) == 'l' ? OP_CLEAR: OP_COPY; break;
247 case 'a': op = Clocase(c[1]) == 'p' ? OP_APPEND : OP_AFTER; break;
248 case 'p': op = OP_PREPEND; break;
249 case 'r': op = OP_REMOVE; break;
250 case 'e': op = OP_EDIT; break;
251 case 'b': op = OP_BEFORE; break;
252 default: op = OP_SET; break;
254 if (strcasecmp(c, cf_op_names[op])) {
255 msg = cf_printf("Unknown operation %s", c);
261 msg = cf_interpret_line(name, op, words-1, pars);
266 log(L_ERROR, "File %s, line %d: %s", name_fb, line_num, msg);
267 return "included from here";
270 #ifndef DEFAULT_CONFIG
271 #define DEFAULT_CONFIG NULL
273 byte *cf_def_file = DEFAULT_CONFIG;
275 static uns postpone_commit; // only for cf_getopt()
276 static uns everything_committed; // after the 1st load, this flag is set on
281 if (cf_check_stack())
283 if (cf_commit_all(postpone_commit ? CF_NO_COMMIT : everything_committed ? CF_COMMIT : CF_COMMIT_ALL))
285 if (!postpone_commit)
286 everything_committed = 1;
291 load_file(byte *file)
294 struct fastbuf *fb = bopen_try(file, O_RDONLY, 1<<14);
296 log(L_ERROR, "Cannot open %s: %m", file);
299 byte *msg = parse_fastbuf(file, fb, 0);
301 int err = !!msg || done_stack();
308 load_string(byte *string)
312 fbbuf_init_read(&fb, string, strlen(string), 0);
313 byte *msg = parse_fastbuf("memory string", &fb, 0);
314 return !!msg || done_stack();
317 /* Safe loading and reloading */
320 cf_reload(byte *file)
323 struct cf_journal_item *oldj = cf_journal_new_transaction(1);
324 uns ec = everything_committed;
325 everything_committed = 0;
326 int err = load_file(file);
330 cf_journal_commit_transaction(1, NULL);
334 everything_committed = ec;
335 cf_journal_rollback_transaction(1, oldj);
344 struct cf_journal_item *oldj = cf_journal_new_transaction(1);
345 int err = load_file(file);
347 cf_journal_commit_transaction(1, oldj);
349 cf_journal_rollback_transaction(1, oldj);
356 struct cf_journal_item *oldj = cf_journal_new_transaction(0);
357 int err = load_string(string);
359 cf_journal_commit_transaction(0, oldj);
361 cf_journal_rollback_transaction(0, oldj);
365 /* Command-line parser */
371 if (cf_load(cf_def_file))
372 die("Cannot load default config %s", cf_def_file);
378 if (postpone_commit) {
381 die("Cannot commit after the initialization");
386 cf_getopt(int argc, char * const argv[], const char *short_opts, const struct option *long_opts, int *long_index)
388 static int other_options = 0;
390 int res = getopt_long (argc, argv, short_opts, long_opts, long_index);
391 if (res == 'S' || res == 'C' || res == 0x64436667)
394 die("The -S and -C options must precede all other arguments");
399 die("Cannot set %s", optarg);
400 } else if (res == 'C') {
403 die("Cannot load config file %s", optarg);
406 else { /* --dumpconfig */
409 struct fastbuf *b = bfdopen(1, 4096);
416 /* unhandled option or end of options */
417 if (res != ':' && res != '?')