1ea906c41SOllivier Robert
2ea906c41SOllivier Robert /*
32b15cb3dSCy Schubert * \file save.c
4ea906c41SOllivier Robert *
5ea906c41SOllivier Robert * This module's routines will take the currently set options and
6ea906c41SOllivier Robert * store them into an ".rc" file for re-interpretation the next
7ea906c41SOllivier Robert * time the invoking program is run.
82b15cb3dSCy Schubert *
92b15cb3dSCy Schubert * @addtogroup autoopts
102b15cb3dSCy Schubert * @{
11ea906c41SOllivier Robert */
12ea906c41SOllivier Robert /*
132b15cb3dSCy Schubert * This file is part of AutoOpts, a companion to AutoGen.
142b15cb3dSCy Schubert * AutoOpts is free software.
15*a466cc55SCy Schubert * AutoOpts is Copyright (C) 1992-2018 by Bruce Korb - all rights reserved
16ea906c41SOllivier Robert *
172b15cb3dSCy Schubert * AutoOpts is available under any one of two licenses. The license
182b15cb3dSCy Schubert * in use must be one of these two and the choice is under the control
192b15cb3dSCy Schubert * of the user of the license.
20ea906c41SOllivier Robert *
212b15cb3dSCy Schubert * The GNU Lesser General Public License, version 3 or later
222b15cb3dSCy Schubert * See the files "COPYING.lgplv3" and "COPYING.gplv3"
23ea906c41SOllivier Robert *
242b15cb3dSCy Schubert * The Modified Berkeley Software Distribution License
252b15cb3dSCy Schubert * See the file "COPYING.mbsd"
26ea906c41SOllivier Robert *
272b15cb3dSCy Schubert * These files have the following sha256 sums:
28ea906c41SOllivier Robert *
292b15cb3dSCy Schubert * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3
302b15cb3dSCy Schubert * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3
312b15cb3dSCy Schubert * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd
32ea906c41SOllivier Robert */
33*a466cc55SCy Schubert #include "save-flags.h"
34ea906c41SOllivier Robert
352b15cb3dSCy Schubert /**
36*a466cc55SCy Schubert * find the config file directory name
37*a466cc55SCy Schubert *
38*a466cc55SCy Schubert * @param opts the options descriptor
39*a466cc55SCy Schubert * @param p_free tell caller if name was allocated or not
402b15cb3dSCy Schubert */
412b15cb3dSCy Schubert static char const *
find_dir_name(tOptions * opts,int * p_free)422b15cb3dSCy Schubert find_dir_name(tOptions * opts, int * p_free)
43ea906c41SOllivier Robert {
44*a466cc55SCy Schubert char const * dir;
45ea906c41SOllivier Robert
462b15cb3dSCy Schubert if ( (opts->specOptIdx.save_opts == NO_EQUIVALENT)
472b15cb3dSCy Schubert || (opts->specOptIdx.save_opts == 0))
48ea906c41SOllivier Robert return NULL;
49ea906c41SOllivier Robert
50*a466cc55SCy Schubert dir = opts->pOptDesc[ opts->specOptIdx.save_opts ].optArg.argString;
51*a466cc55SCy Schubert if ((dir != NULL) && (*dir != NUL)) {
52*a466cc55SCy Schubert char const * pz = strchr(dir, '>');
53*a466cc55SCy Schubert if (pz == NULL)
54*a466cc55SCy Schubert return dir;
55*a466cc55SCy Schubert while (*(++pz) == '>') ;
56*a466cc55SCy Schubert pz += strspn(pz, " \t");
57*a466cc55SCy Schubert dir = pz;
58*a466cc55SCy Schubert if (*dir != NUL)
59*a466cc55SCy Schubert return dir;
60*a466cc55SCy Schubert }
61*a466cc55SCy Schubert
62*a466cc55SCy Schubert if (opts->papzHomeList == NULL)
63*a466cc55SCy Schubert return NULL;
64ea906c41SOllivier Robert
65ea906c41SOllivier Robert /*
66ea906c41SOllivier Robert * This function only works if there is a directory where
67ea906c41SOllivier Robert * we can stash the RC (INI) file.
68ea906c41SOllivier Robert */
69*a466cc55SCy Schubert for (int idx = 0;; idx++) {
70*a466cc55SCy Schubert char f_name[ AG_PATH_MAX+1 ];
71*a466cc55SCy Schubert
72*a466cc55SCy Schubert dir = opts->papzHomeList[idx];
73*a466cc55SCy Schubert
74*a466cc55SCy Schubert switch (*dir) {
75*a466cc55SCy Schubert case '$':
76*a466cc55SCy Schubert break;
77*a466cc55SCy Schubert case NUL:
78*a466cc55SCy Schubert continue;
79*a466cc55SCy Schubert default:
80*a466cc55SCy Schubert return dir;
81*a466cc55SCy Schubert }
82*a466cc55SCy Schubert if (optionMakePath(f_name, (int)sizeof(f_name), dir, opts->pzProgPath)) {
83*a466cc55SCy Schubert *p_free = true;
84*a466cc55SCy Schubert AGDUPSTR(dir, f_name, "homerc");
85*a466cc55SCy Schubert return dir;
86*a466cc55SCy Schubert }
87*a466cc55SCy Schubert }
88ea906c41SOllivier Robert return NULL;
89ea906c41SOllivier Robert }
90ea906c41SOllivier Robert
912b15cb3dSCy Schubert /**
92*a466cc55SCy Schubert * Find the name of the save-the-options file
93*a466cc55SCy Schubert *
94*a466cc55SCy Schubert * @param opts the options descriptor
95*a466cc55SCy Schubert * @param p_free_name tell caller if name was allocated or not
962b15cb3dSCy Schubert */
972b15cb3dSCy Schubert static char const *
find_file_name(tOptions * opts,int * p_free_name)982b15cb3dSCy Schubert find_file_name(tOptions * opts, int * p_free_name)
99ea906c41SOllivier Robert {
100ea906c41SOllivier Robert struct stat stBuf;
101ea906c41SOllivier Robert int free_dir_name = 0;
102ea906c41SOllivier Robert
103*a466cc55SCy Schubert char const * res = find_dir_name(opts, &free_dir_name);
104*a466cc55SCy Schubert if (res == NULL)
105*a466cc55SCy Schubert return res;
106ea906c41SOllivier Robert
107ea906c41SOllivier Robert /*
108ea906c41SOllivier Robert * See if we can find the specified directory. We use a once-only loop
109ea906c41SOllivier Robert * structure so we can bail out early.
110ea906c41SOllivier Robert */
111*a466cc55SCy Schubert if (stat(res, &stBuf) != 0) do {
1122b15cb3dSCy Schubert char z[AG_PATH_MAX];
1132b15cb3dSCy Schubert char * dirchp;
114ea906c41SOllivier Robert
115ea906c41SOllivier Robert /*
116ea906c41SOllivier Robert * IF we could not, check to see if we got a full
117ea906c41SOllivier Robert * path to a file name that has not been created yet.
118ea906c41SOllivier Robert */
1192b15cb3dSCy Schubert if (errno != ENOENT) {
1202b15cb3dSCy Schubert bogus_name:
121*a466cc55SCy Schubert fprintf(stderr, zsave_warn, opts->pzProgName, res);
122*a466cc55SCy Schubert fprintf(stderr, zNoStat, errno, strerror(errno), res);
1232b15cb3dSCy Schubert if (free_dir_name)
124*a466cc55SCy Schubert AGFREE(res);
1252b15cb3dSCy Schubert return NULL;
1262b15cb3dSCy Schubert }
127ea906c41SOllivier Robert
128ea906c41SOllivier Robert /*
129ea906c41SOllivier Robert * Strip off the last component, stat the remaining string and
130ea906c41SOllivier Robert * that string must name a directory
131ea906c41SOllivier Robert */
132*a466cc55SCy Schubert dirchp = strrchr(res, DIRCH);
1332b15cb3dSCy Schubert if (dirchp == NULL) {
134ea906c41SOllivier Robert stBuf.st_mode = S_IFREG;
1352b15cb3dSCy Schubert break; /* found directory -- viz., "." */
136ea906c41SOllivier Robert }
137ea906c41SOllivier Robert
138*a466cc55SCy Schubert if ((size_t)(dirchp - res) >= sizeof(z))
1392b15cb3dSCy Schubert goto bogus_name;
140ea906c41SOllivier Robert
141*a466cc55SCy Schubert memcpy(z, res, (size_t)(dirchp - res));
142*a466cc55SCy Schubert z[dirchp - res] = NUL;
143ea906c41SOllivier Robert
1442b15cb3dSCy Schubert if ((stat(z, &stBuf) != 0) || ! S_ISDIR(stBuf.st_mode))
1452b15cb3dSCy Schubert goto bogus_name;
1462b15cb3dSCy Schubert stBuf.st_mode = S_IFREG; /* file within this directory */
1472b15cb3dSCy Schubert } while (false);
148ea906c41SOllivier Robert
149ea906c41SOllivier Robert /*
150ea906c41SOllivier Robert * IF what we found was a directory,
151ea906c41SOllivier Robert * THEN tack on the config file name
152ea906c41SOllivier Robert */
153ea906c41SOllivier Robert if (S_ISDIR(stBuf.st_mode)) {
154ea906c41SOllivier Robert
155ea906c41SOllivier Robert {
156*a466cc55SCy Schubert size_t sz = strlen(res) + strlen(opts->pzRcName) + 2;
157ea906c41SOllivier Robert char * pzPath = (char *)AGALOC(sz, "file name");
158*a466cc55SCy Schubert if ( snprintf(pzPath, sz, "%s/%s", res, opts->pzRcName)
159*a466cc55SCy Schubert >= (int)sz)
160*a466cc55SCy Schubert option_exits(EXIT_FAILURE);
161*a466cc55SCy Schubert
162ea906c41SOllivier Robert if (free_dir_name)
163*a466cc55SCy Schubert AGFREE(res);
164*a466cc55SCy Schubert res = pzPath;
165ea906c41SOllivier Robert free_dir_name = 1;
166ea906c41SOllivier Robert }
167ea906c41SOllivier Robert
168ea906c41SOllivier Robert /*
169ea906c41SOllivier Robert * IF we cannot stat the object for any reason other than
170ea906c41SOllivier Robert * it does not exist, then we bail out
171ea906c41SOllivier Robert */
172*a466cc55SCy Schubert if (stat(res, &stBuf) != 0) {
173ea906c41SOllivier Robert if (errno != ENOENT) {
174*a466cc55SCy Schubert fprintf(stderr, zsave_warn, opts->pzProgName, res);
175ea906c41SOllivier Robert fprintf(stderr, zNoStat, errno, strerror(errno),
176*a466cc55SCy Schubert res);
177*a466cc55SCy Schubert AGFREE(res);
178ea906c41SOllivier Robert return NULL;
179ea906c41SOllivier Robert }
180ea906c41SOllivier Robert
181ea906c41SOllivier Robert /*
182ea906c41SOllivier Robert * It does not exist yet, but it will be a regular file
183ea906c41SOllivier Robert */
184ea906c41SOllivier Robert stBuf.st_mode = S_IFREG;
185ea906c41SOllivier Robert }
186ea906c41SOllivier Robert }
187ea906c41SOllivier Robert
188ea906c41SOllivier Robert /*
189ea906c41SOllivier Robert * Make sure that whatever we ultimately found, that it either is
190ea906c41SOllivier Robert * or will soon be a file.
191ea906c41SOllivier Robert */
192ea906c41SOllivier Robert if (! S_ISREG(stBuf.st_mode)) {
193*a466cc55SCy Schubert fprintf(stderr, zsave_warn, opts->pzProgName, res);
194ea906c41SOllivier Robert if (free_dir_name)
195*a466cc55SCy Schubert AGFREE(res);
196ea906c41SOllivier Robert return NULL;
197ea906c41SOllivier Robert }
198ea906c41SOllivier Robert
199ea906c41SOllivier Robert /*
200ea906c41SOllivier Robert * Get rid of the old file
201ea906c41SOllivier Robert */
202ea906c41SOllivier Robert *p_free_name = free_dir_name;
203*a466cc55SCy Schubert return res;
204ea906c41SOllivier Robert }
205ea906c41SOllivier Robert
2062b15cb3dSCy Schubert /**
2072b15cb3dSCy Schubert * print one option entry to the save file.
2082b15cb3dSCy Schubert *
2092b15cb3dSCy Schubert * @param[in] fp the file pointer for the save file
2102b15cb3dSCy Schubert * @param[in] od the option descriptor to print
2112b15cb3dSCy Schubert * @param[in] l_arg the last argument for the option
212*a466cc55SCy Schubert * @param[in] save_fl include usage in comments
2132b15cb3dSCy Schubert */
214ea906c41SOllivier Robert static void
prt_entry(FILE * fp,tOptDesc * od,char const * l_arg,save_flags_mask_t save_fl)215*a466cc55SCy Schubert prt_entry(FILE * fp, tOptDesc * od, char const * l_arg, save_flags_mask_t save_fl)
216ea906c41SOllivier Robert {
2172b15cb3dSCy Schubert int space_ct;
2182b15cb3dSCy Schubert
219*a466cc55SCy Schubert if (save_fl & SVFL_USAGE)
220*a466cc55SCy Schubert fprintf(fp, ao_name_use_fmt, od->pz_Name, od->pzText);
221*a466cc55SCy Schubert if (UNUSED_OPT(od) && (save_fl & SVFL_DEFAULT))
222*a466cc55SCy Schubert fputs(ao_default_use, fp);
223*a466cc55SCy Schubert
224ea906c41SOllivier Robert /*
225ea906c41SOllivier Robert * There is an argument. Pad the name so values line up.
226ea906c41SOllivier Robert * Not disabled *OR* this got equivalenced to another opt,
227ea906c41SOllivier Robert * then use current option name.
228ea906c41SOllivier Robert * Otherwise, there must be a disablement name.
229ea906c41SOllivier Robert */
230ea906c41SOllivier Robert {
2312b15cb3dSCy Schubert char const * pz =
232*a466cc55SCy Schubert (od->pz_DisableName == NULL)
2332b15cb3dSCy Schubert ? od->pz_Name
234*a466cc55SCy Schubert : (DISABLED_OPT(od)
235*a466cc55SCy Schubert ? od->pz_DisableName
236*a466cc55SCy Schubert : ((od->optEquivIndex == NO_EQUIVALENT)
237*a466cc55SCy Schubert ? od->pz_Name : od->pz_DisableName)
238*a466cc55SCy Schubert );
239*a466cc55SCy Schubert
2402b15cb3dSCy Schubert space_ct = 17 - strlen(pz);
2412b15cb3dSCy Schubert fputs(pz, fp);
242ea906c41SOllivier Robert }
2432b15cb3dSCy Schubert
2442b15cb3dSCy Schubert if ( (l_arg == NULL)
2452b15cb3dSCy Schubert && (OPTST_GET_ARGTYPE(od->fOptState) != OPARG_TYPE_NUMERIC))
2462b15cb3dSCy Schubert goto end_entry;
2472b15cb3dSCy Schubert
2482b15cb3dSCy Schubert fputs(" = ", fp);
2492b15cb3dSCy Schubert while (space_ct-- > 0) fputc(' ', fp);
2502b15cb3dSCy Schubert
251ea906c41SOllivier Robert /*
252ea906c41SOllivier Robert * IF the option is numeric only,
253ea906c41SOllivier Robert * THEN the char pointer is really the number
254ea906c41SOllivier Robert */
2552b15cb3dSCy Schubert if (OPTST_GET_ARGTYPE(od->fOptState) == OPARG_TYPE_NUMERIC)
256276da39aSCy Schubert fprintf(fp, "%d", (int)(intptr_t)l_arg);
257ea906c41SOllivier Robert
258ea906c41SOllivier Robert else {
259ea906c41SOllivier Robert for (;;) {
2602b15cb3dSCy Schubert char const * eol = strchr(l_arg, NL);
261ea906c41SOllivier Robert
262ea906c41SOllivier Robert /*
263ea906c41SOllivier Robert * IF this is the last line
264ea906c41SOllivier Robert * THEN bail and print it
265ea906c41SOllivier Robert */
2662b15cb3dSCy Schubert if (eol == NULL)
267ea906c41SOllivier Robert break;
268ea906c41SOllivier Robert
269ea906c41SOllivier Robert /*
270ea906c41SOllivier Robert * Print the continuation and the text from the current line
271ea906c41SOllivier Robert */
2722b15cb3dSCy Schubert (void)fwrite(l_arg, (size_t)(eol - l_arg), (size_t)1, fp);
2732b15cb3dSCy Schubert l_arg = eol+1; /* advance the Last Arg pointer */
274ea906c41SOllivier Robert fputs("\\\n", fp);
275ea906c41SOllivier Robert }
276ea906c41SOllivier Robert
277ea906c41SOllivier Robert /*
278ea906c41SOllivier Robert * Terminate the entry
279ea906c41SOllivier Robert */
2802b15cb3dSCy Schubert fputs(l_arg, fp);
2812b15cb3dSCy Schubert }
2822b15cb3dSCy Schubert
2832b15cb3dSCy Schubert end_entry:
2842b15cb3dSCy Schubert fputc(NL, fp);
2852b15cb3dSCy Schubert }
2862b15cb3dSCy Schubert
2872b15cb3dSCy Schubert /**
288*a466cc55SCy Schubert * print an option's value
289*a466cc55SCy Schubert *
290*a466cc55SCy Schubert * @param[in] fp the file pointer for the save file
291*a466cc55SCy Schubert * @param[in] od the option descriptor to print
2922b15cb3dSCy Schubert */
2932b15cb3dSCy Schubert static void
prt_value(FILE * fp,int depth,tOptDesc * od,tOptionValue const * ovp)294*a466cc55SCy Schubert prt_value(FILE * fp, int depth, tOptDesc * od, tOptionValue const * ovp)
2952b15cb3dSCy Schubert {
2962b15cb3dSCy Schubert while (--depth >= 0)
2972b15cb3dSCy Schubert putc(' ', fp), putc(' ', fp);
2982b15cb3dSCy Schubert
2992b15cb3dSCy Schubert switch (ovp->valType) {
3002b15cb3dSCy Schubert default:
3012b15cb3dSCy Schubert case OPARG_TYPE_NONE:
3022b15cb3dSCy Schubert fprintf(fp, NULL_ATR_FMT, ovp->pzName);
3032b15cb3dSCy Schubert break;
3042b15cb3dSCy Schubert
3052b15cb3dSCy Schubert case OPARG_TYPE_STRING:
3062b15cb3dSCy Schubert prt_string(fp, ovp->pzName, ovp->v.strVal);
3072b15cb3dSCy Schubert break;
3082b15cb3dSCy Schubert
3092b15cb3dSCy Schubert case OPARG_TYPE_ENUMERATION:
3102b15cb3dSCy Schubert case OPARG_TYPE_MEMBERSHIP:
311*a466cc55SCy Schubert if (od != NULL) {
312*a466cc55SCy Schubert uint32_t opt_state = od->fOptState;
313*a466cc55SCy Schubert uintptr_t val = od->optArg.argEnum;
3142b15cb3dSCy Schubert char const * typ = (ovp->valType == OPARG_TYPE_ENUMERATION)
3152b15cb3dSCy Schubert ? "keyword" : "set-membership";
3162b15cb3dSCy Schubert
3172b15cb3dSCy Schubert fprintf(fp, TYPE_ATR_FMT, ovp->pzName, typ);
3182b15cb3dSCy Schubert
3192b15cb3dSCy Schubert /*
3202b15cb3dSCy Schubert * This is a magic incantation that will convert the
3212b15cb3dSCy Schubert * bit flag values back into a string suitable for printing.
3222b15cb3dSCy Schubert */
323*a466cc55SCy Schubert (*(od->pOptProc))(OPTPROC_RETURN_VALNAME, od );
324*a466cc55SCy Schubert if (od->optArg.argString != NULL) {
325*a466cc55SCy Schubert fputs(od->optArg.argString, fp);
3262b15cb3dSCy Schubert
3272b15cb3dSCy Schubert if (ovp->valType != OPARG_TYPE_ENUMERATION) {
3282b15cb3dSCy Schubert /*
3292b15cb3dSCy Schubert * set membership strings get allocated
3302b15cb3dSCy Schubert */
331*a466cc55SCy Schubert AGFREE(od->optArg.argString);
332ea906c41SOllivier Robert }
333ea906c41SOllivier Robert }
334ea906c41SOllivier Robert
335*a466cc55SCy Schubert od->optArg.argEnum = val;
336*a466cc55SCy Schubert od->fOptState = opt_state;
3372b15cb3dSCy Schubert fprintf(fp, END_XML_FMT, ovp->pzName);
3382b15cb3dSCy Schubert break;
3392b15cb3dSCy Schubert }
3402b15cb3dSCy Schubert /* FALLTHROUGH */
3412b15cb3dSCy Schubert
3422b15cb3dSCy Schubert case OPARG_TYPE_NUMERIC:
3432b15cb3dSCy Schubert fprintf(fp, NUMB_ATR_FMT, ovp->pzName, ovp->v.longVal);
3442b15cb3dSCy Schubert break;
3452b15cb3dSCy Schubert
3462b15cb3dSCy Schubert case OPARG_TYPE_BOOLEAN:
3472b15cb3dSCy Schubert fprintf(fp, BOOL_ATR_FMT, ovp->pzName,
3482b15cb3dSCy Schubert ovp->v.boolVal ? "true" : "false");
3492b15cb3dSCy Schubert break;
3502b15cb3dSCy Schubert
3512b15cb3dSCy Schubert case OPARG_TYPE_HIERARCHY:
3522b15cb3dSCy Schubert prt_val_list(fp, ovp->pzName, ovp->v.nestVal);
3532b15cb3dSCy Schubert break;
3542b15cb3dSCy Schubert }
3552b15cb3dSCy Schubert }
3562b15cb3dSCy Schubert
3572b15cb3dSCy Schubert /**
358*a466cc55SCy Schubert * Print a string value in XML format
359*a466cc55SCy Schubert *
360*a466cc55SCy Schubert * @param[in] fp the file pointer for the save file
3612b15cb3dSCy Schubert */
3622b15cb3dSCy Schubert static void
prt_string(FILE * fp,char const * name,char const * pz)3632b15cb3dSCy Schubert prt_string(FILE * fp, char const * name, char const * pz)
3642b15cb3dSCy Schubert {
3652b15cb3dSCy Schubert fprintf(fp, OPEN_XML_FMT, name);
3662b15cb3dSCy Schubert for (;;) {
3672b15cb3dSCy Schubert int ch = ((int)*(pz++)) & 0xFF;
3682b15cb3dSCy Schubert
3692b15cb3dSCy Schubert switch (ch) {
3702b15cb3dSCy Schubert case NUL: goto string_done;
3712b15cb3dSCy Schubert
3722b15cb3dSCy Schubert case '&':
3732b15cb3dSCy Schubert case '<':
3742b15cb3dSCy Schubert case '>':
3752b15cb3dSCy Schubert #if __GNUC__ >= 4
3762b15cb3dSCy Schubert case 1 ... (' ' - 1):
3772b15cb3dSCy Schubert case ('~' + 1) ... 0xFF:
3782b15cb3dSCy Schubert #endif
3792b15cb3dSCy Schubert emit_special_char(fp, ch);
3802b15cb3dSCy Schubert break;
3812b15cb3dSCy Schubert
3822b15cb3dSCy Schubert default:
3832b15cb3dSCy Schubert #if __GNUC__ < 4
3842b15cb3dSCy Schubert if ( ((ch >= 1) && (ch <= (' ' - 1)))
3852b15cb3dSCy Schubert || ((ch >= ('~' + 1)) && (ch <= 0xFF)) ) {
3862b15cb3dSCy Schubert emit_special_char(fp, ch);
3872b15cb3dSCy Schubert break;
3882b15cb3dSCy Schubert }
3892b15cb3dSCy Schubert #endif
3902b15cb3dSCy Schubert putc(ch, fp);
3912b15cb3dSCy Schubert }
3922b15cb3dSCy Schubert } string_done:;
3932b15cb3dSCy Schubert fprintf(fp, END_XML_FMT, name);
3942b15cb3dSCy Schubert }
3952b15cb3dSCy Schubert
3962b15cb3dSCy Schubert /**
397*a466cc55SCy Schubert * Print an option that can have multiple values in XML format
398*a466cc55SCy Schubert *
399*a466cc55SCy Schubert * @param[in] fp file pointer
4002b15cb3dSCy Schubert */
4012b15cb3dSCy Schubert static void
prt_val_list(FILE * fp,char const * name,tArgList * al)4022b15cb3dSCy Schubert prt_val_list(FILE * fp, char const * name, tArgList * al)
4032b15cb3dSCy Schubert {
4042b15cb3dSCy Schubert static int depth = 1;
4052b15cb3dSCy Schubert
4062b15cb3dSCy Schubert int sp_ct;
4072b15cb3dSCy Schubert int opt_ct;
4082b15cb3dSCy Schubert void ** opt_list;
4092b15cb3dSCy Schubert
4102b15cb3dSCy Schubert if (al == NULL)
4112b15cb3dSCy Schubert return;
4122b15cb3dSCy Schubert opt_ct = al->useCt;
413*a466cc55SCy Schubert opt_list = (void **)al->apzArgs;
4142b15cb3dSCy Schubert
4152b15cb3dSCy Schubert if (opt_ct <= 0) {
4162b15cb3dSCy Schubert fprintf(fp, OPEN_CLOSE_FMT, name);
4172b15cb3dSCy Schubert return;
4182b15cb3dSCy Schubert }
4192b15cb3dSCy Schubert
4202b15cb3dSCy Schubert fprintf(fp, NESTED_OPT_FMT, name);
4212b15cb3dSCy Schubert
4222b15cb3dSCy Schubert depth++;
4232b15cb3dSCy Schubert while (--opt_ct >= 0) {
4242b15cb3dSCy Schubert tOptionValue const * ovp = *(opt_list++);
4252b15cb3dSCy Schubert
4262b15cb3dSCy Schubert prt_value(fp, depth, NULL, ovp);
4272b15cb3dSCy Schubert }
4282b15cb3dSCy Schubert depth--;
4292b15cb3dSCy Schubert
4302b15cb3dSCy Schubert for (sp_ct = depth; --sp_ct >= 0;)
4312b15cb3dSCy Schubert putc(' ', fp), putc(' ', fp);
4322b15cb3dSCy Schubert fprintf(fp, "</%s>\n", name);
4332b15cb3dSCy Schubert }
4342b15cb3dSCy Schubert
4352b15cb3dSCy Schubert /**
436*a466cc55SCy Schubert * printed a nested/hierarchical value
437*a466cc55SCy Schubert *
438*a466cc55SCy Schubert * @param[in] fp file pointer
439*a466cc55SCy Schubert * @param[in] od option descriptor
440*a466cc55SCy Schubert * @param[in] save_fl include usage in comments
4412b15cb3dSCy Schubert */
4422b15cb3dSCy Schubert static void
prt_nested(FILE * fp,tOptDesc * od,save_flags_mask_t save_fl)443*a466cc55SCy Schubert prt_nested(FILE * fp, tOptDesc * od, save_flags_mask_t save_fl)
4442b15cb3dSCy Schubert {
4452b15cb3dSCy Schubert int opt_ct;
446*a466cc55SCy Schubert tArgList * al = od->optCookie;
4472b15cb3dSCy Schubert void ** opt_list;
4482b15cb3dSCy Schubert
449*a466cc55SCy Schubert if (save_fl & SVFL_USAGE)
450*a466cc55SCy Schubert fprintf(fp, ao_name_use_fmt, od->pz_Name, od->pzText);
451*a466cc55SCy Schubert
452*a466cc55SCy Schubert /*
453*a466cc55SCy Schubert * Never show a default value if a hierarchical value is empty.
454*a466cc55SCy Schubert */
455*a466cc55SCy Schubert if (UNUSED_OPT(od) || (al == NULL))
4562b15cb3dSCy Schubert return;
4572b15cb3dSCy Schubert
4582b15cb3dSCy Schubert opt_ct = al->useCt;
459*a466cc55SCy Schubert opt_list = (void **)al->apzArgs;
4602b15cb3dSCy Schubert
4612b15cb3dSCy Schubert if (opt_ct <= 0)
4622b15cb3dSCy Schubert return;
4632b15cb3dSCy Schubert
4642b15cb3dSCy Schubert do {
4652b15cb3dSCy Schubert tOptionValue const * base = *(opt_list++);
4662b15cb3dSCy Schubert tOptionValue const * ovp = optionGetValue(base, NULL);
4672b15cb3dSCy Schubert
4682b15cb3dSCy Schubert if (ovp == NULL)
4692b15cb3dSCy Schubert continue;
4702b15cb3dSCy Schubert
471*a466cc55SCy Schubert fprintf(fp, NESTED_OPT_FMT, od->pz_Name);
4722b15cb3dSCy Schubert
4732b15cb3dSCy Schubert do {
474*a466cc55SCy Schubert prt_value(fp, 1, od, ovp);
4752b15cb3dSCy Schubert
4762b15cb3dSCy Schubert } while (ovp = optionNextValue(base, ovp),
4772b15cb3dSCy Schubert ovp != NULL);
4782b15cb3dSCy Schubert
479*a466cc55SCy Schubert fprintf(fp, "</%s>\n", od->pz_Name);
4802b15cb3dSCy Schubert } while (--opt_ct > 0);
4812b15cb3dSCy Schubert }
4822b15cb3dSCy Schubert
483*a466cc55SCy Schubert #ifdef _MSC_VER
484*a466cc55SCy Schubert /**
485*a466cc55SCy Schubert * truncate() emulation for Microsoft C
486*a466cc55SCy Schubert *
487*a466cc55SCy Schubert * @param[in] fname the save file name
488*a466cc55SCy Schubert * @param[in] newsz new size of fname in octets
489*a466cc55SCy Schubert */
490*a466cc55SCy Schubert static int
truncate(char const * fname,size_t newsz)491*a466cc55SCy Schubert truncate(char const* fname, size_t newsz)
492*a466cc55SCy Schubert {
493*a466cc55SCy Schubert int fd;
494*a466cc55SCy Schubert int err;
495*a466cc55SCy Schubert
496*a466cc55SCy Schubert fd = open(fname, O_RDWR);
497*a466cc55SCy Schubert if (fd < 0)
498*a466cc55SCy Schubert return fd;
499*a466cc55SCy Schubert err = _chsize_s(fd, newsz);
500*a466cc55SCy Schubert close(fd);
501*a466cc55SCy Schubert if (0 != err)
502*a466cc55SCy Schubert errno = err;
503*a466cc55SCy Schubert return err;
504*a466cc55SCy Schubert }
505*a466cc55SCy Schubert #endif /* _MSC_VER */
506*a466cc55SCy Schubert
507*a466cc55SCy Schubert /**
508*a466cc55SCy Schubert * remove the current program settings
509*a466cc55SCy Schubert *
510*a466cc55SCy Schubert * @param[in] opts the program options structure
511*a466cc55SCy Schubert * @param[in] fname the save file name
512*a466cc55SCy Schubert */
513*a466cc55SCy Schubert static void
remove_settings(tOptions * opts,char const * fname)514*a466cc55SCy Schubert remove_settings(tOptions * opts, char const * fname)
515*a466cc55SCy Schubert {
516*a466cc55SCy Schubert size_t const name_len = strlen(opts->pzProgName);
517*a466cc55SCy Schubert tmap_info_t map_info;
518*a466cc55SCy Schubert char * text = text_mmap(fname, PROT_READ|PROT_WRITE, MAP_PRIVATE, &map_info);
519*a466cc55SCy Schubert char * scan = text;
520*a466cc55SCy Schubert
521*a466cc55SCy Schubert for (;;) {
522*a466cc55SCy Schubert char * next = scan = strstr(scan, zCfgProg);
523*a466cc55SCy Schubert if (scan == NULL)
524*a466cc55SCy Schubert goto leave;
525*a466cc55SCy Schubert
526*a466cc55SCy Schubert scan = SPN_WHITESPACE_CHARS(scan + zCfgProg_LEN);
527*a466cc55SCy Schubert if ( (strneqvcmp(scan, opts->pzProgName, (int)name_len) == 0)
528*a466cc55SCy Schubert && (IS_END_XML_TOKEN_CHAR(scan[name_len])) ) {
529*a466cc55SCy Schubert
530*a466cc55SCy Schubert scan = next;
531*a466cc55SCy Schubert break;
532*a466cc55SCy Schubert }
533*a466cc55SCy Schubert }
534*a466cc55SCy Schubert
535*a466cc55SCy Schubert /*
536*a466cc55SCy Schubert * If not NULL, "scan" points to the "<?program" string introducing
537*a466cc55SCy Schubert * the program segment we are to remove. See if another segment follows.
538*a466cc55SCy Schubert * If so, copy text. If not se trim off this segment.
539*a466cc55SCy Schubert */
540*a466cc55SCy Schubert {
541*a466cc55SCy Schubert char * next = strstr(scan + zCfgProg_LEN, zCfgProg);
542*a466cc55SCy Schubert size_t new_sz;
543*a466cc55SCy Schubert
544*a466cc55SCy Schubert if (next == NULL)
545*a466cc55SCy Schubert new_sz = map_info.txt_size - strlen(scan);
546*a466cc55SCy Schubert else {
547*a466cc55SCy Schubert int fd = open(fname, O_RDWR);
548*a466cc55SCy Schubert if (fd < 0) return;
549*a466cc55SCy Schubert if (lseek(fd, (scan - text), SEEK_SET) < 0)
550*a466cc55SCy Schubert scan = next;
551*a466cc55SCy Schubert else if (write(fd, next, strlen(next)) < 0)
552*a466cc55SCy Schubert scan = next;
553*a466cc55SCy Schubert if (close(fd) < 0)
554*a466cc55SCy Schubert scan = next;
555*a466cc55SCy Schubert new_sz = map_info.txt_size - (next - scan);
556*a466cc55SCy Schubert }
557*a466cc55SCy Schubert if (new_sz != map_info.txt_size)
558*a466cc55SCy Schubert if (truncate(fname, new_sz) < 0)
559*a466cc55SCy Schubert scan = next; // we removed it, so shorten file
560*a466cc55SCy Schubert }
561*a466cc55SCy Schubert
562*a466cc55SCy Schubert leave:
563*a466cc55SCy Schubert text_munmap(&map_info);
564*a466cc55SCy Schubert }
565*a466cc55SCy Schubert
5662b15cb3dSCy Schubert /**
5672b15cb3dSCy Schubert * open the file for saving option state.
5682b15cb3dSCy Schubert *
5692b15cb3dSCy Schubert * @param[in] opts the program options structure
570*a466cc55SCy Schubert * @param[in] save_fl flags for saving data
5712b15cb3dSCy Schubert * @returns the open file pointer. It may be NULL.
5722b15cb3dSCy Schubert */
5732b15cb3dSCy Schubert static FILE *
open_sv_file(tOptions * opts,save_flags_mask_t save_fl)574*a466cc55SCy Schubert open_sv_file(tOptions * opts, save_flags_mask_t save_fl)
5752b15cb3dSCy Schubert {
5762b15cb3dSCy Schubert FILE * fp;
5772b15cb3dSCy Schubert
5782b15cb3dSCy Schubert {
5792b15cb3dSCy Schubert int free_name = 0;
580*a466cc55SCy Schubert char const * fname = find_file_name(opts, &free_name);
581*a466cc55SCy Schubert if (fname == NULL)
5822b15cb3dSCy Schubert return NULL;
5832b15cb3dSCy Schubert
584*a466cc55SCy Schubert if (save_fl == 0)
585*a466cc55SCy Schubert unlink(fname);
586*a466cc55SCy Schubert else
587*a466cc55SCy Schubert remove_settings(opts, fname);
588*a466cc55SCy Schubert
589*a466cc55SCy Schubert fp = fopen(fname, "a" FOPEN_BINARY_FLAG);
5902b15cb3dSCy Schubert if (fp == NULL) {
591*a466cc55SCy Schubert fprintf(stderr, zsave_warn, opts->pzProgName, fname);
592*a466cc55SCy Schubert fprintf(stderr, zNoCreat, errno, strerror(errno), fname);
5932b15cb3dSCy Schubert if (free_name)
594*a466cc55SCy Schubert AGFREE(fname);
5952b15cb3dSCy Schubert return fp;
5962b15cb3dSCy Schubert }
5972b15cb3dSCy Schubert
5982b15cb3dSCy Schubert if (free_name)
599*a466cc55SCy Schubert AGFREE(fname);
6002b15cb3dSCy Schubert }
6012b15cb3dSCy Schubert
602*a466cc55SCy Schubert do {
603*a466cc55SCy Schubert struct stat sbuf;
604*a466cc55SCy Schubert if (fstat(fileno(fp), &sbuf) < 0)
605*a466cc55SCy Schubert break;
606*a466cc55SCy Schubert
607*a466cc55SCy Schubert if (sbuf.st_size > zPresetFile_LEN) {
608*a466cc55SCy Schubert /* non-zero size implies save_fl is non-zero */
609*a466cc55SCy Schubert fprintf(fp, zFmtProg, opts->pzProgName);
610*a466cc55SCy Schubert return fp;
611*a466cc55SCy Schubert }
612*a466cc55SCy Schubert } while (false);
613*a466cc55SCy Schubert
614*a466cc55SCy Schubert /*
615*a466cc55SCy Schubert * We have a new file. Insert a header
616*a466cc55SCy Schubert */
6172b15cb3dSCy Schubert fputs("# ", fp);
6182b15cb3dSCy Schubert {
6192b15cb3dSCy Schubert char const * e = strchr(opts->pzUsageTitle, NL);
6202b15cb3dSCy Schubert if (e++ != NULL)
6212b15cb3dSCy Schubert fwrite(opts->pzUsageTitle, 1, e - opts->pzUsageTitle, fp);
6222b15cb3dSCy Schubert }
6232b15cb3dSCy Schubert
6242b15cb3dSCy Schubert {
6252b15cb3dSCy Schubert time_t cur_time = time(NULL);
6262b15cb3dSCy Schubert char * time_str = ctime(&cur_time);
6272b15cb3dSCy Schubert
6282b15cb3dSCy Schubert fprintf(fp, zPresetFile, time_str);
6292b15cb3dSCy Schubert #ifdef HAVE_ALLOCATED_CTIME
6302b15cb3dSCy Schubert /*
6312b15cb3dSCy Schubert * The return values for ctime(), localtime(), and gmtime()
6322b15cb3dSCy Schubert * normally point to static data that is overwritten by each call.
6332b15cb3dSCy Schubert * The test to detect allocated ctime, so we leak the memory.
6342b15cb3dSCy Schubert */
635276da39aSCy Schubert AGFREE(time_str);
6362b15cb3dSCy Schubert #endif
6372b15cb3dSCy Schubert }
638*a466cc55SCy Schubert if (save_fl != 0)
639*a466cc55SCy Schubert fprintf(fp, zFmtProg, opts->pzProgName);
6402b15cb3dSCy Schubert return fp;
6412b15cb3dSCy Schubert }
6422b15cb3dSCy Schubert
6432b15cb3dSCy Schubert /**
644*a466cc55SCy Schubert * print option without an arg
645*a466cc55SCy Schubert *
646*a466cc55SCy Schubert * @param[in] fp file pointer
647*a466cc55SCy Schubert * @param[in] vod value option descriptor
648*a466cc55SCy Schubert * @param[in] pod primary option descriptor
649*a466cc55SCy Schubert * @param[in] save_fl include usage in comments
6502b15cb3dSCy Schubert */
6512b15cb3dSCy Schubert static void
prt_no_arg_opt(FILE * fp,tOptDesc * vod,tOptDesc * pod,save_flags_mask_t save_fl)652*a466cc55SCy Schubert prt_no_arg_opt(FILE * fp, tOptDesc * vod, tOptDesc * pod, save_flags_mask_t save_fl)
6532b15cb3dSCy Schubert {
6542b15cb3dSCy Schubert /*
6552b15cb3dSCy Schubert * The aliased to argument indicates whether or not the option
6562b15cb3dSCy Schubert * is "disabled". However, the original option has the name
657*a466cc55SCy Schubert * string, so we get that there, not with "vod".
6582b15cb3dSCy Schubert */
6592b15cb3dSCy Schubert char const * pznm =
660*a466cc55SCy Schubert (DISABLED_OPT(vod)) ? pod->pz_DisableName : pod->pz_Name;
6612b15cb3dSCy Schubert /*
6622b15cb3dSCy Schubert * If the option was disabled and the disablement name is NULL,
6632b15cb3dSCy Schubert * then the disablement was caused by aliasing.
6642b15cb3dSCy Schubert * Use the name as the string to emit.
6652b15cb3dSCy Schubert */
6662b15cb3dSCy Schubert if (pznm == NULL)
667*a466cc55SCy Schubert pznm = pod->pz_Name;
668*a466cc55SCy Schubert
669*a466cc55SCy Schubert if (save_fl & SVFL_USAGE)
670*a466cc55SCy Schubert fprintf(fp, ao_name_use_fmt, pod->pz_Name, pod->pzText);
671*a466cc55SCy Schubert if (UNUSED_OPT(pod) && (save_fl & SVFL_DEFAULT))
672*a466cc55SCy Schubert fputs(ao_default_use, fp);
6732b15cb3dSCy Schubert
6742b15cb3dSCy Schubert fprintf(fp, "%s\n", pznm);
6752b15cb3dSCy Schubert }
6762b15cb3dSCy Schubert
6772b15cb3dSCy Schubert /**
678*a466cc55SCy Schubert * print the string valued argument(s).
679*a466cc55SCy Schubert *
680*a466cc55SCy Schubert * @param[in] fp file pointer
681*a466cc55SCy Schubert * @param[in] od value option descriptor
682*a466cc55SCy Schubert * @param[in] save_fl include usage in comments
6832b15cb3dSCy Schubert */
6842b15cb3dSCy Schubert static void
prt_str_arg(FILE * fp,tOptDesc * od,save_flags_mask_t save_fl)685*a466cc55SCy Schubert prt_str_arg(FILE * fp, tOptDesc * od, save_flags_mask_t save_fl)
6862b15cb3dSCy Schubert {
687*a466cc55SCy Schubert if (UNUSED_OPT(od) || ((od->fOptState & OPTST_STACKED) == 0)) {
688*a466cc55SCy Schubert char const * arg = od->optArg.argString;
689*a466cc55SCy Schubert if (arg == NULL)
690*a466cc55SCy Schubert arg = "''";
691*a466cc55SCy Schubert prt_entry(fp, od, arg, save_fl);
692*a466cc55SCy Schubert
693*a466cc55SCy Schubert } else {
694*a466cc55SCy Schubert tArgList * pAL = (tArgList *)od->optCookie;
6952b15cb3dSCy Schubert int uct = pAL->useCt;
6962b15cb3dSCy Schubert char const ** ppz = pAL->apzArgs;
6972b15cb3dSCy Schubert
6982b15cb3dSCy Schubert /*
6992b15cb3dSCy Schubert * un-disable multiple copies of disabled options.
7002b15cb3dSCy Schubert */
7012b15cb3dSCy Schubert if (uct > 1)
702*a466cc55SCy Schubert od->fOptState &= ~OPTST_DISABLED;
7032b15cb3dSCy Schubert
704*a466cc55SCy Schubert while (uct-- > 0) {
705*a466cc55SCy Schubert prt_entry(fp, od, *(ppz++), save_fl);
706*a466cc55SCy Schubert save_fl &= ~SVFL_USAGE;
707*a466cc55SCy Schubert }
7082b15cb3dSCy Schubert }
7092b15cb3dSCy Schubert }
7102b15cb3dSCy Schubert
7112b15cb3dSCy Schubert /**
7122b15cb3dSCy Schubert * print the string value of an enumeration.
7132b15cb3dSCy Schubert *
7142b15cb3dSCy Schubert * @param[in] fp the file pointer to write to
7152b15cb3dSCy Schubert * @param[in] od the option descriptor with the enumerated value
716*a466cc55SCy Schubert * @param[in] save_fl include usage in comments
7172b15cb3dSCy Schubert */
7182b15cb3dSCy Schubert static void
prt_enum_arg(FILE * fp,tOptDesc * od,save_flags_mask_t save_fl)719*a466cc55SCy Schubert prt_enum_arg(FILE * fp, tOptDesc * od, save_flags_mask_t save_fl)
7202b15cb3dSCy Schubert {
7212b15cb3dSCy Schubert uintptr_t val = od->optArg.argEnum;
7222b15cb3dSCy Schubert
7232b15cb3dSCy Schubert /*
7242b15cb3dSCy Schubert * This is a magic incantation that will convert the
7252b15cb3dSCy Schubert * bit flag values back into a string suitable for printing.
7262b15cb3dSCy Schubert */
7272b15cb3dSCy Schubert (*(od->pOptProc))(OPTPROC_RETURN_VALNAME, od);
728*a466cc55SCy Schubert prt_entry(fp, od, VOIDP(od->optArg.argString), save_fl);
7292b15cb3dSCy Schubert
7302b15cb3dSCy Schubert od->optArg.argEnum = val;
7312b15cb3dSCy Schubert }
7322b15cb3dSCy Schubert
7332b15cb3dSCy Schubert /**
7342b15cb3dSCy Schubert * Print the bits set in a bit mask option.
735*a466cc55SCy Schubert *
7362b15cb3dSCy Schubert * We call the option handling function with a magic value for
7372b15cb3dSCy Schubert * the options pointer and it allocates and fills in the string.
7382b15cb3dSCy Schubert * We print that with a call to prt_entry().
7392b15cb3dSCy Schubert *
7402b15cb3dSCy Schubert * @param[in] fp the file pointer to write to
7412b15cb3dSCy Schubert * @param[in] od the option descriptor with a bit mask value type
742*a466cc55SCy Schubert * @param[in] save_fl include usage in comments
7432b15cb3dSCy Schubert */
7442b15cb3dSCy Schubert static void
prt_set_arg(FILE * fp,tOptDesc * od,save_flags_mask_t save_fl)745*a466cc55SCy Schubert prt_set_arg(FILE * fp, tOptDesc * od, save_flags_mask_t save_fl)
7462b15cb3dSCy Schubert {
7472b15cb3dSCy Schubert char * list = optionMemberList(od);
7482b15cb3dSCy Schubert size_t len = strlen(list);
7492b15cb3dSCy Schubert char * buf = (char *)AGALOC(len + 3, "dir name");
7502b15cb3dSCy Schubert *buf= '=';
7512b15cb3dSCy Schubert memcpy(buf+1, list, len + 1);
752*a466cc55SCy Schubert prt_entry(fp, od, buf, save_fl);
7532b15cb3dSCy Schubert AGFREE(buf);
7542b15cb3dSCy Schubert AGFREE(list);
7552b15cb3dSCy Schubert }
7562b15cb3dSCy Schubert
7572b15cb3dSCy Schubert /**
7582b15cb3dSCy Schubert * figure out what the option file name argument is.
7592b15cb3dSCy Schubert * If one can be found, call prt_entry() to emit it.
7602b15cb3dSCy Schubert *
7612b15cb3dSCy Schubert * @param[in] fp the file pointer to write to.
7622b15cb3dSCy Schubert * @param[in] od the option descriptor with a bit mask value type
7632b15cb3dSCy Schubert * @param[in] opts the program options descriptor
764*a466cc55SCy Schubert * @param[in] save_fl include usage in comments
7652b15cb3dSCy Schubert */
7662b15cb3dSCy Schubert static void
prt_file_arg(FILE * fp,tOptDesc * od,tOptions * opts,save_flags_mask_t save_fl)767*a466cc55SCy Schubert prt_file_arg(FILE * fp, tOptDesc * od, tOptions * opts, save_flags_mask_t save_fl)
7682b15cb3dSCy Schubert {
7692b15cb3dSCy Schubert /*
7702b15cb3dSCy Schubert * If the cookie is not NULL, then it has the file name, period.
7712b15cb3dSCy Schubert * Otherwise, if we have a non-NULL string argument, then....
7722b15cb3dSCy Schubert */
7732b15cb3dSCy Schubert if (od->optCookie != NULL)
774*a466cc55SCy Schubert prt_entry(fp, od, od->optCookie, save_fl);
7752b15cb3dSCy Schubert
7762b15cb3dSCy Schubert else if (HAS_originalOptArgArray(opts)) {
7772b15cb3dSCy Schubert char const * orig =
7782b15cb3dSCy Schubert opts->originalOptArgArray[od->optIndex].argString;
7792b15cb3dSCy Schubert
780*a466cc55SCy Schubert if (od->optArg.argString == orig) {
781*a466cc55SCy Schubert if (save_fl)
782*a466cc55SCy Schubert fprintf(fp, ao_name_use_fmt, od->pz_Name, od->pzText);
7832b15cb3dSCy Schubert return;
7842b15cb3dSCy Schubert }
785*a466cc55SCy Schubert
786*a466cc55SCy Schubert prt_entry(fp, od, od->optArg.argString, save_fl);
787*a466cc55SCy Schubert
788*a466cc55SCy Schubert } else if (save_fl)
789*a466cc55SCy Schubert fprintf(fp, ao_name_use_fmt, od->pz_Name, od->pzText);
7902b15cb3dSCy Schubert }
791ea906c41SOllivier Robert
792ea906c41SOllivier Robert /*=export_func optionSaveFile
793ea906c41SOllivier Robert *
794ea906c41SOllivier Robert * what: saves the option state to a file
795ea906c41SOllivier Robert *
7962b15cb3dSCy Schubert * arg: tOptions *, opts, program options descriptor
797ea906c41SOllivier Robert *
798ea906c41SOllivier Robert * doc:
799ea906c41SOllivier Robert *
800ea906c41SOllivier Robert * This routine will save the state of option processing to a file. The name
801ea906c41SOllivier Robert * of that file can be specified with the argument to the @code{--save-opts}
802ea906c41SOllivier Robert * option, or by appending the @code{rcfile} attribute to the last
803ea906c41SOllivier Robert * @code{homerc} attribute. If no @code{rcfile} attribute was specified, it
804ea906c41SOllivier Robert * will default to @code{.@i{programname}rc}. If you wish to specify another
805ea906c41SOllivier Robert * file, you should invoke the @code{SET_OPT_SAVE_OPTS(@i{filename})} macro.
806ea906c41SOllivier Robert *
8072b15cb3dSCy Schubert * The recommend usage is as follows:
8082b15cb3dSCy Schubert * @example
8092b15cb3dSCy Schubert * optionProcess(&progOptions, argc, argv);
8102b15cb3dSCy Schubert * if (i_want_a_non_standard_place_for_this)
8112b15cb3dSCy Schubert * SET_OPT_SAVE_OPTS("myfilename");
8122b15cb3dSCy Schubert * optionSaveFile(&progOptions);
8132b15cb3dSCy Schubert * @end example
8142b15cb3dSCy Schubert *
815ea906c41SOllivier Robert * err:
816ea906c41SOllivier Robert *
817ea906c41SOllivier Robert * If no @code{homerc} file was specified, this routine will silently return
818ea906c41SOllivier Robert * and do nothing. If the output file cannot be created or updated, a message
819ea906c41SOllivier Robert * will be printed to @code{stderr} and the routine will return.
820ea906c41SOllivier Robert =*/
821ea906c41SOllivier Robert void
optionSaveFile(tOptions * opts)8222b15cb3dSCy Schubert optionSaveFile(tOptions * opts)
823ea906c41SOllivier Robert {
8242b15cb3dSCy Schubert tOptDesc * od;
825ea906c41SOllivier Robert int ct;
826*a466cc55SCy Schubert FILE * fp;
827*a466cc55SCy Schubert save_flags_mask_t save_flags = SVFL_NONE;
828ea906c41SOllivier Robert
829*a466cc55SCy Schubert do {
830*a466cc55SCy Schubert char * temp_str;
831*a466cc55SCy Schubert char const * dir = opts->pOptDesc[ opts->specOptIdx.save_opts ].optArg.argString;
832*a466cc55SCy Schubert size_t flen;
833*a466cc55SCy Schubert
834*a466cc55SCy Schubert if (dir == NULL)
835*a466cc55SCy Schubert break;
836*a466cc55SCy Schubert temp_str = strchr(dir, '>');
837*a466cc55SCy Schubert if (temp_str == NULL)
838*a466cc55SCy Schubert break;
839*a466cc55SCy Schubert if (temp_str[1] == '>')
840*a466cc55SCy Schubert save_flags = SVFL_UPDATE;
841*a466cc55SCy Schubert flen = (temp_str - dir);
842*a466cc55SCy Schubert if (flen == 0)
843*a466cc55SCy Schubert break;
844*a466cc55SCy Schubert temp_str = AGALOC(flen + 1, "flag search str");
845*a466cc55SCy Schubert memcpy(temp_str, dir, flen);
846*a466cc55SCy Schubert temp_str[flen] = NUL;
847*a466cc55SCy Schubert save_flags |= save_flags_str2mask(temp_str, SVFL_NONE);
848*a466cc55SCy Schubert AGFREE(temp_str);
849*a466cc55SCy Schubert } while (false);
850*a466cc55SCy Schubert
851*a466cc55SCy Schubert fp = open_sv_file(opts, save_flags & SVFL_UPDATE);
8522b15cb3dSCy Schubert if (fp == NULL)
853ea906c41SOllivier Robert return;
854ea906c41SOllivier Robert
855ea906c41SOllivier Robert /*
856ea906c41SOllivier Robert * FOR each of the defined options, ...
857ea906c41SOllivier Robert */
8582b15cb3dSCy Schubert ct = opts->presetOptCt;
8592b15cb3dSCy Schubert od = opts->pOptDesc;
860ea906c41SOllivier Robert do {
861*a466cc55SCy Schubert tOptDesc * vod;
862ea906c41SOllivier Robert
863ea906c41SOllivier Robert /*
8642b15cb3dSCy Schubert * Equivalenced options get picked up when the equivalenced-to
865*a466cc55SCy Schubert * option is processed. And do not save options with any state
866*a466cc55SCy Schubert * bits in the DO_NOT_SAVE collection
867*a466cc55SCy Schubert *
868*a466cc55SCy Schubert * ** option cannot be preset
869*a466cc55SCy Schubert * #define OPTST_NO_INIT 0x0000100U
870*a466cc55SCy Schubert * ** disable from cmd line
871*a466cc55SCy Schubert * #define OPTST_NO_COMMAND 0x2000000U
872*a466cc55SCy Schubert * ** alias for other option
873*a466cc55SCy Schubert * #define OPTST_ALIAS 0x8000000U
874ea906c41SOllivier Robert */
8752b15cb3dSCy Schubert if ((od->fOptState & OPTST_DO_NOT_SAVE_MASK) != 0)
876ea906c41SOllivier Robert continue;
877ea906c41SOllivier Robert
8782b15cb3dSCy Schubert if ( (od->optEquivIndex != NO_EQUIVALENT)
8792b15cb3dSCy Schubert && (od->optEquivIndex != od->optIndex))
880ea906c41SOllivier Robert continue;
881ea906c41SOllivier Robert
882*a466cc55SCy Schubert if (UNUSED_OPT(od) && ((save_flags & SVFL_USAGE_DEFAULT_MASK) == SVFL_NONE))
883*a466cc55SCy Schubert continue;
884*a466cc55SCy Schubert
885ea906c41SOllivier Robert /*
8862b15cb3dSCy Schubert * The option argument data are found at the equivalenced-to option,
8872b15cb3dSCy Schubert * but the actual option argument type comes from the original
8882b15cb3dSCy Schubert * option descriptor. Be careful!
889ea906c41SOllivier Robert */
890*a466cc55SCy Schubert vod = ((od->fOptState & OPTST_EQUIVALENCE) != 0)
8912b15cb3dSCy Schubert ? (opts->pOptDesc + od->optActualIndex) : od;
892ea906c41SOllivier Robert
8932b15cb3dSCy Schubert switch (OPTST_GET_ARGTYPE(od->fOptState)) {
8942b15cb3dSCy Schubert case OPARG_TYPE_NONE:
895*a466cc55SCy Schubert prt_no_arg_opt(fp, vod, od, save_flags);
8962b15cb3dSCy Schubert break;
897ea906c41SOllivier Robert
898ea906c41SOllivier Robert case OPARG_TYPE_NUMERIC:
899*a466cc55SCy Schubert prt_entry(fp, vod, VOIDP(vod->optArg.argInt), save_flags);
900ea906c41SOllivier Robert break;
901ea906c41SOllivier Robert
902ea906c41SOllivier Robert case OPARG_TYPE_STRING:
903*a466cc55SCy Schubert prt_str_arg(fp, vod, save_flags);
904ea906c41SOllivier Robert break;
905ea906c41SOllivier Robert
906ea906c41SOllivier Robert case OPARG_TYPE_ENUMERATION:
907*a466cc55SCy Schubert prt_enum_arg(fp, vod, save_flags);
908ea906c41SOllivier Robert break;
9092b15cb3dSCy Schubert
9102b15cb3dSCy Schubert case OPARG_TYPE_MEMBERSHIP:
911*a466cc55SCy Schubert prt_set_arg(fp, vod, save_flags);
9122b15cb3dSCy Schubert break;
913ea906c41SOllivier Robert
914ea906c41SOllivier Robert case OPARG_TYPE_BOOLEAN:
915*a466cc55SCy Schubert prt_entry(fp, vod, vod->optArg.argBool ? "true" : "false", save_flags);
9162b15cb3dSCy Schubert break;
9172b15cb3dSCy Schubert
9182b15cb3dSCy Schubert case OPARG_TYPE_HIERARCHY:
919*a466cc55SCy Schubert prt_nested(fp, vod, save_flags);
9202b15cb3dSCy Schubert break;
9212b15cb3dSCy Schubert
9222b15cb3dSCy Schubert case OPARG_TYPE_FILE:
923*a466cc55SCy Schubert prt_file_arg(fp, vod, opts, save_flags);
924ea906c41SOllivier Robert break;
925ea906c41SOllivier Robert
926ea906c41SOllivier Robert default:
927ea906c41SOllivier Robert break; /* cannot handle - skip it */
928ea906c41SOllivier Robert }
9292b15cb3dSCy Schubert } while (od++, (--ct > 0));
930ea906c41SOllivier Robert
931ea906c41SOllivier Robert fclose(fp);
932ea906c41SOllivier Robert }
9332b15cb3dSCy Schubert /** @}
9342b15cb3dSCy Schubert *
935ea906c41SOllivier Robert * Local Variables:
936ea906c41SOllivier Robert * mode: C
937ea906c41SOllivier Robert * c-file-style: "stroustrup"
938ea906c41SOllivier Robert * indent-tabs-mode: nil
939ea906c41SOllivier Robert * End:
940ea906c41SOllivier Robert * end of autoopts/save.c */
941