/* vi: set sw=4 ts=4: */
/*
 * Copyright (C) 2007 by Rupert Li <rupert@amit.com.tw>
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>

typedef struct {
	int op;
    int fac_op;       //0(none), '='(equal), '!'(not equal)
    int fac_val;
    int sev_op;       //0(none), '&'(and)
    int sev_val;
    int tag_op;       //0(none), '='(equal), '!'(not equal)
    int tag_len;
    char tag_val[16];
} RLOG_FILTER;

#define RLOG_FILTER_0       0 //for the subscriber
#define RLOG_FILTER_C       1 //to console (stderr)
#define RLOG_FILTER_N       2 //to network (syslog)

#define NUMBER_OF_RLOG_FILTERS 3

static RLOG_FILTER log_filter[NUMBER_OF_RLOG_FILTERS];

#define FILTER_FAIL         0
#define FILTER_PASS         1
#define FILTER_BY_SETTING   2

static void init_filter(void)
{
	/* by default, all filters are returning fail */
	memset(log_filter, 0, sizeof(log_filter));
}

/* 'cmd' is something like 'sc F=10,S&0x7E,T="Test"'
 */
static int set_filter_cmd(char *cmd)
{
	RLOG_FILTER *filter=0;
	int	filter_idx;
	int err;
	char *c;

	/* decide which filter to be set */

	switch (cmd[1])
	{
	case '0':
		filter_idx = RLOG_FILTER_0;
		break;
	case 'c':
		filter_idx = RLOG_FILTER_C;
 		break;
	case 'n':
		filter_idx = RLOG_FILTER_N;
 		break;
	default:
		err = -1;
		goto error_exit;
	}

	/* parse the filter string */

	filter = &(log_filter[filter_idx]);
    memset(filter, 0, sizeof(RLOG_FILTER));
	c = cmd + 3; // F=0,S&7F,T="..."

	if (*c == '+')
	{
		filter->op = FILTER_PASS;
		//fprintf(stderr, "all pass\n");
		return 0;
	}

	if (*c == '-')
	{
		filter->op = FILTER_FAIL;
		//fprintf(stderr, "all fail\n");
		return 0;
	}

	do
	{
		err = -1;

		//F=XX,F!=XX,S=XXX,T="XXX"
		switch (*c)
		{
		case 'F':
			if (c[1] == '=')
			{
				filter->fac_op = '=';
				c += 2;
			}
			else if ((c[1] == '!') && (c[2] == '='))
			{
				filter->fac_op = '!';
				c += 3;
			}
			
			if (filter->fac_op)
			{
				filter->fac_val = strtoul(c, &c, 0);
				if (*c == ',') c++;
				err = 0;
				//fprintf(stderr, "op=%d val=%d\n", filter->fac_op, filter->fac_val);
			}
			break;

		case 'S':
			if (c[1] == '&')
			{
				filter->sev_op = '&';
				c += 2;
			}

			if (filter->sev_op)
			{
				filter->sev_val = strtoul(c, &c, 16);
				if (*c == ',') c++;
				err = 0;
				//fprintf(stderr, "op=%d val=%d\n", filter->sev_op, filter->sev_val);
			}
			break;

		case 'T':
			if (c[1] == '=')
			{
				filter->tag_op = '=';
				c += 2;
			}
			else if ((c[1] == '!') && (c[2] == '='))
			{
				filter->tag_op = '!';
				c += 3;
			}

			if (filter->tag_op)
			{
				if (*c == '"')
				{
					char *d = strchr(++c, '"');
					if (d) 
					{
						*d = 0;
						strcpy(filter->tag_val, c);
						filter->tag_len = strlen(filter->tag_val);
						c = d + 1;
						if (*c == ',') c++;
						err = 0;
						//fprintf(stderr, "op=%d val=%s\n", filter->tag_op, filter->tag_val);
					}
				}
			}
			break;
		}
	}
	while (*c && (err == 0));

error_exit:

	if (err == 0)
	{
		filter->op = FILTER_BY_SETTING;
	}
	else
	{
		fprintf(stderr, "RingLog: bad filter\n");
	}

	return 0;
}

static int test_filter(int filter_id, int pri, const char *msg)
{
	RLOG_FILTER *filter = &(log_filter[filter_id]);

	if (filter->op != FILTER_BY_SETTING)
	{
		return filter->op;
	}

	if (filter->fac_op)
	{
		switch (filter->fac_op)
		{
		case '=':
			if (filter->fac_val != LOG_FAC(pri)) return FILTER_FAIL;
			break;
		case '!':
			if (filter->fac_val == LOG_FAC(pri)) return FILTER_FAIL;
			break;
		}
	}
	
	if (filter->sev_op)
	{
		switch (filter->sev_op)
		{
		case '&':
			if (!(filter->sev_val & (1 << LOG_PRI(pri)))) return FILTER_FAIL;
			break;
		}
	}

	if (filter->tag_op)
	{
		switch (filter->tag_op)
		{
		case '=':
			if (strncmp(filter->tag_val, msg, filter->tag_len) != 0) return FILTER_FAIL;
			break;
		case '!':
			if (strncmp(filter->tag_val, msg, filter->tag_len) == 0) return FILTER_FAIL;
			break;
		}
	}

	return FILTER_PASS;
}

