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