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 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 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 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