xref: /netbsd-src/external/bsd/ntp/dist/sntp/libopts/save.c (revision c2f76ff004a2cb67efe5b12d97bd3ef7fe89e18d)
1 /*	$NetBSD: save.c,v 1.2 2010/12/04 23:08:36 christos Exp $	*/
2 
3 
4 /*
5  *  save.c  Id: 5a69234fab4c2d8d7eaf4aed4dbb3052ce6be5b6
6  * Time-stamp:      "2009-07-20 20:40:28 bkorb"
7  *
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.
11  *
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
15  *
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.
19  *
20  *   The GNU Lesser General Public License, version 3 or later
21  *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
22  *
23  *   The Modified Berkeley Software Distribution License
24  *      See the file "COPYING.mbsd"
25  *
26  *  These files have the following md5sums:
27  *
28  *  43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3
29  *  06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3
30  *  66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
31  */
32 
33 static char const  zWarn[] = "%s WARNING:  cannot save options - ";
34 static char const close_xml[] = "</%s>\n";
35 
36 /* = = = START-STATIC-FORWARD = = = */
37 /* static forward declarations maintained by mk-fwd */
38 static tCC*
39 findDirName( tOptions* pOpts, int* p_free );
40 
41 static tCC*
42 findFileName( tOptions* pOpts, int* p_free_name );
43 
44 static void
45 printEntry(
46     FILE *     fp,
47     tOptDesc * p,
48     tCC*       pzLA );
49 
50 static void
51 print_a_value(FILE * fp, int depth, tOptDesc * pOD, tOptionValue const * ovp);
52 
53 static void
54 print_a_string(FILE * fp, char const * name, char const * pz);
55 
56 static void
57 printValueList(FILE * fp, char const * name, tArgList * al);
58 
59 static void
60 printHierarchy(FILE * fp, tOptDesc * p);
61 
62 static FILE *
63 openSaveFile( tOptions* pOpts );
64 
65 static void
66 printNoArgOpt(FILE * fp, tOptDesc * p, tOptDesc * pOD);
67 
68 static void
69 printStringArg(FILE * fp, tOptDesc * pOD);
70 
71 static void
72 printEnumArg(FILE * fp, tOptDesc * pOD);
73 
74 static void
75 printSetMemberArg(FILE * fp, tOptDesc * pOD);
76 
77 static void
78 printFileArg(FILE * fp, tOptDesc * pOD, tOptions* pOpts);
79 /* = = = END-STATIC-FORWARD = = = */
80 
81 static tCC*
82 findDirName( tOptions* pOpts, int* p_free )
83 {
84     tCC*  pzDir;
85 
86     if (  (pOpts->specOptIdx.save_opts == NO_EQUIVALENT)
87        || (pOpts->specOptIdx.save_opts == 0))
88         return NULL;
89 
90     pzDir = pOpts->pOptDesc[ pOpts->specOptIdx.save_opts ].optArg.argString;
91     if ((pzDir != NULL) && (*pzDir != NUL))
92         return pzDir;
93 
94     /*
95      *  This function only works if there is a directory where
96      *  we can stash the RC (INI) file.
97      */
98     {
99         tCC* const* papz = pOpts->papzHomeList;
100         if (papz == NULL)
101             return NULL;
102 
103         while (papz[1] != NULL) papz++;
104         pzDir = *papz;
105     }
106 
107     /*
108      *  IF it does not require deciphering an env value, then just copy it
109      */
110     if (*pzDir != '$')
111         return pzDir;
112 
113     {
114         tCC*  pzEndDir = strchr( ++pzDir, DIRCH );
115         char* pzFileName;
116         char* pzEnv;
117 
118         if (pzEndDir != NULL) {
119             char z[ AO_NAME_SIZE ];
120             if ((pzEndDir - pzDir) > AO_NAME_LIMIT )
121                 return NULL;
122             strncpy( z, pzDir, (size_t)(pzEndDir - pzDir) );
123             z[ (pzEndDir - pzDir) ] = NUL;
124             pzEnv = getenv( z );
125         } else {
126 
127             /*
128              *  Make sure we can get the env value (after stripping off
129              *  any trailing directory or file names)
130              */
131             pzEnv = getenv( pzDir );
132         }
133 
134         if (pzEnv == NULL) {
135             fprintf( stderr, zWarn, pOpts->pzProgName );
136             fprintf( stderr, zNotDef, pzDir );
137             return NULL;
138         }
139 
140         if (pzEndDir == NULL)
141             return pzEnv;
142 
143         {
144             size_t sz = strlen( pzEnv ) + strlen( pzEndDir ) + 2;
145             pzFileName = (char*)AGALOC( sz, "dir name" );
146         }
147 
148         if (pzFileName == NULL)
149             return NULL;
150 
151         *p_free = 1;
152         /*
153          *  Glue together the full name into the allocated memory.
154          *  FIXME: We lose track of this memory.
155          */
156         sprintf( pzFileName, "%s/%s", pzEnv, pzEndDir );
157         return pzFileName;
158     }
159 }
160 
161 
162 static tCC*
163 findFileName( tOptions* pOpts, int* p_free_name )
164 {
165     tCC*   pzDir;
166     struct stat stBuf;
167     int    free_dir_name = 0;
168 
169     pzDir = findDirName( pOpts, &free_dir_name );
170     if (pzDir == NULL)
171         return NULL;
172 
173     /*
174      *  See if we can find the specified directory.  We use a once-only loop
175      *  structure so we can bail out early.
176      */
177     if (stat( pzDir, &stBuf ) != 0) do {
178 
179         /*
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.
182          */
183         if (errno == ENOENT) {
184             char z[AG_PATH_MAX];
185 
186             /*
187              *  Strip off the last component, stat the remaining string and
188              *  that string must name a directory
189              */
190             char* pzDirCh = strrchr( pzDir, DIRCH );
191             if (pzDirCh == NULL) {
192                 stBuf.st_mode = S_IFREG;
193                 continue;  /* bail out of error condition */
194             }
195 
196             strncpy( z, pzDir, (size_t)(pzDirCh - pzDir));
197             z[ pzDirCh - pzDir ] = NUL;
198 
199             if (  (stat( z, &stBuf ) == 0)
200                && S_ISDIR( stBuf.st_mode )) {
201 
202                 /*
203                  *  We found the directory.  Restore the file name and
204                  *  mark the full name as a regular file
205                  */
206                 stBuf.st_mode = S_IFREG;
207                 continue;  /* bail out of error condition */
208             }
209         }
210 
211         /*
212          *  We got a bogus name.
213          */
214         fprintf( stderr, zWarn, pOpts->pzProgName );
215         fprintf( stderr, zNoStat, errno, strerror( errno ), pzDir );
216         if (free_dir_name)
217             AGFREE( pzDir );
218         return NULL;
219     } while (0);
220 
221     /*
222      *  IF what we found was a directory,
223      *  THEN tack on the config file name
224      */
225     if (S_ISDIR( stBuf.st_mode )) {
226         size_t sz = strlen( pzDir ) + strlen( pOpts->pzRcName ) + 2;
227 
228         {
229             char*  pzPath = (char*)AGALOC( sz, "file name" );
230 #ifdef HAVE_SNPRINTF
231             snprintf( pzPath, sz, "%s/%s", pzDir, pOpts->pzRcName );
232 #else
233             sprintf( pzPath, "%s/%s", pzDir, pOpts->pzRcName );
234 #endif
235             if (free_dir_name)
236                 AGFREE( pzDir );
237             pzDir = pzPath;
238             free_dir_name = 1;
239         }
240 
241         /*
242          *  IF we cannot stat the object for any reason other than
243          *     it does not exist, then we bail out
244          */
245         if (stat( pzDir, &stBuf ) != 0) {
246             if (errno != ENOENT) {
247                 fprintf( stderr, zWarn, pOpts->pzProgName );
248                 fprintf( stderr, zNoStat, errno, strerror( errno ),
249                          pzDir );
250                 AGFREE( pzDir );
251                 return NULL;
252             }
253 
254             /*
255              *  It does not exist yet, but it will be a regular file
256              */
257             stBuf.st_mode = S_IFREG;
258         }
259     }
260 
261     /*
262      *  Make sure that whatever we ultimately found, that it either is
263      *  or will soon be a file.
264      */
265     if (! S_ISREG( stBuf.st_mode )) {
266         fprintf( stderr, zWarn, pOpts->pzProgName );
267         fprintf( stderr, zNotFile, pzDir );
268         if (free_dir_name)
269             AGFREE( pzDir );
270         return NULL;
271     }
272 
273     /*
274      *  Get rid of the old file
275      */
276     unlink( pzDir );
277     *p_free_name = free_dir_name;
278     return pzDir;
279 }
280 
281 
282 static void
283 printEntry(
284     FILE *     fp,
285     tOptDesc * p,
286     tCC*       pzLA )
287 {
288     /*
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.
293      */
294     {
295         char const * pz;
296         if (! DISABLED_OPT(p) || (p->optEquivIndex != NO_EQUIVALENT))
297             pz = p->pz_Name;
298         else
299             pz = p->pz_DisableName;
300 
301         fprintf(fp, "%-18s", pz);
302     }
303     /*
304      *  IF the option is numeric only,
305      *  THEN the char pointer is really the number
306      */
307     if (OPTST_GET_ARGTYPE(p->fOptState) == OPARG_TYPE_NUMERIC)
308         fprintf( fp, "  %d\n", (int)(t_word)pzLA );
309 
310     /*
311      *  OTHERWISE, FOR each line of the value text, ...
312      */
313     else if (pzLA == NULL)
314         fputc( '\n', fp );
315 
316     else {
317         fputc( ' ', fp ); fputc( ' ', fp );
318         for (;;) {
319             tCC* pzNl = strchr( pzLA, '\n' );
320 
321             /*
322              *  IF this is the last line
323              *  THEN bail and print it
324              */
325             if (pzNl == NULL)
326                 break;
327 
328             /*
329              *  Print the continuation and the text from the current line
330              */
331             (void)fwrite( pzLA, (size_t)(pzNl - pzLA), (size_t)1, fp );
332             pzLA = pzNl+1; /* advance the Last Arg pointer */
333             fputs( "\\\n", fp );
334         }
335 
336         /*
337          *  Terminate the entry
338          */
339         fputs( pzLA, fp );
340         fputc( '\n', fp );
341     }
342 }
343 
344 
345 static void
346 print_a_value(FILE * fp, int depth, tOptDesc * pOD, tOptionValue const * ovp)
347 {
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";
352 
353     while (--depth >= 0)
354         putc(' ', fp), putc(' ', fp);
355 
356     switch (ovp->valType) {
357     default:
358     case OPARG_TYPE_NONE:
359         fprintf(fp, null_atr, ovp->pzName);
360         break;
361 
362     case OPARG_TYPE_STRING:
363         print_a_string(fp, ovp->pzName, ovp->v.strVal);
364         break;
365 
366     case OPARG_TYPE_ENUMERATION:
367     case OPARG_TYPE_MEMBERSHIP:
368         if (pOD != NULL) {
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";
373 
374             fprintf(fp, type_atr, ovp->pzName, typ);
375 
376             /*
377              *  This is a magic incantation that will convert the
378              *  bit flag values back into a string suitable for printing.
379              */
380             (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD );
381             if (pOD->optArg.argString != NULL) {
382                 fputs(pOD->optArg.argString, fp);
383 
384                 if (ovp->valType != OPARG_TYPE_ENUMERATION) {
385                     /*
386                      *  set membership strings get allocated
387                      */
388                     AGFREE( pOD->optArg.argString );
389                 }
390             }
391 
392             pOD->optArg.argEnum = val;
393             pOD->fOptState = opt_state;
394             fprintf(fp, close_xml, ovp->pzName);
395             break;
396         }
397         /* FALLTHROUGH */
398 
399     case OPARG_TYPE_NUMERIC:
400         fprintf(fp, numb_atr, ovp->pzName, ovp->v.longVal);
401         break;
402 
403     case OPARG_TYPE_BOOLEAN:
404         fprintf(fp, bool_atr, ovp->pzName,
405                 ovp->v.boolVal ? "true" : "false");
406         break;
407 
408     case OPARG_TYPE_HIERARCHY:
409         printValueList(fp, ovp->pzName, ovp->v.nestVal);
410         break;
411     }
412 }
413 
414 
415 static void
416 print_a_string(FILE * fp, char const * name, char const * pz)
417 {
418     static char const open_atr[]  = "<%s>";
419 
420     fprintf(fp, open_atr, name);
421     for (;;) {
422         int ch = ((int)*(pz++)) & 0xFF;
423 
424         switch (ch) {
425         case NUL: goto string_done;
426 
427         case '&':
428         case '<':
429         case '>':
430 #if __GNUC__ >= 4
431         case 1 ... (' ' - 1):
432         case ('~' + 1) ... 0xFF:
433 #endif
434             emit_special_char(fp, ch);
435             break;
436 
437         default:
438 #if __GNUC__ < 4
439             if (  ((ch >= 1) && (ch <= (' ' - 1)))
440                || ((ch >= ('~' + 1)) && (ch <= 0xFF)) ) {
441                 emit_special_char(fp, ch);
442                 break;
443             }
444 #endif
445             putc(ch, fp);
446         }
447     } string_done:;
448     fprintf(fp, close_xml, name);
449 }
450 
451 
452 static void
453 printValueList(FILE * fp, char const * name, tArgList * al)
454 {
455     static int depth = 1;
456 
457     int sp_ct;
458     int opt_ct;
459     void ** opt_list;
460 
461     if (al == NULL)
462         return;
463     opt_ct   = al->useCt;
464     opt_list = (void **)al->apzArgs;
465 
466     if (opt_ct <= 0) {
467         fprintf(fp, "<%s/>\n", name);
468         return;
469     }
470 
471     fprintf(fp, "<%s type=nested>\n", name);
472 
473     depth++;
474     while (--opt_ct >= 0) {
475         tOptionValue const * ovp = *(opt_list++);
476 
477         print_a_value(fp, depth, NULL, ovp);
478     }
479     depth--;
480 
481     for (sp_ct = depth; --sp_ct >= 0;)
482         putc(' ', fp), putc(' ', fp);
483     fprintf(fp, "</%s>\n", name);
484 }
485 
486 
487 static void
488 printHierarchy(FILE * fp, tOptDesc * p)
489 {
490     int opt_ct;
491     tArgList * al = p->optCookie;
492     void ** opt_list;
493 
494     if (al == NULL)
495         return;
496 
497     opt_ct   = al->useCt;
498     opt_list = (void **)al->apzArgs;
499 
500     if (opt_ct <= 0)
501         return;
502 
503     do  {
504         tOptionValue const * base = *(opt_list++);
505         tOptionValue const * ovp = optionGetValue(base, NULL);
506 
507         if (ovp == NULL)
508             continue;
509 
510         fprintf(fp, "<%s type=nested>\n", p->pz_Name);
511 
512         do  {
513             print_a_value(fp, 1, p, ovp);
514 
515         } while (ovp = optionNextValue(base, ovp),
516                  ovp != NULL);
517 
518         fprintf(fp, "</%s>\n", p->pz_Name);
519     } while (--opt_ct > 0);
520 }
521 
522 
523 static FILE *
524 openSaveFile( tOptions* pOpts )
525 {
526     FILE*     fp;
527 
528     {
529         int   free_name = 0;
530         tCC*  pzFName = findFileName( pOpts, &free_name );
531         if (pzFName == NULL)
532             return NULL;
533 
534         fp = fopen( pzFName, "w" FOPEN_BINARY_FLAG );
535         if (fp == NULL) {
536             fprintf( stderr, zWarn, pOpts->pzProgName );
537             fprintf( stderr, zNoCreat, errno, strerror( errno ), pzFName );
538             if (free_name)
539                 AGFREE( pzFName );
540             return fp;
541         }
542 
543         if (free_name)
544             AGFREE( pzFName );
545     }
546 
547     {
548         char const*  pz = pOpts->pzUsageTitle;
549         fputs( "#  ", fp );
550         do { fputc( *pz, fp ); } while (*(pz++) != '\n');
551     }
552 
553     {
554         time_t  timeVal = time( NULL );
555         char*   pzTime  = ctime( &timeVal );
556 
557         fprintf( fp, zPresetFile, pzTime );
558 #ifdef HAVE_ALLOCATED_CTIME
559         /*
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.
563          */
564         AGFREE( pzTime );
565 #endif
566     }
567 
568     return fp;
569 }
570 
571 static void
572 printNoArgOpt(FILE * fp, tOptDesc * p, tOptDesc * pOD)
573 {
574     /*
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".
578      */
579     char const * pznm =
580         (DISABLED_OPT( p )) ? pOD->pz_DisableName : pOD->pz_Name;
581     /*
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.
585      */
586     if (pznm == NULL)
587         pznm = pOD->pz_Name;
588 
589     fprintf(fp, "%s\n", pznm);
590 }
591 
592 static void
593 printStringArg(FILE * fp, tOptDesc * pOD)
594 {
595     if (pOD->fOptState & OPTST_STACKED) {
596         tArgList*  pAL = (tArgList*)pOD->optCookie;
597         int        uct = pAL->useCt;
598         tCC**      ppz = pAL->apzArgs;
599 
600         /*
601          *  un-disable multiple copies of disabled options.
602          */
603         if (uct > 1)
604             pOD->fOptState &= ~OPTST_DISABLED;
605 
606         while (uct-- > 0)
607             printEntry( fp, pOD, *(ppz++) );
608     } else {
609         printEntry( fp, pOD, pOD->optArg.argString );
610     }
611 }
612 
613 static void
614 printEnumArg(FILE * fp, tOptDesc * pOD)
615 {
616     uintptr_t val = pOD->optArg.argEnum;
617 
618     /*
619      *  This is a magic incantation that will convert the
620      *  bit flag values back into a string suitable for printing.
621      */
622     (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD);
623     printEntry( fp, pOD, (void*)(pOD->optArg.argString));
624 
625     pOD->optArg.argEnum = val;
626 }
627 
628 static void
629 printSetMemberArg(FILE * fp, tOptDesc * pOD)
630 {
631     uintptr_t val = pOD->optArg.argEnum;
632 
633     /*
634      *  This is a magic incantation that will convert the
635      *  bit flag values back into a string suitable for printing.
636      */
637     (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD);
638     printEntry( fp, pOD, (void*)(pOD->optArg.argString));
639 
640     if (pOD->optArg.argString != NULL) {
641         /*
642          *  set membership strings get allocated
643          */
644         AGFREE( pOD->optArg.argString );
645         pOD->fOptState &= ~OPTST_ALLOC_ARG;
646     }
647 
648     pOD->optArg.argEnum = val;
649 }
650 
651 static void
652 printFileArg(FILE * fp, tOptDesc * pOD, tOptions* pOpts)
653 {
654     /*
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....
657      */
658     if (pOD->optCookie != NULL)
659         printEntry(fp, pOD, pOD->optCookie);
660 
661     else if (HAS_originalOptArgArray(pOpts)) {
662         char const * orig =
663             pOpts->originalOptArgArray[pOD->optIndex].argString;
664 
665         if (pOD->optArg.argString == orig)
666             return;
667 
668         printEntry(fp, pOD, pOD->optArg.argString);
669     }
670 }
671 
672 
673 /*=export_func  optionSaveFile
674  *
675  * what:  saves the option state to a file
676  *
677  * arg:   tOptions*,   pOpts,  program options descriptor
678  *
679  * doc:
680  *
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.
687  *
688  * The recommend usage is as follows:
689  * @example
690  *    optionProcess(&progOptions, argc, argv);
691  *    if (i_want_a_non_standard_place_for_this)
692  *        SET_OPT_SAVE_OPTS("myfilename");
693  *    optionSaveFile(&progOptions);
694  * @end example
695  *
696  * err:
697  *
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.
701 =*/
702 void
703 optionSaveFile( tOptions* pOpts )
704 {
705     tOptDesc* pOD;
706     int       ct;
707     FILE*     fp = openSaveFile(pOpts);
708 
709     if (fp == NULL)
710         return;
711 
712     /*
713      *  FOR each of the defined options, ...
714      */
715     ct  = pOpts->presetOptCt;
716     pOD = pOpts->pOptDesc;
717     do  {
718         tOptDesc*  p;
719 
720         /*
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)
725          *
726          *  Equivalenced options get picked up when the equivalenced-to
727          *  option is processed.
728          */
729         if (UNUSED_OPT( pOD ))
730             continue;
731 
732         if ((pOD->fOptState & OPTST_DO_NOT_SAVE_MASK) != 0)
733             continue;
734 
735         if (  (pOD->optEquivIndex != NO_EQUIVALENT)
736            && (pOD->optEquivIndex != pOD->optIndex))
737             continue;
738 
739         /*
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!
743          */
744         p = ((pOD->fOptState & OPTST_EQUIVALENCE) != 0)
745             ? (pOpts->pOptDesc + pOD->optActualIndex) : pOD;
746 
747         switch (OPTST_GET_ARGTYPE(pOD->fOptState)) {
748         case OPARG_TYPE_NONE:
749             printNoArgOpt(fp, p, pOD);
750             break;
751 
752         case OPARG_TYPE_NUMERIC:
753             printEntry( fp, p, (void*)(p->optArg.argInt));
754             break;
755 
756         case OPARG_TYPE_STRING:
757             printStringArg(fp, p);
758             break;
759 
760         case OPARG_TYPE_ENUMERATION:
761             printEnumArg(fp, p);
762             break;
763 
764         case OPARG_TYPE_MEMBERSHIP:
765             printSetMemberArg(fp, p);
766             break;
767 
768         case OPARG_TYPE_BOOLEAN:
769             printEntry( fp, p, p->optArg.argBool ? "true" : "false" );
770             break;
771 
772         case OPARG_TYPE_HIERARCHY:
773             printHierarchy(fp, p);
774             break;
775 
776         case OPARG_TYPE_FILE:
777             printFileArg(fp, p, pOpts);
778             break;
779 
780         default:
781             break; /* cannot handle - skip it */
782         }
783     } while ( (pOD++), (--ct > 0));
784 
785     fclose( fp );
786 }
787 /*
788  * Local Variables:
789  * mode: C
790  * c-file-style: "stroustrup"
791  * indent-tabs-mode: nil
792  * End:
793  * end of autoopts/save.c */
794