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