1/*	$NetBSD: enumeration.c,v 1.2 2010/12/04 23:08:34 christos Exp $	*/
2
3
4/*
5 *  Id: 27595043d23170eb4bb8b9831fc54016944e00e8
6 * Time-stamp:      "2008-07-27 12:28:01 bkorb"
7 *
8 *   Automated Options Paged Usage module.
9 *
10 *  This routine will run run-on options through a pager so the
11 *  user may examine, print or edit them at their leisure.
12 *
13 *  This file is part of AutoOpts, a companion to AutoGen.
14 *  AutoOpts is free software.
15 *  AutoOpts is copyright (c) 1992-2009 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 md5sums:
28 *
29 *  43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3
30 *  06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3
31 *  66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
32 */
33
34tSCC*  pz_enum_err_fmt;
35
36/* = = = START-STATIC-FORWARD = = = */
37/* static forward declarations maintained by mk-fwd */
38static void
39enumError(
40    tOptions*     pOpts,
41    tOptDesc*     pOD,
42    tCC* const *  paz_names,
43    int           name_ct );
44
45static uintptr_t
46findName(
47    tCC*          pzName,
48    tOptions*     pOpts,
49    tOptDesc*     pOD,
50    tCC* const *  paz_names,
51    unsigned int  name_ct );
52/* = = = END-STATIC-FORWARD = = = */
53
54static void
55enumError(
56    tOptions*     pOpts,
57    tOptDesc*     pOD,
58    tCC* const *  paz_names,
59    int           name_ct )
60{
61    size_t max_len = 0;
62    size_t ttl_len = 0;
63    int    ct_down = name_ct;
64    int    hidden  = 0;
65
66    /*
67     *  A real "pOpts" pointer means someone messed up.  Give a real error.
68     */
69    if (pOpts > OPTPROC_EMIT_LIMIT)
70        fprintf(option_usage_fp, pz_enum_err_fmt, pOpts->pzProgName,
71                pOD->optArg.argString, pOD->pz_Name);
72
73    fprintf(option_usage_fp, zValidKeys, pOD->pz_Name);
74
75    /*
76     *  If the first name starts with this funny character, then we have
77     *  a first value with an unspellable name.  You cannot specify it.
78     *  So, we don't list it either.
79     */
80    if (**paz_names == 0x7F) {
81        paz_names++;
82        hidden  = 1;
83        ct_down = --name_ct;
84    }
85
86    /*
87     *  Figure out the maximum length of any name, plus the total length
88     *  of all the names.
89     */
90    {
91        tCC * const * paz = paz_names;
92
93        do  {
94            size_t len = strlen( *(paz++) ) + 1;
95            if (len > max_len)
96                max_len = len;
97            ttl_len += len;
98        } while (--ct_down > 0);
99
100        ct_down = name_ct;
101    }
102
103    /*
104     *  IF any one entry is about 1/2 line or longer, print one per line
105     */
106    if (max_len > 35) {
107        do  {
108            fprintf( option_usage_fp, "  %s\n", *(paz_names++) );
109        } while (--ct_down > 0);
110    }
111
112    /*
113     *  ELSE IF they all fit on one line, then do so.
114     */
115    else if (ttl_len < 76) {
116        fputc( ' ', option_usage_fp );
117        do  {
118            fputc( ' ', option_usage_fp );
119            fputs( *(paz_names++), option_usage_fp );
120        } while (--ct_down > 0);
121        fputc( '\n', option_usage_fp );
122    }
123
124    /*
125     *  Otherwise, columnize the output
126     */
127    else {
128        size_t   ent_no = 0;
129        char  zFmt[16];  /* format for all-but-last entries on a line */
130
131        sprintf( zFmt, "%%-%ds", (int)max_len );
132        max_len = 78 / max_len; /* max_len is now max entries on a line */
133        fputs( "  ", option_usage_fp );
134
135        /*
136         *  Loop through all but the last entry
137         */
138        ct_down = name_ct;
139        while (--ct_down > 0) {
140            if (++ent_no == max_len) {
141                /*
142                 *  Last entry on a line.  Start next line, too.
143                 */
144                fprintf( option_usage_fp, "%s\n  ", *(paz_names++) );
145                ent_no = 0;
146            }
147
148            else
149                fprintf(option_usage_fp, zFmt, *(paz_names++) );
150        }
151        fprintf(option_usage_fp, "%s\n", *paz_names);
152    }
153
154    if (pOpts > OPTPROC_EMIT_LIMIT) {
155        fprintf(option_usage_fp, zIntRange, hidden, name_ct - 1 + hidden);
156
157        (*(pOpts->pUsageProc))( pOpts, EXIT_FAILURE );
158        /* NOTREACHED */
159    }
160
161
162    if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_MEMBERSHIP) {
163        fprintf(option_usage_fp, zLowerBits, name_ct);
164        fputs(zSetMemberSettings, option_usage_fp);
165    } else {
166        fprintf(option_usage_fp, zIntRange, hidden, name_ct - 1 + hidden);
167    }
168}
169
170
171static uintptr_t
172findName(
173    tCC*          pzName,
174    tOptions*     pOpts,
175    tOptDesc*     pOD,
176    tCC* const *  paz_names,
177    unsigned int  name_ct )
178{
179    /*
180     *  Return the matching index as a pointer sized integer.
181     *  The result gets stashed in a char* pointer.
182     */
183    uintptr_t     res = name_ct;
184    size_t        len = strlen( (const char*)pzName );
185    uintptr_t     idx;
186
187    if (IS_DEC_DIGIT_CHAR(*pzName)) {
188        char * pz = (char *)(void *)(intptr_t)pzName;
189        unsigned long val = strtoul(pz, &pz, 0);
190        if ((*pz == NUL) && (val < name_ct))
191            return (uintptr_t)val;
192        enumError(pOpts, pOD, paz_names, (int)name_ct);
193        return name_ct;
194    }
195
196    /*
197     *  Look for an exact match, but remember any partial matches.
198     *  Multiple partial matches means we have an ambiguous match.
199     */
200    for (idx = 0; idx < name_ct; idx++) {
201        if (strncmp((const char*)paz_names[idx], (const char*)pzName, len) == 0) {
202            if (paz_names[idx][len] == NUL)
203                return idx;  /* full match */
204
205            res = (res != name_ct) ? (uintptr_t)~0 : idx; /* save partial match */
206        }
207    }
208
209    if (res < name_ct)
210        return res; /* partial match */
211
212    pz_enum_err_fmt = (res == name_ct) ? zNoKey : zAmbigKey;
213    option_usage_fp = stderr;
214    enumError(pOpts, pOD, paz_names, (int)name_ct);
215    return name_ct;
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=*/
231char const*
232optionKeywordName(
233    tOptDesc*     pOD,
234    unsigned int  enum_val )
235{
236    tOptDesc od;
237
238    od.optArg.argEnum = enum_val;
239    (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, &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=*/
262uintptr_t
263optionEnumerationVal(
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 (uintptr_t)OPTPROC_EMIT_USAGE:
277        /*
278         *  print the list of enumeration names.
279         */
280        enumError(pOpts, pOD, paz_names, (int)name_ct);
281        break;
282
283    case (uintptr_t)OPTPROC_EMIT_SHELL:
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 (uintptr_t)OPTPROC_RETURN_VALNAME:
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        pOD->optArg.argString = 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=*/
341void
342optionSetMembers(
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 (uintptr_t)OPTPROC_EMIT_USAGE:
354        /*
355         *  print the list of enumeration names.
356         */
357        enumError(OPTPROC_EMIT_USAGE, pOD, paz_names, (int)name_ct );
358        return;
359
360    case (uintptr_t)OPTPROC_EMIT_SHELL:
361    {
362        /*
363         *  print the name string.
364         */
365        size_t    ix   =  0;
366        uintptr_t bits = (uintptr_t)pOD->optCookie;
367        size_t    len  = 0;
368
369        bits &= ((uintptr_t)1 << (uintptr_t)name_ct) - (uintptr_t)1;
370
371        while (bits != 0) {
372            if (bits & 1) {
373                if (len++ > 0) fputs( " | ", stdout );
374                fputs(paz_names[ix], stdout);
375            }
376            if (++ix >= name_ct) break;
377            bits >>= 1;
378        }
379        return;
380    }
381
382    case (uintptr_t)OPTPROC_RETURN_VALNAME:
383    {
384        char*     pz;
385        uintptr_t bits = (uintptr_t)pOD->optCookie;
386        size_t    ix   = 0;
387        size_t    len  = 5;
388
389        bits &= ((uintptr_t)1 << (uintptr_t)name_ct) - (uintptr_t)1;
390
391        /*
392         *  Replace the enumeration value with the name string.
393         *  First, determine the needed length, then allocate and fill in.
394         */
395        while (bits != 0) {
396            if (bits & 1)
397                len += strlen( paz_names[ix]) + 8;
398            if (++ix >= name_ct) break;
399            bits >>= 1;
400        }
401
402        pOD->optArg.argString = pz = AGALOC(len, "enum name");
403
404        /*
405         *  Start by clearing all the bits.  We want to turn off any defaults
406         *  because we will be restoring to current state, not adding to
407         *  the default set of bits.
408         */
409        strcpy( pz, "none" );
410        pz += 4;
411        bits = (uintptr_t)pOD->optCookie;
412        bits &= ((uintptr_t)1 << (uintptr_t)name_ct) - (uintptr_t)1;
413        ix = 0;
414
415        while (bits != 0) {
416            if (bits & 1) {
417                strcpy( pz, " + " );
418                strcpy( pz+3, paz_names[ix]);
419                pz += strlen( paz_names[ix]) + 3;
420            }
421            if (++ix >= name_ct) break;
422            bits >>= 1;
423        }
424        return;
425    }
426
427    default:
428        break;
429    }
430
431    if ((pOD->fOptState & OPTST_RESET) != 0)
432        return;
433
434    {
435        tCC*      pzArg = pOD->optArg.argString;
436        uintptr_t res;
437        if ((pzArg == NULL) || (*pzArg == NUL)) {
438            pOD->optCookie = (void*)0;
439            return;
440        }
441
442        res = (uintptr_t)pOD->optCookie;
443        for (;;) {
444            tSCC zSpn[] = " ,|+\t\r\f\n";
445            int  iv, len;
446
447            pzArg += strspn( pzArg, zSpn );
448            iv = (*pzArg == '!');
449            if (iv)
450                pzArg += strspn( pzArg+1, zSpn ) + 1;
451
452            len = strcspn( pzArg, zSpn );
453            if (len == 0)
454                break;
455
456            if ((len == 3) && (strncmp(pzArg, zAll, (size_t)3) == 0)) {
457                if (iv)
458                     res = 0;
459                else res = ~0UL;
460            }
461            else if ((len == 4) && (strncmp(pzArg, zNone, (size_t)4) == 0)) {
462                if (! iv)
463                    res = 0;
464            }
465            else do {
466                char* pz;
467                uintptr_t bit = strtoul( pzArg, &pz, 0 );
468
469                if (pz != pzArg + len) {
470                    char z[ AO_NAME_SIZE ];
471                    tCC* p;
472                    unsigned int  shift_ct;
473
474                    if (*pz != NUL) {
475                        if (len >= AO_NAME_LIMIT)
476                            break;
477                        strncpy( z, pzArg, (size_t)len );
478                        z[len] = NUL;
479                        p = z;
480                    } else {
481                        p = pzArg;
482                    }
483
484                    shift_ct = findName(p, pOpts, pOD, paz_names, name_ct);
485                    if (shift_ct >= name_ct) {
486                        pOD->optCookie = (void*)0;
487                        return;
488                    }
489                    bit = 1UL << shift_ct;
490                }
491                if (iv)
492                     res &= ~bit;
493                else res |= bit;
494            } while (0);
495
496            if (pzArg[len] == NUL)
497                break;
498            pzArg += len + 1;
499        }
500        if (name_ct < (8 * sizeof( uintptr_t ))) {
501            res &= (1UL << name_ct) - 1UL;
502        }
503
504        pOD->optCookie = (void*)res;
505    }
506}
507
508/*
509 * Local Variables:
510 * mode: C
511 * c-file-style: "stroustrup"
512 * indent-tabs-mode: nil
513 * End:
514 * end of autoopts/enumeration.c */
515