/******************************************************************************
*   This file is part of TinTin++                                             *
*                                                                             *
*   Copyright 2004-2020 Igor van den Hoven                                    *
*                                                                             *
*   TinTin++ 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 3 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 TinTin++.  If not, see https://www.gnu.org/licenses.           *
******************************************************************************/

/******************************************************************************
*                               T I N T I N + +                               *
*                                                                             *
*                      coded by Igor van den Hoven 2004                       *
******************************************************************************/

#include "tintin.h"

void init_pos(struct session *ses, int row, int col)
{
	push_call("init_pos(%p)",ses);

	goto_pos(ses, row, col);

	gtd->screen->sav_row[0] = ses->cur_row;
	gtd->screen->sav_col[0] = ses->cur_col;

	gtd->screen->sav_lev = 1;

	pop_call();
	return;
}

void hide_cursor(struct session *ses)
{
	if (!HAS_BIT(gtd->flags, TINTIN_FLAG_HIDDENCURSOR))
	{
		print_stdout(0, 0, "\e[?25l");
	}
}

void show_cursor(struct session *ses)
{
	if (!HAS_BIT(gtd->flags, TINTIN_FLAG_HIDDENCURSOR))
	{
		print_stdout(0, 0, "\e[?25h");
	}
}

void save_pos(struct session *ses)
{
	gtd->screen->sav_row[gtd->screen->sav_lev] = ses->cur_row;
	gtd->screen->sav_col[gtd->screen->sav_lev] = ses->cur_col;

	if (gtd->screen->sav_lev < STACK_SIZE)
	{
		gtd->screen->sav_lev++;
	}
	else
	{
		syserr_printf(ses, "sav_lev++ above 1000.");
	}

	hide_cursor(ses);
}

void restore_pos(struct session *ses)
{
	if (gtd->screen->sav_lev > 0)
	{
		gtd->screen->sav_lev--;
	}
	else
	{
		gtd->screen->sav_lev = 0;
		syserr_printf(ses, "restore_pos: sav_lev-- below 0.");
	}

	if (gtd->screen->sav_lev == 1 /* gtd->screen->sav_row[gtd->screen->sav_lev] == inputline_cur_row()*/ /*gtd->screen->rows*/)
	{
		goto_pos(ses, inputline_cur_row(), inputline_cur_col());
	}
	else
	{
		goto_pos(ses, gtd->screen->sav_row[gtd->screen->sav_lev], gtd->screen->sav_col[gtd->screen->sav_lev]);
	}
	show_cursor(ses);
}

void goto_pos(struct session *ses, int row, int col)
{
	if (row < 1 || col < 1)
	{
		tintin_printf2(NULL, "\e[1;31merror: invalid row,col: goto_pos(%d,%d)\n",row,col);

		dump_stack();

		return;
	}
	// Add \r to capture a stray \e in broken packets

	print_stdout(0, 0, "\r\e[%d;%dH", row, col);

	ses->cur_row = row;
	ses->cur_col = col;
}

void erase_cols(int cnt)
{
	if (cnt)
	{
		print_stdout(0, 0, "\e[%dX", cnt);
	}
}

/*
	unused
*/

void reset(struct session *ses)
{
	ses->cur_row = 1;
	ses->cur_col = 1;

	print_stdout(0, 0, "\ec");
}


void scroll_region(struct session *ses, int top, int bot)
{
	push_call("scroll_region(%p,%d,%d)",ses,top,bot);

	if (ses == gtd->ses)
	{
		if (top != 1)
		{
			print_stdout(0, 0, "\e[?1049h\e[?7787h\e[%d;%dr", top, bot);
		}
		else
		{
			print_stdout(0, 0, "\e[?1049l\e[?7787l\e[%d;%dr", top, bot);
		}
	}
	ses->split->top_row = top;
	ses->split->bot_row = bot;

	check_all_events(ses, EVENT_FLAG_VT100, 0, 4, "VT100 SCROLL REGION", ntos(top), ntos(bot), ntos(gtd->screen->rows), ntos(gtd->screen->cols), ntos(get_scroll_cols(ses)));

	pop_call();
	return;
}

