/******************************************************************************
*   TinTin++                                                                  *
*   Copyright (C) 2004 (See CREDITS file)                                     *
*                                                                             *
*   This program is protected under the GNU GPL (See COPYING)                 *
*                                                                             *
*   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA *
*******************************************************************************/

/******************************************************************************
*                (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                 *
*                                                                             *
*                         coded by Peter Unold 1992                           *
*                     recoded by Igor van den Hoven 2004                      *
******************************************************************************/

#include <sys/types.h>
#include <pcre.h>

#include "tintin.h"


int match(struct session *ses, char *str, char *exp)
{
	char expbuf[BUFFER_SIZE];

	if (ses)
	{
		sprintf(expbuf, "^%s$", exp);

		substitute(ses, expbuf, expbuf, SUB_VAR|SUB_FUN);
	}
	else
	{
		sprintf(expbuf, "^%s$", exp);
	}
	return tintin_regexp(NULL, str, expbuf, 0, 0);
}

int find(struct session *ses, char *str, char *exp, int sub)
{
	char expbuf[BUFFER_SIZE], strbuf[BUFFER_SIZE];

	if (ses)
	{
		substitute(ses, str, strbuf, SUB_VAR|SUB_FUN);
		substitute(ses, exp, expbuf, SUB_VAR|SUB_FUN);

		return tintin_regexp(NULL, strbuf, expbuf, 0, sub);
	}
	else
	{
		return tintin_regexp(NULL, str, exp, 0, sub);
	}
}

DO_COMMAND(do_regexp)
{
	char left[BUFFER_SIZE], right[BUFFER_SIZE], is_true[BUFFER_SIZE], is_false[BUFFER_SIZE];

	arg = get_arg_in_braces(arg, left,  0);
	arg = get_arg_in_braces(arg, right, 0);
	arg = get_arg_in_braces(arg, is_true,  1);
	arg = get_arg_in_braces(arg, is_false, 1);

	if (*is_true == 0)
	{
		tintin_printf2(ses, "SYNTAX: #REGEXP {string} {expression} {true} {false}.");
	}
	else
	{
		substitute(ses, left,  left,  SUB_VAR|SUB_FUN);
		substitute(ses, right, right, SUB_VAR|SUB_FUN);

		if (tintin_regexp(NULL, left, right, 0, SUB_CMD))
		{
			substitute(ses, is_true, is_true, SUB_CMD);

			ses = script_driver(ses, -1, is_true);
		}
		else if (*is_false)
		{
			ses = script_driver(ses, -1, is_false);
		}
	}
	return ses;
}

int regexp_compare(pcre *nodepcre, char *str, char *exp, int option, int flag)
{
	pcre *regex;
	const char *error;
	int i, j, matches, match[303];

	if (nodepcre == NULL)
	{
		regex = pcre_compile(exp, option, &error, &i, NULL);
	}
	else
	{
		regex = nodepcre;
	}

	if (regex == NULL)
	{
		return FALSE;
	}

	matches = pcre_exec(regex, NULL, str, strlen(str), 0, 0, match, 303);

	if (matches <= 0)
	{
		if (nodepcre == NULL)
		{
			free(regex);
		}
		return FALSE;
	}

	switch (flag)
	{
		case SUB_CMD:
			for (i = 0 ; i < matches ; i++)
			{
				gtd->cmds[i] = refstring(gtd->cmds[i], "%.*s", match[i*2+1] - match[i*2], &str[match[i*2]]);
			}
			break;

		case SUB_CMD + SUB_FIX:
			for (i = 0 ; i < matches ; i++)
			{
				j = gtd->args[i];

				gtd->cmds[j] = refstring(gtd->cmds[j], "%.*s", match[i*2+1] - match[i*2], &str[match[i*2]]);
			}
			break;

		case SUB_ARG:
			for (i = 0 ; i < matches ; i++)
			{
				gtd->vars[i] = refstring(gtd->vars[i], "%.*s", match[i*2+1] - match[i*2], &str[match[i*2]]);
			}
			break;

		case SUB_ARG + SUB_FIX:
			for (i = 0 ; i < matches ; i++)
			{
				j = gtd->args[i];

				gtd->vars[j] = refstring(gtd->vars[j], "%.*s", match[i*2+1] - match[i*2], &str[match[i*2]]);
			}
			break;
	}

	if (nodepcre == NULL)
	{
		free(regex);
	}

	return TRUE;
}

