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