File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / ntp / sntp / libopts / load.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue May 29 12:08:38 2012 UTC (12 years, 7 months ago) by misho
Branches: ntp, MAIN
CVS tags: v4_2_6p5p0, v4_2_6p5, HEAD
ntp 4.2.6p5

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>