void reset_scroll_region(struct session *ses)
{
	if (ses == gtd->ses)
	{
		print_stdout(0, 0, "\e[?1049l\e[?7787l\e[r");
	}
	ses->split->top_row = 1;
	ses->split->top_col = 1;
	ses->split->bot_row = gtd->screen->rows;
	ses->split->bot_col = gtd->screen->cols;
}


int skip_vt102_codes(char *str)
{
	int skip;

	switch (str[0])
	{
		case   7:   /* BEL */
		case   8:   /* BS  */
	//	case   9:      HT
	//	case  10:      LF
		case  11:   /* VT  */
		case  12:   /* FF  */
		case  13:   /* CR  */
		case  14:   /* SO  */
		case  15:   /* SI  */
		case  17:   /* DC1 */
		case  19:   /* DC3 */
		case  24:   /* CAN */
		case  26:   /* SUB */
			return 1;

		case  27:   /* ESC */
			break;

		case  28: // HTML_OPEN
			for (skip = 1 ; str[skip] ; skip++)
			{
				if (str[skip] == 30) // HTML_CLOSE
				{
					return skip + 1;
				}
			}
			return 0;

		case 127:   /* DEL */
			return 1;

		default:
			return 0;
	}

	switch (str[1])
	{
		case '\0':
			return 1;

		case '%':
		case '#':
		case '(':
		case ')':
			return str[2] ? 3 : 2;

		case ']':
			switch (str[2])
			{
				case 0:
					return 2;

				case 'P':
					for (skip = 3 ; skip < 10 ; skip++)
					{
						if (str[skip] == 0)
						{
							break;
						}
					}
					return skip;

				case 'R':
					return 3;

				default:
					for (skip = 2 ; str[skip] ; skip++)
					{
						if (str[skip] == '\a')
						{
							return skip + 1;
						}

						if (str[skip] == '\e' && str[skip+1] == '\\')
						{
							return skip + 2;
						}
					}
					break;
			}
			return 2;

		case '[':
			break;

		default:
			return 2;
	}

	for (skip = 2 ; str[skip] != 0 ; skip++)
	{
		if (is_csichar(str[skip]))
		{
			return skip + 1;
		}
	}
	return skip;
}

int skip_vt102_codes_non_graph(char *str)
{
	int skip = 0;

	switch (str[skip])
	{
		case   7:   // BEL
//		case   8:   // BS  
//		case   9:   // HT  
//		case  10:   // LF  
		case  11:   // VT  
		case  12:   // FF  
		case  13:   // CR  
		case  14:   // SO  
		case  15:   // SI  
		case  17:   // DC1 
		case  19:   // DC3 
		case  24:   // CAN 
		case  26:   // SUB 
		case 127:   // DEL 
			return 1;

		case  27:   // ESC 
			break;

		default:
			return 0;
	}

	switch (str[1])
	{
		case '\0':
			return 0;

		case 'c':
		case 'D':
		case 'E':
		case 'H':
		case 'M':
		case 'Z':
		case '7':
		case '8':
		case '>':
		case '=':
			return 2;

		case '%':
		case '#':
		case '(':
		case ')':
			return str[2] ? 3 : 2;

		case ']':
			switch (str[2])
			{
				case 0:
					return 2;

				case 'P':
					for (skip = 3 ; skip < 10 ; skip++)
					{
						if (str[skip] == 0)
						{
							break;
						}
					}
					return skip;

				case 'R':
					return str[3] ? 3 : 2;

				case '6':
					return 0;

				default:
					for (skip = 2 ; str[skip] ; skip++)
					{
						if (str[skip] == '\a')
						{
							return skip + 1;
						}

						if (str[skip] == '\e' && str[skip+1] == '\\')
						{
							return skip + 2;
						}
					}
					break;
			}
			return 2;

		case '[':
			break;

		default:
			return 2;
	}

	for (skip = 2 ; str[skip] != 0 ; skip++)
	{
		if (str[skip] == 'm')
		{
			return 0;
		}

		if (is_csichar(str[skip]))
		{
			return skip + 1;
		}
	}
	return 0;
}

