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