summaryrefslogtreecommitdiffstats
path: root/src/util/byte_mask.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/byte_mask.c')
-rw-r--r--src/util/byte_mask.c306
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