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