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