xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/byte_mask.c (revision 33881f779a77dce6440bdc44610d94de75bebefe)
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