#include <sys_defs.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdarg.h>
#include <msg.h>
#include <mymalloc.h>
#include <vstring.h>
#include <vstream.h>
#include <vstring_vstream.h>
#include <stringops.h>
#include <argv.h>
#include <dict.h>
#include <match_list.h>
#define MATCH_DICTIONARY(pattern) \
((pattern)[0] != '[' && strchr((pattern), ':') != 0)
static ARGV *match_list_parse(MATCH_LIST *match_list, ARGV *pat_list,
char *string, int init_match)
{
const char *myname = "match_list_parse";
VSTRING *buf = vstring_alloc(10);
VSTREAM *fp;
const char *delim = CHARS_COMMA_SP;
char *bp = string;
char *start;
char *item;
char *map_type_name_flags;
int match;
#define OPEN_FLAGS O_RDONLY
#define DICT_FLAGS (DICT_FLAG_LOCK | DICT_FLAG_UTF8_REQUEST)
#define STR(x) vstring_str(x)
while ((start = mystrtokq(&bp, delim, CHARS_BRACE)) != 0) {
if (*start == '#') {
msg_warn("%s: comment at end of line is not supported: %s %s",
match_list->pname, start, bp);
break;
}
for (match = init_match, item = start; *item == '!'; item++)
match = !match;
if (*item == 0)
msg_fatal("%s: no pattern after '!'", match_list->pname);
if (*item == '/') {
if ((fp = vstream_fopen(item, O_RDONLY, 0)) == 0) {
vstring_sprintf(buf, "%s:%s", DICT_TYPE_NOFILE, item);
if (dict_handle(STR(buf)) == 0)
dict_register(STR(buf),
dict_surrogate(DICT_TYPE_NOFILE, item,
OPEN_FLAGS, DICT_FLAGS,
"open file %s: %m", item));
argv_add(pat_list, STR(buf), (char *) 0);
} else {
while (vstring_fgets(buf, fp))
if (vstring_str(buf)[0] != '#')
pat_list = match_list_parse(match_list, pat_list,
vstring_str(buf), match);
if (vstream_fclose(fp))
msg_fatal("%s: read file %s: %m", myname, item);
}
} else if (MATCH_DICTIONARY(item)) {
vstring_sprintf(buf, "%s%s(%o,%s)", match ? "" : "!",
item, OPEN_FLAGS, dict_flags_str(DICT_FLAGS));
map_type_name_flags = STR(buf) + (match == 0);
if (dict_handle(map_type_name_flags) == 0)
dict_register(map_type_name_flags,
dict_open(item, OPEN_FLAGS, DICT_FLAGS));
argv_add(pat_list, STR(buf), (char *) 0);
} else {
casefold(match_list->fold_buf, match ?
item : STR(vstring_sprintf(buf, "!%s", item)));
argv_add(pat_list, STR(match_list->fold_buf), (char *) 0);
}
}
vstring_free(buf);
return (pat_list);
}
MATCH_LIST *match_list_init(const char *pname, int flags,
const char *patterns, int match_count,...)
{
MATCH_LIST *list;
char *saved_patterns;
va_list ap;
int i;
if (flags & ~MATCH_FLAG_ALL)
msg_panic("match_list_init: bad flags 0x%x", flags);
list = (MATCH_LIST *) mymalloc(sizeof(*list));
list->pname = mystrdup(pname);
list->flags = flags;
list->match_count = match_count;
list->match_func =
(MATCH_LIST_FN *) mymalloc(match_count * sizeof(MATCH_LIST_FN));
list->match_args =
(const char **) mymalloc(match_count * sizeof(const char *));
va_start(ap, match_count);
for (i = 0; i < match_count; i++)
list->match_func[i] = va_arg(ap, MATCH_LIST_FN);
va_end(ap);
list->error = 0;
list->fold_buf = vstring_alloc(20);
#define DO_MATCH 1
saved_patterns = mystrdup(patterns);
list->patterns = match_list_parse(list, argv_alloc(1), saved_patterns,
DO_MATCH);
argv_terminate(list->patterns);
myfree(saved_patterns);
return (list);
}
int match_list_match(MATCH_LIST *list,...)
{
const char *myname = "match_list_match";
char **cpp;
char *pat;
int match;
int i;
va_list ap;
va_start(ap, list);
for (i = 0; i < list->match_count; i++)
list->match_args[i] = va_arg(ap, const char *);
va_end(ap);
list->error = 0;
for (cpp = list->patterns->argv; (pat = *cpp) != 0; cpp++) {
for (match = 1; *pat == '!'; pat++)
match = !match;
for (i = 0; i < list->match_count; i++) {
casefold(list->fold_buf, list->match_args[i]);
if (list->match_func[i] (list, STR(list->fold_buf), pat))
return (match);
else if (list->error != 0)
return (0);
}
}
if (msg_verbose)
for (i = 0; i < list->match_count; i++)
msg_info("%s: %s: no match", myname, list->match_args[i]);
return (0);
}
void match_list_free(MATCH_LIST *list)
{
myfree(list->pname);
argv_free(list->patterns);
myfree((void *) list->match_func);
myfree((void *) list->match_args);
vstring_free(list->fold_buf);
myfree((void *) list);
}