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