5 * Time-stamp: "2012-03-31 13:15:19 bkorb"
7 * This module's routines will take the currently set options and
8 * store them into an ".rc" file for re-interpretation the next
9 * time the invoking program is run.
11 * This file is part of AutoOpts, a companion to AutoGen.
12 * AutoOpts is free software.
13 * AutoOpts is Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
15 * AutoOpts is available under any one of two licenses. The license
16 * in use must be one of these two and the choice is under the control
17 * of the user of the license.
19 * The GNU Lesser General Public License, version 3 or later
20 * See the files "COPYING.lgplv3" and "COPYING.gplv3"
22 * The Modified Berkeley Software Distribution License
23 * See the file "COPYING.mbsd"
25 * These files have the following md5sums:
27 * 43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3
28 * 06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3
29 * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
32 /* = = = START-STATIC-FORWARD = = = */
34 find_dir_name(tOptions
* pOpts
, int * p_free
);
37 find_file_name(tOptions
* pOpts
, int * p_free_name
);
40 prt_entry(FILE * fp
, tOptDesc
* p
, char const * pzLA
);
43 prt_value(FILE * fp
, int depth
, tOptDesc
* pOD
, tOptionValue
const * ovp
);
46 prt_string(FILE * fp
, char const * name
, char const * pz
);
49 prt_val_list(FILE * fp
, char const * name
, tArgList
* al
);
52 prt_nested(FILE * fp
, tOptDesc
* p
);
55 open_sv_file(tOptions
* pOpts
);
58 prt_no_arg_opt(FILE * fp
, tOptDesc
* p
, tOptDesc
* pOD
);
61 prt_str_arg(FILE * fp
, tOptDesc
* pOD
);
64 prt_enum_arg(FILE * fp
, tOptDesc
* pOD
);
67 prt_set_arg(FILE * fp
, tOptDesc
* pOD
);
70 prt_file_arg(FILE * fp
, tOptDesc
* pOD
, tOptions
* pOpts
);
71 /* = = = END-STATIC-FORWARD = = = */
74 find_dir_name(tOptions
* pOpts
, int * p_free
)
78 if ( (pOpts
->specOptIdx
.save_opts
== NO_EQUIVALENT
)
79 || (pOpts
->specOptIdx
.save_opts
== 0))
82 pzDir
= pOpts
->pOptDesc
[ pOpts
->specOptIdx
.save_opts
].optArg
.argString
;
83 if ((pzDir
!= NULL
) && (*pzDir
!= NUL
))
87 * This function only works if there is a directory where
88 * we can stash the RC (INI) file.
91 char const * const* papz
= pOpts
->papzHomeList
;
95 while (papz
[1] != NULL
) papz
++;
100 * IF it does not require deciphering an env value, then just copy it
106 char const * pzEndDir
= strchr(++pzDir
, DIRCH
);
110 if (pzEndDir
!= NULL
) {
111 char z
[ AO_NAME_SIZE
];
112 if ((pzEndDir
- pzDir
) > AO_NAME_LIMIT
)
114 memcpy(z
, pzDir
, (size_t)(pzEndDir
- pzDir
));
115 z
[pzEndDir
- pzDir
] = NUL
;
120 * Make sure we can get the env value (after stripping off
121 * any trailing directory or file names)
123 pzEnv
= getenv(pzDir
);
127 fprintf(stderr
, SAVE_WARN
, pOpts
->pzProgName
);
128 fprintf(stderr
, zNotDef
, pzDir
);
132 if (pzEndDir
== NULL
)
136 size_t sz
= strlen(pzEnv
) + strlen(pzEndDir
) + 2;
137 pzFileName
= (char*)AGALOC(sz
, "dir name");
140 if (pzFileName
== NULL
)
145 * Glue together the full name into the allocated memory.
146 * FIXME: We lose track of this memory.
148 sprintf(pzFileName
, "%s/%s", pzEnv
, pzEndDir
);
155 find_file_name(tOptions
* pOpts
, int * p_free_name
)
158 int free_dir_name
= 0;
160 char const * pzDir
= find_dir_name(pOpts
, &free_dir_name
);
165 * See if we can find the specified directory. We use a once-only loop
166 * structure so we can bail out early.
168 if (stat(pzDir
, &stBuf
) != 0) do {
173 * IF we could not, check to see if we got a full
174 * path to a file name that has not been created yet.
176 if (errno
!= ENOENT
) {
178 fprintf(stderr
, SAVE_WARN
, pOpts
->pzProgName
);
179 fprintf(stderr
, zNoStat
, errno
, strerror(errno
), pzDir
);
181 AGFREE((void*)pzDir
);
186 * Strip off the last component, stat the remaining string and
187 * that string must name a directory
189 dirchp
= strrchr(pzDir
, DIRCH
);
190 if (dirchp
== NULL
) {
191 stBuf
.st_mode
= S_IFREG
;
192 break; /* found directory -- viz., "." */
195 if ((size_t)(dirchp
- pzDir
) >= sizeof(z
))
198 memcpy(z
, pzDir
, (size_t)(dirchp
- pzDir
));
199 z
[dirchp
- pzDir
] = NUL
;
201 if ((stat(z
, &stBuf
) != 0) || ! S_ISDIR(stBuf
.st_mode
))
203 stBuf
.st_mode
= S_IFREG
; /* file within this directory */
207 * IF what we found was a directory,
208 * THEN tack on the config file name
210 if (S_ISDIR(stBuf
.st_mode
)) {
211 size_t sz
= strlen(pzDir
) + strlen(pOpts
->pzRcName
) + 2;
214 char * pzPath
= (char*)AGALOC(sz
, "file name");
216 snprintf(pzPath
, sz
, "%s/%s", pzDir
, pOpts
->pzRcName
);
218 sprintf(pzPath
, "%s/%s", pzDir
, pOpts
->pzRcName
);
221 AGFREE((void*)pzDir
);
227 * IF we cannot stat the object for any reason other than
228 * it does not exist, then we bail out
230 if (stat(pzDir
, &stBuf
) != 0) {
231 if (errno
!= ENOENT
) {
232 fprintf(stderr
, SAVE_WARN
, pOpts
->pzProgName
);
233 fprintf(stderr
, zNoStat
, errno
, strerror(errno
),
235 AGFREE((void*)pzDir
);
240 * It does not exist yet, but it will be a regular file
242 stBuf
.st_mode
= S_IFREG
;
247 * Make sure that whatever we ultimately found, that it either is
248 * or will soon be a file.
250 if (! S_ISREG(stBuf
.st_mode
)) {
251 fprintf(stderr
, SAVE_WARN
, pOpts
->pzProgName
);
252 fprintf(stderr
, zNotFile
, pzDir
);
254 AGFREE((void*)pzDir
);
259 * Get rid of the old file
262 *p_free_name
= free_dir_name
;
268 prt_entry(FILE * fp
, tOptDesc
* p
, char const * pzLA
)
271 * There is an argument. Pad the name so values line up.
272 * Not disabled *OR* this got equivalenced to another opt,
273 * then use current option name.
274 * Otherwise, there must be a disablement name.
278 if (! DISABLED_OPT(p
) || (p
->optEquivIndex
!= NO_EQUIVALENT
))
281 pz
= p
->pz_DisableName
;
283 fprintf(fp
, "%-18s", pz
);
286 * IF the option is numeric only,
287 * THEN the char pointer is really the number
289 if (OPTST_GET_ARGTYPE(p
->fOptState
) == OPARG_TYPE_NUMERIC
)
290 fprintf(fp
, " %d\n", (int)(t_word
)pzLA
);
293 * OTHERWISE, FOR each line of the value text, ...
295 else if (pzLA
== NULL
)
299 fputc(' ', fp
); fputc(' ', fp
);
301 char const * pzNl
= strchr(pzLA
, NL
);
304 * IF this is the last line
305 * THEN bail and print it
311 * Print the continuation and the text from the current line
313 (void)fwrite(pzLA
, (size_t)(pzNl
- pzLA
), (size_t)1, fp
);
314 pzLA
= pzNl
+1; /* advance the Last Arg pointer */
319 * Terminate the entry
328 prt_value(FILE * fp
, int depth
, tOptDesc
* pOD
, tOptionValue
const * ovp
)
331 putc(' ', fp
), putc(' ', fp
);
333 switch (ovp
->valType
) {
335 case OPARG_TYPE_NONE
:
336 fprintf(fp
, NULL_ATR_FMT
, ovp
->pzName
);
339 case OPARG_TYPE_STRING
:
340 prt_string(fp
, ovp
->pzName
, ovp
->v
.strVal
);
343 case OPARG_TYPE_ENUMERATION
:
344 case OPARG_TYPE_MEMBERSHIP
:
346 tAoUI opt_state
= pOD
->fOptState
;
347 uintptr_t val
= pOD
->optArg
.argEnum
;
348 char const * typ
= (ovp
->valType
== OPARG_TYPE_ENUMERATION
)
349 ? "keyword" : "set-membership";
351 fprintf(fp
, TYPE_ATR_FMT
, ovp
->pzName
, typ
);
354 * This is a magic incantation that will convert the
355 * bit flag values back into a string suitable for printing.
357 (*(pOD
->pOptProc
))(OPTPROC_RETURN_VALNAME
, pOD
);
358 if (pOD
->optArg
.argString
!= NULL
) {
359 fputs(pOD
->optArg
.argString
, fp
);
361 if (ovp
->valType
!= OPARG_TYPE_ENUMERATION
) {
363 * set membership strings get allocated
365 AGFREE((void*)pOD
->optArg
.argString
);
369 pOD
->optArg
.argEnum
= val
;
370 pOD
->fOptState
= opt_state
;
371 fprintf(fp
, END_XML_FMT
, ovp
->pzName
);
376 case OPARG_TYPE_NUMERIC
:
377 fprintf(fp
, NUMB_ATR_FMT
, ovp
->pzName
, ovp
->v
.longVal
);
380 case OPARG_TYPE_BOOLEAN
:
381 fprintf(fp
, BOOL_ATR_FMT
, ovp
->pzName
,
382 ovp
->v
.boolVal
? "true" : "false");
385 case OPARG_TYPE_HIERARCHY
:
386 prt_val_list(fp
, ovp
->pzName
, ovp
->v
.nestVal
);
393 prt_string(FILE * fp
, char const * name
, char const * pz
)
395 fprintf(fp
, OPEN_XML_FMT
, name
);
397 int ch
= ((int)*(pz
++)) & 0xFF;
400 case NUL
: goto string_done
;
406 case 1 ... (' ' - 1):
407 case ('~' + 1) ... 0xFF:
409 emit_special_char(fp
, ch
);
414 if ( ((ch
>= 1) && (ch
<= (' ' - 1)))
415 || ((ch
>= ('~' + 1)) && (ch
<= 0xFF)) ) {
416 emit_special_char(fp
, ch
);
423 fprintf(fp
, END_XML_FMT
, name
);
428 prt_val_list(FILE * fp
, char const * name
, tArgList
* al
)
430 static int depth
= 1;
439 opt_list
= (void **)al
->apzArgs
;
442 fprintf(fp
, OPEN_CLOSE_FMT
, name
);
446 fprintf(fp
, NESTED_OPT_FMT
, name
);
449 while (--opt_ct
>= 0) {
450 tOptionValue
const * ovp
= *(opt_list
++);
452 prt_value(fp
, depth
, NULL
, ovp
);
456 for (sp_ct
= depth
; --sp_ct
>= 0;)
457 putc(' ', fp
), putc(' ', fp
);
458 fprintf(fp
, "</%s>\n", name
);
463 prt_nested(FILE * fp
, tOptDesc
* p
)
466 tArgList
* al
= p
->optCookie
;
473 opt_list
= (void **)al
->apzArgs
;
479 tOptionValue
const * base
= *(opt_list
++);
480 tOptionValue
const * ovp
= optionGetValue(base
, NULL
);
485 fprintf(fp
, NESTED_OPT_FMT
, p
->pz_Name
);
488 prt_value(fp
, 1, p
, ovp
);
490 } while (ovp
= optionNextValue(base
, ovp
),
493 fprintf(fp
, "</%s>\n", p
->pz_Name
);
494 } while (--opt_ct
> 0);
499 open_sv_file(tOptions
* pOpts
)
505 char const * pzFName
= find_file_name(pOpts
, &free_name
);
509 fp
= fopen(pzFName
, "w" FOPEN_BINARY_FLAG
);
511 fprintf(stderr
, SAVE_WARN
, pOpts
->pzProgName
);
512 fprintf(stderr
, zNoCreat
, errno
, strerror(errno
), pzFName
);
514 AGFREE((void*) pzFName
);
519 AGFREE((void*)pzFName
);
523 char const * pz
= pOpts
->pzUsageTitle
;
525 do { fputc(*pz
, fp
); } while (*(pz
++) != NL
);
529 time_t timeVal
= time(NULL
);
530 char * pzTime
= ctime(&timeVal
);
532 fprintf(fp
, zPresetFile
, pzTime
);
533 #ifdef HAVE_ALLOCATED_CTIME
535 * The return values for ctime(), localtime(), and gmtime()
536 * normally point to static data that is overwritten by each call.
537 * The test to detect allocated ctime, so we leak the memory.
539 AGFREE((void*)pzTime
);
547 prt_no_arg_opt(FILE * fp
, tOptDesc
* p
, tOptDesc
* pOD
)
550 * The aliased to argument indicates whether or not the option
551 * is "disabled". However, the original option has the name
552 * string, so we get that there, not with "p".
555 (DISABLED_OPT(p
)) ? pOD
->pz_DisableName
: pOD
->pz_Name
;
557 * If the option was disabled and the disablement name is NULL,
558 * then the disablement was caused by aliasing.
559 * Use the name as the string to emit.
564 fprintf(fp
, "%s\n", pznm
);
568 prt_str_arg(FILE * fp
, tOptDesc
* pOD
)
570 if (pOD
->fOptState
& OPTST_STACKED
) {
571 tArgList
* pAL
= (tArgList
*)pOD
->optCookie
;
572 int uct
= pAL
->useCt
;
573 char const ** ppz
= pAL
->apzArgs
;
576 * un-disable multiple copies of disabled options.
579 pOD
->fOptState
&= ~OPTST_DISABLED
;
582 prt_entry(fp
, pOD
, *(ppz
++));
584 prt_entry(fp
, pOD
, pOD
->optArg
.argString
);
589 prt_enum_arg(FILE * fp
, tOptDesc
* pOD
)
591 uintptr_t val
= pOD
->optArg
.argEnum
;
594 * This is a magic incantation that will convert the
595 * bit flag values back into a string suitable for printing.
597 (*(pOD
->pOptProc
))(OPTPROC_RETURN_VALNAME
, pOD
);
598 prt_entry(fp
, pOD
, (void*)(pOD
->optArg
.argString
));
600 pOD
->optArg
.argEnum
= val
;
604 prt_set_arg(FILE * fp
, tOptDesc
* pOD
)
606 uintptr_t val
= pOD
->optArg
.argEnum
;
609 * This is a magic incantation that will convert the
610 * bit flag values back into a string suitable for printing.
612 (*(pOD
->pOptProc
))(OPTPROC_RETURN_VALNAME
, pOD
);
613 prt_entry(fp
, pOD
, (void*)(pOD
->optArg
.argString
));
615 if (pOD
->optArg
.argString
!= NULL
) {
617 * set membership strings get allocated
619 AGFREE((void*)pOD
->optArg
.argString
);
620 pOD
->fOptState
&= ~OPTST_ALLOC_ARG
;
623 pOD
->optArg
.argEnum
= val
;
627 prt_file_arg(FILE * fp
, tOptDesc
* pOD
, tOptions
* pOpts
)
630 * If the cookie is not NULL, then it has the file name, period.
631 * Otherwise, if we have a non-NULL string argument, then....
633 if (pOD
->optCookie
!= NULL
)
634 prt_entry(fp
, pOD
, pOD
->optCookie
);
636 else if (HAS_originalOptArgArray(pOpts
)) {
638 pOpts
->originalOptArgArray
[pOD
->optIndex
].argString
;
640 if (pOD
->optArg
.argString
== orig
)
643 prt_entry(fp
, pOD
, pOD
->optArg
.argString
);
648 /*=export_func optionSaveFile
650 * what: saves the option state to a file
652 * arg: tOptions*, pOpts, program options descriptor
656 * This routine will save the state of option processing to a file. The name
657 * of that file can be specified with the argument to the @code{--save-opts}
658 * option, or by appending the @code{rcfile} attribute to the last
659 * @code{homerc} attribute. If no @code{rcfile} attribute was specified, it
660 * will default to @code{.@i{programname}rc}. If you wish to specify another
661 * file, you should invoke the @code{SET_OPT_SAVE_OPTS(@i{filename})} macro.
663 * The recommend usage is as follows:
665 * optionProcess(&progOptions, argc, argv);
666 * if (i_want_a_non_standard_place_for_this)
667 * SET_OPT_SAVE_OPTS("myfilename");
668 * optionSaveFile(&progOptions);
673 * If no @code{homerc} file was specified, this routine will silently return
674 * and do nothing. If the output file cannot be created or updated, a message
675 * will be printed to @code{stderr} and the routine will return.
678 optionSaveFile(tOptions
* pOpts
)
682 FILE * fp
= open_sv_file(pOpts
);
688 * FOR each of the defined options, ...
690 ct
= pOpts
->presetOptCt
;
691 pOD
= pOpts
->pOptDesc
;
696 * IF the option has not been defined
697 * OR it does not take an initialization value
698 * OR it is equivalenced to another option
699 * THEN continue (ignore it)
701 * Equivalenced options get picked up when the equivalenced-to
702 * option is processed.
707 if ((pOD
->fOptState
& OPTST_DO_NOT_SAVE_MASK
) != 0)
710 if ( (pOD
->optEquivIndex
!= NO_EQUIVALENT
)
711 && (pOD
->optEquivIndex
!= pOD
->optIndex
))
715 * The option argument data are found at the equivalenced-to option,
716 * but the actual option argument type comes from the original
717 * option descriptor. Be careful!
719 p
= ((pOD
->fOptState
& OPTST_EQUIVALENCE
) != 0)
720 ? (pOpts
->pOptDesc
+ pOD
->optActualIndex
) : pOD
;
722 switch (OPTST_GET_ARGTYPE(pOD
->fOptState
)) {
723 case OPARG_TYPE_NONE
:
724 prt_no_arg_opt(fp
, p
, pOD
);
727 case OPARG_TYPE_NUMERIC
:
728 prt_entry(fp
, p
, (void*)(p
->optArg
.argInt
));
731 case OPARG_TYPE_STRING
:
735 case OPARG_TYPE_ENUMERATION
:
739 case OPARG_TYPE_MEMBERSHIP
:
743 case OPARG_TYPE_BOOLEAN
:
744 prt_entry(fp
, p
, p
->optArg
.argBool
? "true" : "false");
747 case OPARG_TYPE_HIERARCHY
:
751 case OPARG_TYPE_FILE
:
752 prt_file_arg(fp
, p
, pOpts
);
756 break; /* cannot handle - skip it */
758 } while (pOD
++, (--ct
> 0));
765 * c-file-style: "stroustrup"
766 * indent-tabs-mode: nil
768 * end of autoopts/save.c */