xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/byte_mask.c (revision 33881f779a77dce6440bdc44610d94de75bebefe)
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 
byte_mask_opt(const char * context,const BYTE_MASK * table,const char * bytes,int flags)156 int     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 
str_byte_mask_opt(VSTRING * buf,const char * context,const BYTE_MASK * table,int mask,int flags)205 const 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 
main(int argc,char ** argv)259 int     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