#include "mimemagic.h"
#include <qfile.h>
#include <qtstream.h>
#include <stdlib.h>
#include <iostream.h>
#include <fcntl.h>

#define HBYTE(x) ((char)(((x) >> 8) & 0x00FF))
#define LBYTE(x) ((char)((x) & 0x00FF))
#define BYTE1(x) ((char)(((x) >> 24) & 0x000000FF))
#define BYTE2(x) ((char)(((x) >> 16) & 0x000000FF))
#define BYTE3(x) ((char)(((x) >> 8) & 0x000000FF))
#define BYTE4(x) ((char)((x) & 0x000000FF))

void reformatString(QString& str)
{
	int	p = 0;
	int	len = 0;
	while ((p = str.find('\\',p)) >= 0) {
		if (str[p+1] == '\\') {			// case of "...\\..."
			p += 2;
		}
		else if (isdigit(str[p+1])) {			// case of "...\047..."
			char	c[2];
			c[0] = (char)strtol(str.data()+p+1,0,8); c[1] = (char)0;
			str.replace(p,4,c);
			p++;
		}
		else if (isprint(str[p+1])) {
			char	c[2];
			c[0] = str[p+1]; c[1] = (char)0;
			str.replace(p,2,c);
			p++;
		}
		else p++;
	}
}

MagicNumber::MagicNumber(const QString& line, MagicNumber *p)
{
	childs.setAutoDelete(TRUE);
	parent = p;
	if (p) layer = p->layer+1;
	else layer = 0;
	QString		tmp = line.simplifyWhiteSpace();
	tmp += " ";
	char	*c = tmp.data();
	while (*c == '>') c++;
	c = strtok(c," ");
	offset = strtol(c,0,10);
	c = strtok(0," ");
	QString		type(c);
	c = strtok(0," ");
	QString		str(c);
	while (str[str.length()-1] == '\\') {
		c = strtok(0," ");
		str.truncate(str.length()-1);
		str += " ";
		str += c;
	}
	c = strtok(0," ");
	text = c;
	if (strncmp(type.data(),"string",6) == 0) {
		reformatString(str);
		data = new char[str.length()];
		data2 = new char[str.length()];
		len = str.length();
		memcpy(data,str.data(),len);
	}
	else if (strncmp(type.data(),"byte",4) == 0) {
		data = new char[1];
		data2 = new char[1];
		len = 1;
		data[0] = (char)strtol(str.data(),0,0);
	}
	else if (strncmp(type.data(),"belong",6) == 0 || strncmp(type.data(),"long",4) == 0) {
		data = new char[4];
		data2 = new char[4];
		len = 4;
		long	p = strtol(str.data(),0,0);
		data[0] = BYTE1(p); data[1] = BYTE2(p); data[2] = BYTE3(p); data[3] = BYTE4(p);
	}
	else if (strncmp(type.data(),"lelong",6) == 0) {
		data = new char[4];
		data2 = new char[4];
		len = 4;
		long	p = strtol(str.data(),0,0);
		data[0] = BYTE4(p); data[1] = BYTE3(p); data[2] = BYTE2(p); data[3] = BYTE1(p);
	}
	else if (strncmp(type.data(),"beshort",6) == 0 || strncmp(type.data(),"short",4) == 0) {
		data = new char[2];
		data2 = new char[2];
		len = 2;
		short	p = (short)strtol(str.data(),0,0);
		data[0] = HBYTE(p); data[1] = LBYTE(p);
	}
	else if (strncmp(type.data(),"leshort",6) == 0) {
		data = new char[2];
		data2 = new char[2];
		len = 2;
		short	p = (short)strtol(str.data(),0,0);
		data[0] = LBYTE(p); data[1] = HBYTE(p);
	}
}

MagicNumber::~MagicNumber()
{
	delete [] data;
	delete [] data2;
}

//-------------------------------------------------------------------------------------

MimeMagic::MimeMagic(const char *magic)
{
	list.setAutoDelete(TRUE);
	parseMagicFile(magic);
}

void MimeMagic::setMagicFile(const char *magic)
{
	list.clear();
	parseMagicFile(magic);
}

bool MimeMagic::parseMagicFile(const char *magic)
{
	MagicNumber	*m1, *current;
	QFile		f(magic);
	if (!f.open(IO_ReadOnly | IO_Translate)) return FALSE;
	QTextStream	t(&f);
	QString		line;
	int		layer = 0, l = 0;
	while (!t.eof()) {
		l = 0;
		line = t.readLine();
		if (line[0] == '#' || line.isEmpty()) continue;
		while (line[l] == '>') l++;
		if (l == 0) {
			m1 = new MagicNumber(line,0);
			list.append(m1);
		}
		else if (l == layer) {
			m1 = new MagicNumber(line,current->parent);
			current->parent->childs.append(m1);
		}
		else if (l > layer) {
			m1 = new MagicNumber(line,current);
			current->childs.append(m1);
		}
		else {
			for (int i=layer;i>l;i--) current = current->parent;
			m1 = new MagicNumber(line,current->parent);
			current->parent->childs.append(m1);
		}
		current = m1;
		layer = l;
	}
	return TRUE;
}

const char* MimeMagic::getMimeType(const char *filename)
{
	result = "";
	int	fd = ::open(filename,O_RDONLY);
	if (fd < 0) return 0;
	QListIterator<MagicNumber>	it(list);
	for ( ; it.current(); ++it)
		if (it.current()->match(fd,result)) break;
	::close(fd);
	return result.data();
}

