Return to save.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / ntp / sntp / libopts |
1.1 ! misho 1: ! 2: /* ! 3: * \file save.c ! 4: * ! 5: * Time-stamp: "2011-04-06 09:21:44 bkorb" ! 6: * ! 7: * This module's routines will take the currently set options and ! 8: * store them into an ".rc" file for re-interpretation the next ! 9: * time the invoking program is run. ! 10: * ! 11: * This file is part of AutoOpts, a companion to AutoGen. ! 12: * AutoOpts is free software. ! 13: * AutoOpts is Copyright (c) 1992-2011 by Bruce Korb - all rights reserved ! 14: * ! 15: * AutoOpts is available under any one of two licenses. The license ! 16: * in use must be one of these two and the choice is under the control ! 17: * of the user of the license. ! 18: * ! 19: * The GNU Lesser General Public License, version 3 or later ! 20: * See the files "COPYING.lgplv3" and "COPYING.gplv3" ! 21: * ! 22: * The Modified Berkeley Software Distribution License ! 23: * See the file "COPYING.mbsd" ! 24: * ! 25: * These files have the following md5sums: ! 26: * ! 27: * 43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3 ! 28: * 06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3 ! 29: * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd ! 30: */ ! 31: ! 32: static char const zWarn[] = "%s WARNING: cannot save options - "; ! 33: static char const close_xml[] = "</%s>\n"; ! 34: ! 35: /* = = = START-STATIC-FORWARD = = = */ ! 36: static tCC* ! 37: findDirName(tOptions* pOpts, int* p_free); ! 38: ! 39: static char const * ! 40: findFileName(tOptions * pOpts, int * p_free_name); ! 41: ! 42: static void ! 43: printEntry( ! 44: FILE * fp, ! 45: tOptDesc * p, ! 46: tCC* pzLA ); ! 47: ! 48: static void ! 49: print_a_value(FILE * fp, int depth, tOptDesc * pOD, tOptionValue const * ovp); ! 50: ! 51: static void ! 52: print_a_string(FILE * fp, char const * name, char const * pz); ! 53: ! 54: static void ! 55: printValueList(FILE * fp, char const * name, tArgList * al); ! 56: ! 57: static void ! 58: printHierarchy(FILE * fp, tOptDesc * p); ! 59: ! 60: static FILE * ! 61: openSaveFile(tOptions* pOpts); ! 62: ! 63: static void ! 64: printNoArgOpt(FILE * fp, tOptDesc * p, tOptDesc * pOD); ! 65: ! 66: static void ! 67: printStringArg(FILE * fp, tOptDesc * pOD); ! 68: ! 69: static void ! 70: printEnumArg(FILE * fp, tOptDesc * pOD); ! 71: ! 72: static void ! 73: printSetMemberArg(FILE * fp, tOptDesc * pOD); ! 74: ! 75: static void ! 76: printFileArg(FILE * fp, tOptDesc * pOD, tOptions* pOpts); ! 77: /* = = = END-STATIC-FORWARD = = = */ ! 78: ! 79: static tCC* ! 80: findDirName(tOptions* pOpts, int* p_free) ! 81: { ! 82: tCC* pzDir; ! 83: ! 84: if ( (pOpts->specOptIdx.save_opts == NO_EQUIVALENT) ! 85: || (pOpts->specOptIdx.save_opts == 0)) ! 86: return NULL; ! 87: ! 88: pzDir = pOpts->pOptDesc[ pOpts->specOptIdx.save_opts ].optArg.argString; ! 89: if ((pzDir != NULL) && (*pzDir != NUL)) ! 90: return pzDir; ! 91: ! 92: /* ! 93: * This function only works if there is a directory where ! 94: * we can stash the RC (INI) file. ! 95: */ ! 96: { ! 97: tCC* const* papz = pOpts->papzHomeList; ! 98: if (papz == NULL) ! 99: return NULL; ! 100: ! 101: while (papz[1] != NULL) papz++; ! 102: pzDir = *papz; ! 103: } ! 104: ! 105: /* ! 106: * IF it does not require deciphering an env value, then just copy it ! 107: */ ! 108: if (*pzDir != '$') ! 109: return pzDir; ! 110: ! 111: { ! 112: tCC* pzEndDir = strchr(++pzDir, DIRCH); ! 113: char* pzFileName; ! 114: char* pzEnv; ! 115: ! 116: if (pzEndDir != NULL) { ! 117: char z[ AO_NAME_SIZE ]; ! 118: if ((pzEndDir - pzDir) > AO_NAME_LIMIT ) ! 119: return NULL; ! 120: memcpy(z, pzDir, (size_t)(pzEndDir - pzDir)); ! 121: z[pzEndDir - pzDir] = NUL; ! 122: pzEnv = getenv(z); ! 123: } else { ! 124: ! 125: /* ! 126: * Make sure we can get the env value (after stripping off ! 127: * any trailing directory or file names) ! 128: */ ! 129: pzEnv = getenv(pzDir); ! 130: } ! 131: ! 132: if (pzEnv == NULL) { ! 133: fprintf(stderr, zWarn, pOpts->pzProgName); ! 134: fprintf(stderr, zNotDef, pzDir); ! 135: return NULL; ! 136: } ! 137: ! 138: if (pzEndDir == NULL) ! 139: return pzEnv; ! 140: ! 141: { ! 142: size_t sz = strlen(pzEnv) + strlen(pzEndDir) + 2; ! 143: pzFileName = (char*)AGALOC(sz, "dir name"); ! 144: } ! 145: ! 146: if (pzFileName == NULL) ! 147: return NULL; ! 148: ! 149: *p_free = 1; ! 150: /* ! 151: * Glue together the full name into the allocated memory. ! 152: * FIXME: We lose track of this memory. ! 153: */ ! 154: sprintf(pzFileName, "%s/%s", pzEnv, pzEndDir); ! 155: return pzFileName; ! 156: } ! 157: } ! 158: ! 159: ! 160: static char const * ! 161: findFileName(tOptions * pOpts, int * p_free_name) ! 162: { ! 163: struct stat stBuf; ! 164: int free_dir_name = 0; ! 165: ! 166: char const * pzDir = findDirName(pOpts, &free_dir_name); ! 167: if (pzDir == NULL) ! 168: return NULL; ! 169: ! 170: /* ! 171: * See if we can find the specified directory. We use a once-only loop ! 172: * structure so we can bail out early. ! 173: */ ! 174: if (stat(pzDir, &stBuf) != 0) do { ! 175: char z[AG_PATH_MAX]; ! 176: char * dirchp; ! 177: ! 178: /* ! 179: * IF we could not, check to see if we got a full ! 180: * path to a file name that has not been created yet. ! 181: */ ! 182: if (errno != ENOENT) { ! 183: bogus_name: ! 184: fprintf(stderr, zWarn, pOpts->pzProgName); ! 185: fprintf(stderr, zNoStat, errno, strerror(errno), pzDir); ! 186: if (free_dir_name) ! 187: AGFREE((void*)pzDir); ! 188: return NULL; ! 189: } ! 190: ! 191: /* ! 192: * Strip off the last component, stat the remaining string and ! 193: * that string must name a directory ! 194: */ ! 195: dirchp = strrchr(pzDir, DIRCH); ! 196: if (dirchp == NULL) { ! 197: stBuf.st_mode = S_IFREG; ! 198: break; /* found directory -- viz., "." */ ! 199: } ! 200: ! 201: if ((dirchp - pzDir) >= sizeof(z)) ! 202: goto bogus_name; ! 203: ! 204: memcpy(z, pzDir, (size_t)(dirchp - pzDir)); ! 205: z[dirchp - pzDir] = NUL; ! 206: ! 207: if ((stat(z, &stBuf) != 0) || ! S_ISDIR(stBuf.st_mode)) ! 208: goto bogus_name; ! 209: stBuf.st_mode = S_IFREG; /* file within this directory */ ! 210: } while (0); ! 211: ! 212: /* ! 213: * IF what we found was a directory, ! 214: * THEN tack on the config file name ! 215: */ ! 216: if (S_ISDIR(stBuf.st_mode)) { ! 217: size_t sz = strlen(pzDir) + strlen(pOpts->pzRcName) + 2; ! 218: ! 219: { ! 220: char* pzPath = (char*)AGALOC(sz, "file name"); ! 221: #ifdef HAVE_SNPRINTF ! 222: snprintf(pzPath, sz, "%s/%s", pzDir, pOpts->pzRcName); ! 223: #else ! 224: sprintf(pzPath, "%s/%s", pzDir, pOpts->pzRcName); ! 225: #endif ! 226: if (free_dir_name) ! 227: AGFREE((void*)pzDir); ! 228: pzDir = pzPath; ! 229: free_dir_name = 1; ! 230: } ! 231: ! 232: /* ! 233: * IF we cannot stat the object for any reason other than ! 234: * it does not exist, then we bail out ! 235: */ ! 236: if (stat(pzDir, &stBuf) != 0) { ! 237: if (errno != ENOENT) { ! 238: fprintf(stderr, zWarn, pOpts->pzProgName); ! 239: fprintf(stderr, zNoStat, errno, strerror(errno), ! 240: pzDir); ! 241: AGFREE((void*)pzDir); ! 242: return NULL; ! 243: } ! 244: ! 245: /* ! 246: * It does not exist yet, but it will be a regular file ! 247: */ ! 248: stBuf.st_mode = S_IFREG; ! 249: } ! 250: } ! 251: ! 252: /* ! 253: * Make sure that whatever we ultimately found, that it either is ! 254: * or will soon be a file. ! 255: */ ! 256: if (! S_ISREG(stBuf.st_mode)) { ! 257: fprintf(stderr, zWarn, pOpts->pzProgName); ! 258: fprintf(stderr, zNotFile, pzDir); ! 259: if (free_dir_name) ! 260: AGFREE((void*)pzDir); ! 261: return NULL; ! 262: } ! 263: ! 264: /* ! 265: * Get rid of the old file ! 266: */ ! 267: unlink(pzDir); ! 268: *p_free_name = free_dir_name; ! 269: return pzDir; ! 270: } ! 271: ! 272: ! 273: static void ! 274: printEntry( ! 275: FILE * fp, ! 276: tOptDesc * p, ! 277: tCC* pzLA ) ! 278: { ! 279: /* ! 280: * There is an argument. Pad the name so values line up. ! 281: * Not disabled *OR* this got equivalenced to another opt, ! 282: * then use current option name. ! 283: * Otherwise, there must be a disablement name. ! 284: */ ! 285: { ! 286: char const * pz; ! 287: if (! DISABLED_OPT(p) || (p->optEquivIndex != NO_EQUIVALENT)) ! 288: pz = p->pz_Name; ! 289: else ! 290: pz = p->pz_DisableName; ! 291: ! 292: fprintf(fp, "%-18s", pz); ! 293: } ! 294: /* ! 295: * IF the option is numeric only, ! 296: * THEN the char pointer is really the number ! 297: */ ! 298: if (OPTST_GET_ARGTYPE(p->fOptState) == OPARG_TYPE_NUMERIC) ! 299: fprintf(fp, " %d\n", (int)(t_word)pzLA); ! 300: ! 301: /* ! 302: * OTHERWISE, FOR each line of the value text, ... ! 303: */ ! 304: else if (pzLA == NULL) ! 305: fputc('\n', fp); ! 306: ! 307: else { ! 308: fputc(' ', fp); fputc(' ', fp); ! 309: for (;;) { ! 310: tCC* pzNl = strchr(pzLA, '\n'); ! 311: ! 312: /* ! 313: * IF this is the last line ! 314: * THEN bail and print it ! 315: */ ! 316: if (pzNl == NULL) ! 317: break; ! 318: ! 319: /* ! 320: * Print the continuation and the text from the current line ! 321: */ ! 322: (void)fwrite(pzLA, (size_t)(pzNl - pzLA), (size_t)1, fp); ! 323: pzLA = pzNl+1; /* advance the Last Arg pointer */ ! 324: fputs("\\\n", fp); ! 325: } ! 326: ! 327: /* ! 328: * Terminate the entry ! 329: */ ! 330: fputs(pzLA, fp); ! 331: fputc('\n', fp); ! 332: } ! 333: } ! 334: ! 335: ! 336: static void ! 337: print_a_value(FILE * fp, int depth, tOptDesc * pOD, tOptionValue const * ovp) ! 338: { ! 339: static char const bool_atr[] = "<%1$s type=boolean>%2$s</%1$s>\n"; ! 340: static char const numb_atr[] = "<%1$s type=integer>0x%2$lX</%1$s>\n"; ! 341: static char const type_atr[] = "<%s type=%s>"; ! 342: static char const null_atr[] = "<%s/>\n"; ! 343: ! 344: while (--depth >= 0) ! 345: putc(' ', fp), putc(' ', fp); ! 346: ! 347: switch (ovp->valType) { ! 348: default: ! 349: case OPARG_TYPE_NONE: ! 350: fprintf(fp, null_atr, ovp->pzName); ! 351: break; ! 352: ! 353: case OPARG_TYPE_STRING: ! 354: print_a_string(fp, ovp->pzName, ovp->v.strVal); ! 355: break; ! 356: ! 357: case OPARG_TYPE_ENUMERATION: ! 358: case OPARG_TYPE_MEMBERSHIP: ! 359: if (pOD != NULL) { ! 360: tAoUI opt_state = pOD->fOptState; ! 361: uintptr_t val = pOD->optArg.argEnum; ! 362: char const * typ = (ovp->valType == OPARG_TYPE_ENUMERATION) ! 363: ? "keyword" : "set-membership"; ! 364: ! 365: fprintf(fp, type_atr, ovp->pzName, typ); ! 366: ! 367: /* ! 368: * This is a magic incantation that will convert the ! 369: * bit flag values back into a string suitable for printing. ! 370: */ ! 371: (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD ); ! 372: if (pOD->optArg.argString != NULL) { ! 373: fputs(pOD->optArg.argString, fp); ! 374: ! 375: if (ovp->valType != OPARG_TYPE_ENUMERATION) { ! 376: /* ! 377: * set membership strings get allocated ! 378: */ ! 379: AGFREE((void*)pOD->optArg.argString); ! 380: } ! 381: } ! 382: ! 383: pOD->optArg.argEnum = val; ! 384: pOD->fOptState = opt_state; ! 385: fprintf(fp, close_xml, ovp->pzName); ! 386: break; ! 387: } ! 388: /* FALLTHROUGH */ ! 389: ! 390: case OPARG_TYPE_NUMERIC: ! 391: fprintf(fp, numb_atr, ovp->pzName, ovp->v.longVal); ! 392: break; ! 393: ! 394: case OPARG_TYPE_BOOLEAN: ! 395: fprintf(fp, bool_atr, ovp->pzName, ! 396: ovp->v.boolVal ? "true" : "false"); ! 397: break; ! 398: ! 399: case OPARG_TYPE_HIERARCHY: ! 400: printValueList(fp, ovp->pzName, ovp->v.nestVal); ! 401: break; ! 402: } ! 403: } ! 404: ! 405: ! 406: static void ! 407: print_a_string(FILE * fp, char const * name, char const * pz) ! 408: { ! 409: static char const open_atr[] = "<%s>"; ! 410: ! 411: fprintf(fp, open_atr, name); ! 412: for (;;) { ! 413: int ch = ((int)*(pz++)) & 0xFF; ! 414: ! 415: switch (ch) { ! 416: case NUL: goto string_done; ! 417: ! 418: case '&': ! 419: case '<': ! 420: case '>': ! 421: #if __GNUC__ >= 4 ! 422: case 1 ... (' ' - 1): ! 423: case ('~' + 1) ... 0xFF: ! 424: #endif ! 425: emit_special_char(fp, ch); ! 426: break; ! 427: ! 428: default: ! 429: #if __GNUC__ < 4 ! 430: if ( ((ch >= 1) && (ch <= (' ' - 1))) ! 431: || ((ch >= ('~' + 1)) && (ch <= 0xFF)) ) { ! 432: emit_special_char(fp, ch); ! 433: break; ! 434: } ! 435: #endif ! 436: putc(ch, fp); ! 437: } ! 438: } string_done:; ! 439: fprintf(fp, close_xml, name); ! 440: } ! 441: ! 442: ! 443: static void ! 444: printValueList(FILE * fp, char const * name, tArgList * al) ! 445: { ! 446: static int depth = 1; ! 447: ! 448: int sp_ct; ! 449: int opt_ct; ! 450: void ** opt_list; ! 451: ! 452: if (al == NULL) ! 453: return; ! 454: opt_ct = al->useCt; ! 455: opt_list = (void **)al->apzArgs; ! 456: ! 457: if (opt_ct <= 0) { ! 458: fprintf(fp, "<%s/>\n", name); ! 459: return; ! 460: } ! 461: ! 462: fprintf(fp, "<%s type=nested>\n", name); ! 463: ! 464: depth++; ! 465: while (--opt_ct >= 0) { ! 466: tOptionValue const * ovp = *(opt_list++); ! 467: ! 468: print_a_value(fp, depth, NULL, ovp); ! 469: } ! 470: depth--; ! 471: ! 472: for (sp_ct = depth; --sp_ct >= 0;) ! 473: putc(' ', fp), putc(' ', fp); ! 474: fprintf(fp, "</%s>\n", name); ! 475: } ! 476: ! 477: ! 478: static void ! 479: printHierarchy(FILE * fp, tOptDesc * p) ! 480: { ! 481: int opt_ct; ! 482: tArgList * al = p->optCookie; ! 483: void ** opt_list; ! 484: ! 485: if (al == NULL) ! 486: return; ! 487: ! 488: opt_ct = al->useCt; ! 489: opt_list = (void **)al->apzArgs; ! 490: ! 491: if (opt_ct <= 0) ! 492: return; ! 493: ! 494: do { ! 495: tOptionValue const * base = *(opt_list++); ! 496: tOptionValue const * ovp = optionGetValue(base, NULL); ! 497: ! 498: if (ovp == NULL) ! 499: continue; ! 500: ! 501: fprintf(fp, "<%s type=nested>\n", p->pz_Name); ! 502: ! 503: do { ! 504: print_a_value(fp, 1, p, ovp); ! 505: ! 506: } while (ovp = optionNextValue(base, ovp), ! 507: ovp != NULL); ! 508: ! 509: fprintf(fp, "</%s>\n", p->pz_Name); ! 510: } while (--opt_ct > 0); ! 511: } ! 512: ! 513: ! 514: static FILE * ! 515: openSaveFile(tOptions* pOpts) ! 516: { ! 517: FILE* fp; ! 518: ! 519: { ! 520: int free_name = 0; ! 521: tCC* pzFName = findFileName(pOpts, &free_name); ! 522: if (pzFName == NULL) ! 523: return NULL; ! 524: ! 525: fp = fopen(pzFName, "w" FOPEN_BINARY_FLAG); ! 526: if (fp == NULL) { ! 527: fprintf(stderr, zWarn, pOpts->pzProgName); ! 528: fprintf(stderr, zNoCreat, errno, strerror(errno), pzFName); ! 529: if (free_name) ! 530: AGFREE((void*) pzFName ); ! 531: return fp; ! 532: } ! 533: ! 534: if (free_name) ! 535: AGFREE((void*)pzFName); ! 536: } ! 537: ! 538: { ! 539: char const* pz = pOpts->pzUsageTitle; ! 540: fputs("# ", fp); ! 541: do { fputc(*pz, fp); } while (*(pz++) != '\n'); ! 542: } ! 543: ! 544: { ! 545: time_t timeVal = time(NULL); ! 546: char* pzTime = ctime(&timeVal); ! 547: ! 548: fprintf(fp, zPresetFile, pzTime); ! 549: #ifdef HAVE_ALLOCATED_CTIME ! 550: /* ! 551: * The return values for ctime(), localtime(), and gmtime() ! 552: * normally point to static data that is overwritten by each call. ! 553: * The test to detect allocated ctime, so we leak the memory. ! 554: */ ! 555: AGFREE((void*)pzTime); ! 556: #endif ! 557: } ! 558: ! 559: return fp; ! 560: } ! 561: ! 562: static void ! 563: printNoArgOpt(FILE * fp, tOptDesc * p, tOptDesc * pOD) ! 564: { ! 565: /* ! 566: * The aliased to argument indicates whether or not the option ! 567: * is "disabled". However, the original option has the name ! 568: * string, so we get that there, not with "p". ! 569: */ ! 570: char const * pznm = ! 571: (DISABLED_OPT(p)) ? pOD->pz_DisableName : pOD->pz_Name; ! 572: /* ! 573: * If the option was disabled and the disablement name is NULL, ! 574: * then the disablement was caused by aliasing. ! 575: * Use the name as the string to emit. ! 576: */ ! 577: if (pznm == NULL) ! 578: pznm = pOD->pz_Name; ! 579: ! 580: fprintf(fp, "%s\n", pznm); ! 581: } ! 582: ! 583: static void ! 584: printStringArg(FILE * fp, tOptDesc * pOD) ! 585: { ! 586: if (pOD->fOptState & OPTST_STACKED) { ! 587: tArgList* pAL = (tArgList*)pOD->optCookie; ! 588: int uct = pAL->useCt; ! 589: tCC** ppz = pAL->apzArgs; ! 590: ! 591: /* ! 592: * un-disable multiple copies of disabled options. ! 593: */ ! 594: if (uct > 1) ! 595: pOD->fOptState &= ~OPTST_DISABLED; ! 596: ! 597: while (uct-- > 0) ! 598: printEntry(fp, pOD, *(ppz++)); ! 599: } else { ! 600: printEntry(fp, pOD, pOD->optArg.argString); ! 601: } ! 602: } ! 603: ! 604: static void ! 605: printEnumArg(FILE * fp, tOptDesc * pOD) ! 606: { ! 607: uintptr_t val = pOD->optArg.argEnum; ! 608: ! 609: /* ! 610: * This is a magic incantation that will convert the ! 611: * bit flag values back into a string suitable for printing. ! 612: */ ! 613: (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD); ! 614: printEntry(fp, pOD, (void*)(pOD->optArg.argString)); ! 615: ! 616: pOD->optArg.argEnum = val; ! 617: } ! 618: ! 619: static void ! 620: printSetMemberArg(FILE * fp, tOptDesc * pOD) ! 621: { ! 622: uintptr_t val = pOD->optArg.argEnum; ! 623: ! 624: /* ! 625: * This is a magic incantation that will convert the ! 626: * bit flag values back into a string suitable for printing. ! 627: */ ! 628: (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD); ! 629: printEntry(fp, pOD, (void*)(pOD->optArg.argString)); ! 630: ! 631: if (pOD->optArg.argString != NULL) { ! 632: /* ! 633: * set membership strings get allocated ! 634: */ ! 635: AGFREE((void*)pOD->optArg.argString); ! 636: pOD->fOptState &= ~OPTST_ALLOC_ARG; ! 637: } ! 638: ! 639: pOD->optArg.argEnum = val; ! 640: } ! 641: ! 642: static void ! 643: printFileArg(FILE * fp, tOptDesc * pOD, tOptions* pOpts) ! 644: { ! 645: /* ! 646: * If the cookie is not NULL, then it has the file name, period. ! 647: * Otherwise, if we have a non-NULL string argument, then.... ! 648: */ ! 649: if (pOD->optCookie != NULL) ! 650: printEntry(fp, pOD, pOD->optCookie); ! 651: ! 652: else if (HAS_originalOptArgArray(pOpts)) { ! 653: char const * orig = ! 654: pOpts->originalOptArgArray[pOD->optIndex].argString; ! 655: ! 656: if (pOD->optArg.argString == orig) ! 657: return; ! 658: ! 659: printEntry(fp, pOD, pOD->optArg.argString); ! 660: } ! 661: } ! 662: ! 663: ! 664: /*=export_func optionSaveFile ! 665: * ! 666: * what: saves the option state to a file ! 667: * ! 668: * arg: tOptions*, pOpts, program options descriptor ! 669: * ! 670: * doc: ! 671: * ! 672: * This routine will save the state of option processing to a file. The name ! 673: * of that file can be specified with the argument to the @code{--save-opts} ! 674: * option, or by appending the @code{rcfile} attribute to the last ! 675: * @code{homerc} attribute. If no @code{rcfile} attribute was specified, it ! 676: * will default to @code{.@i{programname}rc}. If you wish to specify another ! 677: * file, you should invoke the @code{SET_OPT_SAVE_OPTS(@i{filename})} macro. ! 678: * ! 679: * The recommend usage is as follows: ! 680: * @example ! 681: * optionProcess(&progOptions, argc, argv); ! 682: * if (i_want_a_non_standard_place_for_this) ! 683: * SET_OPT_SAVE_OPTS("myfilename"); ! 684: * optionSaveFile(&progOptions); ! 685: * @end example ! 686: * ! 687: * err: ! 688: * ! 689: * If no @code{homerc} file was specified, this routine will silently return ! 690: * and do nothing. If the output file cannot be created or updated, a message ! 691: * will be printed to @code{stderr} and the routine will return. ! 692: =*/ ! 693: void ! 694: optionSaveFile(tOptions* pOpts) ! 695: { ! 696: tOptDesc* pOD; ! 697: int ct; ! 698: FILE* fp = openSaveFile(pOpts); ! 699: ! 700: if (fp == NULL) ! 701: return; ! 702: ! 703: /* ! 704: * FOR each of the defined options, ... ! 705: */ ! 706: ct = pOpts->presetOptCt; ! 707: pOD = pOpts->pOptDesc; ! 708: do { ! 709: tOptDesc* p; ! 710: ! 711: /* ! 712: * IF the option has not been defined ! 713: * OR it does not take an initialization value ! 714: * OR it is equivalenced to another option ! 715: * THEN continue (ignore it) ! 716: * ! 717: * Equivalenced options get picked up when the equivalenced-to ! 718: * option is processed. ! 719: */ ! 720: if (UNUSED_OPT(pOD)) ! 721: continue; ! 722: ! 723: if ((pOD->fOptState & OPTST_DO_NOT_SAVE_MASK) != 0) ! 724: continue; ! 725: ! 726: if ( (pOD->optEquivIndex != NO_EQUIVALENT) ! 727: && (pOD->optEquivIndex != pOD->optIndex)) ! 728: continue; ! 729: ! 730: /* ! 731: * The option argument data are found at the equivalenced-to option, ! 732: * but the actual option argument type comes from the original ! 733: * option descriptor. Be careful! ! 734: */ ! 735: p = ((pOD->fOptState & OPTST_EQUIVALENCE) != 0) ! 736: ? (pOpts->pOptDesc + pOD->optActualIndex) : pOD; ! 737: ! 738: switch (OPTST_GET_ARGTYPE(pOD->fOptState)) { ! 739: case OPARG_TYPE_NONE: ! 740: printNoArgOpt(fp, p, pOD); ! 741: break; ! 742: ! 743: case OPARG_TYPE_NUMERIC: ! 744: printEntry(fp, p, (void*)(p->optArg.argInt)); ! 745: break; ! 746: ! 747: case OPARG_TYPE_STRING: ! 748: printStringArg(fp, p); ! 749: break; ! 750: ! 751: case OPARG_TYPE_ENUMERATION: ! 752: printEnumArg(fp, p); ! 753: break; ! 754: ! 755: case OPARG_TYPE_MEMBERSHIP: ! 756: printSetMemberArg(fp, p); ! 757: break; ! 758: ! 759: case OPARG_TYPE_BOOLEAN: ! 760: printEntry(fp, p, p->optArg.argBool ? "true" : "false"); ! 761: break; ! 762: ! 763: case OPARG_TYPE_HIERARCHY: ! 764: printHierarchy(fp, p); ! 765: break; ! 766: ! 767: case OPARG_TYPE_FILE: ! 768: printFileArg(fp, p, pOpts); ! 769: break; ! 770: ! 771: default: ! 772: break; /* cannot handle - skip it */ ! 773: } ! 774: } while (pOD++, (--ct > 0)); ! 775: ! 776: fclose(fp); ! 777: } ! 778: /* ! 779: * Local Variables: ! 780: * mode: C ! 781: * c-file-style: "stroustrup" ! 782: * indent-tabs-mode: nil ! 783: * End: ! 784: * end of autoopts/save.c */