summaryrefslogtreecommitdiffstats
path: root/src/util/name_mask.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/name_mask.c')
-rw-r--r--src/util/name_mask.c511
1 files changed, 511 insertions, 0 deletions
diff --git a/src/util/name_mask.c b/src/util/name_mask.c
new file mode 100644
index 0000000..92e956f
--- /dev/null
+++ b/src/util/name_mask.c
@@ -0,0 +1,511 @@
+/*++
+/* NAME
+/* name_mask 3
+/* SUMMARY
+/* map names to bit mask
+/* SYNOPSIS
+/* #include <name_mask.h>
+/*
+/* int name_mask(context, table, names)
+/* const char *context;
+/* const NAME_MASK *table;
+/* const char *names;
+/*
+/* long long_name_mask(context, table, names)
+/* const char *context;
+/* const LONG_NAME_MASK *table;
+/* const char *names;
+/*
+/* const char *str_name_mask(context, table, mask)
+/* const char *context;
+/* const NAME_MASK *table;
+/* int mask;
+/*
+/* const char *str_long_name_mask(context, table, mask)
+/* const char *context;
+/* const LONG_NAME_MASK *table;
+/* long mask;
+/*
+/* int name_mask_opt(context, table, names, flags)
+/* const char *context;
+/* const NAME_MASK *table;
+/* const char *names;
+/* int flags;
+/*
+/* long long_name_mask_opt(context, table, names, flags)
+/* const char *context;
+/* const LONG_NAME_MASK *table;
+/* const char *names;
+/* int flags;
+/*
+/* int name_mask_delim_opt(context, table, names, delim, flags)
+/* const char *context;
+/* const NAME_MASK *table;
+/* const char *names;
+/* const char *delim;
+/* int flags;
+/*
+/* long long_name_mask_delim_opt(context, table, names, delim, flags)
+/* const char *context;
+/* const LONG_NAME_MASK *table;
+/* const char *names;
+/* const char *delim;
+/* int flags;
+/*
+/* const char *str_name_mask_opt(buf, context, table, mask, flags)
+/* VSTRING *buf;
+/* const char *context;
+/* const NAME_MASK *table;
+/* int mask;
+/* int flags;
+/*
+/* const char *str_long_name_mask_opt(buf, context, table, mask, flags)
+/* VSTRING *buf;
+/* const char *context;
+/* const LONG_NAME_MASK *table;
+/* long mask;
+/* int flags;
+/* DESCRIPTION
+/* name_mask() takes a null-terminated \fItable\fR with (name, mask)
+/* values and computes the bit-wise OR of the masks that correspond
+/* to the names listed in the \fInames\fR argument, separated by
+/* comma and/or whitespace characters. The "long_" version returns
+/* a "long int" bitmask, rather than an "int" bitmask.
+/*
+/* str_name_mask() translates a mask into its equlvalent names.
+/* The result is written to a static buffer that is overwritten
+/* upon each call. The "long_" version converts a "long int"
+/* bitmask, rather than an "int" bitmask.
+/*
+/* name_mask_opt() and str_name_mask_opt() are extended versions
+/* with additional fine control. name_mask_delim_opt() supports
+/* non-default delimiter characters.
+/*
+/* Arguments:
+/* .IP buf
+/* Null pointer or pointer to buffer storage.
+/* .IP context
+/* What kind of names and
+/* masks are being manipulated, in order to make error messages
+/* more understandable. Typically, this would be the name of a
+/* user-configurable parameter.
+/* .IP table
+/* Table with (name, bit mask) pairs.
+/* .IP names
+/* A list of names that is to be converted into a bit mask.
+/* .IP mask
+/* A bit mask.
+/* .IP delim
+/* Delimiter characters to use instead of whitespace and commas.
+/* .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: NAME_MASK_FATAL, NAME_MASK_RETURN,
+/* NAME_MASK_WARN or NAME_MASK_IGNORE.
+/*
+/* When converting from mask to string, at least one of the
+/* following must be specified: NAME_MASK_NUMBER, NAME_MASK_FATAL,
+/* NAME_MASK_RETURN, NAME_MASK_WARN or NAME_MASK_IGNORE.
+/* .RS
+/* .IP NAME_MASK_NUMBER
+/* When converting from string to mask, accept hexadecimal
+/* inputs starting with "0x" followed by hexadecimal digits.
+/* Each hexadecimal input may specify multiple bits. This
+/* feature is ignored for hexadecimal inputs that cannot be
+/* converted (malformed, out of range, etc.).
+/*
+/* When converting from mask to string, represent bits not
+/* defined in \fItable\fR as "0x" followed by hexadecimal
+/* digits. This conversion always succeeds.
+/* .IP NAME_MASK_FATAL
+/* Require that all names listed in \fIname\fR exist in
+/* \fItable\fR or that they can be parsed as a hexadecimal
+/* string, and require that all bits listed in \fImask\fR exist
+/* in \fItable\fR or that they can be converted to hexadecimal
+/* string. Terminate with a fatal run-time error if this
+/* condition is not met. This feature is enabled by default
+/* when calling name_mask() or str_name_mask().
+/* .IP NAME_MASK_RETURN
+/* Require that all names listed in \fIname\fR exist in
+/* \fItable\fR or that they can be parsed as a hexadecimal
+/* string, and require that all bits listed in \fImask\fR exist
+/* in \fItable\fR or that they can be converted to hexadecimal
+/* string. Log a warning, and return 0 (name_mask()) or a
+/* null pointer (str_name_mask()) if this condition is not
+/* met. This feature is not enabled by default when calling
+/* name_mask() or str_name_mask().
+/* .IP NAME_MASK_WARN
+/* Require that all names listed in \fIname\fR exist in
+/* \fItable\fR or that they can be parsed as a hexadecimal
+/* string, and require that all bits listed in \fImask\fR exist
+/* in \fItable\fR or that they can be converted to hexadecimal
+/* string. Log a warning if this condition is not met, continue
+/* processing, and return all valid bits or names. This feature
+/* is not enabled by default when calling name_mask() or
+/* str_name_mask().
+/* .IP NAME_MASK_IGNORE
+/* Silently ignore names listed in \fIname\fR that don't exist
+/* in \fItable\fR and that can't be parsed as a hexadecimal
+/* string, and silently ignore bits listed in \fImask\fR that
+/* don't exist in \fItable\fR and that can't be converted to
+/* hexadecimal string.
+/* .IP NAME_MASK_ANY_CASE
+/* Enable case-insensitive matching.
+/* This feature is not enabled by default when calling name_mask();
+/* it has no effect with str_name_mask().
+/* .IP NAME_MASK_COMMA
+/* Use comma instead of space when converting a mask to string.
+/* .IP NAME_MASK_PIPE
+/* Use "|" instead of space when converting a mask to string.
+/* .RE
+/* The value NAME_MASK_NONE explicitly requests no features,
+/* and NAME_MASK_DEFAULT enables the default options.
+/* DIAGNOSTICS
+/* Fatal: the \fInames\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.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <name_mask.h>
+#include <vstring.h>
+
+static int hex_to_ulong(char *, unsigned long, unsigned long *);
+
+#define STR(x) vstring_str(x)
+
+/* name_mask_delim_opt - compute mask corresponding to list of names */
+
+int name_mask_delim_opt(const char *context, const NAME_MASK *table,
+ const char *names, const char *delim, int flags)
+{
+ const char *myname = "name_mask";
+ char *saved_names = mystrdup(names);
+ char *bp = saved_names;
+ int result = 0;
+ const NAME_MASK *np;
+ char *name;
+ int (*lookup) (const char *, const char *);
+ unsigned long ulval;
+
+ if ((flags & NAME_MASK_REQUIRED) == 0)
+ msg_panic("%s: missing NAME_MASK_FATAL/RETURN/WARN/IGNORE flag",
+ myname);
+
+ if (flags & NAME_MASK_ANY_CASE)
+ lookup = strcasecmp;
+ else
+ lookup = strcmp;
+
+ /*
+ * Break up the names string, and look up each component in the table. If
+ * the name is found, merge its mask with the result.
+ */
+ while ((name = mystrtok(&bp, delim)) != 0) {
+ for (np = table; /* void */ ; np++) {
+ if (np->name == 0) {
+ if ((flags & NAME_MASK_NUMBER)
+ && hex_to_ulong(name, ~0U, &ulval)) {
+ result |= (unsigned int) ulval;
+ } else if (flags & NAME_MASK_FATAL) {
+ msg_fatal("unknown %s value \"%s\" in \"%s\"",
+ context, name, names);
+ } else if (flags & NAME_MASK_RETURN) {
+ msg_warn("unknown %s value \"%s\" in \"%s\"",
+ context, name, names);
+ myfree(saved_names);
+ return (0);
+ } else if (flags & NAME_MASK_WARN) {
+ msg_warn("unknown %s value \"%s\" in \"%s\"",
+ context, name, names);
+ }
+ break;
+ }
+ if (lookup(name, np->name) == 0) {
+ if (msg_verbose)
+ msg_info("%s: %s", myname, name);
+ result |= np->mask;
+ break;
+ }
+ }
+ }
+ myfree(saved_names);
+ return (result);
+}
+
+/* str_name_mask_opt - mask to string */
+
+const char *str_name_mask_opt(VSTRING *buf, const char *context,
+ const NAME_MASK *table,
+ int mask, int flags)
+{
+ const char *myname = "name_mask";
+ const NAME_MASK *np;
+ ssize_t len;
+ static VSTRING *my_buf = 0;
+ int delim = (flags & NAME_MASK_COMMA ? ',' :
+ (flags & NAME_MASK_PIPE ? '|' : ' '));
+
+ if ((flags & STR_NAME_MASK_REQUIRED) == 0)
+ msg_panic("%s: missing NAME_MASK_NUMBER/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->name == 0) {
+ if (flags & NAME_MASK_NUMBER) {
+ vstring_sprintf_append(buf, "0x%x%c", mask, delim);
+ } else if (flags & NAME_MASK_FATAL) {
+ msg_fatal("%s: unknown %s bit in mask: 0x%x",
+ myname, context, mask);
+ } else if (flags & NAME_MASK_RETURN) {
+ msg_warn("%s: unknown %s bit in mask: 0x%x",
+ myname, context, mask);
+ return (0);
+ } else if (flags & NAME_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, "%s%c", np->name, delim);
+ }
+ }
+ if ((len = VSTRING_LEN(buf)) > 0)
+ vstring_truncate(buf, len - 1);
+ VSTRING_TERMINATE(buf);
+
+ return (STR(buf));
+}
+
+/* long_name_mask_delim_opt - compute mask corresponding to list of names */
+
+long long_name_mask_delim_opt(const char *context,
+ const LONG_NAME_MASK * table,
+ const char *names, const char *delim,
+ int flags)
+{
+ const char *myname = "name_mask";
+ char *saved_names = mystrdup(names);
+ char *bp = saved_names;
+ long result = 0;
+ const LONG_NAME_MASK *np;
+ char *name;
+ int (*lookup) (const char *, const char *);
+ unsigned long ulval;
+
+ if ((flags & NAME_MASK_REQUIRED) == 0)
+ msg_panic("%s: missing NAME_MASK_FATAL/RETURN/WARN/IGNORE flag",
+ myname);
+
+ if (flags & NAME_MASK_ANY_CASE)
+ lookup = strcasecmp;
+ else
+ lookup = strcmp;
+
+ /*
+ * Break up the names string, and look up each component in the table. If
+ * the name is found, merge its mask with the result.
+ */
+ while ((name = mystrtok(&bp, delim)) != 0) {
+ for (np = table; /* void */ ; np++) {
+ if (np->name == 0) {
+ if ((flags & NAME_MASK_NUMBER)
+ && hex_to_ulong(name, ~0UL, &ulval)) {
+ result |= ulval;
+ } else if (flags & NAME_MASK_FATAL) {
+ msg_fatal("unknown %s value \"%s\" in \"%s\"",
+ context, name, names);
+ } else if (flags & NAME_MASK_RETURN) {
+ msg_warn("unknown %s value \"%s\" in \"%s\"",
+ context, name, names);
+ myfree(saved_names);
+ return (0);
+ } else if (flags & NAME_MASK_WARN) {
+ msg_warn("unknown %s value \"%s\" in \"%s\"",
+ context, name, names);
+ }
+ break;
+ }
+ if (lookup(name, np->name) == 0) {
+ if (msg_verbose)
+ msg_info("%s: %s", myname, name);
+ result |= np->mask;
+ break;
+ }
+ }
+ }
+
+ myfree(saved_names);
+ return (result);
+}
+
+/* str_long_name_mask_opt - mask to string */
+
+const char *str_long_name_mask_opt(VSTRING *buf, const char *context,
+ const LONG_NAME_MASK * table,
+ long mask, int flags)
+{
+ const char *myname = "name_mask";
+ ssize_t len;
+ static VSTRING *my_buf = 0;
+ int delim = (flags & NAME_MASK_COMMA ? ',' :
+ (flags & NAME_MASK_PIPE ? '|' : ' '));
+ const LONG_NAME_MASK *np;
+
+ if ((flags & STR_NAME_MASK_REQUIRED) == 0)
+ msg_panic("%s: missing NAME_MASK_NUMBER/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->name == 0) {
+ if (flags & NAME_MASK_NUMBER) {
+ vstring_sprintf_append(buf, "0x%lx%c", mask, delim);
+ } else if (flags & NAME_MASK_FATAL) {
+ msg_fatal("%s: unknown %s bit in mask: 0x%lx",
+ myname, context, mask);
+ } else if (flags & NAME_MASK_RETURN) {
+ msg_warn("%s: unknown %s bit in mask: 0x%lx",
+ myname, context, mask);
+ return (0);
+ } else if (flags & NAME_MASK_WARN) {
+ msg_warn("%s: unknown %s bit in mask: 0x%lx",
+ myname, context, mask);
+ }
+ break;
+ }
+ if (mask & np->mask) {
+ mask &= ~np->mask;
+ vstring_sprintf_append(buf, "%s%c", np->name, delim);
+ }
+ }
+ if ((len = VSTRING_LEN(buf)) > 0)
+ vstring_truncate(buf, len - 1);
+ VSTRING_TERMINATE(buf);
+
+ return (STR(buf));
+}
+
+/* hex_to_ulong - 0x... to unsigned long or smaller */
+
+static int hex_to_ulong(char *value, unsigned long mask, unsigned long *ulp)
+{
+ unsigned long result;
+ char *cp;
+
+ if (strncasecmp(value, "0x", 2) != 0)
+ return (0);
+
+ /*
+ * Check for valid hex number. Since the value starts with 0x, strtoul()
+ * will not allow a negative sign before the first nibble. So we don't
+ * need to worry about explicit +/- signs.
+ */
+ errno = 0;
+ result = strtoul(value, &cp, 16);
+ if (*cp != '\0' || errno == ERANGE)
+ return (0);
+
+ *ulp = (result & mask);
+ return (*ulp == result);
+}
+
+#ifdef TEST
+
+ /*
+ * Stand-alone test program.
+ */
+#include <stdlib.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+
+int main(int argc, char **argv)
+{
+ static const NAME_MASK demo_table[] = {
+ "zero", 1 << 0,
+ "one", 1 << 1,
+ "two", 1 << 2,
+ "three", 1 << 3,
+ 0, 0,
+ };
+ static const NAME_MASK feature_table[] = {
+ "DEFAULT", NAME_MASK_DEFAULT,
+ "FATAL", NAME_MASK_FATAL,
+ "ANY_CASE", NAME_MASK_ANY_CASE,
+ "RETURN", NAME_MASK_RETURN,
+ "COMMA", NAME_MASK_COMMA,
+ "PIPE", NAME_MASK_PIPE,
+ "NUMBER", NAME_MASK_NUMBER,
+ "WARN", NAME_MASK_WARN,
+ "IGNORE", NAME_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 = name_mask_opt("name", demo_table,
+ STR(in_buf), in_feature_mask);
+ demo_str = str_name_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)");
+ vstream_fflush(VSTREAM_OUT);
+ }
+ vstring_free(in_buf);
+ vstring_free(out_buf);
+ exit(0);
+}
+
+#endif