1
2 /*
3 * \file save.c
4 *
5 * This module's routines will take the currently set options and
6 * store them into an ".rc" file for re-interpretation the next
7 * time the invoking program is run.
8 *
9 * @addtogroup autoopts
10 * @{
11 */
12 /*
13 * This file is part of AutoOpts, a companion to AutoGen.
14 * AutoOpts is free software.
15 * AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved
16 *
17 * AutoOpts is available under any one of two licenses. The license
18 * in use must be one of these two and the choice is under the control
19 * of the user of the license.
20 *
21 * The GNU Lesser General Public License, version 3 or later
22 * See the files "COPYING.lgplv3" and "COPYING.gplv3"
23 *
24 * The Modified Berkeley Software Distribution License
25 * See the file "COPYING.mbsd"
26 *
27 * These files have the following sha256 sums:
28 *
29 * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3
30 * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3
31 * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd
32 */
33
34 /* = = = START-STATIC-FORWARD = = = */
35 static char const *
36 find_dir_name(tOptions * opts, int * p_free);
37
38 static char const *
39 find_file_name(tOptions * opts, int * p_free_name);
40
41 static void
42 prt_entry(FILE * fp, tOptDesc * od, char const * l_arg);
43
44 static void
45 prt_value(FILE * fp, int depth, tOptDesc * pOD, tOptionValue const * ovp);
46
47 static void
48 prt_string(FILE * fp, char const * name, char const * pz);
49
50 static void
51 prt_val_list(FILE * fp, char const * name, tArgList * al);
52
53 static void
54 prt_nested(FILE * fp, tOptDesc * p);
55
56 static FILE *
57 open_sv_file(tOptions * opts);
58
59 static void
60 prt_no_arg_opt(FILE * fp, tOptDesc * p, tOptDesc * pOD);
61
62 static void
63 prt_str_arg(FILE * fp, tOptDesc * pOD);
64
65 static void
66 prt_enum_arg(FILE * fp, tOptDesc * od);
67
68 static void
69 prt_set_arg(FILE * fp, tOptDesc * od);
70
71 static void
72 prt_file_arg(FILE * fp, tOptDesc * od, tOptions * opts);
73 /* = = = END-STATIC-FORWARD = = = */
74
75 /**
76 */
77 static char const *
find_dir_name(tOptions * opts,int * p_free)78 find_dir_name(tOptions * opts, int * p_free)
79 {
80 char const * pzDir;
81
82 if ( (opts->specOptIdx.save_opts == NO_EQUIVALENT)
83 || (opts->specOptIdx.save_opts == 0))
84 return NULL;
85
86 pzDir = opts->pOptDesc[ opts->specOptIdx.save_opts ].optArg.argString;
87 if ((pzDir != NULL) && (*pzDir != NUL))
88 return pzDir;
89
90 /*
91 * This function only works if there is a directory where
92 * we can stash the RC (INI) file.
93 */
94 {
95 char const * const * papz = opts->papzHomeList;
96 if (papz == NULL)
97 return NULL;
98
99 while (papz[1] != NULL) papz++;
100 pzDir = *papz;
101 }
102
103 /*
104 * IF it does not require deciphering an env value, then just copy it
105 */
106 if (*pzDir != '$')
107 return pzDir;
108
109 {
110 char const * pzEndDir = strchr(++pzDir, DIRCH);
111 char * pzFileName;
112 char * pzEnv;
113
114 if (pzEndDir != NULL) {
115 char z[ AO_NAME_SIZE ];
116 if ((pzEndDir - pzDir) > AO_NAME_LIMIT )
117 return NULL;
118 memcpy(z, pzDir, (size_t)(pzEndDir - pzDir));
119 z[pzEndDir - pzDir] = NUL;
120 pzEnv = getenv(z);
121 } else {
122
123 /*
124 * Make sure we can get the env value (after stripping off
125 * any trailing directory or file names)
126 */
127 pzEnv = getenv(pzDir);
128 }
129
130 if (pzEnv == NULL) {
131 fprintf(stderr, zsave_warn, opts->pzProgName);
132 fprintf(stderr, zNotDef, pzDir);
133 return NULL;
134 }
135
136 if (pzEndDir == NULL)
137 return pzEnv;
138
139 {
140 size_t sz = strlen(pzEnv) + strlen(pzEndDir) + 2;
141 pzFileName = (char *)AGALOC(sz, "dir name");
142 }
143
144 if (pzFileName == NULL)
145 return NULL;
146
147 *p_free = 1;
148 /*
149 * Glue together the full name into the allocated memory.
150 * FIXME: We lose track of this memory.
151 */
152 sprintf(pzFileName, "%s/%s", pzEnv, pzEndDir);
153 return pzFileName;
154 }
155 }
156
157 /**
158 */
159 static char const *
find_file_name(tOptions * opts,int * p_free_name)160 find_file_name(tOptions * opts, int * p_free_name)
161 {
162 struct stat stBuf;
163 int free_dir_name = 0;
164
165 char const * pzDir = find_dir_name(opts, &free_dir_name);
166 if (pzDir == NULL)
167 return NULL;
168
169 /*
170 * See if we can find the specified directory. We use a once-only loop
171 * structure so we can bail out early.
172 */
173 if (stat(pzDir, &stBuf) != 0) do {
174 char z[AG_PATH_MAX];
175 char * dirchp;
176
177 /*
178 * IF we could not, check to see if we got a full
179 * path to a file name that has not been created yet.
180 */
181 if (errno != ENOENT) {
182 bogus_name:
183 fprintf(stderr, zsave_warn, opts->pzProgName);
184 fprintf(stderr, zNoStat, errno, strerror(errno), pzDir);
185 if (free_dir_name)
186 AGFREE(pzDir);
187 return NULL;
188 }
189
190 /*
191 * Strip off the last component, stat the remaining string and
192 * that string must name a directory
193 */
194 dirchp = strrchr(pzDir, DIRCH);
195 if (dirchp == NULL) {
196 stBuf.st_mode = S_IFREG;
197 break; /* found directory -- viz., "." */
198 }
199
200 if ((size_t)(dirchp - pzDir) >= sizeof(z))
201 goto bogus_name;
202
203 memcpy(z, pzDir, (size_t)(dirchp - pzDir));
204 z[dirchp - pzDir] = NUL;
205
206 if ((stat(z, &stBuf) != 0) || ! S_ISDIR(stBuf.st_mode))
207 goto bogus_name;
208 stBuf.st_mode = S_IFREG; /* file within this directory */
209 } while (false);
210
211 /*
212 * IF what we found was a directory,
213 * THEN tack on the config file name
214 */
215 if (S_ISDIR(stBuf.st_mode)) {
216 size_t sz = strlen(pzDir) + strlen(opts->pzRcName) + 2;
217
218 {
219 char * pzPath = (char *)AGALOC(sz, "file name");
220 #ifdef HAVE_SNPRINTF
221 snprintf(pzPath, sz, "%s/%s", pzDir, opts->pzRcName);
222 #else
223 sprintf(pzPath, "%s/%s", pzDir, opts->pzRcName);
224 #endif
225 if (free_dir_name)
226 AGFREE(pzDir);
227 pzDir = pzPath;
228 free_dir_name = 1;
229 }
230
231 /*
232 * IF we cannot stat the object for any reason other than
233 * it does not exist, then we bail out
234 */
235 if (stat(pzDir, &stBuf) != 0) {
236 if (errno != ENOENT) {
237 fprintf(stderr, zsave_warn, opts->pzProgName);
238 fprintf(stderr, zNoStat, errno, strerror(errno),
239 pzDir);
240 AGFREE(pzDir);
241 return NULL;
242 }
243
244 /*
245 * It does not exist yet, but it will be a regular file
246 */
247 stBuf.st_mode = S_IFREG;
248 }
249 }
250
251 /*
252 * Make sure that whatever we ultimately found, that it either is
253 * or will soon be a file.
254 */
255 if (! S_ISREG(stBuf.st_mode)) {
256 fprintf(stderr, zsave_warn, opts->pzProgName, pzDir);
257 if (free_dir_name)
258 AGFREE(pzDir);
259 return NULL;
260 }
261
262 /*
263 * Get rid of the old file
264 */
265 unlink(pzDir);
266 *p_free_name = free_dir_name;
267 return pzDir;
268 }
269
270 /**
271 * print one option entry to the save file.
272 *
273 * @param[in] fp the file pointer for the save file
274 * @param[in] od the option descriptor to print
275 * @param[in] l_arg the last argument for the option
276 */
277 static void
prt_entry(FILE * fp,tOptDesc * od,char const * l_arg)278 prt_entry(FILE * fp, tOptDesc * od, char const * l_arg)
279 {
280 int space_ct;
281
282 /*
283 * There is an argument. Pad the name so values line up.
284 * Not disabled *OR* this got equivalenced to another opt,
285 * then use current option name.
286 * Otherwise, there must be a disablement name.
287 */
288 {
289 char const * pz =
290 (! DISABLED_OPT(od) || (od->optEquivIndex != NO_EQUIVALENT))
291 ? od->pz_Name
292 : od->pz_DisableName;
293 space_ct = 17 - strlen(pz);
294 fputs(pz, fp);
295 }
296
297 if ( (l_arg == NULL)
298 && (OPTST_GET_ARGTYPE(od->fOptState) != OPARG_TYPE_NUMERIC))
299 goto end_entry;
300
301 fputs(" = ", fp);
302 while (space_ct-- > 0) fputc(' ', fp);
303
304 /*
305 * IF the option is numeric only,
306 * THEN the char pointer is really the number
307 */
308 if (OPTST_GET_ARGTYPE(od->fOptState) == OPARG_TYPE_NUMERIC)
309 fprintf(fp, "%d", (int)(intptr_t)l_arg);
310
311 else {
312 for (;;) {
313 char const * eol = strchr(l_arg, NL);
314
315 /*
316 * IF this is the last line
317 * THEN bail and print it
318 */
319 if (eol == NULL)
320 break;
321
322 /*
323 * Print the continuation and the text from the current line
324 */
325 (void)fwrite(l_arg, (size_t)(eol - l_arg), (size_t)1, fp);
326 l_arg = eol+1; /* advance the Last Arg pointer */
327 fputs("\\\n", fp);
328 }
329
330 /*
331 * Terminate the entry
332 */
333 fputs(l_arg, fp);
334 }
335
336 end_entry:
337 fputc(NL, fp);
338 }
339
340 /**
341 */
342 static void
prt_value(FILE * fp,int depth,tOptDesc * pOD,tOptionValue const * ovp)343 prt_value(FILE * fp, int depth, tOptDesc * pOD, tOptionValue const * ovp)
344 {
345 while (--depth >= 0)
346 putc(' ', fp), putc(' ', fp);
347
348 switch (ovp->valType) {
349 default:
350 case OPARG_TYPE_NONE:
351 fprintf(fp, NULL_ATR_FMT, ovp->pzName);
352 break;
353
354 case OPARG_TYPE_STRING:
355 prt_string(fp, ovp->pzName, ovp->v.strVal);
356 break;
357
358 case OPARG_TYPE_ENUMERATION:
359 case OPARG_TYPE_MEMBERSHIP:
360 if (pOD != NULL) {
361 uint32_t opt_state = pOD->fOptState;
362 uintptr_t val = pOD->optArg.argEnum;
363 char const * typ = (ovp->valType == OPARG_TYPE_ENUMERATION)
364 ? "keyword" : "set-membership";
365
366 fprintf(fp, TYPE_ATR_FMT, ovp->pzName, typ);
367
368 /*
369 * This is a magic incantation that will convert the
370 * bit flag values back into a string suitable for printing.
371 */
372 (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD );
373 if (pOD->optArg.argString != NULL) {
374 fputs(pOD->optArg.argString, fp);
375
376 if (ovp->valType != OPARG_TYPE_ENUMERATION) {
377 /*
378 * set membership strings get allocated
379 */
380 AGFREE(pOD->optArg.argString);
381 }
382 }
383
384 pOD->optArg.argEnum = val;
385 pOD->fOptState = opt_state;
386 fprintf(fp, END_XML_FMT, ovp->pzName);
387 break;
388 }
389 /* FALLTHROUGH */
390
391 case OPARG_TYPE_NUMERIC:
392 fprintf(fp, NUMB_ATR_FMT, ovp->pzName, ovp->v.longVal);
393 break;
394
395 case OPARG_TYPE_BOOLEAN:
396 fprintf(fp, BOOL_ATR_FMT, ovp->pzName,
397 ovp->v.boolVal ? "true" : "false");
398 break;
399
400 case OPARG_TYPE_HIERARCHY:
401 prt_val_list(fp, ovp->pzName, ovp->v.nestVal);
402 break;
403 }
404 }
405
406 /**
407 */
408 static void
prt_string(FILE * fp,char const * name,char const * pz)409 prt_string(FILE * fp, char const * name, char const * pz)
410 {
411 fprintf(fp, OPEN_XML_FMT, name);
412 for (;;) {
413 int ch = ((int)*(pz++)) & 0xFF;
414
415 switch (ch) {
416 case NUL: goto string_done;
417
418 case '&':
419 case '<':
420 case '>':
421 #if __GNUC__ >= 4
422 case 1 ... (' ' - 1):
423 case ('~' + 1) ... 0xFF:
424 #endif
425 emit_special_char(fp, ch);
426 break;
427
428 default:
429 #if __GNUC__ < 4
430 if ( ((ch >= 1) && (ch <= (' ' - 1)))
431 || ((ch >= ('~' + 1)) && (ch <= 0xFF)) ) {
432 emit_special_char(fp, ch);
433 break;
434 }
435 #endif
436 putc(ch, fp);
437 }
438 } string_done:;
439 fprintf(fp, END_XML_FMT, name);
440 }
441
442 /**
443 */
444 static void
prt_val_list(FILE * fp,char const * name,tArgList * al)445 prt_val_list(FILE * fp, char const * name, tArgList * al)
446 {
447 static int depth = 1;
448
449 int sp_ct;
450 int opt_ct;
451 void ** opt_list;
452
453 if (al == NULL)
454 return;
455 opt_ct = al->useCt;
456 opt_list = VOIDP(al->apzArgs);
457
458 if (opt_ct <= 0) {
459 fprintf(fp, OPEN_CLOSE_FMT, name);
460 return;
461 }
462
463 fprintf(fp, NESTED_OPT_FMT, name);
464
465 depth++;
466 while (--opt_ct >= 0) {
467 tOptionValue const * ovp = *(opt_list++);
468
469 prt_value(fp, depth, NULL, ovp);
470 }
471 depth--;
472
473 for (sp_ct = depth; --sp_ct >= 0;)
474 putc(' ', fp), putc(' ', fp);
475 fprintf(fp, "</%s>\n", name);
476 }
477
478 /**
479 */
480 static void
prt_nested(FILE * fp,tOptDesc * p)481 prt_nested(FILE * fp, tOptDesc * p)
482 {
483 int opt_ct;
484 tArgList * al = p->optCookie;
485 void ** opt_list;
486
487 if (al == NULL)
488 return;
489
490 opt_ct = al->useCt;
491 opt_list = VOIDP(al->apzArgs);
492
493 if (opt_ct <= 0)
494 return;
495
496 do {
497 tOptionValue const * base = *(opt_list++);
498 tOptionValue const * ovp = optionGetValue(base, NULL);
499
500 if (ovp == NULL)
501 continue;
502
503 fprintf(fp, NESTED_OPT_FMT, p->pz_Name);
504
505 do {
506 prt_value(fp, 1, p, ovp);
507
508 } while (ovp = optionNextValue(base, ovp),
509 ovp != NULL);
510
511 fprintf(fp, "</%s>\n", p->pz_Name);
512 } while (--opt_ct > 0);
513 }
514
515 /**
516 * open the file for saving option state.
517 *
518 * @param[in] opts the program options structure
519 * @returns the open file pointer. It may be NULL.
520 */
521 static FILE *
open_sv_file(tOptions * opts)522 open_sv_file(tOptions * opts)
523 {
524 FILE * fp;
525
526 {
527 int free_name = 0;
528 char const * pzFName = find_file_name(opts, &free_name);
529 if (pzFName == NULL)
530 return NULL;
531
532 fp = fopen(pzFName, "w" FOPEN_BINARY_FLAG);
533 if (fp == NULL) {
534 fprintf(stderr, zsave_warn, opts->pzProgName);
535 fprintf(stderr, zNoCreat, errno, strerror(errno), pzFName);
536 if (free_name)
537 AGFREE(pzFName);
538 return fp;
539 }
540
541 if (free_name)
542 AGFREE(pzFName);
543 }
544
545 fputs("# ", fp);
546 {
547 char const * e = strchr(opts->pzUsageTitle, NL);
548 if (e++ != NULL)
549 fwrite(opts->pzUsageTitle, 1, e - opts->pzUsageTitle, fp);
550 }
551
552 {
553 time_t cur_time = time(NULL);
554 char * time_str = ctime(&cur_time);
555
556 fprintf(fp, zPresetFile, time_str);
557 #ifdef HAVE_ALLOCATED_CTIME
558 /*
559 * The return values for ctime(), localtime(), and gmtime()
560 * normally point to static data that is overwritten by each call.
561 * The test to detect allocated ctime, so we leak the memory.
562 */
563 AGFREE(time_str);
564 #endif
565 }
566
567 return fp;
568 }
569
570 /**
571 */
572 static void
prt_no_arg_opt(FILE * fp,tOptDesc * p,tOptDesc * pOD)573 prt_no_arg_opt(FILE * fp, tOptDesc * p, tOptDesc * pOD)
574 {
575 /*
576 * The aliased to argument indicates whether or not the option
577 * is "disabled". However, the original option has the name
578 * string, so we get that there, not with "p".
579 */
580 char const * pznm =
581 (DISABLED_OPT(p)) ? pOD->pz_DisableName : pOD->pz_Name;
582 /*
583 * If the option was disabled and the disablement name is NULL,
584 * then the disablement was caused by aliasing.
585 * Use the name as the string to emit.
586 */
587 if (pznm == NULL)
588 pznm = pOD->pz_Name;
589
590 fprintf(fp, "%s\n", pznm);
591 }
592
593 /**
594 */
595 static void
prt_str_arg(FILE * fp,tOptDesc * pOD)596 prt_str_arg(FILE * fp, tOptDesc * pOD)
597 {
598 if (pOD->fOptState & OPTST_STACKED) {
599 tArgList * pAL = (tArgList *)pOD->optCookie;
600 int uct = pAL->useCt;
601 char const ** ppz = pAL->apzArgs;
602
603 /*
604 * un-disable multiple copies of disabled options.
605 */
606 if (uct > 1)
607 pOD->fOptState &= ~OPTST_DISABLED;
608
609 while (uct-- > 0)
610 prt_entry(fp, pOD, *(ppz++));
611 } else {
612 prt_entry(fp, pOD, pOD->optArg.argString);
613 }
614 }
615
616 /**
617 * print the string value of an enumeration.
618 *
619 * @param[in] fp the file pointer to write to
620 * @param[in] od the option descriptor with the enumerated value
621 */
622 static void
prt_enum_arg(FILE * fp,tOptDesc * od)623 prt_enum_arg(FILE * fp, tOptDesc * od)
624 {
625 uintptr_t val = od->optArg.argEnum;
626
627 /*
628 * This is a magic incantation that will convert the
629 * bit flag values back into a string suitable for printing.
630 */
631 (*(od->pOptProc))(OPTPROC_RETURN_VALNAME, od);
632 prt_entry(fp, od, VOIDP(od->optArg.argString));
633
634 od->optArg.argEnum = val;
635 }
636
637 /**
638 * Print the bits set in a bit mask option.
639 * We call the option handling function with a magic value for
640 * the options pointer and it allocates and fills in the string.
641 * We print that with a call to prt_entry().
642 *
643 * @param[in] fp the file pointer to write to
644 * @param[in] od the option descriptor with a bit mask value type
645 */
646 static void
prt_set_arg(FILE * fp,tOptDesc * od)647 prt_set_arg(FILE * fp, tOptDesc * od)
648 {
649 char * list = optionMemberList(od);
650 size_t len = strlen(list);
651 char * buf = (char *)AGALOC(len + 3, "dir name");
652 *buf= '=';
653 memcpy(buf+1, list, len + 1);
654 prt_entry(fp, od, buf);
655 AGFREE(buf);
656 AGFREE(list);
657 }
658
659 /**
660 * figure out what the option file name argument is.
661 * If one can be found, call prt_entry() to emit it.
662 *
663 * @param[in] fp the file pointer to write to.
664 * @param[in] od the option descriptor with a bit mask value type
665 * @param[in] opts the program options descriptor
666 */
667 static void
prt_file_arg(FILE * fp,tOptDesc * od,tOptions * opts)668 prt_file_arg(FILE * fp, tOptDesc * od, tOptions * opts)
669 {
670 /*
671 * If the cookie is not NULL, then it has the file name, period.
672 * Otherwise, if we have a non-NULL string argument, then....
673 */
674 if (od->optCookie != NULL)
675 prt_entry(fp, od, od->optCookie);
676
677 else if (HAS_originalOptArgArray(opts)) {
678 char const * orig =
679 opts->originalOptArgArray[od->optIndex].argString;
680
681 if (od->optArg.argString == orig)
682 return;
683
684 prt_entry(fp, od, od->optArg.argString);
685 }
686 }
687
688 /*=export_func optionSaveFile
689 *
690 * what: saves the option state to a file
691 *
692 * arg: tOptions *, opts, program options descriptor
693 *
694 * doc:
695 *
696 * This routine will save the state of option processing to a file. The name
697 * of that file can be specified with the argument to the @code{--save-opts}
698 * option, or by appending the @code{rcfile} attribute to the last
699 * @code{homerc} attribute. If no @code{rcfile} attribute was specified, it
700 * will default to @code{.@i{programname}rc}. If you wish to specify another
701 * file, you should invoke the @code{SET_OPT_SAVE_OPTS(@i{filename})} macro.
702 *
703 * The recommend usage is as follows:
704 * @example
705 * optionProcess(&progOptions, argc, argv);
706 * if (i_want_a_non_standard_place_for_this)
707 * SET_OPT_SAVE_OPTS("myfilename");
708 * optionSaveFile(&progOptions);
709 * @end example
710 *
711 * err:
712 *
713 * If no @code{homerc} file was specified, this routine will silently return
714 * and do nothing. If the output file cannot be created or updated, a message
715 * will be printed to @code{stderr} and the routine will return.
716 =*/
717 void
optionSaveFile(tOptions * opts)718 optionSaveFile(tOptions * opts)
719 {
720 tOptDesc * od;
721 int ct;
722 FILE * fp = open_sv_file(opts);
723
724 if (fp == NULL)
725 return;
726
727 /*
728 * FOR each of the defined options, ...
729 */
730 ct = opts->presetOptCt;
731 od = opts->pOptDesc;
732 do {
733 tOptDesc * p;
734
735 /*
736 * IF the option has not been defined
737 * OR it does not take an initialization value
738 * OR it is equivalenced to another option
739 * THEN continue (ignore it)
740 *
741 * Equivalenced options get picked up when the equivalenced-to
742 * option is processed.
743 */
744 if (UNUSED_OPT(od))
745 continue;
746
747 if ((od->fOptState & OPTST_DO_NOT_SAVE_MASK) != 0)
748 continue;
749
750 if ( (od->optEquivIndex != NO_EQUIVALENT)
751 && (od->optEquivIndex != od->optIndex))
752 continue;
753
754 /*
755 * The option argument data are found at the equivalenced-to option,
756 * but the actual option argument type comes from the original
757 * option descriptor. Be careful!
758 */
759 p = ((od->fOptState & OPTST_EQUIVALENCE) != 0)
760 ? (opts->pOptDesc + od->optActualIndex) : od;
761
762 switch (OPTST_GET_ARGTYPE(od->fOptState)) {
763 case OPARG_TYPE_NONE:
764 prt_no_arg_opt(fp, p, od);
765 break;
766
767 case OPARG_TYPE_NUMERIC:
768 prt_entry(fp, p, VOIDP(p->optArg.argInt));
769 break;
770
771 case OPARG_TYPE_STRING:
772 prt_str_arg(fp, p);
773 break;
774
775 case OPARG_TYPE_ENUMERATION:
776 prt_enum_arg(fp, p);
777 break;
778
779 case OPARG_TYPE_MEMBERSHIP:
780 prt_set_arg(fp, p);
781 break;
782
783 case OPARG_TYPE_BOOLEAN:
784 prt_entry(fp, p, p->optArg.argBool ? "true" : "false");
785 break;
786
787 case OPARG_TYPE_HIERARCHY:
788 prt_nested(fp, p);
789 break;
790
791 case OPARG_TYPE_FILE:
792 prt_file_arg(fp, p, opts);
793 break;
794
795 default:
796 break; /* cannot handle - skip it */
797 }
798 } while (od++, (--ct > 0));
799
800 fclose(fp);
801 }
802 /** @}
803 *
804 * Local Variables:
805 * mode: C
806 * c-file-style: "stroustrup"
807 * indent-tabs-mode: nil
808 * End:
809 * end of autoopts/save.c */
810