/*++ /* NAME /* name_mask 3 /* SUMMARY /* map names to bit mask /* SYNOPSIS /* #include /* /* 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 equivalent 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 #include #include #include #ifdef STRCASECMP_IN_STRINGS_H #include #endif /* Utility library. */ #include #include #include #include #include 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 #include #include 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