Return to load.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / ntp / sntp / libopts |
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 */