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