pcre *regexp_compile(char *exp, int option)
{
	const char *error;
	int i;

	return pcre_compile(exp, option, &error, &i, NULL);
}


/******************************************************************************
* copy *string into *result, but substitute the various expressions with the  *
* values they stand for.                                                      *
******************************************************************************/

int substitute(struct session *ses, char *string, char *result, int flags)
{
	struct listnode *node;
	char temp[BUFFER_SIZE], buf[BUFFER_SIZE], buffer[BUFFER_SIZE], *pti, *pto, *ptt;
	char *pte;
	int i, cnt, escape = FALSE, flags_neol = flags;

	push_call("substitute(%p,%p,%p,%d)",ses,string,result,flags);

	pti = string;
	pto = (string == result) ? buffer : result;

	DEL_BIT(flags_neol, SUB_EOL|SUB_LNF);

	while (TRUE)
	{

#ifdef BIG5
		if (*pti & 0x80)
		{
			*pto++ = *pti++;
			if (*pti)
			{
				*pto++ = *pti++;
			}
			continue;
		}
#endif

		switch (*pti)
		{
			case '\0':
				if (HAS_BIT(flags, SUB_EOL))
				{
					if (HAS_BIT(ses->flags, SES_FLAG_RUN))
					{
						*pto++ = '\r';
					}
					else
					{
						*pto++ = '\r';
						*pto++ = '\n';
					}
				}

				if (HAS_BIT(flags, SUB_LNF))
				{
					*pto++ = '\n';
				}

				*pto = 0;

				pop_call();

				if (string == result)
				{
					strcpy(result, buffer);

					return pto - buffer;
				}
				else
				{
					return pto - result;
				}

			case '$':
				if (HAS_BIT(flags, SUB_VAR) && (pti[1] == DEFAULT_OPEN || isalpha((int) pti[1]) || pti[1] == '$'))
				{
					int def = FALSE;

					if (pti[1] == '$')
					{
						while (pti[1] == '$')
						{
							*pto++ = *pti++;
						}

						if (pti[1] == DEFAULT_OPEN || isalnum((int) pti[1]))
						{
							pti++;
						}
						else
						{
							*pto++ = *pti++;
						}
						continue;
					}

					pti++;

					if (*pti == DEFAULT_OPEN)
					{
						def = TRUE;

						pti = get_arg_in_braces(pti, buf, TRUE);

						substitute(ses, buf, temp, flags_neol);
					}
					else
					{
						ptt = temp;

						while (isalnum((int) *pti) || *pti == '_')
						{
							*ptt++ = *pti++;
						}
						*ptt = 0;
					}

					pti = get_arg_at_brackets(pti, temp + strlen(temp));

					substitute(ses, temp, buf, flags_neol);

					get_nest_node(ses->list[LIST_VARIABLE], buf, temp, def);

					substitute(ses, temp, pto, flags_neol - SUB_VAR);

					pto += strlen(pto);
				}
				else
				{
					*pto++ = *pti++;
				}
				break;

			case '<':
				if (HAS_BIT(flags, SUB_COL))
				{
					if (isdigit((int) pti[1]) && isdigit((int) pti[2]) && isdigit((int) pti[3]) && pti[4] == '>')
					{
						if (pti[1] != '8' || pti[2] != '8' || pti[3] != '8')
						{
							*pto++ = ESCAPE;
							*pto++ = '[';

							switch (pti[1])
							{
								case '2':
									*pto++ = '2';
									*pto++ = '2';
									*pto++ = ';';
									break;
								case '8':
									break;
								default:
									*pto++ = pti[1];
									*pto++ = ';';
							}
							switch (pti[2])
							{
								case '8':
									break;
								default:
									*pto++ = '3';
									*pto++ = pti[2];
									*pto++ = ';';
									break;
							}
							switch (pti[3])
							{
								case '8':
									break;
								default:
									*pto++ = '4';
									*pto++ = pti[3];
									*pto++ = ';';
									break;
							}
							pto--;
							*pto++ = 'm';
						}
						pti += 5;
					}
					else if (pti[1] >= 'a' && pti[1] <= 'f' && pti[2] >= 'a' && pti[2] <= 'f' && pti[3] >= 'a' && pti[3] <= 'f' && pti[4] == '>')
					{
						*pto++ = ESCAPE;
						*pto++ = '[';
						*pto++ = '3';
						*pto++ = '8';
						*pto++ = ';';
						*pto++ = '5';
						*pto++ = ';';
						cnt = 16 + (pti[1] - 'a') * 36 + (pti[2] - 'a') * 6 + (pti[3] - 'a');
						*pto++ = '0' + cnt / 100;
						*pto++ = '0' + cnt % 100 / 10;
						*pto++ = '0' + cnt % 10;
						*pto++ = 'm';
						pti += 5;
					}
					else if (pti[1] >= 'A' && pti[1] <= 'F' && pti[2] >= 'A' && pti[2] <= 'F' && pti[3] >= 'A' && pti[3] <= 'F' && pti[4] == '>')
					{
						*pto++ = ESCAPE;
						*pto++ = '[';
						*pto++ = '4';
						*pto++ = '8';
						*pto++ = ';';
						*pto++ = '5';
						*pto++ = ';';
						cnt = 16 + (pti[1] - 'A') * 36 + (pti[2] - 'A') * 6 + (pti[3] - 'A');
						*pto++ = '0' + cnt / 100;
						*pto++ = '0' + cnt % 100 / 10;
						*pto++ = '0' + cnt % 10;
						*pto++ = 'm';
						pti += 5;
					}
					else if (pti[1] == 'g' && isdigit((int) pti[2]) && isdigit((int) pti[3]) && pti[4] == '>')
					{
						*pto++ = ESCAPE;
						*pto++ = '[';
						*pto++ = '3';
						*pto++ = '8';
						*pto++ = ';';
						*pto++ = '5';
						*pto++ = ';';
						cnt = 232 + (pti[2] - '0') * 10 + (pti[3] - '0');
						*pto++ = '0' + cnt / 100;
						*pto++ = '0' + cnt % 100 / 10;
						*pto++ = '0' + cnt % 10;
						*pto++ = 'm';
						pti += 5;
					}
					else if (pti[1] == 'G' && isdigit((int) pti[2]) && isdigit((int) pti[3]) && pti[4] == '>')
					{
						*pto++ = ESCAPE;
						*pto++ = '[';
						*pto++ = '4';
						*pto++ = '8';
						*pto++ = ';';
						*pto++ = '5';
						*pto++ = ';';
						cnt = 232 + (pti[2] - '0') * 10 + (pti[3] - '0');
						*pto++ = '0' + cnt / 100;
						*pto++ = '0' + cnt % 100 / 10;
						*pto++ = '0' + cnt % 10;
						*pto++ = 'm';
						pti += 5;
					}
					else
					{
						*pto++ = *pti++;
					}
				}
				else
				{
					*pto++ = *pti++;
				}
				break;

			case '@':
				if (HAS_BIT(flags, SUB_FUN))
				{
					if (pti[1] == '@')
					{
						escape = TRUE;
						*pto++ = *pti++;

						continue;
					}

					for (ptt = temp, i = 1 ; isalnum((int) pti[i]) || pti[i] == '_' ; i++)
					{
						*ptt++ = pti[i];
					}
					*ptt = 0;

					node = search_node_list(ses->list[LIST_FUNCTION], temp);

					if (node == NULL || pti[i] != DEFAULT_OPEN)
					{
						escape = FALSE;
						*pto++ = *pti++;
						continue;
					}

					if (escape)
					{
						pti++;
						continue;
					}

					pti = get_arg_in_braces(&pti[i], temp, FALSE);

					substitute(ses, temp, buf, flags_neol);

					show_debug(ses, LIST_FUNCTION, "#DEBUG FUNCTION {%s}", node->left);

					RESTRING(gtd->vars[0], buf);

					pte = buf;

					for (i = 1 ; i < 100 ; i++)
					{
						pte = get_arg_in_braces(pte, temp, TRUE);

						RESTRING(gtd->vars[i], temp);

						if (*pte == 0)
						{
							break;
						}

						if (*pte == COMMAND_SEPARATOR)
						{
							pte++;
						}

					}

					substitute(ses, node->right, buf, SUB_ARG);

					script_driver(ses, LIST_FUNCTION, buf);

					substitute(ses, "$result", pto, flags_neol|SUB_VAR);

					pto += strlen(pto);
				}
				else
				{
					*pto++ = *pti++;
				}
				break;

			case '%':
				if (HAS_BIT(flags, SUB_ARG) && (isdigit((int) pti[1]) || pti[1] == '%'))
				{
					if (pti[1] == '%')
					{
						while (pti[1] == '%')
						{
							*pto++ = *pti++;
						}
						pti++;
					}
					else
					{
						i = isdigit((int) pti[2]) ? (pti[1] - '0') * 10 + pti[2] - '0' : pti[1] - '0';

						ptt = gtd->vars[i];

						while (*ptt)
						{
#ifdef BIG5
							if (*ptt & 0x80)
							{
								*pto++ = *ptt++;
								if (*ptt)
								{
									*pto++ = *ptt++;
								}
								continue;
							}
#endif
							if (HAS_BIT(flags, SUB_SEC))
							{
								switch (*ptt)
								{
									case '\\':
										*pto++ = '\\';
										*pto++ = '\\';
										break;

									case '{':
										*pto++ = '\\';
										*pto++ = 'x';
										*pto++ = '7';
										*pto++ = 'B';
										break;

									case '}':
										*pto++ = '\\';
										*pto++ = 'x';
										*pto++ = '7';
										*pto++ = 'D';
										break;

									case COMMAND_SEPARATOR:
										*pto++ = '\\';
										*pto++ = COMMAND_SEPARATOR;
										break;

									default:
										*pto++ = *ptt;
										break;
								}
								ptt++;
							}
							else
							{
								*pto++ = *ptt++;
							}
						}
						pti += isdigit((int) pti[2]) ? 3 : 2;
					}
				}
				else
				{
					*pto++ = *pti++;
				}
				break;

			case '&':
				if (HAS_BIT(flags, SUB_CMD) && (isdigit((int) pti[1]) || pti[1] == '&'))
				{
					if (pti[1] == '&')
					{
						while (pti[1] == '&')
						{
							*pto++ = *pti++;
						}
						if (isdigit((int) pti[1]))
						{
							pti++;
						}
						else
						{
							*pto++ = *pti++;
						}
					}
					else
					{
						i = isdigit((int) pti[2]) ? (pti[1] - '0') * 10 + pti[2] - '0' : pti[1] - '0';

						for (cnt = 0 ; gtd->cmds[i][cnt] ; cnt++)
						{
							*pto++ = gtd->cmds[i][cnt];
						}
						pti += isdigit((int) pti[2]) ? 3 : 2;
					}
				}
				else if (HAS_BIT(flags, SUB_VAR) && (pti[1] == DEFAULT_OPEN || isalpha((int) pti[1]) || pti[1] == '&'))
				{
					int def = 0;

					if (pti[1] == '&')
					{
						while (pti[1] == '&')
						{
							*pto++ = *pti++;
						}

						if (pti[1] == DEFAULT_OPEN || isalnum((int) pti[1]))
						{
							pti++;
						}
						else
						{
							*pto++ = *pti++;
						}
						continue;
					}


					pti++;

					if (*pti == DEFAULT_OPEN)
					{
						def = TRUE;

						pti = get_arg_in_braces(pti, buf, TRUE);

						substitute(ses, buf, temp, flags_neol);
					}
					else
					{
						for (ptt = temp ; isalnum((int) *pti) || *pti == '_' ; i++)
						{
							*ptt++ = *pti++;
						}
						*ptt = 0;
					}

					pti = get_arg_at_brackets(pti, temp + strlen(temp));

					substitute(ses, temp, buf, flags_neol);

					get_nest_index(ses->list[LIST_VARIABLE], buf, temp, def);

					substitute(ses, temp, pto, flags_neol - SUB_VAR);

					pto += strlen(pto);
				}
				else
				{
					*pto++ = *pti++;
				}
				break;

			case '\\':
				if (HAS_BIT(flags, SUB_ESC))
				{
					pti++;

					switch (*pti)
					{
						case 'a':
							*pto++ = '\a';
							break;
						case 'b':
							*pto++ = '\b';
							break;
						case 'c':
							if (pti[1])
							{
								pti++;
								*pto++ = *pti % 32;
							}
							break;
						case 'e':
							*pto++ = '\033';
							break;
						case 'n':
							*pto++ = '\n';
							break;
						case 'r':
							*pto++ = '\r';
							break;
						case 't':
							*pto++ = '\t';
							break;
						case 'x':
							if (pti[1] && pti[2])
							{
								pti++;
								*pto++ = hex_number(pti);
								pti++;
							}
							break;
						case '0':
							if (pti[1] && pti[2])
							{
								pti++;
								*pto++ = oct_number(pti);
								pti++;
							}
							break;
						case '\0':
							DEL_BIT(flags, SUB_EOL);
							DEL_BIT(flags, SUB_LNF);
							continue;
						default:
							*pto++ = *pti;
							break;
					}
					pti++;
				}
				else
				{
					*pto++ = *pti++;
				}
				break;

			default:
				if (HAS_BIT(flags, SUB_SEC) && !HAS_BIT(flags, SUB_ARG))
				{
					switch (*pti)
					{
						case '\\':
							*pto++ = '\\';
							*pto++ = '\\';
							break;

						case '{':
							*pto++ = '\\';
							*pto++ = 'x';
							*pto++ = '7';
							*pto++ = 'B';
							break;

						case '}':
							*pto++ = '\\';
							*pto++ = 'x';
							*pto++ = '7';
							*pto++ = 'D';
							break;

						case COMMAND_SEPARATOR:
							*pto++ = '\\';
							*pto++ = COMMAND_SEPARATOR;
							break;

						default:
							*pto++ = *pti;
							break;
					}
					pti++;
				}
				else
				{
					*pto++ = *pti++;
				}
				break;
		}
	}
}


