/*
    c99tree - add or interpret backslash escaping
    Copyright (C) 2010  Tibor Palinkas

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include <stdlib.h>
#include <string.h>
#include "escaping.h"

char *escape(const char *input)
{
	char *out, *o;
	const char *i;

	if (input == NULL)
		return strdup("");

	out = malloc(strlen(input) * 2 + 1);
	for(i = input, o = out; *i != '\0'; i++,o++) {
		switch(*i) {
			case '"':  *o = '\\'; o++; *o = '"'; break;
			case '\n': *o = '\\'; o++; *o = 'n'; break;
			case '\\': *o = '\\'; o++; *o = '\\'; break;
			default:   *o = *i;
		}
	}
	*o = '\0';
	return out;
}

char *escape_xml(const char *input)
{
	char *out, *o;
	const char *i;

	if (input == NULL)
		return strdup("");

	out = malloc(strlen(input) * 6 + 1);
	for(i = input, o = out; *i != '\0'; i++,o++) {
		if (*i < 10) {
			*o = '&'; o++;
			*o = '#'; o++;
			*o = *i + '0'; o++;
			*o = ';';
		}
		else if (*i < 32) {
			*o = '&'; o++;
			*o = '#'; o++;
			*o = (*i) / 10 + '0'; o++;
			*o = (*i) % 10 + '0'; o++;
			*o = ';';
		}
		else {
			switch(*i) {
				case '&':  strcpy(o, "&amp;");   o += 4; break;
				case '"':  strcpy(o, "&quot;");  o += 5; break;
				case '<':  strcpy(o, "&lt;");    o += 3;  break;
				case '>':  strcpy(o, "&gt;");    o += 3;  break;
				default:   *o = *i;
			}
		}
	}
	*o = '\0';
	return out;
}


static void unescape_xml_(char *i, char *o)
{
	char *end;

	for(; *i != '\0'; i++) {
		switch(*i) {
			case '&':
				i++;
				end = strchr(i, ';');
				if (end != NULL) {
					if (*i == '#') {
						char *e;
						i++;
						*o = strtol(i, &e, 10);
						if (*e != '\0')
							*o = '?';
					}
					else if (strncmp(i, "amp;", 4) == 0) *o = '&';
					else if (strncmp(i, "quot;", 5) == 0) *o = '"';
					else if (strncmp(i, "lt;", 3) == 0) *o = '<';
					else if (strncmp(i, "gt;", 3) == 0) *o = '>';
					else *o = '?';
					i = end;
				}
				o++;
				break;
			default:
				*o = *i;
				o++;
		}
	}
	*o = '\0';
}

static void unescape_(char *i, char *o)
{
	for(; *i != '\0'; i++) {
		switch(*i) {
			case '\\':
				i++;
				switch(*i) {
					case 'n':   *o = '\n'; break;
					case 't':   *o = '\t'; break;
					default:    *o = *i; /* solves \\ as well */
				}
				o++;
				break;
			default:
				*o = *i;
				o++;
		}
	}
	*o = '\0';
}

#ifdef unused_yet
void unescape(char *str)
{
	unescape_(str, str);
}
#endif

char *unescape_dup(char *str)
{
	char *out;

	out = malloc(strlen(str)+1);
	unescape_(str, out);
	return out;
}

char *unescape_dup_xml(char *str)
{
	char *out;

	out = malloc(strlen(str)+1);
	unescape_xml_(str, out);
	return out;
}
