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