]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/inc_conf.c
Minor doc tweaks
[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-2005 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 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_fstype(LEX *lc, RES_ITEM *item, int index, int pass);
43 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass);
44 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass);
45 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass);
46 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass);
47 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass);
48 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass);
49 static void setup_current_opts(void);
50
51
52 /* We build the current resource here as we are
53  * scanning the resource configuration definition,
54  * then move it to allocated memory when the resource
55  * scan is complete.
56  */
57 extern URES res_all;
58 extern int  res_all_size;
59
60 /* We build the current new Include and Exclude items here */
61 static INCEXE res_incexe;
62
63 /*
64  * new Include/Exclude items
65  *   name             handler     value    code flags default_value
66  */
67 static RES_ITEM newinc_items[] = {
68    {"file",            store_fname,   NULL,     0, 0, 0},
69    {"options",         options_res,   NULL,     0, 0, 0},
70    {NULL, NULL, NULL, 0, 0, 0}
71 };
72
73 /*
74  * Items that are valid in an Options resource
75  */
76 static RES_ITEM options_items[] = {
77    {"compression",     store_opts,    NULL,     0, 0, 0},
78    {"signature",       store_opts,    NULL,     0, 0, 0},
79    {"verify",          store_opts,    NULL,     0, 0, 0},
80    {"onefs",           store_opts,    NULL,     0, 0, 0},
81    {"recurse",         store_opts,    NULL,     0, 0, 0},
82    {"sparse",          store_opts,    NULL,     0, 0, 0},
83    {"hardlinks",       store_opts,    NULL,     0, 0, 0},
84    {"readfifo",        store_opts,    NULL,     0, 0, 0},
85    {"replace",         store_opts,    NULL,     0, 0, 0},
86    {"portable",        store_opts,    NULL,     0, 0, 0},
87    {"mtimeonly",       store_opts,    NULL,     0, 0, 0},
88    {"keepatime",       store_opts,    NULL,     0, 0, 0},
89    {"regex",           store_regex,   NULL,     0, 0, 0},
90    {"regexdir",        store_regex,   NULL,     1, 0, 0},
91    {"regexfile",       store_regex,   NULL,     2, 0, 0},
92    {"base",            store_base,    NULL,     0, 0, 0},
93    {"wild",            store_wild,    NULL,     0, 0, 0},
94    {"wilddir",         store_wild,    NULL,     1, 0, 0},
95    {"wildfile",        store_wild,    NULL,     2, 0, 0},
96    {"exclude",         store_opts,    NULL,     0, 0, 0},
97    {"aclsupport",      store_opts,    NULL,     0, 0, 0},
98    {"reader",          store_reader,  NULL,     0, 0, 0},
99    {"writer",          store_writer,  NULL,     0, 0, 0},
100    {"ignorecase",      store_opts,    NULL,     0, 0, 0},
101    {"fstype",          store_fstype,  NULL,     0, 0, 0},
102    {"hfsplussupport",  store_opts,    NULL,     0, 0, 0},
103    {NULL, NULL, NULL, 0, 0, 0}
104 };
105
106
107 /* Define FileSet KeyWord values */
108
109 enum {
110    INC_KW_NONE,
111    INC_KW_COMPRESSION,
112    INC_KW_SIGNATURE,
113    INC_KW_ENCRYPTION,
114    INC_KW_VERIFY,
115    INC_KW_ONEFS,
116    INC_KW_RECURSE,
117    INC_KW_SPARSE,
118    INC_KW_HARDLINK,
119    INC_KW_REPLACE,               /* restore options */
120    INC_KW_READFIFO,              /* Causes fifo data to be read */
121    INC_KW_PORTABLE,
122    INC_KW_MTIMEONLY,
123    INC_KW_KEEPATIME,
124    INC_KW_EXCLUDE,
125    INC_KW_ACL,
126    INC_KW_IGNORECASE,
127    INC_KW_HFSPLUS
128 };
129
130 /*
131  * Include keywords -- these are keywords that can appear
132  *    in the options lists of an old include ( Include = compression= ...)
133  */
134 static struct s_kw FS_option_kw[] = {
135    {"compression", INC_KW_COMPRESSION},
136    {"signature",   INC_KW_SIGNATURE},
137    {"encryption",  INC_KW_ENCRYPTION},
138    {"verify",      INC_KW_VERIFY},
139    {"onefs",       INC_KW_ONEFS},
140    {"recurse",     INC_KW_RECURSE},
141    {"sparse",      INC_KW_SPARSE},
142    {"hardlinks",   INC_KW_HARDLINK},
143    {"replace",     INC_KW_REPLACE},
144    {"readfifo",    INC_KW_READFIFO},
145    {"portable",    INC_KW_PORTABLE},
146    {"mtimeonly",   INC_KW_MTIMEONLY},
147    {"keepatime",   INC_KW_KEEPATIME},
148    {"exclude",     INC_KW_EXCLUDE},
149    {"aclsupport",  INC_KW_ACL},
150    {"ignorecase",  INC_KW_IGNORECASE},
151    {"hfsplussupport", INC_KW_HFSPLUS},
152    {NULL,          0}
153 };
154
155 /* Options for FileSet keywords */
156
157 struct s_fs_opt {
158    const char *name;
159    int keyword;
160    const char *option;
161 };
162
163 /*
164  * Options permitted for each keyword and resulting value.
165  * The output goes into opts, which are then transmitted to
166  * the FD for application as options to the following list of
167  * included files.
168  */
169 static struct s_fs_opt FS_options[] = {
170    {"md5",      INC_KW_SIGNATURE,    "M"},
171    {"sha1",     INC_KW_SIGNATURE,    "S"},
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, i;
275    int options = lc->options;
276    int keyword;
277    char inc_opts[100];
278    int inc_opts_len;
279
280    /*
281     * Decide if we are doing a new Include or an old include. The
282     *  new Include is followed immediately by open brace, whereas the
283     *  old include has options following the Include.
284     */
285    token = lex_get_token(lc, T_SKIP_EOL);
286    if (token == T_BOB) {
287       store_newinc(lc, item, index, pass);
288       return;
289    }
290
291    /* What follows is scanning for the OLD style Include/Exclude */
292
293    if (token != T_EQUALS) {
294       scan_err1(lc, _("Expecting an equals sign, got: %s\n"), lc->str);
295    }
296    lc->options |= LOPT_NO_IDENT;      /* make spaces significant */
297    memset(&res_incexe, 0, sizeof(INCEXE));
298
299    /* Get include options */
300    inc_opts[0] = 0;
301    while ((token=lex_get_token(lc, T_SKIP_EOL)) != T_BOB) {
302
303       keyword = INC_KW_NONE;
304       for (i=0; FS_option_kw[i].name; i++) {
305          if (strcasecmp(lc->str, FS_option_kw[i].name) == 0) {
306             keyword = FS_option_kw[i].token;
307             break;
308          }
309       }
310       if (keyword == INC_KW_NONE) {
311          scan_err1(lc, _("Expected a FileSet keyword, got: %s"), lc->str);
312       }
313       /* Option keyword should be following by = <option> */
314       if ((token=lex_get_token(lc, T_SKIP_EOL)) != T_EQUALS) {
315          scan_err1(lc, _("expected an = following keyword, got: %s"), lc->str);
316       } else {
317          /* Scan right hand side of option */
318          scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
319       }
320       if (token == T_BOB) {
321          break;
322       }
323    }
324
325    if (!inc_opts[0]) {
326       bstrncat(inc_opts, "0", sizeof(inc_opts));   /* set no options */
327    }
328    inc_opts_len = strlen(inc_opts);
329
330    if (pass == 1) {
331       INCEXE *incexe;
332       if (!res_all.res_fs.have_MD5) {
333          MD5Init(&res_all.res_fs.md5c);
334          res_all.res_fs.have_MD5 = TRUE;
335       }
336       setup_current_opts();
337       bstrncpy(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
338       Dmsg2(900, "old pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
339
340       /* Create incexe structure */
341       Dmsg0(900, "Create INCEXE structure\n");
342       incexe = (INCEXE *)malloc(sizeof(INCEXE));
343       memcpy(incexe, &res_incexe, sizeof(INCEXE));
344       memset(&res_incexe, 0, sizeof(INCEXE));
345       if (item->code == 0) { /* include */
346          if (res_all.res_fs.num_includes == 0) {
347             res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
348           } else {
349             res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
350                            sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
351           }
352           res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
353           Dmsg1(900, "num_includes=%d\n", res_all.res_fs.num_includes);
354       } else {    /* exclude */
355          if (res_all.res_fs.num_excludes == 0) {
356             res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
357           } else {
358             res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
359                            sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
360           }
361           res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
362           Dmsg1(900, "num_excludes=%d\n", res_all.res_fs.num_excludes);
363       }
364
365       /* Pickup include/exclude names.  They are stored in INCEXE
366        *  structures which contains the options and the name.
367        */
368       while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOB) {
369          switch (token) {
370          case T_COMMA:
371             continue;
372
373          case T_IDENTIFIER:
374          case T_UNQUOTED_STRING:
375          case T_QUOTED_STRING:
376             if (res_all.res_fs.have_MD5) {
377                MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
378             }
379             if (incexe->name_list.size() == 0) {
380                incexe->name_list.init(10, true);
381             }
382             incexe->name_list.append(bstrdup(lc->str));
383             Dmsg1(900, "Add to name_list %s\n", lc->str);
384             break;
385          default:
386             scan_err1(lc, "Expected a filename, got: %s", lc->str);
387          }
388       }
389       /* Note, MD5Final is done in backup.c */
390    } else { /* pass 2 */
391       while (lex_get_token(lc, T_ALL) != T_EOB)
392          {}
393    }
394    scan_to_eol(lc);
395    lc->options = options;
396    set_bit(index, res_all.hdr.item_present);
397 }
398
399
400 /*
401  * Store NEW style FileSet FInclude/FExclude info
402  *
403  *  Note, when this routine is called, we are inside a FileSet
404  *  resource.  We treat the Include/Execlude like a sort of
405  *  mini-resource within the FileSet resource.
406  */
407 static void store_newinc(LEX *lc, RES_ITEM *item, int index, int pass)
408 {
409    int token, i;
410    INCEXE *incexe;
411    bool options;
412
413    if (!res_all.res_fs.have_MD5) {
414       MD5Init(&res_all.res_fs.md5c);
415       res_all.res_fs.have_MD5 = true;
416    }
417    memset(&res_incexe, 0, sizeof(INCEXE));
418    res_all.res_fs.new_include = true;
419    while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
420       if (token == T_EOB) {
421          break;
422       }
423       if (token != T_IDENTIFIER) {
424          scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
425       }
426       for (i=0; newinc_items[i].name; i++) {
427          options = strcasecmp(lc->str, "options") == 0;
428          if (strcasecmp(newinc_items[i].name, lc->str) == 0) {
429             if (!options) {
430                token = lex_get_token(lc, T_SKIP_EOL);
431                if (token != T_EQUALS) {
432                   scan_err1(lc, "expected an equals, got: %s", lc->str);
433                }
434             }
435             /* Call item handler */
436             newinc_items[i].handler(lc, &newinc_items[i], i, pass);
437             i = -1;
438             break;
439          }
440       }
441       if (i >=0) {
442          scan_err1(lc, "Keyword %s not permitted in this resource", lc->str);
443       }
444    }
445    if (pass == 1) {
446       incexe = (INCEXE *)malloc(sizeof(INCEXE));
447       memcpy(incexe, &res_incexe, sizeof(INCEXE));
448       memset(&res_incexe, 0, sizeof(INCEXE));
449       if (item->code == 0) { /* include */
450          if (res_all.res_fs.num_includes == 0) {
451             res_all.res_fs.include_items = (INCEXE **)malloc(sizeof(INCEXE *));
452          } else {
453             res_all.res_fs.include_items = (INCEXE **)realloc(res_all.res_fs.include_items,
454                            sizeof(INCEXE *) * (res_all.res_fs.num_includes + 1));
455          }
456          res_all.res_fs.include_items[res_all.res_fs.num_includes++] = incexe;
457          Dmsg1(900, "num_includes=%d\n", res_all.res_fs.num_includes);
458       } else {    /* exclude */
459          if (res_all.res_fs.num_excludes == 0) {
460             res_all.res_fs.exclude_items = (INCEXE **)malloc(sizeof(INCEXE *));
461          } else {
462             res_all.res_fs.exclude_items = (INCEXE **)realloc(res_all.res_fs.exclude_items,
463                            sizeof(INCEXE *) * (res_all.res_fs.num_excludes + 1));
464          }
465          res_all.res_fs.exclude_items[res_all.res_fs.num_excludes++] = incexe;
466          Dmsg1(900, "num_excludes=%d\n", res_all.res_fs.num_excludes);
467       }
468    }
469    scan_to_eol(lc);
470    set_bit(index, res_all.hdr.item_present);
471 }
472
473
474 /* Store regex info */
475 static void store_regex(LEX *lc, RES_ITEM *item, int index, int pass)
476 {
477    int token, rc;
478    regex_t preg;
479    char prbuf[500];
480    const char *type;
481    int newsize;
482
483    token = lex_get_token(lc, T_SKIP_EOL);
484    if (pass == 1) {
485       /* Pickup regex string
486        */
487       switch (token) {
488       case T_IDENTIFIER:
489       case T_UNQUOTED_STRING:
490       case T_QUOTED_STRING:
491          rc = regcomp(&preg, lc->str, REG_EXTENDED);
492          if (rc != 0) {
493             regerror(rc, &preg, prbuf, sizeof(prbuf));
494             regfree(&preg);
495             scan_err1(lc, _("Regex compile error. ERR=%s\n"), prbuf);
496             break;
497          }
498          regfree(&preg);
499          if (item->code == 1) {
500             type = "regexdir";
501             res_incexe.current_opts->regexdir.append(bstrdup(lc->str));
502             newsize = res_incexe.current_opts->regexdir.size();
503          } else if (item->code == 2) {
504             type = "regexfile";
505             res_incexe.current_opts->regexfile.append(bstrdup(lc->str));
506             newsize = res_incexe.current_opts->regexfile.size();
507          } else {
508             type = "regex";
509             res_incexe.current_opts->regex.append(bstrdup(lc->str));
510             newsize = res_incexe.current_opts->regex.size();
511          }
512          Dmsg4(900, "set %s %p size=%d %s\n",
513             type, res_incexe.current_opts, newsize, lc->str);
514          break;
515       default:
516          scan_err1(lc, _("Expected a regex string, got: %s\n"), lc->str);
517       }
518    }
519    scan_to_eol(lc);
520 }
521
522 /* Store Base info */
523 static void store_base(LEX *lc, RES_ITEM *item, int index, int pass)
524 {
525    int token;
526
527    token = lex_get_token(lc, T_NAME);
528    if (pass == 1) {
529       /*
530        * Pickup Base Job Name
531        */
532       res_incexe.current_opts->base.append(bstrdup(lc->str));
533    }
534    scan_to_eol(lc);
535 }
536
537 /* Store reader info */
538 static void store_reader(LEX *lc, RES_ITEM *item, int index, int pass)
539 {
540    int token;
541
542    token = lex_get_token(lc, T_NAME);
543    if (pass == 1) {
544       /*
545        * Pickup reader command
546        */
547       res_incexe.current_opts->reader = bstrdup(lc->str);
548    }
549    scan_to_eol(lc);
550 }
551
552 /* Store writer innfo */
553 static void store_writer(LEX *lc, RES_ITEM *item, int index, int pass)
554 {
555    int token;
556
557    token = lex_get_token(lc, T_NAME);
558    if (pass == 1) {
559       /*
560        * Pickup writer command
561        */
562       res_incexe.current_opts->writer = bstrdup(lc->str);
563    }
564    scan_to_eol(lc);
565 }
566
567
568
569 /* Store Wild-card info */
570 static void store_wild(LEX *lc, RES_ITEM *item, int index, int pass)
571 {
572    int token;
573    const char *type;
574    int newsize;
575
576    token = lex_get_token(lc, T_SKIP_EOL);
577    if (pass == 1) {
578       /*
579        * Pickup Wild-card string
580        */
581       switch (token) {
582       case T_IDENTIFIER:
583       case T_UNQUOTED_STRING:
584       case T_QUOTED_STRING:
585          if (item->code == 1) {
586             type = "wilddir";
587             res_incexe.current_opts->wilddir.append(bstrdup(lc->str));
588             newsize = res_incexe.current_opts->wilddir.size();
589          } else if (item->code == 2) {
590             type = "wildfile";
591             res_incexe.current_opts->wildfile.append(bstrdup(lc->str));
592             newsize = res_incexe.current_opts->wildfile.size();
593          } else {
594             type = "wild";
595             res_incexe.current_opts->wild.append(bstrdup(lc->str));
596             newsize = res_incexe.current_opts->wild.size();
597          }
598          Dmsg4(9, "set %s %p size=%d %s\n",
599             type, res_incexe.current_opts, newsize, lc->str);
600          break;
601       default:
602          scan_err1(lc, _("Expected a wild-card string, got: %s\n"), lc->str);
603       }
604    }
605    scan_to_eol(lc);
606 }
607
608 /* Store fstype info */
609 static void store_fstype(LEX *lc, RES_ITEM *item, int index, int pass)
610 {
611    int token;
612
613    token = lex_get_token(lc, T_SKIP_EOL);
614    if (pass == 1) {
615       /* Pickup fstype string */
616       switch (token) {
617       case T_IDENTIFIER:
618       case T_UNQUOTED_STRING:
619       case T_QUOTED_STRING:
620          res_incexe.current_opts->fstype.append(bstrdup(lc->str));
621          Dmsg3(900, "set fstype %p size=%d %s\n",
622             res_incexe.current_opts, res_incexe.current_opts->fstype.size(), lc->str);
623          break;
624       default:
625          scan_err1(lc, _("Expected an fstype string, got: %s\n"), lc->str);
626       }
627    }
628    scan_to_eol(lc);
629 }
630
631 /*
632  * Store Filename info. Note, for minor efficiency reasons, we
633  * always increase the name buffer by 10 items because we expect
634  * to add more entries.
635  */
636 static void store_fname(LEX *lc, RES_ITEM *item, int index, int pass)
637 {
638    int token;
639    INCEXE *incexe;
640
641    token = lex_get_token(lc, T_SKIP_EOL);
642    if (pass == 1) {
643       /* Pickup Filename string
644        */
645       switch (token) {
646          case T_IDENTIFIER:
647          case T_UNQUOTED_STRING:
648          case T_QUOTED_STRING:
649             if (res_all.res_fs.have_MD5) {
650                MD5Update(&res_all.res_fs.md5c, (unsigned char *)lc->str, lc->str_len);
651             }
652             incexe = &res_incexe;
653             if (incexe->name_list.size() == 0) {
654                incexe->name_list.init(10, true);
655             }
656             incexe->name_list.append(bstrdup(lc->str));
657             Dmsg1(900, "Add to name_list %s\n", lc->str);
658             break;
659          default:
660             scan_err1(lc, _("Expected a filename, got: %s"), lc->str);
661       }
662    }
663    scan_to_eol(lc);
664 }
665
666
667 /*
668  * Come here when Options seen in Include/Exclude
669  */
670 static void options_res(LEX *lc, RES_ITEM *item, int index, int pass)
671 {
672    int token, i;
673
674    token = lex_get_token(lc, T_SKIP_EOL);
675    if (token != T_BOB) {
676       scan_err1(lc, "Expecting open brace. Got %s", lc->str);
677    }
678
679    if (pass == 1) {
680       setup_current_opts();
681    }
682
683    while ((token = lex_get_token(lc, T_ALL)) != T_EOF) {
684       if (token == T_EOL) {
685          continue;
686       }
687       if (token == T_EOB) {
688          break;
689       }
690       if (token != T_IDENTIFIER) {
691          scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
692       }
693       for (i=0; options_items[i].name; i++) {
694          if (strcasecmp(options_items[i].name, lc->str) == 0) {
695             token = lex_get_token(lc, T_SKIP_EOL);
696             if (token != T_EQUALS) {
697                scan_err1(lc, "expected an equals, got: %s", lc->str);
698             }
699             /* Call item handler */
700             options_items[i].handler(lc, &options_items[i], i, pass);
701             i = -1;
702             break;
703          }
704       }
705       if (i >=0) {
706          scan_err1(lc, "Keyword %s not permitted in this resource", lc->str);
707       }
708    }
709 }
710
711
712 /*
713  * New style options come here
714  */
715 static void store_opts(LEX *lc, RES_ITEM *item, int index, int pass)
716 {
717    int i;
718    int keyword;
719    char inc_opts[100];
720
721    inc_opts[0] = 0;
722    keyword = INC_KW_NONE;
723    /* Look up the keyword */
724    for (i=0; FS_option_kw[i].name; i++) {
725       if (strcasecmp(item->name, FS_option_kw[i].name) == 0) {
726          keyword = FS_option_kw[i].token;
727          break;
728       }
729    }
730    if (keyword == INC_KW_NONE) {
731       scan_err1(lc, "Expected a FileSet keyword, got: %s", lc->str);
732    }
733    /* Now scan for the value */
734    scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts));
735    if (pass == 1) {
736       bstrncat(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS);
737       Dmsg2(900, "new pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts);
738    }
739    scan_to_eol(lc);
740 }
741
742
743
744 /* If current_opts not defined, create first entry */
745 static void setup_current_opts(void)
746 {
747    FOPTS *fo = (FOPTS *)malloc(sizeof(FOPTS));
748    memset(fo, 0, sizeof(FOPTS));
749    fo->regex.init(1, true);
750    fo->regexdir.init(1, true);
751    fo->regexfile.init(1, true);
752    fo->wild.init(1, true);
753    fo->wilddir.init(1, true);
754    fo->wildfile.init(1, true);
755    fo->base.init(1, true);
756    fo->fstype.init(1, true);
757    res_incexe.current_opts = fo;
758    if (res_incexe.num_opts == 0) {
759       res_incexe.opts_list = (FOPTS **)malloc(sizeof(FOPTS *));
760    } else {
761       res_incexe.opts_list = (FOPTS **)realloc(res_incexe.opts_list,
762                      sizeof(FOPTS *) * (res_incexe.num_opts + 1));
763    }
764    res_incexe.opts_list[res_incexe.num_opts++] = fo;
765 }