/*
 * Policy statement parser for QI meta-API.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <syslog.h>
#include <errno.h>
#include "qick.h"

#define max(a, b)	((a) > (b) ? (a) : (b))
#define span(a, b)	((long)(b) - (long)(a))

static char rcsid[] = "$Id: parse.c,v 1.1 2000/08/18 19:30:40 dgc Exp $";

#define BUFMAX	512

#define VY_AND		Vocabulary[0]
#define VY_OR		Vocabulary[1]
#define VY_XOR		Vocabulary[2]
#define VY_NOT		Vocabulary[3]
#define VY_LINK		Vocabulary[4]
#define VY_DEFAULT	Vocabulary[5]
#define VY_EQ		Vocabulary[6]
#define VY_MATCH	Vocabulary[7]
#define VY_LT		Vocabulary[8]
#define VY_LE		Vocabulary[9]
#define VY_GT		Vocabulary[10]
#define VY_GE		Vocabulary[11]

char *Vocabulary[] = {
	"and",
	"or",
	"xor",
	"not",
	"link",
	"default",
	"=",
	"~",
	"<",
	"<=",
	">",
	">=",
	NULL
};

qip_t *
qip_parse_simple(qick_t *qick, char *name, int wc, char *wv[])
{
	qip_t	*new;
	qit_t	*qt;

	char	*field;
	int	 i, op, inv, servers;

	/* make up a policy */
	new = qip_new(qick, name);

	/* Parse incrementally. */
	op = -1;
	field = NULL;
	inv = 0;

	for (i = 0; i < wc; ++i) {

		if (!strcasecmp(wv[i], VY_OR)) {
			op = QI_POLICY_OR;
			continue;

		} else if (!strcasecmp(wv[i], VY_AND)) {
			op = QI_POLICY_AND;
			continue;

		} else if (!strcasecmp(wv[i], VY_XOR)) {
			op = QI_POLICY_XOR;
			continue;

		} else if (!strcasecmp(wv[i], VY_NOT)) {
			inv = !inv;
			continue;

		} else if (!strcasecmp(wv[i], VY_LINK)) {
			qt = qip_extend(new, NULL);
			qit_setlink(qt, qip_find(qick, wv[++i]));
			continue;

		} else if (!strcasecmp(wv[i], VY_DEFAULT)) {
			qt = qip_extend(new, NULL);
			qit_setlink(qt, qip_default(qick));
			continue;

		} else if (field == NULL) {
			field = wv[i];
			continue;

		} else {
			qt = qip_extend(new, NULL);	/* extend */
			qit_setmatch(qt, field, wv[i],
				QI_POLICY_RE_BASIC | REG_ICASE	/* ign case */,
				NULL /* don't store regex errors */, 0);
			field = NULL;
		}

		if (inv) {
			qt = qip_extend(new, NULL);	/* extend again... */
			qit_setop(qt, QI_POLICY_NOT);	/* and not previous */
			inv = 0;
		}

		if (op > -1) {
			qt = qip_extend(new, NULL);	/* extend again... */
			qit_setop(qt, op);		/* and op them */
			op = -1;
		}
	}
	/* fin */

	return new;
}

qip_t *
qip_parse(qick_t *qick, char *name, int wc, char *wv[])
{
	qip_t	*new;
	qit_t	*qt;
	char	*field, *p, *q, cmp[BUFMAX + 1];
	int	 i, start, len, flags;

	/* make up a policy */
	new = qip_new(qick, name);

	/* Parse incrementally. */
	field = NULL;

	for (i = 0; i < wc; ++i) {

		if (!strcasecmp(wv[i], VY_OR)) {
			qt = qip_extend(new, NULL);
			qit_setop(qt, QI_POLICY_OR);
			continue;

		} else if (!strcasecmp(wv[i], VY_AND)) {
			qt = qip_extend(new, NULL);
			qit_setop(qt, QI_POLICY_AND);
			continue;

		} else if (!strcasecmp(wv[i], VY_XOR)) {
			qt = qip_extend(new, NULL);
			qit_setop(qt, QI_POLICY_XOR);
			continue;

		} else if (!strcasecmp(wv[i], VY_NOT)) {
			qt = qip_extend(new, NULL);
			qit_setop(qt, QI_POLICY_NOT);
			continue;

		} else if (!strcasecmp(wv[i], VY_LINK)) {
			qt = qip_extend(new, NULL);
			qit_setlink(qt, qip_find(qick, wv[++i]));
			continue;

		} else if (!strcasecmp(wv[i], VY_DEFAULT)) {
			qt = qip_extend(new, NULL);
			qit_setlink(qt, qip_default(qick));
			continue;

		} else if (field == NULL) {
			field = wv[i];
			continue;

		} else {
			++i;
			strncpy(cmp, "\"", BUFMAX-2);
			flags = QI_POLICY_RE_BASIC;

			if (!strcasecmp(wv[i-1], VY_EQ) ||
			    !strcasecmp(wv[i-1], VY_MATCH)) {
				if (strchr("\"'", *wv[i]) == NULL) {
					start = 0;
					len = strlen(wv[i]);
				} else {
					start = 1;
					len = strlen(wv[i]) - 1;
				}
			}

			if (!strcasecmp(wv[i-1], VY_MATCH)) {
				if (len && wv[i][start] == '/') {
					++start;
					len = 0;
					while (wv[i][start+len] != '/')
						++len;
					if (wv[i][start+len+1] == 'i')
						flags |= QI_POLICY_RE_ICASE;
				}
			}

			strncpy(cmp, &wv[i][1], len);

			qt = qip_extend(new, NULL);
			qit_setmatch(qt, field, cmp, flags,
				NULL /* don't store regex errors */, 0);
			field = NULL;
		}

	}
	/* fin */

	return new;
}

int
qip_split(char ***wvp, char *sp)
{
	int	 wc, i;
	char	*p, *q, *s;
	char	**wv;
	int	 squoted, dquoted;
	int	 escaped;

	/* Estimate number of words. */
	i = 0;
	for (p = sp; *p != '\0'; ++p) {
		if (isspace(*p))
			++i;
	}
	++i;	/* last word */

	/* Allocate space for those words. */
	s = strdup(sp);
	wv = malloc(sizeof(char *) * (i+1));
	for (wc = 0; wc < i; ++wc)
		wv[wc] = NULL;

	/* Loop the incoming string, blocking off words. */
	/* We clean up internal syntax later. */
	p = s;
	squoted = dquoted = 0;
	wv[wc = 0] = p;
	while (*p != '\0') {
		if (*p == '\\') {
			if (*++p == '\0')
				break;
		} else if (*p == '"') {
			dquoted = !dquoted;
		} else if (*p == '\'') {
			squoted = !squoted;
		} else if (squoted || dquoted) {
			++p;
			continue;
		} else if (isspace(*p)) {
			*p = '\0';
			if (*++p == '\0')
				break;
			while (isspace(*p))
				++p;
			wv[++wc] = p;
		}
		++p;
	}
	wv[++wc] = NULL;

	for (i = 0; i < wc; ++i) {
		p = q = wv[i];
		while (*q != '\0') {
			if (*q == '\\') {
				*p = *++q;
				if (*q == '\0')
					break;
			} else {
				*p = *q;
			}
			++p;
			++q;
		}
		*p = '\0';
		wv[i] = strdup(wv[i]);
	}

	free(s);
	for (i = 0; i < wc; ++i) {
		qi_debug("= %02d: %s\n", i, wv[i]);
	}

	*wvp = wv;
	return wc;
}

