]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/inc_conf.c
migrate
[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 extern URES res_all;
55 extern int  res_all_size;
56
57 /* We build the current new Include and Exclude items here */
58 static INCEXE res_incexe;
59
60 /*
61  * new Include/Exclude items
62  *   name             handler     value    code flags default_value
63  */
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}
68 };
69
70 /*
71  * Items that are valid in an Options resource
72  */
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}
101 };
102
103
104 /* Define FileSet KeyWord values */
105 enum {
106    INC_KW_NONE,
107    INC_KW_COMPRESSION,
108    INC_KW_DIGEST,
109    INC_KW_ENCRYPTION,
110    INC_KW_VERIFY,
111    INC_KW_ONEFS,
112    INC_KW_RECURSE,
113    INC_KW_SPARSE,
114    INC_KW_HARDLINK,
115    INC_KW_REPLACE,               /* restore options */
116    INC_KW_READFIFO,              /* Causes fifo data to be read */
117    INC_KW_PORTABLE,
118    INC_KW_MTIMEONLY,
119    INC_KW_KEEPATIME,
120    INC_KW_EXCLUDE,
121    INC_KW_ACL,
122    INC_KW_IGNORECASE,
123    INC_KW_HFSPLUS
124 };
125
126 /*
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.
131  */
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},
150    {NULL,          0}
151 };
152
153 /* Options for FileSet keywords */
154
155 struct s_fs_opt {
156    const char *name;
157    int keyword;
158    const char *option;
159 };
160
161 /*
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
165  * included files.
166  */
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"},
211    {NULL,       0,                      0}
212 };
213
214
215
216 /*
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.
221  */
222 static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen)
223 {
224    int token, i;
225    char option[3];
226    int lcopts = lc->options;
227
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);
238
239    /*
240     * Standard keyword options for Include/Exclude
241     */
242    } else {
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];
248             i = 0;
249             break;
250          }
251       }
252       if (i != 0) {
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);
257       }
258    }
259    lc->options = lcopts;
260
261    /* If option terminated by comma, eat it */
262    if (lc->ch == ',') {
263       token = lex_get_token(lc, T_ALL);      /* yes, eat comma */
264    }
265 }
266
267 /*
268  *
269  * Store FileSet Include/Exclude info
270  *  NEW style includes are handled in store_newinc()
271  */
272 void store_inc(LEX *lc, RES_ITEM *item, int index, int pass)
273 {
274    int token;
275
276    /*
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.
280     */
281    token = lex_get_token(lc, T_SKIP_EOL);
282    if (token == T_BOB) {
283       store_newinc(lc, item, index, pass);
284       return;
285    }
286    scan_err0(lc, _("Old style Include/Exclude not supported\n"));
287 }
288
289
290 /*
291  * Store NEW style FileSet FInclude/FExclude info
292  *
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.
296  */
297 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass)
298 {
299    int token, i;
300    INCEXE *incexe;
301    bool options;
302
303    if (!res_all.res_fs.have_MD5) {
304       MD5Init(&res_all.res_fs.md5c);
305       res_all.res_fs.have_MD5 = true;
306    }
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) {
311          break;
312       }
313       if (token != T_IDENTIFIER) {
314          scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
315       }
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) {
319             if (!options) {
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);
323                }
324             }
325             /* Call item handler */
326             newinc_items[i].handler(lc, &newinc_items[i], i, pass);
327             i = -1;
328             break;
329          }
330       }
331       if (i >=0) {
332          scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
333       }
334    }
335    if (pass == 1) {
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 *));
342          } else {
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));
345          }
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 *));
351          } else {
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));
354          }
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);
357       }
358    }
359    scan_to_eol(lc);
360    set_bit(index, res_all.hdr.item_present);
361 }
362
363
364 /* Store regex info */
365 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass)
366 {
367    int token, rc;
368    regex_t preg;
369    char prbuf[500];
370    const char *type;
371    int newsize;
372
373    token = lex_get_token(lc, T_SKIP_EOL);
374    if (pass == 1) {
375       /* Pickup regex string
376        */
377       switch (token) {
378       case T_IDENTIFIER:
379       case T_UNQUOTED_STRING:
380       case T_QUOTED_STRING:
381          rc = regcomp(&preg, lc->str, REG_EXTENDED);
382          if (rc != 0) {
383             regerror(rc, &preg, prbuf, sizeof(prbuf));
384             regfree(&preg);
385             scan_err1(lc, _("Regex compile error. ERR=%s\n"), prbuf);
386             break;
387          }
388          regfree(&preg);
389          if (item->code == 1) {
390             type = "regexdir";
391             res_incexe.current_opts->regexdir.append(bstrdup(lc->str));
392             newsize = res_incexe.current_opts->regexdir.size();
393          } else if (item->code == 2) {
394             type = "regexfile";
395             res_incexe.current_opts->regexfile.append(bstrdup(lc->str));
396             newsize = res_incexe.current_opts->regexfile.size();
397          } else {
398             type = "regex";
399             res_incexe.current_opts->regex.append(bstrdup(lc->str));
400             newsize = res_incexe.current_opts->regex.size();
401          }
402          Dmsg4(900, "set %s %p size=%d %s\n",
403             type, res_incexe.current_opts, newsize, lc->str);
404          break;
405       default:
406          scan_err1(lc, _("Expected a regex string, got: %s\n"), lc->str);
407       }
408    }
409    scan_to_eol(lc);
410 }
411
412 /* Store Base info */
413 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass)
414 {
415    int token;
416
417    token = lex_get_token(lc, T_NAME);
418    if (pass == 1) {
419       /*
420        * Pickup Base Job Name
421        */
422       res_incexe.current_opts->base.append(bstrdup(lc->str));
423    }
424    scan_to_eol(lc);
425 }
426
427 /* Store reader info */
428 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass)
429 {
430    int token;
431
432    token = lex_get_token(lc, T_NAME);
433    if (pass == 1) {
434       /*
435        * Pickup reader command
436        */
437       res_incexe.current_opts->reader = bstrdup(lc->str);
438    }
439    scan_to_eol(lc);
440 }
441
442 /* Store writer innfo */
443 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass)
444 {
445    int token;
446
447    token = lex_get_token(lc, T_NAME);
448    if (pass == 1) {
449       /*
450        * Pickup writer command
451        */
452       res_incexe.current_opts->writer = bstrdup(lc->str);
453    }
454    scan_to_eol(lc);
455 }
456
457
458
459 /* Store Wild-card info */
460 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass)
461 {
462    int token;
463    const char *type;
464    int newsize;
465
466    token = lex_get_token(lc, T_SKIP_EOL);
467    if (pass == 1) {
468       /*
469        * Pickup Wild-card string
470        */
471       switch (token) {
472       case T_IDENTIFIER:
473       case T_UNQUOTED_STRING:
474       case T_QUOTED_STRING:
475          if (item->code == 1) {
476             type = "wilddir";
477             res_incexe.current_opts->wilddir.append(bstrdup(lc->str));
478             newsize = res_incexe.current_opts->wilddir.size();
479          } else if (item->code == 2) {
480             type = "wildfile";
481             res_incexe.current_opts->wildfile.append(bstrdup(lc->str));
482             newsize = res_incexe.current_opts->wildfile.size();
483          } else {
484             type = "wild";
485             res_incexe.current_opts->wild.append(bstrdup(lc->str));
486             newsize = res_incexe.current_opts->wild.size();
487          }
488          Dmsg4(9, "set %s %p size=%d %s\n",
489             type, res_incexe.current_opts, newsize, lc->str);
490          break;
491       default:
492          scan_err1(lc, _("Expected a wild-card string, got: %s\n"), lc->str);
493       }
494    }
495    scan_to_eol(lc);
496 }
497
498 /* Store fstype info */
499 static void store_fstype(LEX *lc, RES_ITEM *item, int index, int pass)
500 {
501    int token;
502
503    token = lex_get_token(lc, T_SKIP_EOL);
504    if (pass == 1) {
505       /* Pickup fstype string */
506       switch (token) {
507       case T_IDENTIFIER:
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);
513          break;
514       default:
515          scan_err1(lc, _("Expected an fstype string, got: %s\n"), lc->str);
516       }
517    }
518    scan_to_eol(lc);
519 }
520
521 /*
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.
525  */
526 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass)
527 {
528    int token;
529    INCEXE *incexe;
530
531    token = lex_get_token(lc, T_SKIP_EOL);
532    if (pass == 1) {
533       /* Pickup Filename string
534        */
535       switch (token) {
536       case T_IDENTIFIER:
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);
540             /* NOT REACHED */
541          }
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);
545          }
546          incexe = &res_incexe;
547          if (incexe->name_list.size() == 0) {
548             incexe->name_list.init(10, true);
549          }
550          incexe->name_list.append(bstrdup(lc->str));
551          Dmsg1(900, "Add to name_list %s\n", lc->str);
552          break;
553       default:
554          scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
555       }
556    }
557    scan_to_eol(lc);
558 }
559
560
561 /*
562  * Come here when Options seen in Include/Exclude
563  */
564 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass)
565 {
566    int token, i;
567
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);
571    }
572
573    if (pass == 1) {
574       setup_current_opts();
575    }
576
577    while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
578       if (token == T_EOL) {
579          continue;
580       }
581       if (token == T_EOB) {
582          break;
583       }
584       if (token != T_IDENTIFIER) {
585          scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
586       }
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);
592             }
593             /* Call item handler */
594             options_items[i].handler(lc, &options_items[i], i, pass);
595             i = -1;
596             break;
597          }
598       }
599       if (i >=0) {
600          scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
601       }
602    }
603 }
604
605
606 /*
607  * New style options come here
608  */
609 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass)
610 {
611    int i;
612    int keyword;
613    char inc_opts[100];
614
615    inc_opts[0] = 0;
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;
621          break;
622       }
623    }
624    if (keyword == INC_KW_NONE) {
625       scan_err1(lc, _("Expected a FileSet keyword, got: %s"), lc->str);
626    }
627    /* Now scan for the value */
628    scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
629    if (pass == 1) {
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);
632    }
633    scan_to_eol(lc);
634 }
635
636
637
638 /* If current_opts not defined, create first entry */
639 static void setup_current_opts(void)
640 {
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 *));
654    } else {
655       res_incexe.opts_list = (FOPTS **)realloc(res_incexe.opts_list,
656                      sizeof(FOPTS *) * (res_incexe.num_opts + 1));
657    }
658    res_incexe.opts_list[res_incexe.num_opts++] = fo;
659 }