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