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