/*++ /* NAME /* byte_mask 3 /* SUMMARY /* map byte sequence to bit mask /* SYNOPSIS /* #include /* /* 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 #include #include #include #include /* Utility library. */ #include #include #include #include #include #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 #include #include #include 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