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