1181834Sroberto
2280849Scy/**
3280849Scy *  \file load.c
4181834Sroberto *
5181834Sroberto *  This file contains the routines that deal with processing text strings
6181834Sroberto *  for options, either from a NUL-terminated string passed in or from an
7181834Sroberto *  rc/ini file.
8280849Scy *
9280849Scy * @addtogroup autoopts
10280849Scy * @{
11181834Sroberto */
12181834Sroberto/*
13280849Scy *  This file is part of AutoOpts, a companion to AutoGen.
14280849Scy *  AutoOpts is free software.
15285169Scy *  AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved
16181834Sroberto *
17280849Scy *  AutoOpts is available under any one of two licenses.  The license
18280849Scy *  in use must be one of these two and the choice is under the control
19280849Scy *  of the user of the license.
20181834Sroberto *
21280849Scy *   The GNU Lesser General Public License, version 3 or later
22280849Scy *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
23181834Sroberto *
24280849Scy *   The Modified Berkeley Software Distribution License
25280849Scy *      See the file "COPYING.mbsd"
26181834Sroberto *
27280849Scy *  These files have the following sha256 sums:
28181834Sroberto *
29280849Scy *  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
30280849Scy *  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
31280849Scy *  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
32181834Sroberto */
33181834Sroberto
34181834Sroberto/* = = = START-STATIC-FORWARD = = = */
35280849Scystatic bool
36280849Scyget_realpath(char * buf, size_t b_sz);
37181834Sroberto
38280849Scystatic bool
39280849Scyadd_prog_path(char * buf, int b_sz, char const * fname, char const * prg_path);
40181834Sroberto
41280849Scystatic bool
42280849Scyadd_env_val(char * buf, int buf_sz, char const * name);
43280849Scy
44280849Scystatic char *
45280849Scyassemble_arg_val(char * txt, tOptionLoadMode mode);
46280849Scy
47280849Scystatic char *
48280849Scytrim_quotes(char * arg);
49280849Scy
50280849Scystatic bool
51280849Scydirection_ok(opt_state_mask_t f, int dir);
52181834Sroberto/* = = = END-STATIC-FORWARD = = = */
53181834Sroberto
54280849Scystatic bool
55280849Scyget_realpath(char * buf, size_t b_sz)
56280849Scy{
57280849Scy#if defined(HAVE_CANONICALIZE_FILE_NAME)
58280849Scy    {
59280849Scy        size_t name_len;
60280849Scy
61280849Scy        char * pz = canonicalize_file_name(buf);
62280849Scy        if (pz == NULL)
63280849Scy            return false;
64280849Scy
65280849Scy        name_len = strlen(pz);
66280849Scy        if (name_len >= (size_t)b_sz) {
67280849Scy            free(pz);
68280849Scy            return false;
69280849Scy        }
70280849Scy
71280849Scy        memcpy(buf, pz, name_len + 1);
72280849Scy        free(pz);
73280849Scy    }
74280849Scy
75280849Scy#elif defined(HAVE_REALPATH)
76280849Scy    {
77280849Scy        size_t name_len;
78280849Scy        char z[PATH_MAX+1];
79280849Scy
80280849Scy        if (realpath(buf, z) == NULL)
81280849Scy            return false;
82280849Scy
83280849Scy        name_len = strlen(z);
84280849Scy        if (name_len >= b_sz)
85280849Scy            return false;
86280849Scy
87280849Scy        memcpy(buf, z, name_len + 1);
88280849Scy    }
89280849Scy#endif
90280849Scy    return true;
91280849Scy}
92280849Scy
93181834Sroberto/*=export_func  optionMakePath
94181834Sroberto * private:
95181834Sroberto *
96181834Sroberto * what:  translate and construct a path
97285169Scy * arg:   + char *       + p_buf     + The result buffer +
98285169Scy * arg:   + int          + b_sz      + The size of this buffer +
99285169Scy * arg:   + char const * + fname     + The input name +
100285169Scy * arg:   + char const * + prg_path  + The full path of the current program +
101181834Sroberto *
102280849Scy * ret-type: bool
103280849Scy * ret-desc: true if the name was handled, otherwise false.
104181834Sroberto *           If the name does not start with ``$'', then it is handled
105181834Sroberto *           simply by copying the input name to the output buffer and
106280849Scy *           resolving the name with either
107280849Scy *           @code{canonicalize_file_name(3GLIBC)} or @code{realpath(3C)}.
108181834Sroberto *
109181834Sroberto * doc:
110181834Sroberto *
111280849Scy *  This routine will copy the @code{pzName} input name into the
112280849Scy *  @code{pzBuf} output buffer, not exceeding @code{bufSize} bytes.  If the
113181834Sroberto *  first character of the input name is a @code{'$'} character, then there
114181834Sroberto *  is special handling:
115181834Sroberto *  @*
116181834Sroberto *  @code{$$} is replaced with the directory name of the @code{pzProgPath},
117181834Sroberto *  searching @code{$PATH} if necessary.
118181834Sroberto *  @*
119181834Sroberto *  @code{$@} is replaced with the AutoGen package data installation directory
120181834Sroberto *  (aka @code{pkgdatadir}).
121181834Sroberto *  @*
122181834Sroberto *  @code{$NAME} is replaced by the contents of the @code{NAME} environment
123181834Sroberto *  variable.  If not found, the search fails.
124181834Sroberto *
125181834Sroberto *  Please note: both @code{$$} and @code{$NAME} must be at the start of the
126181834Sroberto *     @code{pzName} string and must either be the entire string or be followed
127181834Sroberto *     by the @code{'/'} (backslash on windows) character.
128181834Sroberto *
129280849Scy * err:  @code{false} is returned if:
130181834Sroberto *       @*
131181834Sroberto *       @bullet{} The input name exceeds @code{bufSize} bytes.
132181834Sroberto *       @*
133181834Sroberto *       @bullet{} @code{$$}, @code{$@@} or @code{$NAME} is not the full string
134181834Sroberto *                 and the next character is not '/'.
135181834Sroberto *       @*
136181834Sroberto *       @bullet{} libopts was built without PKGDATADIR defined and @code{$@@}
137181834Sroberto *                 was specified.
138181834Sroberto *       @*
139181834Sroberto *       @bullet{} @code{NAME} is not a known environment variable
140181834Sroberto *       @*
141181834Sroberto *       @bullet{} @code{canonicalize_file_name} or @code{realpath} return
142181834Sroberto *                 errors (cannot resolve the resulting path).
143181834Sroberto=*/
144280849Scybool
145280849ScyoptionMakePath(char * p_buf, int b_sz, char const * fname, char const * prg_path)
146181834Sroberto{
147280849Scy    {
148280849Scy        size_t len = strlen(fname);
149181834Sroberto
150280849Scy        if (((size_t)b_sz <= len) || (len == 0))
151280849Scy            return false;
152280849Scy    }
153181834Sroberto
154181834Sroberto    /*
155181834Sroberto     *  IF not an environment variable, just copy the data
156181834Sroberto     */
157280849Scy    if (*fname != '$') {
158280849Scy        char   const * src = fname;
159280849Scy        char * dst = p_buf;
160280849Scy        int    ct  = b_sz;
161181834Sroberto
162181834Sroberto        for (;;) {
163280849Scy            if ( (*(dst++) = *(src++)) == NUL)
164181834Sroberto                break;
165181834Sroberto            if (--ct <= 0)
166280849Scy                return false;
167181834Sroberto        }
168181834Sroberto    }
169181834Sroberto
170181834Sroberto    /*
171181834Sroberto     *  IF the name starts with "$$", then it must be "$$" or
172181834Sroberto     *  it must start with "$$/".  In either event, replace the "$$"
173181834Sroberto     *  with the path to the executable and append a "/" character.
174181834Sroberto     */
175280849Scy    else switch (fname[1]) {
176181834Sroberto    case NUL:
177280849Scy        return false;
178181834Sroberto
179181834Sroberto    case '$':
180280849Scy        if (! add_prog_path(p_buf, b_sz, fname, prg_path))
181280849Scy            return false;
182181834Sroberto        break;
183181834Sroberto
184181834Sroberto    case '@':
185280849Scy        if (program_pkgdatadir[0] == NUL)
186280849Scy            return false;
187181834Sroberto
188280849Scy        if (snprintf(p_buf, (size_t)b_sz, "%s%s",
189280849Scy                     program_pkgdatadir, fname + 2) >= b_sz)
190280849Scy            return false;
191181834Sroberto        break;
192181834Sroberto
193181834Sroberto    default:
194280849Scy        if (! add_env_val(p_buf, b_sz, fname))
195280849Scy            return false;
196181834Sroberto    }
197181834Sroberto
198280849Scy    return get_realpath(p_buf, b_sz);
199181834Sroberto}
200181834Sroberto
201280849Scy/**
202280849Scy * convert a leading "$$" into a path to the executable.
203280849Scy */
204280849Scystatic bool
205280849Scyadd_prog_path(char * buf, int b_sz, char const * fname, char const * prg_path)
206181834Sroberto{
207280849Scy    char const *   path;
208280849Scy    char const *   pz;
209181834Sroberto    int     skip = 2;
210181834Sroberto
211280849Scy    switch (fname[2]) {
212181834Sroberto    case DIRCH:
213181834Sroberto        skip = 3;
214181834Sroberto    case NUL:
215181834Sroberto        break;
216181834Sroberto    default:
217280849Scy        return false;
218181834Sroberto    }
219181834Sroberto
220181834Sroberto    /*
221181834Sroberto     *  See if the path is included in the program name.
222181834Sroberto     *  If it is, we're done.  Otherwise, we have to hunt
223181834Sroberto     *  for the program using "pathfind".
224181834Sroberto     */
225280849Scy    if (strchr(prg_path, DIRCH) != NULL)
226280849Scy        path = prg_path;
227181834Sroberto    else {
228294554Sdelphij        path = pathfind(getenv("PATH"), prg_path, "rx");
229181834Sroberto
230280849Scy        if (path == NULL)
231280849Scy            return false;
232181834Sroberto    }
233181834Sroberto
234280849Scy    pz = strrchr(path, DIRCH);
235181834Sroberto
236181834Sroberto    /*
237181834Sroberto     *  IF we cannot find a directory name separator,
238181834Sroberto     *  THEN we do not have a path name to our executable file.
239181834Sroberto     */
240181834Sroberto    if (pz == NULL)
241280849Scy        return false;
242181834Sroberto
243280849Scy    fname += skip;
244181834Sroberto
245181834Sroberto    /*
246181834Sroberto     *  Concatenate the file name to the end of the executable path.
247181834Sroberto     *  The result may be either a file or a directory.
248181834Sroberto     */
249280849Scy    if ((unsigned)(pz - path) + 1 + strlen(fname) >= (unsigned)b_sz)
250280849Scy        return false;
251181834Sroberto
252280849Scy    memcpy(buf, path, (size_t)((pz - path)+1));
253280849Scy    strcpy(buf + (pz - path) + 1, fname);
254181834Sroberto
255181834Sroberto    /*
256280849Scy     *  If the "path" path was gotten from "pathfind()", then it was
257181834Sroberto     *  allocated and we need to deallocate it.
258181834Sroberto     */
259280849Scy    if (path != prg_path)
260280849Scy        AGFREE(path);
261280849Scy    return true;
262181834Sroberto}
263181834Sroberto
264280849Scy/**
265280849Scy * Add an environment variable value.
266280849Scy */
267280849Scystatic bool
268280849Scyadd_env_val(char * buf, int buf_sz, char const * name)
269181834Sroberto{
270280849Scy    char * dir_part = buf;
271181834Sroberto
272181834Sroberto    for (;;) {
273280849Scy        int ch = (int)*++name;
274280849Scy        if (! IS_VALUE_NAME_CHAR(ch))
275181834Sroberto            break;
276280849Scy        *(dir_part++) = (char)ch;
277181834Sroberto    }
278181834Sroberto
279280849Scy    if (dir_part == buf)
280280849Scy        return false;
281181834Sroberto
282280849Scy    *dir_part = NUL;
283181834Sroberto
284280849Scy    dir_part = getenv(buf);
285181834Sroberto
286181834Sroberto    /*
287181834Sroberto     *  Environment value not found -- skip the home list entry
288181834Sroberto     */
289280849Scy    if (dir_part == NULL)
290280849Scy        return false;
291181834Sroberto
292280849Scy    if (strlen(dir_part) + 1 + strlen(name) >= (unsigned)buf_sz)
293280849Scy        return false;
294181834Sroberto
295280849Scy    sprintf(buf, "%s%s", dir_part, name);
296280849Scy    return true;
297181834Sroberto}
298181834Sroberto
299280849Scy/**
300280849Scy * Trim leading and trailing white space.
301280849Scy * If we are cooking the text and the text is quoted, then "cook"
302280849Scy * the string.  To cook, the string must be quoted.
303280849Scy *
304280849Scy * @param[in,out] txt  the input and output string
305280849Scy * @param[in]     mode the handling mode (cooking method)
306280849Scy */
307181834SrobertoLOCAL void
308280849Scymunge_str(char * txt, tOptionLoadMode mode)
309181834Sroberto{
310280849Scy    char * pzE;
311181834Sroberto
312181834Sroberto    if (mode == OPTION_LOAD_KEEP)
313181834Sroberto        return;
314181834Sroberto
315280849Scy    if (IS_WHITESPACE_CHAR(*txt)) {
316280849Scy        char * src = SPN_WHITESPACE_CHARS(txt+1);
317280849Scy        size_t l   = strlen(src) + 1;
318280849Scy        memmove(txt, src, l);
319280849Scy        pzE = txt + l - 1;
320280849Scy
321181834Sroberto    } else
322280849Scy        pzE = txt + strlen(txt);
323181834Sroberto
324280849Scy    pzE  = SPN_WHITESPACE_BACK(txt, pzE);
325181834Sroberto    *pzE = NUL;
326181834Sroberto
327181834Sroberto    if (mode == OPTION_LOAD_UNCOOKED)
328181834Sroberto        return;
329181834Sroberto
330280849Scy    switch (*txt) {
331181834Sroberto    default: return;
332181834Sroberto    case '"':
333181834Sroberto    case '\'': break;
334181834Sroberto    }
335181834Sroberto
336181834Sroberto    switch (pzE[-1]) {
337181834Sroberto    default: return;
338181834Sroberto    case '"':
339181834Sroberto    case '\'': break;
340181834Sroberto    }
341181834Sroberto
342280849Scy    (void)ao_string_cook(txt, NULL);
343181834Sroberto}
344181834Sroberto
345280849Scystatic char *
346280849Scyassemble_arg_val(char * txt, tOptionLoadMode mode)
347181834Sroberto{
348280849Scy    char * end = strpbrk(txt, ARG_BREAK_STR);
349280849Scy    int    space_break;
350181834Sroberto
351181834Sroberto    /*
352181834Sroberto     *  Not having an argument to a configurable name is okay.
353181834Sroberto     */
354280849Scy    if (end == NULL)
355280849Scy        return txt + strlen(txt);
356181834Sroberto
357181834Sroberto    /*
358181834Sroberto     *  If we are keeping all whitespace, then the  modevalue starts with the
359181834Sroberto     *  character that follows the end of the configurable name, regardless
360181834Sroberto     *  of which character caused it.
361181834Sroberto     */
362181834Sroberto    if (mode == OPTION_LOAD_KEEP) {
363280849Scy        *(end++) = NUL;
364280849Scy        return end;
365181834Sroberto    }
366181834Sroberto
367181834Sroberto    /*
368181834Sroberto     *  If the name ended on a white space character, remember that
369181834Sroberto     *  because we'll have to skip over an immediately following ':' or '='
370181834Sroberto     *  (and the white space following *that*).
371181834Sroberto     */
372280849Scy    space_break = IS_WHITESPACE_CHAR(*end);
373280849Scy    *(end++) = NUL;
374181834Sroberto
375280849Scy    end = SPN_WHITESPACE_CHARS(end);
376280849Scy    if (space_break && ((*end == ':') || (*end == '=')))
377280849Scy        end = SPN_WHITESPACE_CHARS(end+1);
378280849Scy
379280849Scy    return end;
380181834Sroberto}
381181834Sroberto
382280849Scystatic char *
383280849Scytrim_quotes(char * arg)
384280849Scy{
385280849Scy    switch (*arg) {
386280849Scy    case '"':
387280849Scy    case '\'':
388280849Scy        ao_string_cook(arg, NULL);
389280849Scy    }
390280849Scy    return arg;
391280849Scy}
392181834Sroberto
393280849Scy/**
394280849Scy * See if the option is to be processed in the current scan direction
395280849Scy * (-1 or +1).
396181834Sroberto */
397280849Scystatic bool
398280849Scydirection_ok(opt_state_mask_t f, int dir)
399181834Sroberto{
400280849Scy    if (dir == 0)
401280849Scy        return true;
402181834Sroberto
403280849Scy    switch (f & (OPTST_IMM|OPTST_DISABLE_IMM)) {
404181834Sroberto    case 0:
405181834Sroberto        /*
406181834Sroberto         *  The selected option has no immediate action.
407181834Sroberto         *  THEREFORE, if the direction is PRESETTING
408181834Sroberto         *  THEN we skip this option.
409181834Sroberto         */
410280849Scy        if (PRESETTING(dir))
411280849Scy            return false;
412181834Sroberto        break;
413181834Sroberto
414181834Sroberto    case OPTST_IMM:
415280849Scy        if (PRESETTING(dir)) {
416181834Sroberto            /*
417181834Sroberto             *  We are in the presetting direction with an option we handle
418181834Sroberto             *  immediately for enablement, but normally for disablement.
419181834Sroberto             *  Therefore, skip if disabled.
420181834Sroberto             */
421280849Scy            if ((f & OPTST_DISABLED) == 0)
422280849Scy                return false;
423181834Sroberto        } else {
424181834Sroberto            /*
425181834Sroberto             *  We are in the processing direction with an option we handle
426181834Sroberto             *  immediately for enablement, but normally for disablement.
427181834Sroberto             *  Therefore, skip if NOT disabled.
428181834Sroberto             */
429280849Scy            if ((f & OPTST_DISABLED) != 0)
430280849Scy                return false;
431181834Sroberto        }
432181834Sroberto        break;
433181834Sroberto
434181834Sroberto    case OPTST_DISABLE_IMM:
435280849Scy        if (PRESETTING(dir)) {
436181834Sroberto            /*
437181834Sroberto             *  We are in the presetting direction with an option we handle
438181834Sroberto             *  immediately for disablement, but normally for disablement.
439181834Sroberto             *  Therefore, skip if NOT disabled.
440181834Sroberto             */
441280849Scy            if ((f & OPTST_DISABLED) != 0)
442280849Scy                return false;
443181834Sroberto        } else {
444181834Sroberto            /*
445181834Sroberto             *  We are in the processing direction with an option we handle
446181834Sroberto             *  immediately for disablement, but normally for disablement.
447181834Sroberto             *  Therefore, skip if disabled.
448181834Sroberto             */
449280849Scy            if ((f & OPTST_DISABLED) == 0)
450280849Scy                return false;
451181834Sroberto        }
452181834Sroberto        break;
453181834Sroberto
454181834Sroberto    case OPTST_IMM|OPTST_DISABLE_IMM:
455181834Sroberto        /*
456181834Sroberto         *  The selected option is always for immediate action.
457181834Sroberto         *  THEREFORE, if the direction is PROCESSING
458181834Sroberto         *  THEN we skip this option.
459181834Sroberto         */
460280849Scy        if (PROCESSING(dir))
461280849Scy            return false;
462181834Sroberto        break;
463181834Sroberto    }
464280849Scy    return true;
465280849Scy}
466181834Sroberto
467280849Scy/**
468280849Scy *  Load an option from a block of text.  The text must start with the
469280849Scy *  configurable/option name and be followed by its associated value.
470280849Scy *  That value may be processed in any of several ways.  See "tOptionLoadMode"
471280849Scy *  in autoopts.h.
472280849Scy *
473280849Scy * @param[in,out] opts       program options descriptor
474280849Scy * @param[in,out] opt_state  option processing state
475280849Scy * @param[in,out] line       source line with long option name in it
476280849Scy * @param[in]     direction  current processing direction (preset or not)
477280849Scy * @param[in]     load_mode  option loading mode (OPTION_LOAD_*)
478280849Scy */
479280849ScyLOCAL void
480280849Scyload_opt_line(tOptions * opts, tOptState * opt_state, char * line,
481280849Scy              tDirection direction, tOptionLoadMode load_mode )
482280849Scy{
483181834Sroberto    /*
484280849Scy     * When parsing a stored line, we only look at the characters after
485280849Scy     * a hyphen.  Long names must always be at least two characters and
486280849Scy     * short options are always exactly one character long.
487280849Scy     */
488280849Scy    line = SPN_LOAD_LINE_SKIP_CHARS(line);
489280849Scy
490280849Scy    {
491280849Scy        char * arg = assemble_arg_val(line, load_mode);
492280849Scy
493280849Scy        if (IS_OPTION_NAME_CHAR(line[1])) {
494280849Scy
495280849Scy            if (! SUCCESSFUL(opt_find_long(opts, line, opt_state)))
496280849Scy                return;
497280849Scy
498280849Scy        } else if (! SUCCESSFUL(opt_find_short(opts, *line, opt_state)))
499280849Scy            return;
500280849Scy
501280849Scy        if ((! CALLED(direction)) && (opt_state->flags & OPTST_NO_INIT))
502280849Scy            return;
503280849Scy
504280849Scy        opt_state->pzOptArg = trim_quotes(arg);
505280849Scy    }
506280849Scy
507280849Scy    if (! direction_ok(opt_state->flags, direction))
508280849Scy        return;
509280849Scy
510280849Scy    /*
511181834Sroberto     *  Fix up the args.
512181834Sroberto     */
513280849Scy    if (OPTST_GET_ARGTYPE(opt_state->pOD->fOptState) == OPARG_TYPE_NONE) {
514280849Scy        if (*opt_state->pzOptArg != NUL)
515181834Sroberto            return;
516280849Scy        opt_state->pzOptArg = NULL;
517181834Sroberto
518280849Scy    } else if (opt_state->pOD->fOptState & OPTST_ARG_OPTIONAL) {
519280849Scy        if (*opt_state->pzOptArg == NUL)
520280849Scy             opt_state->pzOptArg = NULL;
521181834Sroberto        else {
522280849Scy            AGDUPSTR(opt_state->pzOptArg, opt_state->pzOptArg, "opt arg");
523280849Scy            opt_state->flags |= OPTST_ALLOC_ARG;
524181834Sroberto        }
525181834Sroberto
526181834Sroberto    } else {
527280849Scy        if (*opt_state->pzOptArg == NUL)
528280849Scy             opt_state->pzOptArg = zNil;
529181834Sroberto        else {
530280849Scy            AGDUPSTR(opt_state->pzOptArg, opt_state->pzOptArg, "opt arg");
531280849Scy            opt_state->flags |= OPTST_ALLOC_ARG;
532181834Sroberto        }
533181834Sroberto    }
534181834Sroberto
535181834Sroberto    {
536181834Sroberto        tOptionLoadMode sv = option_load_mode;
537181834Sroberto        option_load_mode = load_mode;
538280849Scy        handle_opt(opts, opt_state);
539181834Sroberto        option_load_mode = sv;
540181834Sroberto    }
541181834Sroberto}
542181834Sroberto
543181834Sroberto/*=export_func  optionLoadLine
544181834Sroberto *
545181834Sroberto * what:  process a string for an option name and value
546181834Sroberto *
547285169Scy * arg:   tOptions *,   opts,  program options descriptor
548285169Scy * arg:   char const *, line,  NUL-terminated text
549181834Sroberto *
550181834Sroberto * doc:
551181834Sroberto *
552181834Sroberto *  This is a client program callable routine for setting options from, for
553181834Sroberto *  example, the contents of a file that they read in.  Only one option may
554181834Sroberto *  appear in the text.  It will be treated as a normal (non-preset) option.
555181834Sroberto *
556181834Sroberto *  When passed a pointer to the option struct and a string, it will find
557181834Sroberto *  the option named by the first token on the string and set the option
558181834Sroberto *  argument to the remainder of the string.  The caller must NUL terminate
559280849Scy *  the string.  The caller need not skip over any introductory hyphens.
560280849Scy *  Any embedded new lines will be included in the option
561181834Sroberto *  argument.  If the input looks like one or more quoted strings, then the
562181834Sroberto *  input will be "cooked".  The "cooking" is identical to the string
563181834Sroberto *  formation used in AutoGen definition files (@pxref{basic expression}),
564181834Sroberto *  except that you may not use backquotes.
565181834Sroberto *
566181834Sroberto * err:   Invalid options are silently ignored.  Invalid option arguments
567181834Sroberto *        will cause a warning to print, but the function should return.
568181834Sroberto=*/
569181834Srobertovoid
570280849ScyoptionLoadLine(tOptions * opts, char const * line)
571181834Sroberto{
572181834Sroberto    tOptState st = OPTSTATE_INITIALIZER(SET);
573280849Scy    char *    pz;
574280849Scy    proc_state_mask_t sv_flags = opts->fOptSet;
575280849Scy    opts->fOptSet &= ~OPTPROC_ERRSTOP;
576280849Scy    AGDUPSTR(pz, line, "opt line");
577280849Scy    load_opt_line(opts, &st, pz, DIRECTION_CALLED, OPTION_LOAD_COOKED);
578280849Scy    AGFREE(pz);
579280849Scy    opts->fOptSet = sv_flags;
580181834Sroberto}
581280849Scy/** @}
582280849Scy *
583181834Sroberto * Local Variables:
584181834Sroberto * mode: C
585181834Sroberto * c-file-style: "stroustrup"
586181834Sroberto * indent-tabs-mode: nil
587181834Sroberto * End:
588181834Sroberto * end of autoopts/load.c */
589