1:
2: /**
3: * \file enumeration.c
4: *
5: * Time-stamp: "2011-04-06 10:48:22 bkorb"
6: *
7: * Automated Options Paged Usage module.
8: *
9: * This routine will run run-on options through a pager so the
10: * user may examine, print or edit them at their leisure.
11: *
12: * This file is part of AutoOpts, a companion to AutoGen.
13: * AutoOpts is free software.
14: * AutoOpts is Copyright (c) 1992-2011 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 * pz_enum_err_fmt;
34:
35: /* = = = START-STATIC-FORWARD = = = */
36: static void
37: enum_err(tOptions * pOpts, tOptDesc * pOD,
38: char const * const * paz_names, int name_ct);
39:
40: static uintptr_t
41: find_name(char const * pzName, tOptions * pOpts, tOptDesc * pOD,
42: char const * const * paz_names, unsigned int name_ct);
43:
44: static void
45: set_memb_usage(tOptions * pOpts, tOptDesc * pOD, char const * const * paz_names,
46: unsigned int name_ct);
47:
48: static void
49: set_memb_shell(tOptions * pOpts, tOptDesc * pOD, char const * const * paz_names,
50: unsigned int name_ct);
51:
52: static void
53: set_memb_names(tOptions * pOpts, tOptDesc * pOD, char const * const * paz_names,
54: unsigned int name_ct);
55: /* = = = END-STATIC-FORWARD = = = */
56:
57: static void
58: enum_err(tOptions * pOpts, tOptDesc * pOD,
59: char const * const * paz_names, 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: char const * 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: int 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: if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_MEMBERSHIP) {
162: fprintf(option_usage_fp, zLowerBits, name_ct);
163: fputs(zSetMemberSettings, option_usage_fp);
164: } else {
165: fprintf(option_usage_fp, zIntRange, hidden, name_ct - 1 + hidden);
166: }
167: }
168:
169:
170: static uintptr_t
171: find_name(char const * pzName, tOptions * pOpts, tOptDesc * pOD,
172: char const * const * paz_names, unsigned int name_ct)
173: {
174: /*
175: * Return the matching index as a pointer sized integer.
176: * The result gets stashed in a char* pointer.
177: */
178: uintptr_t res = name_ct;
179: size_t len = strlen((char*)pzName);
180: uintptr_t idx;
181:
182: if (IS_DEC_DIGIT_CHAR(*pzName)) {
183: char * pz = (char *)(void *)pzName;
184: unsigned long val = strtoul(pz, &pz, 0);
185: if ((*pz == NUL) && (val < name_ct))
186: return (uintptr_t)val;
187: enum_err(pOpts, pOD, paz_names, (int)name_ct);
188: return name_ct;
189: }
190:
191: /*
192: * Look for an exact match, but remember any partial matches.
193: * Multiple partial matches means we have an ambiguous match.
194: */
195: for (idx = 0; idx < name_ct; idx++) {
196: if (strncmp((char*)paz_names[idx], (char*)pzName, len) == 0) {
197: if (paz_names[idx][len] == NUL)
198: return idx; /* full match */
199:
200: res = (res != name_ct) ? ~0 : idx; /* save partial match */
201: }
202: }
203:
204: if (res < name_ct)
205: return res; /* partial match */
206:
207: pz_enum_err_fmt = (res == name_ct) ? zNoKey : zAmbigKey;
208: option_usage_fp = stderr;
209: enum_err(pOpts, pOD, paz_names, (int)name_ct);
210: return name_ct;
211: }
212:
213:
214: /*=export_func optionKeywordName
215: * what: Convert between enumeration values and strings
216: * private:
217: *
218: * arg: tOptDesc*, pOD, enumeration option description
219: * arg: unsigned int, enum_val, the enumeration value to map
220: *
221: * ret_type: char const *
222: * ret_desc: the enumeration name from const memory
223: *
224: * doc: This converts an enumeration value into the matching string.
225: =*/
226: char const *
227: optionKeywordName(tOptDesc * pOD, unsigned int enum_val)
228: {
229: tOptDesc od;
230:
231: od.optArg.argEnum = enum_val;
232: (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, &od );
233: return od.optArg.argString;
234: }
235:
236:
237: /*=export_func optionEnumerationVal
238: * what: Convert from a string to an enumeration value
239: * private:
240: *
241: * arg: tOptions*, pOpts, the program options descriptor
242: * arg: tOptDesc*, pOD, enumeration option description
243: * arg: char const * const *, paz_names, list of enumeration names
244: * arg: unsigned int, name_ct, number of names in list
245: *
246: * ret_type: uintptr_t
247: * ret_desc: the enumeration value
248: *
249: * doc: This converts the optArg.argString string from the option description
250: * into the index corresponding to an entry in the name list.
251: * This will match the generated enumeration value.
252: * Full matches are always accepted. Partial matches are accepted
253: * if there is only one partial match.
254: =*/
255: uintptr_t
256: optionEnumerationVal(tOptions * pOpts, tOptDesc * pOD,
257: char const * const * paz_names, unsigned int name_ct)
258: {
259: uintptr_t res = 0UL;
260:
261: /*
262: * IF the program option descriptor pointer is invalid,
263: * then it is some sort of special request.
264: */
265: switch ((uintptr_t)pOpts) {
266: case (uintptr_t)OPTPROC_EMIT_USAGE:
267: /*
268: * print the list of enumeration names.
269: */
270: enum_err(pOpts, pOD, paz_names, (int)name_ct);
271: break;
272:
273: case (uintptr_t)OPTPROC_EMIT_SHELL:
274: {
275: unsigned int ix = pOD->optArg.argEnum;
276: /*
277: * print the name string.
278: */
279: if (ix >= name_ct)
280: printf("INVALID-%d", ix);
281: else
282: fputs(paz_names[ ix ], stdout);
283:
284: break;
285: }
286:
287: case (uintptr_t)OPTPROC_RETURN_VALNAME:
288: {
289: tSCC zInval[] = "*INVALID*";
290: unsigned int ix = pOD->optArg.argEnum;
291: /*
292: * Replace the enumeration value with the name string.
293: */
294: if (ix >= name_ct)
295: return (uintptr_t)zInval;
296:
297: pOD->optArg.argString = paz_names[ix];
298: break;
299: }
300:
301: default:
302: res = find_name(pOD->optArg.argString, pOpts, pOD, paz_names, name_ct);
303:
304: if (pOD->fOptState & OPTST_ALLOC_ARG) {
305: AGFREE(pOD->optArg.argString);
306: pOD->fOptState &= ~OPTST_ALLOC_ARG;
307: pOD->optArg.argString = NULL;
308: }
309: }
310:
311: return res;
312: }
313:
314: static void
315: set_memb_usage(tOptions * pOpts, tOptDesc * pOD, char const * const * paz_names,
316: unsigned int name_ct)
317: {
318: /*
319: * print the list of enumeration names.
320: */
321: enum_err(OPTPROC_EMIT_USAGE, pOD, paz_names, (int)name_ct );
322: }
323:
324: static void
325: set_memb_shell(tOptions * pOpts, tOptDesc * pOD, char const * const * paz_names,
326: unsigned int name_ct)
327: {
328: /*
329: * print the name string.
330: */
331: int ix = 0;
332: uintptr_t bits = (uintptr_t)pOD->optCookie;
333: size_t len = 0;
334:
335: bits &= ((uintptr_t)1 << (uintptr_t)name_ct) - (uintptr_t)1;
336:
337: while (bits != 0) {
338: if (bits & 1) {
339: if (len++ > 0) fputs(" | ", stdout);
340: fputs(paz_names[ix], stdout);
341: }
342: if (++ix >= name_ct) break;
343: bits >>= 1;
344: }
345: }
346:
347: static void
348: set_memb_names(tOptions * pOpts, tOptDesc * pOD, char const * const * paz_names,
349: unsigned int name_ct)
350: {
351: static char const none[] = "none";
352: static char const plus[3] = " + ";
353:
354: char * pz;
355: uintptr_t bits = (uintptr_t)pOD->optCookie;
356: int ix = 0;
357: size_t len = sizeof(none);
358:
359: bits &= ((uintptr_t)1 << (uintptr_t)name_ct) - (uintptr_t)1;
360:
361: /*
362: * Replace the enumeration value with the name string.
363: * First, determine the needed length, then allocate and fill in.
364: */
365: while (bits != 0) {
366: if (bits & 1)
367: len += strlen(paz_names[ix]) + sizeof(plus);
368: if (++ix >= name_ct) break;
369: bits >>= 1;
370: }
371:
372: pOD->optArg.argString = pz = AGALOC(len, "enum name");
373:
374: /*
375: * Start by clearing all the bits. We want to turn off any defaults
376: * because we will be restoring to current state, not adding to
377: * the default set of bits.
378: */
379: memcpy(pz, none, sizeof(none)-1);
380: pz += sizeof(none)-1;
381: bits = (uintptr_t)pOD->optCookie;
382: bits &= ((uintptr_t)1 << (uintptr_t)name_ct) - (uintptr_t)1;
383: ix = 0;
384:
385: while (bits != 0) {
386: if (bits & 1) {
387: size_t nln = strlen(paz_names[ix]);
388: memcpy(pz, plus, sizeof(plus));
389: memcpy(pz+sizeof(plus), paz_names[ix], nln);
390: pz += strlen(paz_names[ix]) + 3;
391: }
392: if (++ix >= name_ct) break;
393: bits >>= 1;
394: }
395: *pz = NUL;
396: }
397:
398: /*=export_func optionSetMembers
399: * what: Convert between bit flag values and strings
400: * private:
401: *
402: * arg: tOptions*, pOpts, the program options descriptor
403: * arg: tOptDesc*, pOD, enumeration option description
404: * arg: char const * const *,
405: * paz_names, list of enumeration names
406: * arg: unsigned int, name_ct, number of names in list
407: *
408: * doc: This converts the optArg.argString string from the option description
409: * into the index corresponding to an entry in the name list.
410: * This will match the generated enumeration value.
411: * Full matches are always accepted. Partial matches are accepted
412: * if there is only one partial match.
413: =*/
414: void
415: optionSetMembers(tOptions * pOpts, tOptDesc * pOD,
416: char const* const * paz_names, unsigned int name_ct)
417: {
418: /*
419: * IF the program option descriptor pointer is invalid,
420: * then it is some sort of special request.
421: */
422: switch ((uintptr_t)pOpts) {
423: case (uintptr_t)OPTPROC_EMIT_USAGE:
424: set_memb_usage(pOpts, pOD, paz_names, name_ct);
425: return;
426:
427: case (uintptr_t)OPTPROC_EMIT_SHELL:
428: set_memb_shell(pOpts, pOD, paz_names, name_ct);
429: return;
430:
431: case (uintptr_t)OPTPROC_RETURN_VALNAME:
432: set_memb_names(pOpts, pOD, paz_names, name_ct);
433: return;
434:
435: default:
436: break;
437: }
438:
439: if ((pOD->fOptState & OPTST_RESET) != 0)
440: return;
441:
442: {
443: char const* pzArg = pOD->optArg.argString;
444: uintptr_t res;
445: if ((pzArg == NULL) || (*pzArg == NUL)) {
446: pOD->optCookie = (void*)0;
447: return;
448: }
449:
450: res = (uintptr_t)pOD->optCookie;
451: for (;;) {
452: tSCC zSpn[] = " ,|+\t\r\f\n";
453: int iv, len;
454:
455: pzArg += strspn(pzArg, zSpn);
456: iv = (*pzArg == '!');
457: if (iv)
458: pzArg += strspn(pzArg+1, zSpn) + 1;
459:
460: len = strcspn(pzArg, zSpn);
461: if (len == 0)
462: break;
463:
464: if ((len == 3) && (strncmp(pzArg, zAll, 3) == 0)) {
465: if (iv)
466: res = 0;
467: else res = ~0UL;
468: }
469: else if ((len == 4) && (strncmp(pzArg, zNone, 4) == 0)) {
470: if (! iv)
471: res = 0;
472: }
473: else do {
474: char* pz;
475: uintptr_t bit = strtoul(pzArg, &pz, 0);
476:
477: if (pz != pzArg + len) {
478: char z[ AO_NAME_SIZE ];
479: char const* p;
480: int shift_ct;
481:
482: if (*pz != NUL) {
483: if (len >= AO_NAME_LIMIT)
484: break;
485: memcpy(z, pzArg, (size_t)len);
486: z[len] = NUL;
487: p = z;
488: } else {
489: p = pzArg;
490: }
491:
492: shift_ct = find_name(p, pOpts, pOD, paz_names, name_ct);
493: if (shift_ct >= name_ct) {
494: pOD->optCookie = (void*)0;
495: return;
496: }
497: bit = 1UL << shift_ct;
498: }
499: if (iv)
500: res &= ~bit;
501: else res |= bit;
502: } while (0);
503:
504: if (pzArg[len] == NUL)
505: break;
506: pzArg += len + 1;
507: }
508: if (name_ct < (8 * sizeof(uintptr_t))) {
509: res &= (1UL << name_ct) - 1UL;
510: }
511:
512: pOD->optCookie = (void*)res;
513: }
514: }
515:
516: /*
517: * Local Variables:
518: * mode: C
519: * c-file-style: "stroustrup"
520: * indent-tabs-mode: nil
521: * End:
522: * end of autoopts/enumeration.c */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>