/******************************************************************************
* Calls tintin_regexp checking if the string matches, and automatically fills *
* in the text represented by the wildcards on success.                        *
******************************************************************************/

int check_one_regexp(struct session *ses, struct listnode *node, char *line, char *original, int option)
{
	char *exp, *str;

	if (node->regex == NULL)
	{
		char result[BUFFER_SIZE];

		substitute(ses, node->left, result, SUB_VAR|SUB_FUN);

		exp = result;
	}
	else
	{
		exp = node->left;
	}

	if (HAS_BIT(node->flags, NODE_FLAG_META))
	{
		exp++;
		str = original;
	}
	else
	{
		str = line;
	}	

	return tintin_regexp(node->regex, str, exp, option, SUB_ARG);
}

/*
	Keep synched with tintin_regexp and tintin_regexp_compile
*/

int tintin_regexp_check(char *exp)
{
	if (*exp == '^')
	{
		return TRUE;
	}

	while (*exp)
	{
#ifdef BIG5
		if (*exp & 0x80)
		{
			exp += 2;
			continue;
		}
#endif		
		switch (exp[0])
		{
			case '\\':
			case '{':
				return TRUE;

			case '$':
				if (exp[1] == 0)
				{
					return TRUE;
				}
				break;

			case '%':
				switch (exp[1])
				{
					case '0':
					case '1':
					case '2':
					case '3':
					case '4':
					case '5':
					case '6':
					case '7':
					case '8':
					case '9':
					case 'd':
					case 'D':
					case 'i':
					case 'I':
					case 's':
					case 'S':
					case 'w':
					case 'W':
					case '?':
					case '*':
					case '+':
					case '.':
					case '%':
						return TRUE;
				}
				break;
		}
		exp++;
	}
	return FALSE;
}
				
