#include <sys_defs.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include <msg.h>
#include <mymalloc.h>
#include <vstream.h>
#include <vstring.h>
#include <htable.h>
#include <attr.h>
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
static int attr_scan_plain_string(VSTREAM *fp, VSTRING *plain_buf,
int terminator, const char *context)
{
#if 0
extern int var_line_limit;
int limit = var_line_limit * 4;
#endif
int ch;
VSTRING_RESET(plain_buf);
while ((ch = VSTREAM_GETC(fp)) != '\n'
&& (terminator == 0 || ch != terminator)) {
if (ch == VSTREAM_EOF) {
msg_warn("%s on %s while reading %s",
vstream_ftimeout(fp) ? "timeout" : "premature end-of-input",
VSTREAM_PATH(fp), context);
return (-1);
}
VSTRING_ADDCH(plain_buf, ch);
#if 0
if (LEN(plain_buf) > limit) {
msg_warn("string length > %d characters from %s while reading %s",
limit, VSTREAM_PATH(fp), context);
return (-1);
}
#endif
}
VSTRING_TERMINATE(plain_buf);
if (msg_verbose)
msg_info("%s: %s", context, *STR(plain_buf) ? STR(plain_buf) : "(end)");
return (ch);
}
static int attr_scan_plain_number(VSTREAM *fp, unsigned *ptr, VSTRING *str_buf,
int terminator, const char *context)
{
char junk = 0;
int ch;
if ((ch = attr_scan_plain_string(fp, str_buf, terminator, context)) < 0)
return (-1);
if (sscanf(STR(str_buf), "%u%c", ptr, &junk) != 1 || junk != 0) {
msg_warn("malformed numerical data from %s while reading %s: %.100s",
VSTREAM_PATH(fp), context, STR(str_buf));
return (-1);
}
return (ch);
}
static int attr_scan_plain_long_number(VSTREAM *fp, unsigned long *ptr,
VSTRING *str_buf,
int terminator,
const char *context)
{
char junk = 0;
int ch;
if ((ch = attr_scan_plain_string(fp, str_buf, terminator, context)) < 0)
return (-1);
if (sscanf(STR(str_buf), "%lu%c", ptr, &junk) != 1 || junk != 0) {
msg_warn("malformed numerical data from %s while reading %s: %.100s",
VSTREAM_PATH(fp), context, STR(str_buf));
return (-1);
}
return (ch);
}
int attr_vscan_plain(VSTREAM *fp, int flags, va_list ap)
{
const char *myname = "attr_scan_plain";
static VSTRING *str_buf = 0;
static VSTRING *name_buf = 0;
int wanted_type = -1;
char *wanted_name;
unsigned int *number;
unsigned long *long_number;
VSTRING *string;
HTABLE *hash_table;
int ch;
int conversions;
if (flags & ~ATTR_FLAG_ALL)
msg_panic("%s: bad flags: 0x%x", myname, flags);
if (str_buf == 0) {
str_buf = vstring_alloc(10);
name_buf = vstring_alloc(10);
}
for (conversions = 0; ; conversions++) {
if (wanted_type != ATTR_TYPE_HASH) {
wanted_type = va_arg(ap, int);
if (wanted_type == ATTR_TYPE_END) {
if ((flags & ATTR_FLAG_MORE) != 0)
return (conversions);
wanted_name = "(list terminator)";
} else if (wanted_type == ATTR_TYPE_HASH) {
wanted_name = "(any attribute name or list terminator)";
hash_table = va_arg(ap, HTABLE *);
if (va_arg(ap, int) !=ATTR_TYPE_END)
msg_panic("%s: ATTR_TYPE_HASH not followed by ATTR_TYPE_END",
myname);
} else {
wanted_name = va_arg(ap, char *);
}
}
for (;;) {
if (msg_verbose)
msg_info("%s: wanted attribute: %s",
VSTREAM_PATH(fp), wanted_name);
if ((ch = attr_scan_plain_string(fp, name_buf, '=',
"input attribute name")) == VSTREAM_EOF)
return (-1);
if (ch == '\n' && LEN(name_buf) == 0) {
if (wanted_type == ATTR_TYPE_END
|| wanted_type == ATTR_TYPE_HASH)
return (conversions);
if ((flags & ATTR_FLAG_MISSING) != 0)
msg_warn("missing attribute %s in input from %s",
wanted_name, VSTREAM_PATH(fp));
return (conversions);
}
if (wanted_type == ATTR_TYPE_HASH
|| (wanted_type != ATTR_TYPE_END
&& strcmp(wanted_name, STR(name_buf)) == 0))
break;
if ((flags & ATTR_FLAG_EXTRA) != 0) {
msg_warn("unexpected attribute %s in input from %s",
STR(name_buf), VSTREAM_PATH(fp));
return (conversions);
}
while (ch != '\n' && (ch = VSTREAM_GETC(fp)) != VSTREAM_EOF)
;
}
switch (wanted_type) {
case ATTR_TYPE_NUM:
if (ch != '=') {
msg_warn("missing value for number attribute %s from %s",
STR(name_buf), VSTREAM_PATH(fp));
return (-1);
}
number = va_arg(ap, unsigned int *);
if ((ch = attr_scan_plain_number(fp, number, str_buf, 0,
"input attribute value")) < 0)
return (-1);
break;
case ATTR_TYPE_LONG:
if (ch != '=') {
msg_warn("missing value for number attribute %s from %s",
STR(name_buf), VSTREAM_PATH(fp));
return (-1);
}
long_number = va_arg(ap, unsigned long *);
if ((ch = attr_scan_plain_long_number(fp, long_number, str_buf,
0, "input attribute value")) < 0)
return (-1);
break;
case ATTR_TYPE_STR:
if (ch != '=') {
msg_warn("missing value for string attribute %s from %s",
STR(name_buf), VSTREAM_PATH(fp));
return (-1);
}
string = va_arg(ap, VSTRING *);
if ((ch = attr_scan_plain_string(fp, string, 0,
"input attribute value")) < 0)
return (-1);
break;
case ATTR_TYPE_HASH:
if (ch != '=') {
msg_warn("missing value for string attribute %s from %s",
STR(name_buf), VSTREAM_PATH(fp));
return (-1);
}
if ((ch = attr_scan_plain_string(fp, str_buf, 0,
"input attribute value")) < 0)
return (-1);
if (htable_locate(hash_table, STR(name_buf)) != 0) {
if ((flags & ATTR_FLAG_EXTRA) != 0) {
msg_warn("duplicate attribute %s in input from %s",
STR(name_buf), VSTREAM_PATH(fp));
return (conversions);
}
} else if (hash_table->used >= ATTR_HASH_LIMIT) {
msg_warn("attribute count exceeds limit %d in input from %s",
ATTR_HASH_LIMIT, VSTREAM_PATH(fp));
return (conversions);
} else {
htable_enter(hash_table, STR(name_buf),
mystrdup(STR(str_buf)));
}
break;
default:
msg_panic("%s: unknown type code: %d", myname, wanted_type);
}
}
}
int attr_scan_plain(VSTREAM *fp, int flags,...)
{
va_list ap;
int ret;
va_start(ap, flags);
ret = attr_vscan_plain(fp, flags, ap);
va_end(ap);
return (ret);
}
#ifdef TEST
#include <msg_vstream.h>
int var_line_limit = 2048;
int main(int unused_argc, char **used_argv)
{
VSTRING *str_val = vstring_alloc(1);
HTABLE *table = htable_create(1);
HTABLE_INFO **ht_info_list;
HTABLE_INFO **ht;
int int_val;
long long_val;
int ret;
msg_verbose = 1;
msg_vstream_init(used_argv[0], VSTREAM_ERR);
if ((ret = attr_scan_plain(VSTREAM_IN,
ATTR_FLAG_STRICT,
ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val,
ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val,
ATTR_TYPE_STR, ATTR_NAME_STR, str_val,
ATTR_TYPE_HASH, table,
ATTR_TYPE_END)) > 3) {
vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val);
vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
ht_info_list = htable_list(table);
for (ht = ht_info_list; *ht; ht++)
vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value);
myfree((char *) ht_info_list);
} else {
vstream_printf("return: %d\n", ret);
}
if ((ret = attr_scan_plain(VSTREAM_IN,
ATTR_FLAG_STRICT,
ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val,
ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val,
ATTR_TYPE_STR, ATTR_NAME_STR, str_val,
ATTR_TYPE_END)) == 3) {
vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val);
vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
ht_info_list = htable_list(table);
for (ht = ht_info_list; *ht; ht++)
vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value);
myfree((char *) ht_info_list);
} else {
vstream_printf("return: %d\n", ret);
}
if (vstream_fflush(VSTREAM_OUT) != 0)
msg_fatal("write error: %m");
vstring_free(str_val);
htable_free(table, myfree);
return (0);
}
#endif