Home | History | Annotate | Line # | Download | only in libopts
load.c revision 1.1
      1 /*	$NetBSD: load.c,v 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