1
0

save.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917
  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-2018 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. #include "save-flags.h"
  33. /**
  34. * find the config file directory name
  35. *
  36. * @param opts the options descriptor
  37. * @param p_free tell caller if name was allocated or not
  38. */
  39. static char const *
  40. find_dir_name(tOptions * opts, int * p_free)
  41. {
  42. char const * dir;
  43. int idx;
  44. if ( (opts->specOptIdx.save_opts == NO_EQUIVALENT)
  45. || (opts->specOptIdx.save_opts == 0))
  46. return NULL;
  47. dir = opts->pOptDesc[ opts->specOptIdx.save_opts ].optArg.argString;
  48. if ((dir != NULL) && (*dir != NUL)) {
  49. char const * pz = strchr(dir, '>');
  50. if (pz == NULL)
  51. return dir;
  52. while (*(++pz) == '>') ;
  53. pz += strspn(pz, " \t");
  54. dir = pz;
  55. if (*dir != NUL)
  56. return dir;
  57. }
  58. if (opts->papzHomeList == NULL)
  59. return NULL;
  60. /*
  61. * This function only works if there is a directory where
  62. * we can stash the RC (INI) file.
  63. */
  64. for (idx = 0;; idx++) {
  65. char f_name[ AG_PATH_MAX+1 ];
  66. dir = opts->papzHomeList[idx];
  67. switch (*dir) {
  68. case '$':
  69. break;
  70. case NUL:
  71. continue;
  72. default:
  73. return dir;
  74. }
  75. if (optionMakePath(f_name, (int)sizeof(f_name), dir, opts->pzProgPath)) {
  76. *p_free = true;
  77. AGDUPSTR(dir, f_name, "homerc");
  78. return dir;
  79. }
  80. }
  81. return NULL;
  82. }
  83. /**
  84. * Find the name of the save-the-options file
  85. *
  86. * @param opts the options descriptor
  87. * @param p_free_name tell caller if name was allocated or not
  88. */
  89. static char const *
  90. find_file_name(tOptions * opts, int * p_free_name)
  91. {
  92. struct stat stBuf;
  93. int free_dir_name = 0;
  94. char const * res = find_dir_name(opts, &free_dir_name);
  95. if (res == NULL)
  96. return res;
  97. /*
  98. * See if we can find the specified directory. We use a once-only loop
  99. * structure so we can bail out early.
  100. */
  101. if (stat(res, &stBuf) != 0) do {
  102. char z[AG_PATH_MAX];
  103. char * dirchp;
  104. /*
  105. * IF we could not, check to see if we got a full
  106. * path to a file name that has not been created yet.
  107. */
  108. if (errno != ENOENT) {
  109. bogus_name:
  110. fprintf(stderr, zsave_warn, opts->pzProgName, res);
  111. fprintf(stderr, zNoStat, errno, strerror(errno), res);
  112. if (free_dir_name)
  113. AGFREE(res);
  114. return NULL;
  115. }
  116. /*
  117. * Strip off the last component, stat the remaining string and
  118. * that string must name a directory
  119. */
  120. dirchp = strrchr(res, DIRCH);
  121. if (dirchp == NULL) {
  122. stBuf.st_mode = S_IFREG;
  123. break; /* found directory -- viz., "." */
  124. }
  125. if ((size_t)(dirchp - res) >= sizeof(z))
  126. goto bogus_name;
  127. memcpy(z, res, (size_t)(dirchp - res));
  128. z[dirchp - res] = NUL;
  129. if ((stat(z, &stBuf) != 0) || ! S_ISDIR(stBuf.st_mode))
  130. goto bogus_name;
  131. stBuf.st_mode = S_IFREG; /* file within this directory */
  132. } while (false);
  133. /*
  134. * IF what we found was a directory,
  135. * THEN tack on the config file name
  136. */
  137. if (S_ISDIR(stBuf.st_mode)) {
  138. {
  139. size_t sz = strlen(res) + strlen(opts->pzRcName) + 2;
  140. char * pzPath = (char *)AGALOC(sz, "file name");
  141. if ( snprintf(pzPath, sz, "%s/%s", res, opts->pzRcName)
  142. >= (int)sz)
  143. option_exits(EXIT_FAILURE);
  144. if (free_dir_name)
  145. AGFREE(res);
  146. res = pzPath;
  147. free_dir_name = 1;
  148. }
  149. /*
  150. * IF we cannot stat the object for any reason other than
  151. * it does not exist, then we bail out
  152. */
  153. if (stat(res, &stBuf) != 0) {
  154. if (errno != ENOENT) {
  155. fprintf(stderr, zsave_warn, opts->pzProgName, res);
  156. fprintf(stderr, zNoStat, errno, strerror(errno),
  157. res);
  158. AGFREE(res);
  159. return NULL;
  160. }
  161. /*
  162. * It does not exist yet, but it will be a regular file
  163. */
  164. stBuf.st_mode = S_IFREG;
  165. }
  166. }
  167. /*
  168. * Make sure that whatever we ultimately found, that it either is
  169. * or will soon be a file.
  170. */
  171. if (! S_ISREG(stBuf.st_mode)) {
  172. fprintf(stderr, zsave_warn, opts->pzProgName, res);
  173. if (free_dir_name)
  174. AGFREE(res);
  175. return NULL;
  176. }
  177. /*
  178. * Get rid of the old file
  179. */
  180. *p_free_name = free_dir_name;
  181. return res;
  182. }
  183. /**
  184. * print one option entry to the save file.
  185. *
  186. * @param[in] fp the file pointer for the save file
  187. * @param[in] od the option descriptor to print
  188. * @param[in] l_arg the last argument for the option
  189. * @param[in] save_fl include usage in comments
  190. */
  191. static void
  192. prt_entry(FILE * fp, tOptDesc * od, char const * l_arg, save_flags_mask_t save_fl)
  193. {
  194. int space_ct;
  195. if (save_fl & SVFL_USAGE)
  196. fprintf(fp, ao_name_use_fmt, od->pz_Name, od->pzText);
  197. if (UNUSED_OPT(od) && (save_fl & SVFL_DEFAULT))
  198. fputs(ao_default_use, fp);
  199. /*
  200. * There is an argument. Pad the name so values line up.
  201. * Not disabled *OR* this got equivalenced to another opt,
  202. * then use current option name.
  203. * Otherwise, there must be a disablement name.
  204. */
  205. {
  206. char const * pz =
  207. (od->pz_DisableName == NULL)
  208. ? od->pz_Name
  209. : (DISABLED_OPT(od)
  210. ? od->pz_DisableName
  211. : ((od->optEquivIndex == NO_EQUIVALENT)
  212. ? od->pz_Name : od->pz_DisableName)
  213. );
  214. space_ct = 17 - strlen(pz);
  215. fputs(pz, fp);
  216. }
  217. if ( (l_arg == NULL)
  218. && (OPTST_GET_ARGTYPE(od->fOptState) != OPARG_TYPE_NUMERIC))
  219. goto end_entry;
  220. fputs(" = ", fp);
  221. while (space_ct-- > 0) fputc(' ', fp);
  222. /*
  223. * IF the option is numeric only,
  224. * THEN the char pointer is really the number
  225. */
  226. if (OPTST_GET_ARGTYPE(od->fOptState) == OPARG_TYPE_NUMERIC)
  227. fprintf(fp, "%d", (int)(intptr_t)l_arg);
  228. else {
  229. for (;;) {
  230. char const * eol = strchr(l_arg, NL);
  231. /*
  232. * IF this is the last line
  233. * THEN bail and print it
  234. */
  235. if (eol == NULL)
  236. break;
  237. /*
  238. * Print the continuation and the text from the current line
  239. */
  240. (void)fwrite(l_arg, (size_t)(eol - l_arg), (size_t)1, fp);
  241. l_arg = eol+1; /* advance the Last Arg pointer */
  242. fputs("\\\n", fp);
  243. }
  244. /*
  245. * Terminate the entry
  246. */
  247. fputs(l_arg, fp);
  248. }
  249. end_entry:
  250. fputc(NL, fp);
  251. }
  252. /**
  253. * print an option's value
  254. *
  255. * @param[in] fp the file pointer for the save file
  256. * @param[in] od the option descriptor to print
  257. */
  258. static void
  259. prt_value(FILE * fp, int depth, tOptDesc * od, tOptionValue const * ovp)
  260. {
  261. while (--depth >= 0)
  262. putc(' ', fp), putc(' ', fp);
  263. switch (ovp->valType) {
  264. default:
  265. case OPARG_TYPE_NONE:
  266. fprintf(fp, NULL_ATR_FMT, ovp->pzName);
  267. break;
  268. case OPARG_TYPE_STRING:
  269. prt_string(fp, ovp->pzName, ovp->v.strVal);
  270. break;
  271. case OPARG_TYPE_ENUMERATION:
  272. case OPARG_TYPE_MEMBERSHIP:
  273. if (od != NULL) {
  274. uint32_t opt_state = od->fOptState;
  275. uintptr_t val = od->optArg.argEnum;
  276. char const * typ = (ovp->valType == OPARG_TYPE_ENUMERATION)
  277. ? "keyword" : "set-membership";
  278. fprintf(fp, TYPE_ATR_FMT, ovp->pzName, typ);
  279. /*
  280. * This is a magic incantation that will convert the
  281. * bit flag values back into a string suitable for printing.
  282. */
  283. (*(od->pOptProc))(OPTPROC_RETURN_VALNAME, od );
  284. if (od->optArg.argString != NULL) {
  285. fputs(od->optArg.argString, fp);
  286. if (ovp->valType != OPARG_TYPE_ENUMERATION) {
  287. /*
  288. * set membership strings get allocated
  289. */
  290. AGFREE(od->optArg.argString);
  291. }
  292. }
  293. od->optArg.argEnum = val;
  294. od->fOptState = opt_state;
  295. fprintf(fp, END_XML_FMT, ovp->pzName);
  296. break;
  297. }
  298. /* FALLTHROUGH */
  299. case OPARG_TYPE_NUMERIC:
  300. fprintf(fp, NUMB_ATR_FMT, ovp->pzName, ovp->v.longVal);
  301. break;
  302. case OPARG_TYPE_BOOLEAN:
  303. fprintf(fp, BOOL_ATR_FMT, ovp->pzName,
  304. ovp->v.boolVal ? "true" : "false");
  305. break;
  306. case OPARG_TYPE_HIERARCHY:
  307. prt_val_list(fp, ovp->pzName, ovp->v.nestVal);
  308. break;
  309. }
  310. }
  311. /**
  312. * Print a string value in XML format
  313. *
  314. * @param[in] fp the file pointer for the save file
  315. */
  316. static void
  317. prt_string(FILE * fp, char const * name, char const * pz)
  318. {
  319. fprintf(fp, OPEN_XML_FMT, name);
  320. for (;;) {
  321. int ch = ((int)*(pz++)) & 0xFF;
  322. switch (ch) {
  323. case NUL: goto string_done;
  324. case '&':
  325. case '<':
  326. case '>':
  327. #if __GNUC__ >= 4
  328. case 1 ... (' ' - 1):
  329. case ('~' + 1) ... 0xFF:
  330. #endif
  331. emit_special_char(fp, ch);
  332. break;
  333. default:
  334. #if __GNUC__ < 4
  335. if ( ((ch >= 1) && (ch <= (' ' - 1)))
  336. || ((ch >= ('~' + 1)) && (ch <= 0xFF)) ) {
  337. emit_special_char(fp, ch);
  338. break;
  339. }
  340. #endif
  341. putc(ch, fp);
  342. }
  343. } string_done:;
  344. fprintf(fp, END_XML_FMT, name);
  345. }
  346. /**
  347. * Print an option that can have multiple values in XML format
  348. *
  349. * @param[in] fp file pointer
  350. */
  351. static void
  352. prt_val_list(FILE * fp, char const * name, tArgList * al)
  353. {
  354. static int depth = 1;
  355. int sp_ct;
  356. int opt_ct;
  357. void ** opt_list;
  358. if (al == NULL)
  359. return;
  360. opt_ct = al->useCt;
  361. opt_list = (void **)al->apzArgs;
  362. if (opt_ct <= 0) {
  363. fprintf(fp, OPEN_CLOSE_FMT, name);
  364. return;
  365. }
  366. fprintf(fp, NESTED_OPT_FMT, name);
  367. depth++;
  368. while (--opt_ct >= 0) {
  369. tOptionValue const * ovp = *(opt_list++);
  370. prt_value(fp, depth, NULL, ovp);
  371. }
  372. depth--;
  373. for (sp_ct = depth; --sp_ct >= 0;)
  374. putc(' ', fp), putc(' ', fp);
  375. fprintf(fp, "</%s>\n", name);
  376. }
  377. /**
  378. * printed a nested/hierarchical value
  379. *
  380. * @param[in] fp file pointer
  381. * @param[in] od option descriptor
  382. * @param[in] save_fl include usage in comments
  383. */
  384. static void
  385. prt_nested(FILE * fp, tOptDesc * od, save_flags_mask_t save_fl)
  386. {
  387. int opt_ct;
  388. tArgList * al = od->optCookie;
  389. void ** opt_list;
  390. if (save_fl & SVFL_USAGE)
  391. fprintf(fp, ao_name_use_fmt, od->pz_Name, od->pzText);
  392. /*
  393. * Never show a default value if a hierarchical value is empty.
  394. */
  395. if (UNUSED_OPT(od) || (al == NULL))
  396. return;
  397. opt_ct = al->useCt;
  398. opt_list = (void **)al->apzArgs;
  399. if (opt_ct <= 0)
  400. return;
  401. do {
  402. tOptionValue const * base = *(opt_list++);
  403. tOptionValue const * ovp = optionGetValue(base, NULL);
  404. if (ovp == NULL)
  405. continue;
  406. fprintf(fp, NESTED_OPT_FMT, od->pz_Name);
  407. do {
  408. prt_value(fp, 1, od, ovp);
  409. } while (ovp = optionNextValue(base, ovp),
  410. ovp != NULL);
  411. fprintf(fp, "</%s>\n", od->pz_Name);
  412. } while (--opt_ct > 0);
  413. }
  414. /**
  415. * remove the current program settings
  416. *
  417. * @param[in] opts the program options structure
  418. * @param[in] fname the save file name
  419. */
  420. static void
  421. remove_settings(tOptions * opts, char const * fname)
  422. {
  423. size_t const name_len = strlen(opts->pzProgName);
  424. tmap_info_t map_info;
  425. char * text = text_mmap(fname, PROT_READ|PROT_WRITE, MAP_PRIVATE, &map_info);
  426. char * scan = text;
  427. for (;;) {
  428. char * next = scan = strstr(scan, zCfgProg);
  429. if (scan == NULL)
  430. goto leave;
  431. scan = SPN_WHITESPACE_CHARS(scan + zCfgProg_LEN);
  432. if ( (strneqvcmp(scan, opts->pzProgName, (int)name_len) == 0)
  433. && (IS_END_XML_TOKEN_CHAR(scan[name_len])) ) {
  434. scan = next;
  435. break;
  436. }
  437. }
  438. /*
  439. * If not NULL, "scan" points to the "<?program" string introducing
  440. * the program segment we are to remove. See if another segment follows.
  441. * If so, copy text. If not se trim off this segment.
  442. */
  443. {
  444. char * next = strstr(scan + zCfgProg_LEN, zCfgProg);
  445. size_t new_sz;
  446. if (next == NULL)
  447. new_sz = map_info.txt_size - strlen(scan);
  448. else {
  449. int fd = open(fname, O_RDWR);
  450. if (fd < 0) return;
  451. if (lseek(fd, (scan - text), SEEK_SET) < 0)
  452. scan = next;
  453. else if (write(fd, next, strlen(next)) < 0)
  454. scan = next;
  455. if (close(fd) < 0)
  456. scan = next;
  457. new_sz = map_info.txt_size - (next - scan);
  458. }
  459. if (new_sz != map_info.txt_size)
  460. if (truncate(fname, new_sz) < 0)
  461. scan = next; // we removed it, so shorten file
  462. }
  463. leave:
  464. text_munmap(&map_info);
  465. }
  466. /**
  467. * open the file for saving option state.
  468. *
  469. * @param[in] opts the program options structure
  470. * @param[in] save_fl flags for saving data
  471. * @returns the open file pointer. It may be NULL.
  472. */
  473. static FILE *
  474. open_sv_file(tOptions * opts, save_flags_mask_t save_fl)
  475. {
  476. FILE * fp;
  477. {
  478. int free_name = 0;
  479. char const * fname = find_file_name(opts, &free_name);
  480. if (fname == NULL)
  481. return NULL;
  482. if (save_fl == 0)
  483. unlink(fname);
  484. else
  485. remove_settings(opts, fname);
  486. fp = fopen(fname, "a" FOPEN_BINARY_FLAG);
  487. if (fp == NULL) {
  488. fprintf(stderr, zsave_warn, opts->pzProgName, fname);
  489. fprintf(stderr, zNoCreat, errno, strerror(errno), fname);
  490. if (free_name)
  491. AGFREE(fname);
  492. return fp;
  493. }
  494. if (free_name)
  495. AGFREE(fname);
  496. }
  497. do {
  498. struct stat sbuf;
  499. if (fstat(fileno(fp), &sbuf) < 0)
  500. break;
  501. if (sbuf.st_size > zPresetFile_LEN) {
  502. /* non-zero size implies save_fl is non-zero */
  503. fprintf(fp, zFmtProg, opts->pzProgName);
  504. return fp;
  505. }
  506. } while (false);
  507. /*
  508. * We have a new file. Insert a header
  509. */
  510. fputs("# ", fp);
  511. {
  512. char const * e = strchr(opts->pzUsageTitle, NL);
  513. if (e++ != NULL)
  514. fwrite(opts->pzUsageTitle, 1, e - opts->pzUsageTitle, fp);
  515. }
  516. {
  517. time_t cur_time = time(NULL);
  518. char * time_str = ctime(&cur_time);
  519. fprintf(fp, zPresetFile, time_str);
  520. #ifdef HAVE_ALLOCATED_CTIME
  521. /*
  522. * The return values for ctime(), localtime(), and gmtime()
  523. * normally point to static data that is overwritten by each call.
  524. * The test to detect allocated ctime, so we leak the memory.
  525. */
  526. AGFREE(time_str);
  527. #endif
  528. }
  529. if (save_fl != 0)
  530. fprintf(fp, zFmtProg, opts->pzProgName);
  531. return fp;
  532. }
  533. /**
  534. * print option without an arg
  535. *
  536. * @param[in] fp file pointer
  537. * @param[in] vod value option descriptor
  538. * @param[in] pod primary option descriptor
  539. * @param[in] save_fl include usage in comments
  540. */
  541. static void
  542. prt_no_arg_opt(FILE * fp, tOptDesc * vod, tOptDesc * pod, save_flags_mask_t save_fl)
  543. {
  544. /*
  545. * The aliased to argument indicates whether or not the option
  546. * is "disabled". However, the original option has the name
  547. * string, so we get that there, not with "vod".
  548. */
  549. char const * pznm =
  550. (DISABLED_OPT(vod)) ? pod->pz_DisableName : pod->pz_Name;
  551. /*
  552. * If the option was disabled and the disablement name is NULL,
  553. * then the disablement was caused by aliasing.
  554. * Use the name as the string to emit.
  555. */
  556. if (pznm == NULL)
  557. pznm = pod->pz_Name;
  558. if (save_fl & SVFL_USAGE)
  559. fprintf(fp, ao_name_use_fmt, pod->pz_Name, pod->pzText);
  560. if (UNUSED_OPT(pod) && (save_fl & SVFL_DEFAULT))
  561. fputs(ao_default_use, fp);
  562. fprintf(fp, "%s\n", pznm);
  563. }
  564. /**
  565. * print the string valued argument(s).
  566. *
  567. * @param[in] fp file pointer
  568. * @param[in] od value option descriptor
  569. * @param[in] save_fl include usage in comments
  570. */
  571. static void
  572. prt_str_arg(FILE * fp, tOptDesc * od, save_flags_mask_t save_fl)
  573. {
  574. if (UNUSED_OPT(od) || ((od->fOptState & OPTST_STACKED) == 0)) {
  575. char const * arg = od->optArg.argString;
  576. if (arg == NULL)
  577. arg = "''";
  578. prt_entry(fp, od, arg, save_fl);
  579. } else {
  580. tArgList * pAL = (tArgList *)od->optCookie;
  581. int uct = pAL->useCt;
  582. char const ** ppz = pAL->apzArgs;
  583. /*
  584. * un-disable multiple copies of disabled options.
  585. */
  586. if (uct > 1)
  587. od->fOptState &= ~OPTST_DISABLED;
  588. while (uct-- > 0) {
  589. prt_entry(fp, od, *(ppz++), save_fl);
  590. save_fl &= ~SVFL_USAGE;
  591. }
  592. }
  593. }
  594. /**
  595. * print the string value of an enumeration.
  596. *
  597. * @param[in] fp the file pointer to write to
  598. * @param[in] od the option descriptor with the enumerated value
  599. * @param[in] save_fl include usage in comments
  600. */
  601. static void
  602. prt_enum_arg(FILE * fp, tOptDesc * od, save_flags_mask_t save_fl)
  603. {
  604. uintptr_t val = od->optArg.argEnum;
  605. /*
  606. * This is a magic incantation that will convert the
  607. * bit flag values back into a string suitable for printing.
  608. */
  609. (*(od->pOptProc))(OPTPROC_RETURN_VALNAME, od);
  610. prt_entry(fp, od, VOIDP(od->optArg.argString), save_fl);
  611. od->optArg.argEnum = val;
  612. }
  613. /**
  614. * Print the bits set in a bit mask option.
  615. *
  616. * We call the option handling function with a magic value for
  617. * the options pointer and it allocates and fills in the string.
  618. * We print that with a call to prt_entry().
  619. *
  620. * @param[in] fp the file pointer to write to
  621. * @param[in] od the option descriptor with a bit mask value type
  622. * @param[in] save_fl include usage in comments
  623. */
  624. static void
  625. prt_set_arg(FILE * fp, tOptDesc * od, save_flags_mask_t save_fl)
  626. {
  627. char * list = optionMemberList(od);
  628. size_t len = strlen(list);
  629. char * buf = (char *)AGALOC(len + 3, "dir name");
  630. *buf= '=';
  631. memcpy(buf+1, list, len + 1);
  632. prt_entry(fp, od, buf, save_fl);
  633. AGFREE(buf);
  634. AGFREE(list);
  635. }
  636. /**
  637. * figure out what the option file name argument is.
  638. * If one can be found, call prt_entry() to emit it.
  639. *
  640. * @param[in] fp the file pointer to write to.
  641. * @param[in] od the option descriptor with a bit mask value type
  642. * @param[in] opts the program options descriptor
  643. * @param[in] save_fl include usage in comments
  644. */
  645. static void
  646. prt_file_arg(FILE * fp, tOptDesc * od, tOptions * opts, save_flags_mask_t save_fl)
  647. {
  648. /*
  649. * If the cookie is not NULL, then it has the file name, period.
  650. * Otherwise, if we have a non-NULL string argument, then....
  651. */
  652. if (od->optCookie != NULL)
  653. prt_entry(fp, od, od->optCookie, save_fl);
  654. else if (HAS_originalOptArgArray(opts)) {
  655. char const * orig =
  656. opts->originalOptArgArray[od->optIndex].argString;
  657. if (od->optArg.argString == orig) {
  658. if (save_fl)
  659. fprintf(fp, ao_name_use_fmt, od->pz_Name, od->pzText);
  660. return;
  661. }
  662. prt_entry(fp, od, od->optArg.argString, save_fl);
  663. } else if (save_fl)
  664. fprintf(fp, ao_name_use_fmt, od->pz_Name, od->pzText);
  665. }
  666. /*=export_func optionSaveFile
  667. *
  668. * what: saves the option state to a file
  669. *
  670. * arg: tOptions *, opts, program options descriptor
  671. *
  672. * doc:
  673. *
  674. * This routine will save the state of option processing to a file. The name
  675. * of that file can be specified with the argument to the @code{--save-opts}
  676. * option, or by appending the @code{rcfile} attribute to the last
  677. * @code{homerc} attribute. If no @code{rcfile} attribute was specified, it
  678. * will default to @code{.@i{programname}rc}. If you wish to specify another
  679. * file, you should invoke the @code{SET_OPT_SAVE_OPTS(@i{filename})} macro.
  680. *
  681. * The recommend usage is as follows:
  682. * @example
  683. * optionProcess(&progOptions, argc, argv);
  684. * if (i_want_a_non_standard_place_for_this)
  685. * SET_OPT_SAVE_OPTS("myfilename");
  686. * optionSaveFile(&progOptions);
  687. * @end example
  688. *
  689. * err:
  690. *
  691. * If no @code{homerc} file was specified, this routine will silently return
  692. * and do nothing. If the output file cannot be created or updated, a message
  693. * will be printed to @code{stderr} and the routine will return.
  694. =*/
  695. void
  696. optionSaveFile(tOptions * opts)
  697. {
  698. tOptDesc * od;
  699. int ct;
  700. FILE * fp;
  701. save_flags_mask_t save_flags = SVFL_NONE;
  702. do {
  703. char * temp_str;
  704. char const * dir = opts->pOptDesc[ opts->specOptIdx.save_opts ].optArg.argString;
  705. size_t flen;
  706. if (dir == NULL)
  707. break;
  708. temp_str = strchr(dir, '>');
  709. if (temp_str == NULL)
  710. break;
  711. if (temp_str[1] == '>')
  712. save_flags = SVFL_UPDATE;
  713. flen = (temp_str - dir);
  714. if (flen == 0)
  715. break;
  716. temp_str = AGALOC(flen + 1, "flag search str");
  717. memcpy(temp_str, dir, flen);
  718. temp_str[flen] = NUL;
  719. save_flags |= save_flags_str2mask(temp_str, SVFL_NONE);
  720. AGFREE(temp_str);
  721. } while (false);
  722. fp = open_sv_file(opts, save_flags & SVFL_UPDATE);
  723. if (fp == NULL)
  724. return;
  725. /*
  726. * FOR each of the defined options, ...
  727. */
  728. ct = opts->presetOptCt;
  729. od = opts->pOptDesc;
  730. do {
  731. tOptDesc * vod;
  732. /*
  733. * Equivalenced options get picked up when the equivalenced-to
  734. * option is processed. And do not save options with any state
  735. * bits in the DO_NOT_SAVE collection
  736. *
  737. * ** option cannot be preset
  738. * #define OPTST_NO_INIT 0x0000100U
  739. * ** disable from cmd line
  740. * #define OPTST_NO_COMMAND 0x2000000U
  741. * ** alias for other option
  742. * #define OPTST_ALIAS 0x8000000U
  743. */
  744. if ((od->fOptState & OPTST_DO_NOT_SAVE_MASK) != 0)
  745. continue;
  746. if ( (od->optEquivIndex != NO_EQUIVALENT)
  747. && (od->optEquivIndex != od->optIndex))
  748. continue;
  749. if (UNUSED_OPT(od) && ((save_flags & SVFL_USAGE_DEFAULT_MASK) == SVFL_NONE))
  750. continue;
  751. /*
  752. * The option argument data are found at the equivalenced-to option,
  753. * but the actual option argument type comes from the original
  754. * option descriptor. Be careful!
  755. */
  756. vod = ((od->fOptState & OPTST_EQUIVALENCE) != 0)
  757. ? (opts->pOptDesc + od->optActualIndex) : od;
  758. switch (OPTST_GET_ARGTYPE(od->fOptState)) {
  759. case OPARG_TYPE_NONE:
  760. prt_no_arg_opt(fp, vod, od, save_flags);
  761. break;
  762. case OPARG_TYPE_NUMERIC:
  763. prt_entry(fp, vod, VOIDP(vod->optArg.argInt), save_flags);
  764. break;
  765. case OPARG_TYPE_STRING:
  766. prt_str_arg(fp, vod, save_flags);
  767. break;
  768. case OPARG_TYPE_ENUMERATION:
  769. prt_enum_arg(fp, vod, save_flags);
  770. break;
  771. case OPARG_TYPE_MEMBERSHIP:
  772. prt_set_arg(fp, vod, save_flags);
  773. break;
  774. case OPARG_TYPE_BOOLEAN:
  775. prt_entry(fp, vod, vod->optArg.argBool ? "true" : "false", save_flags);
  776. break;
  777. case OPARG_TYPE_HIERARCHY:
  778. prt_nested(fp, vod, save_flags);
  779. break;
  780. case OPARG_TYPE_FILE:
  781. prt_file_arg(fp, vod, opts, save_flags);
  782. break;
  783. default:
  784. break; /* cannot handle - skip it */
  785. }
  786. } while (od++, (--ct > 0));
  787. fclose(fp);
  788. }
  789. /** @}
  790. *
  791. * Local Variables:
  792. * mode: C
  793. * c-file-style: "stroustrup"
  794. * indent-tabs-mode: nil
  795. * End:
  796. * end of autoopts/save.c */