5 * save.c Id: 5a69234fab4c2d8d7eaf4aed4dbb3052ce6be5b6
6 * Time-stamp: "2009-07-20 20:40:28 bkorb"
8 * This module's routines will take the currently set options and
9 * store them into an ".rc" file for re-interpretation the next
10 * time the invoking program is run.
12 * This file is part of AutoOpts, a companion to AutoGen.
13 * AutoOpts is free software.
14 * AutoOpts is copyright (c) 1992-2009 by Bruce Korb - all rights reserved
16 * AutoOpts is available under any one of two licenses. The license
17 * in use must be one of these two and the choice is under the control
18 * of the user of the license.
20 * The GNU Lesser General Public License, version 3 or later
21 * See the files "COPYING.lgplv3" and "COPYING.gplv3"
23 * The Modified Berkeley Software Distribution License
24 * See the file "COPYING.mbsd"
26 * These files have the following md5sums:
28 * 43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3
29 * 06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3
30 * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
33 static char const zWarn
[] = "%s WARNING: cannot save options - ";
34 static char const close_xml
[] = "</%s>\n";
36 /* = = = START-STATIC-FORWARD = = = */
37 /* static forward declarations maintained by mk-fwd */
39 findDirName( tOptions
* pOpts
, int* p_free
);
42 findFileName( tOptions
* pOpts
, int* p_free_name
);
51 print_a_value(FILE * fp
, int depth
, tOptDesc
* pOD
, tOptionValue
const * ovp
);
54 print_a_string(FILE * fp
, char const * name
, char const * pz
);
57 printValueList(FILE * fp
, char const * name
, tArgList
* al
);
60 printHierarchy(FILE * fp
, tOptDesc
* p
);
63 openSaveFile( tOptions
* pOpts
);
66 printNoArgOpt(FILE * fp
, tOptDesc
* p
, tOptDesc
* pOD
);
69 printStringArg(FILE * fp
, tOptDesc
* pOD
);
72 printEnumArg(FILE * fp
, tOptDesc
* pOD
);
75 printSetMemberArg(FILE * fp
, tOptDesc
* pOD
);
78 printFileArg(FILE * fp
, tOptDesc
* pOD
, tOptions
* pOpts
);
79 /* = = = END-STATIC-FORWARD = = = */
82 findDirName( tOptions
* pOpts
, int* p_free
)
86 if ( (pOpts
->specOptIdx
.save_opts
== NO_EQUIVALENT
)
87 || (pOpts
->specOptIdx
.save_opts
== 0))
90 pzDir
= pOpts
->pOptDesc
[ pOpts
->specOptIdx
.save_opts
].optArg
.argString
;
91 if ((pzDir
!= NULL
) && (*pzDir
!= NUL
))
95 * This function only works if there is a directory where
96 * we can stash the RC (INI) file.
99 tCC
* const* papz
= pOpts
->papzHomeList
;
103 while (papz
[1] != NULL
) papz
++;
108 * IF it does not require deciphering an env value, then just copy it
114 tCC
* pzEndDir
= strchr( ++pzDir
, DIRCH
);
118 if (pzEndDir
!= NULL
) {
119 char z
[ AO_NAME_SIZE
];
120 if ((pzEndDir
- pzDir
) > AO_NAME_LIMIT
)
122 strncpy( z
, pzDir
, (size_t)(pzEndDir
- pzDir
) );
123 z
[ (pzEndDir
- pzDir
) ] = NUL
;
128 * Make sure we can get the env value (after stripping off
129 * any trailing directory or file names)
131 pzEnv
= getenv( pzDir
);
135 fprintf( stderr
, zWarn
, pOpts
->pzProgName
);
136 fprintf( stderr
, zNotDef
, pzDir
);
140 if (pzEndDir
== NULL
)
144 size_t sz
= strlen( pzEnv
) + strlen( pzEndDir
) + 2;
145 pzFileName
= (char*)AGALOC( sz
, "dir name" );
148 if (pzFileName
== NULL
)
153 * Glue together the full name into the allocated memory.
154 * FIXME: We lose track of this memory.
156 sprintf( pzFileName
, "%s/%s", pzEnv
, pzEndDir
);
163 findFileName( tOptions
* pOpts
, int* p_free_name
)
167 int free_dir_name
= 0;
169 pzDir
= findDirName( pOpts
, &free_dir_name
);
174 * See if we can find the specified directory. We use a once-only loop
175 * structure so we can bail out early.
177 if (stat( pzDir
, &stBuf
) != 0) do {
180 * IF we could not, check to see if we got a full
181 * path to a file name that has not been created yet.
183 if (errno
== ENOENT
) {
187 * Strip off the last component, stat the remaining string and
188 * that string must name a directory
190 char* pzDirCh
= strrchr( pzDir
, DIRCH
);
191 if (pzDirCh
== NULL
) {
192 stBuf
.st_mode
= S_IFREG
;
193 continue; /* bail out of error condition */
196 strncpy( z
, pzDir
, (size_t)(pzDirCh
- pzDir
));
197 z
[ pzDirCh
- pzDir
] = NUL
;
199 if ( (stat( z
, &stBuf
) == 0)
200 && S_ISDIR( stBuf
.st_mode
)) {
203 * We found the directory. Restore the file name and
204 * mark the full name as a regular file
206 stBuf
.st_mode
= S_IFREG
;
207 continue; /* bail out of error condition */
212 * We got a bogus name.
214 fprintf( stderr
, zWarn
, pOpts
->pzProgName
);
215 fprintf( stderr
, zNoStat
, errno
, strerror( errno
), pzDir
);
217 AGFREE( (void*)pzDir
);
222 * IF what we found was a directory,
223 * THEN tack on the config file name
225 if (S_ISDIR( stBuf
.st_mode
)) {
226 size_t sz
= strlen( pzDir
) + strlen( pOpts
->pzRcName
) + 2;
229 char* pzPath
= (char*)AGALOC( sz
, "file name" );
231 snprintf( pzPath
, sz
, "%s/%s", pzDir
, pOpts
->pzRcName
);
233 sprintf( pzPath
, "%s/%s", pzDir
, pOpts
->pzRcName
);
236 AGFREE( (void*)pzDir
);
242 * IF we cannot stat the object for any reason other than
243 * it does not exist, then we bail out
245 if (stat( pzDir
, &stBuf
) != 0) {
246 if (errno
!= ENOENT
) {
247 fprintf( stderr
, zWarn
, pOpts
->pzProgName
);
248 fprintf( stderr
, zNoStat
, errno
, strerror( errno
),
250 AGFREE( (void*)pzDir
);
255 * It does not exist yet, but it will be a regular file
257 stBuf
.st_mode
= S_IFREG
;
262 * Make sure that whatever we ultimately found, that it either is
263 * or will soon be a file.
265 if (! S_ISREG( stBuf
.st_mode
)) {
266 fprintf( stderr
, zWarn
, pOpts
->pzProgName
);
267 fprintf( stderr
, zNotFile
, pzDir
);
269 AGFREE( (void*)pzDir
);
274 * Get rid of the old file
277 *p_free_name
= free_dir_name
;
289 * There is an argument. Pad the name so values line up.
290 * Not disabled *OR* this got equivalenced to another opt,
291 * then use current option name.
292 * Otherwise, there must be a disablement name.
296 if (! DISABLED_OPT(p
) || (p
->optEquivIndex
!= NO_EQUIVALENT
))
299 pz
= p
->pz_DisableName
;
301 fprintf(fp
, "%-18s", pz
);
304 * IF the option is numeric only,
305 * THEN the char pointer is really the number
307 if (OPTST_GET_ARGTYPE(p
->fOptState
) == OPARG_TYPE_NUMERIC
)
308 fprintf( fp
, " %d\n", (int)(t_word
)pzLA
);
311 * OTHERWISE, FOR each line of the value text, ...
313 else if (pzLA
== NULL
)
317 fputc( ' ', fp
); fputc( ' ', fp
);
319 tCC
* pzNl
= strchr( pzLA
, '\n' );
322 * IF this is the last line
323 * THEN bail and print it
329 * Print the continuation and the text from the current line
331 (void)fwrite( pzLA
, (size_t)(pzNl
- pzLA
), (size_t)1, fp
);
332 pzLA
= pzNl
+1; /* advance the Last Arg pointer */
337 * Terminate the entry
346 print_a_value(FILE * fp
, int depth
, tOptDesc
* pOD
, tOptionValue
const * ovp
)
348 static char const bool_atr
[] = "<%1$s type=boolean>%2$s</%1$s>\n";
349 static char const numb_atr
[] = "<%1$s type=integer>0x%2$lX</%1$s>\n";
350 static char const type_atr
[] = "<%s type=%s>";
351 static char const null_atr
[] = "<%s/>\n";
354 putc(' ', fp
), putc(' ', fp
);
356 switch (ovp
->valType
) {
358 case OPARG_TYPE_NONE
:
359 fprintf(fp
, null_atr
, ovp
->pzName
);
362 case OPARG_TYPE_STRING
:
363 print_a_string(fp
, ovp
->pzName
, ovp
->v
.strVal
);
366 case OPARG_TYPE_ENUMERATION
:
367 case OPARG_TYPE_MEMBERSHIP
:
369 tAoUI opt_state
= pOD
->fOptState
;
370 uintptr_t val
= pOD
->optArg
.argEnum
;
371 char const * typ
= (ovp
->valType
== OPARG_TYPE_ENUMERATION
)
372 ? "keyword" : "set-membership";
374 fprintf(fp
, type_atr
, ovp
->pzName
, typ
);
377 * This is a magic incantation that will convert the
378 * bit flag values back into a string suitable for printing.
380 (*(pOD
->pOptProc
))(OPTPROC_RETURN_VALNAME
, pOD
);
381 if (pOD
->optArg
.argString
!= NULL
) {
382 fputs(pOD
->optArg
.argString
, fp
);
384 if (ovp
->valType
!= OPARG_TYPE_ENUMERATION
) {
386 * set membership strings get allocated
388 AGFREE( (void*)pOD
->optArg
.argString
);
392 pOD
->optArg
.argEnum
= val
;
393 pOD
->fOptState
= opt_state
;
394 fprintf(fp
, close_xml
, ovp
->pzName
);
399 case OPARG_TYPE_NUMERIC
:
400 fprintf(fp
, numb_atr
, ovp
->pzName
, ovp
->v
.longVal
);
403 case OPARG_TYPE_BOOLEAN
:
404 fprintf(fp
, bool_atr
, ovp
->pzName
,
405 ovp
->v
.boolVal
? "true" : "false");
408 case OPARG_TYPE_HIERARCHY
:
409 printValueList(fp
, ovp
->pzName
, ovp
->v
.nestVal
);
416 print_a_string(FILE * fp
, char const * name
, char const * pz
)
418 static char const open_atr
[] = "<%s>";
420 fprintf(fp
, open_atr
, name
);
422 int ch
= ((int)*(pz
++)) & 0xFF;
425 case NUL
: goto string_done
;
431 case 1 ... (' ' - 1):
432 case ('~' + 1) ... 0xFF:
434 emit_special_char(fp
, ch
);
439 if ( ((ch
>= 1) && (ch
<= (' ' - 1)))
440 || ((ch
>= ('~' + 1)) && (ch
<= 0xFF)) ) {
441 emit_special_char(fp
, ch
);
448 fprintf(fp
, close_xml
, name
);
453 printValueList(FILE * fp
, char const * name
, tArgList
* al
)
455 static int depth
= 1;
464 opt_list
= (void **)al
->apzArgs
;
467 fprintf(fp
, "<%s/>\n", name
);
471 fprintf(fp
, "<%s type=nested>\n", name
);
474 while (--opt_ct
>= 0) {
475 tOptionValue
const * ovp
= *(opt_list
++);
477 print_a_value(fp
, depth
, NULL
, ovp
);
481 for (sp_ct
= depth
; --sp_ct
>= 0;)
482 putc(' ', fp
), putc(' ', fp
);
483 fprintf(fp
, "</%s>\n", name
);
488 printHierarchy(FILE * fp
, tOptDesc
* p
)
491 tArgList
* al
= p
->optCookie
;
498 opt_list
= (void **)al
->apzArgs
;
504 tOptionValue
const * base
= *(opt_list
++);
505 tOptionValue
const * ovp
= optionGetValue(base
, NULL
);
510 fprintf(fp
, "<%s type=nested>\n", p
->pz_Name
);
513 print_a_value(fp
, 1, p
, ovp
);
515 } while (ovp
= optionNextValue(base
, ovp
),
518 fprintf(fp
, "</%s>\n", p
->pz_Name
);
519 } while (--opt_ct
> 0);
524 openSaveFile( tOptions
* pOpts
)
530 tCC
* pzFName
= findFileName( pOpts
, &free_name
);
534 fp
= fopen( pzFName
, "w" FOPEN_BINARY_FLAG
);
536 fprintf( stderr
, zWarn
, pOpts
->pzProgName
);
537 fprintf( stderr
, zNoCreat
, errno
, strerror( errno
), pzFName
);
539 AGFREE((void*) pzFName
);
544 AGFREE( (void*)pzFName
);
548 char const* pz
= pOpts
->pzUsageTitle
;
550 do { fputc( *pz
, fp
); } while (*(pz
++) != '\n');
554 time_t timeVal
= time( NULL
);
555 char* pzTime
= ctime( &timeVal
);
557 fprintf( fp
, zPresetFile
, pzTime
);
558 #ifdef HAVE_ALLOCATED_CTIME
560 * The return values for ctime(), localtime(), and gmtime()
561 * normally point to static data that is overwritten by each call.
562 * The test to detect allocated ctime, so we leak the memory.
564 AGFREE( (void*)pzTime
);
572 printNoArgOpt(FILE * fp
, tOptDesc
* p
, tOptDesc
* pOD
)
575 * The aliased to argument indicates whether or not the option
576 * is "disabled". However, the original option has the name
577 * string, so we get that there, not with "p".
580 (DISABLED_OPT( p
)) ? pOD
->pz_DisableName
: pOD
->pz_Name
;
582 * If the option was disabled and the disablement name is NULL,
583 * then the disablement was caused by aliasing.
584 * Use the name as the string to emit.
589 fprintf(fp
, "%s\n", pznm
);
593 printStringArg(FILE * fp
, tOptDesc
* pOD
)
595 if (pOD
->fOptState
& OPTST_STACKED
) {
596 tArgList
* pAL
= (tArgList
*)pOD
->optCookie
;
597 int uct
= pAL
->useCt
;
598 tCC
** ppz
= pAL
->apzArgs
;
601 * un-disable multiple copies of disabled options.
604 pOD
->fOptState
&= ~OPTST_DISABLED
;
607 printEntry( fp
, pOD
, *(ppz
++) );
609 printEntry( fp
, pOD
, pOD
->optArg
.argString
);
614 printEnumArg(FILE * fp
, tOptDesc
* pOD
)
616 uintptr_t val
= pOD
->optArg
.argEnum
;
619 * This is a magic incantation that will convert the
620 * bit flag values back into a string suitable for printing.
622 (*(pOD
->pOptProc
))(OPTPROC_RETURN_VALNAME
, pOD
);
623 printEntry( fp
, pOD
, (void*)(pOD
->optArg
.argString
));
625 pOD
->optArg
.argEnum
= val
;
629 printSetMemberArg(FILE * fp
, tOptDesc
* pOD
)
631 uintptr_t val
= pOD
->optArg
.argEnum
;
634 * This is a magic incantation that will convert the
635 * bit flag values back into a string suitable for printing.
637 (*(pOD
->pOptProc
))(OPTPROC_RETURN_VALNAME
, pOD
);
638 printEntry( fp
, pOD
, (void*)(pOD
->optArg
.argString
));
640 if (pOD
->optArg
.argString
!= NULL
) {
642 * set membership strings get allocated
644 AGFREE( (void*)pOD
->optArg
.argString
);
645 pOD
->fOptState
&= ~OPTST_ALLOC_ARG
;
648 pOD
->optArg
.argEnum
= val
;
652 printFileArg(FILE * fp
, tOptDesc
* pOD
, tOptions
* pOpts
)
655 * If the cookie is not NULL, then it has the file name, period.
656 * Otherwise, if we have a non-NULL string argument, then....
658 if (pOD
->optCookie
!= NULL
)
659 printEntry(fp
, pOD
, pOD
->optCookie
);
661 else if (HAS_originalOptArgArray(pOpts
)) {
663 pOpts
->originalOptArgArray
[pOD
->optIndex
].argString
;
665 if (pOD
->optArg
.argString
== orig
)
668 printEntry(fp
, pOD
, pOD
->optArg
.argString
);
673 /*=export_func optionSaveFile
675 * what: saves the option state to a file
677 * arg: tOptions*, pOpts, program options descriptor
681 * This routine will save the state of option processing to a file. The name
682 * of that file can be specified with the argument to the @code{--save-opts}
683 * option, or by appending the @code{rcfile} attribute to the last
684 * @code{homerc} attribute. If no @code{rcfile} attribute was specified, it
685 * will default to @code{.@i{programname}rc}. If you wish to specify another
686 * file, you should invoke the @code{SET_OPT_SAVE_OPTS( @i{filename} )} macro.
688 * The recommend usage is as follows:
690 * optionProcess(&progOptions, argc, argv);
691 * if (i_want_a_non_standard_place_for_this)
692 * SET_OPT_SAVE_OPTS("myfilename");
693 * optionSaveFile(&progOptions);
698 * If no @code{homerc} file was specified, this routine will silently return
699 * and do nothing. If the output file cannot be created or updated, a message
700 * will be printed to @code{stderr} and the routine will return.
703 optionSaveFile( tOptions
* pOpts
)
707 FILE* fp
= openSaveFile(pOpts
);
713 * FOR each of the defined options, ...
715 ct
= pOpts
->presetOptCt
;
716 pOD
= pOpts
->pOptDesc
;
721 * IF the option has not been defined
722 * OR it does not take an initialization value
723 * OR it is equivalenced to another option
724 * THEN continue (ignore it)
726 * Equivalenced options get picked up when the equivalenced-to
727 * option is processed.
729 if (UNUSED_OPT( pOD
))
732 if ((pOD
->fOptState
& OPTST_DO_NOT_SAVE_MASK
) != 0)
735 if ( (pOD
->optEquivIndex
!= NO_EQUIVALENT
)
736 && (pOD
->optEquivIndex
!= pOD
->optIndex
))
740 * The option argument data are found at the equivalenced-to option,
741 * but the actual option argument type comes from the original
742 * option descriptor. Be careful!
744 p
= ((pOD
->fOptState
& OPTST_EQUIVALENCE
) != 0)
745 ? (pOpts
->pOptDesc
+ pOD
->optActualIndex
) : pOD
;
747 switch (OPTST_GET_ARGTYPE(pOD
->fOptState
)) {
748 case OPARG_TYPE_NONE
:
749 printNoArgOpt(fp
, p
, pOD
);
752 case OPARG_TYPE_NUMERIC
:
753 printEntry( fp
, p
, (void*)(p
->optArg
.argInt
));
756 case OPARG_TYPE_STRING
:
757 printStringArg(fp
, p
);
760 case OPARG_TYPE_ENUMERATION
:
764 case OPARG_TYPE_MEMBERSHIP
:
765 printSetMemberArg(fp
, p
);
768 case OPARG_TYPE_BOOLEAN
:
769 printEntry( fp
, p
, p
->optArg
.argBool
? "true" : "false" );
772 case OPARG_TYPE_HIERARCHY
:
773 printHierarchy(fp
, p
);
776 case OPARG_TYPE_FILE
:
777 printFileArg(fp
, p
, pOpts
);
781 break; /* cannot handle - skip it */
783 } while ( (pOD
++), (--ct
> 0));
790 * c-file-style: "stroustrup"
791 * indent-tabs-mode: nil
793 * end of autoopts/save.c */