1 /*	$NetBSD: name_mask.c,v 1.1.1.1 2009/06/23 10:09:00 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	name_mask 3
6 /* SUMMARY
7 /*	map names to bit mask
8 /* SYNOPSIS
9 /*	#include <name_mask.h>
10 /*
11 /*	int	name_mask(context, table, names)
12 /*	const char *context;
13 /*	const NAME_MASK *table;
14 /*	const char *names;
15 /*
16 /*	const char *str_name_mask(context, table, mask)
17 /*	const char *context;
18 /*	const NAME_MASK *table;
19 /*	int	mask;
20 /*
21 /*	int	name_mask_opt(context, table, names, flags)
22 /*	const char *context;
23 /*	const NAME_MASK *table;
24 /*	const char *names;
25 /*	int	flags;
26 /*
27 /*	int	name_mask_delim_opt(context, table, names, delim, flags)
28 /*	const char *context;
29 /*	const NAME_MASK *table;
30 /*	const char *names;
31 /*	const char *delim;
32 /*	int	flags;
33 /*
34 /*	const char *str_name_mask_opt(buf, context, table, mask, flags)
35 /*	VSTRING	*buf;
36 /*	const char *context;
37 /*	const NAME_MASK *table;
38 /*	int	mask;
39 /*	int	flags;
40 /* DESCRIPTION
41 /*	name_mask() takes a null-terminated \fItable\fR with (name, mask)
42 /*	values and computes the bit-wise OR of the masks that correspond
43 /*	to the names listed in the \fInames\fR argument, separated by
44 /*	comma and/or whitespace characters.
45 /*
46 /*	str_name_mask() translates a mask into its equivalent names.
47 /*	The result is written to a static buffer that is overwritten
48 /*	upon each call.
49 /*
50 /*	name_mask_opt() and str_name_mask_opt() are extended versions
51 /*	with additional fine control. name_mask_delim_opt() supports
52 /*	non-default delimiter characters.
53 /*
54 /*	Arguments:
55 /* .IP buf
56 /*	Null pointer or pointer to buffer storage.
57 /* .IP context
58 /*	What kind of names and
59 /*	masks are being manipulated, in order to make error messages
60 /*	more understandable. Typically, this would be the name of a
61 /*	user-configurable parameter.
62 /* .IP table
63 /*	Table with (name, bit mask) pairs.
64 /* .IP names
65 /*	A list of names that is to be converted into a bit mask.
66 /* .IP mask
67 /*	A bit mask.
68 /* .IP flags
69 /*	Bit-wise OR of zero or more of the following:
70 /* .IP delim
71 /*	Delimiter characters to use instead of whitespace and commas.
72 /* .RS
73 /* .IP NAME_MASK_FATAL
74 /*	Require that all names listed in \fIname\fR exist in \fItable\fR,
75 /*	and that all bits listed in \fImask\fR exist in \fItable\fR.
76 /*	Terminate with a fatal run-time error if this condition is not met.
77 /*	This feature is enabled by default when calling name_mask()
78 /*	or str_name_mask().
79 /* .IP NAME_MASK_RETURN
80 /*	Require that all names listed in \fIname\fR exist in \fItable\fR,
81 /*	and that all bits listed in \fImask\fR exist in \fItable\fR.
82 /*	Log a warning, and return 0 (name_mask()) or a null pointer
83 /*	(str_name_mask()) if this condition is not met.
84 /* .IP NAME_MASK_NUMBER
85 /*	Require that all bits listed in \fImask\fR exist in \fItable\fR.
86 /*	For unrecognized bits, print the numerical hexadecimal form.
87 /* .IP NAME_MASK_ANY_CASE
88 /*	Enable case-insensitive matching.
89 /*	This feature is not enabled by default when calling name_mask();
90 /*	it has no effect with str_name_mask().
91 /* .IP NAME_MASK_COMMA
92 /*	Use comma instead of space when converting a mask to string.
93 /* .IP NAME_MASK_PIPE
94 /*	Use "|" instead of space when converting a mask to string.
95 /* .RE
96 /*	The value NAME_MASK_NONE explicitly requests no features,
97 /*	and NAME_MASK_DEFAULT enables the default options.
98 /* DIAGNOSTICS
99 /*	Fatal: the \fInames\fR argument specifies a name not found in
100 /*	\fItable\fR, or the \fImask\fR specifies a bit not found in
101 /*	\fItable\fR.
102 /* LICENSE
103 /* .ad
104 /* .fi
105 /*	The Secure Mailer license must be distributed with this software.
106 /* AUTHOR(S)
107 /*	Wietse Venema
108 /*	IBM T.J. Watson Research
109 /*	P.O. Box 704
110 /*	Yorktown Heights, NY 10598, USA
111 /*--*/
112 
113 /* System library. */
114 
115 #include <sys_defs.h>
116 #include <string.h>
117 
118 #ifdef STRCASECMP_IN_STRINGS_H
119 #include <strings.h>
120 #endif
121 
122 /* Utility library. */
123 
124 #include <msg.h>
125 #include <mymalloc.h>
126 #include <stringops.h>
127 #include <name_mask.h>
128 #include <vstring.h>
129 
130 #define STR(x) vstring_str(x)
131 
132 /* name_mask_delim_opt - compute mask corresponding to list of names */
133 
134 int     name_mask_delim_opt(const char *context, const NAME_MASK *table,
135 		            const char *names, const char *delim, int flags)
136 {
137     const char *myname = "name_mask";
138     char   *saved_names = mystrdup(names);
139     char   *bp = saved_names;
140     int     result = 0;
141     const NAME_MASK *np;
142     char   *name;
143     int     (*lookup) (const char *, const char *);
144 
145     if (flags & NAME_MASK_ANY_CASE)
146 	lookup = strcasecmp;
147     else
148 	lookup = strcmp;
149 
150     /*
151      * Break up the names string, and look up each component in the table. If
152      * the name is found, merge its mask with the result.
153      */
154     while ((name = mystrtok(&bp, delim)) != 0) {
155 	for (np = table; /* void */ ; np++) {
156 	    if (np->name == 0) {
157 		if (flags & NAME_MASK_FATAL)
158 		    msg_fatal("unknown %s value \"%s\" in \"%s\"",
159 			      context, name, names);
160 		if (flags & NAME_MASK_RETURN) {
161 		    msg_warn("unknown %s value \"%s\" in \"%s\"",
162 			     context, name, names);
163 		    return (0);
164 		}
165 		break;
166 	    }
167 	    if (lookup(name, np->name) == 0) {
168 		if (msg_verbose)
169 		    msg_info("%s: %s", myname, name);
170 		result |= np->mask;
171 		break;
172 	    }
173 	}
174     }
175     myfree(saved_names);
176     return (result);
177 }
178 
179 /* str_name_mask_opt - mask to string */
180 
181 const char *str_name_mask_opt(VSTRING *buf, const char *context,
182 			              const NAME_MASK *table,
183 			              int mask, int flags)
184 {
185     const char *myname = "name_mask";
186     const NAME_MASK *np;
187     int     len;
188     static VSTRING *my_buf = 0;
189     int     delim = (flags & NAME_MASK_COMMA ? ',' :
190 		     (flags & NAME_MASK_PIPE ? '|' : ' '));
191 
192     if (buf == 0) {
193 	if (my_buf == 0)
194 	    my_buf = vstring_alloc(1);
195 	buf = my_buf;
196     }
197     VSTRING_RESET(buf);
198 
199     for (np = table; mask != 0; np++) {
200 	if (np->name == 0) {
201 	    if (flags & NAME_MASK_FATAL) {
202 		msg_fatal("%s: unknown %s bit in mask: 0x%x",
203 			  myname, context, mask);
204 	    } else if (flags & NAME_MASK_RETURN) {
205 		msg_warn("%s: unknown %s bit in mask: 0x%x",
206 			 myname, context, mask);
207 		return (0);
208 	    } else if (flags & NAME_MASK_NUMBER) {
209 		vstring_sprintf_append(buf, "0x%x%c", mask, delim);
210 	    }
211 	    break;
212 	}
213 	if (mask & np->mask) {
214 	    mask &= ~np->mask;
215 	    vstring_sprintf_append(buf, "%s%c", np->name, delim);
216 	}
217     }
218     if ((len = VSTRING_LEN(buf)) > 0)
219 	vstring_truncate(buf, len - 1);
220     VSTRING_TERMINATE(buf);
221 
222     return (STR(buf));
223 }
224 
225 #ifdef TEST
226 
227  /*
228   * Stand-alone test program.
229   */
230 #include <stdlib.h>
231 #include <vstream.h>
232 
233 int     main(int argc, char **argv)
234 {
235     static const NAME_MASK table[] = {
236 	"zero", 1 << 0,
237 	"one", 1 << 1,
238 	"two", 1 << 2,
239 	"three", 1 << 3,
240 	0, 0,
241     };
242     int     mask;
243     VSTRING *buf = vstring_alloc(1);
244 
245     while (--argc && *++argv) {
246 	mask = name_mask("test", table, *argv);
247 	vstream_printf("%s -> 0x%x -> %s\n",
248 		       *argv, mask, str_name_mask("mask_test", table, mask));
249 	vstream_fflush(VSTREAM_OUT);
250     }
251     vstring_free(buf);
252     exit(0);
253 }
254 
255 #endif
256