1*33881f77Schristos /* $NetBSD: byte_mask.c,v 1.2 2020/03/18 19:05:21 christos Exp $ */
2f3bc92a4Schristos
3f3bc92a4Schristos /*++
4f3bc92a4Schristos /* NAME
5f3bc92a4Schristos /* byte_mask 3
6f3bc92a4Schristos /* SUMMARY
7f3bc92a4Schristos /* map byte sequence to bit mask
8f3bc92a4Schristos /* SYNOPSIS
9f3bc92a4Schristos /* #include <byte_mask.h>
10f3bc92a4Schristos /*
11f3bc92a4Schristos /* typedef struct {
12f3bc92a4Schristos /* .in +4
13f3bc92a4Schristos /* int byte_val;
14f3bc92a4Schristos /* int mask;
15f3bc92a4Schristos /* .in -4
16f3bc92a4Schristos /* } BYTE_MASK;
17f3bc92a4Schristos /*
18f3bc92a4Schristos /* int byte_mask(
19f3bc92a4Schristos /* const char *context,
20f3bc92a4Schristos /* const BYTE_MASK *table,
21f3bc92a4Schristos /* const char *bytes);
22f3bc92a4Schristos /*
23f3bc92a4Schristos /* const char *str_byte_mask(
24f3bc92a4Schristos /* const char *context,
25f3bc92a4Schristos /* const BYTE_MASK *table,
26f3bc92a4Schristos /* int mask);
27f3bc92a4Schristos /*
28f3bc92a4Schristos /* int byte_mask_opt(
29f3bc92a4Schristos /* const char *context;
30f3bc92a4Schristos /* const BYTE_MASK *table,
31f3bc92a4Schristos /* const char *bytes,
32f3bc92a4Schristos /* int flags);
33f3bc92a4Schristos /*
34f3bc92a4Schristos /* const char *str_byte_mask_opt(
35f3bc92a4Schristos /* VSTRING *buf,
36f3bc92a4Schristos /* const char *context,
37f3bc92a4Schristos /* const BYTE_MASK *table,
38f3bc92a4Schristos /* int mask,
39f3bc92a4Schristos /* int flags);
40f3bc92a4Schristos /* DESCRIPTION
41f3bc92a4Schristos /* byte_mask() takes a null-terminated \fItable\fR with (byte
42f3bc92a4Schristos /* value, single-bit mask) pairs and computes the bit-wise OR
43f3bc92a4Schristos /* of the masks that correspond to the byte values pointed to
44f3bc92a4Schristos /* by the \fIbytes\fR argument.
45f3bc92a4Schristos /*
46f3bc92a4Schristos /* str_byte_mask() translates a bit mask into its equivalent
47f3bc92a4Schristos /* bytes. The result is written to a static buffer that is
48f3bc92a4Schristos /* overwritten upon each call.
49f3bc92a4Schristos /*
50f3bc92a4Schristos /* byte_mask_opt() and str_byte_mask_opt() are extended versions
51f3bc92a4Schristos /* with additional fine control.
52f3bc92a4Schristos /*
53f3bc92a4Schristos /* Arguments:
54f3bc92a4Schristos /* .IP buf
55f3bc92a4Schristos /* Null pointer or pointer to buffer storage.
56f3bc92a4Schristos /* .IP context
57f3bc92a4Schristos /* What kind of byte values and bit masks are being manipulated,
58f3bc92a4Schristos /* reported in error messages. Typically, this would be the
59f3bc92a4Schristos /* name of a user-configurable parameter or command-line
60f3bc92a4Schristos /* attribute.
61f3bc92a4Schristos /* .IP table
62f3bc92a4Schristos /* Table with (byte value, single-bit mask) pairs.
63f3bc92a4Schristos /* .IP bytes
64f3bc92a4Schristos /* A null-terminated string that is to be converted into a bit
65f3bc92a4Schristos /* mask.
66f3bc92a4Schristos /* .IP mask
67f3bc92a4Schristos /* A bit mask that is to be converted into null-terminated
68f3bc92a4Schristos /* string.
69f3bc92a4Schristos /* .IP flags
70f3bc92a4Schristos /* Bit-wise OR of one or more of the following. Where features
71f3bc92a4Schristos /* would have conflicting results (e.g., FATAL versus IGNORE),
72f3bc92a4Schristos /* the feature that takes precedence is described first.
73f3bc92a4Schristos /*
74f3bc92a4Schristos /* When converting from string to mask, at least one of the
75f3bc92a4Schristos /* following must be specified: BYTE_MASK_FATAL, BYTE_MASK_RETURN,
76f3bc92a4Schristos /* BYTE_MASK_WARN or BYTE_MASK_IGNORE.
77f3bc92a4Schristos /*
78f3bc92a4Schristos /* When converting from mask to string, at least one of the
79f3bc92a4Schristos /* following must be specified: BYTE_MASK_FATAL, BYTE_MASK_RETURN,
80f3bc92a4Schristos /* BYTE_MASK_WARN or BYTE_MASK_IGNORE.
81f3bc92a4Schristos /* .RS
82f3bc92a4Schristos /* .IP BYTE_MASK_FATAL
83f3bc92a4Schristos /* Require that all values in \fIbytes\fR exist in \fItable\fR,
84f3bc92a4Schristos /* and require that all bits listed in \fImask\fR exist in
85f3bc92a4Schristos /* \fItable\fR. Terminate with a fatal run-time error if this
86f3bc92a4Schristos /* condition is not met. This feature is enabled by default
87f3bc92a4Schristos /* when calling byte_mask() or str_name_mask().
88f3bc92a4Schristos /* .IP BYTE_MASK_RETURN
89f3bc92a4Schristos /* Require that all values in \fIbytes\fR exist in \fItable\fR,
90f3bc92a4Schristos /* and require that all bits listed in \fImask\fR exist in
91f3bc92a4Schristos /* \fItable\fR. Log a warning, and return 0 (byte_mask()) or
92f3bc92a4Schristos /* a null pointer (str_byte_mask()) if this condition is not
93f3bc92a4Schristos /* met. This feature is not enabled by default when calling
94f3bc92a4Schristos /* byte_mask() or str_name_mask().
95f3bc92a4Schristos /* .IP BYTE_MASK_WARN
96f3bc92a4Schristos /* Require that all values in \fIbytes\fR exist in \fItable\fR,
97f3bc92a4Schristos /* and require that all bits listed in \fImask\fR exist in
98f3bc92a4Schristos /* \fItable\fR. Log a warning if this condition is not met,
99f3bc92a4Schristos /* continue processing, and return all valid bits or bytes.
100f3bc92a4Schristos /* This feature is not enabled by default when calling byte_mask()
101f3bc92a4Schristos /* or str_byte_mask().
102f3bc92a4Schristos /* .IP BYTE_MASK_IGNORE
103f3bc92a4Schristos /* Silently ignore values in \fIbytes\fR that don't exist in
104f3bc92a4Schristos /* \fItable\fR, and silently ignore bits listed in \fImask\fR
105f3bc92a4Schristos /* that don't exist in \fItable\fR. This feature is not enabled
106f3bc92a4Schristos /* by default when calling byte_mask() or str_byte_mask().
107f3bc92a4Schristos /* .IP BYTE_MASK_ANY_CASE
108f3bc92a4Schristos /* Enable case-insensitive matching. This feature is not
109f3bc92a4Schristos /* enabled by default when calling byte_mask(); it has no
110f3bc92a4Schristos /* effect with str_byte_mask().
111f3bc92a4Schristos /* .RE
112f3bc92a4Schristos /* The value BYTE_MASK_NONE explicitly requests no features,
113f3bc92a4Schristos /* and BYTE_MASK_DEFAULT enables the default options.
114f3bc92a4Schristos /* DIAGNOSTICS
115f3bc92a4Schristos /* Fatal: the \fIbytes\fR argument specifies a name not found in
116f3bc92a4Schristos /* \fItable\fR, or the \fImask\fR specifies a bit not found in
117f3bc92a4Schristos /* \fItable\fR.
118f3bc92a4Schristos /* LICENSE
119f3bc92a4Schristos /* .ad
120f3bc92a4Schristos /* .fi
121f3bc92a4Schristos /* The Secure Mailer license must be distributed with this software.
122f3bc92a4Schristos /* HISTORY
123f3bc92a4Schristos /* This code is a clone of Postfix name_mask(3).
124f3bc92a4Schristos /* AUTHOR(S)
125f3bc92a4Schristos /* Wietse Venema
126f3bc92a4Schristos /* IBM T.J. Watson Research
127f3bc92a4Schristos /* P.O. Box 704
128f3bc92a4Schristos /* Yorktown Heights, NY 10598, USA
129f3bc92a4Schristos /*
130f3bc92a4Schristos /* Wietse Venema
131f3bc92a4Schristos /* Google, Inc.
132f3bc92a4Schristos /* 111 8th Avenue
133f3bc92a4Schristos /* New York, NY 10011, USA
134f3bc92a4Schristos /*--*/
135f3bc92a4Schristos
136f3bc92a4Schristos /* System library. */
137f3bc92a4Schristos
138f3bc92a4Schristos #include <sys_defs.h>
139f3bc92a4Schristos #include <string.h>
140f3bc92a4Schristos #include <errno.h>
141f3bc92a4Schristos #include <stdlib.h>
142f3bc92a4Schristos #include <ctype.h>
143f3bc92a4Schristos
144f3bc92a4Schristos /* Utility library. */
145f3bc92a4Schristos
146f3bc92a4Schristos #include <msg.h>
147f3bc92a4Schristos #include <mymalloc.h>
148f3bc92a4Schristos #include <stringops.h>
149f3bc92a4Schristos #include <byte_mask.h>
150f3bc92a4Schristos #include <vstring.h>
151f3bc92a4Schristos
152f3bc92a4Schristos #define STR(x) vstring_str(x)
153f3bc92a4Schristos
154f3bc92a4Schristos /* byte_mask_opt - compute mask corresponding to byte string */
155f3bc92a4Schristos
byte_mask_opt(const char * context,const BYTE_MASK * table,const char * bytes,int flags)156f3bc92a4Schristos int byte_mask_opt(const char *context, const BYTE_MASK *table,
157f3bc92a4Schristos const char *bytes, int flags)
158f3bc92a4Schristos {
159f3bc92a4Schristos const char myname[] = "byte_mask";
160f3bc92a4Schristos const char *bp;
161f3bc92a4Schristos int result = 0;
162f3bc92a4Schristos const BYTE_MASK *np;
163f3bc92a4Schristos
164f3bc92a4Schristos if ((flags & BYTE_MASK_REQUIRED) == 0)
165f3bc92a4Schristos msg_panic("%s: missing BYTE_MASK_FATAL/RETURN/WARN/IGNORE flag",
166f3bc92a4Schristos myname);
167f3bc92a4Schristos
168f3bc92a4Schristos /*
169f3bc92a4Schristos * Iterate over bytes string, and look up each byte in the table. If the
170f3bc92a4Schristos * byte is found, merge its mask with the result.
171f3bc92a4Schristos */
172f3bc92a4Schristos for (bp = bytes; *bp; bp++) {
173f3bc92a4Schristos int byte_val = *(const unsigned char *) bp;
174f3bc92a4Schristos
175f3bc92a4Schristos for (np = table; /* void */ ; np++) {
176f3bc92a4Schristos if (np->byte_val == 0) {
177f3bc92a4Schristos if (flags & BYTE_MASK_FATAL) {
178f3bc92a4Schristos msg_fatal("unknown %s value \"%c\" in \"%s\"",
179f3bc92a4Schristos context, byte_val, bytes);
180f3bc92a4Schristos } else if (flags & BYTE_MASK_RETURN) {
181f3bc92a4Schristos msg_warn("unknown %s value \"%c\" in \"%s\"",
182f3bc92a4Schristos context, byte_val, bytes);
183f3bc92a4Schristos return (0);
184f3bc92a4Schristos } else if (flags & BYTE_MASK_WARN) {
185f3bc92a4Schristos msg_warn("unknown %s value \"%c\" in \"%s\"",
186f3bc92a4Schristos context, byte_val, bytes);
187f3bc92a4Schristos }
188f3bc92a4Schristos break;
189f3bc92a4Schristos }
190f3bc92a4Schristos if ((flags & BYTE_MASK_ANY_CASE) ?
191f3bc92a4Schristos (TOLOWER(byte_val) == TOLOWER(np->byte_val)) :
192f3bc92a4Schristos (byte_val == np->byte_val)) {
193f3bc92a4Schristos if (msg_verbose)
194f3bc92a4Schristos msg_info("%s: %c", myname, byte_val);
195f3bc92a4Schristos result |= np->mask;
196f3bc92a4Schristos break;
197f3bc92a4Schristos }
198f3bc92a4Schristos }
199f3bc92a4Schristos }
200f3bc92a4Schristos return (result);
201f3bc92a4Schristos }
202f3bc92a4Schristos
203f3bc92a4Schristos /* str_byte_mask_opt - mask to string */
204f3bc92a4Schristos
str_byte_mask_opt(VSTRING * buf,const char * context,const BYTE_MASK * table,int mask,int flags)205f3bc92a4Schristos const char *str_byte_mask_opt(VSTRING *buf, const char *context,
206f3bc92a4Schristos const BYTE_MASK *table,
207f3bc92a4Schristos int mask, int flags)
208f3bc92a4Schristos {
209f3bc92a4Schristos const char myname[] = "byte_mask";
210f3bc92a4Schristos const BYTE_MASK *np;
211f3bc92a4Schristos static VSTRING *my_buf = 0;
212f3bc92a4Schristos
213f3bc92a4Schristos if ((flags & STR_BYTE_MASK_REQUIRED) == 0)
214f3bc92a4Schristos msg_panic("%s: missing BYTE_MASK_FATAL/RETURN/WARN/IGNORE flag",
215f3bc92a4Schristos myname);
216f3bc92a4Schristos
217f3bc92a4Schristos if (buf == 0) {
218f3bc92a4Schristos if (my_buf == 0)
219f3bc92a4Schristos my_buf = vstring_alloc(1);
220f3bc92a4Schristos buf = my_buf;
221f3bc92a4Schristos }
222f3bc92a4Schristos VSTRING_RESET(buf);
223f3bc92a4Schristos
224f3bc92a4Schristos for (np = table; mask != 0; np++) {
225f3bc92a4Schristos if (np->byte_val == 0) {
226f3bc92a4Schristos if (flags & BYTE_MASK_FATAL) {
227f3bc92a4Schristos msg_fatal("%s: unknown %s bit in mask: 0x%x",
228f3bc92a4Schristos myname, context, mask);
229f3bc92a4Schristos } else if (flags & BYTE_MASK_RETURN) {
230f3bc92a4Schristos msg_warn("%s: unknown %s bit in mask: 0x%x",
231f3bc92a4Schristos myname, context, mask);
232f3bc92a4Schristos return (0);
233f3bc92a4Schristos } else if (flags & BYTE_MASK_WARN) {
234f3bc92a4Schristos msg_warn("%s: unknown %s bit in mask: 0x%x",
235f3bc92a4Schristos myname, context, mask);
236f3bc92a4Schristos }
237f3bc92a4Schristos break;
238f3bc92a4Schristos }
239f3bc92a4Schristos if (mask & np->mask) {
240f3bc92a4Schristos mask &= ~np->mask;
241f3bc92a4Schristos vstring_sprintf_append(buf, "%c", np->byte_val);
242f3bc92a4Schristos }
243f3bc92a4Schristos }
244f3bc92a4Schristos VSTRING_TERMINATE(buf);
245f3bc92a4Schristos
246f3bc92a4Schristos return (STR(buf));
247f3bc92a4Schristos }
248f3bc92a4Schristos
249f3bc92a4Schristos #ifdef TEST
250f3bc92a4Schristos
251f3bc92a4Schristos /*
252f3bc92a4Schristos * Stand-alone test program.
253f3bc92a4Schristos */
254f3bc92a4Schristos #include <stdlib.h>
255f3bc92a4Schristos #include <vstream.h>
256f3bc92a4Schristos #include <vstring_vstream.h>
257f3bc92a4Schristos #include <name_mask.h>
258f3bc92a4Schristos
main(int argc,char ** argv)259f3bc92a4Schristos int main(int argc, char **argv)
260f3bc92a4Schristos {
261f3bc92a4Schristos static const BYTE_MASK demo_table[] = {
262f3bc92a4Schristos '0', 1 << 0,
263f3bc92a4Schristos '1', 1 << 1,
264f3bc92a4Schristos '2', 1 << 2,
265f3bc92a4Schristos '3', 1 << 3,
266f3bc92a4Schristos 0, 0,
267f3bc92a4Schristos };
268f3bc92a4Schristos static const NAME_MASK feature_table[] = {
269f3bc92a4Schristos "DEFAULT", BYTE_MASK_DEFAULT,
270f3bc92a4Schristos "FATAL", BYTE_MASK_FATAL,
271f3bc92a4Schristos "ANY_CASE", BYTE_MASK_ANY_CASE,
272f3bc92a4Schristos "RETURN", BYTE_MASK_RETURN,
273f3bc92a4Schristos "WARN", BYTE_MASK_WARN,
274f3bc92a4Schristos "IGNORE", BYTE_MASK_IGNORE,
275f3bc92a4Schristos 0,
276f3bc92a4Schristos };
277f3bc92a4Schristos int in_feature_mask;
278f3bc92a4Schristos int out_feature_mask;
279f3bc92a4Schristos int demo_mask;
280f3bc92a4Schristos const char *demo_str;
281f3bc92a4Schristos VSTRING *out_buf = vstring_alloc(1);
282f3bc92a4Schristos VSTRING *in_buf = vstring_alloc(1);
283f3bc92a4Schristos
284f3bc92a4Schristos if (argc != 3)
285f3bc92a4Schristos msg_fatal("usage: %s in-feature-mask out-feature-mask", argv[0]);
286f3bc92a4Schristos in_feature_mask = name_mask(argv[1], feature_table, argv[1]);
287f3bc92a4Schristos out_feature_mask = name_mask(argv[2], feature_table, argv[2]);
288f3bc92a4Schristos while (vstring_get_nonl(in_buf, VSTREAM_IN) != VSTREAM_EOF) {
289f3bc92a4Schristos demo_mask = byte_mask_opt("name", demo_table,
290f3bc92a4Schristos STR(in_buf), in_feature_mask);
291f3bc92a4Schristos demo_str = str_byte_mask_opt(out_buf, "mask", demo_table,
292f3bc92a4Schristos demo_mask, out_feature_mask);
293f3bc92a4Schristos vstream_printf("%s -> 0x%x -> %s\n",
294f3bc92a4Schristos STR(in_buf), demo_mask,
295f3bc92a4Schristos demo_str ? demo_str : "(null)");
296f3bc92a4Schristos demo_mask <<=1;
297f3bc92a4Schristos demo_str = str_byte_mask_opt(out_buf, "mask", demo_table,
298f3bc92a4Schristos demo_mask, out_feature_mask);
299f3bc92a4Schristos vstream_printf("0x%x -> %s\n",
300f3bc92a4Schristos demo_mask, demo_str ? demo_str : "(null)");
301f3bc92a4Schristos vstream_fflush(VSTREAM_OUT);
302f3bc92a4Schristos }
303f3bc92a4Schristos vstring_free(in_buf);
304f3bc92a4Schristos vstring_free(out_buf);
305f3bc92a4Schristos exit(0);
306f3bc92a4Schristos }
307f3bc92a4Schristos
308f3bc92a4Schristos #endif
309