11
11
/* const NAME_MASK *table;
12
12
/* const char *names;
14
/* long long_name_mask(context, table, names)
15
/* const char *context;
16
/* const LONG_NAME_MASK *table;
14
19
/* const char *str_name_mask(context, table, mask)
15
20
/* const char *context;
16
21
/* const NAME_MASK *table;
24
/* const char *str_long_name_mask(context, table, mask)
25
/* const char *context;
26
/* const LONG_NAME_MASK *table;
19
29
/* int name_mask_opt(context, table, names, flags)
20
30
/* const char *context;
21
31
/* const NAME_MASK *table;
22
32
/* const char *names;
35
/* long long_name_mask_opt(context, table, names, flags)
36
/* const char *context;
37
/* const LONG_NAME_MASK *table;
25
41
/* int name_mask_delim_opt(context, table, names, delim, flags)
26
42
/* const char *context;
27
43
/* const NAME_MASK *table;
29
45
/* const char *delim;
48
/* long long_name_mask_delim_opt(context, table, names, delim, flags)
49
/* const char *context;
50
/* const LONG_NAME_MASK *table;
32
55
/* const char *str_name_mask_opt(buf, context, table, mask, flags)
34
57
/* const char *context;
35
58
/* const NAME_MASK *table;
62
/* const char *str_long_name_mask_opt(buf, context, table, mask, flags)
64
/* const char *context;
65
/* const LONG_NAME_MASK *table;
39
69
/* name_mask() takes a null-terminated \fItable\fR with (name, mask)
40
70
/* values and computes the bit-wise OR of the masks that correspond
41
71
/* to the names listed in the \fInames\fR argument, separated by
42
/* comma and/or whitespace characters.
72
/* comma and/or whitespace characters. The "long_" version returns
73
/* a "long int" bitmask, rather than an "int" bitmask.
44
/* str_name_mask() translates a mask into its equivalent names.
75
/* str_name_mask() translates a mask into its equlvalent names.
45
76
/* The result is written to a static buffer that is overwritten
77
/* upon each call. The "long_" version converts a "long int"
78
/* bitmask, rather than an "int" bitmask.
48
80
/* name_mask_opt() and str_name_mask_opt() are extended versions
49
81
/* with additional fine control. name_mask_delim_opt() supports
63
95
/* A list of names that is to be converted into a bit mask.
67
/* Bit-wise OR of zero or more of the following:
69
99
/* Delimiter characters to use instead of whitespace and commas.
101
/* Bit-wise OR of one or more of the following. Where features
102
/* would have conflicting results (e.g., FATAL versus IGNORE),
103
/* the feature that takes precedence is described first.
105
/* When converting from string to mask, at least one of the
106
/* following must be specified: NAME_MASK_FATAL, NAME_MASK_RETURN,
107
/* NAME_MASK_WARN or NAME_MASK_IGNORE.
109
/* When converting from mask to string, at least one of the
110
/* following must be specified: NAME_MASK_NUMBER, NAME_MASK_FATAL,
111
/* NAME_MASK_RETURN, NAME_MASK_WARN or NAME_MASK_IGNORE.
113
/* .IP NAME_MASK_NUMBER
114
/* When converting from string to mask, accept hexadecimal
115
/* inputs starting with "0x" followed by hexadecimal digits.
116
/* Each hexadecimal input may specify multiple bits. This
117
/* feature is ignored for hexadecimal inputs that cannot be
118
/* converted (malformed, out of range, etc.).
120
/* When converting from mask to string, represent bits not
121
/* defined in \fItable\fR as "0x" followed by hexadecimal
122
/* digits. This conversion always succeeds.
71
123
/* .IP NAME_MASK_FATAL
72
/* Require that all names listed in \fIname\fR exist in \fItable\fR,
73
/* and that all bits listed in \fImask\fR exist in \fItable\fR.
74
/* Terminate with a fatal run-time error if this condition is not met.
75
/* This feature is enabled by default when calling name_mask()
76
/* or str_name_mask().
124
/* Require that all names listed in \fIname\fR exist in
125
/* \fItable\fR or that they can be parsed as a hexadecimal
126
/* string, and require that all bits listed in \fImask\fR exist
127
/* in \fItable\fR or that they can be converted to hexadecimal
128
/* string. Terminate with a fatal run-time error if this
129
/* condition is not met. This feature is enabled by default
130
/* when calling name_mask() or str_name_mask().
77
131
/* .IP NAME_MASK_RETURN
78
/* Require that all names listed in \fIname\fR exist in \fItable\fR,
79
/* and that all bits listed in \fImask\fR exist in \fItable\fR.
80
/* Log a warning, and return 0 (name_mask()) or a null pointer
81
/* (str_name_mask()) if this condition is not met.
82
/* .IP NAME_MASK_NUMBER
83
/* Require that all bits listed in \fImask\fR exist in \fItable\fR.
84
/* For unrecognized bits, print the numerical hexadecimal form.
132
/* Require that all names listed in \fIname\fR exist in
133
/* \fItable\fR or that they can be parsed as a hexadecimal
134
/* string, and require that all bits listed in \fImask\fR exist
135
/* in \fItable\fR or that they can be converted to hexadecimal
136
/* string. Log a warning, and return 0 (name_mask()) or a
137
/* null pointer (str_name_mask()) if this condition is not
138
/* met. This feature is not enabled by default when calling
139
/* name_mask() or str_name_mask().
140
/* .IP NAME_MASK_WARN
141
/* Require that all names listed in \fIname\fR exist in
142
/* \fItable\fR or that they can be parsed as a hexadecimal
143
/* string, and require that all bits listed in \fImask\fR exist
144
/* in \fItable\fR or that they can be converted to hexadecimal
145
/* string. Log a warning if this condition is not met, continue
146
/* processing, and return all valid bits or names. This feature
147
/* is not enabled by default when calling name_mask() or
149
/* .IP NAME_MASK_IGNORE
150
/* Silently ignore names listed in \fIname\fR that don't exist
151
/* in \fItable\fR and that can't be parsed as a hexadecimal
152
/* string, and silently ignore bits listed in \fImask\fR that
153
/* don't exist in \fItable\fR and that can't be converted to
154
/* hexadecimal string.
85
155
/* .IP NAME_MASK_ANY_CASE
86
156
/* Enable case-insensitive matching.
87
157
/* This feature is not enabled by default when calling name_mask();
197
286
for (np = table; mask != 0; np++) {
198
287
if (np->name == 0) {
199
if (flags & NAME_MASK_FATAL) {
288
if (flags & NAME_MASK_NUMBER) {
289
vstring_sprintf_append(buf, "0x%x%c", mask, delim);
290
} else if (flags & NAME_MASK_FATAL) {
200
291
msg_fatal("%s: unknown %s bit in mask: 0x%x",
201
292
myname, context, mask);
202
293
} else if (flags & NAME_MASK_RETURN) {
203
294
msg_warn("%s: unknown %s bit in mask: 0x%x",
204
295
myname, context, mask);
206
} else if (flags & NAME_MASK_NUMBER) {
207
vstring_sprintf_append(buf, "0x%x%c", mask, delim);
211
if (mask & np->mask) {
213
vstring_sprintf_append(buf, "%s%c", np->name, delim);
216
if ((len = VSTRING_LEN(buf)) > 0)
217
vstring_truncate(buf, len - 1);
218
VSTRING_TERMINATE(buf);
297
} else if (flags & NAME_MASK_WARN) {
298
msg_warn("%s: unknown %s bit in mask: 0x%x",
299
myname, context, mask);
303
if (mask & np->mask) {
305
vstring_sprintf_append(buf, "%s%c", np->name, delim);
308
if ((len = VSTRING_LEN(buf)) > 0)
309
vstring_truncate(buf, len - 1);
310
VSTRING_TERMINATE(buf);
315
/* long_name_mask_delim_opt - compute mask corresponding to list of names */
317
long long_name_mask_delim_opt(const char *context,
318
const LONG_NAME_MASK * table,
319
const char *names, const char *delim,
322
const char *myname = "name_mask";
323
char *saved_names = mystrdup(names);
324
char *bp = saved_names;
326
const LONG_NAME_MASK *np;
328
int (*lookup) (const char *, const char *);
331
if ((flags & NAME_MASK_REQUIRED) == 0)
332
msg_panic("%s: missing NAME_MASK_FATAL/RETURN/WARN/IGNORE flag",
335
if (flags & NAME_MASK_ANY_CASE)
341
* Break up the names string, and look up each component in the table. If
342
* the name is found, merge its mask with the result.
344
while ((name = mystrtok(&bp, delim)) != 0) {
345
for (np = table; /* void */ ; np++) {
347
if ((flags & NAME_MASK_NUMBER)
348
&& hex_to_ulong(name, ~0UL, &ulval)) {
350
} else if (flags & NAME_MASK_FATAL) {
351
msg_fatal("unknown %s value \"%s\" in \"%s\"",
352
context, name, names);
353
} else if (flags & NAME_MASK_RETURN) {
354
msg_warn("unknown %s value \"%s\" in \"%s\"",
355
context, name, names);
357
} else if (flags & NAME_MASK_WARN) {
358
msg_warn("unknown %s value \"%s\" in \"%s\"",
359
context, name, names);
363
if (lookup(name, np->name) == 0) {
365
msg_info("%s: %s", myname, name);
376
/* str_long_name_mask_opt - mask to string */
378
const char *str_long_name_mask_opt(VSTRING *buf, const char *context,
379
const LONG_NAME_MASK * table,
380
long mask, int flags)
382
const char *myname = "name_mask";
384
static VSTRING *my_buf = 0;
385
int delim = (flags & NAME_MASK_COMMA ? ',' :
386
(flags & NAME_MASK_PIPE ? '|' : ' '));
387
const LONG_NAME_MASK *np;
389
if ((flags & STR_NAME_MASK_REQUIRED) == 0)
390
msg_panic("%s: missing NAME_MASK_NUMBER/FATAL/RETURN/WARN/IGNORE flag",
395
my_buf = vstring_alloc(1);
400
for (np = table; mask != 0; np++) {
402
if (flags & NAME_MASK_NUMBER) {
403
vstring_sprintf_append(buf, "0x%lx%c", mask, delim);
404
} else if (flags & NAME_MASK_FATAL) {
405
msg_fatal("%s: unknown %s bit in mask: 0x%lx",
406
myname, context, mask);
407
} else if (flags & NAME_MASK_RETURN) {
408
msg_warn("%s: unknown %s bit in mask: 0x%lx",
409
myname, context, mask);
411
} else if (flags & NAME_MASK_WARN) {
412
msg_warn("%s: unknown %s bit in mask: 0x%lx",
413
myname, context, mask);
417
if (mask & np->mask) {
419
vstring_sprintf_append(buf, "%s%c", np->name, delim);
422
if ((len = VSTRING_LEN(buf)) > 0)
423
vstring_truncate(buf, len - 1);
424
VSTRING_TERMINATE(buf);
429
/* hex_to_ulong - 0x... to unsigned long or smaller */
431
static int hex_to_ulong(char *value, unsigned long mask, unsigned long *ulp)
433
unsigned long result;
436
if (strncasecmp(value, "0x", 2) != 0)
440
* Check for valid hex number. Since the value starts with 0x, strtoul()
441
* will not allow a negative sign before the first nibble. So we don't
442
* need to worry about explicit +/- signs.
445
result = strtoul(value, &cp, 16);
446
if (*cp != '\0' || errno == ERANGE)
450
*ulp = (result & mask);
451
return (*ulp == result);
228
459
#include <stdlib.h>
229
460
#include <vstream.h>
461
#include <vstring_vstream.h>
231
463
int main(int argc, char **argv)
233
static const NAME_MASK table[] = {
465
static const NAME_MASK demo_table[] = {
241
VSTRING *buf = vstring_alloc(1);
472
static const NAME_MASK feature_table[] = {
473
"DEFAULT", NAME_MASK_DEFAULT,
474
"FATAL", NAME_MASK_FATAL,
475
"ANY_CASE", NAME_MASK_ANY_CASE,
476
"RETURN", NAME_MASK_RETURN,
477
"COMMA", NAME_MASK_COMMA,
478
"PIPE", NAME_MASK_PIPE,
479
"NUMBER", NAME_MASK_NUMBER,
480
"WARN", NAME_MASK_WARN,
481
"IGNORE", NAME_MASK_IGNORE,
485
int out_feature_mask;
487
const char *demo_str;
488
VSTRING *out_buf = vstring_alloc(1);
489
VSTRING *in_buf = vstring_alloc(1);
243
while (--argc && *++argv) {
244
mask = name_mask("test", table, *argv);
492
msg_fatal("usage: %s in-feature-mask out-feature-mask", argv[0]);
493
in_feature_mask = name_mask(argv[1], feature_table, argv[1]);
494
out_feature_mask = name_mask(argv[2], feature_table, argv[2]);
495
while (vstring_get_nonl(in_buf, VSTREAM_IN) != VSTREAM_EOF) {
496
demo_mask = name_mask_opt("name", demo_table,
497
STR(in_buf), in_feature_mask);
498
demo_str = str_name_mask_opt(out_buf, "mask", demo_table,
499
demo_mask, out_feature_mask);
245
500
vstream_printf("%s -> 0x%x -> %s\n",
246
*argv, mask, str_name_mask("mask_test", table, mask));
501
STR(in_buf), demo_mask,
502
demo_str ? demo_str : "(null)");
247
503
vstream_fflush(VSTREAM_OUT);
505
vstring_free(in_buf);
506
vstring_free(out_buf);