int get_vt102_width(struct session *ses, char *str, int *str_len)
{
	int raw_len;

	*str_len = 0;

	if (*str)
	{
		raw_len = skip_vt102_codes(str);

		if (raw_len)
		{
			return raw_len;
		}

		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC))
		{
			return get_euc_width(ses, str, str_len);
		}

		if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8))
		{
			return get_utf8_width(str, str_len, NULL);
		}

		return get_ascii_width(str, str_len);
	}
	return 0;
}

int strip_vt102_width(struct session *ses, char *str, int *str_width)
{
	int width;
	char *pts;

	pts = str;

	*str_width = 0;

	while (*pts)
	{
		pts += get_vt102_width(ses, pts, &width);

		*str_width += width;
	}
	return pts - str;
}


int find_color_code(char *str)
{
	int skip;

	switch (str[0])
	{
		case  ASCII_ESC:
			break;

		default:
			return 0;
	}

	switch (str[1])
	{
		case '[':
			break;

		default:
			return 0;
	}

	for (skip = 2 ; str[skip] != 0 ; skip++)
	{
		if (str[skip] == 'm')
		{
			return skip + 1;
		}

		if (is_csichar(str[skip]))
		{
			return 0;
		}
	}
	return 0;
}

int find_escaped_color_code(char *str)
{
	int skip;

	switch (str[0])
	{
		case  '\\':
			break;

		default:
			return 0;
	}

	switch (str[1])
	{
		case 'e':
			break;
		default:
			return 0;
	}

	if (str[2] == '[')
	{
		for (skip = 3 ; str[skip] != 0 ; skip++)
		{
			if (str[skip] == 'm')
			{
				return skip + 1;
			}

			if (is_csichar(str[skip]))
			{
				return 0;
			}
		}
		return 0;
	}

	if (str[2] == ']')
	{
		for (skip = 3 ; str[skip] != 0 ; skip++)
		{
			if (str[skip] == '\\' && str[skip + 1] == 'a')
			{
				return skip + 2;
			}
		}
	}
	return 0;
}

int find_secure_color_code(char *str)
{
	int skip;

	if (*str != ASCII_ESC)
	{
		return 0;
	}

	if (str[1] == '[')
	{
		for (skip = 2 ; str[skip] != 0 ; skip++)
		{
			switch (str[skip])
			{
				case 'm':
					if (is_digit(str[skip - 1]))
					{
						return skip + 1;
					}
					return 0;

				case ';':
				case ':':
				case '0':
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
				case '7':
				case '8':
				case '9':
					break;

				default:
					return 0;
			}
		}
	}
	else if (str[1] == ']' && str[2] == '6' && str[3] == '8' && str[4] == ';')
	{
		for (skip = 5 ; str[skip] != 0 ; skip++)
		{
			if (str[skip] == ASCII_BEL)
			{
				return skip + 1;
			}
		}
		return 0;
	}
	return 0;
}


int strip_vt102_codes(char *str, char *buf)
{
	char *pti, *pto;

	pti = (char *) str;
	pto = (char *) buf;

	while (*pti)
	{
		while (skip_vt102_codes(pti))
		{
			pti += skip_vt102_codes(pti);
		}

		if (*pti)
		{
			*pto++ = *pti++;
		}
	}
	*pto = 0;

	return pto - buf;
}


void strip_vt102_codes_non_graph(char *str, char *buf)
{
	char *pti, *pto;

	pti = str;
	pto = buf;

	while (*pti)
	{
		while (skip_vt102_codes_non_graph(pti))
		{
			pti += skip_vt102_codes_non_graph(pti);
		}

		if (*pti)
		{
			*pto++ = *pti++;
		}
	}
	*pto = 0;
}

