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