PageRenderTime 70ms CodeModel.GetById 29ms app.highlight 35ms RepoModel.GetById 2ms app.codeStats 0ms

/contrib/ntp/libopts/enumeration.c

https://bitbucket.org/freebsd/freebsd-head/
C | 498 lines | 270 code | 55 blank | 173 comment | 80 complexity | 8da93fde6382cac4008082a0dc7b442f MD5 | raw file
  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
 55tSCC*  pz_enum_err_fmt;
 56
 57/* = = = START-STATIC-FORWARD = = = */
 58/* static forward declarations maintained by :mkfwd */
 59static void
 60enumError(
 61    tOptions*     pOpts,
 62    tOptDesc*     pOD,
 63    tCC* const *  paz_names,
 64    int           name_ct );
 65
 66static uintptr_t
 67findName(
 68    tCC*          pzName,
 69    tOptions*     pOpts,
 70    tOptDesc*     pOD,
 71    tCC* const *  paz_names,
 72    unsigned int  name_ct );
 73/* = = = END-STATIC-FORWARD = = = */
 74
 75static void
 76enumError(
 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
173static uintptr_t
174findName(
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=*/
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))( (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=*/
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 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=*/
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 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 */