void strip_non_vt102_codes(char *str, char *buf)
{
	char *pti, *pto;
	int len;

	pti = str;
	pto = buf;

	while (*pti)
	{
		while ((len = skip_vt102_codes(pti)) != 0)
		{
			memcpy(pto, pti, len);
			pti += len;
			pto += len;
		}

		if (*pti)
		{
			pti++;
		}
	}
	*pto = 0;
}

char *strip_vt102_strstr(char *str, char *buf, int *len)
{ 
	char *pti, *ptm, *pts;

	push_call("strip_vt102_strstr(%p,%p,%p)",str,buf,len);

	pts = str;

	while (*pts)
	{
		while (skip_vt102_codes(pts))
		{
			pts += skip_vt102_codes(pts);
		}

		pti = pts;
		ptm = buf;

		while (*pti)
		{
			if (*pti++ != *ptm++)
			{
				break;
			}

			if (*ptm == 0)
			{
				if (len)
				{
					*len = pti - pts;
				}
				pop_call();
				return pts;
			}

			while (skip_vt102_codes(pti))
			{
				pti += skip_vt102_codes(pti);
			}
		}
		pts++;
	}
	pop_call();
	return NULL;
}

// mix old and str, then copy compressed color string to buf which can point to old.

void get_color_codes(char *old, char *str, char *buf, int flags)
{
	char *pti, *ptb, *ptc, col[100];
	int hop, vtc, fgc, bgc;
	int rgb[6] = { 0, 0, 0, 0, 0, 0 };

	hop =  0;
	vtc =  0;
	fgc = -1;
	bgc = -1;

	start:

	pti = hop ? str : old;

	while (*pti)
	{
		if (*pti != ASCII_ESC)
		{
			if (!HAS_BIT(flags, GET_ALL))
			{
				break;
			}
			pti++;

			continue;
		}

		pti++;

		if (*pti != '[')
		{
			continue;
		}
		pti++;

		if (*pti == 'm')
		{
			vtc =  0;
			fgc = -1;
			bgc = -1;

			pti++;

			continue;
		}

		ptc = col;

		while (*pti)
		{
			if (*pti >= '0' && *pti <= '9')
			{
				if (ptc - col > 20)
				{
					break;
				}

				*ptc++ = *pti++;

				continue;
			}

			if (*pti != ';' && *pti != 'm')
			{
				pti++;

				break;
			}

			*ptc = 0;

			ptc = col;

			if (HAS_BIT(vtc, COL_XTF_R))
			{
				fgc = URANGE(0, atoi(col), 255);
				DEL_BIT(vtc, COL_XTF_5|COL_XTF_R);
				SET_BIT(vtc, COL_XTF);
			}
			else if (HAS_BIT(vtc, COL_XTB_R))
			{
				bgc = URANGE(0, atoi(col), 255);
				DEL_BIT(vtc, COL_XTB_5|COL_XTB_R);
				SET_BIT(vtc, COL_XTB);
			}
			else if (HAS_BIT(vtc, COL_TCF_R))
			{
				if (rgb[0] == 256)
				{
					rgb[0] = URANGE(0, atoi(col), 255);
				}
				else if (rgb[1] == 256)
				{
					rgb[1] = URANGE(0, atoi(col), 255);
				}
				else
				{
					rgb[2] = URANGE(0, atoi(col), 255);

					fgc = rgb[0] * 256 * 256 + rgb[1] * 256 + rgb[2];
					
					DEL_BIT(vtc, COL_TCF_2|COL_TCF_R);
					SET_BIT(vtc, COL_TCB);
				}
			}
			else if (HAS_BIT(vtc, COL_TCB_R))
			{
				if (rgb[3] == 256)
				{
					rgb[3] = URANGE(0, atoi(col), 255);
				}
				else if (rgb[4] == 256)
				{
					rgb[4] = URANGE(0, atoi(col), 255);
				}
				else
				{
					rgb[5] = URANGE(0, atoi(col), 255);

					fgc = rgb[3] * 256 * 256 + rgb[4] * 256 + rgb[5];
					
					DEL_BIT(vtc, COL_TCB_2|COL_TCF_R);
					SET_BIT(vtc, COL_TCB);
				}
			}
			else
			{
				switch (atoi(col))
				{
					case 0:
						vtc = 0;
						fgc = -1;
						bgc = -1;
						break;
					case 1:
						SET_BIT(vtc, COL_BLD);
						break;
					case 2:
						if (HAS_BIT(vtc, COL_TCF_2))
						{
							rgb[0] = rgb[1] = rgb[2] = 256;
							SET_BIT(vtc, COL_TCF_R);
						}
						else if (HAS_BIT(vtc, COL_TCB_2))
						{
							rgb[3] = rgb[4] = rgb[5] = 256;
							SET_BIT(vtc, COL_TCB_R);
						}
						else
						{
							DEL_BIT(vtc, COL_BLD);
						}
						break;

					case 3:
						SET_BIT(vtc, COL_ITA);
						break;

					case 4:
						SET_BIT(vtc, COL_UND);
						break;
					case 5:
						if (HAS_BIT(vtc, COL_XTF_5))
						{
							SET_BIT(vtc, COL_XTF_R);
						}
						else if (HAS_BIT(vtc, COL_XTB_5))
						{
							SET_BIT(vtc, COL_XTB_R);
						}
						else
						{
							SET_BIT(vtc, COL_BLK);
						}
						break;
					case 7:
						SET_BIT(vtc, COL_REV);
						break;
					case 21:
					case 22:
						DEL_BIT(vtc, COL_BLD);
						break;

					case 23:
						DEL_BIT(vtc, COL_ITA);
						break;

					case 24:
						DEL_BIT(vtc, COL_UND);
						break;
					case 25:
						DEL_BIT(vtc, COL_BLK);
						break;
					case 27:
						DEL_BIT(vtc, COL_REV);
						break;
					case 38:
						SET_BIT(vtc, COL_XTF_5|COL_TCF_2);
						fgc = -1;
						break;
					case 48:
						SET_BIT(vtc, COL_XTB_5|COL_TCB_2);
						bgc = -1;
						break;
					default:
						switch (atoi(col) / 10)
						{
							case 3:
							case 10:
								fgc = atoi(col);
								DEL_BIT(vtc, COL_XTF|COL_TCF);
								break;
							
							case 4:
							case 9:
								bgc = atoi(col);
								DEL_BIT(vtc, COL_XTB|COL_TCB);
								break;
						}
						break;
				}
			}

			if (*pti++ == 'm')
			{
				break;
			}
		}
	}

	if (++hop == 1)
	{
		goto start;
	}

	ptb = buf;

	*ptb++ = '\e';
	*ptb++ = '[';
	*ptb++ = '0';

	if (HAS_BIT(vtc, COL_BLD))
	{
		*ptb++ = ';';
		*ptb++ = '1';
	}
	if (HAS_BIT(vtc, COL_ITA))
	{
		*ptb++ = ';';
		*ptb++ = '3';
	}

	if (HAS_BIT(vtc, COL_UND))
	{
		*ptb++ = ';';
		*ptb++ = '4';
	}
	if (HAS_BIT(vtc, COL_BLK))
	{
		*ptb++ = ';';
		*ptb++ = '5';
	}
	if (HAS_BIT(vtc, COL_REV))
	{
		*ptb++ = ';';
		*ptb++ = '7';
	}

	if (HAS_BIT(vtc, COL_XTF))
	{
		ptb += sprintf(ptb, ";38;5;%d", fgc);
	}
	else if (HAS_BIT(vtc, COL_TCF))
	{
		ptb += sprintf(ptb, ";38;2;%d;%d;%d", fgc / 256 / 256, fgc / 256 % 256, fgc % 256);
	}
	else if (fgc != -1)
	{
		ptb += sprintf(ptb, ";%d", fgc);
	}

	if (HAS_BIT(vtc, COL_XTB))
	{
		ptb += sprintf(ptb, ";48;5;%d", bgc);
	}
	else if (HAS_BIT(vtc, COL_TCB))
	{
		ptb += sprintf(ptb, ";48;2;%d;%d;%d", bgc / 256 / 256, bgc / 256 % 256, bgc % 256);
	}
	else if (bgc != -1)
	{
		ptb += sprintf(ptb, ";%d", bgc);
	}

	*ptb++ = 'm';

	*ptb = 0;

	return;
}

