2 * Configuration file parser for new and old Include and
5 * Kern Sibbald, March MMIII
10 Copyright (C) 2003-2006 Kern Sibbald
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 version 2 as amended with additional clauses defined in the
15 file LICENSE in the main source directory.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 the file LICENSE for additional details.
27 #include "lib/regex.h"
32 /* Forward referenced subroutines */
34 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass);
36 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass);
37 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass);
38 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass);
39 static void store_fstype(LEX *lc, RES_ITEM *item, int index, int pass);
40 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass);
41 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass);
42 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass);
43 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass);
44 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass);
45 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass);
46 static void setup_current_opts(void);
49 /* We build the current resource here as we are
50 * scanning the resource configuration definition,
51 * then move it to allocated memory when the resource
55 extern int res_all_size;
57 /* We build the current new Include and Exclude items here */
58 static INCEXE res_incexe;
61 * new Include/Exclude items
62 * name handler value code flags default_value
64 static RES_ITEM newinc_items[] = {
65 {"file", store_fname, NULL, 0, 0, 0},
66 {"options", options_res, NULL, 0, 0, 0},
67 {NULL, NULL, NULL, 0, 0, 0}
71 * Items that are valid in an Options resource
73 static RES_ITEM options_items[] = {
74 {"compression", store_opts, NULL, 0, 0, 0},
75 {"signature", store_opts, NULL, 0, 0, 0},
76 {"verify", store_opts, NULL, 0, 0, 0},
77 {"onefs", store_opts, NULL, 0, 0, 0},
78 {"recurse", store_opts, NULL, 0, 0, 0},
79 {"sparse", store_opts, NULL, 0, 0, 0},
80 {"hardlinks", store_opts, NULL, 0, 0, 0},
81 {"readfifo", store_opts, NULL, 0, 0, 0},
82 {"replace", store_opts, NULL, 0, 0, 0},
83 {"portable", store_opts, NULL, 0, 0, 0},
84 {"mtimeonly", store_opts, NULL, 0, 0, 0},
85 {"keepatime", store_opts, NULL, 0, 0, 0},
86 {"regex", store_regex, NULL, 0, 0, 0},
87 {"regexdir", store_regex, NULL, 1, 0, 0},
88 {"regexfile", store_regex, NULL, 2, 0, 0},
89 {"base", store_base, NULL, 0, 0, 0},
90 {"wild", store_wild, NULL, 0, 0, 0},
91 {"wilddir", store_wild, NULL, 1, 0, 0},
92 {"wildfile", store_wild, NULL, 2, 0, 0},
93 {"exclude", store_opts, NULL, 0, 0, 0},
94 {"aclsupport", store_opts, NULL, 0, 0, 0},
95 {"reader", store_reader, NULL, 0, 0, 0},
96 {"writer", store_writer, NULL, 0, 0, 0},
97 {"ignorecase", store_opts, NULL, 0, 0, 0},
98 {"fstype", store_fstype, NULL, 0, 0, 0},
99 {"hfsplussupport", store_opts, NULL, 0, 0, 0},
100 {NULL, NULL, NULL, 0, 0, 0}
104 /* Define FileSet KeyWord values */
115 INC_KW_REPLACE, /* restore options */
116 INC_KW_READFIFO, /* Causes fifo data to be read */
127 * This is the list of options that can be stored by store_opts
128 * Note, now that the old style Include/Exclude code is gone,
129 * the INC_KW code could be put into the "code" field of the
130 * options given above.
132 static struct s_kw FS_option_kw[] = {
133 {"compression", INC_KW_COMPRESSION},
134 {"signature", INC_KW_DIGEST},
135 {"encryption", INC_KW_ENCRYPTION},
136 {"verify", INC_KW_VERIFY},
137 {"onefs", INC_KW_ONEFS},
138 {"recurse", INC_KW_RECURSE},
139 {"sparse", INC_KW_SPARSE},
140 {"hardlinks", INC_KW_HARDLINK},
141 {"replace", INC_KW_REPLACE},
142 {"readfifo", INC_KW_READFIFO},
143 {"portable", INC_KW_PORTABLE},
144 {"mtimeonly", INC_KW_MTIMEONLY},
145 {"keepatime", INC_KW_KEEPATIME},
146 {"exclude", INC_KW_EXCLUDE},
147 {"aclsupport", INC_KW_ACL},
148 {"ignorecase", INC_KW_IGNORECASE},
149 {"hfsplussupport", INC_KW_HFSPLUS},
153 /* Options for FileSet keywords */
162 * Options permitted for each keyword and resulting value.
163 * The output goes into opts, which are then transmitted to
164 * the FD for application as options to the following list of
167 static struct s_fs_opt FS_options[] = {
168 {"md5", INC_KW_DIGEST, "M"},
169 {"sha1", INC_KW_DIGEST, "S"},
170 {"sha256", INC_KW_DIGEST, "S2"},
171 {"sha512", INC_KW_DIGEST, "S3"},
172 {"gzip", INC_KW_COMPRESSION, "Z6"},
173 {"gzip1", INC_KW_COMPRESSION, "Z1"},
174 {"gzip2", INC_KW_COMPRESSION, "Z2"},
175 {"gzip3", INC_KW_COMPRESSION, "Z3"},
176 {"gzip4", INC_KW_COMPRESSION, "Z4"},
177 {"gzip5", INC_KW_COMPRESSION, "Z5"},
178 {"gzip6", INC_KW_COMPRESSION, "Z6"},
179 {"gzip7", INC_KW_COMPRESSION, "Z7"},
180 {"gzip8", INC_KW_COMPRESSION, "Z8"},
181 {"gzip9", INC_KW_COMPRESSION, "Z9"},
182 {"blowfish", INC_KW_ENCRYPTION, "B"}, /* ***FIXME*** not implemented */
183 {"3des", INC_KW_ENCRYPTION, "3"}, /* ***FIXME*** not implemented */
184 {"yes", INC_KW_ONEFS, "0"},
185 {"no", INC_KW_ONEFS, "f"},
186 {"yes", INC_KW_RECURSE, "0"},
187 {"no", INC_KW_RECURSE, "h"},
188 {"yes", INC_KW_SPARSE, "s"},
189 {"no", INC_KW_SPARSE, "0"},
190 {"yes", INC_KW_HARDLINK, "0"},
191 {"no", INC_KW_HARDLINK, "H"},
192 {"always", INC_KW_REPLACE, "a"},
193 {"ifnewer", INC_KW_REPLACE, "w"},
194 {"never", INC_KW_REPLACE, "n"},
195 {"yes", INC_KW_READFIFO, "r"},
196 {"no", INC_KW_READFIFO, "0"},
197 {"yes", INC_KW_PORTABLE, "p"},
198 {"no", INC_KW_PORTABLE, "0"},
199 {"yes", INC_KW_MTIMEONLY, "m"},
200 {"no", INC_KW_MTIMEONLY, "0"},
201 {"yes", INC_KW_KEEPATIME, "k"},
202 {"no", INC_KW_KEEPATIME, "0"},
203 {"yes", INC_KW_EXCLUDE, "e"},
204 {"no", INC_KW_EXCLUDE, "0"},
205 {"yes", INC_KW_ACL, "A"},
206 {"no", INC_KW_ACL, "0"},
207 {"yes", INC_KW_IGNORECASE, "i"},
208 {"no", INC_KW_IGNORECASE, "0"},
209 {"yes", INC_KW_HFSPLUS, "R"}, /* "R" for resource fork */
210 {"no", INC_KW_HFSPLUS, "0"},
217 * Scan for right hand side of Include options (keyword=option) is
218 * converted into one or two characters. Verifyopts=xxxx is Vxxxx:
219 * Whatever is found is concatenated to the opts string.
220 * This code is also used inside an Options resource.
222 static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen)
226 int lcopts = lc->options;
228 option[0] = 0; /* default option = none */
229 option[2] = 0; /* terminate options */
230 lc->options |= LOPT_STRING; /* force string */
231 token = lex_get_token(lc, T_STRING); /* expect at least one option */
232 if (keyword == INC_KW_VERIFY) { /* special case */
233 /* ***FIXME**** ensure these are in permitted set */
234 bstrncat(opts, "V", optlen); /* indicate Verify */
235 bstrncat(opts, lc->str, optlen);
236 bstrncat(opts, ":", optlen); /* terminate it */
237 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
240 * Standard keyword options for Include/Exclude
243 for (i=0; FS_options[i].name; i++) {
244 if (strcasecmp(lc->str, FS_options[i].name) == 0 && FS_options[i].keyword == keyword) {
245 /* NOTE! maximum 2 letters here or increase option[3] */
246 option[0] = FS_options[i].option[0];
247 option[1] = FS_options[i].option[1];
253 scan_err1(lc, _("Expected a FileSet option keyword, got:%s:"), lc->str);
254 } else { /* add option */
255 bstrncat(opts, option, optlen);
256 Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
259 lc->options = lcopts;
261 /* If option terminated by comma, eat it */
263 token = lex_get_token(lc, T_ALL); /* yes, eat comma */
269 * Store FileSet Include/Exclude info
270 * NEW style includes are handled in store_newinc()
272 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass)
277 * Decide if we are doing a new Include or an old include. The
278 * new Include is followed immediately by open brace, whereas the
279 * old include has options following the Include.
281 token = lex_get_token(lc, T_SKIP_EOL);
282 if (token == T_BOB) {
283 store_newinc(lc, item, index, pass);
286 scan_err0(lc, _("Old style Include/Exclude not supported\n"));
291 * Store NEW style FileSet FInclude/FExclude info
293 * Note, when this routine is called, we are inside a FileSet
294 * resource. We treat the Include/Execlude like a sort of
295 * mini-resource within the FileSet resource.
297 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass)
303 if (!res_all.res_fs.have_MD5) {
304 MD5Init(&res_all.res_fs.md5c);
305 res_all.res_fs.have_MD5 = true;
307 memset(&res_incexe, 0, sizeof(INCEXE));
308 res_all.res_fs.new_include = true;
309 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
310 if (token == T_EOB) {
313 if (token != T_IDENTIFIER) {
314 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
316 for (i=0; newinc_items[i].name; i++) {
317 options = strcasecmp(lc->str, "options") == 0;
318 if (strcasecmp(newinc_items[i].name, lc->str) == 0) {
320 token = lex_get_token(lc, T_SKIP_EOL);
321 if (token != T_EQUALS) {
322 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
325 /* Call item handler */
326 newinc_items[i].handler(lc, &newinc_items[i], i, pass);
332 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
336 incexe = (INCEXE *)malloc(sizeof(INCEXE));
337 memcpy(incexe, &res_incexe, sizeof(INCEXE));
338 memset(&res_incexe, 0, sizeof(INCEXE));
339 if (item->code == 0) { /* include */
340 if (res_all.res_fs.num_includes == 0) {
341 res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
343 res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
344 sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
346 res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
347 Dmsg1(900, "num_includes=%d\n", res_all.res_fs.num_includes);
348 } else { /* exclude */
349 if (res_all.res_fs.num_excludes == 0) {
350 res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
352 res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
353 sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
355 res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
356 Dmsg1(900, "num_excludes=%d\n", res_all.res_fs.num_excludes);
360 set_bit(index, res_all.hdr.item_present);
364 /* Store regex info */
365 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass)
373 token = lex_get_token(lc, T_SKIP_EOL);
375 /* Pickup regex string
379 case T_UNQUOTED_STRING:
380 case T_QUOTED_STRING:
381 rc = regcomp(&preg, lc->str, REG_EXTENDED);
383 regerror(rc, &preg, prbuf, sizeof(prbuf));
385 scan_err1(lc, _("Regex compile error. ERR=%s\n"), prbuf);
389 if (item->code == 1) {
391 res_incexe.current_opts->regexdir.append(bstrdup(lc->str));
392 newsize = res_incexe.current_opts->regexdir.size();
393 } else if (item->code == 2) {
395 res_incexe.current_opts->regexfile.append(bstrdup(lc->str));
396 newsize = res_incexe.current_opts->regexfile.size();
399 res_incexe.current_opts->regex.append(bstrdup(lc->str));
400 newsize = res_incexe.current_opts->regex.size();
402 Dmsg4(900, "set %s %p size=%d %s\n",
403 type, res_incexe.current_opts, newsize, lc->str);
406 scan_err1(lc, _("Expected a regex string, got: %s\n"), lc->str);
412 /* Store Base info */
413 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass)
417 token = lex_get_token(lc, T_NAME);
420 * Pickup Base Job Name
422 res_incexe.current_opts->base.append(bstrdup(lc->str));
427 /* Store reader info */
428 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass)
432 token = lex_get_token(lc, T_NAME);
435 * Pickup reader command
437 res_incexe.current_opts->reader = bstrdup(lc->str);
442 /* Store writer innfo */
443 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass)
447 token = lex_get_token(lc, T_NAME);
450 * Pickup writer command
452 res_incexe.current_opts->writer = bstrdup(lc->str);
459 /* Store Wild-card info */
460 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass)
466 token = lex_get_token(lc, T_SKIP_EOL);
469 * Pickup Wild-card string
473 case T_UNQUOTED_STRING:
474 case T_QUOTED_STRING:
475 if (item->code == 1) {
477 res_incexe.current_opts->wilddir.append(bstrdup(lc->str));
478 newsize = res_incexe.current_opts->wilddir.size();
479 } else if (item->code == 2) {
481 res_incexe.current_opts->wildfile.append(bstrdup(lc->str));
482 newsize = res_incexe.current_opts->wildfile.size();
485 res_incexe.current_opts->wild.append(bstrdup(lc->str));
486 newsize = res_incexe.current_opts->wild.size();
488 Dmsg4(9, "set %s %p size=%d %s\n",
489 type, res_incexe.current_opts, newsize, lc->str);
492 scan_err1(lc, _("Expected a wild-card string, got: %s\n"), lc->str);
498 /* Store fstype info */
499 static void store_fstype(LEX *lc, RES_ITEM *item, int index, int pass)
503 token = lex_get_token(lc, T_SKIP_EOL);
505 /* Pickup fstype string */
508 case T_UNQUOTED_STRING:
509 case T_QUOTED_STRING:
510 res_incexe.current_opts->fstype.append(bstrdup(lc->str));
511 Dmsg3(900, "set fstype %p size=%d %s\n",
512 res_incexe.current_opts, res_incexe.current_opts->fstype.size(), lc->str);
515 scan_err1(lc, _("Expected an fstype string, got: %s\n"), lc->str);
522 * Store Filename info. Note, for minor efficiency reasons, we
523 * always increase the name buffer by 10 items because we expect
524 * to add more entries.
526 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass)
531 token = lex_get_token(lc, T_SKIP_EOL);
533 /* Pickup Filename string
537 case T_UNQUOTED_STRING:
538 if (strchr(lc->str, '\\')) {
539 scan_err1(lc, _("Backslash found. Use forward slashes or quote the string.: %s\n"), lc->str);
542 case T_QUOTED_STRING:
543 if (res_all.res_fs.have_MD5) {
544 MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
546 incexe = &res_incexe;
547 if (incexe->name_list.size() == 0) {
548 incexe->name_list.init(10, true);
550 incexe->name_list.append(bstrdup(lc->str));
551 Dmsg1(900, "Add to name_list %s\n", lc->str);
554 scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
562 * Come here when Options seen in Include/Exclude
564 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass)
568 token = lex_get_token(lc, T_SKIP_EOL);
569 if (token != T_BOB) {
570 scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
574 setup_current_opts();
577 while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
578 if (token == T_EOL) {
581 if (token == T_EOB) {
584 if (token != T_IDENTIFIER) {
585 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
587 for (i=0; options_items[i].name; i++) {
588 if (strcasecmp(options_items[i].name, lc->str) == 0) {
589 token = lex_get_token(lc, T_SKIP_EOL);
590 if (token != T_EQUALS) {
591 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
593 /* Call item handler */
594 options_items[i].handler(lc, &options_items[i], i, pass);
600 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
607 * New style options come here
609 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass)
616 keyword = INC_KW_NONE;
617 /* Look up the keyword */
618 for (i=0; FS_option_kw[i].name; i++) {
619 if (strcasecmp(item->name, FS_option_kw[i].name) == 0) {
620 keyword = FS_option_kw[i].token;
624 if (keyword == INC_KW_NONE) {
625 scan_err1(lc, _("Expected a FileSet keyword, got: %s"), lc->str);
627 /* Now scan for the value */
628 scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
630 bstrncat(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
631 Dmsg2(900, "new pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
638 /* If current_opts not defined, create first entry */
639 static void setup_current_opts(void)
641 FOPTS *fo = (FOPTS *)malloc(sizeof(FOPTS));
642 memset(fo, 0, sizeof(FOPTS));
643 fo->regex.init(1, true);
644 fo->regexdir.init(1, true);
645 fo->regexfile.init(1, true);
646 fo->wild.init(1, true);
647 fo->wilddir.init(1, true);
648 fo->wildfile.init(1, true);
649 fo->base.init(1, true);
650 fo->fstype.init(1, true);
651 res_incexe.current_opts = fo;
652 if (res_incexe.num_opts == 0) {
653 res_incexe.opts_list = (FOPTS **)malloc(sizeof(FOPTS *));
655 res_incexe.opts_list = (FOPTS **)realloc(res_incexe.opts_list,
656 sizeof(FOPTS *) * (res_incexe.num_opts + 1));
658 res_incexe.opts_list[res_incexe.num_opts++] = fo;