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