5 * Time-stamp: "2012-03-31 13:22:33 bkorb"
7 * Automated Options Paged Usage module.
9 * This routine will run run-on options through a pager so the
10 * user may examine, print or edit them at their leisure.
12 * This file is part of AutoOpts, a companion to AutoGen.
13 * AutoOpts is free software.
14 * AutoOpts is Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
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.
20 * The GNU Lesser General Public License, version 3 or later
21 * See the files "COPYING.lgplv3" and "COPYING.gplv3"
23 * The Modified Berkeley Software Distribution License
24 * See the file "COPYING.mbsd"
26 * These files have the following md5sums:
28 * 43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3
29 * 06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3
30 * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
33 static char const * pz_enum_err_fmt
;
35 /* = = = START-STATIC-FORWARD = = = */
37 enum_err(tOptions
* pOpts
, tOptDesc
* pOD
,
38 char const * const * paz_names
, int name_ct
);
41 find_name(char const * pzName
, tOptions
* pOpts
, tOptDesc
* pOD
,
42 char const * const * paz_names
, unsigned int name_ct
);
45 set_memb_usage(tOptions
* pOpts
, tOptDesc
* pOD
, char const * const * paz_names
,
46 unsigned int name_ct
);
49 set_memb_shell(tOptions
* pOpts
, tOptDesc
* pOD
, char const * const * paz_names
,
50 unsigned int name_ct
);
53 set_memb_names(tOptions
* pOpts
, tOptDesc
* pOD
, char const * const * paz_names
,
54 unsigned int name_ct
);
55 /* = = = END-STATIC-FORWARD = = = */
58 enum_err(tOptions
* pOpts
, tOptDesc
* pOD
,
59 char const * const * paz_names
, int name_ct
)
63 int ct_down
= name_ct
;
67 * A real "pOpts" pointer means someone messed up. Give a real error.
69 if (pOpts
> OPTPROC_EMIT_LIMIT
)
70 fprintf(option_usage_fp
, pz_enum_err_fmt
, pOpts
->pzProgName
,
71 pOD
->optArg
.argString
, pOD
->pz_Name
);
73 fprintf(option_usage_fp
, zValidKeys
, pOD
->pz_Name
);
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.
80 if (**paz_names
== 0x7F) {
87 * Figure out the maximum length of any name, plus the total length
91 char const * const * paz
= paz_names
;
94 size_t len
= strlen(*(paz
++)) + 1;
98 } while (--ct_down
> 0);
104 * IF any one entry is about 1/2 line or longer, print one per line
108 fprintf(option_usage_fp
, ENUM_ERR_SEP_LINE_FMT
, *(paz_names
++));
109 } while (--ct_down
> 0);
113 * ELSE IF they all fit on one line, then do so.
115 else if (ttl_len
< 76) {
116 fputc(' ', option_usage_fp
);
118 fputc(' ', option_usage_fp
);
119 fputs(*(paz_names
++), option_usage_fp
);
120 } while (--ct_down
> 0);
121 fputc(NL
, option_usage_fp
);
125 * Otherwise, columnize the output
128 unsigned int ent_no
= 0;
129 char zFmt
[16]; /* format for all-but-last entries on a line */
131 sprintf(zFmt
, ENUM_ERR_STR_WIDTH_FMT
, (int)max_len
);
132 max_len
= 78 / max_len
; /* max_len is now max entries on a line */
133 fputs(TWO_SPACES_STR
, option_usage_fp
);
136 * Loop through all but the last entry
139 while (--ct_down
> 0) {
140 if (++ent_no
== max_len
) {
142 * Last entry on a line. Start next line, too.
144 fprintf(option_usage_fp
, NLSTR_SPACE_FMT
, *(paz_names
++));
149 fprintf(option_usage_fp
, zFmt
, *(paz_names
++) );
151 fprintf(option_usage_fp
, NLSTR_FMT
, *paz_names
);
154 if (pOpts
> OPTPROC_EMIT_LIMIT
) {
155 fprintf(option_usage_fp
, zIntRange
, hidden
, name_ct
- 1 + hidden
);
157 (*(pOpts
->pUsageProc
))(pOpts
, EXIT_FAILURE
);
161 if (OPTST_GET_ARGTYPE(pOD
->fOptState
) == OPARG_TYPE_MEMBERSHIP
) {
162 fprintf(option_usage_fp
, zLowerBits
, name_ct
);
163 fputs(zSetMemberSettings
, option_usage_fp
);
165 fprintf(option_usage_fp
, zIntRange
, hidden
, name_ct
- 1 + hidden
);
170 * Convert a name or number into a binary number.
171 * "~0" and "-1" will be converted to the largest value in the enumeration.
173 * @param pzName the keyword name (number) to convert
174 * @param pOpts the program's option descriptor
175 * @param pOD the option descriptor for this option
176 * @param paz_names the list of keywords for this option
177 * @param name_ct the count of keywords
180 find_name(char const * pzName
, tOptions
* pOpts
, tOptDesc
* pOD
,
181 char const * const * paz_names
, unsigned int name_ct
)
184 * Return the matching index as a pointer sized integer.
185 * The result gets stashed in a char* pointer.
187 uintptr_t res
= name_ct
;
188 size_t len
= strlen((char*)pzName
);
191 if (IS_DEC_DIGIT_CHAR(*pzName
)) {
192 char * pz
= (char *)(void *)pzName
;
193 unsigned long val
= strtoul(pz
, &pz
, 0);
194 if ((*pz
== NUL
) && (val
< name_ct
))
195 return (uintptr_t)val
;
196 pz_enum_err_fmt
= zTooLarge
;
197 option_usage_fp
= stderr
;
198 enum_err(pOpts
, pOD
, paz_names
, (int)name_ct
);
202 if (IS_INVERSION_CHAR(*pzName
) && (pzName
[2] == NUL
)) {
203 if ( ((pzName
[0] == '~') && (pzName
[1] == '0'))
204 || ((pzName
[0] == '-') && (pzName
[1] == '1')))
205 return (uintptr_t)(name_ct
- 1);
210 * Look for an exact match, but remember any partial matches.
211 * Multiple partial matches means we have an ambiguous match.
213 for (idx
= 0; idx
< name_ct
; idx
++) {
214 if (strncmp((char*)paz_names
[idx
], (char*)pzName
, len
) == 0) {
215 if (paz_names
[idx
][len
] == NUL
)
216 return idx
; /* full match */
219 res
= idx
; /* save partial match */
221 res
= ~0; /* may yet find full match */
226 return res
; /* partial match */
230 pz_enum_err_fmt
= (res
== name_ct
) ? zNoKey
: zAmbigKey
;
231 option_usage_fp
= stderr
;
232 enum_err(pOpts
, pOD
, paz_names
, (int)name_ct
);
237 /*=export_func optionKeywordName
238 * what: Convert between enumeration values and strings
241 * arg: tOptDesc*, pOD, enumeration option description
242 * arg: unsigned int, enum_val, the enumeration value to map
244 * ret_type: char const *
245 * ret_desc: the enumeration name from const memory
247 * doc: This converts an enumeration value into the matching string.
250 optionKeywordName(tOptDesc
* pOD
, unsigned int enum_val
)
253 .optArg
.argEnum
= enum_val
};
255 (*(pOD
->pOptProc
))(OPTPROC_RETURN_VALNAME
, &od
);
256 return od
.optArg
.argString
;
260 /*=export_func optionEnumerationVal
261 * what: Convert from a string to an enumeration value
264 * arg: tOptions*, pOpts, the program options descriptor
265 * arg: tOptDesc*, pOD, enumeration option description
266 * arg: char const * const *, paz_names, list of enumeration names
267 * arg: unsigned int, name_ct, number of names in list
269 * ret_type: uintptr_t
270 * ret_desc: the enumeration value
272 * doc: This converts the optArg.argString string from the option description
273 * into the index corresponding to an entry in the name list.
274 * This will match the generated enumeration value.
275 * Full matches are always accepted. Partial matches are accepted
276 * if there is only one partial match.
279 optionEnumerationVal(tOptions
* pOpts
, tOptDesc
* pOD
,
280 char const * const * paz_names
, unsigned int name_ct
)
285 * IF the program option descriptor pointer is invalid,
286 * then it is some sort of special request.
288 switch ((uintptr_t)pOpts
) {
289 case (uintptr_t)OPTPROC_EMIT_USAGE
:
291 * print the list of enumeration names.
293 enum_err(pOpts
, pOD
, paz_names
, (int)name_ct
);
296 case (uintptr_t)OPTPROC_EMIT_SHELL
:
298 unsigned int ix
= pOD
->optArg
.argEnum
;
300 * print the name string.
303 printf(INVALID_FMT
, ix
);
305 fputs(paz_names
[ ix
], stdout
);
310 case (uintptr_t)OPTPROC_RETURN_VALNAME
:
312 unsigned int ix
= pOD
->optArg
.argEnum
;
314 * Replace the enumeration value with the name string.
317 return (uintptr_t)INVALID_STR
;
319 pOD
->optArg
.argString
= paz_names
[ix
];
324 res
= find_name(pOD
->optArg
.argString
, pOpts
, pOD
, paz_names
, name_ct
);
326 if (pOD
->fOptState
& OPTST_ALLOC_ARG
) {
327 AGFREE(pOD
->optArg
.argString
);
328 pOD
->fOptState
&= ~OPTST_ALLOC_ARG
;
329 pOD
->optArg
.argString
= NULL
;
337 set_memb_usage(tOptions
* pOpts
, tOptDesc
* pOD
, char const * const * paz_names
,
338 unsigned int name_ct
)
341 * print the list of enumeration names.
344 enum_err(OPTPROC_EMIT_USAGE
, pOD
, paz_names
, (int)name_ct
);
348 set_memb_shell(tOptions
* pOpts
, tOptDesc
* pOD
, char const * const * paz_names
,
349 unsigned int name_ct
)
352 * print the name string.
355 uintptr_t bits
= (uintptr_t)pOD
->optCookie
;
359 bits
&= ((uintptr_t)1 << (uintptr_t)name_ct
) - (uintptr_t)1;
363 if (len
++ > 0) fputs(OR_STR
, stdout
);
364 fputs(paz_names
[ix
], stdout
);
366 if (++ix
>= name_ct
) break;
372 set_memb_names(tOptions
* pOpts
, tOptDesc
* pOD
, char const * const * paz_names
,
373 unsigned int name_ct
)
376 uintptr_t bits
= (uintptr_t)pOD
->optCookie
;
378 size_t len
= NONE_STR_LEN
+ 1;
381 bits
&= ((uintptr_t)1 << (uintptr_t)name_ct
) - (uintptr_t)1;
384 * Replace the enumeration value with the name string.
385 * First, determine the needed length, then allocate and fill in.
389 len
+= strlen(paz_names
[ix
]) + PLUS_STR_LEN
+ 1;
390 if (++ix
>= name_ct
) break;
394 pOD
->optArg
.argString
= pz
= AGALOC(len
, "enum");
397 * Start by clearing all the bits. We want to turn off any defaults
398 * because we will be restoring to current state, not adding to
399 * the default set of bits.
401 memcpy(pz
, NONE_STR
, NONE_STR_LEN
);
403 bits
= (uintptr_t)pOD
->optCookie
;
404 bits
&= ((uintptr_t)1 << (uintptr_t)name_ct
) - (uintptr_t)1;
409 size_t nln
= strlen(paz_names
[ix
]);
410 memcpy(pz
, PLUS_STR
, PLUS_STR_LEN
);
411 memcpy(pz
+PLUS_STR_LEN
, paz_names
[ix
], nln
);
412 pz
+= nln
+ PLUS_STR_LEN
;
414 if (++ix
>= name_ct
) break;
420 /*=export_func optionSetMembers
421 * what: Convert between bit flag values and strings
424 * arg: tOptions*, pOpts, the program options descriptor
425 * arg: tOptDesc*, pOD, enumeration option description
426 * arg: char const * const *,
427 * paz_names, list of enumeration names
428 * arg: unsigned int, name_ct, number of names in list
430 * doc: This converts the optArg.argString string from the option description
431 * into the index corresponding to an entry in the name list.
432 * This will match the generated enumeration value.
433 * Full matches are always accepted. Partial matches are accepted
434 * if there is only one partial match.
437 optionSetMembers(tOptions
* pOpts
, tOptDesc
* pOD
,
438 char const* const * paz_names
, unsigned int name_ct
)
441 * IF the program option descriptor pointer is invalid,
442 * then it is some sort of special request.
444 switch ((uintptr_t)pOpts
) {
445 case (uintptr_t)OPTPROC_EMIT_USAGE
:
446 set_memb_usage(pOpts
, pOD
, paz_names
, name_ct
);
449 case (uintptr_t)OPTPROC_EMIT_SHELL
:
450 set_memb_shell(pOpts
, pOD
, paz_names
, name_ct
);
453 case (uintptr_t)OPTPROC_RETURN_VALNAME
:
454 set_memb_names(pOpts
, pOD
, paz_names
, name_ct
);
461 if ((pOD
->fOptState
& OPTST_RESET
) != 0)
465 char const * pzArg
= pOD
->optArg
.argString
;
467 if ((pzArg
== NULL
) || (*pzArg
== NUL
)) {
468 pOD
->optCookie
= (void*)0;
472 res
= (uintptr_t)pOD
->optCookie
;
476 pzArg
= SPN_SET_SEPARATOR_CHARS(pzArg
);
477 iv
= (*pzArg
== '!');
479 pzArg
= SPN_WHITESPACE_CHARS(pzArg
+1);
481 len
= BRK_SET_SEPARATOR_CHARS(pzArg
) - pzArg
;
485 if ((len
== 3) && (strncmp(pzArg
, zAll
, 3) == 0)) {
490 else if ((len
== 4) && (strncmp(pzArg
, zNone
, 4) == 0)) {
496 uintptr_t bit
= strtoul(pzArg
, &pz
, 0);
498 if (pz
!= pzArg
+ len
) {
499 char z
[ AO_NAME_SIZE
];
501 unsigned int shift_ct
;
504 if (len
>= AO_NAME_LIMIT
)
506 memcpy(z
, pzArg
, (size_t)len
);
513 shift_ct
= find_name(p
, pOpts
, pOD
, paz_names
, name_ct
);
514 if (shift_ct
>= name_ct
) {
515 pOD
->optCookie
= (void*)0;
518 bit
= 1UL << shift_ct
;
525 if (pzArg
[len
] == NUL
)
529 if (name_ct
< (8 * sizeof(uintptr_t))) {
530 res
&= (1UL << name_ct
) - 1UL;
533 pOD
->optCookie
= (void*)res
;
540 * c-file-style: "stroustrup"
541 * indent-tabs-mode: nil
543 * end of autoopts/enumeration.c */