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