save.c 21 KB

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