int tintin_regexp(pcre *nodepcre, char *str, char *exp, int option, int flag)
{
	char out[BUFFER_SIZE], *pti, *pto;
	int arg = 1, var = 1, fix = 0;

	pti = exp;
	pto = out;

	while (*pti == '^')
	{
		*pto++ = *pti++;
	}

	while (*pti)
	{
#ifdef BIG5
		if (*pti & 0x80)
		{
			*pto++ = *pti++;
			*pto++ = *pti++;
			continue;
		}
#endif
		switch (pti[0])
		{
			case '\\':
				*pto++ = *pti++;
				*pto++ = *pti++;
				break;

			case '{':
				gtd->args[up(var)] = up(arg);
				*pto++ = '(';
				pti = get_arg_in_braces(pti, pto, TRUE);
				pto += strlen(pto);
				*pto++ = ')';
				break;

			case '[':
			case ']':
			case '(':
			case ')':
			case '|':
			case '.':
			case '?':
			case '+':
			case '*':
			case '^':
				*pto++ = '\\';
				*pto++ = *pti++;
				break;

			case '$':
				if (pti[1] != DEFAULT_OPEN && !isalnum((int) pti[1]))
				{
					int i = 0;

					while (pti[++i] == '$')
					{
						continue;
					}

					if (pti[i])
					{
						*pto++ = '\\';
					}
				}
				*pto++ = *pti++;
				break;

			case '%':
				switch (pti[1])
				{
					case '0':
					case '1':
					case '2':
					case '3':
					case '4':
					case '5':
					case '6':
					case '7':
					case '8':
					case '9':
						fix = SUB_FIX;
						arg = isdigit((int) pti[2]) ? (pti[1] - '0') * 10 + (pti[2] - '0') : pti[1] - '0';
						gtd->args[up(var)] = up(arg);
						pti += isdigit((int) pti[2]) ? 3 : 2;
						strcpy(pto, *pti == 0 ? "(.*)" : "(.*?)");
						pto += strlen(pto);
						break;

					case 'd':
						gtd->args[up(var)] = up(arg);
						pti += 2;
						strcpy(pto, *pti == 0 ? "([0-9]*)" : "([0-9]*?)");
						pto += strlen(pto);
						break;

					case 'D':
						gtd->args[up(var)] = up(arg);
						pti += 2;
						strcpy(pto, *pti == 0 ? "([^0-9]*)" : "([^0-9]*?)");
						pto += strlen(pto);
						break;

					case 'i':
						pti += 2;
						strcpy(pto, "(?i)");
						pto += strlen(pto);
						break;

					case 'I':
						pti += 2;
						strcpy(pto, "(?-i)");
						pto += strlen(pto);
						break;

					case 's':
						gtd->args[up(var)] = up(arg);
						pti += 2;
						strcpy(pto, *pti == 0 ? "(\\s*)" : "(\\s*?)");
						pto += strlen(pto);
						break;

					case 'S':
						gtd->args[up(var)] = up(arg);
						pti += 2;
						strcpy(pto, *pti == 0 ? "(\\S*)" : "(\\S*?)");
						pto += strlen(pto);
						break;

					case 'w':
						gtd->args[up(var)] = up(arg);
						pti += 2;
						strcpy(pto, *pti == 0 ? "([a-zA-Z]*)" : "([a-zA-Z]*?)");
						pto += strlen(pto);
						break;

					case 'W':
						gtd->args[up(var)] = up(arg);
						pti += 2;
						strcpy(pto, *pti == 0 ? "([^a-zA-Z]*)" : "([^a-zA-Z]*?)");
						pto += strlen(pto);
						break;

					case '?':
						gtd->args[up(var)] = up(arg);
						pti += 2;
						strcpy(pto, *pti == 0 ? "(.?)" : "(.?" "?)");
						pto += strlen(pto);
						break;

					case '*':
						gtd->args[up(var)] = up(arg);
						pti += 2;
						strcpy(pto, *pti == 0 ? "(.*)" : "(.*?)");
						pto += strlen(pto);
						break;

					case '+':
						gtd->args[up(var)] = up(arg);
						pti += 2;
						strcpy(pto, *pti == 0 ? "(.+)" : "(.+?)");
						pto += strlen(pto);
						break;

					case '.':
						gtd->args[up(var)] = up(arg);
						pti += 2;
						strcpy(pto, "(.)");
						pto += strlen(pto);
						break;

					case '%':
						*pto++ = *pti++;
						pti++;
						break;

					default:
						*pto++ = *pti++;
						break;
				}
				break;

			default:
				*pto++ = *pti++;
				break;
		}
	}
	*pto = 0;

	return regexp_compare(nodepcre, str, out, option, flag + fix);
}

