Current File : //root/postfix-3.2.0/src/util/name_mask.c |
/*++
/* 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