int strip_vt102_strlen(struct session *ses, char *str)
{
	char *pti;
	int size, width, str_len;
	
	str_len = 0;

	pti = str;

	while (*pti)
	{
		size = skip_vt102_codes(pti);

		if (size == 0)
		{
/*			if (*pti == '\t')
			{
				width = ses->tab_width - str_len % ses->tab_width;
				size  = 1;
			}
			else
*/
			if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(pti))
			{
				size = get_utf8_width(pti, &width, NULL);
			}
			else
			{
				size = get_ascii_width(pti, &width);
			}
			str_len += width;
		}
		pti += size;
	}
	return str_len;
}

// outdated

int strip_color_strlen(struct session *ses, char *str)
{
	char *buf = str_alloc_stack(0);

	substitute(ses, str, buf, SUB_ESC|SUB_COL);

	return strip_vt102_strlen(ses, buf);
}

int interpret_vt102_codes(struct session *ses, char *str, int real)
{
	char *data = str_alloc_stack(0);
	int skip = 0;

	switch (str[skip])
	{
		case   8:
			ses->cur_col = UMAX(1, ses->cur_col - 1);
			return TRUE;

		case  27:   /* ESC */
			break;

		case 11:    /* VT  */
			ses->cur_row = UMIN(gtd->screen->rows, ses->cur_row + 1);
			return TRUE;

		case 12:    /* FF  */
			ses->cur_row = UMIN(gtd->screen->rows, ses->cur_row + 1);
			return TRUE;

		case  13:   /* CR  */
			ses->cur_col = 1;
			return TRUE;

		default:
			return TRUE;
	}

	switch (str[1])
	{
		case '7':
			ses->sav_row = ses->cur_row;
			ses->sav_col = ses->cur_col;
			return TRUE;

		case '8':
			ses->cur_row = ses->sav_row;
			ses->cur_col = ses->sav_col;
			return TRUE;

		case 'c':
			ses->cur_row = 1;
			ses->cur_col = 1;
			ses->sav_row = ses->cur_row;
			ses->sav_col = ses->cur_col;
			return TRUE;

		case 'D':
			ses->cur_row = URANGE(1, ses->cur_row + 1, gtd->screen->rows);
			return TRUE;

		case 'E':
			ses->cur_row = URANGE(1, ses->cur_row + 1, gtd->screen->rows);
			ses->cur_col = 1;
			return TRUE;

		case 'M':
			ses->cur_row = URANGE(1, ses->cur_row - 1, gtd->screen->rows);
			return TRUE;

		case '[':
			break;

		default:
			return TRUE;
	}

	for (skip = 2 ; str[skip] != 0 ; skip++)
	{
		switch (str[skip])
		{
			case '@':
			case '`':
			case ']':
				return TRUE;

			case 'c':
				return FALSE;

			case 'A':
				ses->cur_row -= UMAX(1, atoi(data));
				break;

			case 'B':
			case 'e':
				ses->cur_row += UMAX(1, atoi(data));
				break;

			case 'C':
			case 'a':
				ses->cur_col += UMAX(1, atoi(data));
				break;

			case 'D':
				ses->cur_col -= UMAX(1, atoi(data));
				break;

			case 'E':
				ses->cur_row -= UMAX(1, atoi(data));
				ses->cur_col = 1;
				break;

			case 'F':
				ses->cur_row -= UMAX(1, atoi(data));
				ses->cur_col = 1;
				break;

			case 'G':
				ses->cur_col = UMAX(1, atoi(data));
				break;

			case 'H':
			case 'f':
				if (sscanf(data, "%d;%d", &ses->cur_row, &ses->cur_col) != 2)
				{
					if (sscanf(data, "%d", &ses->cur_row) == 1)
					{
						ses->cur_col = 1;
					}
					else
					{
						ses->cur_row = 1;
						ses->cur_col = 1;
					}
				}
				break;

			case 'd':
				ses->cur_row = atoi(data);
				break;

			case 'r':
				if (sscanf(data, "%d;%d", &ses->split->top_row, &ses->split->bot_row) != 2)
				{
					if (sscanf(data, "%d", &ses->split->top_row) != 1)
					{
						ses->split->top_row = 1;
						ses->split->bot_row = gtd->screen->rows;
					}
					else
					{
						ses->split->bot_row = gtd->screen->rows;
					}
				}
				ses->cur_row = 1;
				ses->cur_col = 1;
				break;

			case 's':
				ses->sav_row = ses->cur_row;
				ses->sav_col = ses->cur_col;
				break;

			case 'u':
				ses->cur_row = ses->sav_row;
				ses->cur_col = ses->sav_col;
				break;

			case 'x':
				return FALSE;

			default:
				data[skip - 2] = str[skip];
				data[skip - 1] = 0;
				break;
		}

		if (is_alpha(str[skip]))
		{
			ses->cur_row = URANGE(1, ses->cur_row, gtd->screen->rows);

			ses->cur_col = URANGE(1, ses->cur_col, gtd->screen->cols + 1);


			ses->split->top_row = URANGE(1, ses->split->top_row, gtd->screen->rows);

			ses->split->bot_row = ses->split->bot_row ? URANGE(1, ses->split->bot_row, gtd->screen->rows) : gtd->screen->rows;
			
			return TRUE;
		}
	}
	return TRUE;
}

