Annotation of embedaddon/ntp/sntp/libopts/load.c, revision 1.1
1.1 ! misho 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>