save.c revision 294569
1275970Scy 2275970Scy/* 3275970Scy * \file save.c 4275970Scy * 5275970Scy * This module's routines will take the currently set options and 6275970Scy * store them into an ".rc" file for re-interpretation the next 7275970Scy * time the invoking program is run. 8275970Scy * 9275970Scy * @addtogroup autoopts 10275970Scy * @{ 11275970Scy */ 12275970Scy/* 13275970Scy * This file is part of AutoOpts, a companion to AutoGen. 14275970Scy * AutoOpts is free software. 15275970Scy * AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved 16275970Scy * 17275970Scy * AutoOpts is available under any one of two licenses. The license 18275970Scy * in use must be one of these two and the choice is under the control 19275970Scy * of the user of the license. 20275970Scy * 21275970Scy * The GNU Lesser General Public License, version 3 or later 22275970Scy * See the files "COPYING.lgplv3" and "COPYING.gplv3" 23275970Scy * 24275970Scy * The Modified Berkeley Software Distribution License 25275970Scy * See the file "COPYING.mbsd" 26275970Scy * 27275970Scy * These files have the following sha256 sums: 28275970Scy * 29275970Scy * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3 30275970Scy * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3 31275970Scy * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd 32275970Scy */ 33275970Scy 34275970Scy/* = = = START-STATIC-FORWARD = = = */ 35275970Scystatic char const * 36275970Scyfind_dir_name(tOptions * opts, int * p_free); 37275970Scy 38275970Scystatic char const * 39275970Scyfind_file_name(tOptions * opts, int * p_free_name); 40275970Scy 41275970Scystatic void 42275970Scyprt_entry(FILE * fp, tOptDesc * od, char const * l_arg); 43275970Scy 44275970Scystatic void 45275970Scyprt_value(FILE * fp, int depth, tOptDesc * pOD, tOptionValue const * ovp); 46275970Scy 47275970Scystatic void 48275970Scyprt_string(FILE * fp, char const * name, char const * pz); 49275970Scy 50275970Scystatic void 51275970Scyprt_val_list(FILE * fp, char const * name, tArgList * al); 52275970Scy 53275970Scystatic void 54275970Scyprt_nested(FILE * fp, tOptDesc * p); 55275970Scy 56275970Scystatic FILE * 57275970Scyopen_sv_file(tOptions * opts); 58275970Scy 59275970Scystatic void 60275970Scyprt_no_arg_opt(FILE * fp, tOptDesc * p, tOptDesc * pOD); 61275970Scy 62275970Scystatic void 63275970Scyprt_str_arg(FILE * fp, tOptDesc * pOD); 64275970Scy 65275970Scystatic void 66275970Scyprt_enum_arg(FILE * fp, tOptDesc * od); 67275970Scy 68275970Scystatic void 69275970Scyprt_set_arg(FILE * fp, tOptDesc * od); 70275970Scy 71275970Scystatic void 72275970Scyprt_file_arg(FILE * fp, tOptDesc * od, tOptions * opts); 73275970Scy/* = = = END-STATIC-FORWARD = = = */ 74275970Scy 75275970Scy/** 76275970Scy */ 77275970Scystatic char const * 78275970Scyfind_dir_name(tOptions * opts, int * p_free) 79275970Scy{ 80275970Scy char const * pzDir; 81275970Scy 82275970Scy if ( (opts->specOptIdx.save_opts == NO_EQUIVALENT) 83275970Scy || (opts->specOptIdx.save_opts == 0)) 84275970Scy return NULL; 85275970Scy 86275970Scy pzDir = opts->pOptDesc[ opts->specOptIdx.save_opts ].optArg.argString; 87275970Scy if ((pzDir != NULL) && (*pzDir != NUL)) 88275970Scy return pzDir; 89275970Scy 90275970Scy /* 91275970Scy * This function only works if there is a directory where 92285612Sdelphij * we can stash the RC (INI) file. 93285612Sdelphij */ 94275970Scy { 95275970Scy char const * const * papz = opts->papzHomeList; 96275970Scy if (papz == NULL) 97275970Scy return NULL; 98285612Sdelphij 99275970Scy while (papz[1] != NULL) papz++; 100275970Scy pzDir = *papz; 101275970Scy } 102275970Scy 103275970Scy /* 104275970Scy * IF it does not require deciphering an env value, then just copy it 105275970Scy */ 106275970Scy if (*pzDir != '$') 107275970Scy return pzDir; 108275970Scy 109275970Scy { 110275970Scy char const * pzEndDir = strchr(++pzDir, DIRCH); 111275970Scy char * pzFileName; 112285612Sdelphij char * pzEnv; 113285612Sdelphij 114285612Sdelphij if (pzEndDir != NULL) { 115285612Sdelphij char z[ AO_NAME_SIZE ]; 116285612Sdelphij if ((pzEndDir - pzDir) > AO_NAME_LIMIT ) 117285612Sdelphij return NULL; 118285612Sdelphij memcpy(z, pzDir, (size_t)(pzEndDir - pzDir)); 119285612Sdelphij z[pzEndDir - pzDir] = NUL; 120285612Sdelphij pzEnv = getenv(z); 121285612Sdelphij } else { 122285612Sdelphij 123275970Scy /* 124275970Scy * Make sure we can get the env value (after stripping off 125275970Scy * any trailing directory or file names) 126275970Scy */ 127275970Scy pzEnv = getenv(pzDir); 128275970Scy } 129275970Scy 130275970Scy if (pzEnv == NULL) { 131275970Scy fprintf(stderr, zsave_warn, opts->pzProgName); 132275970Scy fprintf(stderr, zNotDef, pzDir); 133275970Scy return NULL; 134275970Scy } 135275970Scy 136275970Scy if (pzEndDir == NULL) 137275970Scy return pzEnv; 138275970Scy 139275970Scy { 140275970Scy size_t sz = strlen(pzEnv) + strlen(pzEndDir) + 2; 141275970Scy pzFileName = (char *)AGALOC(sz, "dir name"); 142275970Scy } 143275970Scy 144275970Scy if (pzFileName == NULL) 145275970Scy return NULL; 146275970Scy 147275970Scy *p_free = 1; 148275970Scy /* 149275970Scy * Glue together the full name into the allocated memory. 150275970Scy * FIXME: We lose track of this memory. 151275970Scy */ 152275970Scy sprintf(pzFileName, "%s/%s", pzEnv, pzEndDir); 153275970Scy return pzFileName; 154275970Scy } 155275970Scy} 156275970Scy 157275970Scy/** 158275970Scy */ 159275970Scystatic char const * 160275970Scyfind_file_name(tOptions * opts, int * p_free_name) 161275970Scy{ 162275970Scy struct stat stBuf; 163275970Scy int free_dir_name = 0; 164275970Scy 165275970Scy char const * pzDir = find_dir_name(opts, &free_dir_name); 166275970Scy if (pzDir == NULL) 167275970Scy return NULL; 168275970Scy 169275970Scy /* 170275970Scy * See if we can find the specified directory. We use a once-only loop 171275970Scy * structure so we can bail out early. 172275970Scy */ 173275970Scy if (stat(pzDir, &stBuf) != 0) do { 174275970Scy char z[AG_PATH_MAX]; 175275970Scy char * dirchp; 176275970Scy 177275970Scy /* 178275970Scy * IF we could not, check to see if we got a full 179275970Scy * path to a file name that has not been created yet. 180275970Scy */ 181275970Scy if (errno != ENOENT) { 182275970Scy bogus_name: 183275970Scy fprintf(stderr, zsave_warn, opts->pzProgName); 184275970Scy fprintf(stderr, zNoStat, errno, strerror(errno), pzDir); 185275970Scy if (free_dir_name) 186275970Scy AGFREE(pzDir); 187275970Scy return NULL; 188275970Scy } 189275970Scy 190275970Scy /* 191275970Scy * Strip off the last component, stat the remaining string and 192285612Sdelphij * that string must name a directory 193285612Sdelphij */ 194285612Sdelphij dirchp = strrchr(pzDir, DIRCH); 195285612Sdelphij if (dirchp == NULL) { 196285612Sdelphij stBuf.st_mode = S_IFREG; 197275970Scy break; /* found directory -- viz., "." */ 198285612Sdelphij } 199275970Scy 200285612Sdelphij if ((size_t)(dirchp - pzDir) >= sizeof(z)) 201275970Scy goto bogus_name; 202275970Scy 203275970Scy memcpy(z, pzDir, (size_t)(dirchp - pzDir)); 204275970Scy z[dirchp - pzDir] = NUL; 205275970Scy 206275970Scy if ((stat(z, &stBuf) != 0) || ! S_ISDIR(stBuf.st_mode)) 207275970Scy goto bogus_name; 208275970Scy stBuf.st_mode = S_IFREG; /* file within this directory */ 209275970Scy } while (false); 210275970Scy 211275970Scy /* 212275970Scy * IF what we found was a directory, 213275970Scy * THEN tack on the config file name 214275970Scy */ 215275970Scy if (S_ISDIR(stBuf.st_mode)) { 216275970Scy size_t sz = strlen(pzDir) + strlen(opts->pzRcName) + 2; 217285612Sdelphij 218275970Scy { 219275970Scy char * pzPath = (char *)AGALOC(sz, "file name"); 220275970Scy#ifdef HAVE_SNPRINTF 221275970Scy snprintf(pzPath, sz, "%s/%s", pzDir, opts->pzRcName); 222275970Scy#else 223275970Scy sprintf(pzPath, "%s/%s", pzDir, opts->pzRcName); 224275970Scy#endif 225275970Scy if (free_dir_name) 226275970Scy AGFREE(pzDir); 227275970Scy pzDir = pzPath; 228275970Scy free_dir_name = 1; 229275970Scy } 230275970Scy 231275970Scy /* 232275970Scy * IF we cannot stat the object for any reason other than 233275970Scy * it does not exist, then we bail out 234275970Scy */ 235275970Scy if (stat(pzDir, &stBuf) != 0) { 236275970Scy if (errno != ENOENT) { 237275970Scy fprintf(stderr, zsave_warn, opts->pzProgName); 238275970Scy fprintf(stderr, zNoStat, errno, strerror(errno), 239275970Scy pzDir); 240275970Scy AGFREE(pzDir); 241275970Scy return NULL; 242275970Scy } 243275970Scy 244275970Scy /* 245275970Scy * It does not exist yet, but it will be a regular file 246275970Scy */ 247275970Scy stBuf.st_mode = S_IFREG; 248275970Scy } 249275970Scy } 250275970Scy 251275970Scy /* 252275970Scy * Make sure that whatever we ultimately found, that it either is 253275970Scy * or will soon be a file. 254275970Scy */ 255275970Scy if (! S_ISREG(stBuf.st_mode)) { 256275970Scy fprintf(stderr, zsave_warn, opts->pzProgName, pzDir); 257275970Scy if (free_dir_name) 258275970Scy AGFREE(pzDir); 259275970Scy return NULL; 260275970Scy } 261275970Scy 262275970Scy /* 263275970Scy * Get rid of the old file 264275970Scy */ 265275970Scy unlink(pzDir); 266275970Scy *p_free_name = free_dir_name; 267275970Scy return pzDir; 268275970Scy} 269275970Scy 270275970Scy/** 271275970Scy * print one option entry to the save file. 272275970Scy * 273275970Scy * @param[in] fp the file pointer for the save file 274275970Scy * @param[in] od the option descriptor to print 275275970Scy * @param[in] l_arg the last argument for the option 276275970Scy */ 277275970Scystatic void 278275970Scyprt_entry(FILE * fp, tOptDesc * od, char const * l_arg) 279275970Scy{ 280275970Scy int space_ct; 281275970Scy 282275970Scy /* 283275970Scy * There is an argument. Pad the name so values line up. 284275970Scy * Not disabled *OR* this got equivalenced to another opt, 285275970Scy * then use current option name. 286275970Scy * Otherwise, there must be a disablement name. 287275970Scy */ 288275970Scy { 289275970Scy char const * pz = 290275970Scy (! DISABLED_OPT(od) || (od->optEquivIndex != NO_EQUIVALENT)) 291275970Scy ? od->pz_Name 292275970Scy : od->pz_DisableName; 293275970Scy space_ct = 17 - strlen(pz); 294275970Scy fputs(pz, fp); 295275970Scy } 296275970Scy 297275970Scy if ( (l_arg == NULL) 298275970Scy && (OPTST_GET_ARGTYPE(od->fOptState) != OPARG_TYPE_NUMERIC)) 299275970Scy goto end_entry; 300275970Scy 301275970Scy fputs(" = ", fp); 302275970Scy while (space_ct-- > 0) fputc(' ', fp); 303275970Scy 304275970Scy /* 305275970Scy * IF the option is numeric only, 306275970Scy * THEN the char pointer is really the number 307285612Sdelphij */ 308275970Scy if (OPTST_GET_ARGTYPE(od->fOptState) == OPARG_TYPE_NUMERIC) 309275970Scy fprintf(fp, "%d", (int)(intptr_t)l_arg); 310275970Scy 311275970Scy else { 312275970Scy for (;;) { 313275970Scy char const * eol = strchr(l_arg, NL); 314275970Scy 315285612Sdelphij /* 316285612Sdelphij * IF this is the last line 317285612Sdelphij * THEN bail and print it 318285612Sdelphij */ 319285612Sdelphij if (eol == NULL) 320285612Sdelphij break; 321285612Sdelphij 322275970Scy /* 323285612Sdelphij * Print the continuation and the text from the current line 324285612Sdelphij */ 325285612Sdelphij (void)fwrite(l_arg, (size_t)(eol - l_arg), (size_t)1, fp); 326285612Sdelphij l_arg = eol+1; /* advance the Last Arg pointer */ 327275970Scy fputs("\\\n", fp); 328285612Sdelphij } 329285612Sdelphij 330285612Sdelphij /* 331285612Sdelphij * Terminate the entry 332285612Sdelphij */ 333285612Sdelphij fputs(l_arg, fp); 334285612Sdelphij } 335285612Sdelphij 336285612Sdelphijend_entry: 337285612Sdelphij fputc(NL, fp); 338285612Sdelphij} 339285612Sdelphij 340285612Sdelphij/** 341275970Scy */ 342275970Scystatic void 343275970Scyprt_value(FILE * fp, int depth, tOptDesc * pOD, tOptionValue const * ovp) 344275970Scy{ 345285612Sdelphij while (--depth >= 0) 346285612Sdelphij putc(' ', fp), putc(' ', fp); 347275970Scy 348275970Scy switch (ovp->valType) { 349275970Scy default: 350275970Scy case OPARG_TYPE_NONE: 351275970Scy fprintf(fp, NULL_ATR_FMT, ovp->pzName); 352285612Sdelphij break; 353275970Scy 354275970Scy case OPARG_TYPE_STRING: 355275970Scy prt_string(fp, ovp->pzName, ovp->v.strVal); 356275970Scy break; 357275970Scy 358275970Scy case OPARG_TYPE_ENUMERATION: 359275970Scy case OPARG_TYPE_MEMBERSHIP: 360275970Scy if (pOD != NULL) { 361275970Scy uint32_t opt_state = pOD->fOptState; 362275970Scy uintptr_t val = pOD->optArg.argEnum; 363285612Sdelphij char const * typ = (ovp->valType == OPARG_TYPE_ENUMERATION) 364275970Scy ? "keyword" : "set-membership"; 365275970Scy 366275970Scy fprintf(fp, TYPE_ATR_FMT, ovp->pzName, typ); 367275970Scy 368275970Scy /* 369275970Scy * This is a magic incantation that will convert the 370275970Scy * bit flag values back into a string suitable for printing. 371275970Scy */ 372275970Scy (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD ); 373275970Scy if (pOD->optArg.argString != NULL) { 374275970Scy fputs(pOD->optArg.argString, fp); 375285612Sdelphij 376285612Sdelphij if (ovp->valType != OPARG_TYPE_ENUMERATION) { 377285612Sdelphij /* 378285612Sdelphij * set membership strings get allocated 379285612Sdelphij */ 380285612Sdelphij AGFREE(pOD->optArg.argString); 381285612Sdelphij } 382285612Sdelphij } 383285612Sdelphij 384285612Sdelphij pOD->optArg.argEnum = val; 385285612Sdelphij pOD->fOptState = opt_state; 386285612Sdelphij fprintf(fp, END_XML_FMT, ovp->pzName); 387285612Sdelphij break; 388285612Sdelphij } 389285612Sdelphij /* FALLTHROUGH */ 390285612Sdelphij 391275970Scy case OPARG_TYPE_NUMERIC: 392275970Scy fprintf(fp, NUMB_ATR_FMT, ovp->pzName, ovp->v.longVal); 393275970Scy break; 394275970Scy 395275970Scy case OPARG_TYPE_BOOLEAN: 396275970Scy fprintf(fp, BOOL_ATR_FMT, ovp->pzName, 397275970Scy ovp->v.boolVal ? "true" : "false"); 398275970Scy break; 399275970Scy 400275970Scy case OPARG_TYPE_HIERARCHY: 401285612Sdelphij prt_val_list(fp, ovp->pzName, ovp->v.nestVal); 402275970Scy break; 403275970Scy } 404275970Scy} 405285612Sdelphij 406275970Scy/** 407275970Scy */ 408275970Scystatic void 409275970Scyprt_string(FILE * fp, char const * name, char const * pz) 410275970Scy{ 411275970Scy fprintf(fp, OPEN_XML_FMT, name); 412275970Scy for (;;) { 413275970Scy int ch = ((int)*(pz++)) & 0xFF; 414275970Scy 415275970Scy switch (ch) { 416275970Scy case NUL: goto string_done; 417275970Scy 418275970Scy case '&': 419275970Scy case '<': 420275970Scy case '>': 421275970Scy#if __GNUC__ >= 4 422275970Scy case 1 ... (' ' - 1): 423275970Scy case ('~' + 1) ... 0xFF: 424275970Scy#endif 425275970Scy emit_special_char(fp, ch); 426275970Scy break; 427275970Scy 428275970Scy default: 429275970Scy#if __GNUC__ < 4 430275970Scy if ( ((ch >= 1) && (ch <= (' ' - 1))) 431275970Scy || ((ch >= ('~' + 1)) && (ch <= 0xFF)) ) { 432275970Scy emit_special_char(fp, ch); 433275970Scy break; 434275970Scy } 435275970Scy#endif 436275970Scy putc(ch, fp); 437275970Scy } 438275970Scy } string_done:; 439275970Scy fprintf(fp, END_XML_FMT, name); 440275970Scy} 441285612Sdelphij 442275970Scy/** 443275970Scy */ 444275970Scystatic void 445275970Scyprt_val_list(FILE * fp, char const * name, tArgList * al) 446275970Scy{ 447275970Scy static int depth = 1; 448275970Scy 449275970Scy int sp_ct; 450275970Scy int opt_ct; 451275970Scy void ** opt_list; 452275970Scy 453275970Scy if (al == NULL) 454275970Scy return; 455275970Scy opt_ct = al->useCt; 456275970Scy opt_list = VOIDP(al->apzArgs); 457275970Scy 458275970Scy if (opt_ct <= 0) { 459275970Scy fprintf(fp, OPEN_CLOSE_FMT, name); 460275970Scy return; 461275970Scy } 462275970Scy 463275970Scy fprintf(fp, NESTED_OPT_FMT, name); 464275970Scy 465275970Scy depth++; 466275970Scy while (--opt_ct >= 0) { 467275970Scy tOptionValue const * ovp = *(opt_list++); 468275970Scy 469275970Scy prt_value(fp, depth, NULL, ovp); 470275970Scy } 471275970Scy depth--; 472275970Scy 473275970Scy for (sp_ct = depth; --sp_ct >= 0;) 474275970Scy putc(' ', fp), putc(' ', fp); 475275970Scy fprintf(fp, "</%s>\n", name); 476275970Scy} 477275970Scy 478275970Scy/** 479275970Scy */ 480275970Scystatic void 481275970Scyprt_nested(FILE * fp, tOptDesc * p) 482275970Scy{ 483275970Scy int opt_ct; 484275970Scy tArgList * al = p->optCookie; 485275970Scy void ** opt_list; 486275970Scy 487275970Scy if (al == NULL) 488275970Scy return; 489275970Scy 490275970Scy opt_ct = al->useCt; 491285612Sdelphij opt_list = VOIDP(al->apzArgs); 492275970Scy 493275970Scy if (opt_ct <= 0) 494275970Scy return; 495275970Scy 496275970Scy do { 497275970Scy tOptionValue const * base = *(opt_list++); 498275970Scy tOptionValue const * ovp = optionGetValue(base, NULL); 499275970Scy 500275970Scy if (ovp == NULL) 501275970Scy continue; 502275970Scy 503275970Scy fprintf(fp, NESTED_OPT_FMT, p->pz_Name); 504275970Scy 505275970Scy do { 506275970Scy prt_value(fp, 1, p, ovp); 507275970Scy 508275970Scy } while (ovp = optionNextValue(base, ovp), 509275970Scy ovp != NULL); 510285612Sdelphij 511275970Scy fprintf(fp, "</%s>\n", p->pz_Name); 512275970Scy } while (--opt_ct > 0); 513275970Scy} 514275970Scy 515275970Scy/** 516275970Scy * open the file for saving option state. 517275970Scy * 518275970Scy * @param[in] opts the program options structure 519275970Scy * @returns the open file pointer. It may be NULL. 520275970Scy */ 521275970Scystatic FILE * 522275970Scyopen_sv_file(tOptions * opts) 523275970Scy{ 524275970Scy FILE * fp; 525275970Scy 526275970Scy { 527275970Scy int free_name = 0; 528275970Scy char const * pzFName = find_file_name(opts, &free_name); 529275970Scy if (pzFName == NULL) 530275970Scy return NULL; 531275970Scy 532275970Scy fp = fopen(pzFName, "w" FOPEN_BINARY_FLAG); 533275970Scy if (fp == NULL) { 534275970Scy fprintf(stderr, zsave_warn, opts->pzProgName); 535275970Scy fprintf(stderr, zNoCreat, errno, strerror(errno), pzFName); 536275970Scy if (free_name) 537275970Scy AGFREE(pzFName); 538275970Scy return fp; 539275970Scy } 540275970Scy 541275970Scy if (free_name) 542275970Scy AGFREE(pzFName); 543275970Scy } 544275970Scy 545275970Scy fputs("# ", fp); 546275970Scy { 547275970Scy char const * e = strchr(opts->pzUsageTitle, NL); 548275970Scy if (e++ != NULL) 549275970Scy fwrite(opts->pzUsageTitle, 1, e - opts->pzUsageTitle, fp); 550275970Scy } 551275970Scy 552275970Scy { 553275970Scy time_t cur_time = time(NULL); 554275970Scy char * time_str = ctime(&cur_time); 555275970Scy 556275970Scy fprintf(fp, zPresetFile, time_str); 557275970Scy#ifdef HAVE_ALLOCATED_CTIME 558275970Scy /* 559275970Scy * The return values for ctime(), localtime(), and gmtime() 560275970Scy * normally point to static data that is overwritten by each call. 561275970Scy * The test to detect allocated ctime, so we leak the memory. 562275970Scy */ 563275970Scy AGFREE(time_str); 564275970Scy#endif 565275970Scy } 566275970Scy 567275970Scy return fp; 568275970Scy} 569275970Scy 570275970Scy/** 571275970Scy */ 572275970Scystatic void 573275970Scyprt_no_arg_opt(FILE * fp, tOptDesc * p, tOptDesc * pOD) 574275970Scy{ 575275970Scy /* 576275970Scy * The aliased to argument indicates whether or not the option 577275970Scy * is "disabled". However, the original option has the name 578275970Scy * string, so we get that there, not with "p". 579275970Scy */ 580275970Scy char const * pznm = 581275970Scy (DISABLED_OPT(p)) ? pOD->pz_DisableName : pOD->pz_Name; 582275970Scy /* 583275970Scy * If the option was disabled and the disablement name is NULL, 584275970Scy * then the disablement was caused by aliasing. 585275970Scy * Use the name as the string to emit. 586275970Scy */ 587275970Scy if (pznm == NULL) 588275970Scy pznm = pOD->pz_Name; 589275970Scy 590275970Scy fprintf(fp, "%s\n", pznm); 591275970Scy} 592275970Scy 593275970Scy/** 594275970Scy */ 595275970Scystatic void 596275970Scyprt_str_arg(FILE * fp, tOptDesc * pOD) 597275970Scy{ 598275970Scy if (pOD->fOptState & OPTST_STACKED) { 599275970Scy tArgList * pAL = (tArgList *)pOD->optCookie; 600285612Sdelphij int uct = pAL->useCt; 601275970Scy char const ** ppz = pAL->apzArgs; 602275970Scy 603275970Scy /* 604275970Scy * un-disable multiple copies of disabled options. 605275970Scy */ 606275970Scy if (uct > 1) 607275970Scy pOD->fOptState &= ~OPTST_DISABLED; 608275970Scy 609275970Scy while (uct-- > 0) 610275970Scy prt_entry(fp, pOD, *(ppz++)); 611275970Scy } else { 612275970Scy prt_entry(fp, pOD, pOD->optArg.argString); 613275970Scy } 614275970Scy} 615275970Scy 616285612Sdelphij/** 617275970Scy * print the string value of an enumeration. 618275970Scy * 619275970Scy * @param[in] fp the file pointer to write to 620275970Scy * @param[in] od the option descriptor with the enumerated value 621275970Scy */ 622275970Scystatic void 623275970Scyprt_enum_arg(FILE * fp, tOptDesc * od) 624275970Scy{ 625275970Scy uintptr_t val = od->optArg.argEnum; 626275970Scy 627275970Scy /* 628275970Scy * This is a magic incantation that will convert the 629275970Scy * bit flag values back into a string suitable for printing. 630275970Scy */ 631275970Scy (*(od->pOptProc))(OPTPROC_RETURN_VALNAME, od); 632275970Scy prt_entry(fp, od, VOIDP(od->optArg.argString)); 633285612Sdelphij 634275970Scy od->optArg.argEnum = val; 635275970Scy} 636275970Scy 637275970Scy/** 638275970Scy * Print the bits set in a bit mask option. 639275970Scy * We call the option handling function with a magic value for 640275970Scy * the options pointer and it allocates and fills in the string. 641275970Scy * We print that with a call to prt_entry(). 642275970Scy * 643275970Scy * @param[in] fp the file pointer to write to 644275970Scy * @param[in] od the option descriptor with a bit mask value type 645275970Scy */ 646275970Scystatic void 647275970Scyprt_set_arg(FILE * fp, tOptDesc * od) 648275970Scy{ 649275970Scy char * list = optionMemberList(od); 650275970Scy size_t len = strlen(list); 651285612Sdelphij char * buf = (char *)AGALOC(len + 3, "dir name"); 652285612Sdelphij *buf= '='; 653285612Sdelphij memcpy(buf+1, list, len + 1); 654285612Sdelphij prt_entry(fp, od, buf); 655285612Sdelphij AGFREE(buf); 656285612Sdelphij AGFREE(list); 657285612Sdelphij} 658285612Sdelphij 659285612Sdelphij/** 660285612Sdelphij * figure out what the option file name argument is. 661285612Sdelphij * If one can be found, call prt_entry() to emit it. 662285612Sdelphij * 663285612Sdelphij * @param[in] fp the file pointer to write to. 664285612Sdelphij * @param[in] od the option descriptor with a bit mask value type 665285612Sdelphij * @param[in] opts the program options descriptor 666285612Sdelphij */ 667285612Sdelphijstatic void 668285612Sdelphijprt_file_arg(FILE * fp, tOptDesc * od, tOptions * opts) 669285612Sdelphij{ 670285612Sdelphij /* 671285612Sdelphij * If the cookie is not NULL, then it has the file name, period. 672285612Sdelphij * Otherwise, if we have a non-NULL string argument, then.... 673285612Sdelphij */ 674285612Sdelphij if (od->optCookie != NULL) 675285612Sdelphij prt_entry(fp, od, od->optCookie); 676285612Sdelphij 677285612Sdelphij else if (HAS_originalOptArgArray(opts)) { 678285612Sdelphij char const * orig = 679285612Sdelphij opts->originalOptArgArray[od->optIndex].argString; 680285612Sdelphij 681285612Sdelphij if (od->optArg.argString == orig) 682285612Sdelphij return; 683285612Sdelphij 684285612Sdelphij prt_entry(fp, od, od->optArg.argString); 685285612Sdelphij } 686285612Sdelphij} 687285612Sdelphij 688285612Sdelphij/*=export_func optionSaveFile 689285612Sdelphij * 690285612Sdelphij * what: saves the option state to a file 691285612Sdelphij * 692285612Sdelphij * arg: tOptions *, opts, program options descriptor 693285612Sdelphij * 694285612Sdelphij * doc: 695285612Sdelphij * 696285612Sdelphij * This routine will save the state of option processing to a file. The name 697285612Sdelphij * of that file can be specified with the argument to the @code{--save-opts} 698285612Sdelphij * option, or by appending the @code{rcfile} attribute to the last 699285612Sdelphij * @code{homerc} attribute. If no @code{rcfile} attribute was specified, it 700285612Sdelphij * will default to @code{.@i{programname}rc}. If you wish to specify another 701285612Sdelphij * file, you should invoke the @code{SET_OPT_SAVE_OPTS(@i{filename})} macro. 702285612Sdelphij * 703285612Sdelphij * The recommend usage is as follows: 704285612Sdelphij * @example 705285612Sdelphij * optionProcess(&progOptions, argc, argv); 706285612Sdelphij * if (i_want_a_non_standard_place_for_this) 707285612Sdelphij * SET_OPT_SAVE_OPTS("myfilename"); 708285612Sdelphij * optionSaveFile(&progOptions); 709285612Sdelphij * @end example 710285612Sdelphij * 711285612Sdelphij * err: 712285612Sdelphij * 713285612Sdelphij * If no @code{homerc} file was specified, this routine will silently return 714285612Sdelphij * and do nothing. If the output file cannot be created or updated, a message 715285612Sdelphij * will be printed to @code{stderr} and the routine will return. 716275970Scy=*/ 717275970Scyvoid 718275970ScyoptionSaveFile(tOptions * opts) 719275970Scy{ 720275970Scy tOptDesc * od; 721275970Scy int ct; 722275970Scy FILE * fp = open_sv_file(opts); 723275970Scy 724275970Scy if (fp == NULL) 725275970Scy return; 726275970Scy 727275970Scy /* 728275970Scy * FOR each of the defined options, ... 729275970Scy */ 730275970Scy ct = opts->presetOptCt; 731275970Scy od = opts->pOptDesc; 732275970Scy do { 733275970Scy tOptDesc * p; 734275970Scy 735275970Scy /* 736275970Scy * IF the option has not been defined 737275970Scy * OR it does not take an initialization value 738275970Scy * OR it is equivalenced to another option 739275970Scy * THEN continue (ignore it) 740275970Scy * 741275970Scy * Equivalenced options get picked up when the equivalenced-to 742275970Scy * option is processed. 743275970Scy */ 744275970Scy if (UNUSED_OPT(od)) 745275970Scy continue; 746330141Sdelphij 747330141Sdelphij if ((od->fOptState & OPTST_DO_NOT_SAVE_MASK) != 0) 748330141Sdelphij continue; 749330141Sdelphij 750330141Sdelphij if ( (od->optEquivIndex != NO_EQUIVALENT) 751330141Sdelphij && (od->optEquivIndex != od->optIndex)) 752330141Sdelphij continue; 753330141Sdelphij 754330141Sdelphij /* 755330141Sdelphij * The option argument data are found at the equivalenced-to option, 756330141Sdelphij * but the actual option argument type comes from the original 757330141Sdelphij * option descriptor. Be careful! 758275970Scy */ 759285612Sdelphij p = ((od->fOptState & OPTST_EQUIVALENCE) != 0) 760330141Sdelphij ? (opts->pOptDesc + od->optActualIndex) : od; 761330141Sdelphij 762330141Sdelphij switch (OPTST_GET_ARGTYPE(od->fOptState)) { 763330141Sdelphij case OPARG_TYPE_NONE: 764285612Sdelphij prt_no_arg_opt(fp, p, od); 765275970Scy break; 766275970Scy 767275970Scy case OPARG_TYPE_NUMERIC: 768275970Scy prt_entry(fp, p, VOIDP(p->optArg.argInt)); 769275970Scy break; 770285612Sdelphij 771275970Scy case OPARG_TYPE_STRING: 772275970Scy prt_str_arg(fp, p); 773275970Scy break; 774275970Scy 775275970Scy case OPARG_TYPE_ENUMERATION: 776275970Scy prt_enum_arg(fp, p); 777275970Scy break; 778275970Scy 779275970Scy case OPARG_TYPE_MEMBERSHIP: 780275970Scy prt_set_arg(fp, p); 781275970Scy break; 782275970Scy 783275970Scy case OPARG_TYPE_BOOLEAN: 784275970Scy prt_entry(fp, p, p->optArg.argBool ? "true" : "false"); 785275970Scy break; 786275970Scy 787275970Scy case OPARG_TYPE_HIERARCHY: 788275970Scy prt_nested(fp, p); 789275970Scy break; 790275970Scy 791275970Scy case OPARG_TYPE_FILE: 792275970Scy prt_file_arg(fp, p, opts); 793275970Scy break; 794275970Scy 795275970Scy default: 796275970Scy break; /* cannot handle - skip it */ 797275970Scy } 798275970Scy } while (od++, (--ct > 0)); 799275970Scy 800275970Scy fclose(fp); 801275970Scy} 802285612Sdelphij/** @} 803275970Scy * 804275970Scy * Local Variables: 805275970Scy * mode: C 806275970Scy * c-file-style: "stroustrup" 807275970Scy * indent-tabs-mode: nil 808275970Scy * End: 809275970Scy * end of autoopts/save.c */ 810275970Scy