Postfix3.3.1
attr_scan0.c
[詳解]
1 /*++
2 /* NAME
3 /* attr_scan0 3
4 /* SUMMARY
5 /* recover attributes from byte stream
6 /* SYNOPSIS
7 /* #include <attr.h>
8 /*
9 /* int attr_scan0(fp, flags, type, name, ..., ATTR_TYPE_END)
10 /* VSTREAM *fp;
11 /* int flags;
12 /* int type;
13 /* char *name;
14 /*
15 /* int attr_vscan0(fp, flags, ap)
16 /* VSTREAM *fp;
17 /* int flags;
18 /* va_list ap;
19 /*
20 /* int attr_scan_more0(fp)
21 /* VSTREAM *fp;
22 /* DESCRIPTION
23 /* attr_scan0() takes zero or more (name, value) request attributes
24 /* and recovers the attribute values from the byte stream that was
25 /* possibly generated by attr_print0().
26 /*
27 /* attr_vscan0() provides an alternative interface that is convenient
28 /* for calling from within a variadic function.
29 /*
30 /* attr_scan_more0() returns 0 when a terminator is found (and
31 /* consumes that terminator), returns 1 when more input is
32 /* expected (without consuming input), and returns -1 otherwise
33 /* (error).
34 /*
35 /* The input stream is formatted as follows, where (item)* stands
36 /* for zero or more instances of the specified item, and where
37 /* (item1 | item2) stands for choice:
38 /*
39 /* .in +5
40 /* attr-list :== (simple-attr | multi-attr)* null
41 /* .br
42 /* multi-attr :== "{" null simple-attr* "}" null
43 /* .br
44 /* simple-attr :== attr-name null attr-value null
45 /* .br
46 /* attr-name :== any string not containing null
47 /* .br
48 /* attr-value :== any string not containing null
49 /* .br
50 /* null :== the ASCII null character
51 /* .in
52 /*
53 /* All attribute names and attribute values are sent as null terminated
54 /* strings. Each string must be no longer than 4*var_line_limit
55 /* characters including the terminator.
56 /* These formatting rules favor implementations in C.
57 /*
58 /* Normally, attributes must be received in the sequence as specified with
59 /* the attr_scan0() argument list. The input stream may contain additional
60 /* attributes at any point in the input stream, including additional
61 /* instances of requested attributes.
62 /*
63 /* Additional input attributes or input attribute instances are silently
64 /* skipped over, unless the ATTR_FLAG_EXTRA processing flag is specified
65 /* (see below). This allows for some flexibility in the evolution of
66 /* protocols while still providing the option of being strict where
67 /* this is desirable.
68 /*
69 /* Arguments:
70 /* .IP fp
71 /* Stream to recover the input attributes from.
72 /* .IP flags
73 /* The bit-wise OR of zero or more of the following.
74 /* .RS
75 /* .IP ATTR_FLAG_MISSING
76 /* Log a warning when the input attribute list terminates before all
77 /* requested attributes are recovered. It is always an error when the
78 /* input stream ends without the newline attribute list terminator.
79 /* .IP ATTR_FLAG_EXTRA
80 /* Log a warning and stop attribute recovery when the input stream
81 /* contains an attribute that was not requested. This includes the
82 /* case of additional instances of a requested attribute.
83 /* .IP ATTR_FLAG_MORE
84 /* After recovering the requested attributes, leave the input stream
85 /* in a state that is usable for more attr_scan0() operations from the
86 /* same input attribute list.
87 /* By default, attr_scan0() skips forward past the input attribute list
88 /* terminator.
89 /* .IP ATTR_FLAG_STRICT
90 /* For convenience, this value combines both ATTR_FLAG_MISSING and
91 /* ATTR_FLAG_EXTRA.
92 /* .IP ATTR_FLAG_NONE
93 /* For convenience, this value requests none of the above.
94 /* .RE
95 /* .IP List of attributes followed by terminator:
96 /* .RS
97 /* .IP "RECV_ATTR_INT(const char *name, int *ptr)"
98 /* This argument is followed by an attribute name and an integer pointer.
99 /* .IP "RECV_ATTR_LONG(const char *name, long *ptr)"
100 /* This argument is followed by an attribute name and a long pointer.
101 /* .IP "RECV_ATTR_STR(const char *name, VSTRING *vp)"
102 /* This argument is followed by an attribute name and a VSTRING pointer.
103 /* .IP "RECV_ATTR_DATA(const char *name, VSTRING *vp)"
104 /* This argument is followed by an attribute name and a VSTRING pointer.
105 /* .IP "RECV_ATTR_FUNC(ATTR_SCAN_SLAVE_FN, void *data)"
106 /* This argument is followed by a function pointer and a generic data
107 /* pointer. The caller-specified function returns < 0 in case of
108 /* error.
109 /* .IP "RECV_ATTR_HASH(HTABLE *table)"
110 /* .IP "RECV_ATTR_NAMEVAL(NVTABLE *table)"
111 /* Receive a sequence of attribute names and string values.
112 /* There can be no more than 1024 attributes in a hash table.
113 /* .sp
114 /* The attribute string values are stored in the hash table under
115 /* keys equal to the attribute name (obtained from the input stream).
116 /* Values from the input stream are added to the hash table. Existing
117 /* hash table entries are not replaced.
118 /* .sp
119 /* Note: the SEND_ATTR_HASH or SEND_ATTR_NAMEVAL requests
120 /* format their payload as a multi-attr sequence (see syntax
121 /* above). When the receiver's input does not start with a
122 /* multi-attr delimiter (i.e. the sender did not request
123 /* SEND_ATTR_HASH or SEND_ATTR_NAMEVAL), the receiver will
124 /* store all attribute names and values up to the attribute
125 /* list terminator. In terms of code, this means that the
126 /* RECV_ATTR_HASH or RECV_ATTR_NAMEVAL request must be followed
127 /* by ATTR_TYPE_END.
128 /* .IP ATTR_TYPE_END
129 /* This argument terminates the requested attribute list.
130 /* .RE
131 /* BUGS
132 /* RECV_ATTR_HASH (RECV_ATTR_NAMEVAL) accepts attributes with arbitrary
133 /* names from possibly untrusted sources.
134 /* This is unsafe, unless the resulting table is queried only with
135 /* known to be good attribute names.
136 /* DIAGNOSTICS
137 /* attr_scan0() and attr_vscan0() return -1 when malformed input is
138 /* detected (string too long, incomplete line, missing end marker).
139 /* Otherwise, the result value is the number of attributes that were
140 /* successfully recovered from the input stream (a hash table counts
141 /* as the number of entries stored into the table).
142 /*
143 /* Panic: interface violation. All system call errors are fatal.
144 /* SEE ALSO
145 /* attr_print0(3) send attributes over byte stream.
146 /* LICENSE
147 /* .ad
148 /* .fi
149 /* The Secure Mailer license must be distributed with this software.
150 /* AUTHOR(S)
151 /* Wietse Venema
152 /* IBM T.J. Watson Research
153 /* P.O. Box 704
154 /* Yorktown Heights, NY 10598, USA
155 /*
156 /* Wietse Venema
157 /* Google, Inc.
158 /* 111 8th Avenue
159 /* New York, NY 10011, USA
160 /*--*/
161 
162 /* System library. */
163 
164 #include <sys_defs.h>
165 #include <stdarg.h>
166 #include <string.h>
167 #include <stdio.h>
168 
169 /* Utility library. */
170 
171 #include <msg.h>
172 #include <mymalloc.h>
173 #include <vstream.h>
174 #include <vstring.h>
175 #include <vstring_vstream.h>
176 #include <htable.h>
177 #include <base64_code.h>
178 #include <attr.h>
179 
180 /* Application specific. */
181 
182 #define STR(x) vstring_str(x)
183 #define LEN(x) VSTRING_LEN(x)
184 
185 /* attr_scan0_string - pull a string from the input stream */
186 
187 static int attr_scan0_string(VSTREAM *fp, VSTRING *plain_buf, const char *context)
188 {
189  int ch;
190 
191  if ((ch = vstring_get_null(plain_buf, fp)) == VSTREAM_EOF) {
192  msg_warn("%s on %s while reading %s",
193  vstream_ftimeout(fp) ? "timeout" : "premature end-of-input",
194  VSTREAM_PATH(fp), context);
195  return (-1);
196  }
197  if (ch != 0) {
198  msg_warn("unexpected end-of-input from %s while reading %s",
199  VSTREAM_PATH(fp), context);
200  return (-1);
201  }
202  if (msg_verbose)
203  msg_info("%s: %s", context, *STR(plain_buf) ? STR(plain_buf) : "(end)");
204  return (ch);
205 }
206 
207 /* attr_scan0_data - pull a data blob from the input stream */
208 
209 static int attr_scan0_data(VSTREAM *fp, VSTRING *str_buf,
210  const char *context)
211 {
212  static VSTRING *base64_buf = 0;
213  int ch;
214 
215  if (base64_buf == 0)
216  base64_buf = vstring_alloc(10);
217  if ((ch = attr_scan0_string(fp, base64_buf, context)) < 0)
218  return (-1);
219  if (base64_decode(str_buf, STR(base64_buf), LEN(base64_buf)) == 0) {
220  msg_warn("malformed base64 data from %s while reading %s: %.100s",
221  VSTREAM_PATH(fp), context, STR(base64_buf));
222  return (-1);
223  }
224  return (ch);
225 }
226 
227 /* attr_scan0_number - pull a number from the input stream */
228 
229 static int attr_scan0_number(VSTREAM *fp, unsigned *ptr, VSTRING *str_buf,
230  const char *context)
231 {
232  char junk = 0;
233  int ch;
234 
235  if ((ch = attr_scan0_string(fp, str_buf, context)) < 0)
236  return (-1);
237  if (sscanf(STR(str_buf), "%u%c", ptr, &junk) != 1 || junk != 0) {
238  msg_warn("malformed numerical data from %s while reading %s: %.100s",
239  VSTREAM_PATH(fp), context, STR(str_buf));
240  return (-1);
241  }
242  return (ch);
243 }
244 
245 /* attr_scan0_long_number - pull a number from the input stream */
246 
247 static int attr_scan0_long_number(VSTREAM *fp, unsigned long *ptr,
248  VSTRING *str_buf,
249  const char *context)
250 {
251  char junk = 0;
252  int ch;
253 
254  if ((ch = attr_scan0_string(fp, str_buf, context)) < 0)
255  return (-1);
256  if (sscanf(STR(str_buf), "%lu%c", ptr, &junk) != 1 || junk != 0) {
257  msg_warn("malformed numerical data from %s while reading %s: %.100s",
258  VSTREAM_PATH(fp), context, STR(str_buf));
259  return (-1);
260  }
261  return (ch);
262 }
263 
264 /* attr_vscan0 - receive attribute list from stream */
265 
266 int attr_vscan0(VSTREAM *fp, int flags, va_list ap)
267 {
268  const char *myname = "attr_scan0";
269  static VSTRING *str_buf = 0;
270  static VSTRING *name_buf = 0;
271  int wanted_type = -1;
272  char *wanted_name;
273  unsigned int *number;
274  unsigned long *long_number;
275  VSTRING *string;
276  HTABLE *hash_table;
277  int ch;
278  int conversions;
279  ATTR_SCAN_SLAVE_FN scan_fn;
280  void *scan_arg;
281 
282  /*
283  * Sanity check.
284  */
285  if (flags & ~ATTR_FLAG_ALL)
286  msg_panic("%s: bad flags: 0x%x", myname, flags);
287 
288  /*
289  * EOF check.
290  */
291  if ((ch = VSTREAM_GETC(fp)) == VSTREAM_EOF)
292  return (0);
293  vstream_ungetc(fp, ch);
294 
295  /*
296  * Initialize.
297  */
298  if (str_buf == 0) {
299  str_buf = vstring_alloc(10);
300  name_buf = vstring_alloc(10);
301  }
302 
303  /*
304  * Iterate over all (type, name, value) triples.
305  */
306  for (conversions = 0; /* void */ ; conversions++) {
307 
308  /*
309  * Determine the next attribute type and attribute name on the
310  * caller's wish list.
311  *
312  * If we're reading into a hash table, we already know that the
313  * attribute value is string-valued, and we get the attribute name
314  * from the input stream instead. This is secure only when the
315  * resulting table is queried with known to be good attribute names.
316  */
317  if (wanted_type != ATTR_TYPE_HASH
318  && wanted_type != ATTR_TYPE_CLOSE) {
319  wanted_type = va_arg(ap, int);
320  if (wanted_type == ATTR_TYPE_END) {
321  if ((flags & ATTR_FLAG_MORE) != 0)
322  return (conversions);
323  wanted_name = "(list terminator)";
324  } else if (wanted_type == ATTR_TYPE_HASH) {
325  wanted_name = "(any attribute name or list terminator)";
326  hash_table = va_arg(ap, HTABLE *);
327  } else if (wanted_type != ATTR_TYPE_FUNC) {
328  wanted_name = va_arg(ap, char *);
329  }
330  }
331 
332  /*
333  * Locate the next attribute of interest in the input stream.
334  */
335  while (wanted_type != ATTR_TYPE_FUNC) {
336 
337  /*
338  * Get the name of the next attribute. Hitting EOF is always bad.
339  * Hitting the end-of-input early is OK if the caller is prepared
340  * to deal with missing inputs.
341  */
342  if (msg_verbose)
343  msg_info("%s: wanted attribute: %s",
344  VSTREAM_PATH(fp), wanted_name);
345  if ((ch = attr_scan0_string(fp, name_buf,
346  "input attribute name")) == VSTREAM_EOF)
347  return (-1);
348  if (LEN(name_buf) == 0) {
349  if (wanted_type == ATTR_TYPE_END
350  || wanted_type == ATTR_TYPE_HASH)
351  return (conversions);
352  if ((flags & ATTR_FLAG_MISSING) != 0)
353  msg_warn("missing attribute %s in input from %s",
354  wanted_name, VSTREAM_PATH(fp));
355  return (conversions);
356  }
357 
358  /*
359  * See if the caller asks for this attribute.
360  */
361  if (wanted_type == ATTR_TYPE_HASH
362  && strcmp(ATTR_NAME_OPEN, STR(name_buf)) == 0) {
363  wanted_type = ATTR_TYPE_CLOSE;
364  wanted_name = "(any attribute name or '}')";
365  /* Advance in the input stream. */
366  continue;
367  } else if (wanted_type == ATTR_TYPE_CLOSE
368  && strcmp(ATTR_NAME_CLOSE, STR(name_buf)) == 0) {
369  /* Advance in the argument list. */
370  wanted_type = -1;
371  break;
372  }
373  if (wanted_type == ATTR_TYPE_HASH
374  || wanted_type == ATTR_TYPE_CLOSE
375  || (wanted_type != ATTR_TYPE_END
376  && strcmp(wanted_name, STR(name_buf)) == 0))
377  break;
378  if ((flags & ATTR_FLAG_EXTRA) != 0) {
379  msg_warn("unexpected attribute %s from %s (expecting: %s)",
380  STR(name_buf), VSTREAM_PATH(fp), wanted_name);
381  return (conversions);
382  }
383 
384  /*
385  * Skip over this attribute. The caller does not ask for it.
386  */
387  (void) attr_scan0_string(fp, str_buf, "input attribute value");
388  }
389 
390  /*
391  * Do the requested conversion.
392  */
393  switch (wanted_type) {
394  case ATTR_TYPE_INT:
395  number = va_arg(ap, unsigned int *);
396  if ((ch = attr_scan0_number(fp, number, str_buf,
397  "input attribute value")) < 0)
398  return (-1);
399  break;
400  case ATTR_TYPE_LONG:
401  long_number = va_arg(ap, unsigned long *);
402  if ((ch = attr_scan0_long_number(fp, long_number, str_buf,
403  "input attribute value")) < 0)
404  return (-1);
405  break;
406  case ATTR_TYPE_STR:
407  string = va_arg(ap, VSTRING *);
408  if ((ch = attr_scan0_string(fp, string,
409  "input attribute value")) < 0)
410  return (-1);
411  break;
412  case ATTR_TYPE_DATA:
413  string = va_arg(ap, VSTRING *);
414  if ((ch = attr_scan0_data(fp, string,
415  "input attribute value")) < 0)
416  return (-1);
417  break;
418  case ATTR_TYPE_FUNC:
419  scan_fn = va_arg(ap, ATTR_SCAN_SLAVE_FN);
420  scan_arg = va_arg(ap, void *);
421  if (scan_fn(attr_scan0, fp, flags | ATTR_FLAG_MORE, scan_arg) < 0)
422  return (-1);
423  break;
424  case ATTR_TYPE_HASH:
425  case ATTR_TYPE_CLOSE:
426  if ((ch = attr_scan0_string(fp, str_buf,
427  "input attribute value")) < 0)
428  return (-1);
429  if (htable_locate(hash_table, STR(name_buf)) != 0) {
430  if ((flags & ATTR_FLAG_EXTRA) != 0) {
431  msg_warn("duplicate attribute %s in input from %s",
432  STR(name_buf), VSTREAM_PATH(fp));
433  return (conversions);
434  }
435  } else if (hash_table->used >= ATTR_HASH_LIMIT) {
436  msg_warn("attribute count exceeds limit %d in input from %s",
438  return (conversions);
439  } else {
440  htable_enter(hash_table, STR(name_buf),
441  mystrdup(STR(str_buf)));
442  }
443  break;
444  case -1:
445  conversions -= 1;
446  break;
447  default:
448  msg_panic("%s: unknown type code: %d", myname, wanted_type);
449  }
450  }
451 }
452 
453 /* attr_scan0 - read attribute list from stream */
454 
455 int attr_scan0(VSTREAM *fp, int flags,...)
456 {
457  va_list ap;
458  int ret;
459 
460  va_start(ap, flags);
461  ret = attr_vscan0(fp, flags, ap);
462  va_end(ap);
463  return (ret);
464 }
465 
466 /* attr_scan_more0 - look ahead for more */
467 
469 {
470  int ch;
471 
472  switch (ch = VSTREAM_GETC(fp)) {
473  case 0:
474  if (msg_verbose)
475  msg_info("%s: terminator (consumed)", VSTREAM_PATH(fp));
476  return (0);
477  case VSTREAM_EOF:
478  if (msg_verbose)
479  msg_info("%s: EOF", VSTREAM_PATH(fp));
480  return (-1);
481  default:
482  if (msg_verbose)
483  msg_info("%s: non-terminator '%c' (lookahead)",
484  VSTREAM_PATH(fp), ch);
485  (void) vstream_ungetc(fp, ch);
486  return (1);
487  }
488 }
489 
490 #ifdef TEST
491 
492  /*
493  * Proof of concept test program. Mirror image of the attr_scan0 test
494  * program.
495  */
496 #include <msg_vstream.h>
497 
498 int var_line_limit = 2048;
499 
500 int main(int unused_argc, char **used_argv)
501 {
502  VSTRING *data_val = vstring_alloc(1);
503  VSTRING *str_val = vstring_alloc(1);
504  HTABLE *table = htable_create(1);
505  HTABLE_INFO **ht_info_list;
506  HTABLE_INFO **ht;
507  int int_val;
508  long long_val;
509  long long_val2;
510  int ret;
511 
512  msg_verbose = 1;
513  msg_vstream_init(used_argv[0], VSTREAM_ERR);
514  if ((ret = attr_scan0(VSTREAM_IN,
516  RECV_ATTR_INT(ATTR_NAME_INT, &int_val),
517  RECV_ATTR_LONG(ATTR_NAME_LONG, &long_val),
518  RECV_ATTR_STR(ATTR_NAME_STR, str_val),
519  RECV_ATTR_DATA(ATTR_NAME_DATA, data_val),
520  RECV_ATTR_HASH(table),
521  RECV_ATTR_LONG(ATTR_NAME_LONG, &long_val2),
522  ATTR_TYPE_END)) > 4) {
523  vstream_printf("%s %d\n", ATTR_NAME_INT, int_val);
524  vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
525  vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
526  vstream_printf("%s %s\n", ATTR_NAME_DATA, STR(str_val));
527  ht_info_list = htable_list(table);
528  for (ht = ht_info_list; *ht; ht++)
529  vstream_printf("(hash) %s %s\n", ht[0]->key, (char *) ht[0]->value);
530  myfree((void *) ht_info_list);
531  vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val2);
532  } else {
533  vstream_printf("return: %d\n", ret);
534  }
535  if ((ret = attr_scan0(VSTREAM_IN,
537  RECV_ATTR_INT(ATTR_NAME_INT, &int_val),
538  RECV_ATTR_LONG(ATTR_NAME_LONG, &long_val),
539  RECV_ATTR_STR(ATTR_NAME_STR, str_val),
540  RECV_ATTR_DATA(ATTR_NAME_DATA, data_val),
541  ATTR_TYPE_END)) == 4) {
542  vstream_printf("%s %d\n", ATTR_NAME_INT, int_val);
543  vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
544  vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
545  vstream_printf("%s %s\n", ATTR_NAME_DATA, STR(data_val));
546  ht_info_list = htable_list(table);
547  for (ht = ht_info_list; *ht; ht++)
548  vstream_printf("(hash) %s %s\n", ht[0]->key, (char *) ht[0]->value);
549  myfree((void *) ht_info_list);
550  } else {
551  vstream_printf("return: %d\n", ret);
552  }
553  if (vstream_fflush(VSTREAM_OUT) != 0)
554  msg_fatal("write error: %m");
555 
556  vstring_free(data_val);
557  vstring_free(str_val);
558  htable_free(table, myfree);
559 
560  return (0);
561 }
562 
563 #endif
int msg_verbose
Definition: msg.c:177
void htable_free(HTABLE *table, void(*free_fn)(void *))
Definition: htable.c:287
#define VSTREAM_EOF
Definition: vstream.h:110
void myfree(void *ptr)
Definition: mymalloc.c:207
HTABLE_INFO * htable_locate(HTABLE *table, const char *key)
Definition: htable.c:242
#define ATTR_FLAG_EXTRA
Definition: attr.h:100
char * mystrdup(const char *str)
Definition: mymalloc.c:225
int vstring_get_null(VSTRING *vp, VSTREAM *fp)
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
#define VSTREAM_OUT
Definition: vstream.h:67
#define ATTR_FLAG_MISSING
Definition: attr.h:99
ssize_t used
Definition: htable.h:27
int main(int argc, char **argv)
Definition: anvil.c:1010
#define VSTREAM_GETC(vp)
Definition: vstream.h:108
#define ATTR_TYPE_FUNC
Definition: attr.h:47
#define ATTR_TYPE_CLOSE
Definition: attr.h:53
#define RECV_ATTR_INT(name, val)
Definition: attr.h:71
#define ATTR_TYPE_END
Definition: attr.h:39
#define VSTREAM_PATH(vp)
Definition: vstream.h:126
#define RECV_ATTR_HASH(val)
Definition: attr.h:73
#define VSTREAM_IN
Definition: vstream.h:66
#define LEN(x)
Definition: attr_scan0.c:183
Definition: htable.h:25
HTABLE * htable_create(ssize_t size)
Definition: htable.c:179
HTABLE_INFO ** htable_list(HTABLE *table)
Definition: htable.c:330
#define ATTR_TYPE_INT
Definition: attr.h:40
#define ATTR_TYPE_HASH
Definition: attr.h:43
#define vstream_ungetc(vp, ch)
Definition: vstream.h:109
#define vstream_ftimeout(vp)
Definition: vstream.h:124
#define RECV_ATTR_LONG(name, val)
Definition: attr.h:75
VSTREAM * vstream_printf(const char *fmt,...)
Definition: vstream.c:1335
void msg_warn(const char *fmt,...)
Definition: msg.c:215
#define RECV_ATTR_DATA(name, val)
Definition: attr.h:76
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
#define ATTR_TYPE_STR
Definition: attr.h:42
int var_line_limit
Definition: mail_params.c:263
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
VSTRING * base64_decode(VSTRING *, const char *, ssize_t)
Definition: base64_code.c:135
int vstream_fflush(VSTREAM *stream)
Definition: vstream.c:1257
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
int attr_vscan0(VSTREAM *fp, int flags, va_list ap)
Definition: attr_scan0.c:266
int attr_scan0(VSTREAM *fp, int flags,...)
Definition: attr_scan0.c:455
#define STR(x)
Definition: attr_scan0.c:182
void msg_vstream_init(const char *name, VSTREAM *vp)
Definition: msg_vstream.c:77
#define ATTR_TYPE_LONG
Definition: attr.h:45
int attr_scan_more0(VSTREAM *fp)
Definition: attr_scan0.c:468
#define ATTR_TYPE_DATA
Definition: attr.h:46
int(* ATTR_SCAN_SLAVE_FN)(ATTR_SCAN_MASTER_FN, VSTREAM *, int, void *)
Definition: attr.h:32
#define ATTR_NAME_OPEN
Definition: attr.h:54
#define ATTR_FLAG_MORE
Definition: attr.h:101
#define ATTR_NAME_CLOSE
Definition: attr.h:55
#define VSTREAM_ERR
Definition: vstream.h:68
#define ATTR_FLAG_ALL
Definition: attr.h:104
#define RECV_ATTR_STR(name, val)
Definition: attr.h:72
#define ATTR_FLAG_STRICT
Definition: attr.h:103
HTABLE_INFO * htable_enter(HTABLE *table, const char *key, void *value)
Definition: htable.c:212
void msg_info(const char *fmt,...)
Definition: msg.c:199
#define ATTR_HASH_LIMIT
Definition: attr.h:57