diff options
Diffstat (limited to 'src/util/byte_mask.c')
-rw-r--r-- | src/util/byte_mask.c | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/src/util/byte_mask.c b/src/util/byte_mask.c new file mode 100644 index 0000000..e818476 --- /dev/null +++ b/src/util/byte_mask.c @@ -0,0 +1,306 @@ +/*++ +/* NAME +/* byte_mask 3 +/* SUMMARY +/* map byte sequence to bit mask +/* SYNOPSIS +/* #include <byte_mask.h> +/* +/* typedef struct { +/* .in +4 +/* int byte_val; +/* int mask; +/* .in -4 +/* } BYTE_MASK; +/* +/* int byte_mask( +/* const char *context, +/* const BYTE_MASK *table, +/* const char *bytes); +/* +/* const char *str_byte_mask( +/* const char *context, +/* const BYTE_MASK *table, +/* int mask); +/* +/* int byte_mask_opt( +/* const char *context; +/* const BYTE_MASK *table, +/* const char *bytes, +/* int flags); +/* +/* const char *str_byte_mask_opt( +/* VSTRING *buf, +/* const char *context, +/* const BYTE_MASK *table, +/* int mask, +/* int flags); +/* DESCRIPTION +/* byte_mask() takes a null-terminated \fItable\fR with (byte +/* value, single-bit mask) pairs and computes the bit-wise OR +/* of the masks that correspond to the byte values pointed to +/* by the \fIbytes\fR argument. +/* +/* str_byte_mask() translates a bit mask into its equivalent +/* bytes. The result is written to a static buffer that is +/* overwritten upon each call. +/* +/* byte_mask_opt() and str_byte_mask_opt() are extended versions +/* with additional fine control. +/* +/* Arguments: +/* .IP buf +/* Null pointer or pointer to buffer storage. +/* .IP context +/* What kind of byte values and bit masks are being manipulated, +/* reported in error messages. Typically, this would be the +/* name of a user-configurable parameter or command-line +/* attribute. +/* .IP table +/* Table with (byte value, single-bit mask) pairs. +/* .IP bytes +/* A null-terminated string that is to be converted into a bit +/* mask. +/* .IP mask +/* A bit mask that is to be converted into null-terminated +/* string. +/* .IP flags +/* Bit-wise OR of one or more of the following. Where features +/* would have conflicting results (e.g., FATAL versus IGNORE), +/* the feature that takes precedence is described first. +/* +/* When converting from string to mask, at least one of the +/* following must be specified: BYTE_MASK_FATAL, BYTE_MASK_RETURN, +/* BYTE_MASK_WARN or BYTE_MASK_IGNORE. +/* +/* When converting from mask to string, at least one of the +/* following must be specified: BYTE_MASK_FATAL, BYTE_MASK_RETURN, +/* BYTE_MASK_WARN or BYTE_MASK_IGNORE. +/* .RS +/* .IP BYTE_MASK_FATAL +/* Require that all values in \fIbytes\fR exist in \fItable\fR, +/* and require that all bits listed in \fImask\fR exist in +/* \fItable\fR. Terminate with a fatal run-time error if this +/* condition is not met. This feature is enabled by default +/* when calling byte_mask() or str_name_mask(). +/* .IP BYTE_MASK_RETURN +/* Require that all values in \fIbytes\fR exist in \fItable\fR, +/* and require that all bits listed in \fImask\fR exist in +/* \fItable\fR. Log a warning, and return 0 (byte_mask()) or +/* a null pointer (str_byte_mask()) if this condition is not +/* met. This feature is not enabled by default when calling +/* byte_mask() or str_name_mask(). +/* .IP BYTE_MASK_WARN +/* Require that all values in \fIbytes\fR exist in \fItable\fR, +/* and require that all bits listed in \fImask\fR exist in +/* \fItable\fR. Log a warning if this condition is not met, +/* continue processing, and return all valid bits or bytes. +/* This feature is not enabled by default when calling byte_mask() +/* or str_byte_mask(). +/* .IP BYTE_MASK_IGNORE +/* Silently ignore values in \fIbytes\fR that don't exist in +/* \fItable\fR, and silently ignore bits listed in \fImask\fR +/* that don't exist in \fItable\fR. This feature is not enabled +/* by default when calling byte_mask() or str_byte_mask(). +/* .IP BYTE_MASK_ANY_CASE +/* Enable case-insensitive matching. This feature is not +/* enabled by default when calling byte_mask(); it has no +/* effect with str_byte_mask(). +/* .RE +/* The value BYTE_MASK_NONE explicitly requests no features, +/* and BYTE_MASK_DEFAULT enables the default options. +/* DIAGNOSTICS +/* Fatal: the \fIbytes\fR argument specifies a name not found in +/* \fItable\fR, or the \fImask\fR specifies a bit not found in +/* \fItable\fR. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* HISTORY +/* This code is a clone of Postfix name_mask(3). +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <ctype.h> + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <stringops.h> +#include <byte_mask.h> +#include <vstring.h> + +#define STR(x) vstring_str(x) + +/* byte_mask_opt - compute mask corresponding to byte string */ + +int byte_mask_opt(const char *context, const BYTE_MASK *table, + const char *bytes, int flags) +{ + const char myname[] = "byte_mask"; + const char *bp; + int result = 0; + const BYTE_MASK *np; + + if ((flags & BYTE_MASK_REQUIRED) == 0) + msg_panic("%s: missing BYTE_MASK_FATAL/RETURN/WARN/IGNORE flag", + myname); + + /* + * Iterate over bytes string, and look up each byte in the table. If the + * byte is found, merge its mask with the result. + */ + for (bp = bytes; *bp; bp++) { + int byte_val = *(const unsigned char *) bp; + + for (np = table; /* void */ ; np++) { + if (np->byte_val == 0) { + if (flags & BYTE_MASK_FATAL) { + msg_fatal("unknown %s value \"%c\" in \"%s\"", + context, byte_val, bytes); + } else if (flags & BYTE_MASK_RETURN) { + msg_warn("unknown %s value \"%c\" in \"%s\"", + context, byte_val, bytes); + return (0); + } else if (flags & BYTE_MASK_WARN) { + msg_warn("unknown %s value \"%c\" in \"%s\"", + context, byte_val, bytes); + } + break; + } + if ((flags & BYTE_MASK_ANY_CASE) ? + (TOLOWER(byte_val) == TOLOWER(np->byte_val)) : + (byte_val == np->byte_val)) { + if (msg_verbose) + msg_info("%s: %c", myname, byte_val); + result |= np->mask; + break; + } + } + } + return (result); +} + +/* str_byte_mask_opt - mask to string */ + +const char *str_byte_mask_opt(VSTRING *buf, const char *context, + const BYTE_MASK *table, + int mask, int flags) +{ + const char myname[] = "byte_mask"; + const BYTE_MASK *np; + static VSTRING *my_buf = 0; + + if ((flags & STR_BYTE_MASK_REQUIRED) == 0) + msg_panic("%s: missing BYTE_MASK_FATAL/RETURN/WARN/IGNORE flag", + myname); + + if (buf == 0) { + if (my_buf == 0) + my_buf = vstring_alloc(1); + buf = my_buf; + } + VSTRING_RESET(buf); + + for (np = table; mask != 0; np++) { + if (np->byte_val == 0) { + if (flags & BYTE_MASK_FATAL) { + msg_fatal("%s: unknown %s bit in mask: 0x%x", + myname, context, mask); + } else if (flags & BYTE_MASK_RETURN) { + msg_warn("%s: unknown %s bit in mask: 0x%x", + myname, context, mask); + return (0); + } else if (flags & BYTE_MASK_WARN) { + msg_warn("%s: unknown %s bit in mask: 0x%x", + myname, context, mask); + } + break; + } + if (mask & np->mask) { + mask &= ~np->mask; + vstring_sprintf_append(buf, "%c", np->byte_val); + } + } + VSTRING_TERMINATE(buf); + + return (STR(buf)); +} + +#ifdef TEST + + /* + * Stand-alone test program. + */ +#include <stdlib.h> +#include <vstream.h> +#include <vstring_vstream.h> +#include <name_mask.h> + +int main(int argc, char **argv) +{ + static const BYTE_MASK demo_table[] = { + '0', 1 << 0, + '1', 1 << 1, + '2', 1 << 2, + '3', 1 << 3, + 0, 0, + }; + static const NAME_MASK feature_table[] = { + "DEFAULT", BYTE_MASK_DEFAULT, + "FATAL", BYTE_MASK_FATAL, + "ANY_CASE", BYTE_MASK_ANY_CASE, + "RETURN", BYTE_MASK_RETURN, + "WARN", BYTE_MASK_WARN, + "IGNORE", BYTE_MASK_IGNORE, + 0, + }; + int in_feature_mask; + int out_feature_mask; + int demo_mask; + const char *demo_str; + VSTRING *out_buf = vstring_alloc(1); + VSTRING *in_buf = vstring_alloc(1); + + if (argc != 3) + msg_fatal("usage: %s in-feature-mask out-feature-mask", argv[0]); + in_feature_mask = name_mask(argv[1], feature_table, argv[1]); + out_feature_mask = name_mask(argv[2], feature_table, argv[2]); + while (vstring_get_nonl(in_buf, VSTREAM_IN) != VSTREAM_EOF) { + demo_mask = byte_mask_opt("name", demo_table, + STR(in_buf), in_feature_mask); + demo_str = str_byte_mask_opt(out_buf, "mask", demo_table, + demo_mask, out_feature_mask); + vstream_printf("%s -> 0x%x -> %s\n", + STR(in_buf), demo_mask, + demo_str ? demo_str : "(null)"); + demo_mask <<=1; + demo_str = str_byte_mask_opt(out_buf, "mask", demo_table, + demo_mask, out_feature_mask); + vstream_printf("0x%x -> %s\n", + demo_mask, demo_str ? demo_str : "(null)"); + vstream_fflush(VSTREAM_OUT); + } + vstring_free(in_buf); + vstring_free(out_buf); + exit(0); +} + +#endif |