/*
 * dml - Dialog Markup Language
 *
 * $Id: parser.c,v 1.3 2002/07/29 13:07:05 malekith Exp $
 * Author: Michal Moskal <malekith@pld-linux.org>
 * include COPYING-GNU
 */

#include "pi.h"
#include "ml.h"
#include <string.h>
#include <ctype.h>

struct pi_amp pi_amps[] = {
	{ "amp", '&' },
	{ "lt", '<' },
	{ "gt", '>' },
	{ "quote", '"' },
	{ "dq", '"' },
	{ "bt", '`' },
	{ "sq", '\'' },
	{ "bs", '\\' },
	{ "ob", '{' },
	{ "cb", '}' },
	{ 0, 0 }
};

struct pi_tag pi_tags[] = {
	{ pi_tag_b, "b", PI_TAG_END|PI_TAG_TEXTAREA },
	{ pi_tag_i, "i", PI_TAG_END|PI_TAG_TEXTAREA },
	{ pi_tag_br, "br", PI_TAG_TEXTAREA|PI_TAG_NORMAL },
	{ pi_tag_text, "text", PI_TAG_END|PI_TAG_ARGS|PI_TAG_NORMAL },
	{ pi_tag_input, "input", PI_TAG_ARGS|PI_TAG_NORMAL },
	{ pi_tag_button, "button", PI_TAG_ARGS|PI_TAG_NORMAL },
	{ pi_tag_check, "check", PI_TAG_ARGS|PI_TAG_NORMAL },
	{ pi_tag_meta, "meta", PI_TAG_ARGS|PI_TAG_NORMAL },
	{ pi_tag_menu, "menu", PI_TAG_END|PI_TAG_ARGS|PI_TAG_NORMAL },
	{ pi_tag_item, "item", PI_TAG_ARGS|PI_TAG_MENU },
	{ 0, 0, 0 }
};

static const char *parse_amp(struct dbuf *db, const char *p)
{
	int i;
	const char *a;
	
	p++;
	for (i = 0; ((a = pi_amps[i].name)); i++)
		if (strncasecmp(a, p, strlen(a)) == 0)
			break;
	if (a) {
		p += strlen(a);
		if (*p == ';')
			p++;
		db_addc(db, pi_amps[i].c);
	} else {
		db_addc(db, '&');
	}

	return p;
}

static const char *parse_tag(struct dbuf *db, const char *p, int mask)
{
	int end, i, n;
	const char *a;
	
	if ((end = (*++p == '/')))
		p++;
		
	for (i = 0; ((a = pi_tags[i].name)); i++)
		if (strncasecmp(a, p, (n = strlen(a))) == 0 &&
		    (isspace(p[n]) || p[n] == '>'))
			break;
			
	if (a == 0 || 
	    (end && (pi_tags[i].flags & PI_TAG_END) == 0) ||
	    ((pi_tags[i].flags & mask) == 0)) {
		db_addc(db, '<');
		if (end)
			db_addc(db, '/');
		return p;
	}

	p += n;
	db_addc(db, pi_tag_start);
	db_addc(db, pi_tags[i].id + end);

	while (*p && *p != '>') {
		while (isspace(*p))
			p++;
		if (*p == '>')
			break;
		db_addc(db, pi_tag_arg);
		while (!strchr("=> \t\n", *p))
			if ((unsigned char)*p <= 3)
				p++;
			else
				db_addc(db, *p++);
		db_addc(db, pi_tag_arg);
		if (*p == '=') {
			p++;
			end = 0;
			if (*p == '"' || *p == '\'')
				end = *p++;
			while (*p && (end ? *p != end : 
						!strchr(" \t\n>", *p)))
				if ((unsigned char)*p <= 3)
					p++;
				else
					db_addc(db, *p++);
			if (end && *p == end)
				p++;
		}
	}

	db_addc(db, pi_tag_end);

	if (*p)
		p++;
	return p;
}

/*
 * preparse given ml program
 */
char *pi_parse_ml(const char *input, int mask)
{
	struct dbuf *db;
	int after_tag = 1;

	db = db_new();

	while (*input)
		switch (*input) {
		case '\n':
			if (!after_tag)
				db_addc(db, ' ');
		case '\t':
		case pi_tag_start:
		case pi_tag_end:
		case pi_tag_arg:
			input++;
			break;
		case '&':
			after_tag = 0;
			input = parse_amp(db, input);
			break;
		case '<':
			after_tag = 1;
			input = parse_tag(db, input, mask);
			break;
		default:
			after_tag = 0;
			db_addc(db, *input++);
			break;
		}
	db_addc(db, pi_tag_eos);

	return db_finish(db, 0);
}
