xref: /netbsd-src/external/bsd/ntp/dist/sntp/libopts/load.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: load.c,v 1.10 2024/08/18 20:47:24 christos Exp $	*/
2abb0f93cSkardel 
3abb0f93cSkardel 
4f003fb54Skardel /**
5f003fb54Skardel  *  \file load.c
6abb0f93cSkardel  *
7abb0f93cSkardel  *  This file contains the routines that deal with processing text strings
8abb0f93cSkardel  *  for options, either from a NUL-terminated string passed in or from an
9abb0f93cSkardel  *  rc/ini file.
10abb0f93cSkardel  *
112950cc38Schristos  * @addtogroup autoopts
122950cc38Schristos  * @{
132950cc38Schristos  */
142950cc38Schristos /*
15abb0f93cSkardel  *  This file is part of AutoOpts, a companion to AutoGen.
16abb0f93cSkardel  *  AutoOpts is free software.
17*eabc0478Schristos  *  AutoOpts is Copyright (C) 1992-2018 by Bruce Korb - all rights reserved
18abb0f93cSkardel  *
19abb0f93cSkardel  *  AutoOpts is available under any one of two licenses.  The license
20abb0f93cSkardel  *  in use must be one of these two and the choice is under the control
21abb0f93cSkardel  *  of the user of the license.
22abb0f93cSkardel  *
23abb0f93cSkardel  *   The GNU Lesser General Public License, version 3 or later
24abb0f93cSkardel  *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
25abb0f93cSkardel  *
26abb0f93cSkardel  *   The Modified Berkeley Software Distribution License
27abb0f93cSkardel  *      See the file "COPYING.mbsd"
28abb0f93cSkardel  *
292950cc38Schristos  *  These files have the following sha256 sums:
30abb0f93cSkardel  *
312950cc38Schristos  *  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
322950cc38Schristos  *  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
332950cc38Schristos  *  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
34abb0f93cSkardel  */
35abb0f93cSkardel 
362950cc38Schristos static bool
372950cc38Schristos get_realpath(char * buf, size_t b_sz)
382950cc38Schristos {
392950cc38Schristos #if defined(HAVE_CANONICALIZE_FILE_NAME)
402950cc38Schristos     {
412950cc38Schristos         size_t name_len;
422950cc38Schristos 
432950cc38Schristos         char * pz = canonicalize_file_name(buf);
442950cc38Schristos         if (pz == NULL)
452950cc38Schristos             return false;
462950cc38Schristos 
472950cc38Schristos         name_len = strlen(pz);
482950cc38Schristos         if (name_len >= (size_t)b_sz) {
492950cc38Schristos             free(pz);
502950cc38Schristos             return false;
512950cc38Schristos         }
522950cc38Schristos 
532950cc38Schristos         memcpy(buf, pz, name_len + 1);
542950cc38Schristos         free(pz);
552950cc38Schristos     }
562950cc38Schristos 
572950cc38Schristos #elif defined(HAVE_REALPATH)
582950cc38Schristos     {
592950cc38Schristos         size_t name_len;
602950cc38Schristos         char z[PATH_MAX+1];
612950cc38Schristos 
622950cc38Schristos         if (realpath(buf, z) == NULL)
632950cc38Schristos             return false;
642950cc38Schristos 
652950cc38Schristos         name_len = strlen(z);
662950cc38Schristos         if (name_len >= b_sz)
672950cc38Schristos             return false;
682950cc38Schristos 
692950cc38Schristos         memcpy(buf, z, name_len + 1);
702950cc38Schristos     }
712950cc38Schristos #endif
722950cc38Schristos     return true;
732950cc38Schristos }
742950cc38Schristos 
75abb0f93cSkardel /*=export_func  optionMakePath
76abb0f93cSkardel  * private:
77abb0f93cSkardel  *
78abb0f93cSkardel  * what:  translate and construct a path
792950cc38Schristos  * arg:   + char *       + p_buf     + The result buffer +
802950cc38Schristos  * arg:   + int          + b_sz      + The size of this buffer +
812950cc38Schristos  * arg:   + char const * + fname     + The input name +
822950cc38Schristos  * arg:   + char const * + prg_path  + The full path of the current program +
83abb0f93cSkardel  *
842950cc38Schristos  * ret-type: bool
852950cc38Schristos  * ret-desc: true if the name was handled, otherwise false.
86abb0f93cSkardel  *           If the name does not start with ``$'', then it is handled
87abb0f93cSkardel  *           simply by copying the input name to the output buffer and
88abb0f93cSkardel  *           resolving the name with either
89abb0f93cSkardel  *           @code{canonicalize_file_name(3GLIBC)} or @code{realpath(3C)}.
90abb0f93cSkardel  *
91abb0f93cSkardel  * doc:
92abb0f93cSkardel  *
93f003fb54Skardel  *  This routine will copy the @code{pzName} input name into the
94f003fb54Skardel  *  @code{pzBuf} output buffer, not exceeding @code{bufSize} bytes.  If the
95abb0f93cSkardel  *  first character of the input name is a @code{'$'} character, then there
96abb0f93cSkardel  *  is special handling:
97abb0f93cSkardel  *  @*
98abb0f93cSkardel  *  @code{$$} is replaced with the directory name of the @code{pzProgPath},
99abb0f93cSkardel  *  searching @code{$PATH} if necessary.
100abb0f93cSkardel  *  @*
101abb0f93cSkardel  *  @code{$@} is replaced with the AutoGen package data installation directory
102abb0f93cSkardel  *  (aka @code{pkgdatadir}).
103abb0f93cSkardel  *  @*
104abb0f93cSkardel  *  @code{$NAME} is replaced by the contents of the @code{NAME} environment
105abb0f93cSkardel  *  variable.  If not found, the search fails.
106abb0f93cSkardel  *
107abb0f93cSkardel  *  Please note: both @code{$$} and @code{$NAME} must be at the start of the
108abb0f93cSkardel  *     @code{pzName} string and must either be the entire string or be followed
109abb0f93cSkardel  *     by the @code{'/'} (backslash on windows) character.
110abb0f93cSkardel  *
1112950cc38Schristos  * err:  @code{false} is returned if:
112abb0f93cSkardel  *       @*
113abb0f93cSkardel  *       @bullet{} The input name exceeds @code{bufSize} bytes.
114abb0f93cSkardel  *       @*
115abb0f93cSkardel  *       @bullet{} @code{$$}, @code{$@@} or @code{$NAME} is not the full string
116abb0f93cSkardel  *                 and the next character is not '/'.
117abb0f93cSkardel  *       @*
118abb0f93cSkardel  *       @bullet{} libopts was built without PKGDATADIR defined and @code{$@@}
119abb0f93cSkardel  *                 was specified.
120abb0f93cSkardel  *       @*
121abb0f93cSkardel  *       @bullet{} @code{NAME} is not a known environment variable
122abb0f93cSkardel  *       @*
123abb0f93cSkardel  *       @bullet{} @code{canonicalize_file_name} or @code{realpath} return
124abb0f93cSkardel  *                 errors (cannot resolve the resulting path).
125abb0f93cSkardel =*/
1262950cc38Schristos bool
1272950cc38Schristos optionMakePath(char * p_buf, int b_sz, char const * fname, char const * prg_path)
128abb0f93cSkardel {
1292950cc38Schristos     {
1302950cc38Schristos         size_t len = strlen(fname);
131abb0f93cSkardel 
1322950cc38Schristos         if (((size_t)b_sz <= len) || (len == 0))
1332950cc38Schristos             return false;
1342950cc38Schristos     }
135abb0f93cSkardel 
136abb0f93cSkardel     /*
137abb0f93cSkardel      *  IF not an environment variable, just copy the data
138abb0f93cSkardel      */
1392950cc38Schristos     if (*fname != '$') {
1402950cc38Schristos         char   const * src = fname;
1412950cc38Schristos         char * dst = p_buf;
1422950cc38Schristos         int    ct  = b_sz;
143abb0f93cSkardel 
144abb0f93cSkardel         for (;;) {
1452950cc38Schristos             if ( (*(dst++) = *(src++)) == NUL)
146abb0f93cSkardel                 break;
147abb0f93cSkardel             if (--ct <= 0)
1482950cc38Schristos                 return false;
149abb0f93cSkardel         }
150abb0f93cSkardel     }
151abb0f93cSkardel 
152abb0f93cSkardel     /*
153abb0f93cSkardel      *  IF the name starts with "$$", then it must be "$$" or
154abb0f93cSkardel      *  it must start with "$$/".  In either event, replace the "$$"
155abb0f93cSkardel      *  with the path to the executable and append a "/" character.
156abb0f93cSkardel      */
1572950cc38Schristos     else switch (fname[1]) {
158abb0f93cSkardel     case NUL:
1592950cc38Schristos         return false;
160abb0f93cSkardel 
161abb0f93cSkardel     case '$':
1622950cc38Schristos         if (! add_prog_path(p_buf, b_sz, fname, prg_path))
1632950cc38Schristos             return false;
164abb0f93cSkardel         break;
165abb0f93cSkardel 
166abb0f93cSkardel     case '@':
167f003fb54Skardel         if (program_pkgdatadir[0] == NUL)
1682950cc38Schristos             return false;
169abb0f93cSkardel 
1702950cc38Schristos         if (snprintf(p_buf, (size_t)b_sz, "%s%s",
1712950cc38Schristos                      program_pkgdatadir, fname + 2) >= b_sz)
1722950cc38Schristos             return false;
173abb0f93cSkardel         break;
174abb0f93cSkardel 
175abb0f93cSkardel     default:
1762950cc38Schristos         if (! add_env_val(p_buf, b_sz, fname))
1772950cc38Schristos             return false;
178f003fb54Skardel     }
179abb0f93cSkardel 
1802950cc38Schristos     return get_realpath(p_buf, b_sz);
1812950cc38Schristos }
1822950cc38Schristos 
1832950cc38Schristos /**
1842950cc38Schristos  * convert a leading "$$" into a path to the executable.
1852950cc38Schristos  */
1862950cc38Schristos static bool
1872950cc38Schristos add_prog_path(char * buf, int b_sz, char const * fname, char const * prg_path)
188abb0f93cSkardel {
1892950cc38Schristos     char const *   path;
190f003fb54Skardel     char const *   pz;
191abb0f93cSkardel     int     skip = 2;
192*eabc0478Schristos     size_t  fname_len;
193*eabc0478Schristos     size_t  dir_len;  //!< length of the directory portion of the path to the exe
194abb0f93cSkardel 
1952950cc38Schristos     switch (fname[2]) {
196abb0f93cSkardel     case DIRCH:
197abb0f93cSkardel         skip = 3;
198abb0f93cSkardel     case NUL:
199abb0f93cSkardel         break;
200abb0f93cSkardel     default:
2012950cc38Schristos         return false;
202abb0f93cSkardel     }
203abb0f93cSkardel 
204abb0f93cSkardel     /*
205abb0f93cSkardel      *  See if the path is included in the program name.
206abb0f93cSkardel      *  If it is, we're done.  Otherwise, we have to hunt
207abb0f93cSkardel      *  for the program using "pathfind".
208abb0f93cSkardel      */
2092950cc38Schristos     if (strchr(prg_path, DIRCH) != NULL)
2102950cc38Schristos         path = prg_path;
211abb0f93cSkardel     else {
2125d681e99Schristos         path = pathfind(getenv("PATH"), prg_path, "rx");
213abb0f93cSkardel 
2142950cc38Schristos         if (path == NULL)
2152950cc38Schristos             return false;
216abb0f93cSkardel     }
217abb0f93cSkardel 
2182950cc38Schristos     pz = strrchr(path, DIRCH);
219abb0f93cSkardel 
220abb0f93cSkardel     /*
221abb0f93cSkardel      *  IF we cannot find a directory name separator,
222abb0f93cSkardel      *  THEN we do not have a path name to our executable file.
223abb0f93cSkardel      */
224abb0f93cSkardel     if (pz == NULL)
2252950cc38Schristos         return false;
226abb0f93cSkardel 
2272950cc38Schristos     fname    += skip;
228*eabc0478Schristos     fname_len = strlen(fname) + 1; // + NUL byte
229*eabc0478Schristos     dir_len   = (pz - path) + 1;   // + dir sep character
230abb0f93cSkardel 
231abb0f93cSkardel     /*
232abb0f93cSkardel      *  Concatenate the file name to the end of the executable path.
233abb0f93cSkardel      *  The result may be either a file or a directory.
234abb0f93cSkardel      */
235*eabc0478Schristos     if (dir_len + fname_len > (unsigned)b_sz)
2362950cc38Schristos         return false;
237abb0f93cSkardel 
238*eabc0478Schristos     memcpy(buf, path, dir_len);
239*eabc0478Schristos     memcpy(buf + dir_len, fname, fname_len);
240abb0f93cSkardel 
241abb0f93cSkardel     /*
2422950cc38Schristos      *  If the "path" path was gotten from "pathfind()", then it was
243abb0f93cSkardel      *  allocated and we need to deallocate it.
244abb0f93cSkardel      */
2452950cc38Schristos     if (path != prg_path)
2462950cc38Schristos         AGFREE(path);
2472950cc38Schristos     return true;
248abb0f93cSkardel }
249abb0f93cSkardel 
2502950cc38Schristos /**
2512950cc38Schristos  * Add an environment variable value.
2522950cc38Schristos  */
2532950cc38Schristos static bool
2542950cc38Schristos add_env_val(char * buf, int buf_sz, char const * name)
255abb0f93cSkardel {
2562950cc38Schristos     char * dir_part = buf;
257abb0f93cSkardel 
258abb0f93cSkardel     for (;;) {
2592950cc38Schristos         int ch = (int)*++name;
260abb0f93cSkardel         if (! IS_VALUE_NAME_CHAR(ch))
261abb0f93cSkardel             break;
2622950cc38Schristos         *(dir_part++) = (char)ch;
263abb0f93cSkardel     }
264abb0f93cSkardel 
2652950cc38Schristos     if (dir_part == buf)
2662950cc38Schristos         return false;
267abb0f93cSkardel 
2682950cc38Schristos     *dir_part = NUL;
269abb0f93cSkardel 
2702950cc38Schristos     dir_part = getenv(buf);
271abb0f93cSkardel 
272abb0f93cSkardel     /*
273abb0f93cSkardel      *  Environment value not found -- skip the home list entry
274abb0f93cSkardel      */
2752950cc38Schristos     if (dir_part == NULL)
2762950cc38Schristos         return false;
277abb0f93cSkardel 
278*eabc0478Schristos     {
279*eabc0478Schristos         size_t dir_len = strlen(dir_part);
280*eabc0478Schristos         size_t nm_len  = strlen(name) + 1;
281abb0f93cSkardel 
282*eabc0478Schristos         if (dir_len + nm_len >= (unsigned)buf_sz)
283*eabc0478Schristos             return false;
284*eabc0478Schristos         memcpy(buf, dir_part, dir_len);
285*eabc0478Schristos         memcpy(buf + dir_len, name, nm_len);
286*eabc0478Schristos     }
287*eabc0478Schristos 
2882950cc38Schristos     return true;
289abb0f93cSkardel }
290abb0f93cSkardel 
2912950cc38Schristos /**
2922950cc38Schristos  * Trim leading and trailing white space.
2932950cc38Schristos  * If we are cooking the text and the text is quoted, then "cook"
2942950cc38Schristos  * the string.  To cook, the string must be quoted.
2952950cc38Schristos  *
2962950cc38Schristos  * @param[in,out] txt  the input and output string
2972950cc38Schristos  * @param[in]     mode the handling mode (cooking method)
2982950cc38Schristos  */
299*eabc0478Schristos static void
3002950cc38Schristos munge_str(char * txt, tOptionLoadMode mode)
301abb0f93cSkardel {
302*eabc0478Schristos     char * end;
303abb0f93cSkardel 
304abb0f93cSkardel     if (mode == OPTION_LOAD_KEEP)
305abb0f93cSkardel         return;
306abb0f93cSkardel 
3072950cc38Schristos     if (IS_WHITESPACE_CHAR(*txt)) {
3082950cc38Schristos         char * src = SPN_WHITESPACE_CHARS(txt+1);
3092950cc38Schristos         size_t l   = strlen(src) + 1;
3102950cc38Schristos         memmove(txt, src, l);
311*eabc0478Schristos         end = txt + l - 1;
312abb0f93cSkardel 
3132950cc38Schristos     } else
314*eabc0478Schristos         end = txt + strlen(txt);
3152950cc38Schristos 
316*eabc0478Schristos     end  = SPN_WHITESPACE_BACK(txt, end);
317*eabc0478Schristos     *end = NUL;
318abb0f93cSkardel 
319abb0f93cSkardel     if (mode == OPTION_LOAD_UNCOOKED)
320abb0f93cSkardel         return;
321abb0f93cSkardel 
3222950cc38Schristos     switch (*txt) {
323abb0f93cSkardel     default: return;
324abb0f93cSkardel     case '"':
325abb0f93cSkardel     case '\'': break;
326abb0f93cSkardel     }
327abb0f93cSkardel 
328*eabc0478Schristos     switch (end[-1]) {
329abb0f93cSkardel     default: return;
330abb0f93cSkardel     case '"':
331abb0f93cSkardel     case '\'': break;
332abb0f93cSkardel     }
333abb0f93cSkardel 
3342950cc38Schristos     (void)ao_string_cook(txt, NULL);
335abb0f93cSkardel }
336abb0f93cSkardel 
337abb0f93cSkardel static char *
3382950cc38Schristos assemble_arg_val(char * txt, tOptionLoadMode mode)
339abb0f93cSkardel {
3402950cc38Schristos     char * end = strpbrk(txt, ARG_BREAK_STR);
341abb0f93cSkardel     int    space_break;
342abb0f93cSkardel 
343abb0f93cSkardel     /*
344abb0f93cSkardel      *  Not having an argument to a configurable name is okay.
345abb0f93cSkardel      */
3462950cc38Schristos     if (end == NULL)
3472950cc38Schristos         return txt + strlen(txt);
348abb0f93cSkardel 
349abb0f93cSkardel     /*
350abb0f93cSkardel      *  If we are keeping all whitespace, then the  modevalue starts with the
351abb0f93cSkardel      *  character that follows the end of the configurable name, regardless
352abb0f93cSkardel      *  of which character caused it.
353abb0f93cSkardel      */
354abb0f93cSkardel     if (mode == OPTION_LOAD_KEEP) {
3552950cc38Schristos         *(end++) = NUL;
3562950cc38Schristos         return end;
357abb0f93cSkardel     }
358abb0f93cSkardel 
359abb0f93cSkardel     /*
360abb0f93cSkardel      *  If the name ended on a white space character, remember that
361abb0f93cSkardel      *  because we'll have to skip over an immediately following ':' or '='
362abb0f93cSkardel      *  (and the white space following *that*).
363abb0f93cSkardel      */
3642950cc38Schristos     space_break = IS_WHITESPACE_CHAR(*end);
3652950cc38Schristos     *(end++) = NUL;
366abb0f93cSkardel 
3672950cc38Schristos     end = SPN_WHITESPACE_CHARS(end);
3682950cc38Schristos     if (space_break && ((*end == ':') || (*end == '=')))
3692950cc38Schristos         end = SPN_WHITESPACE_CHARS(end+1);
3702950cc38Schristos 
3712950cc38Schristos     return end;
372abb0f93cSkardel }
373abb0f93cSkardel 
3742950cc38Schristos static char *
3752950cc38Schristos trim_quotes(char * arg)
3762950cc38Schristos {
3772950cc38Schristos     switch (*arg) {
3782950cc38Schristos     case '"':
3792950cc38Schristos     case '\'':
3802950cc38Schristos         ao_string_cook(arg, NULL);
3812950cc38Schristos     }
3822950cc38Schristos     return arg;
3832950cc38Schristos }
384abb0f93cSkardel 
3852950cc38Schristos /**
3862950cc38Schristos  * See if the option is to be processed in the current scan direction
3872950cc38Schristos  * (-1 or +1).
388abb0f93cSkardel  */
3892950cc38Schristos static bool
3902950cc38Schristos direction_ok(opt_state_mask_t f, int dir)
391abb0f93cSkardel {
3922950cc38Schristos     if (dir == 0)
3932950cc38Schristos         return true;
394abb0f93cSkardel 
3952950cc38Schristos     switch (f & (OPTST_IMM|OPTST_DISABLE_IMM)) {
396abb0f93cSkardel     case 0:
397abb0f93cSkardel         /*
398abb0f93cSkardel          *  The selected option has no immediate action.
399abb0f93cSkardel          *  THEREFORE, if the direction is PRESETTING
400abb0f93cSkardel          *  THEN we skip this option.
401abb0f93cSkardel          */
4022950cc38Schristos         if (PRESETTING(dir))
4032950cc38Schristos             return false;
404abb0f93cSkardel         break;
405abb0f93cSkardel 
406abb0f93cSkardel     case OPTST_IMM:
4072950cc38Schristos         if (PRESETTING(dir)) {
408abb0f93cSkardel             /*
409abb0f93cSkardel              *  We are in the presetting direction with an option we handle
410abb0f93cSkardel              *  immediately for enablement, but normally for disablement.
411abb0f93cSkardel              *  Therefore, skip if disabled.
412abb0f93cSkardel              */
4132950cc38Schristos             if ((f & OPTST_DISABLED) == 0)
4142950cc38Schristos                 return false;
415abb0f93cSkardel         } else {
416abb0f93cSkardel             /*
417abb0f93cSkardel              *  We are in the processing direction with an option we handle
418abb0f93cSkardel              *  immediately for enablement, but normally for disablement.
419abb0f93cSkardel              *  Therefore, skip if NOT disabled.
420abb0f93cSkardel              */
4212950cc38Schristos             if ((f & OPTST_DISABLED) != 0)
4222950cc38Schristos                 return false;
423abb0f93cSkardel         }
424abb0f93cSkardel         break;
425abb0f93cSkardel 
426abb0f93cSkardel     case OPTST_DISABLE_IMM:
4272950cc38Schristos         if (PRESETTING(dir)) {
428abb0f93cSkardel             /*
429abb0f93cSkardel              *  We are in the presetting direction with an option we handle
430*eabc0478Schristos              *  immediately for disablement, but normally for handling.
431abb0f93cSkardel              *  Therefore, skip if NOT disabled.
432abb0f93cSkardel              */
4332950cc38Schristos             if ((f & OPTST_DISABLED) != 0)
4342950cc38Schristos                 return false;
435abb0f93cSkardel         } else {
436abb0f93cSkardel             /*
437abb0f93cSkardel              *  We are in the processing direction with an option we handle
438*eabc0478Schristos              *  immediately for disablement, but normally for handling.
439abb0f93cSkardel              *  Therefore, skip if disabled.
440abb0f93cSkardel              */
4412950cc38Schristos             if ((f & OPTST_DISABLED) == 0)
4422950cc38Schristos                 return false;
443abb0f93cSkardel         }
444abb0f93cSkardel         break;
445abb0f93cSkardel 
446abb0f93cSkardel     case OPTST_IMM|OPTST_DISABLE_IMM:
447abb0f93cSkardel         /*
448abb0f93cSkardel          *  The selected option is always for immediate action.
449abb0f93cSkardel          *  THEREFORE, if the direction is PROCESSING
450abb0f93cSkardel          *  THEN we skip this option.
451abb0f93cSkardel          */
4522950cc38Schristos         if (PROCESSING(dir))
4532950cc38Schristos             return false;
454abb0f93cSkardel         break;
455abb0f93cSkardel     }
4562950cc38Schristos     return true;
4572950cc38Schristos }
4582950cc38Schristos 
4592950cc38Schristos /**
4602950cc38Schristos  *  Load an option from a block of text.  The text must start with the
4612950cc38Schristos  *  configurable/option name and be followed by its associated value.
4622950cc38Schristos  *  That value may be processed in any of several ways.  See "tOptionLoadMode"
4632950cc38Schristos  *  in autoopts.h.
4642950cc38Schristos  *
4652950cc38Schristos  * @param[in,out] opts       program options descriptor
4662950cc38Schristos  * @param[in,out] opt_state  option processing state
4672950cc38Schristos  * @param[in,out] line       source line with long option name in it
4682950cc38Schristos  * @param[in]     direction  current processing direction (preset or not)
4692950cc38Schristos  * @param[in]     load_mode  option loading mode (OPTION_LOAD_*)
4702950cc38Schristos  */
471*eabc0478Schristos static void
4722950cc38Schristos load_opt_line(tOptions * opts, tOptState * opt_state, char * line,
4732950cc38Schristos               tDirection direction, tOptionLoadMode load_mode )
4742950cc38Schristos {
4752950cc38Schristos     /*
4762950cc38Schristos      * When parsing a stored line, we only look at the characters after
4772950cc38Schristos      * a hyphen.  Long names must always be at least two characters and
4782950cc38Schristos      * short options are always exactly one character long.
4792950cc38Schristos      */
4802950cc38Schristos     line = SPN_LOAD_LINE_SKIP_CHARS(line);
4812950cc38Schristos 
4822950cc38Schristos     {
4832950cc38Schristos         char * arg = assemble_arg_val(line, load_mode);
4842950cc38Schristos 
4852950cc38Schristos         if (IS_OPTION_NAME_CHAR(line[1])) {
4862950cc38Schristos 
4872950cc38Schristos             if (! SUCCESSFUL(opt_find_long(opts, line, opt_state)))
4882950cc38Schristos                 return;
4892950cc38Schristos 
4902950cc38Schristos         } else if (! SUCCESSFUL(opt_find_short(opts, *line, opt_state)))
4912950cc38Schristos             return;
4922950cc38Schristos 
4932950cc38Schristos         if ((! CALLED(direction)) && (opt_state->flags & OPTST_NO_INIT))
4942950cc38Schristos             return;
4952950cc38Schristos 
4962950cc38Schristos         opt_state->pzOptArg = trim_quotes(arg);
4972950cc38Schristos     }
4982950cc38Schristos 
4992950cc38Schristos     if (! direction_ok(opt_state->flags, direction))
5002950cc38Schristos         return;
501abb0f93cSkardel 
502abb0f93cSkardel     /*
503abb0f93cSkardel      *  Fix up the args.
504abb0f93cSkardel      */
5052950cc38Schristos     if (OPTST_GET_ARGTYPE(opt_state->pOD->fOptState) == OPARG_TYPE_NONE) {
5062950cc38Schristos         if (*opt_state->pzOptArg != NUL)
507abb0f93cSkardel             return;
5082950cc38Schristos         opt_state->pzOptArg = NULL;
509abb0f93cSkardel 
5102950cc38Schristos     } else if (opt_state->pOD->fOptState & OPTST_ARG_OPTIONAL) {
5112950cc38Schristos         if (*opt_state->pzOptArg == NUL)
5122950cc38Schristos              opt_state->pzOptArg = NULL;
513abb0f93cSkardel         else {
5142950cc38Schristos             AGDUPSTR(opt_state->pzOptArg, opt_state->pzOptArg, "opt arg");
5152950cc38Schristos             opt_state->flags |= OPTST_ALLOC_ARG;
516abb0f93cSkardel         }
517abb0f93cSkardel 
518abb0f93cSkardel     } else {
5192950cc38Schristos         if (*opt_state->pzOptArg == NUL)
5202950cc38Schristos              opt_state->pzOptArg = zNil;
521abb0f93cSkardel         else {
5222950cc38Schristos             AGDUPSTR(opt_state->pzOptArg, opt_state->pzOptArg, "opt arg");
5232950cc38Schristos             opt_state->flags |= OPTST_ALLOC_ARG;
524abb0f93cSkardel         }
525abb0f93cSkardel     }
526abb0f93cSkardel 
527abb0f93cSkardel     {
528abb0f93cSkardel         tOptionLoadMode sv = option_load_mode;
529abb0f93cSkardel         option_load_mode = load_mode;
5302950cc38Schristos         handle_opt(opts, opt_state);
531abb0f93cSkardel         option_load_mode = sv;
532abb0f93cSkardel     }
533abb0f93cSkardel }
534abb0f93cSkardel 
535abb0f93cSkardel /*=export_func  optionLoadLine
536abb0f93cSkardel  *
537abb0f93cSkardel  * what:  process a string for an option name and value
538abb0f93cSkardel  *
5392950cc38Schristos  * arg:   tOptions *,   opts,  program options descriptor
5402950cc38Schristos  * arg:   char const *, line,  NUL-terminated text
541abb0f93cSkardel  *
542abb0f93cSkardel  * doc:
543abb0f93cSkardel  *
544abb0f93cSkardel  *  This is a client program callable routine for setting options from, for
545abb0f93cSkardel  *  example, the contents of a file that they read in.  Only one option may
546abb0f93cSkardel  *  appear in the text.  It will be treated as a normal (non-preset) option.
547abb0f93cSkardel  *
548abb0f93cSkardel  *  When passed a pointer to the option struct and a string, it will find
549abb0f93cSkardel  *  the option named by the first token on the string and set the option
550abb0f93cSkardel  *  argument to the remainder of the string.  The caller must NUL terminate
5512950cc38Schristos  *  the string.  The caller need not skip over any introductory hyphens.
5522950cc38Schristos  *  Any embedded new lines will be included in the option
553abb0f93cSkardel  *  argument.  If the input looks like one or more quoted strings, then the
554abb0f93cSkardel  *  input will be "cooked".  The "cooking" is identical to the string
555abb0f93cSkardel  *  formation used in AutoGen definition files (@pxref{basic expression}),
556abb0f93cSkardel  *  except that you may not use backquotes.
557abb0f93cSkardel  *
558abb0f93cSkardel  * err:   Invalid options are silently ignored.  Invalid option arguments
559abb0f93cSkardel  *        will cause a warning to print, but the function should return.
560abb0f93cSkardel =*/
561abb0f93cSkardel void
5622950cc38Schristos optionLoadLine(tOptions * opts, char const * line)
563abb0f93cSkardel {
564abb0f93cSkardel     tOptState st = OPTSTATE_INITIALIZER(SET);
565abb0f93cSkardel     char *    pz;
5662950cc38Schristos     proc_state_mask_t sv_flags = opts->fOptSet;
5672950cc38Schristos     opts->fOptSet &= ~OPTPROC_ERRSTOP;
5682950cc38Schristos     AGDUPSTR(pz, line, "opt line");
5692950cc38Schristos     load_opt_line(opts, &st, pz, DIRECTION_CALLED, OPTION_LOAD_COOKED);
570abb0f93cSkardel     AGFREE(pz);
5712950cc38Schristos     opts->fOptSet = sv_flags;
572abb0f93cSkardel }
5732950cc38Schristos /** @}
5742950cc38Schristos  *
575abb0f93cSkardel  * Local Variables:
576abb0f93cSkardel  * mode: C
577abb0f93cSkardel  * c-file-style: "stroustrup"
578abb0f93cSkardel  * indent-tabs-mode: nil
579abb0f93cSkardel  * End:
580abb0f93cSkardel  * end of autoopts/load.c */
581