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