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