Postfix3.3.1
mac_expand.c
[詳解]
1 /*++
2 /* NAME
3 /* mac_expand 3
4 /* SUMMARY
5 /* attribute expansion
6 /* SYNOPSIS
7 /* #include <mac_expand.h>
8 /*
9 /* int mac_expand(result, pattern, flags, filter, lookup, context)
10 /* VSTRING *result;
11 /* const char *pattern;
12 /* int flags;
13 /* const char *filter;
14 /* const char *lookup(const char *key, int mode, void *context)
15 /* void *context;
16 /* DESCRIPTION
17 /* This module implements parameter-less named attribute
18 /* expansions, both conditional and unconditional. As of Postfix
19 /* 3.0 this code supports relational expression evaluation.
20 /*
21 /* In this text, an attribute is considered "undefined" when its value
22 /* is a null pointer. Otherwise, the attribute is considered "defined"
23 /* and is expected to have as value a null-terminated string.
24 /*
25 /* In the text below, the legacy form $(...) is equivalent to
26 /* ${...}. The legacy form $(...) may eventually disappear
27 /* from documentation. In the text below, the name in $name
28 /* and ${name...} must contain only characters from the set
29 /* [a-zA-Z0-9_].
30 /*
31 /* The following substitutions are supported:
32 /* .IP "$name, ${name}"
33 /* Unconditional attribute-based substition. The result is the
34 /* named attribute value (empty if the attribute is not defined)
35 /* after optional further named attribute substitution.
36 /* .IP "${name?text}, ${name?{text}}"
37 /* Conditional attribute-based substition. If the named attribute
38 /* value is non-empty, the result is the given text, after
39 /* named attribute expansion and relational expression evaluation.
40 /* Otherwise, the result is empty. Whitespace before or after
41 /* {text} is ignored.
42 /* .IP "${name:text}, ${name:{text}}"
43 /* Conditional attribute-based substition. If the attribute
44 /* value is empty or undefined, the expansion is the given
45 /* text, after named attribute expansion and relational expression
46 /* evaluation. Otherwise, the result is empty. Whitespace
47 /* before or after {text} is ignored.
48 /* .IP "${name?{text1}:{text2}}, ${name?{text1}:text2}"
49 /* Conditional attribute-based substition. If the named attribute
50 /* value is non-empty, the result is text1. Otherwise, the
51 /* result is text2. In both cases the result is subject to
52 /* named attribute expansion and relational expression evaluation.
53 /* Whitespace before or after {text1} or {text2} is ignored.
54 /* .IP "${{text1} == ${text2} ? {text3} : {text4}}"
55 /* Relational expression-based substition. First, the content
56 /* of {text1} and ${text2} is subjected to named attribute and
57 /* relational expression-based substitution. Next, the relational
58 /* expression is evaluated. If it evaluates to "true", the
59 /* result is the content of {text3}, otherwise it is the content
60 /* of {text4}, after named attribute and relational expression-based
61 /* substitution. In addition to ==, this supports !=, <, <=,
62 /* >=, and >. Comparisons are numerical when both operands are
63 /* all digits, otherwise the comparisons are lexicographical.
64 /*
65 /* Arguments:
66 /* .IP result
67 /* Storage for the result of expansion. By default, the result
68 /* is truncated upon entry.
69 /* .IP pattern
70 /* The string to be expanded.
71 /* .IP flags
72 /* Bit-wise OR of zero or more of the following:
73 /* .RS
74 /* .IP MAC_EXP_FLAG_RECURSE
75 /* Expand attributes in lookup results. This should never be
76 /* done with data whose origin is untrusted.
77 /* .IP MAC_EXP_FLAG_APPEND
78 /* Append text to the result buffer without truncating it.
79 /* .IP MAC_EXP_FLAG_SCAN
80 /* Scan the input for named attributes, including named
81 /* attributes in all conditional result values. Do not expand
82 /* named attributes, and do not truncate or write to the result
83 /* argument.
84 /* .IP MAC_EXP_FLAG_PRINTABLE
85 /* Use the printable() function instead of \fIfilter\fR.
86 /* .PP
87 /* The constant MAC_EXP_FLAG_NONE specifies a manifest null value.
88 /* .RE
89 /* .IP filter
90 /* A null pointer, or a null-terminated array of characters that
91 /* are allowed to appear in an expansion. Illegal characters are
92 /* replaced by underscores.
93 /* .IP lookup
94 /* The attribute lookup routine. Arguments are: the attribute name,
95 /* MAC_EXP_MODE_TEST to test the existence of the named attribute
96 /* or MAC_EXP_MODE_USE to use the value of the named attribute,
97 /* and the caller context that was given to mac_expand(). A null
98 /* result value means that the requested attribute was not defined.
99 /* .IP context
100 /* Caller context that is passed on to the attribute lookup routine.
101 /* DIAGNOSTICS
102 /* Fatal errors: out of memory. Warnings: syntax errors, unreasonable
103 /* recursion depth.
104 /*
105 /* The result value is the binary OR of zero or more of the following:
106 /* .IP MAC_PARSE_ERROR
107 /* A syntax error was found in \fBpattern\fR, or some attribute had
108 /* an unreasonable nesting depth.
109 /* .IP MAC_PARSE_UNDEF
110 /* An attribute was expanded but its value was not defined.
111 /* SEE ALSO
112 /* mac_parse(3) locate macro references in string.
113 /* LICENSE
114 /* .ad
115 /* .fi
116 /* The Secure Mailer license must be distributed with this software.
117 /* AUTHOR(S)
118 /* Wietse Venema
119 /* IBM T.J. Watson Research
120 /* P.O. Box 704
121 /* Yorktown Heights, NY 10598, USA
122 /*
123 /* Wietse Venema
124 /* Google, Inc.
125 /* 111 8th Avenue
126 /* New York, NY 10011, USA
127 /*--*/
128 
129 /* System library. */
130 
131 #include <sys_defs.h>
132 #include <ctype.h>
133 #include <errno.h>
134 #include <string.h>
135 #include <stdlib.h>
136 
137 /* Utility library. */
138 
139 #include <msg.h>
140 #include <vstring.h>
141 #include <mymalloc.h>
142 #include <stringops.h>
143 #include <name_code.h>
144 #include <mac_parse.h>
145 #include <mac_expand.h>
146 
147  /*
148  * Little helper structure.
149  */
150 typedef struct {
151  VSTRING *result; /* result buffer */
152  int flags; /* features */
153  const char *filter; /* character filter */
154  MAC_EXP_LOOKUP_FN lookup; /* lookup routine */
155  void *context; /* caller context */
156  int status; /* findings */
157  int level; /* nesting level */
159 
160  /*
161  * Support for relational expressions.
162  *
163  * As of Postfix 2.2, ${attr-name?result} or ${attr-name:result} return the
164  * result respectively when the parameter value is non-empty, or when the
165  * parameter value is undefined or empty; support for the ternary ?:
166  * operator was anticipated, but not implemented for 10 years.
167  *
168  * To make ${relational-expr?result} and ${relational-expr:result} work as
169  * expected without breaking the way that ? and : work, relational
170  * expressions evaluate to a non-empty or empty value. It does not matter
171  * what non-empty value we use for TRUE. However we must not use the
172  * undefined (null pointer) value for FALSE - that would raise the
173  * MAC_PARSE_UNDEF flag.
174  *
175  * The value of a relational expression can be exposed with ${relational-expr},
176  * i.e. a relational expression that is not followed by ? or : conditional
177  * expansion.
178  */
179 #define MAC_EXP_BVAL_TRUE "true"
180 #define MAC_EXP_BVAL_FALSE ""
181 
182  /*
183  * Relational operators.
184  */
185 #define MAC_EXP_OP_STR_EQ "=="
186 #define MAC_EXP_OP_STR_NE "!="
187 #define MAC_EXP_OP_STR_LT "<"
188 #define MAC_EXP_OP_STR_LE "<="
189 #define MAC_EXP_OP_STR_GE ">="
190 #define MAC_EXP_OP_STR_GT ">"
191 #define MAC_EXP_OP_STR_ANY "\"" MAC_EXP_OP_STR_EQ \
192  "\" or \"" MAC_EXP_OP_STR_NE "\"" \
193  "\" or \"" MAC_EXP_OP_STR_LT "\"" \
194  "\" or \"" MAC_EXP_OP_STR_LE "\"" \
195  "\" or \"" MAC_EXP_OP_STR_GE "\"" \
196  "\" or \"" MAC_EXP_OP_STR_GT "\""
197 
198 #define MAC_EXP_OP_TOK_NONE 0
199 #define MAC_EXP_OP_TOK_EQ 1
200 #define MAC_EXP_OP_TOK_NE 2
201 #define MAC_EXP_OP_TOK_LT 3
202 #define MAC_EXP_OP_TOK_LE 4
203 #define MAC_EXP_OP_TOK_GE 5
204 #define MAC_EXP_OP_TOK_GT 6
205 
206 static const NAME_CODE mac_exp_op_table[] =
207 {
215 };
216 
217  /*
218  * The whitespace separator set.
219  */
220 #define MAC_EXP_WHITESPACE CHARS_SPACE
221 
222 /* atol_or_die - convert or die */
223 
224 static long atol_or_die(const char *strval)
225 {
226  long result;
227  char *remainder;
228 
229  result = strtol(strval, &remainder, 10);
230  if (*strval == 0 /* can't happen */ || *remainder != 0 || errno == ERANGE)
231  msg_fatal("mac_exp_eval: bad conversion: %s", strval);
232  return (result);
233 }
234 
235 /* mac_exp_eval - evaluate binary expression */
236 
237 static int mac_exp_eval(const char *left, int tok_val,
238  const char *rite)
239 {
240  static const char myname[] = "mac_exp_eval";
241  long delta;
242 
243  /*
244  * Numerical or string comparison.
245  */
246  if (alldig(left) && alldig(rite)) {
247  delta = atol_or_die(left) - atol_or_die(rite);
248  } else {
249  delta = strcmp(left, rite);
250  }
251  switch (tok_val) {
252  case MAC_EXP_OP_TOK_EQ:
253  return (delta == 0);
254  case MAC_EXP_OP_TOK_NE:
255  return (delta != 0);
256  case MAC_EXP_OP_TOK_LT:
257  return (delta < 0);
258  case MAC_EXP_OP_TOK_LE:
259  return (delta <= 0);
260  case MAC_EXP_OP_TOK_GE:
261  return (delta >= 0);
262  case MAC_EXP_OP_TOK_GT:
263  return (delta > 0);
264  default:
265  msg_panic("%s: unknown operator: %d",
266  myname, tok_val);
267  }
268 }
269 
270 /* mac_exp_parse_error - report parse error, set error flag, return status */
271 
272 static int PRINTFLIKE(2, 3) mac_exp_parse_error(MAC_EXP_CONTEXT *mc,
273  const char *fmt,...)
274 {
275  va_list ap;
276 
277  va_start(ap, fmt);
278  vmsg_warn(fmt, ap);
279  va_end(ap);
280  return (mc->status |= MAC_PARSE_ERROR);
281 };
282 
283 /* MAC_EXP_ERR_RETURN - report parse error, set error flag, return status */
284 
285 #define MAC_EXP_ERR_RETURN(mc, fmt, ...) do { \
286  return (mac_exp_parse_error(mc, fmt, __VA_ARGS__)); \
287  } while (0)
288 
289  /*
290  * Postfix 3.0 introduces support for {text} operands. Only with these do we
291  * support the ternary ?: operator and relational operators.
292  *
293  * We cannot support operators in random text, because that would break Postfix
294  * 2.11 compatibility. For example, with the expression "${name?value}", the
295  * value is random text that may contain ':', '?', '{' and '}' characters.
296  * In particular, with Postfix 2.2 .. 2.11, "${name??foo:{b}ar}" evaluates
297  * to "?foo:{b}ar" or empty. There are explicit tests in this directory and
298  * the postconf directory to ensure that Postfix 2.11 compatibility is
299  * maintained.
300  *
301  * Ideally, future Postfix configurations enclose random text operands inside
302  * {} braces. These allow whitespace around operands, which improves
303  * readability.
304  */
305 
306 /* MAC_EXP_FIND_LEFT_CURLY - skip over whitespace to '{', advance read ptr */
307 
308 #define MAC_EXP_FIND_LEFT_CURLY(len, cp) \
309  ((cp[len = strspn(cp, MAC_EXP_WHITESPACE)] == '{') ? \
310  (cp += len) : 0)
311 
312 /* mac_exp_extract_curly_payload - balance {}, skip whitespace, return payload */
313 
314 static char *mac_exp_extract_curly_payload(MAC_EXP_CONTEXT *mc, char **bp)
315 {
316  char *payload;
317  char *cp;
318  int level;
319  int ch;
320 
321  /*
322  * Extract the payload and balance the {}. The caller is expected to skip
323  * leading whitespace before the {. See MAC_EXP_FIND_LEFT_CURLY().
324  */
325  for (level = 1, cp = *bp, payload = ++cp; /* see below */ ; cp++) {
326  if ((ch = *cp) == 0) {
327  mac_exp_parse_error(mc, "unbalanced {} in attribute expression: "
328  "\"%s\"",
329  *bp);
330  return (0);
331  } else if (ch == '{') {
332  level++;
333  } else if (ch == '}') {
334  if (--level <= 0)
335  break;
336  }
337  }
338  *cp++ = 0;
339 
340  /*
341  * Skip trailing whitespace after }.
342  */
343  *bp = cp + strspn(cp, MAC_EXP_WHITESPACE);
344  return (payload);
345 }
346 
347 /* mac_exp_parse_relational - parse relational expression, advance read ptr */
348 
349 static int mac_exp_parse_relational(MAC_EXP_CONTEXT *mc, const char **lookup,
350  char **bp)
351 {
352  char *cp = *bp;
353  VSTRING *left_op_buf;
354  VSTRING *rite_op_buf;
355  const char *left_op_strval;
356  const char *rite_op_strval;
357  char *op_pos;
358  char *op_strval;
359  size_t op_len;
360  int op_tokval;
361  int op_result;
362  size_t tmp_len;
363 
364  /*
365  * Left operand. The caller is expected to skip leading whitespace before
366  * the {. See MAC_EXP_FIND_LEFT_CURLY().
367  */
368  if ((left_op_strval = mac_exp_extract_curly_payload(mc, &cp)) == 0)
369  return (mc->status);
370 
371  /*
372  * Operator. Todo: regexp operator.
373  */
374  op_pos = cp;
375  op_len = strspn(cp, "<>!=?+-*/~&|%"); /* for better diagnostics. */
376  op_strval = mystrndup(cp, op_len);
377  op_tokval = name_code(mac_exp_op_table, NAME_CODE_FLAG_NONE, op_strval);
378  myfree(op_strval);
379  if (op_tokval == MAC_EXP_OP_TOK_NONE)
380  MAC_EXP_ERR_RETURN(mc, "%s expected at: \"...%s}>>>%.20s\"",
381  MAC_EXP_OP_STR_ANY, left_op_strval, cp);
382  cp += op_len;
383 
384  /*
385  * Right operand. Todo: syntax may depend on operator.
386  */
387  if (MAC_EXP_FIND_LEFT_CURLY(tmp_len, cp) == 0)
388  MAC_EXP_ERR_RETURN(mc, "\"{expression}\" expected at: "
389  "\"...{%s} %.*s>>>%.20s\"",
390  left_op_strval, (int) op_len, op_pos, cp);
391  if ((rite_op_strval = mac_exp_extract_curly_payload(mc, &cp)) == 0)
392  return (mc->status);
393 
394  /*
395  * Evaluate the relational expression. Todo: regexp support.
396  */
397  mc->status |=
398  mac_expand(left_op_buf = vstring_alloc(100), left_op_strval,
399  mc->flags, mc->filter, mc->lookup, mc->context);
400  mc->status |=
401  mac_expand(rite_op_buf = vstring_alloc(100), rite_op_strval,
402  mc->flags, mc->filter, mc->lookup, mc->context);
403  op_result = mac_exp_eval(vstring_str(left_op_buf), op_tokval,
404  vstring_str(rite_op_buf));
405  vstring_free(left_op_buf);
406  vstring_free(rite_op_buf);
407  if (mc->status & MAC_PARSE_ERROR)
408  return (mc->status);
409 
410  /*
411  * Here, we fake up a non-empty or empty parameter value lookup result,
412  * for compatibility with the historical code that looks named parameter
413  * values.
414  */
415  *lookup = (op_result ? MAC_EXP_BVAL_TRUE : MAC_EXP_BVAL_FALSE);
416  *bp = cp;
417  return (0);
418 }
419 
420 /* mac_expand_callback - callback for mac_parse */
421 
422 static int mac_expand_callback(int type, VSTRING *buf, void *ptr)
423 {
424  static const char myname[] = "mac_expand_callback";
425  MAC_EXP_CONTEXT *mc = (MAC_EXP_CONTEXT *) ptr;
426  int lookup_mode;
427  const char *lookup;
428  char *cp;
429  int ch;
430  ssize_t res_len;
431  ssize_t tmp_len;
432  const char *res_iftrue;
433  const char *res_iffalse;
434 
435  /*
436  * Sanity check.
437  */
438  if (mc->level++ > 100)
439  mac_exp_parse_error(mc, "unreasonable macro call nesting: \"%s\"",
440  vstring_str(buf));
441  if (mc->status & MAC_PARSE_ERROR)
442  return (mc->status);
443 
444  /*
445  * Named parameter or relational expression. In case of a syntax error,
446  * return without doing damage, and issue a warning instead.
447  */
448  if (type == MAC_PARSE_EXPR) {
449 
450  cp = vstring_str(buf);
451 
452  /*
453  * Relational expression. If recursion is disabled, perform only one
454  * level of $name expansion.
455  */
456  if (MAC_EXP_FIND_LEFT_CURLY(tmp_len, cp)) {
457  if (mac_exp_parse_relational(mc, &lookup, &cp) != 0)
458  return (mc->status);
459 
460  /*
461  * Look for the ? or : operator.
462  */
463  if ((ch = *cp) != 0) {
464  if (ch != '?' && ch != ':')
465  MAC_EXP_ERR_RETURN(mc, "\"?\" or \":\" expected at: "
466  "\"...}>>>%.20s\"", cp);
467  cp++;
468  }
469  }
470 
471  /*
472  * Named parameter.
473  */
474  else {
475  char *start;
476 
477  /*
478  * Look for the ? or : operator. In case of a syntax error,
479  * return without doing damage, and issue a warning instead.
480  */
481  start = (cp += strspn(cp, MAC_EXP_WHITESPACE));
482  for ( /* void */ ; /* void */ ; cp++) {
483  if ((ch = cp[tmp_len = strspn(cp, MAC_EXP_WHITESPACE)]) == 0) {
484  *cp = 0;
485  lookup_mode = MAC_EXP_MODE_USE;
486  break;
487  }
488  if (ch == '?' || ch == ':') {
489  *cp++ = 0;
490  cp += tmp_len;
491  lookup_mode = MAC_EXP_MODE_TEST;
492  break;
493  }
494  ch = *cp;
495  if (!ISALNUM(ch) && ch != '_') {
496  MAC_EXP_ERR_RETURN(mc, "attribute name syntax error at: "
497  "\"...%.*s>>>%.20s\"",
498  (int) (cp - vstring_str(buf)),
499  vstring_str(buf), cp);
500  }
501  }
502 
503  /*
504  * Look up the named parameter. Todo: allow the lookup function
505  * to specify if the result is safe for $name expanson.
506  */
507  lookup = mc->lookup(start, lookup_mode, mc->context);
508  }
509 
510  /*
511  * Return the requested result. After parsing the result operand
512  * following ?, we fall through to parse the result operand following
513  * :. This is necessary with the ternary ?: operator: first, with
514  * MAC_EXP_FLAG_SCAN to parse both result operands with mac_parse(),
515  * and second, to find garbage after any result operand. Without
516  * MAC_EXP_FLAG_SCAN the content of only one of the ?: result
517  * operands will be parsed with mac_parse(); syntax errors in the
518  * other operand will be missed.
519  */
520  switch (ch) {
521  case '?':
522  if (MAC_EXP_FIND_LEFT_CURLY(tmp_len, cp)) {
523  if ((res_iftrue = mac_exp_extract_curly_payload(mc, &cp)) == 0)
524  return (mc->status);
525  } else {
526  res_iftrue = cp;
527  cp = ""; /* no left-over text */
528  }
529  if ((lookup != 0 && *lookup != 0) || (mc->flags & MAC_EXP_FLAG_SCAN))
530  mc->status |= mac_parse(res_iftrue, mac_expand_callback,
531  (void *) mc);
532  if (*cp == 0) /* end of input, OK */
533  break;
534  if (*cp != ':') /* garbage */
535  MAC_EXP_ERR_RETURN(mc, "\":\" expected at: "
536  "\"...%s}>>>%.20s\"", res_iftrue, cp);
537  cp += 1;
538  /* FALLTHROUGH: do not remove, see comment above. */
539  case ':':
540  if (MAC_EXP_FIND_LEFT_CURLY(tmp_len, cp)) {
541  if ((res_iffalse = mac_exp_extract_curly_payload(mc, &cp)) == 0)
542  return (mc->status);
543  } else {
544  res_iffalse = cp;
545  cp = ""; /* no left-over text */
546  }
547  if (lookup == 0 || *lookup == 0 || (mc->flags & MAC_EXP_FLAG_SCAN))
548  mc->status |= mac_parse(res_iffalse, mac_expand_callback,
549  (void *) mc);
550  if (*cp != 0) /* garbage */
551  MAC_EXP_ERR_RETURN(mc, "unexpected input at: "
552  "\"...%s}>>>%.20s\"", res_iffalse, cp);
553  break;
554  case 0:
555  if (lookup == 0) {
556  mc->status |= MAC_PARSE_UNDEF;
557  } else if (*lookup == 0 || (mc->flags & MAC_EXP_FLAG_SCAN)) {
558  /* void */ ;
559  } else if (mc->flags & MAC_EXP_FLAG_RECURSE) {
560  vstring_strcpy(buf, lookup);
561  mc->status |= mac_parse(vstring_str(buf), mac_expand_callback,
562  (void *) mc);
563  } else {
564  res_len = VSTRING_LEN(mc->result);
565  vstring_strcat(mc->result, lookup);
566  if (mc->flags & MAC_EXP_FLAG_PRINTABLE) {
567  printable(vstring_str(mc->result) + res_len, '_');
568  } else if (mc->filter) {
569  cp = vstring_str(mc->result) + res_len;
570  while (*(cp += strspn(cp, mc->filter)))
571  *cp++ = '_';
572  }
573  }
574  break;
575  default:
576  msg_panic("%s: unknown operator code %d", myname, ch);
577  }
578  }
579 
580  /*
581  * Literal text.
582  */
583  else if ((mc->flags & MAC_EXP_FLAG_SCAN) == 0) {
584  vstring_strcat(mc->result, vstring_str(buf));
585  }
586  mc->level--;
587 
588  return (mc->status);
589 }
590 
591 /* mac_expand - expand $name instances */
592 
593 int mac_expand(VSTRING *result, const char *pattern, int flags,
594  const char *filter,
595  MAC_EXP_LOOKUP_FN lookup, void *context)
596 {
597  MAC_EXP_CONTEXT mc;
598  int status;
599 
600  /*
601  * Bundle up the request and do the substitutions.
602  */
603  mc.result = result;
604  mc.flags = flags;
605  mc.filter = filter;
606  mc.lookup = lookup;
607  mc.context = context;
608  mc.status = 0;
609  mc.level = 0;
610  if ((flags & (MAC_EXP_FLAG_APPEND | MAC_EXP_FLAG_SCAN)) == 0)
611  VSTRING_RESET(result);
612  status = mac_parse(pattern, mac_expand_callback, (void *) &mc);
613  if ((flags & MAC_EXP_FLAG_SCAN) == 0)
614  VSTRING_TERMINATE(result);
615 
616  return (status);
617 }
618 
619 #ifdef TEST
620 
621  /*
622  * This code certainly deserves a stand-alone test program.
623  */
624 #include <stdlib.h>
625 #include <stringops.h>
626 #include <htable.h>
627 #include <vstream.h>
628 #include <vstring_vstream.h>
629 
630 static const char *lookup(const char *name, int unused_mode, void *context)
631 {
632  HTABLE *table = (HTABLE *) context;
633 
634  return (htable_find(table, name));
635 }
636 
637 int main(int unused_argc, char **unused_argv)
638 {
639  VSTRING *buf = vstring_alloc(100);
640  VSTRING *result = vstring_alloc(100);
641  char *cp;
642  char *name;
643  char *value;
644  HTABLE *table;
645  int stat;
646 
647  while (!vstream_feof(VSTREAM_IN)) {
648 
649  table = htable_create(0);
650 
651  /*
652  * Read a block of definitions, terminated with an empty line.
653  */
654  while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) {
655  vstream_printf("<< %s\n", vstring_str(buf));
657  if (VSTRING_LEN(buf) == 0)
658  break;
659  cp = vstring_str(buf);
660  name = mystrtok(&cp, CHARS_SPACE "=");
661  value = mystrtok(&cp, CHARS_SPACE "=");
662  htable_enter(table, name, value ? mystrdup(value) : 0);
663  }
664 
665  /*
666  * Read a block of patterns, terminated with an empty line or EOF.
667  */
668  while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) {
669  vstream_printf("<< %s\n", vstring_str(buf));
671  if (VSTRING_LEN(buf) == 0)
672  break;
673  cp = vstring_str(buf);
674  VSTRING_RESET(result);
675  stat = mac_expand(result, vstring_str(buf), MAC_EXP_FLAG_NONE,
676  (char *) 0, lookup, (void *) table);
677  vstream_printf("stat=%d result=%s\n", stat, vstring_str(result));
679  }
680  htable_free(table, myfree);
681  vstream_printf("\n");
682  }
683 
684  /*
685  * Clean up.
686  */
687  vstring_free(buf);
688  vstring_free(result);
689  exit(0);
690 }
691 
692 #endif
void htable_free(HTABLE *table, void(*free_fn)(void *))
Definition: htable.c:287
#define VSTREAM_EOF
Definition: vstream.h:110
#define MAC_EXP_WHITESPACE
Definition: mac_expand.c:220
void myfree(void *ptr)
Definition: mymalloc.c:207
const char * filter
Definition: mac_expand.c:153
#define MAC_PARSE_EXPR
Definition: mac_parse.h:23
#define MAC_EXP_FLAG_PRINTABLE
Definition: mac_expand.h:27
char * mystrdup(const char *str)
Definition: mymalloc.c:225
int mac_expand(VSTRING *result, const char *pattern, int flags, const char *filter, MAC_EXP_LOOKUP_FN lookup, void *context)
Definition: mac_expand.c:593
#define MAC_EXP_OP_STR_ANY
Definition: mac_expand.c:191
int vstring_get_nonl(VSTRING *vp, VSTREAM *fp)
#define MAC_EXP_OP_STR_GE
Definition: mac_expand.c:189
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
#define MAC_EXP_FLAG_SCAN
Definition: mac_expand.h:26
#define vstring_str(vp)
Definition: vstring.h:71
#define VSTREAM_OUT
Definition: vstream.h:67
#define stat(p, s)
Definition: warn_stat.h:18
int main(int argc, char **argv)
Definition: anvil.c:1010
#define MAC_EXP_OP_STR_GT
Definition: mac_expand.c:190
#define MAC_EXP_OP_TOK_GE
Definition: mac_expand.c:203
#define MAC_EXP_FLAG_RECURSE
Definition: mac_expand.h:24
#define MAC_EXP_OP_TOK_NONE
Definition: mac_expand.c:198
#define VSTREAM_IN
Definition: vstream.h:66
int alldig(const char *string)
Definition: alldig.c:38
#define MAC_EXP_OP_TOK_EQ
Definition: mac_expand.c:199
char * mystrtok(char **src, const char *sep)
Definition: mystrtok.c:54
#define VSTRING_LEN(vp)
Definition: vstring.h:72
Definition: htable.h:25
VSTRING * vstring_strcpy(VSTRING *vp, const char *src)
Definition: vstring.c:431
#define VSTRING_TERMINATE(vp)
Definition: vstring.h:74
#define ISALNUM(c)
Definition: sys_defs.h:1745
int const char * fmt
VSTRING * result
Definition: mac_expand.c:151
HTABLE * htable_create(ssize_t size)
Definition: htable.c:179
#define MAC_EXP_ERR_RETURN(mc, fmt,...)
Definition: mac_expand.c:285
#define MAC_EXP_BVAL_FALSE
Definition: mac_expand.c:180
#define MAC_EXP_OP_TOK_GT
Definition: mac_expand.c:204
#define MAC_EXP_OP_TOK_LT
Definition: mac_expand.c:201
#define MAC_EXP_FLAG_NONE
Definition: mac_expand.h:23
const char *(* MAC_EXP_LOOKUP_FN)(const char *, int, void *)
Definition: mac_expand.h:35
#define MAC_EXP_MODE_USE
Definition: mac_expand.h:33
#define MAC_EXP_OP_STR_LT
Definition: mac_expand.c:187
#define MAC_EXP_OP_TOK_LE
Definition: mac_expand.c:202
#define MAC_EXP_BVAL_TRUE
Definition: mac_expand.c:179
VSTREAM * vstream_printf(const char *fmt,...)
Definition: vstream.c:1335
#define VSTRING_RESET(vp)
Definition: vstring.h:77
#define MAC_EXP_MODE_TEST
Definition: mac_expand.h:32
#define MAC_EXP_FIND_LEFT_CURLY(len, cp)
Definition: mac_expand.c:308
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
#define CHARS_SPACE
Definition: sys_defs.h:1762
#define NAME_CODE_FLAG_NONE
Definition: name_code.h:22
void * htable_find(HTABLE *table, const char *key)
Definition: htable.c:227
int name_code(const NAME_CODE *table, int flags, const char *name)
Definition: name_code.c:65
void vmsg_warn(const char *fmt, va_list ap)
Definition: msg.c:224
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
int vstream_fflush(VSTREAM *stream)
Definition: vstream.c:1257
int mac_parse(const char *value, MAC_PARSE_FN action, void *context)
Definition: mac_parse.c:85
char * mystrndup(const char *str, ssize_t len)
Definition: mymalloc.c:242
#define MAC_EXP_OP_TOK_NE
Definition: mac_expand.c:200
#define vstream_feof(vp)
Definition: vstream.h:121
#define MAC_EXP_OP_STR_NE
Definition: mac_expand.c:186
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
#define MAC_PARSE_UNDEF
Definition: mac_parse.h:28
#define MAC_EXP_OP_STR_LE
Definition: mac_expand.c:188
#define MAC_EXP_OP_STR_EQ
Definition: mac_expand.c:185
#define PRINTFLIKE(x, y)
Definition: sys_defs.h:1600
MAC_EXP_LOOKUP_FN lookup
Definition: mac_expand.c:154
#define MAC_EXP_FLAG_APPEND
Definition: mac_expand.h:25
char * printable(char *string, int replacement)
Definition: printable.c:49
VSTRING * vstring_strcat(VSTRING *vp, const char *src)
Definition: vstring.c:459
#define MAC_PARSE_ERROR
Definition: mac_parse.h:27
HTABLE_INFO * htable_enter(HTABLE *table, const char *key, void *value)
Definition: htable.c:212