]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/inc_conf.c
Update copyright date
[bacula/bacula] / bacula / src / dird / inc_conf.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *   Configuration file parser for new and old Include and
30  *      Exclude records
31  *
32  *     Kern Sibbald, March MMIII
33  *
34  *     Version $Id$
35  */
36
37 #include "bacula.h"
38 #include "dird.h"
39 #ifndef HAVE_REGEX_H
40 #include "lib/bregex.h"
41 #else
42 #include <regex.h>
43 #endif
44
45 /* Forward referenced subroutines */
46
47 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass);
48
49 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass);
50 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass);
51 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass);
52 static void store_fstype(LEX *lc, RES_ITEM *item, int index, int pass);
53 static void store_drivetype(LEX *lc, RES_ITEM *item, int index, int pass);
54 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass);
55 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass);
56 static void store_plugin_name(LEX *lc, RES_ITEM *item, int index, int pass);
57 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass);
58 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass);
59 static void store_plugin(LEX *lc, RES_ITEM *item, int index, int pass);
60 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass);
61 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass);
62 static void setup_current_opts(void);
63
64
65 /* We build the current resource here as we are
66  * scanning the resource configuration definition,
67  * then move it to allocated memory when the resource
68  * scan is complete.
69  */
70 #if defined(_MSC_VER)
71 extern "C" { // work around visual compiler mangling variables
72    extern URES res_all;
73 }
74 #else
75 extern URES res_all;
76 #endif
77 extern int  res_all_size;
78
79 /* We build the current new Include and Exclude items here */
80 static INCEXE res_incexe;
81
82 /*
83  * new Include/Exclude items
84  *   name             handler     value    code flags default_value
85  */
86 static RES_ITEM newinc_items[] = {
87    {"file",            store_fname,   {0},      0, 0, 0},
88    {"plugin",          store_plugin_name,   {0},      0, 0, 0},
89    {"options",         options_res,   {0},      0, 0, 0},
90    {NULL, NULL, {0}, 0, 0, 0}
91 };
92
93 /*
94  * Items that are valid in an Options resource
95  */
96 static RES_ITEM options_items[] = {
97    {"compression",     store_opts,    {0},     0, 0, 0},
98    {"signature",       store_opts,    {0},     0, 0, 0},
99    {"accurate",        store_opts,    {0},     0, 0, 0},
100    {"verify",          store_opts,    {0},     0, 0, 0},
101    {"onefs",           store_opts,    {0},     0, 0, 0},
102    {"recurse",         store_opts,    {0},     0, 0, 0},
103    {"sparse",          store_opts,    {0},     0, 0, 0},
104    {"hardlinks",       store_opts,    {0},     0, 0, 0},
105    {"readfifo",        store_opts,    {0},     0, 0, 0},
106    {"replace",         store_opts,    {0},     0, 0, 0},
107    {"portable",        store_opts,    {0},     0, 0, 0},
108    {"mtimeonly",       store_opts,    {0},     0, 0, 0},
109    {"keepatime",       store_opts,    {0},     0, 0, 0},
110    {"regex",           store_regex,   {0},     0, 0, 0},
111    {"regexdir",        store_regex,   {0},     1, 0, 0},
112    {"regexfile",       store_regex,   {0},     2, 0, 0},
113    {"base",            store_base,    {0},     0, 0, 0},
114    {"wild",            store_wild,    {0},     0, 0, 0},
115    {"wilddir",         store_wild,    {0},     1, 0, 0},
116    {"wildfile",        store_wild,    {0},     2, 0, 0},
117    {"exclude",         store_opts,    {0},     0, 0, 0},
118    {"aclsupport",      store_opts,    {0},     0, 0, 0},
119    {"plugin",          store_plugin,  {0},     0, 0, 0},
120    {"reader",          store_reader,  {0},     0, 0, 0},
121    {"writer",          store_writer,  {0},     0, 0, 0},
122    {"ignorecase",      store_opts,    {0},     0, 0, 0},
123    {"fstype",          store_fstype,  {0},     0, 0, 0},
124    {"hfsplussupport",  store_opts,    {0},     0, 0, 0},
125    {"noatime",         store_opts,    {0},     0, 0, 0},
126    {"enhancedwild",    store_opts,    {0},     0, 0, 0},
127    {"drivetype",       store_drivetype, {0},     0, 0, 0},
128    {"checkfilechanges",store_opts,    {0},     0, 0, 0},
129    {"strippath",       store_opts,    {0},     0, 0, 0},
130    {"honornodumpflag", store_opts,    {0},     0, 0, 0},
131    {NULL, NULL, {0}, 0, 0, 0}
132 };
133
134
135 /* Define FileSet KeyWord values */
136 enum {
137    INC_KW_NONE,
138    INC_KW_COMPRESSION,
139    INC_KW_DIGEST,
140    INC_KW_ENCRYPTION,
141    INC_KW_VERIFY,
142    INC_KW_ACCURATE,
143    INC_KW_ONEFS,
144    INC_KW_RECURSE,
145    INC_KW_SPARSE,
146    INC_KW_HARDLINK,
147    INC_KW_REPLACE,               /* restore options */
148    INC_KW_READFIFO,              /* Causes fifo data to be read */
149    INC_KW_PORTABLE,
150    INC_KW_MTIMEONLY,
151    INC_KW_KEEPATIME,
152    INC_KW_EXCLUDE,
153    INC_KW_ACL,
154    INC_KW_IGNORECASE,
155    INC_KW_HFSPLUS,
156    INC_KW_NOATIME,
157    INC_KW_ENHANCEDWILD,
158    INC_KW_CHKCHANGES,
159    INC_KW_STRIPPATH,
160    INC_KW_HONOR_NODUMP
161 };
162
163 /*
164  * This is the list of options that can be stored by store_opts
165  *   Note, now that the old style Include/Exclude code is gone,
166  *   the INC_KW code could be put into the "code" field of the
167  *   options given above.
168  */
169 static struct s_kw FS_option_kw[] = {
170    {"compression", INC_KW_COMPRESSION},
171    {"signature",   INC_KW_DIGEST},
172    {"encryption",  INC_KW_ENCRYPTION},
173    {"verify",      INC_KW_VERIFY},
174    {"accurate",    INC_KW_ACCURATE},
175    {"onefs",       INC_KW_ONEFS},
176    {"recurse",     INC_KW_RECURSE},
177    {"sparse",      INC_KW_SPARSE},
178    {"hardlinks",   INC_KW_HARDLINK},
179    {"replace",     INC_KW_REPLACE},
180    {"readfifo",    INC_KW_READFIFO},
181    {"portable",    INC_KW_PORTABLE},
182    {"mtimeonly",   INC_KW_MTIMEONLY},
183    {"keepatime",   INC_KW_KEEPATIME},
184    {"exclude",     INC_KW_EXCLUDE},
185    {"aclsupport",  INC_KW_ACL},
186    {"ignorecase",  INC_KW_IGNORECASE},
187    {"hfsplussupport", INC_KW_HFSPLUS},
188    {"noatime",     INC_KW_NOATIME},
189    {"enhancedwild", INC_KW_ENHANCEDWILD},
190    {"checkfilechanges", INC_KW_CHKCHANGES},
191    {"strippath",    INC_KW_STRIPPATH},
192    {"honornodumpflag",    INC_KW_HONOR_NODUMP},
193    {NULL,          0}
194 };
195
196 /* Options for FileSet keywords */
197
198 struct s_fs_opt {
199    const char *name;
200    int keyword;
201    const char *option;
202 };
203
204 /*
205  * Options permitted for each keyword and resulting value.
206  * The output goes into opts, which are then transmitted to
207  * the FD for application as options to the following list of
208  * included files.
209  */
210 static struct s_fs_opt FS_options[] = {
211    {"md5",      INC_KW_DIGEST,        "M"},
212    {"sha1",     INC_KW_DIGEST,        "S"},
213    {"sha256",   INC_KW_DIGEST,       "S2"},
214    {"sha512",   INC_KW_DIGEST,       "S3"},
215    {"gzip",     INC_KW_COMPRESSION,  "Z6"},
216    {"gzip1",    INC_KW_COMPRESSION,  "Z1"},
217    {"gzip2",    INC_KW_COMPRESSION,  "Z2"},
218    {"gzip3",    INC_KW_COMPRESSION,  "Z3"},
219    {"gzip4",    INC_KW_COMPRESSION,  "Z4"},
220    {"gzip5",    INC_KW_COMPRESSION,  "Z5"},
221    {"gzip6",    INC_KW_COMPRESSION,  "Z6"},
222    {"gzip7",    INC_KW_COMPRESSION,  "Z7"},
223    {"gzip8",    INC_KW_COMPRESSION,  "Z8"},
224    {"gzip9",    INC_KW_COMPRESSION,  "Z9"},
225    {"blowfish", INC_KW_ENCRYPTION,    "B"},   /* ***FIXME*** not implemented */
226    {"3des",     INC_KW_ENCRYPTION,    "3"},   /* ***FIXME*** not implemented */
227    {"yes",      INC_KW_ONEFS,         "0"},
228    {"no",       INC_KW_ONEFS,         "f"},
229    {"yes",      INC_KW_RECURSE,       "0"},
230    {"no",       INC_KW_RECURSE,       "h"},
231    {"yes",      INC_KW_SPARSE,        "s"},
232    {"no",       INC_KW_SPARSE,        "0"},
233    {"yes",      INC_KW_HARDLINK,      "0"},
234    {"no",       INC_KW_HARDLINK,      "H"},
235    {"always",   INC_KW_REPLACE,       "a"},
236    {"ifnewer",  INC_KW_REPLACE,       "w"},
237    {"never",    INC_KW_REPLACE,       "n"},
238    {"yes",      INC_KW_READFIFO,      "r"},
239    {"no",       INC_KW_READFIFO,      "0"},
240    {"yes",      INC_KW_PORTABLE,      "p"},
241    {"no",       INC_KW_PORTABLE,      "0"},
242    {"yes",      INC_KW_MTIMEONLY,     "m"},
243    {"no",       INC_KW_MTIMEONLY,     "0"},
244    {"yes",      INC_KW_KEEPATIME,     "k"},
245    {"no",       INC_KW_KEEPATIME,     "0"},
246    {"yes",      INC_KW_EXCLUDE,       "e"},
247    {"no",       INC_KW_EXCLUDE,       "0"},
248    {"yes",      INC_KW_ACL,           "A"},
249    {"no",       INC_KW_ACL,           "0"},
250    {"yes",      INC_KW_IGNORECASE,    "i"},
251    {"no",       INC_KW_IGNORECASE,    "0"},
252    {"yes",      INC_KW_HFSPLUS,       "R"},   /* "R" for resource fork */
253    {"no",       INC_KW_HFSPLUS,       "0"},
254    {"yes",      INC_KW_NOATIME,       "K"},
255    {"no",       INC_KW_NOATIME,       "0"},
256    {"yes",      INC_KW_ENHANCEDWILD,  "K"},
257    {"no",       INC_KW_ENHANCEDWILD,  "0"},
258    {"yes",      INC_KW_CHKCHANGES,    "c"},
259    {"no",       INC_KW_CHKCHANGES,    "0"},
260    {"yes",      INC_KW_HONOR_NODUMP,  "N"},
261    {"no",       INC_KW_HONOR_NODUMP,  "0"},
262    {NULL,       0,                      0}
263 };
264
265
266
267 /*
268  * Scan for right hand side of Include options (keyword=option) is
269  *    converted into one or two characters. Verifyopts=xxxx is Vxxxx:
270  *    Whatever is found is concatenated to the opts string.
271  * This code is also used inside an Options resource.
272  */
273 static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen)
274 {
275    int token, i;
276    char option[3];
277    int lcopts = lc->options;
278
279    option[0] = 0;                     /* default option = none */
280    option[2] = 0;                     /* terminate options */
281    lc->options |= LOPT_STRING;        /* force string */
282    token = lex_get_token(lc, T_STRING);          /* expect at least one option */
283    if (keyword == INC_KW_VERIFY) { /* special case */
284       /* ***FIXME**** ensure these are in permitted set */
285       bstrncat(opts, "V", optlen);         /* indicate Verify */
286       bstrncat(opts, lc->str, optlen);
287       bstrncat(opts, ":", optlen);         /* terminate it */
288       Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
289    } else if (keyword == INC_KW_ACCURATE) { /* special case */
290       /* ***FIXME**** ensure these are in permitted set */
291       bstrncat(opts, "C", optlen);         /* indicate Accurate */
292       bstrncat(opts, lc->str, optlen);
293       bstrncat(opts, ":", optlen);         /* terminate it */
294       Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
295    } else if (keyword == INC_KW_STRIPPATH) { /* another special case */
296       if (!is_an_integer(lc->str)) {
297          scan_err1(lc, _("Expected a strip path positive integer, got:%s:"), lc->str);
298       }
299       bstrncat(opts, "P", optlen);         /* indicate strip path */
300       bstrncat(opts, lc->str, optlen);
301       bstrncat(opts, ":", optlen);         /* terminate it */
302       Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
303    /*
304     * Standard keyword options for Include/Exclude
305     */
306    } else {
307       for (i=0; FS_options[i].name; i++) {
308          if (FS_options[i].keyword == keyword && strcasecmp(lc->str, FS_options[i].name) == 0) {
309             /* NOTE! maximum 2 letters here or increase option[3] */
310             option[0] = FS_options[i].option[0];
311             option[1] = FS_options[i].option[1];
312             i = 0;
313             break;
314          }
315       }
316       if (i != 0) {
317          scan_err1(lc, _("Expected a FileSet option keyword, got:%s:"), lc->str);
318       } else { /* add option */
319          bstrncat(opts, option, optlen);
320          Dmsg3(900, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen);
321       }
322    }
323    lc->options = lcopts;
324
325    /* If option terminated by comma, eat it */
326    if (lc->ch == ',') {
327       token = lex_get_token(lc, T_ALL);      /* yes, eat comma */
328    }
329 }
330
331 /*
332  *
333  * Store FileSet Include/Exclude info
334  *  NEW style includes are handled in store_newinc()
335  */
336 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass)
337 {
338    int token;
339
340    /*
341     * Decide if we are doing a new Include or an old include. The
342     *  new Include is followed immediately by open brace, whereas the
343     *  old include has options following the Include.
344     */
345    token = lex_get_token(lc, T_SKIP_EOL);
346    if (token == T_BOB) {
347       store_newinc(lc, item, index, pass);
348       return;
349    }
350    scan_err0(lc, _("Old style Include/Exclude not supported\n"));
351 }
352
353
354 /*
355  * Store NEW style FileSet FInclude/FExclude info
356  *
357  *  Note, when this routine is called, we are inside a FileSet
358  *  resource.  We treat the Include/Execlude like a sort of
359  *  mini-resource within the FileSet resource.
360  */
361 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass)
362 {
363    int token, i;
364    INCEXE *incexe;
365    bool options;
366
367    if (!res_all.res_fs.have_MD5) {
368       MD5Init(&res_all.res_fs.md5c);
369       res_all.res_fs.have_MD5 = true;
370    }
371    memset(&res_incexe, 0, sizeof(INCEXE));
372    res_all.res_fs.new_include = true;
373    while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
374       if (token == T_EOB) {
375          break;
376       }
377       if (token != T_IDENTIFIER) {
378          scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
379       }
380       for (i=0; newinc_items[i].name; i++) {
381          options = strcasecmp(lc->str, "options") == 0;
382          if (strcasecmp(newinc_items[i].name, lc->str) == 0) {
383             if (!options) {
384                token = lex_get_token(lc, T_SKIP_EOL);
385                if (token != T_EQUALS) {
386                   scan_err1(lc, _("expected an equals, got: %s"), lc->str);
387                }
388             }
389             /* Call item handler */
390             newinc_items[i].handler(lc, &newinc_items[i], i, pass);
391             i = -1;
392             break;
393          }
394       }
395       if (i >=0) {
396          scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
397       }
398    }
399    if (pass == 1) {
400       incexe = (INCEXE *)malloc(sizeof(INCEXE));
401       memcpy(incexe, &res_incexe, sizeof(INCEXE));
402       memset(&res_incexe, 0, sizeof(INCEXE));
403       if (item->code == 0) { /* include */
404          if (res_all.res_fs.num_includes == 0) {
405             res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
406          } else {
407             res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
408                            sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
409          }
410          res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
411          Dmsg1(900, "num_includes=%d\n", res_all.res_fs.num_includes);
412       } else {    /* exclude */
413          if (res_all.res_fs.num_excludes == 0) {
414             res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
415          } else {
416             res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
417                            sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
418          }
419          res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
420          Dmsg1(900, "num_excludes=%d\n", res_all.res_fs.num_excludes);
421       }
422    }
423    scan_to_eol(lc);
424    set_bit(index, res_all.hdr.item_present);
425 }
426
427
428 /* Store regex info */
429 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass)
430 {
431    int token, rc;
432    regex_t preg;
433    char prbuf[500];
434    const char *type;
435    int newsize;
436
437    token = lex_get_token(lc, T_SKIP_EOL);
438    if (pass == 1) {
439       /* Pickup regex string
440        */
441       switch (token) {
442       case T_IDENTIFIER:
443       case T_UNQUOTED_STRING:
444       case T_QUOTED_STRING:
445          rc = regcomp(&preg, lc->str, REG_EXTENDED);
446          if (rc != 0) {
447             regerror(rc, &preg, prbuf, sizeof(prbuf));
448             regfree(&preg);
449             scan_err1(lc, _("Regex compile error. ERR=%s\n"), prbuf);
450             break;
451          }
452          regfree(&preg);
453          if (item->code == 1) {
454             type = "regexdir";
455             res_incexe.current_opts->regexdir.append(bstrdup(lc->str));
456             newsize = res_incexe.current_opts->regexdir.size();
457          } else if (item->code == 2) {
458             type = "regexfile";
459             res_incexe.current_opts->regexfile.append(bstrdup(lc->str));
460             newsize = res_incexe.current_opts->regexfile.size();
461          } else {
462             type = "regex";
463             res_incexe.current_opts->regex.append(bstrdup(lc->str));
464             newsize = res_incexe.current_opts->regex.size();
465          }
466          Dmsg4(900, "set %s %p size=%d %s\n",
467             type, res_incexe.current_opts, newsize, lc->str);
468          break;
469       default:
470          scan_err1(lc, _("Expected a regex string, got: %s\n"), lc->str);
471       }
472    }
473    scan_to_eol(lc);
474 }
475
476 /* Store Base info */
477 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass)
478 {
479    int token;
480
481    token = lex_get_token(lc, T_NAME);
482    if (pass == 1) {
483       /*
484        * Pickup Base Job Name
485        */
486       res_incexe.current_opts->base.append(bstrdup(lc->str));
487    }
488    scan_to_eol(lc);
489 }
490
491 /* Store reader info */
492 static void store_plugin(LEX *lc, RES_ITEM *item, int index, int pass)
493 {
494    int token;
495
496    token = lex_get_token(lc, T_NAME);
497    if (pass == 1) {
498       /*
499        * Pickup plugin command
500        */
501       res_incexe.current_opts->plugin = bstrdup(lc->str);
502    }
503    scan_to_eol(lc);
504 }
505
506
507 /* Store reader info */
508 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass)
509 {
510    int token;
511
512    token = lex_get_token(lc, T_NAME);
513    if (pass == 1) {
514       /*
515        * Pickup reader command
516        */
517       res_incexe.current_opts->reader = bstrdup(lc->str);
518    }
519    scan_to_eol(lc);
520 }
521
522 /* Store writer innfo */
523 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass)
524 {
525    int token;
526
527    token = lex_get_token(lc, T_NAME);
528    if (pass == 1) {
529       /*
530        * Pickup writer command
531        */
532       res_incexe.current_opts->writer = bstrdup(lc->str);
533    }
534    scan_to_eol(lc);
535 }
536
537
538
539 /* Store Wild-card info */
540 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass)
541 {
542    int token;
543    const char *type;
544    int newsize;
545
546    token = lex_get_token(lc, T_SKIP_EOL);
547    if (pass == 1) {
548       /*
549        * Pickup Wild-card string
550        */
551       switch (token) {
552       case T_IDENTIFIER:
553       case T_UNQUOTED_STRING:
554       case T_QUOTED_STRING:
555          if (item->code == 1) {
556             type = "wilddir";
557             res_incexe.current_opts->wilddir.append(bstrdup(lc->str));
558             newsize = res_incexe.current_opts->wilddir.size();
559          } else if (item->code == 2) {
560             if (strpbrk(lc->str, "/\\") != NULL) {
561                type = "wildfile";
562                res_incexe.current_opts->wildfile.append(bstrdup(lc->str));
563                newsize = res_incexe.current_opts->wildfile.size();
564             } else {
565                type = "wildbase";
566                res_incexe.current_opts->wildbase.append(bstrdup(lc->str));
567                newsize = res_incexe.current_opts->wildbase.size();
568             }
569          } else {
570             type = "wild";
571             res_incexe.current_opts->wild.append(bstrdup(lc->str));
572             newsize = res_incexe.current_opts->wild.size();
573          }
574          Dmsg4(9, "set %s %p size=%d %s\n",
575             type, res_incexe.current_opts, newsize, lc->str);
576          break;
577       default:
578          scan_err1(lc, _("Expected a wild-card string, got: %s\n"), lc->str);
579       }
580    }
581    scan_to_eol(lc);
582 }
583
584 /* Store fstype info */
585 static void store_fstype(LEX *lc, RES_ITEM *item, int index, int pass)
586 {
587    int token;
588
589    token = lex_get_token(lc, T_SKIP_EOL);
590    if (pass == 1) {
591       /* Pickup fstype string */
592       switch (token) {
593       case T_IDENTIFIER:
594       case T_UNQUOTED_STRING:
595       case T_QUOTED_STRING:
596          res_incexe.current_opts->fstype.append(bstrdup(lc->str));
597          Dmsg3(900, "set fstype %p size=%d %s\n",
598             res_incexe.current_opts, res_incexe.current_opts->fstype.size(), lc->str);
599          break;
600       default:
601          scan_err1(lc, _("Expected an fstype string, got: %s\n"), lc->str);
602       }
603    }
604    scan_to_eol(lc);
605 }
606
607 /* Store drivetype info */
608 static void store_drivetype(LEX *lc, RES_ITEM *item, int index, int pass)
609 {
610    int token;
611
612    token = lex_get_token(lc, T_SKIP_EOL);
613    if (pass == 1) {
614       /* Pickup drivetype string */
615       switch (token) {
616       case T_IDENTIFIER:
617       case T_UNQUOTED_STRING:
618       case T_QUOTED_STRING:
619          res_incexe.current_opts->drivetype.append(bstrdup(lc->str));
620          Dmsg3(900, "set drivetype %p size=%d %s\n",
621             res_incexe.current_opts, res_incexe.current_opts->drivetype.size(), lc->str);
622          break;
623       default:
624          scan_err1(lc, _("Expected an drivetype string, got: %s\n"), lc->str);
625       }
626    }
627    scan_to_eol(lc);
628 }
629
630 /*
631  * Store Filename info. Note, for minor efficiency reasons, we
632  * always increase the name buffer by 10 items because we expect
633  * to add more entries.
634  */
635 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass)
636 {
637    int token;
638    INCEXE *incexe;
639
640    token = lex_get_token(lc, T_SKIP_EOL);
641    if (pass == 1) {
642       /* Pickup Filename string
643        */
644       switch (token) {
645       case T_IDENTIFIER:
646       case T_UNQUOTED_STRING:
647          if (strchr(lc->str, '\\')) {
648             scan_err1(lc, _("Backslash found. Use forward slashes or quote the string.: %s\n"), lc->str);
649             /* NOT REACHED */
650          }
651       case T_QUOTED_STRING:
652          if (res_all.res_fs.have_MD5) {
653             MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
654          }
655          incexe = &res_incexe;
656          if (incexe->name_list.size() == 0) {
657             incexe->name_list.init(10, true);
658          }
659          incexe->name_list.append(bstrdup(lc->str));
660          Dmsg1(900, "Add to name_list %s\n", lc->str);
661          break;
662       default:
663          scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
664       }
665    }
666    scan_to_eol(lc);
667 }
668
669 /*
670  * Store Filename info. Note, for minor efficiency reasons, we
671  * always increase the name buffer by 10 items because we expect
672  * to add more entries.
673  */
674 static void store_plugin_name(LEX *lc, RES_ITEM *item, int index, int pass)
675 {
676    int token;
677    INCEXE *incexe;
678
679    token = lex_get_token(lc, T_SKIP_EOL);
680    if (pass == 1) {
681       /* Pickup Filename string
682        */
683       switch (token) {
684       case T_IDENTIFIER:
685       case T_UNQUOTED_STRING:
686          if (strchr(lc->str, '\\')) {
687             scan_err1(lc, _("Backslash found. Use forward slashes or quote the string.: %s\n"), lc->str);
688             /* NOT REACHED */
689          }
690       case T_QUOTED_STRING:
691          if (res_all.res_fs.have_MD5) {
692             MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
693          }
694          incexe = &res_incexe;
695          if (incexe->plugin_list.size() == 0) {
696             incexe->plugin_list.init(10, true);
697          }
698          incexe->plugin_list.append(bstrdup(lc->str));
699          Dmsg1(900, "Add to plugin_list %s\n", lc->str);
700          break;
701       default:
702          scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
703       }
704    }
705    scan_to_eol(lc);
706 }
707
708
709
710 /*
711  * Come here when Options seen in Include/Exclude
712  */
713 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass)
714 {
715    int token, i;
716
717    token = lex_get_token(lc, T_SKIP_EOL);
718    if (token != T_BOB) {
719       scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
720    }
721
722    if (pass == 1) {
723       setup_current_opts();
724    }
725
726    while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
727       if (token == T_EOL) {
728          continue;
729       }
730       if (token == T_EOB) {
731          break;
732       }
733       if (token != T_IDENTIFIER) {
734          scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
735       }
736       for (i=0; options_items[i].name; i++) {
737          if (strcasecmp(options_items[i].name, lc->str) == 0) {
738             token = lex_get_token(lc, T_SKIP_EOL);
739             if (token != T_EQUALS) {
740                scan_err1(lc, _("expected an equals, got: %s"), lc->str);
741             }
742             /* Call item handler */
743             options_items[i].handler(lc, &options_items[i], i, pass);
744             i = -1;
745             break;
746          }
747       }
748       if (i >=0) {
749          scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
750       }
751    }
752 }
753
754
755 /*
756  * New style options come here
757  */
758 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass)
759 {
760    int i;
761    int keyword;
762    char inc_opts[100];
763
764    inc_opts[0] = 0;
765    keyword = INC_KW_NONE;
766    /* Look up the keyword */
767    for (i=0; FS_option_kw[i].name; i++) {
768       if (strcasecmp(item->name, FS_option_kw[i].name) == 0) {
769          keyword = FS_option_kw[i].token;
770          break;
771       }
772    }
773    if (keyword == INC_KW_NONE) {
774       scan_err1(lc, _("Expected a FileSet keyword, got: %s"), lc->str);
775    }
776    /* Now scan for the value */
777    scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
778    if (pass == 1) {
779       bstrncat(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
780       Dmsg2(900, "new pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
781    }
782    scan_to_eol(lc);
783 }
784
785
786
787 /* If current_opts not defined, create first entry */
788 static void setup_current_opts(void)
789 {
790    FOPTS *fo = (FOPTS *)malloc(sizeof(FOPTS));
791    memset(fo, 0, sizeof(FOPTS));
792    fo->regex.init(1, true);
793    fo->regexdir.init(1, true);
794    fo->regexfile.init(1, true);
795    fo->wild.init(1, true);
796    fo->wilddir.init(1, true);
797    fo->wildfile.init(1, true);
798    fo->wildbase.init(1, true);
799    fo->base.init(1, true);
800    fo->fstype.init(1, true);
801    fo->drivetype.init(1, true);
802    res_incexe.current_opts = fo;
803    if (res_incexe.num_opts == 0) {
804       res_incexe.opts_list = (FOPTS **)malloc(sizeof(FOPTS *));
805    } else {
806       res_incexe.opts_list = (FOPTS **)realloc(res_incexe.opts_list,
807                      sizeof(FOPTS *) * (res_incexe.num_opts + 1));
808    }
809    res_incexe.opts_list[res_incexe.num_opts++] = fo;
810 }