]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/ntp/libopts/enumeration.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / ntp / libopts / enumeration.c
1
2 /*
3  *  $Id: enumeration.c,v 4.17 2007/02/04 17:44:12 bkorb Exp $
4  * Time-stamp:      "2007-01-13 10:22:35 bkorb"
5  *
6  *   Automated Options Paged Usage module.
7  *
8  *  This routine will run run-on options through a pager so the
9  *  user may examine, print or edit them at their leisure.
10  */
11
12 /*
13  *  Automated Options copyright 1992-2007 Bruce Korb
14  *
15  *  Automated Options is free software.
16  *  You may redistribute it and/or modify it under the terms of the
17  *  GNU General Public License, as published by the Free Software
18  *  Foundation; either version 2, or (at your option) any later version.
19  *
20  *  Automated Options is distributed in the hope that it will be useful,
21  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  *  GNU General Public License for more details.
24  *
25  *  You should have received a copy of the GNU General Public License
26  *  along with Automated Options.  See the file "COPYING".  If not,
27  *  write to:  The Free Software Foundation, Inc.,
28  *             51 Franklin Street, Fifth Floor,
29  *             Boston, MA  02110-1301, USA.
30  *
31  * As a special exception, Bruce Korb gives permission for additional
32  * uses of the text contained in his release of AutoOpts.
33  *
34  * The exception is that, if you link the AutoOpts library with other
35  * files to produce an executable, this does not by itself cause the
36  * resulting executable to be covered by the GNU General Public License.
37  * Your use of that executable is in no way restricted on account of
38  * linking the AutoOpts library code into it.
39  *
40  * This exception does not however invalidate any other reasons why
41  * the executable file might be covered by the GNU General Public License.
42  *
43  * This exception applies only to the code released by Bruce Korb under
44  * the name AutoOpts.  If you copy code from other sources under the
45  * General Public License into a copy of AutoOpts, as the General Public
46  * License permits, the exception does not apply to the code that you add
47  * in this way.  To avoid misleading anyone as to the status of such
48  * modified files, you must delete this exception notice from them.
49  *
50  * If you write modifications of your own for AutoOpts, it is your choice
51  * whether to permit this exception to apply to your modifications.
52  * If you do not wish that, delete this exception notice.
53  */
54
55 tSCC*  pz_enum_err_fmt;
56
57 /* = = = START-STATIC-FORWARD = = = */
58 /* static forward declarations maintained by :mkfwd */
59 static void
60 enumError(
61     tOptions*     pOpts,
62     tOptDesc*     pOD,
63     tCC* const *  paz_names,
64     int           name_ct );
65
66 static uintptr_t
67 findName(
68     tCC*          pzName,
69     tOptions*     pOpts,
70     tOptDesc*     pOD,
71     tCC* const *  paz_names,
72     unsigned int  name_ct );
73 /* = = = END-STATIC-FORWARD = = = */
74
75 static void
76 enumError(
77     tOptions*     pOpts,
78     tOptDesc*     pOD,
79     tCC* const *  paz_names,
80     int           name_ct )
81 {
82     size_t max_len = 0;
83     size_t ttl_len = 0;
84
85     if (pOpts != NULL)
86         fprintf( option_usage_fp, pz_enum_err_fmt, pOpts->pzProgName,
87                  pOD->optArg.argString, pOD->pz_Name );
88
89     fprintf( option_usage_fp, zValidKeys, pOD->pz_Name );
90
91     if (**paz_names == 0x7F) {
92         paz_names++;
93         name_ct--;
94     }
95
96     /*
97      *  Figure out the maximum length of any name, plus the total length
98      *  of all the names.
99      */
100     {
101         tCC * const * paz = paz_names;
102         int   ct  = name_ct;
103
104         do  {
105             size_t len = strlen( *(paz++) ) + 1;
106             if (len > max_len)
107                 max_len = len;
108             ttl_len += len;
109         } while (--ct > 0);
110     }
111
112     /*
113      *  IF any one entry is about 1/2 line or longer, print one per line
114      */
115     if (max_len > 35) {
116         do  {
117             fprintf( option_usage_fp, "  %s\n", *(paz_names++) );
118         } while (--name_ct > 0);
119     }
120
121     /*
122      *  ELSE IF they all fit on one line, then do so.
123      */
124     else if (ttl_len < 76) {
125         fputc( ' ', option_usage_fp );
126         do  {
127             fputc( ' ', option_usage_fp );
128             fputs( *(paz_names++), option_usage_fp );
129         } while (--name_ct > 0);
130         fputc( '\n', option_usage_fp );
131     }
132
133     /*
134      *  Otherwise, columnize the output
135      */
136     else {
137         int   ent_no = 0;
138         char  zFmt[16];  /* format for all-but-last entries on a line */
139
140         sprintf( zFmt, "%%-%ds", (int)max_len );
141         max_len = 78 / max_len; /* max_len is now max entries on a line */
142         fputs( "  ", option_usage_fp );
143
144         /*
145          *  Loop through all but the last entry
146          */
147         while (--name_ct > 0) {
148             if (++ent_no == max_len) {
149                 /*
150                  *  Last entry on a line.  Start next line, too.
151                  */
152                 fprintf( option_usage_fp, "%s\n  ", *(paz_names++) );
153                 ent_no = 0;
154             }
155
156             else
157                 fprintf( option_usage_fp, zFmt, *(paz_names++) );
158         }
159         fprintf( option_usage_fp, "%s\n", *paz_names );
160     }
161
162     /*
163      *  IF we do not have a pOpts pointer, then this output is being requested
164      *  by the usage procedure.  Let's not re-invoke it recursively.
165      */
166     if (pOpts != NULL)
167         (*(pOpts->pUsageProc))( pOpts, EXIT_FAILURE );
168     if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_MEMBERSHIP)
169         fputs( zSetMemberSettings, option_usage_fp );
170 }
171
172
173 static uintptr_t
174 findName(
175     tCC*          pzName,
176     tOptions*     pOpts,
177     tOptDesc*     pOD,
178     tCC* const *  paz_names,
179     unsigned int  name_ct )
180 {
181     uintptr_t     res = name_ct;
182     size_t        len = strlen( (char*)pzName );
183     uintptr_t     idx;
184     /*
185      *  Look for an exact match, but remember any partial matches.
186      *  Multiple partial matches means we have an ambiguous match.
187      */
188     for (idx = 0; idx < name_ct; idx++) {
189         if (strncmp( (char*)paz_names[idx], (char*)pzName, len) == 0) {
190             if (paz_names[idx][len] == NUL)
191                 return idx;  /* full match */
192
193             if (res != name_ct) {
194                 pz_enum_err_fmt = zAmbigKey;
195                 option_usage_fp = stderr;
196                 enumError( pOpts, pOD, paz_names, (int)name_ct );
197             }
198             res = idx; /* save partial match */
199         }
200     }
201
202     /*
203      *  no partial match -> error
204      */
205     if (res == name_ct) {
206         pz_enum_err_fmt = zNoKey;
207         option_usage_fp = stderr;
208         enumError( pOpts, pOD, paz_names, (int)name_ct );
209     }
210
211     /*
212      *  Return the matching index as a char* pointer.
213      *  The result gets stashed in a char* pointer, so it will have to fit.
214      */
215     return res;
216 }
217
218
219 /*=export_func  optionKeywordName
220  * what:  Convert between enumeration values and strings
221  * private:
222  *
223  * arg:   tOptDesc*,     pOD,       enumeration option description
224  * arg:   unsigned int,  enum_val,  the enumeration value to map
225  *
226  * ret_type:  char const*
227  * ret_desc:  the enumeration name from const memory
228  *
229  * doc:   This converts an enumeration value into the matching string.
230 =*/
231 char const*
232 optionKeywordName(
233     tOptDesc*     pOD,
234     unsigned int  enum_val )
235 {
236     tOptDesc od;
237
238     od.optArg.argEnum = enum_val;
239     (*(pOD->pOptProc))( (void*)(2UL), &od );
240     return od.optArg.argString;
241 }
242
243
244 /*=export_func  optionEnumerationVal
245  * what:  Convert from a string to an enumeration value
246  * private:
247  *
248  * arg:   tOptions*,     pOpts,     the program options descriptor
249  * arg:   tOptDesc*,     pOD,       enumeration option description
250  * arg:   char const * const *,  paz_names, list of enumeration names
251  * arg:   unsigned int,  name_ct,   number of names in list
252  *
253  * ret_type:  uintptr_t
254  * ret_desc:  the enumeration value
255  *
256  * doc:   This converts the optArg.argString string from the option description
257  *        into the index corresponding to an entry in the name list.
258  *        This will match the generated enumeration value.
259  *        Full matches are always accepted.  Partial matches are accepted
260  *        if there is only one partial match.
261 =*/
262 uintptr_t
263 optionEnumerationVal(
264     tOptions*     pOpts,
265     tOptDesc*     pOD,
266     tCC * const * paz_names,
267     unsigned int  name_ct )
268 {
269     uintptr_t res = 0UL;
270
271     /*
272      *  IF the program option descriptor pointer is invalid,
273      *  then it is some sort of special request.
274      */
275     switch ((uintptr_t)pOpts) {
276     case 0UL:
277         /*
278          *  print the list of enumeration names.
279          */
280         enumError( pOpts, pOD, paz_names, (int)name_ct );
281         break;
282
283     case 1UL:
284     {
285         unsigned int ix = pOD->optArg.argEnum;
286         /*
287          *  print the name string.
288          */
289         if (ix >= name_ct)
290             printf( "INVALID-%d", ix );
291         else
292             fputs( paz_names[ ix ], stdout );
293
294         break;
295     }
296
297     case 2UL:
298     {
299         tSCC zInval[] = "*INVALID*";
300         unsigned int ix = pOD->optArg.argEnum;
301         /*
302          *  Replace the enumeration value with the name string.
303          */
304         if (ix >= name_ct)
305             return (uintptr_t)zInval;
306
307         res = (uintptr_t)paz_names[ ix ];
308         break;
309     }
310
311     default:
312         res = findName( pOD->optArg.argString, pOpts, pOD, paz_names, name_ct );
313
314         if (pOD->fOptState & OPTST_ALLOC_ARG) {
315             AGFREE(pOD->optArg.argString);
316             pOD->fOptState &= ~OPTST_ALLOC_ARG;
317             pOD->optArg.argString = NULL;
318         }
319     }
320
321     return res;
322 }
323
324
325 /*=export_func  optionSetMembers
326  * what:  Convert between bit flag values and strings
327  * private:
328  *
329  * arg:   tOptions*,     pOpts,     the program options descriptor
330  * arg:   tOptDesc*,     pOD,       enumeration option description
331  * arg:   char const * const *,
332  *                       paz_names, list of enumeration names
333  * arg:   unsigned int,  name_ct,   number of names in list
334  *
335  * doc:   This converts the optArg.argString string from the option description
336  *        into the index corresponding to an entry in the name list.
337  *        This will match the generated enumeration value.
338  *        Full matches are always accepted.  Partial matches are accepted
339  *        if there is only one partial match.
340 =*/
341 void
342 optionSetMembers(
343     tOptions*     pOpts,
344     tOptDesc*     pOD,
345     tCC* const *  paz_names,
346     unsigned int  name_ct )
347 {
348     /*
349      *  IF the program option descriptor pointer is invalid,
350      *  then it is some sort of special request.
351      */
352     switch ((uintptr_t)pOpts) {
353     case 0UL:
354         /*
355          *  print the list of enumeration names.
356          */
357         enumError( pOpts, pOD, paz_names, (int)name_ct );
358         return;
359
360     case 1UL:
361     {
362         /*
363          *  print the name string.
364          */
365         uintptr_t bits = (uintptr_t)pOD->optCookie;
366         uintptr_t res  = 0;
367         size_t    len  = 0;
368
369         while (bits != 0) {
370             if (bits & 1) {
371                 if (len++ > 0) fputs( " | ", stdout );
372                 fputs( paz_names[ res ], stdout );
373             }
374             if (++res >= name_ct) break;
375             bits >>= 1;
376         }
377         return;
378     }
379
380     case 2UL:
381     {
382         char*     pz;
383         uintptr_t bits = (uintptr_t)pOD->optCookie;
384         uintptr_t res  = 0;
385         size_t    len  = 0;
386
387         /*
388          *  Replace the enumeration value with the name string.
389          *  First, determine the needed length, then allocate and fill in.
390          */
391         while (bits != 0) {
392             if (bits & 1)
393                 len += strlen( paz_names[ res ]) + 8;
394             if (++res >= name_ct) break;
395             bits >>= 1;
396         }
397
398         pOD->optArg.argString = pz = AGALOC( len, "enum name" );
399
400         /*
401          *  Start by clearing all the bits.  We want to turn off any defaults
402          *  because we will be restoring to current state, not adding to
403          *  the default set of bits.
404          */
405         strcpy( pz, "none" );
406         pz += 4;
407         bits = (uintptr_t)pOD->optCookie;
408         res = 0;
409         while (bits != 0) {
410             if (bits & 1) {
411                 strcpy( pz, " + " );
412                 strcpy( pz+3, paz_names[ res ]);
413                 pz += strlen( paz_names[ res ]) + 3;
414             }
415             if (++res >= name_ct) break;
416             bits >>= 1;
417         }
418         return;
419     }
420
421     default:
422         break;
423     }
424
425     {
426         tCC*      pzArg = pOD->optArg.argString;
427         uintptr_t res;
428         if ((pzArg == NULL) || (*pzArg == NUL)) {
429             pOD->optCookie = (void*)0;
430             return;
431         }
432
433         res = (uintptr_t)pOD->optCookie;
434         for (;;) {
435             tSCC zSpn[] = " ,|+\t\r\f\n";
436             int  iv, len;
437
438             pzArg += strspn( pzArg, zSpn );
439             iv = (*pzArg == '!');
440             if (iv)
441                 pzArg += strspn( pzArg+1, zSpn ) + 1;
442
443             len = strcspn( pzArg, zSpn );
444             if (len == 0)
445                 break;
446
447             if ((len == 3) && (strncmp(pzArg, zAll, (size_t)3) == 0)) {
448                 if (iv)
449                      res = 0;
450                 else res = ~0UL;
451             }
452             else if ((len == 4) && (strncmp(pzArg, zNone, (size_t)4) == 0)) {
453                 if (! iv)
454                     res = 0;
455             }
456             else do {
457                 char* pz;
458                 uintptr_t bit = strtoul( pzArg, &pz, 0 );
459
460                 if (pz != pzArg + len) {
461                     char z[ AO_NAME_SIZE ];
462                     tCC* p;
463                     if (*pz != NUL) {
464                         if (len >= AO_NAME_LIMIT)
465                             break;
466                         strncpy( z, pzArg, (size_t)len );
467                         z[len] = NUL;
468                         p = z;
469                     } else {
470                         p = pzArg;
471                     }
472
473                     bit = 1UL << findName(p, pOpts, pOD, paz_names, name_ct);
474                 }
475                 if (iv)
476                      res &= ~bit;
477                 else res |= bit;
478             } while (0);
479
480             if (pzArg[len] == NUL)
481                 break;
482             pzArg += len + 1;
483         }
484         if (name_ct < (8 * sizeof( uintptr_t ))) {
485             res &= (1UL << name_ct) - 1UL;
486         }
487
488         pOD->optCookie = (void*)res;
489     }
490 }
491
492 /*
493  * Local Variables:
494  * mode: C
495  * c-file-style: "stroustrup"
496  * indent-tabs-mode: nil
497  * End:
498  * end of autoopts/enumeration.c */