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