xref: /netbsd-src/external/bsd/ntp/dist/sntp/libopts/load.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*	$NetBSD: load.c,v 1.1.1.1 2009/12/13 16:57:15 kardel Exp $	*/
2 
3 
4 /*
5  *  Id: f0ececd5fec43bacb417d7b50294accc2121923f
6  *  Time-stamp:      "2008-12-06 10:16:05 bkorb"
7  *
8  *  This file contains the routines that deal with processing text strings
9  *  for options, either from a NUL-terminated string passed in or from an
10  *  rc/ini file.
11  *
12  *  This file is part of AutoOpts, a companion to AutoGen.
13  *  AutoOpts is free software.
14  *  AutoOpts is copyright (c) 1992-2009 by Bruce Korb - all rights reserved
15  *
16  *  AutoOpts is available under any one of two licenses.  The license
17  *  in use must be one of these two and the choice is under the control
18  *  of the user of the license.
19  *
20  *   The GNU Lesser General Public License, version 3 or later
21  *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
22  *
23  *   The Modified Berkeley Software Distribution License
24  *      See the file "COPYING.mbsd"
25  *
26  *  These files have the following md5sums:
27  *
28  *  43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3
29  *  06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3
30  *  66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
31  */
32 
33 tOptionLoadMode option_load_mode = OPTION_LOAD_UNCOOKED;
34 
35 /* = = = START-STATIC-FORWARD = = = */
36 /* static forward declarations maintained by mk-fwd */
37 static ag_bool
38 insertProgramPath(
39     char*   pzBuf,
40     int     bufSize,
41     tCC*    pzName,
42     tCC*    pzProgPath );
43 
44 static ag_bool
45 insertEnvVal(
46     char*   pzBuf,
47     int     bufSize,
48     tCC*    pzName,
49     tCC*    pzProgPath );
50 
51 static char*
52 assembleArgValue( char* pzTxt, tOptionLoadMode mode );
53 /* = = = END-STATIC-FORWARD = = = */
54 
55 /*=export_func  optionMakePath
56  * private:
57  *
58  * what:  translate and construct a path
59  * arg:   + char*       + pzBuf      + The result buffer +
60  * arg:   + int         + bufSize    + The size of this buffer +
61  * arg:   + char const* + pzName     + The input name +
62  * arg:   + char const* + pzProgPath + The full path of the current program +
63  *
64  * ret-type: ag_bool
65  * ret-desc: AG_TRUE if the name was handled, otherwise AG_FALSE.
66  *           If the name does not start with ``$'', then it is handled
67  *           simply by copying the input name to the output buffer and
68  *           resolving the name with either
69  *           @code{canonicalize_file_name(3GLIBC)} or @code{realpath(3C)}.
70  *
71  * doc:
72  *
73  *  This routine will copy the @code{pzName} input name into the @code{pzBuf}
74  *  output buffer, carefully not exceeding @code{bufSize} bytes.  If the
75  *  first character of the input name is a @code{'$'} character, then there
76  *  is special handling:
77  *  @*
78  *  @code{$$} is replaced with the directory name of the @code{pzProgPath},
79  *  searching @code{$PATH} if necessary.
80  *  @*
81  *  @code{$@} is replaced with the AutoGen package data installation directory
82  *  (aka @code{pkgdatadir}).
83  *  @*
84  *  @code{$NAME} is replaced by the contents of the @code{NAME} environment
85  *  variable.  If not found, the search fails.
86  *
87  *  Please note: both @code{$$} and @code{$NAME} must be at the start of the
88  *     @code{pzName} string and must either be the entire string or be followed
89  *     by the @code{'/'} (backslash on windows) character.
90  *
91  * err:  @code{AG_FALSE} is returned if:
92  *       @*
93  *       @bullet{} The input name exceeds @code{bufSize} bytes.
94  *       @*
95  *       @bullet{} @code{$$}, @code{$@@} or @code{$NAME} is not the full string
96  *                 and the next character is not '/'.
97  *       @*
98  *       @bullet{} libopts was built without PKGDATADIR defined and @code{$@@}
99  *                 was specified.
100  *       @*
101  *       @bullet{} @code{NAME} is not a known environment variable
102  *       @*
103  *       @bullet{} @code{canonicalize_file_name} or @code{realpath} return
104  *                 errors (cannot resolve the resulting path).
105 =*/
106 ag_bool
107 optionMakePath(
108     char*   pzBuf,
109     int     bufSize,
110     tCC*    pzName,
111     tCC*    pzProgPath )
112 {
113     size_t  name_len = strlen( pzName );
114 
115 #   ifndef PKGDATADIR
116 #     define PKGDATADIR ""
117 #   endif
118 
119     tSCC    pkgdatadir[] = PKGDATADIR;
120 
121     ag_bool res = AG_TRUE;
122 
123     if (bufSize <= name_len)
124         return AG_FALSE;
125 
126     /*
127      *  IF not an environment variable, just copy the data
128      */
129     if (*pzName != '$') {
130         tCC*  pzS = pzName;
131         char* pzD = pzBuf;
132         int   ct  = bufSize;
133 
134         for (;;) {
135             if ( (*(pzD++) = *(pzS++)) == NUL)
136                 break;
137             if (--ct <= 0)
138                 return AG_FALSE;
139         }
140     }
141 
142     /*
143      *  IF the name starts with "$$", then it must be "$$" or
144      *  it must start with "$$/".  In either event, replace the "$$"
145      *  with the path to the executable and append a "/" character.
146      */
147     else switch (pzName[1]) {
148     case NUL:
149         return AG_FALSE;
150 
151     case '$':
152         res = insertProgramPath( pzBuf, bufSize, pzName, pzProgPath );
153         break;
154 
155     case '@':
156         if (pkgdatadir[0] == NUL)
157             return AG_FALSE;
158 
159         if (name_len + sizeof (pkgdatadir) > bufSize)
160             return AG_FALSE;
161 
162         strcpy(pzBuf, pkgdatadir);
163         strcpy(pzBuf + sizeof(pkgdatadir) - 1, pzName + 2);
164         break;
165 
166     default:
167         res = insertEnvVal( pzBuf, bufSize, pzName, pzProgPath );
168     }
169 
170     if (! res)
171         return AG_FALSE;
172 
173 #if defined(HAVE_CANONICALIZE_FILE_NAME)
174     {
175         char* pz = canonicalize_file_name(pzBuf);
176         if (pz == NULL)
177             return AG_FALSE;
178         if (strlen(pz) < bufSize)
179             strcpy(pzBuf, pz);
180         free(pz);
181     }
182 
183 #elif defined(HAVE_REALPATH)
184     {
185         char z[ PATH_MAX+1 ];
186 
187         if (realpath( pzBuf, z ) == NULL)
188             return AG_FALSE;
189 
190         if (strlen(z) < bufSize)
191             strcpy( pzBuf, z );
192     }
193 #endif
194 
195     return AG_TRUE;
196 }
197 
198 
199 static ag_bool
200 insertProgramPath(
201     char*   pzBuf,
202     int     bufSize,
203     tCC*    pzName,
204     tCC*    pzProgPath )
205 {
206     tCC*    pzPath;
207     tCC*    pz;
208     int     skip = 2;
209 
210     switch (pzName[2]) {
211     case DIRCH:
212         skip = 3;
213     case NUL:
214         break;
215     default:
216         return AG_FALSE;
217     }
218 
219     /*
220      *  See if the path is included in the program name.
221      *  If it is, we're done.  Otherwise, we have to hunt
222      *  for the program using "pathfind".
223      */
224     if (strchr( pzProgPath, DIRCH ) != NULL)
225         pzPath = pzProgPath;
226     else {
227         pzPath = pathfind( getenv( "PATH" ), (char*)pzProgPath, "rx" );
228 
229         if (pzPath == NULL)
230             return AG_FALSE;
231     }
232 
233     pz = strrchr( pzPath, DIRCH );
234 
235     /*
236      *  IF we cannot find a directory name separator,
237      *  THEN we do not have a path name to our executable file.
238      */
239     if (pz == NULL)
240         return AG_FALSE;
241 
242     pzName += skip;
243 
244     /*
245      *  Concatenate the file name to the end of the executable path.
246      *  The result may be either a file or a directory.
247      */
248     if ((pz - pzPath)+1 + strlen(pzName) >= bufSize)
249         return AG_FALSE;
250 
251     memcpy( pzBuf, pzPath, (size_t)((pz - pzPath)+1) );
252     strcpy( pzBuf + (pz - pzPath) + 1, pzName );
253 
254     /*
255      *  If the "pzPath" path was gotten from "pathfind()", then it was
256      *  allocated and we need to deallocate it.
257      */
258     if (pzPath != pzProgPath)
259         AGFREE(pzPath);
260     return AG_TRUE;
261 }
262 
263 
264 static ag_bool
265 insertEnvVal(
266     char*   pzBuf,
267     int     bufSize,
268     tCC*    pzName,
269     tCC*    pzProgPath )
270 {
271     char* pzDir = pzBuf;
272 
273     for (;;) {
274         int ch = (int)*++pzName;
275         if (! IS_VALUE_NAME_CHAR(ch))
276             break;
277         *(pzDir++) = (char)ch;
278     }
279 
280     if (pzDir == pzBuf)
281         return AG_FALSE;
282 
283     *pzDir = NUL;
284 
285     pzDir = getenv( pzBuf );
286 
287     /*
288      *  Environment value not found -- skip the home list entry
289      */
290     if (pzDir == NULL)
291         return AG_FALSE;
292 
293     if (strlen( pzDir ) + 1 + strlen( pzName ) >= bufSize)
294         return AG_FALSE;
295 
296     sprintf( pzBuf, "%s%s", pzDir, pzName );
297     return AG_TRUE;
298 }
299 
300 
301 LOCAL void
302 mungeString( char* pzTxt, tOptionLoadMode mode )
303 {
304     char* pzE;
305 
306     if (mode == OPTION_LOAD_KEEP)
307         return;
308 
309     if (IS_WHITESPACE_CHAR(*pzTxt)) {
310         char* pzS = pzTxt;
311         char* pzD = pzTxt;
312         while (IS_WHITESPACE_CHAR(*++pzS))  ;
313         while ((*(pzD++) = *(pzS++)) != NUL)   ;
314         pzE = pzD-1;
315     } else
316         pzE = pzTxt + strlen( pzTxt );
317 
318     while ((pzE > pzTxt) && IS_WHITESPACE_CHAR(pzE[-1]))  pzE--;
319     *pzE = NUL;
320 
321     if (mode == OPTION_LOAD_UNCOOKED)
322         return;
323 
324     switch (*pzTxt) {
325     default: return;
326     case '"':
327     case '\'': break;
328     }
329 
330     switch (pzE[-1]) {
331     default: return;
332     case '"':
333     case '\'': break;
334     }
335 
336     (void)ao_string_cook( pzTxt, NULL );
337 }
338 
339 
340 static char*
341 assembleArgValue( char* pzTxt, tOptionLoadMode mode )
342 {
343     tSCC zBrk[] = " \t\n:=";
344     char* pzEnd = strpbrk( pzTxt, zBrk );
345     int   space_break;
346 
347     /*
348      *  Not having an argument to a configurable name is okay.
349      */
350     if (pzEnd == NULL)
351         return pzTxt + strlen(pzTxt);
352 
353     /*
354      *  If we are keeping all whitespace, then the  modevalue starts with the
355      *  character that follows the end of the configurable name, regardless
356      *  of which character caused it.
357      */
358     if (mode == OPTION_LOAD_KEEP) {
359         *(pzEnd++) = NUL;
360         return pzEnd;
361     }
362 
363     /*
364      *  If the name ended on a white space character, remember that
365      *  because we'll have to skip over an immediately following ':' or '='
366      *  (and the white space following *that*).
367      */
368     space_break = IS_WHITESPACE_CHAR(*pzEnd);
369     *(pzEnd++) = NUL;
370     while (IS_WHITESPACE_CHAR(*pzEnd))  pzEnd++;
371     if (space_break && ((*pzEnd == ':') || (*pzEnd == '=')))
372         while (IS_WHITESPACE_CHAR(*++pzEnd))  ;
373 
374     return pzEnd;
375 }
376 
377 
378 /*
379  *  Load an option from a block of text.  The text must start with the
380  *  configurable/option name and be followed by its associated value.
381  *  That value may be processed in any of several ways.  See "tOptionLoadMode"
382  *  in autoopts.h.
383  */
384 LOCAL void
385 loadOptionLine(
386     tOptions*   pOpts,
387     tOptState*  pOS,
388     char*       pzLine,
389     tDirection  direction,
390     tOptionLoadMode   load_mode )
391 {
392     while (IS_WHITESPACE_CHAR(*pzLine))  pzLine++;
393 
394     {
395         char* pzArg = assembleArgValue( pzLine, load_mode );
396 
397         if (! SUCCESSFUL( longOptionFind( pOpts, pzLine, pOS )))
398             return;
399         if (pOS->flags & OPTST_NO_INIT)
400             return;
401         pOS->pzOptArg = pzArg;
402     }
403 
404     switch (pOS->flags & (OPTST_IMM|OPTST_DISABLE_IMM)) {
405     case 0:
406         /*
407          *  The selected option has no immediate action.
408          *  THEREFORE, if the direction is PRESETTING
409          *  THEN we skip this option.
410          */
411         if (PRESETTING(direction))
412             return;
413         break;
414 
415     case OPTST_IMM:
416         if (PRESETTING(direction)) {
417             /*
418              *  We are in the presetting direction with an option we handle
419              *  immediately for enablement, but normally for disablement.
420              *  Therefore, skip if disabled.
421              */
422             if ((pOS->flags & OPTST_DISABLED) == 0)
423                 return;
424         } else {
425             /*
426              *  We are in the processing direction with an option we handle
427              *  immediately for enablement, but normally for disablement.
428              *  Therefore, skip if NOT disabled.
429              */
430             if ((pOS->flags & OPTST_DISABLED) != 0)
431                 return;
432         }
433         break;
434 
435     case OPTST_DISABLE_IMM:
436         if (PRESETTING(direction)) {
437             /*
438              *  We are in the presetting direction with an option we handle
439              *  immediately for disablement, but normally for disablement.
440              *  Therefore, skip if NOT disabled.
441              */
442             if ((pOS->flags & OPTST_DISABLED) != 0)
443                 return;
444         } else {
445             /*
446              *  We are in the processing direction with an option we handle
447              *  immediately for disablement, but normally for disablement.
448              *  Therefore, skip if disabled.
449              */
450             if ((pOS->flags & OPTST_DISABLED) == 0)
451                 return;
452         }
453         break;
454 
455     case OPTST_IMM|OPTST_DISABLE_IMM:
456         /*
457          *  The selected option is always for immediate action.
458          *  THEREFORE, if the direction is PROCESSING
459          *  THEN we skip this option.
460          */
461         if (PROCESSING(direction))
462             return;
463         break;
464     }
465 
466     /*
467      *  Fix up the args.
468      */
469     if (OPTST_GET_ARGTYPE(pOS->pOD->fOptState) == OPARG_TYPE_NONE) {
470         if (*pOS->pzOptArg != NUL)
471             return;
472         pOS->pzOptArg = NULL;
473 
474     } else if (pOS->pOD->fOptState & OPTST_ARG_OPTIONAL) {
475         if (*pOS->pzOptArg == NUL)
476              pOS->pzOptArg = NULL;
477         else {
478             AGDUPSTR( pOS->pzOptArg, pOS->pzOptArg, "option argument" );
479             pOS->flags |= OPTST_ALLOC_ARG;
480         }
481 
482     } else {
483         if (*pOS->pzOptArg == NUL)
484              pOS->pzOptArg = zNil;
485         else {
486             AGDUPSTR( pOS->pzOptArg, pOS->pzOptArg, "option argument" );
487             pOS->flags |= OPTST_ALLOC_ARG;
488         }
489     }
490 
491     {
492         tOptionLoadMode sv = option_load_mode;
493         option_load_mode = load_mode;
494         handleOption( pOpts, pOS );
495         option_load_mode = sv;
496     }
497 }
498 
499 
500 /*=export_func  optionLoadLine
501  *
502  * what:  process a string for an option name and value
503  *
504  * arg:   tOptions*,   pOpts,  program options descriptor
505  * arg:   char const*, pzLine, NUL-terminated text
506  *
507  * doc:
508  *
509  *  This is a client program callable routine for setting options from, for
510  *  example, the contents of a file that they read in.  Only one option may
511  *  appear in the text.  It will be treated as a normal (non-preset) option.
512  *
513  *  When passed a pointer to the option struct and a string, it will find
514  *  the option named by the first token on the string and set the option
515  *  argument to the remainder of the string.  The caller must NUL terminate
516  *  the string.  Any embedded new lines will be included in the option
517  *  argument.  If the input looks like one or more quoted strings, then the
518  *  input will be "cooked".  The "cooking" is identical to the string
519  *  formation used in AutoGen definition files (@pxref{basic expression}),
520  *  except that you may not use backquotes.
521  *
522  * err:   Invalid options are silently ignored.  Invalid option arguments
523  *        will cause a warning to print, but the function should return.
524 =*/
525 void
526 optionLoadLine(
527     tOptions*  pOpts,
528     tCC*       pzLine )
529 {
530     tOptState st = OPTSTATE_INITIALIZER(SET);
531     char* pz;
532     AGDUPSTR( pz, pzLine, "user option line" );
533     loadOptionLine( pOpts, &st, pz, DIRECTION_PROCESS, OPTION_LOAD_COOKED );
534     AGFREE( pz );
535 }
536 /*
537  * Local Variables:
538  * mode: C
539  * c-file-style: "stroustrup"
540  * indent-tabs-mode: nil
541  * End:
542  * end of autoopts/load.c */
543