int catch_vt102_codes(struct session *ses, unsigned char *str, int cplen)
{
	int skip, cnt, len, val[5];

	push_call("catch_vt102_codes(%p)",str);

	switch (str[0])
	{
		case ASCII_ENQ:
			if (check_all_events(ses, EVENT_FLAG_CATCH, 0, 1, "CATCH VT100 ENQ", gtd->system->term))
			{
				pop_call();
				return 1;
			}
			pop_call();
			return 0;
			
		case ASCII_ESC:
			break;

		default:
			pop_call();
			return 0;
	}

	val[0] = val[1] = val[2] = val[3] = 0;

	cnt = 0;

	switch (str[1])
	{
		case '\0':
			break;

		case '[':
			for (len = 2 ; str[len] ; len++)
			{
				if (str[len] >= '0' && str[len] <= '9')
				{
					val[cnt] *= 10;
					val[cnt] += str[len] - '0';
				}
				else
				{
					switch (str[len])
					{
						case ';':
							if (cnt < 4)
							{
								cnt++;
							}
							break;

						case 'c':
							check_all_events(ses, EVENT_FLAG_VT100, 0, 1, "VT100 DA", ntos(val[0]));
							pop_call();
							return len + 1;

						case 'n':
							if (val[0] == 5)
							{
								check_all_events(ses, EVENT_FLAG_VT100, 0, 0, "VT100 DSR");
							}
							if (val[0] == 6)
							{
								check_all_events(ses, EVENT_FLAG_VT100, 0, 2, "VT100 CPR", ntos(gtd->screen->cols), ntos(gtd->screen->rows));
							}
							pop_call();
							return len + 1;

						case 'r':
							if (check_all_events(ses, EVENT_FLAG_CATCH, 0, 2, "CATCH VT100 SCROLL REGION", ntos(val[0]), ntos(val[1])))
							{
								pop_call();
								return len + 1;
							}
							goto end;

						case 'H':
						case 'f':
							if (check_all_events(ses, EVENT_FLAG_CATCH, 0, 2, "CATCH VT100 CURSOR H", ntos(val[0]), ntos(val[1])))
							{
								pop_call();
								return len + 1;
							}
							goto end;

						case 'J':
							if (val[0] == 0)
							{
								if (check_all_events(ses, EVENT_FLAG_CATCH, 0, 0, "CATCH VT100 ERASE SCREEN BELOW"))
								{
									pop_call();
									return len + 1;
								}
							}
							if (val[0] == 1)
							{
								if (check_all_events(ses, EVENT_FLAG_CATCH, 0, 0, "CATCH VT100 ERASE SCREEN ABOVE"))
								{
									pop_call();
									return len + 1;
								}
							}
							if (val[0] == 2)
							{
								if (check_all_events(ses, EVENT_FLAG_CATCH, 0, 0, "CATCH VT100 ERASE SCREEN ALL"))
								{
									pop_call();
									return len + 1;
								}
							}
							if (val[0] == 3)
							{
								if (check_all_events(ses, EVENT_FLAG_CATCH, 0, 0, "CATCH VT100 ERASE SCREEN SAVED"))
								{
									pop_call();
									return len + 1;
								}
							}
							goto end;

						case 'K':
							if (val[0] == 0)
							{
								if (check_all_events(ses, EVENT_FLAG_CATCH, 0, 0, "CATCH VT100 ERASE LINE RIGHT"))
								{
									pop_call();
									return len + 1;
								}
							}
							if (val[0] == 1)
							{
								if (check_all_events(ses, EVENT_FLAG_CATCH, 0, 0, "CATCH VT100 ERASE LINE LEFT"))
								{
									pop_call();
									return len + 1;
								}
							}
							if (val[0] == 2)
							{
								if (check_all_events(ses, EVENT_FLAG_CATCH, 0, 0, "CATCH VT100 ERASE LINE ALL"))
								{
									pop_call();
									return len + 1;
								}
							}
							goto end;

						case 'Z':
							check_all_events(ses, EVENT_FLAG_VT100, 0, 0, "VT100 DECID");
							pop_call();
							return len + 1;

						default:
							goto end;
					}
				}
			}
			break;

		case ']':
			{
				int opt;
				char *osc = str_alloc_stack(0);

				if (str[2] == 'P')
				{
					if (cplen >= 10)
					{
						sprintf(osc, "%.*s", 8, str + 3);
							
						check_all_events(ses, EVENT_FLAG_VT100, 0, 1, "VT100 OSC COLOR PALETTE", osc);
							
						if (check_all_events(ses, EVENT_FLAG_CATCH, 0, 1, "CATCH VT100 OSC", osc))
						{
							pop_call();
							return 11;
						}
					}
					goto end;
				}
				else
				{
					opt = 0;

					for (skip = 2 ; cplen >= skip && skip < NUMBER_SIZE ; skip++)
					{
						if (!is_digit(str[skip]))
						{
							break;
						}
						opt = opt * 10 + (str[skip] - '0');
					}

					if (opt == 68 && cplen >= skip + 3)
					{
						if (str[skip] != ';')
						{
							pop_call();
							return 0;
						}

						if (str[skip + 1] == '2' && str[skip + 2] == ';')
						{
							pop_call();
							return 1;
						}
					}
					
					while (cplen >= skip && skip < BUFFER_SIZE)
					{
						if (str[skip] == ASCII_BEL)
						{
							break;
						}
						skip++;
					}
	
					snprintf(osc, BUFFER_SIZE, "%.*s", skip - 2, str + 2);

					check_all_events(ses, SUB_SEC|EVENT_FLAG_VT100, 0, 1, "VT100 OSC", osc);

					if (check_all_events(ses, SUB_SEC|EVENT_FLAG_CATCH, 0, 1, "CATCH VT100 OSC", osc))
					{
						pop_call();
						return skip + 1;
					}
				}
			}
			break;
	}

	end:

	pop_call();
	return 0;
}
