xref: /freebsd/contrib/ntp/sntp/libopts/save.c (revision a466cc55373fc3cf86837f09da729535b57e69a1)
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