pcre *tintin_regexp_compile(struct listnode *node, char *exp, int option)
{
	char out[BUFFER_SIZE], *pti, *pto;

	pti = exp;
	pto = out;

	if (*pti == '~')
	{
		pti++;
		SET_BIT(node->flags, NODE_FLAG_META);
	}

	while (*pti == '^')
	{
		*pto++ = *pti++;
	}

	while (*pti)
	{

#ifdef BIG5
		if (*pti & 0x80)
		{
			*pto++ = *pti++;
			*pto++ = *pti++;
			continue;
		}
#endif
		switch (pti[0])
		{
			case '\\':
				*pto++ = *pti++;
				*pto++ = *pti++;
				break;

			case '{':
				*pto++ = '(';
				pti = get_arg_in_braces(pti, pto, TRUE);
				while (*pto)
				{
					if (pto[0] == '$' || pto[0] == '@')
					{
						if (pto[1])
						{
							return NULL;
						}
					}
					pto++;
				}
				*pto++ = ')';
				break;

			case '&':
				if (pti[1] == DEFAULT_OPEN || isalnum((int) pti[1]) || pti[1] == '&')
				{
					return NULL;
				}
				*pto++ = *pti++;
				break;

			case '@':
				if (pti[1] == DEFAULT_OPEN || isalnum((int) pti[1]) || pti[1] == '@')
				{
					return NULL;
				}
				*pto++ = *pti++;
				break;

			case '$':
				if (pti[1] == DEFAULT_OPEN || isalnum((int) pti[1]))
				{
					return NULL;
				}
				{
					int i = 0;

					while (pti[++i] == '$')
					{
						continue;
					}

					if (pti[i])
					{
						*pto++ = '\\';
					}
				}
				*pto++ = *pti++;
				break;

			case '[':
			case ']':
			case '(':
			case ')':
			case '|':
			case '.':
			case '?':
			case '+':
			case '*':
			case '^':
				*pto++ = '\\';
				*pto++ = *pti++;
				break;

			case '%':
				switch (pti[1])
				{
					case '0':
					case '1':
					case '2':
					case '3':
					case '4':
					case '5':
					case '6':
					case '7':
					case '8':
					case '9':
						pti += isdigit((int) pti[2]) ? 3 : 2;
						strcpy(pto, *pti == 0 ? "(.*)" : "(.*?)");
						pto += strlen(pto);
						break;

					case 'd':
						pti += 2;
						strcpy(pto, *pti == 0 ? "([0-9]*)" : "([0-9]*?)");
						pto += strlen(pto);
						break;

					case 'D':
						pti += 2;
						strcpy(pto, *pti == 0 ? "([^0-9]*)" : "([^0-9]*?)");
						pto += strlen(pto);
						break;

					case 'i':
						pti += 2;
						strcpy(pto, "(?i)");
						pto += strlen(pto);
						break;

					case 'I':
						pti += 2;
						strcpy(pto, "(?-i)");
						pto += strlen(pto);
						break;

					case 's':
						pti += 2;
						strcpy(pto, *pti == 0 ? "(\\s*)" : "(\\s*?)");
						pto += strlen(pto);
						break;

					case 'S':
						pti += 2;
						strcpy(pto, *pti == 0 ? "(\\S*)" : "(\\S*?)");
						pto += strlen(pto);
						break;

					case 'w':
						pti += 2;
						strcpy(pto, *pti == 0 ? "([a-zA-Z]*)" : "([a-zA-Z]*?)");
						pto += strlen(pto);
						break;

					case 'W':
						pti += 2;
						strcpy(pto, *pti == 0 ? "([^a-zA-Z]*)" : "([^a-zA-Z]*?)");
						pto += strlen(pto);
						break;

					case '?':
						pti += 2;
						strcpy(pto, *pti == 0 ? "(.?)" : "(.?" "?)");
						pto += strlen(pto);
						break;

					case '*':
						pti += 2;
						strcpy(pto, *pti == 0 ? "(.*)" : "(.*?)");
						pto += strlen(pto);
						break;

					case '+':
						pti += 2;
						strcpy(pto, *pti == 0 ? "(.+)" : "(.+?)");
						pto += strlen(pto);
						break;

					case '.':
						pti += 2;
						strcpy(pto, "(.)");
						pto += strlen(pto);
						break;

					case '%':
						*pto++ = *pti++;
						pti++;
						break;

					default:
						*pto++ = *pti++;
						break;
				}
				break;

			default:
				*pto++ = *pti++;
				break;
		}
	}
	*pto = 0;

	return regexp_compile(out, option);
}