From 086d5674fddba275e43890f8fe9b3c45e691f5a1 Mon Sep 17 00:00:00 2001
From: deuce <>
Date: Wed, 31 Jan 2018 23:35:12 +0000
Subject: [PATCH] Major overhaul of ANSI parsing...

Instead of each control function having separate code to parse sequences,
use shared code which splits it into a structure and parses parameters.

This fixes all sorts of ANSI bugs with leading zeros, default values, etc.

Not all sequences have been updated yet, but I'll plug away at this slowly.

Now, instead of a couple sequences having broken parsing, and them all being
broken in different ways, all sequences can be broken in the same way all at
once.
---
 src/conio/cterm.c | 954 ++++++++++++++++++++++++++++------------------
 1 file changed, 587 insertions(+), 367 deletions(-)

diff --git a/src/conio/cterm.c b/src/conio/cterm.c
index 395f99a523..c7b68300ee 100644
--- a/src/conio/cterm.c
+++ b/src/conio/cterm.c
@@ -967,6 +967,228 @@ void CIOLIBCALL cterm_clearscreen(struct cterminal *cterm, char attr)
 		GOTOXY(1,1);
 }
 
+/*
+ * Parses an ESC sequence and returns it broken down
+ */
+
+struct esc_seq {
+	char c1_byte;			// Character after the ESC.  If '[', ctrl_func and param_str will be non-NULL.
+	char final_byte;		// Final Byte (or NULL if c1 function);
+	char *ctrl_func;		// Intermediate Bytes and Final Byte as NULL-terminated string.
+	char *param_str;		// The parameter bytes
+	int param_count;		// The number of parameters, or -1 if parameters were not parsed.
+	str_list_t param;		// The parameters as strings
+	uint64_t *param_int;	// The parameter bytes parsed as integers UINT64_MAX for default value.
+};
+
+static bool parse_parameters(struct esc_seq *seq)
+{
+	char *p;
+	char *dup;
+	char *start = NULL;
+	bool last_was_sc = false;
+	int i;
+
+	if (seq == NULL)
+		return false;
+	if (seq->param_str == NULL)
+		return false;
+
+	dup = strdup(seq->param_str);
+	if (dup == NULL)
+		return false;
+	p = dup;
+
+	// First, skip past any private extension indicator...
+	if (*p >= 0x3c && *p <= 0x3f)
+		p++;
+
+	seq->param_count = 0;
+
+	seq->param = strListInit();
+
+	for (; *p; p++) {
+		/* Ensure it's a legal value */
+		if (*p < 0x30 || *p > 0x3b) {
+			seq->param_count = -1;
+			strListFree(&seq->param);
+			seq->param = NULL;
+			free(dup);
+			return false;
+		}
+		/* Mark the start of the parameter */
+		if (start == NULL)
+			start = p;
+
+		if (*p == ';') {
+			/* End of parameter, add to string list */
+			last_was_sc = true;
+			*p = 0;
+			while(*start == '0' && start[1])
+				start++;
+			strListAppend(&seq->param, start, seq->param_count);
+			seq->param_count++;
+			start = NULL;
+		}
+		else
+			last_was_sc = false;
+	}
+	/* If the string ended with a semi-colon, there's a final zero-length parameter */
+	if (last_was_sc) {
+		strListAppend(&seq->param, "", seq->param_count);
+		seq->param_count++;
+	}
+	else if (start) {
+		/* End of parameter, add to string list */
+		last_was_sc = true;
+		*p = 0;
+		while(*start == '0' && start[1])
+			start++;
+		strListAppend(&seq->param, start, seq->param_count);
+		seq->param_count++;
+	}
+
+	seq->param_int = malloc(seq->param_count * sizeof(seq->param_int[0]));
+	if (seq->param_int == NULL) {
+			seq->param_count = -1;
+			strListFree(&seq->param);
+			seq->param = NULL;
+			free(dup);
+			return false;
+	}
+
+	/* Now, parse all the integer values... */
+	for (i=0; i<seq->param_count; i++) {
+		if (seq->param[i][0] == 0 || seq->param[i][0] == ':') {
+			seq->param_int[i] = UINT64_MAX;
+			continue;
+		}
+		seq->param_int[i] = strtoull(seq->param[i], NULL, 10);
+		if (seq->param_int[i] == ULLONG_MAX) {
+			seq->param_count = -1;
+			strListFree(&seq->param);
+			seq->param = NULL;
+			FREE_AND_NULL(seq->param_int);
+			free(dup);
+			return false;
+		}
+	}
+
+	free(dup);
+	return true;
+}
+
+static void seq_default(struct esc_seq *seq, int index, uint64_t val)
+{
+	char	tmpnum[24];
+
+	// Params not parsed
+	if (seq->param_count == -1)
+		return;
+
+	/* Do we need to add on to get defaults? */
+	if (index >= seq->param_count) {
+		uint64_t *np;
+
+		np = realloc(seq->param_int, (index + 1) * sizeof(seq->param_int[0]));
+		if (np == NULL)
+			return;
+		seq->param_int = np;
+		for (; seq->param_count <= index+1; seq->param_count++) {
+			if (seq->param_count == index) {
+				seq->param_int[index] = val;
+				sprintf(tmpnum, "%" PRIu64, val);
+				strListAppend(&seq->param, tmpnum, seq->param_count);
+			}
+			else {
+				seq->param_int[seq->param_count] = UINT64_MAX;
+				strListAppend(&seq->param, "", seq->param_count);
+			}
+		}
+		return;
+	}
+	if (seq->param_int[index] == UINT64_MAX) {
+		seq->param_int[index] = val;
+		sprintf(tmpnum, "%" PRIu64, val);
+		strListReplace(seq->param, index, tmpnum);
+	}
+}
+
+static void free_sequence(struct esc_seq * seq)
+{
+	if (seq == NULL)
+		return;
+	if (seq->param)
+		strListFree(&seq->param);
+	FREE_AND_NULL(seq->param_int);
+	seq->param_count = -1;
+	FREE_AND_NULL(seq->param_str);
+	FREE_AND_NULL(seq->ctrl_func);
+	free(seq);
+}
+
+static struct esc_seq *parse_sequence(const char *seq)
+{
+	struct esc_seq *ret;
+
+	ret = calloc(1, sizeof(struct esc_seq));
+	if (ret == NULL)
+		return ret;
+	ret->param_count = -1;
+
+	/* Check that it's part of C1 set */
+	if (seq[0] < 0x40 || seq[0] > 0x5f)
+		goto fail;
+
+	ret->c1_byte = seq[0];
+
+	/* Check if it's CSI */
+	if (seq[0] == '[') {
+		size_t parameter_len;
+		size_t intermediate_len;
+
+		parameter_len = strspn(&seq[1], "0123456789:;<=>?");
+		ret->param_str = malloc(parameter_len + 1);
+		if (!ret->param_str)
+			goto fail;
+		memcpy(ret->param_str, &seq[1], parameter_len);
+		ret->param_str[parameter_len] = 0;
+
+		intermediate_len = strspn(&seq[1+parameter_len], " !\"#$%&'()*+,-./");
+		if (seq[1+parameter_len+intermediate_len] < 0x40 || seq[1+parameter_len+intermediate_len] > 0x7e)
+			goto fail;
+		ret->ctrl_func = malloc(intermediate_len + 2);
+		if (!ret->ctrl_func)
+			goto fail;
+		memcpy(ret->ctrl_func, &seq[1+parameter_len], intermediate_len);
+
+		ret->final_byte = ret->ctrl_func[intermediate_len] = seq[1+parameter_len+intermediate_len];
+		/* Validate final byte */
+		if (ret->final_byte < 0x40 || ret->final_byte > 0x7e)
+			goto fail;
+
+		ret->ctrl_func[intermediate_len+1] = 0;
+
+		/*
+		 * Is this a private extension?
+		 * If so, return now, the caller can parse the parameter sequence itself
+		 * if the standard format is used.
+		 */
+		if (ret->param_str[0] >= 0x3c && ret->param_str[0] <= 0x3f)
+			return ret;
+
+		if (!parse_parameters(ret))
+			goto fail;
+	}
+	return ret;
+
+fail:
+	FREE_AND_NULL(ret->ctrl_func);
+	FREE_AND_NULL(ret->param_str);
+	free(ret);
+	return NULL;
+}
+
 static void do_ansi(struct cterminal *cterm, char *retbuf, size_t retsize, int *speed)
 {
 	char	*p;
@@ -976,13 +1198,18 @@ static void do_ansi(struct cterminal *cterm, char *retbuf, size_t retsize, int *
 	int		row,col;
 	int		max_row;
 	struct text_info ti;
+	struct esc_seq *seq;
 
-	switch(cterm->escbuf[0]) {
+	seq = parse_sequence(cterm->escbuf);
+	if (seq == NULL)
+		return;
+
+	switch(seq->c1_byte) {
 		case '[':
 			/* ANSI stuff */
 			p=cterm->escbuf+strlen(cterm->escbuf)-1;
-			if(cterm->escbuf[1]>=60 && cterm->escbuf[1] <= 63) {	/* Private extensions */
-				switch(*p) {
+			if(seq->param_str[0]>=60 && seq->param_str[0] <= 63) {	/* Private extensions */
+				switch(seq->final_byte) {
 					case 'M':
 						if(cterm->escbuf[1] == '=') {	/* ANSI Music setup */
 							i=strtoul(cterm->escbuf+2,NULL,10);
@@ -1000,133 +1227,151 @@ static void do_ansi(struct cterminal *cterm, char *retbuf, size_t retsize, int *
 						}
 						break;
 					case 'h':
-						if(!strcmp(cterm->escbuf,"[?6h")) {
-							cterm->origin_mode=true;
-							GOTOXY(1,cterm->top_margin);
-						}
-						if(!strcmp(cterm->escbuf,"[?7h")) {
-							cterm->autowrap=true;
-						}
-						if(!strcmp(cterm->escbuf,"[?25h")) {
-							cterm->cursor=_NORMALCURSOR;
-							SETCURSORTYPE(cterm->cursor);
-						}
-						if(!strcmp(cterm->escbuf,"[?31h")) {
-							i=GETVIDEOFLAGS();
-							i|=CIOLIB_VIDEO_ALTCHARS;
-							SETVIDEOFLAGS(i);
-						}
-						if(!strcmp(cterm->escbuf,"[?32h")) {
-							i=GETVIDEOFLAGS();
-							i|=CIOLIB_VIDEO_NOBRIGHT;
-							SETVIDEOFLAGS(i);
-						}
-						if(!strcmp(cterm->escbuf,"[?33h")) {
-							i=GETVIDEOFLAGS();
-							i|=CIOLIB_VIDEO_BGBRIGHT;
-							SETVIDEOFLAGS(i);
-						}
-						if(!strcmp(cterm->escbuf,"[?34h")) {
-							i=GETVIDEOFLAGS();
-							i|=CIOLIB_VIDEO_BLINKALTCHARS;
-							SETVIDEOFLAGS(i);
-						}
-						if(!strcmp(cterm->escbuf,"[?35h")) {
-							i=GETVIDEOFLAGS();
-							i|=CIOLIB_VIDEO_NOBLINK;
-							SETVIDEOFLAGS(i);
+						if (seq->param_str[0] == '?' && parse_parameters(seq)) {
+							for (i=0; i<seq->param_count; i++) {
+								switch(seq->param_int[i]) {
+									case 6:
+										cterm->origin_mode=true;
+										GOTOXY(1,cterm->top_margin);
+										break;
+									case 7:
+										cterm->autowrap=true;
+										break;
+									case 25:
+										cterm->cursor=_NORMALCURSOR;
+										SETCURSORTYPE(cterm->cursor);
+										break;
+									case 31:
+										i=GETVIDEOFLAGS();
+										i|=CIOLIB_VIDEO_ALTCHARS;
+										SETVIDEOFLAGS(i);
+										break;
+									case 32:
+										i=GETVIDEOFLAGS();
+										i|=CIOLIB_VIDEO_NOBRIGHT;
+										SETVIDEOFLAGS(i);
+										break;
+									case 33:
+										i=GETVIDEOFLAGS();
+										i|=CIOLIB_VIDEO_BGBRIGHT;
+										SETVIDEOFLAGS(i);
+										break;
+									case 34:
+										i=GETVIDEOFLAGS();
+										i|=CIOLIB_VIDEO_BLINKALTCHARS;
+										SETVIDEOFLAGS(i);
+										break;
+									case 35:
+										i=GETVIDEOFLAGS();
+										i|=CIOLIB_VIDEO_NOBLINK;
+										SETVIDEOFLAGS(i);
+								}
+							}
 						}
-						if(!strcmp(cterm->escbuf,"[=255h"))
+						else if(!strcmp(seq->param_str,"=255"))
 							cterm->doorway_mode=1;
 						break;
 					case 'l':
-						if(!strcmp(cterm->escbuf,"[?6l")) {
-							cterm->origin_mode=false;
-							GOTOXY(1,1);
-						}
-						if(!strcmp(cterm->escbuf,"[?7l")) {
-							cterm->autowrap=false;
-						}
-						if(!strcmp(cterm->escbuf,"[?25l")) {
-							cterm->cursor=_NOCURSOR;
-							SETCURSORTYPE(cterm->cursor);
-						}
-						if(!strcmp(cterm->escbuf,"[?31l")) {
-							i=GETVIDEOFLAGS();
-							i&=~CIOLIB_VIDEO_ALTCHARS;
-							SETVIDEOFLAGS(i);
-						}
-						if(!strcmp(cterm->escbuf,"[?32l")) {
-							i=GETVIDEOFLAGS();
-							i&=~CIOLIB_VIDEO_NOBRIGHT;
-							SETVIDEOFLAGS(i);
-						}
-						if(!strcmp(cterm->escbuf,"[?33l")) {
-							i=GETVIDEOFLAGS();
-							i&=~CIOLIB_VIDEO_BGBRIGHT;
-							SETVIDEOFLAGS(i);
-						}
-						if(!strcmp(cterm->escbuf,"[?34l")) {
-							i=GETVIDEOFLAGS();
-							i&=~CIOLIB_VIDEO_BLINKALTCHARS;
-							SETVIDEOFLAGS(i);
-						}
-						if(!strcmp(cterm->escbuf,"[?35l")) {
-							i=GETVIDEOFLAGS();
-							i&=~CIOLIB_VIDEO_NOBLINK;
-							SETVIDEOFLAGS(i);
+						if (seq->param_str[0] == '?' && parse_parameters(seq)) {
+							for (i=0; i<seq->param_count; i++) {
+								switch(seq->param_int[i]) {
+									case 6:
+										cterm->origin_mode=false;
+										GOTOXY(1,1);
+										break;
+									case 7:
+										cterm->autowrap=false;
+										break;
+									case 25:
+										cterm->cursor=_NOCURSOR;
+										SETCURSORTYPE(cterm->cursor);
+										break;
+									case 31:
+										i=GETVIDEOFLAGS();
+										i&=~CIOLIB_VIDEO_ALTCHARS;
+										SETVIDEOFLAGS(i);
+										break;
+									case 32:
+										i=GETVIDEOFLAGS();
+										i&=~CIOLIB_VIDEO_NOBRIGHT;
+										SETVIDEOFLAGS(i);
+										break;
+									case 33:
+										i=GETVIDEOFLAGS();
+										i&=~CIOLIB_VIDEO_BGBRIGHT;
+										SETVIDEOFLAGS(i);
+										break;
+									case 34:
+										i=GETVIDEOFLAGS();
+										i&=~CIOLIB_VIDEO_BLINKALTCHARS;
+										SETVIDEOFLAGS(i);
+										break;
+									case 35:
+										i=GETVIDEOFLAGS();
+										i&=~CIOLIB_VIDEO_NOBLINK;
+										SETVIDEOFLAGS(i);
+										break;
+								}
+							}
 						}
-						if(!strcmp(cterm->escbuf,"[=255l"))
+						else if(!strcmp(seq->param_str,"=255"))
 							cterm->doorway_mode=0;
 						break;
 					case 'n':	/* Query (extended) state information */
-						if(retbuf == NULL)
-							break;
-						tmp[0] = 0;
-						if ((strcmp(cterm->escbuf,"[=n") == 0) || (strcmp(cterm->escbuf,"[=1n") == 0)) {
-							sprintf(tmp, "\x1b[=1;%u;%u;%u;%u;%u;%un"
-								,CONIO_FIRST_FREE_FONT
-								,(uint8_t)cterm->setfont_result
-								,(uint8_t)cterm->altfont[0]
-								,(uint8_t)cterm->altfont[1]
-								,(uint8_t)cterm->altfont[2]
-								,(uint8_t)cterm->altfont[3]
-							);
-						}
-						else if (!strcmp(cterm->escbuf,"[=2n")) {
-							int vidflags = GETVIDEOFLAGS();
-							strcpy(tmp, "\x1b[=2");
-							if(cterm->origin_mode)
-								strcat(tmp, ";6");
-							if(cterm->autowrap)
-								strcat(tmp, ";7");
-							if(cterm->cursor == _NORMALCURSOR)
-								strcat(tmp, ";25");
-							if(vidflags & CIOLIB_VIDEO_ALTCHARS)
-								strcat(tmp, ";31");
-							if(vidflags & CIOLIB_VIDEO_NOBRIGHT)
-								strcat(tmp, ";32");
-							if(vidflags & CIOLIB_VIDEO_BGBRIGHT)
-								strcat(tmp, ";33");
-							if(vidflags & CIOLIB_VIDEO_BLINKALTCHARS)
-								strcat(tmp, ";34");
-							if(vidflags & CIOLIB_VIDEO_NOBLINK)
-								strcat(tmp, ";35");
-							if (strlen(tmp) == 4) {	// Nothing set
-								strcat(tmp, ";");
+						if (seq->param_str[0] == '=' && parse_parameters(seq)) {
+							int vidflags;
+
+							if(retbuf == NULL)
+								break;
+							tmp[0] = 0;
+							if (seq->param_count > 1)
+								break;
+							seq_default(seq, 0, 1);
+							switch(seq->param_int[0]) {
+								case 1:
+									sprintf(tmp, "\x1b[=1;%u;%u;%u;%u;%u;%un"
+										,CONIO_FIRST_FREE_FONT
+										,(uint8_t)cterm->setfont_result
+										,(uint8_t)cterm->altfont[0]
+										,(uint8_t)cterm->altfont[1]
+										,(uint8_t)cterm->altfont[2]
+										,(uint8_t)cterm->altfont[3]
+									);
+									break;
+								case 2:
+									vidflags = GETVIDEOFLAGS();
+									strcpy(tmp, "\x1b[=2");
+									if(cterm->origin_mode)
+									strcat(tmp, ";6");
+									if(cterm->autowrap)
+										strcat(tmp, ";7");
+									if(cterm->cursor == _NORMALCURSOR)
+										strcat(tmp, ";25");
+									if(vidflags & CIOLIB_VIDEO_ALTCHARS)
+										strcat(tmp, ";31");
+									if(vidflags & CIOLIB_VIDEO_NOBRIGHT)
+										strcat(tmp, ";32");
+									if(vidflags & CIOLIB_VIDEO_BGBRIGHT)
+										strcat(tmp, ";33");
+									if(vidflags & CIOLIB_VIDEO_BLINKALTCHARS)
+										strcat(tmp, ";34");
+									if(vidflags & CIOLIB_VIDEO_NOBLINK)
+										strcat(tmp, ";35");
+									if (strlen(tmp) == 4) {	// Nothing set
+										strcat(tmp, ";");
+									}
+									strcat(tmp, "n");
+									break;
 							}
-							strcat(tmp, "n");
 						}
 						if(*tmp && strlen(retbuf) + strlen(tmp) < retsize)
 							strcat(retbuf, tmp);
 						break;
 					case 's':
-						if(cterm->escbuf[1] == '?') {
-							*p=0;
-							p2=cterm->escbuf+2;
+						if (seq->param_str[0] == '?' && parse_parameters(seq)) {
 							GETTEXTINFO(&ti);
 							i=GETVIDEOFLAGS();
-							if(!*p2) {
+							if(seq->param_count == 0) {
 								/* All the save stuff... */
 								cterm->saved_mode_mask |= (CTERM_SAVEMODE_AUTOWRAP|CTERM_SAVEMODE_CURSOR|CTERM_SAVEMODE_ALTCHARS|CTERM_SAVEMODE_NOBRIGHT|CTERM_SAVEMODE_BGBRIGHT|CTERM_SAVEMODE_ORIGIN);
 								cterm->saved_mode &= ~(CTERM_SAVEMODE_AUTOWRAP|CTERM_SAVEMODE_CURSOR|CTERM_SAVEMODE_ALTCHARS|CTERM_SAVEMODE_NOBRIGHT|CTERM_SAVEMODE_BGBRIGHT|CTERM_SAVEMODE_ORIGIN);
@@ -1140,58 +1385,59 @@ static void do_ansi(struct cterminal *cterm, char *retbuf, size_t retsize, int *
 								cterm->saved_mode |= (cterm->origin_mode)?CTERM_SAVEMODE_ORIGIN:0;
 								break;
 							}
-							while((p=strtok(p2,";"))!=NULL) {
-								p2=NULL;
-								if(!strcmp(p,"6")) {
-									cterm->saved_mode_mask |= CTERM_SAVEMODE_ORIGIN;
-									cterm->saved_mode &= ~(CTERM_SAVEMODE_AUTOWRAP|CTERM_SAVEMODE_CURSOR|CTERM_SAVEMODE_ALTCHARS|CTERM_SAVEMODE_NOBRIGHT|CTERM_SAVEMODE_BGBRIGHT|CTERM_SAVEMODE_ORIGIN);
-									cterm->saved_mode |= cterm->origin_mode?CTERM_SAVEMODE_ORIGIN:0;
-								}
-								if(!strcmp(p,"7")) {
-									cterm->saved_mode_mask |= CTERM_SAVEMODE_AUTOWRAP;
-									cterm->saved_mode &= ~(CTERM_SAVEMODE_AUTOWRAP|CTERM_SAVEMODE_CURSOR|CTERM_SAVEMODE_ALTCHARS|CTERM_SAVEMODE_NOBRIGHT|CTERM_SAVEMODE_BGBRIGHT);
-									cterm->saved_mode |= cterm->autowrap?CTERM_SAVEMODE_AUTOWRAP:0;
-								}
-								if(!strcmp(p,"25")) {
-									cterm->saved_mode_mask |= CTERM_SAVEMODE_CURSOR;
-									cterm->saved_mode &= ~(CTERM_SAVEMODE_CURSOR);
-									cterm->saved_mode |= (cterm->cursor==_NORMALCURSOR)?CTERM_SAVEMODE_CURSOR:0;
-								}
-								if(!strcmp(p,"31")) {
-									cterm->saved_mode_mask |= CTERM_SAVEMODE_ALTCHARS;
-									cterm->saved_mode &= ~(CTERM_SAVEMODE_ALTCHARS);
-									cterm->saved_mode |= (i&CIOLIB_VIDEO_ALTCHARS)?CTERM_SAVEMODE_ALTCHARS:0;
-								}
-								if(!strcmp(p,"32")) {
-									cterm->saved_mode_mask |= CTERM_SAVEMODE_NOBRIGHT;
-									cterm->saved_mode &= ~(CTERM_SAVEMODE_NOBRIGHT);
-									cterm->saved_mode |= (i&CIOLIB_VIDEO_NOBRIGHT)?CTERM_SAVEMODE_NOBRIGHT:0;
-								}
-								if(!strcmp(p,"33")) {
-									cterm->saved_mode_mask |= CTERM_SAVEMODE_BGBRIGHT;
-									cterm->saved_mode &= ~(CTERM_SAVEMODE_BGBRIGHT);
-									cterm->saved_mode |= (i&CIOLIB_VIDEO_BGBRIGHT)?CTERM_SAVEMODE_BGBRIGHT:0;
-								}
-								if(!strcmp(p,"34")) {
-									cterm->saved_mode_mask |= CTERM_SAVEMODE_BLINKALTCHARS;
-									cterm->saved_mode &= ~(CTERM_SAVEMODE_BLINKALTCHARS);
-									cterm->saved_mode |= (i&CIOLIB_VIDEO_BLINKALTCHARS)?CTERM_SAVEMODE_BLINKALTCHARS:0;
-								}
-								if(!strcmp(p,"35")) {
-									cterm->saved_mode_mask |= CTERM_SAVEMODE_NOBLINK;
-									cterm->saved_mode &= ~(CTERM_SAVEMODE_NOBLINK);
-									cterm->saved_mode |= (i&CIOLIB_VIDEO_NOBLINK)?CTERM_SAVEMODE_NOBLINK:0;
+							else {
+								for (i=0; i<seq->param_count; i++) {
+									switch(seq->param_int[i]) {
+										case 6:
+											cterm->saved_mode_mask |= CTERM_SAVEMODE_ORIGIN;
+											cterm->saved_mode &= ~(CTERM_SAVEMODE_AUTOWRAP|CTERM_SAVEMODE_CURSOR|CTERM_SAVEMODE_ALTCHARS|CTERM_SAVEMODE_NOBRIGHT|CTERM_SAVEMODE_BGBRIGHT|CTERM_SAVEMODE_ORIGIN);
+											cterm->saved_mode |= cterm->origin_mode?CTERM_SAVEMODE_ORIGIN:0;
+											break;
+										case 7:
+											cterm->saved_mode_mask |= CTERM_SAVEMODE_AUTOWRAP;
+											cterm->saved_mode &= ~(CTERM_SAVEMODE_AUTOWRAP|CTERM_SAVEMODE_CURSOR|CTERM_SAVEMODE_ALTCHARS|CTERM_SAVEMODE_NOBRIGHT|CTERM_SAVEMODE_BGBRIGHT);
+											cterm->saved_mode |= cterm->autowrap?CTERM_SAVEMODE_AUTOWRAP:0;
+											break;
+										case 25:
+											cterm->saved_mode_mask |= CTERM_SAVEMODE_CURSOR;
+											cterm->saved_mode &= ~(CTERM_SAVEMODE_CURSOR);
+											cterm->saved_mode |= (cterm->cursor==_NORMALCURSOR)?CTERM_SAVEMODE_CURSOR:0;
+											break;
+										case 31:
+											cterm->saved_mode_mask |= CTERM_SAVEMODE_ALTCHARS;
+											cterm->saved_mode &= ~(CTERM_SAVEMODE_ALTCHARS);
+											cterm->saved_mode |= (i&CIOLIB_VIDEO_ALTCHARS)?CTERM_SAVEMODE_ALTCHARS:0;
+											break;
+										case 32:
+											cterm->saved_mode_mask |= CTERM_SAVEMODE_NOBRIGHT;
+											cterm->saved_mode &= ~(CTERM_SAVEMODE_NOBRIGHT);
+											cterm->saved_mode |= (i&CIOLIB_VIDEO_NOBRIGHT)?CTERM_SAVEMODE_NOBRIGHT:0;
+											break;
+										case 33:
+											cterm->saved_mode_mask |= CTERM_SAVEMODE_BGBRIGHT;
+											cterm->saved_mode &= ~(CTERM_SAVEMODE_BGBRIGHT);
+											cterm->saved_mode |= (i&CIOLIB_VIDEO_BGBRIGHT)?CTERM_SAVEMODE_BGBRIGHT:0;
+											break;
+										case 34:
+											cterm->saved_mode_mask |= CTERM_SAVEMODE_BLINKALTCHARS;
+											cterm->saved_mode &= ~(CTERM_SAVEMODE_BLINKALTCHARS);
+											cterm->saved_mode |= (i&CIOLIB_VIDEO_BLINKALTCHARS)?CTERM_SAVEMODE_BLINKALTCHARS:0;
+											break;
+										case 35:
+											cterm->saved_mode_mask |= CTERM_SAVEMODE_NOBLINK;
+											cterm->saved_mode &= ~(CTERM_SAVEMODE_NOBLINK);
+											cterm->saved_mode |= (i&CIOLIB_VIDEO_NOBLINK)?CTERM_SAVEMODE_NOBLINK:0;
+											break;
+									}
 								}
 							}
 						}
 						break;
 					case 'u':
-						if(cterm->escbuf[1] == '?') {
-							*p=0;
-							p2=cterm->escbuf+2;
+						if (seq->param_str[0] == '?' && parse_parameters(seq)) {
 							GETTEXTINFO(&ti);
 							i=GETVIDEOFLAGS();
-							if(!*p2) {
+							if(seq->param_count == 0) {
 								/* All the save stuff... */
 								if(cterm->saved_mode_mask & CTERM_SAVEMODE_ORIGIN)
 									cterm->origin_mode=(cterm->saved_mode & CTERM_SAVEMODE_ORIGIN) ? true : false;
@@ -1234,89 +1480,82 @@ static void do_ansi(struct cterminal *cterm, char *retbuf, size_t retsize, int *
 								SETVIDEOFLAGS(i);
 								break;
 							}
-							while((p=strtok(p2,";"))!=NULL) {
-								p2=NULL;
-								if(!strcmp(p,"6")) {
-									if(cterm->saved_mode_mask & CTERM_SAVEMODE_ORIGIN)
-										cterm->origin_mode=(cterm->saved_mode & CTERM_SAVEMODE_ORIGIN) ? true : false;
-								}
-								if(!strcmp(p,"7")) {
-									if(cterm->saved_mode_mask & CTERM_SAVEMODE_AUTOWRAP)
-										cterm->autowrap=(cterm->saved_mode & CTERM_SAVEMODE_AUTOWRAP) ? true : false;
-								}
-								if(!strcmp(p,"25")) {
-									if(cterm->saved_mode_mask & CTERM_SAVEMODE_CURSOR) {
-										cterm->cursor = (cterm->saved_mode & CTERM_SAVEMODE_CURSOR) ? _NORMALCURSOR : _NOCURSOR;
-										SETCURSORTYPE(cterm->cursor);
-									}
-								}
-								if(!strcmp(p,"31")) {
-									if(cterm->saved_mode_mask & CTERM_SAVEMODE_ALTCHARS) {
-										if(cterm->saved_mode & CTERM_SAVEMODE_ALTCHARS)
-											i |= CIOLIB_VIDEO_ALTCHARS;
-										else
-											i &= ~CIOLIB_VIDEO_ALTCHARS;
-										SETVIDEOFLAGS(i);
-									}
-								}
-								if(!strcmp(p,"32")) {
-									if(cterm->saved_mode_mask & CTERM_SAVEMODE_NOBRIGHT) {
-										if(cterm->saved_mode & CTERM_SAVEMODE_NOBRIGHT)
-											i |= CIOLIB_VIDEO_NOBRIGHT;
-										else
-											i &= ~CIOLIB_VIDEO_NOBRIGHT;
-										SETVIDEOFLAGS(i);
-									}
-								}
-								if(!strcmp(p,"33")) {
-									if(cterm->saved_mode_mask & CTERM_SAVEMODE_BGBRIGHT) {
-										if(cterm->saved_mode & CTERM_SAVEMODE_BGBRIGHT)
-											i |= CIOLIB_VIDEO_BGBRIGHT;
-										else
-											i &= ~CIOLIB_VIDEO_BGBRIGHT;
-										SETVIDEOFLAGS(i);
-									}
-								}
-								if(!strcmp(p,"34")) {
-									if(cterm->saved_mode_mask & CTERM_SAVEMODE_BLINKALTCHARS) {
-										if(cterm->saved_mode & CTERM_SAVEMODE_BLINKALTCHARS)
-											i |= CIOLIB_VIDEO_BLINKALTCHARS;
-										else
-											i &= ~CIOLIB_VIDEO_BLINKALTCHARS;
-										SETVIDEOFLAGS(i);
-									}
-								}
-								if(!strcmp(p,"35")) {
-									if(cterm->saved_mode_mask & CTERM_SAVEMODE_NOBLINK) {
-										if(cterm->saved_mode & CTERM_SAVEMODE_NOBLINK)
-											i |= CIOLIB_VIDEO_NOBLINK;
-										else
-											i &= ~CIOLIB_VIDEO_NOBLINK;
-										SETVIDEOFLAGS(i);
+							else {
+								for (i=0; i<seq->param_count; i++) {
+									switch(seq->param_int[i]) {
+										case 6:
+											if(cterm->saved_mode_mask & CTERM_SAVEMODE_ORIGIN)
+												cterm->origin_mode=(cterm->saved_mode & CTERM_SAVEMODE_ORIGIN) ? true : false;
+											break;
+										case 7:
+											if(cterm->saved_mode_mask & CTERM_SAVEMODE_AUTOWRAP)
+												cterm->autowrap=(cterm->saved_mode & CTERM_SAVEMODE_AUTOWRAP) ? true : false;
+											break;
+										case 25:
+											if(cterm->saved_mode_mask & CTERM_SAVEMODE_CURSOR) {
+												cterm->cursor = (cterm->saved_mode & CTERM_SAVEMODE_CURSOR) ? _NORMALCURSOR : _NOCURSOR;
+												SETCURSORTYPE(cterm->cursor);
+											}
+											break;
+										case 31:
+											if(cterm->saved_mode_mask & CTERM_SAVEMODE_ALTCHARS) {
+												if(cterm->saved_mode & CTERM_SAVEMODE_ALTCHARS)
+													i |= CIOLIB_VIDEO_ALTCHARS;
+												else
+													i &= ~CIOLIB_VIDEO_ALTCHARS;
+												SETVIDEOFLAGS(i);
+											}
+											break;
+										case 32:
+											if(cterm->saved_mode_mask & CTERM_SAVEMODE_NOBRIGHT) {
+												if(cterm->saved_mode & CTERM_SAVEMODE_NOBRIGHT)
+													i |= CIOLIB_VIDEO_NOBRIGHT;
+												else
+													i &= ~CIOLIB_VIDEO_NOBRIGHT;
+												SETVIDEOFLAGS(i);
+											}
+											break;
+										case 33:
+											if(cterm->saved_mode_mask & CTERM_SAVEMODE_BGBRIGHT) {
+												if(cterm->saved_mode & CTERM_SAVEMODE_BGBRIGHT)
+													i |= CIOLIB_VIDEO_BGBRIGHT;
+												else
+													i &= ~CIOLIB_VIDEO_BGBRIGHT;
+												SETVIDEOFLAGS(i);
+											}
+											break;
+										case 34:
+											if(cterm->saved_mode_mask & CTERM_SAVEMODE_BLINKALTCHARS) {
+												if(cterm->saved_mode & CTERM_SAVEMODE_BLINKALTCHARS)
+													i |= CIOLIB_VIDEO_BLINKALTCHARS;
+												else
+													i &= ~CIOLIB_VIDEO_BLINKALTCHARS;
+												SETVIDEOFLAGS(i);
+											}
+											break;
+										case 35:
+											if(cterm->saved_mode_mask & CTERM_SAVEMODE_NOBLINK) {
+												if(cterm->saved_mode & CTERM_SAVEMODE_NOBLINK)
+													i |= CIOLIB_VIDEO_NOBLINK;
+												else
+													i &= ~CIOLIB_VIDEO_NOBLINK;
+												SETVIDEOFLAGS(i);
+											}
+											break;
 									}
 								}
 							}
 						}
 						break;
 					case '{':
-						if(cterm->escbuf[1] == '=') {	/* Font loading */
-							i=255;
-							j=0;
-							if(strlen(cterm->escbuf)>2) {
-								if((p=strtok(cterm->escbuf+2,";"))!=NULL) {
-									i=strtoul(p,NULL,10);
-									if(!i && cterm->escbuf[2] != '0')
-										i=255;
-									if((p=strtok(NULL,";"))!=NULL) {
-										j=strtoul(p,NULL,10);
-									}
-								}
-							}
-							if(i>255)
+						if(seq->param_str[0] == '=' && parse_parameters(seq)) {	/* Font loading */
+							seq_default(seq, 0, 255);
+							seq_default(seq, 1, 0);
+							if(seq->param_int[0]>255)
 								break;
 							cterm->font_read=0;
-							cterm->font_slot=i;
-							switch(j) {
+							cterm->font_slot=seq->param_int[0];
+							switch(seq->param_int[1]) {
 								case 0:
 									cterm->font_size=4096;
 									break;
@@ -1335,25 +1574,108 @@ static void do_ansi(struct cterminal *cterm, char *retbuf, size_t retsize, int *
 				}
 				break;
 			}
-			switch(*p) {
+			else if (seq->ctrl_func[1]) {	// Private control function
+				// Font Select
+				if (strcmp(seq->ctrl_func, " D") == 0) {
+					seq_default(seq, 0, 0);
+					seq_default(seq, 1, 0);
+					switch(seq->param_int[0]) {
+						case 0:	/* Four fonts are currently supported */
+						case 1:
+						case 2:
+						case 3:
+							cterm->setfont_result = SETFONT(seq->param_int[1],FALSE,seq->param_int[0]+1);
+							if(cterm->setfont_result == CIOLIB_SETFONT_SUCCESS)
+								cterm->altfont[seq->param_int[0]] = seq->param_int[1];
+							break;
+					}
+				}
+				// Communication speed
+				else if (strcmp(seq->ctrl_func, "*r") == 0) {
+					/*
+					 * Ps1 			Comm Line 		Ps2 		Communication Speed
+					 * none, 0, 1 	Host Transmit 	none, 0 	Use default speed.
+					 * 2		 	Host Receive 	1 			300
+					 * 3 			Printer 		2 			600
+					 * 4		 	Modem Hi 		3 			1200
+					 * 5		 	Modem Lo 		4 			2400
+					 * 								5 			4800
+					 * 								6 			9600
+					 * 								7 			19200
+					 * 								8 			38400
+					 * 								9 			57600
+					 * 								10 			76800
+					 * 								11 			115200
+					 */
+					int newspeed=-1;
+
+					seq_default(seq, 0, 0);
+					seq_default(seq, 1, 0);
+
+					if (seq->param_int[0] < 2) {
+						switch(seq->param_int[1]) {
+							case 0:
+								newspeed=0;
+								break;
+							case 1:
+								newspeed=300;
+								break;
+							case 2:
+								newspeed=600;
+								break;
+							case 3:
+								newspeed=1200;
+								break;
+							case 4:
+								newspeed=2400;
+								break;
+							case 5:
+								newspeed=4800;
+								break;
+							case 6:
+								newspeed=9600;
+								break;
+							case 7:
+								newspeed=19200;
+								break;
+							case 8:
+								newspeed=38400;
+								break;
+							case 9:
+								newspeed=57600;
+								break;
+							case 10:
+								newspeed=76800;
+								break;
+							case 11:
+								newspeed=115200;
+								break;
+						}
+					}
+					if(newspeed >= 0)
+						*speed = newspeed;
+				}
+				break;
+			}
+			switch(seq->final_byte) {
 				case '@':	/* Insert Char */
 					i=WHEREX();
 					j=WHEREY();
-					k=strtoul(cterm->escbuf+1,NULL,10);
-					if(k<1)
-						k=1;
-					if(k>cterm->width - j)
-						k=cterm->width - j;
-					MOVETEXT(cterm->x+i-1,cterm->y+j-1,cterm->x+cterm->width-1-k,cterm->y+j-1,cterm->x+i-1+k,cterm->y+j-1);
-					for(l=0; l< k; l++)
+					seq_default(seq, 0, 1);
+					if(seq->param_int[0] < 1)
+						seq->param_int[0] = 1;
+					if(seq->param_int[0] > cterm->width - j)
+						seq->param_int[0] = cterm->width - j;
+					MOVETEXT(cterm->x+i-1,cterm->y+j-1,cterm->x+cterm->width-1-seq->param_int[0],cterm->y+j-1,cterm->x+i-1+seq->param_int[0],cterm->y+j-1);
+					for(l=0; l < seq->param_int[0]; l++)
 						PUTCH(' ');
 					GOTOXY(i,j);
 					break;
 				case 'A':	/* Cursor Up */
-					i=strtoul(cterm->escbuf+1,NULL,10);
-					if(i==0)
-						i=1;
-					i=WHEREY()-i;
+					seq_default(seq, 0, 1);
+					if (seq->param_int[0] < 1)
+						seq->param_int[0] = 1;
+					i=WHEREY()-seq->param_int[0];
 					if(i<cterm->top_margin)
 						i=cterm->top_margin;
 					GOTOXY(WHEREX(),i);
@@ -1376,38 +1698,14 @@ static void do_ansi(struct cterminal *cterm, char *retbuf, size_t retsize, int *
 						i=cterm->width;
 					GOTOXY(i,WHEREY());
 					break;
-				case 'D':	/* Cursor Left and Font Select */
-					if(*(p-1)==' ') {	/* Font Select */
-						i=0;
-						j=0;
-						if(strlen(cterm->escbuf)>2) {
-							if((p=strtok(cterm->escbuf+1,";"))!=NULL) {
-								i=strtoul(p,NULL,10);
-								if((p=strtok(NULL,";"))!=NULL) {
-									j=strtoul(p,NULL,10);
-								}
-							}
-							switch(i) {
-								case 0:	/* Four fonts are currently supported */
-								case 1:
-								case 2:
-								case 3:
-									cterm->setfont_result = SETFONT(j,FALSE,i+1);
-									if(cterm->setfont_result == CIOLIB_SETFONT_SUCCESS)
-										cterm->altfont[i] = j;
-									break;
-							}
-						}
-					}
-					else {
-						i=strtoul(cterm->escbuf+1,NULL,10);
-						if(i==0)
-							i=1;
-						i=WHEREX()-i;
-						if(i<1)
-							i=1;
-						GOTOXY(i,WHEREY());
-					}
+				case 'D':	/* Cursor Left */
+					i=strtoul(cterm->escbuf+1,NULL,10);
+					if(i==0)
+						i=1;
+					i=WHEREX()-i;
+					if(i<1)
+						i=1;
+					GOTOXY(i,WHEREY());
 					break;
 				case 'E':	/* Cursor next line */
 					i=strtoul(cterm->escbuf+1,NULL,10);
@@ -1781,98 +2079,19 @@ static void do_ansi(struct cterminal *cterm, char *retbuf, size_t retsize, int *
 				case 'q': /* ToDo?  VT100 keyboard lights */
 					break;
 				case 'r': /* ToDo?  Scrolling reigon */
-					/* Set communication speed (if has a * before it) */
-					if(*(p-1) == '*' && speed != NULL) {
-						/*
-						 * Ps1 			Comm Line 		Ps2 		Communication Speed
-						 * none, 0, 1 	Host Transmit 	none, 0 	Use default speed.
-						 * 2		 	Host Receive 	1 			300
-						 * 3 			Printer 		2 			600
-						 * 4		 	Modem Hi 		3 			1200
-						 * 5		 	Modem Lo 		4 			2400
-						 * 								5 			4800
-						 * 								6 			9600
-						 * 								7 			19200
-						 * 								8 			38400
-						 * 								9 			57600
-						 * 								10 			76800
-						 * 								11 			115200
-						 */
-						int newspeed=0;
-
-						*(--p)=0;
-						if(cterm->escbuf[1]) {
-							p=strtok(cterm->escbuf+1,";");
-							if(p!=NULL) {
-								if(p!=cterm->escbuf+1 || strtoul(p,NULL,10)<2) {
-									if(p==cterm->escbuf+1)
-										p=strtok(NULL,";");
-									if(p!=NULL) {
-										switch(strtoul(p,NULL,10)) {
-											case 0:
-												newspeed=0;
-												break;
-											case 1:
-												newspeed=300;
-												break;
-											case 2:
-												newspeed=600;
-												break;
-											case 3:
-												newspeed=1200;
-												break;
-											case 4:
-												newspeed=2400;
-												break;
-											case 5:
-												newspeed=4800;
-												break;
-											case 6:
-												newspeed=9600;
-												break;
-											case 7:
-												newspeed=19200;
-												break;
-											case 8:
-												newspeed=38400;
-												break;
-											case 9:
-												newspeed=57600;
-												break;
-											case 10:
-												newspeed=76800;
-												break;
-											case 11:
-												newspeed=115200;
-												break;
-											default:
-												newspeed=-1;
-												break;
-										}
-									}
-								}
-								else
-									newspeed = -1;
+					row = 1;
+					max_row = cterm->height;
+					if(strlen(cterm->escbuf)>2) {
+						if((p=strtok(cterm->escbuf+1,";"))!=NULL) {
+							row=strtoul(p,NULL,10);
+							if((p=strtok(NULL,";"))!=NULL) {
+								max_row=strtoul(p,NULL,10);
 							}
 						}
-						if(newspeed >= 0)
-							*speed = newspeed;
 					}
-					else {
-						row = 1;
-						max_row = cterm->height;
-						if(strlen(cterm->escbuf)>2) {
-							if((p=strtok(cterm->escbuf+1,";"))!=NULL) {
-								row=strtoul(p,NULL,10);
-								if((p=strtok(NULL,";"))!=NULL) {
-									max_row=strtoul(p,NULL,10);
-								}
-							}
-						}
-						if(row >= 1 && max_row > row && max_row <= cterm->height) {
-							cterm->top_margin = row;
-							cterm->bottom_margin = max_row;
-						}
+					if(row >= 1 && max_row > row && max_row <= cterm->height) {
+						cterm->top_margin = row;
+						cterm->bottom_margin = max_row;
 					}
 					break;
 				case 's':
@@ -2037,6 +2256,7 @@ static void do_ansi(struct cterminal *cterm, char *retbuf, size_t retsize, int *
 			/* ToDo: Reset Terminal */
 			break;
 	}
+	free_sequence(seq);
 	cterm->escbuf[0]=0;
 	cterm->sequence=0;
 }
-- 
GitLab