Postfix3.3.1
name_mask.c
[詳解]
1 /*++
2 /* NAME
3 /* name_mask 3
4 /* SUMMARY
5 /* map names to bit mask
6 /* SYNOPSIS
7 /* #include <name_mask.h>
8 /*
9 /* int name_mask(context, table, names)
10 /* const char *context;
11 /* const NAME_MASK *table;
12 /* const char *names;
13 /*
14 /* long long_name_mask(context, table, names)
15 /* const char *context;
16 /* const LONG_NAME_MASK *table;
17 /* const char *names;
18 /*
19 /* const char *str_name_mask(context, table, mask)
20 /* const char *context;
21 /* const NAME_MASK *table;
22 /* int mask;
23 /*
24 /* const char *str_long_name_mask(context, table, mask)
25 /* const char *context;
26 /* const LONG_NAME_MASK *table;
27 /* long mask;
28 /*
29 /* int name_mask_opt(context, table, names, flags)
30 /* const char *context;
31 /* const NAME_MASK *table;
32 /* const char *names;
33 /* int flags;
34 /*
35 /* long long_name_mask_opt(context, table, names, flags)
36 /* const char *context;
37 /* const LONG_NAME_MASK *table;
38 /* const char *names;
39 /* int flags;
40 /*
41 /* int name_mask_delim_opt(context, table, names, delim, flags)
42 /* const char *context;
43 /* const NAME_MASK *table;
44 /* const char *names;
45 /* const char *delim;
46 /* int flags;
47 /*
48 /* long long_name_mask_delim_opt(context, table, names, delim, flags)
49 /* const char *context;
50 /* const LONG_NAME_MASK *table;
51 /* const char *names;
52 /* const char *delim;
53 /* int flags;
54 /*
55 /* const char *str_name_mask_opt(buf, context, table, mask, flags)
56 /* VSTRING *buf;
57 /* const char *context;
58 /* const NAME_MASK *table;
59 /* int mask;
60 /* int flags;
61 /*
62 /* const char *str_long_name_mask_opt(buf, context, table, mask, flags)
63 /* VSTRING *buf;
64 /* const char *context;
65 /* const LONG_NAME_MASK *table;
66 /* long mask;
67 /* int flags;
68 /* DESCRIPTION
69 /* name_mask() takes a null-terminated \fItable\fR with (name, mask)
70 /* values and computes the bit-wise OR of the masks that correspond
71 /* to the names listed in the \fInames\fR argument, separated by
72 /* comma and/or whitespace characters. The "long_" version returns
73 /* a "long int" bitmask, rather than an "int" bitmask.
74 /*
75 /* str_name_mask() translates a mask into its equlvalent names.
76 /* The result is written to a static buffer that is overwritten
77 /* upon each call. The "long_" version converts a "long int"
78 /* bitmask, rather than an "int" bitmask.
79 /*
80 /* name_mask_opt() and str_name_mask_opt() are extended versions
81 /* with additional fine control. name_mask_delim_opt() supports
82 /* non-default delimiter characters.
83 /*
84 /* Arguments:
85 /* .IP buf
86 /* Null pointer or pointer to buffer storage.
87 /* .IP context
88 /* What kind of names and
89 /* masks are being manipulated, in order to make error messages
90 /* more understandable. Typically, this would be the name of a
91 /* user-configurable parameter.
92 /* .IP table
93 /* Table with (name, bit mask) pairs.
94 /* .IP names
95 /* A list of names that is to be converted into a bit mask.
96 /* .IP mask
97 /* A bit mask.
98 /* .IP delim
99 /* Delimiter characters to use instead of whitespace and commas.
100 /* .IP flags
101 /* Bit-wise OR of one or more of the following. Where features
102 /* would have conflicting results (e.g., FATAL versus IGNORE),
103 /* the feature that takes precedence is described first.
104 /*
105 /* When converting from string to mask, at least one of the
106 /* following must be specified: NAME_MASK_FATAL, NAME_MASK_RETURN,
107 /* NAME_MASK_WARN or NAME_MASK_IGNORE.
108 /*
109 /* When converting from mask to string, at least one of the
110 /* following must be specified: NAME_MASK_NUMBER, NAME_MASK_FATAL,
111 /* NAME_MASK_RETURN, NAME_MASK_WARN or NAME_MASK_IGNORE.
112 /* .RS
113 /* .IP NAME_MASK_NUMBER
114 /* When converting from string to mask, accept hexadecimal
115 /* inputs starting with "0x" followed by hexadecimal digits.
116 /* Each hexadecimal input may specify multiple bits. This
117 /* feature is ignored for hexadecimal inputs that cannot be
118 /* converted (malformed, out of range, etc.).
119 /*
120 /* When converting from mask to string, represent bits not
121 /* defined in \fItable\fR as "0x" followed by hexadecimal
122 /* digits. This conversion always succeeds.
123 /* .IP NAME_MASK_FATAL
124 /* Require that all names listed in \fIname\fR exist in
125 /* \fItable\fR or that they can be parsed as a hexadecimal
126 /* string, and require that all bits listed in \fImask\fR exist
127 /* in \fItable\fR or that they can be converted to hexadecimal
128 /* string. Terminate with a fatal run-time error if this
129 /* condition is not met. This feature is enabled by default
130 /* when calling name_mask() or str_name_mask().
131 /* .IP NAME_MASK_RETURN
132 /* Require that all names listed in \fIname\fR exist in
133 /* \fItable\fR or that they can be parsed as a hexadecimal
134 /* string, and require that all bits listed in \fImask\fR exist
135 /* in \fItable\fR or that they can be converted to hexadecimal
136 /* string. Log a warning, and return 0 (name_mask()) or a
137 /* null pointer (str_name_mask()) if this condition is not
138 /* met. This feature is not enabled by default when calling
139 /* name_mask() or str_name_mask().
140 /* .IP NAME_MASK_WARN
141 /* Require that all names listed in \fIname\fR exist in
142 /* \fItable\fR or that they can be parsed as a hexadecimal
143 /* string, and require that all bits listed in \fImask\fR exist
144 /* in \fItable\fR or that they can be converted to hexadecimal
145 /* string. Log a warning if this condition is not met, continue
146 /* processing, and return all valid bits or names. This feature
147 /* is not enabled by default when calling name_mask() or
148 /* str_name_mask().
149 /* .IP NAME_MASK_IGNORE
150 /* Silently ignore names listed in \fIname\fR that don't exist
151 /* in \fItable\fR and that can't be parsed as a hexadecimal
152 /* string, and silently ignore bits listed in \fImask\fR that
153 /* don't exist in \fItable\fR and that can't be converted to
154 /* hexadecimal string.
155 /* .IP NAME_MASK_ANY_CASE
156 /* Enable case-insensitive matching.
157 /* This feature is not enabled by default when calling name_mask();
158 /* it has no effect with str_name_mask().
159 /* .IP NAME_MASK_COMMA
160 /* Use comma instead of space when converting a mask to string.
161 /* .IP NAME_MASK_PIPE
162 /* Use "|" instead of space when converting a mask to string.
163 /* .RE
164 /* The value NAME_MASK_NONE explicitly requests no features,
165 /* and NAME_MASK_DEFAULT enables the default options.
166 /* DIAGNOSTICS
167 /* Fatal: the \fInames\fR argument specifies a name not found in
168 /* \fItable\fR, or the \fImask\fR specifies a bit not found in
169 /* \fItable\fR.
170 /* LICENSE
171 /* .ad
172 /* .fi
173 /* The Secure Mailer license must be distributed with this software.
174 /* AUTHOR(S)
175 /* Wietse Venema
176 /* IBM T.J. Watson Research
177 /* P.O. Box 704
178 /* Yorktown Heights, NY 10598, USA
179 /*--*/
180 
181 /* System library. */
182 
183 #include <sys_defs.h>
184 #include <string.h>
185 #include <errno.h>
186 #include <stdlib.h>
187 
188 #ifdef STRCASECMP_IN_STRINGS_H
189 #include <strings.h>
190 #endif
191 
192 /* Utility library. */
193 
194 #include <msg.h>
195 #include <mymalloc.h>
196 #include <stringops.h>
197 #include <name_mask.h>
198 #include <vstring.h>
199 
200 static int hex_to_ulong(char *, unsigned long, unsigned long *);
201 
202 #define STR(x) vstring_str(x)
203 
204 /* name_mask_delim_opt - compute mask corresponding to list of names */
205 
206 int name_mask_delim_opt(const char *context, const NAME_MASK *table,
207  const char *names, const char *delim, int flags)
208 {
209  const char *myname = "name_mask";
210  char *saved_names = mystrdup(names);
211  char *bp = saved_names;
212  int result = 0;
213  const NAME_MASK *np;
214  char *name;
215  int (*lookup) (const char *, const char *);
216  unsigned long ulval;
217 
218  if ((flags & NAME_MASK_REQUIRED) == 0)
219  msg_panic("%s: missing NAME_MASK_FATAL/RETURN/WARN/IGNORE flag",
220  myname);
221 
222  if (flags & NAME_MASK_ANY_CASE)
223  lookup = strcasecmp;
224  else
225  lookup = strcmp;
226 
227  /*
228  * Break up the names string, and look up each component in the table. If
229  * the name is found, merge its mask with the result.
230  */
231  while ((name = mystrtok(&bp, delim)) != 0) {
232  for (np = table; /* void */ ; np++) {
233  if (np->name == 0) {
234  if ((flags & NAME_MASK_NUMBER)
235  && hex_to_ulong(name, ~0U, &ulval)) {
236  result |= (unsigned int) ulval;
237  } else if (flags & NAME_MASK_FATAL) {
238  msg_fatal("unknown %s value \"%s\" in \"%s\"",
239  context, name, names);
240  } else if (flags & NAME_MASK_RETURN) {
241  msg_warn("unknown %s value \"%s\" in \"%s\"",
242  context, name, names);
243  myfree(saved_names);
244  return (0);
245  } else if (flags & NAME_MASK_WARN) {
246  msg_warn("unknown %s value \"%s\" in \"%s\"",
247  context, name, names);
248  }
249  break;
250  }
251  if (lookup(name, np->name) == 0) {
252  if (msg_verbose)
253  msg_info("%s: %s", myname, name);
254  result |= np->mask;
255  break;
256  }
257  }
258  }
259  myfree(saved_names);
260  return (result);
261 }
262 
263 /* str_name_mask_opt - mask to string */
264 
265 const char *str_name_mask_opt(VSTRING *buf, const char *context,
266  const NAME_MASK *table,
267  int mask, int flags)
268 {
269  const char *myname = "name_mask";
270  const NAME_MASK *np;
271  ssize_t len;
272  static VSTRING *my_buf = 0;
273  int delim = (flags & NAME_MASK_COMMA ? ',' :
274  (flags & NAME_MASK_PIPE ? '|' : ' '));
275 
276  if ((flags & STR_NAME_MASK_REQUIRED) == 0)
277  msg_panic("%s: missing NAME_MASK_NUMBER/FATAL/RETURN/WARN/IGNORE flag",
278  myname);
279 
280  if (buf == 0) {
281  if (my_buf == 0)
282  my_buf = vstring_alloc(1);
283  buf = my_buf;
284  }
285  VSTRING_RESET(buf);
286 
287  for (np = table; mask != 0; np++) {
288  if (np->name == 0) {
289  if (flags & NAME_MASK_NUMBER) {
290  vstring_sprintf_append(buf, "0x%x%c", mask, delim);
291  } else if (flags & NAME_MASK_FATAL) {
292  msg_fatal("%s: unknown %s bit in mask: 0x%x",
293  myname, context, mask);
294  } else if (flags & NAME_MASK_RETURN) {
295  msg_warn("%s: unknown %s bit in mask: 0x%x",
296  myname, context, mask);
297  return (0);
298  } else if (flags & NAME_MASK_WARN) {
299  msg_warn("%s: unknown %s bit in mask: 0x%x",
300  myname, context, mask);
301  }
302  break;
303  }
304  if (mask & np->mask) {
305  mask &= ~np->mask;
306  vstring_sprintf_append(buf, "%s%c", np->name, delim);
307  }
308  }
309  if ((len = VSTRING_LEN(buf)) > 0)
310  vstring_truncate(buf, len - 1);
311  VSTRING_TERMINATE(buf);
312 
313  return (STR(buf));
314 }
315 
316 /* long_name_mask_delim_opt - compute mask corresponding to list of names */
317 
318 long long_name_mask_delim_opt(const char *context,
319  const LONG_NAME_MASK * table,
320  const char *names, const char *delim,
321  int flags)
322 {
323  const char *myname = "name_mask";
324  char *saved_names = mystrdup(names);
325  char *bp = saved_names;
326  long result = 0;
327  const LONG_NAME_MASK *np;
328  char *name;
329  int (*lookup) (const char *, const char *);
330  unsigned long ulval;
331 
332  if ((flags & NAME_MASK_REQUIRED) == 0)
333  msg_panic("%s: missing NAME_MASK_FATAL/RETURN/WARN/IGNORE flag",
334  myname);
335 
336  if (flags & NAME_MASK_ANY_CASE)
337  lookup = strcasecmp;
338  else
339  lookup = strcmp;
340 
341  /*
342  * Break up the names string, and look up each component in the table. If
343  * the name is found, merge its mask with the result.
344  */
345  while ((name = mystrtok(&bp, delim)) != 0) {
346  for (np = table; /* void */ ; np++) {
347  if (np->name == 0) {
348  if ((flags & NAME_MASK_NUMBER)
349  && hex_to_ulong(name, ~0UL, &ulval)) {
350  result |= ulval;
351  } else if (flags & NAME_MASK_FATAL) {
352  msg_fatal("unknown %s value \"%s\" in \"%s\"",
353  context, name, names);
354  } else if (flags & NAME_MASK_RETURN) {
355  msg_warn("unknown %s value \"%s\" in \"%s\"",
356  context, name, names);
357  myfree(saved_names);
358  return (0);
359  } else if (flags & NAME_MASK_WARN) {
360  msg_warn("unknown %s value \"%s\" in \"%s\"",
361  context, name, names);
362  }
363  break;
364  }
365  if (lookup(name, np->name) == 0) {
366  if (msg_verbose)
367  msg_info("%s: %s", myname, name);
368  result |= np->mask;
369  break;
370  }
371  }
372  }
373 
374  myfree(saved_names);
375  return (result);
376 }
377 
378 /* str_long_name_mask_opt - mask to string */
379 
380 const char *str_long_name_mask_opt(VSTRING *buf, const char *context,
381  const LONG_NAME_MASK * table,
382  long mask, int flags)
383 {
384  const char *myname = "name_mask";
385  ssize_t len;
386  static VSTRING *my_buf = 0;
387  int delim = (flags & NAME_MASK_COMMA ? ',' :
388  (flags & NAME_MASK_PIPE ? '|' : ' '));
389  const LONG_NAME_MASK *np;
390 
391  if ((flags & STR_NAME_MASK_REQUIRED) == 0)
392  msg_panic("%s: missing NAME_MASK_NUMBER/FATAL/RETURN/WARN/IGNORE flag",
393  myname);
394 
395  if (buf == 0) {
396  if (my_buf == 0)
397  my_buf = vstring_alloc(1);
398  buf = my_buf;
399  }
400  VSTRING_RESET(buf);
401 
402  for (np = table; mask != 0; np++) {
403  if (np->name == 0) {
404  if (flags & NAME_MASK_NUMBER) {
405  vstring_sprintf_append(buf, "0x%lx%c", mask, delim);
406  } else if (flags & NAME_MASK_FATAL) {
407  msg_fatal("%s: unknown %s bit in mask: 0x%lx",
408  myname, context, mask);
409  } else if (flags & NAME_MASK_RETURN) {
410  msg_warn("%s: unknown %s bit in mask: 0x%lx",
411  myname, context, mask);
412  return (0);
413  } else if (flags & NAME_MASK_WARN) {
414  msg_warn("%s: unknown %s bit in mask: 0x%lx",
415  myname, context, mask);
416  }
417  break;
418  }
419  if (mask & np->mask) {
420  mask &= ~np->mask;
421  vstring_sprintf_append(buf, "%s%c", np->name, delim);
422  }
423  }
424  if ((len = VSTRING_LEN(buf)) > 0)
425  vstring_truncate(buf, len - 1);
426  VSTRING_TERMINATE(buf);
427 
428  return (STR(buf));
429 }
430 
431 /* hex_to_ulong - 0x... to unsigned long or smaller */
432 
433 static int hex_to_ulong(char *value, unsigned long mask, unsigned long *ulp)
434 {
435  unsigned long result;
436  char *cp;
437 
438  if (strncasecmp(value, "0x", 2) != 0)
439  return (0);
440 
441  /*
442  * Check for valid hex number. Since the value starts with 0x, strtoul()
443  * will not allow a negative sign before the first nibble. So we don't
444  * need to worry about explicit +/- signs.
445  */
446  errno = 0;
447  result = strtoul(value, &cp, 16);
448  if (*cp != '\0' || errno == ERANGE)
449  return (0);
450 
451  *ulp = (result & mask);
452  return (*ulp == result);
453 }
454 
455 #ifdef TEST
456 
457  /*
458  * Stand-alone test program.
459  */
460 #include <stdlib.h>
461 #include <vstream.h>
462 #include <vstring_vstream.h>
463 
464 int main(int argc, char **argv)
465 {
466  static const NAME_MASK demo_table[] = {
467  "zero", 1 << 0,
468  "one", 1 << 1,
469  "two", 1 << 2,
470  "three", 1 << 3,
471  0, 0,
472  };
473  static const NAME_MASK feature_table[] = {
474  "DEFAULT", NAME_MASK_DEFAULT,
475  "FATAL", NAME_MASK_FATAL,
476  "ANY_CASE", NAME_MASK_ANY_CASE,
477  "RETURN", NAME_MASK_RETURN,
478  "COMMA", NAME_MASK_COMMA,
479  "PIPE", NAME_MASK_PIPE,
480  "NUMBER", NAME_MASK_NUMBER,
481  "WARN", NAME_MASK_WARN,
482  "IGNORE", NAME_MASK_IGNORE,
483  0,
484  };
485  int in_feature_mask;
486  int out_feature_mask;
487  int demo_mask;
488  const char *demo_str;
489  VSTRING *out_buf = vstring_alloc(1);
490  VSTRING *in_buf = vstring_alloc(1);
491 
492  if (argc != 3)
493  msg_fatal("usage: %s in-feature-mask out-feature-mask", argv[0]);
494  in_feature_mask = name_mask(argv[1], feature_table, argv[1]);
495  out_feature_mask = name_mask(argv[2], feature_table, argv[2]);
496  while (vstring_get_nonl(in_buf, VSTREAM_IN) != VSTREAM_EOF) {
497  demo_mask = name_mask_opt("name", demo_table,
498  STR(in_buf), in_feature_mask);
499  demo_str = str_name_mask_opt(out_buf, "mask", demo_table,
500  demo_mask, out_feature_mask);
501  vstream_printf("%s -> 0x%x -> %s\n",
502  STR(in_buf), demo_mask,
503  demo_str ? demo_str : "(null)");
505  }
506  vstring_free(in_buf);
507  vstring_free(out_buf);
508  exit(0);
509 }
510 
511 #endif
int msg_verbose
Definition: msg.c:177
#define VSTREAM_EOF
Definition: vstream.h:110
void myfree(void *ptr)
Definition: mymalloc.c:207
char * mystrdup(const char *str)
Definition: mymalloc.c:225
int vstring_get_nonl(VSTRING *vp, VSTREAM *fp)
#define NAME_MASK_IGNORE
Definition: name_mask.h:34
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
#define VSTREAM_OUT
Definition: vstream.h:67
const char * str_long_name_mask_opt(VSTRING *buf, const char *context, const LONG_NAME_MASK *table, long mask, int flags)
Definition: name_mask.c:380
int main(int argc, char **argv)
Definition: anvil.c:1010
VSTRING * vstring_truncate(VSTRING *vp, ssize_t len)
Definition: vstring.c:415
#define VSTREAM_IN
Definition: vstream.h:66
char * mystrtok(char **src, const char *sep)
Definition: mystrtok.c:54
#define VSTRING_LEN(vp)
Definition: vstring.h:72
int strncasecmp(const char *s1, const char *s2, size_t n)
Definition: strcasecmp.c:52
#define VSTRING_TERMINATE(vp)
Definition: vstring.h:74
#define STR_NAME_MASK_REQUIRED
Definition: name_mask.h:38
VSTRING * vstring_sprintf_append(VSTRING *vp, const char *format,...)
Definition: vstring.c:624
long long_name_mask_delim_opt(const char *context, const LONG_NAME_MASK *table, const char *names, const char *delim, int flags)
Definition: name_mask.c:318
const char * str_name_mask_opt(VSTRING *buf, const char *context, const NAME_MASK *table, int mask, int flags)
Definition: name_mask.c:265
const char * name
Definition: name_mask.h:23
VSTREAM * vstream_printf(const char *fmt,...)
Definition: vstream.c:1335
#define VSTRING_RESET(vp)
Definition: vstring.h:77
void msg_warn(const char *fmt,...)
Definition: msg.c:215
int name_mask_delim_opt(const char *context, const NAME_MASK *table, const char *names, const char *delim, int flags)
Definition: name_mask.c:206
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
#define name_mask(tag, table, str)
Definition: name_mask.h:49
#define NAME_MASK_RETURN
Definition: name_mask.h:29
const char * name
Definition: name_mask.h:61
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
int vstream_fflush(VSTREAM *stream)
Definition: vstream.c:1257
int int
Definition: smtpd_proxy.h:21
int strcasecmp(const char *s1, const char *s2)
Definition: strcasecmp.c:41
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
#define NAME_MASK_NUMBER
Definition: name_mask.h:32
#define NAME_MASK_FATAL
Definition: name_mask.h:27
#define NAME_MASK_DEFAULT
Definition: name_mask.h:43
#define NAME_MASK_ANY_CASE
Definition: name_mask.h:28
#define NAME_MASK_REQUIRED
Definition: name_mask.h:36
#define NAME_MASK_WARN
Definition: name_mask.h:33
#define NAME_MASK_PIPE
Definition: name_mask.h:31
#define STR(x)
Definition: name_mask.c:202
#define name_mask_opt(tag, table, str, flags)
Definition: name_mask.h:46
#define NAME_MASK_COMMA
Definition: name_mask.h:30
int mask
Definition: name_mask.h:24
void msg_info(const char *fmt,...)
Definition: msg.c:199