1 ///////////////////////////////////////////////////////////////////////////////
2 //Telnet Win32 : an ANSI telnet client.
3 //Copyright (C) 1998-2000 Paul Brannan
4 //Copyright (C) 1998 I.Ioannou
5 //Copyright (C) 1997 Brad Johnson
6 //
7 //This program is free software; you can redistribute it and/or
8 //modify it under the terms of the GNU General Public License
9 //as published by the Free Software Foundation; either version 2
10 //of the License, or (at your option) any later version.
11 //
12 //This program is distributed in the hope that it will be useful,
13 //but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //GNU General Public License for more details.
16 //
17 //You should have received a copy of the GNU General Public License
18 //along with this program; if not, write to the Free Software
19 //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 //I.Ioannou
22 //roryt@hol.gr
23 //
24 ///////////////////////////////////////////////////////////////////////////
25 
26 ///////////////////////////////////////////////////////////////////////////////
27 //
28 // Module:		ansiprsr.cpp
29 //
30 // Contents:	ANSI parser base class
31 //
32 // Product:		telnet
33 //
34 // Revisions: August 30, 1998 Paul Brannan <pbranna@clemson.edu>
35 //            July 29, 1998 pbranna@clemson.edu
36 //            June 15, 1998 pbranna@clemson.edu
37 //            May 19, 1998  pbranna@clemson.edu
38 //            24 Dec, 1997  Andrey.V.Smilianets
39 //            05. Sep.1997  roryt@hol.gr (I.Ioannou)
40 //            11.May.1997   roryt@hol.gr (I.Ioannou)
41 //            6.April.1997  roryt@hol.gr (I.Ioannou)
42 //            5.April.1997  jbj@nounname.com
43 //            30.M�rz.1997	Titus_Boxberg@public.uni-hamburg.de
44 //		      14.Sept.1996  jbj@nounname.com
45 //            Version 2.0
46 //
47 //            13.Jul.1995	igor.milavec@uni-lj.si
48 //					  Original code
49 //
50 ///////////////////////////////////////////////////////////////////////////////
51 
52 #include "precomp.h"
53 
54 const int ANSIColors[] = {BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE};
55 
56 // The constructor now takes different arguments and initializes different
57 // variables (Paul Brannan 6/15/98)
58 TANSIParser::TANSIParser(TConsole &RefConsole, KeyTranslator &RefKeyTrans,
59 						 TScroller &RefScroller, TNetwork &RefNetwork,
60 						 TCharmap &RefCharmap):
61 TParser(RefConsole, RefKeyTrans, RefScroller, RefNetwork, RefCharmap) {
62 	Init();
63 	iSavedAttributes = (unsigned char) 7;
64 	// must also check to make sure the string is non-NULL
65 	// (Paul Brannan 5/8/98)
66 	if ((ini.get_dumpfile() != NULL) && (*ini.get_dumpfile() != '\0')){
67 		dumpfile = fopen(ini.get_dumpfile(), "wb");
68 	}else {
69 		dumpfile = NULL;
70 	}
71 	InPrintMode = 0;
72 	printfile = NULL;
73 
74 	fast_write = ini.get_fast_write(); // Paul Brannan 6/28/98
75 	Scroller.init(&StripBuffer);
76 }
77 
78 TANSIParser::~TANSIParser(){
79 	if (dumpfile) fclose (dumpfile);
80 	// Added I.Ioannou 06 April, 1997
81 	if (printfile != NULL) fclose (printfile);
82 }
83 
84 // Created Init() function to initialize the parser but not clear the screen
85 // (Paul Brannan 9/23/98)
86 void TANSIParser::Init() {
87 	// Paul Brannan 6/25/98
88 	map_G0 = 'B'; map_G1 = 'B';
89 	Charmap.setmap(map_G0);
90 	current_map = 'B';
91 
92 	ignore_margins = 0;
93 	vt52_mode = 0;
94 	print_ctrl = 0;
95 	newline_mode = false;
96 
97 	KeyTrans.clear_ext_mode();
98 
99 	iSavedCurY = 0;							// Reset Variables
100 	iSavedCurX = 0;
101 	inGraphMode = 0;
102 	Console.SetScroll(-1, -1);
103 	Console.Normal();						// Reset Attributes
104 
105 	// Set tabs stops
106 	resetTabStops();
107 }
108 
109 void TANSIParser::ResetTerminal() {
110 	Init();
111 	Console.ClearScreen();					// Clear Screen
112 	Console.SetRawCursorPosition(0,0);		// Home Cursor
113 }
114 void TANSIParser::SaveCurY(int iY){
115 	iSavedCurY=iY;
116 }
117 
118 void TANSIParser::SaveCurX(int iX){
119 	iSavedCurX=iX;
120 }
121 
122 void TANSIParser::resetTabStops() {
123 	for(int j = 0; j < MAX_TAB_POSITIONS; j++) {
124 		tab_stops[j] = 8 + j - (j%8);
125 	}
126 }
127 
128 void TANSIParser::ConSetAttribute(unsigned char TextAttrib){
129 	// Paul Brannan 5/8/98
130 	// Made this go a little bit faster by changing from switch{} to an array
131 	// for the colors
132 	if(TextAttrib >= 30) {
133 		if(TextAttrib <= 37) {
134 			Console.SetForeground(ANSIColors[TextAttrib-30]);
135 			return;
136 		} else if((TextAttrib >= 40) && (TextAttrib <= 47)) {
137 			Console.SetBackground(ANSIColors[TextAttrib-40]);
138 			return;
139 		}
140 	}
141 
142 	switch (TextAttrib){
143 		// Text Attributes
144 	case 0: Console.Normal();           break;	// Normal video
145 	case 1: Console.HighVideo();        break;	// High video
146 	case 2: Console.LowVideo();         break;	// Low video
147 	case 4: Console.UnderlineOn();		break;	// Underline on (I.Ioannou)
148 	case 5: Console.BlinkOn();			break;	// Blink video
149 		// Corrected by I.Ioannou 11 May, 1997
150 	case 7: Console.ReverseOn();		break;	// Reverse video
151 	case 8:								break;	// hidden
152 		// All from 10 thru 27 are hacked from linux kernel
153 		// I.Ioannou 06 April, 1997
154 	case 10:
155 		//  I.Ioannou 04 Sep 1997 turn on/off high bit
156 		inGraphMode = 0;
157 		print_ctrl = 0;
158 		Charmap.setmap(current_map ? map_G1:map_G0); // Paul Brannan 6/25/98
159 		break; // ANSI X3.64-1979 (SCO-ish?)
160 		// Select primary font,
161 		// don't display control chars
162 		// if defined, don't set
163 		// bit 8 on output (normal)
164 	case 11:
165 		inGraphMode = 0;
166 		print_ctrl = 1;
167 		Charmap.setmap(0); // Paul Brannan 6/25/98
168 		break; // ANSI X3.64-1979 (SCO-ish?)
169 		// Select first alternate font,
170 		// let chars < 32 be displayed
171 		// as ROM chars
172 	case 12:
173 		inGraphMode = 1;
174 		print_ctrl = 1;
175 		Charmap.setmap(0); // Paul Brannan 6/25/98
176 		break; // ANSI X3.64-1979 (SCO-ish?)
177 		// Select second alternate font,
178 		// toggle high bit before
179 		// displaying as ROM char.
180 
181 	case 21:									// not really Low video
182 	case 22: Console.LowVideo();		break;	// but this works good also
183 	case 24: Console.UnderlineOff();	break;	// Underline off
184 	case 25: Console.BlinkOff();		break;	// blink off
185 		// Corrected by I.Ioannou 11 May, 1997
186 	case 27: Console.ReverseOff();		break;	//Reverse video off
187 
188 	// Mutt needs this (Paul Brannan, Peter Jordan 12/31/98)
189 	// This is from the Linux kernel source
190     case 38: /* ANSI X3.64-1979 (SCO-ish?)
191 			  * Enables underscore, white foreground
192 			  * with white underscore (Linux - use
193 			  * default foreground).
194 			  */
195 			Console.UnderlineOn();
196 			Console.SetForeground(ini.get_normal_fg());
197 			break;
198 	case 39: /* ANSI X3.64-1979 (SCO-ish?)
199 			  * Disable underline option.
200 			  * Reset colour to default? It did this
201 			  * before...
202 			  */
203 			Console.UnderlineOff();
204 			Console.SetForeground(ini.get_normal_fg());
205 			break;
206 	case 49:
207 			Console.SetBackground(ini.get_normal_bg());
208 			break;
209 
210 	}
211 }
212 
213 void TANSIParser::ConSetCursorPos(int x, int y) {
214 	if(ignore_margins)
215 		Console.SetRawCursorPosition(x, y);
216 	else
217 		Console.SetCursorPosition(x, y);
218 }
219 
220 const char* TANSIParser::GetTerminalID()
221 {
222 	return "\033[?1;2c";
223 }
224 
225 // All of the Telnet protocol stuff has been moved to TTelHndl.cpp
226 // This is more consistent with what OO should be
227 // (Paul Brannan 6/15/98)
228 
229 #ifdef __BORLANDC__
230 // argsused doesn't work on MSVC++
231 #pragma argsused
232 #endif
233 
234 // Use this for the VT100 flags (Paul Brannan 12/2/98)
235 #define FLAG_DOLLAR		0x0001
236 #define FLAG_QMARK		0x0002
237 #define FLAG_GREATER	0x0004
238 #define FLAG_LESS		0x0008
239 #define FLAG_EXCLAM		0x0010
240 #define FLAG_AMPERSAND	0x0020
241 #define FLAG_SLASH		0x0040
242 #define FLAG_EQUAL		0x0080
243 #define FLAG_QUOTE		0x0100
244 #define FLAG_OTHER		0x8000
245 
246 char* TANSIParser::ParseEscapeANSI(char* pszBuffer, char* pszBufferEnd)
247 {
248 
249 	//	The buffer contains something like <ESC>[pA
250 	//	where p is an optional decimal number specifying the count by which the
251 	//	appropriate action should take place.
252 	//	The pointer pszBuffer points us to the p, <ESC> and [ are
253 	//	already 'consumed'
254 
255 	//	TITUS: Simplification of the code: Assume default count of 1 in case
256 	//	there are no parameters.
257 	char tmpc;
258 	const int nParam = 10;	// Maximum number of parameters
259 	int	iParam[nParam] = {1, 0, 0, 0, 0};	// Assume 1 Parameter, Default 1
260 	int iCurrentParam = 0;
261 	DWORD flag = 0;
262 	int missing_param = 0;
263 
264 	// Get parameters from escape sequence.
265 	while ((tmpc = *pszBuffer) <= '?') {
266 
267 		if(tmpc < '0' || tmpc > '9') {
268 			// Check for parameter delimiter.
269 			if(tmpc == ';') {
270 				// This is a hack (Paul Brannan 6/27/98)
271 				if(*(pszBuffer - 1) == '[') missing_param = iCurrentParam+1;
272 				pszBuffer++;
273 				continue;
274 			}
275 
276 			// It is legal to have control characters inside ANSI sequences
277 			// (Paul Brannan 6/26/98)
278 			if(tmpc < ' ') {
279 				Console.WriteCtrlChar(tmpc);
280 				pszBuffer++;
281 				continue;
282 			}
283 
284 			// A new way of handling flags (Paul Brannan 12/2/98)
285 			switch(tmpc) {
286 			case '$': flag |= FLAG_DOLLAR; break;
287 			case '?': flag |= FLAG_QMARK; break;
288 			case '>': flag |= FLAG_GREATER; break;
289 			case '<': flag |= FLAG_LESS; break;
290 			case '!': flag |= FLAG_EXCLAM; break;
291 			case '&': flag |= FLAG_AMPERSAND; break;
292 			case '/': flag |= FLAG_SLASH; break;
293 			case '=': flag |= FLAG_EQUAL; break;
294 			case '\"': flag |= FLAG_QUOTE; break;
295 			default: flag |= FLAG_OTHER; break;
296 			}
297 
298 			pszBuffer++;
299 		}
300 
301 		//  Got Numerical Parameter.
302 		iParam[iCurrentParam] = strtoul(pszBuffer, &pszBuffer, 10);
303 		if (iCurrentParam < nParam)
304 			iCurrentParam++;
305 	}
306 
307 	//~~~ TITUS: Apparently the digit is optional (look at termcap or terminfo)
308 	// So: If there is no digit, assume a count of 1
309 
310 	switch ((unsigned char)*pszBuffer++) {
311 		// Insert Character
312 		case '@':
313 			if(iParam[0] == 0) iParam[0] = 1; // Paul Brannan 9/1/98
314 			Console.InsertCharacter(iParam[0]); break;
315 		// Move cursor up.
316 		case 'A':
317 			if(iParam[0] == 0) iParam[0] = 1;
318 			Console.MoveCursorPosition(0, -iParam[0]); break;
319 		// Move cursor down.
320 		// Added by I.Ioannou 06 April, 1997
321 		case 'B':
322 		case 'e':
323 			if(iParam[0] == 0) iParam[0] = 1;
324 			Console.MoveCursorPosition(0, iParam[0]);
325 			break;
326 		// Move cursor right.
327 		// Added by I.Ioannou 06 April, 1997
328 		case 'C':
329 		case 'a':
330 			// Handle cursor size sequences (Jose Cesar Otero Rodriquez and
331 			// Paul Brannan, 3/27/1999)
332 			if(flag & FLAG_EQUAL) {
333 				switch(iParam[0]) {
334 				case 7: Console.SetCursorSize(50); break;
335 				case 11: Console.SetCursorSize(6); break;
336 				case 32: Console.SetCursorSize(0); break;
337 				default: Console.SetCursorSize(13);
338 				}
339 			} else {
340 				if(iParam[0] == 0) iParam[0] = 1;
341 				Console.MoveCursorPosition(iParam[0], 0);
342 				break;
343 			}
344 		// Move cursor left.
345 		case 'D':
346 			if(iParam[0] == 0) iParam[0] = 1;
347 			Console.MoveCursorPosition(-iParam[0], 0);
348 			break;
349 		// Move cursor to beginning of line, p lines down.
350 		// Added by I.Ioannou 06 April, 1997
351 		case 'E':
352 			Console.MoveCursorPosition(-Console.GetCursorX(), iParam[0]);
353 			break;
354 		// Moves active position to beginning of line, p lines up
355 		// Added by I.Ioannou 06 April, 1997
356 		// With '=' this changes the default fg color (Paul Brannan 6/27/98)
357 		case 'F':
358 			if(flag & FLAG_EQUAL)
359 				Console.setDefaultFg(iParam[0]);
360 			else
361 				Console.MoveCursorPosition(-Console.GetCursorX(), -iParam[0]);
362 			break;
363 		// Go to column p
364 		// Added by I.Ioannou 06 April, 1997
365 		// With '=' this changes the default bg color (Paul Brannan 6/27/98)
366 		case '`':
367 		case 'G': // 'G' is from Linux kernel sources
368 			if(flag & FLAG_EQUAL) {
369 				Console.setDefaultBg(iParam[0]);
370 			} else {
371 				if (iCurrentParam < 1)			// Alter Default
372 					iParam[0] = 0;
373 				// this was backward, and we should subtract 1 from x
374 				// (Paul Brannan 5/27/98)
375 				ConSetCursorPos(iParam[0] - 1, Console.GetCursorY());
376 			}
377 			break;
378 		// Set cursor position.
379 		case 'f':
380 		case 'H':
381 			if (iCurrentParam < 2 || iParam[1] < 1)
382 				iParam[1] = 1;
383 			ConSetCursorPos(iParam[1] - 1, iParam[0] - 1);
384 			break;
385 		// Clear screen
386 		case 'J':
387 			if ( iCurrentParam < 1 ) iParam[0] = 0;	// Alter Default
388 			switch (iParam[0]) {
389 				case 0: Console.ClearEOScreen(); break;
390 				case 1: Console.ClearBOScreen(); break;
391 				case 2:
392 					Console.ClearScreen();
393 					Console.SetRawCursorPosition(0, 0);
394 					break;
395 			}
396 			break;
397 		// Clear line
398 		case 'K':
399 			if (iCurrentParam < 1)			// Alter Default
400 				iParam[0] = 0;
401 			switch (iParam[0]) {
402 				case 0: Console.ClearEOLine(); break;
403 				case 1: Console.ClearBOLine(); break;
404 				case 2: Console.ClearLine(); break;
405 			}
406 			break;
407 		//  Insert p new, blank lines.
408 		// Added by I.Ioannou 06 April, 1997
409 		case 'L':
410 			{
411 				// for (int i = 1; i <= iParam[0]; i++)
412 				// This should speed things up a bit (Paul Brannan 9/2/98)
413 				Console.ScrollDown(Console.GetRawCursorY(), -1, iParam[0]);
414 				break;
415 			}
416 		//  Delete p lines.
417 		// Added by I.Ioannou 06 April, 1997
418 		case 'M':
419 			{
420 				for (int i = 1; i <= iParam[0]; i++)
421 				// This should speed things up a bit (Paul Brannan 9/2/98)
422 				Console.ScrollDown(Console.GetRawCursorY(), -1, -1);
423 				break;
424 			}
425 		// DELETE CHAR
426 		case 'P':
427 			Console.DeleteCharacter(iParam[0]);
428 			break;
429 		// Scrolls screen up (down? -- PB) p lines,
430 		// Added by I.Ioannou 06 April, 1997
431 		// ANSI X3.64-1979 references this but I didn't
432 		// found it in any telnet implementation
433 		// note 05 Oct 97  : but SCO terminfo uses them, so uncomment them !!
434 		case 'S':
435 			{
436 				//for (int i = 1; i <= iParam[0]; i++)
437 				// This should speed things up a bit (Paul Brannan 9/2/98)
438 				Console.ScrollDown(-1, -1, -iParam[0]);
439 				break;
440 			}
441 		// Scrolls screen up p lines,
442 		// Added by I.Ioannou 06 April, 1997
443 		// ANSI X3.64-1979 references this but I didn't
444 		// found it in any telnet implementation
445 		// note 05 Oct 97  : but SCO terminfo uses them, so uncomment them !!
446 		case 'T':
447 			{
448 				// for (int i = 1; i <= iParam[0]; i++)
449 				// This should speed things up a bit (Paul Brannan 9/2/98)
450 				Console.ScrollDown(-1, -1, iParam[0]);
451 				break;
452 			}
453 		//  Erases p characters up to the end of line
454 		// Added by I.Ioannou 06 April, 1997
455 		case 'X':
456 			{
457 				int iKeepX = Console.GetRawCursorX();
458 				int iKeepY = Console.GetRawCursorY();
459 				if (iParam[0] > Console.GetWidth())
460 					iParam[0] = Console.GetWidth(); // up to the end of line
461 				for ( int i = 1; i <= iParam[0]; i++ )
462 					Console.WriteString(" ", 1);
463 				Console.SetRawCursorPosition(iKeepX , iKeepY);
464 				break;
465 			}
466 		// Go back p tab stops
467 		// Added by I.Ioannou 06 April, 1997
468 		// Implemented by Paul Brannan, 4/13/2000
469 		case 'Z':
470 			{
471 				int x = Console.GetCursorX();
472 				for(int j = 0; x > 0 && j < iParam[0]; j++)
473 					while(x > 0 && tab_stops[j] == tab_stops[x]) x--;
474 				Console.SetCursorPosition(x, Console.GetCursorY());
475 			}
476 			break;
477 		// Get Terminal ID
478 		case 'c':
479 			{
480 				const char* szTerminalId = GetTerminalID();
481 				Network.WriteString(szTerminalId, strlen(szTerminalId));
482 				break;
483 			}
484 		// TITUS++ 2. November 1998: Repeat Character.
485 		case 'b':
486 			// isprint may be causing problems (Paul Brannan 3/27/99)
487 			// if ( isprint(last_char) ) {
488 				char    buf[150];       // at most 1 line (max 132 chars)
489 
490 				if ( iParam[0] > 149 ) iParam[0] = 149;
491 				memset(buf, last_char, iParam[0]);
492 				buf[iParam[0]] = 0;
493 				if ( fast_write )
494 					Console.WriteStringFast(buf, iParam[0]);
495 				else
496 					Console.WriteString(buf, iParam[0]);
497 			// } /* IF */
498 		break;
499 		// Go to line p
500 		// Added by I.Ioannou 06 April, 1997
501 		case 'd':
502 			if (iCurrentParam < 1)			// Alter Default
503 				iParam[0] = 0;
504 			// this was backward, and we should subtract 1 from y
505 			// (Paul Brannan 5/27/98)
506 			ConSetCursorPos(Console.GetCursorX(), iParam[0] - 1);
507 			break;
508 		// iBCS2 tab erase
509 		// Added by I.Ioannou 06 April, 1997
510 		case 'g':
511 			if (iCurrentParam < 1)			// Alter Default
512 				iParam[0] = 0;
513 			switch (iParam[0]) {
514 				case 0:
515 					{
516 						// Clear the horizontal tab stop at the current active position
517 						for(int j = 0; j < MAX_TAB_POSITIONS; j++) {
518 							int x = Console.GetCursorX();
519 							if(tab_stops[j] == x) tab_stops[j] = tab_stops[x + 1];
520 						}
521 					}
522 					break;
523 				case 2:
524 					// I think this might be "set as default?"
525 					break;
526 				case 3:
527 					{
528 						// Clear all tab stops
529 						for(int j = 0; j < MAX_TAB_POSITIONS; j++)
530 							tab_stops[j] = -1;
531 					}
532 					break;
533 			}
534 			break;
535 		// Set extended mode
536 		case 'h':
537 			{
538 				for (int i = 0; i < iCurrentParam; i++) {
539 					// Changed to a switch statement (Paul Brannan 5/27/98)
540 					if(flag & FLAG_QMARK) {
541 						switch(iParam[i]) {
542 							case 1: // App cursor keys
543 								KeyTrans.set_ext_mode(APP_KEY);
544 								break;
545 							case 2: // VT102 mode
546 								vt52_mode = 0;
547 								KeyTrans.unset_ext_mode(APP2_KEY);
548 								break;
549 							case 3: // 132 columns
550 								if(ini.get_wide_enable()) {
551 									Console.SetWindowSize(132, -1);
552 								}
553 								break;
554 							case 4: // smooth scrolling
555 								break;
556 							case 5: // Light background
557 								Console.Lightbg();
558 								break;
559 							case 6: // Stay in margins
560 								ignore_margins = 0;
561 								break;
562 							case 7:
563 								Console.setLineWrap(true);
564 								break;
565 							case 8:	// Auto-repeat keys
566 								break;
567 							case 18: // Send FF to printer
568 								break;
569 							case 19: // Entire screen legal for printer
570 								break;
571 							case 25: // Visible cursor
572 								break;
573 							case 66: // Application numeric keypad
574 								break;
575 							default:
576 #ifdef DEBUG
577 								Console.Beep();
578 #endif
579 								break;
580 						}
581 					} else {
582 						switch(iParam[i]) {
583 							case 2: // Lock keyboard
584 								break;
585 							case 3: // Act upon control codes (PB 12/5/98)
586 								print_ctrl = 0;
587 								break;
588 							case 4: // Set insert mode
589 								Console.InsertMode(1);
590 								break;
591 							case 12: // Local echo off
592 								break;
593 							case 20: // Newline sends cr/lf
594 								KeyTrans.set_ext_mode(APP4_KEY);
595 								newline_mode = true;
596 								break;
597 							default:
598 #ifdef DEBUG
599 								Console.Beep();
600 #endif
601 								break;
602 						}
603 					}
604 				}
605 			}
606 			break;
607 		// Print Screen
608 		case 'i':
609 			if (iCurrentParam < 1)
610 				iParam[0]=0;
611 			switch (iParam[0]){
612 				case 0: break; // Print Screen
613 				case 1: break; // Print Line
614 				// Added I.Ioannou 06 April, 1997
615 				case 4:
616 					// Stop Print Log
617 					InPrintMode = 0;
618 					if ( printfile != NULL )
619 						fclose(printfile);
620 					break;
621 				case 5:
622 					// Start Print Log
623 					printfile = fopen(ini.get_printer_name(), "ab");
624 					if (printfile != NULL) InPrintMode = 1;
625 					break;
626 			}
627 			break;
628 		// Unset extended mode
629 		case 'l':
630 			{
631 				for (int i = 0; i < iCurrentParam; i++) {
632 					// Changed to a switch statement (Paul Brannan 5/27/98)
633 					if(flag & FLAG_QMARK) {
634 						switch(iParam[i]) {
635 							case 1: // Numeric cursor keys
636 								KeyTrans.unset_ext_mode(APP_KEY);
637 								break;
638 							case 2: // VT52 mode
639 								vt52_mode = 1;
640 								KeyTrans.set_ext_mode(APP2_KEY);
641 								break;
642 							case 3: // 80 columns
643 								if(ini.get_wide_enable()) {
644 									Console.SetWindowSize(80, -1);
645 								}
646 								break;
647 							case 4: // jump scrolling
648 								break;
649 							case 5: // Dark background
650 								Console.Darkbg();
651 								break;
652 							case 6: // Ignore margins
653 								ignore_margins = 1;
654 								break;
655 							case 7:
656 								Console.setLineWrap(false);
657 								break;
658 							case 8:	// Auto-repeat keys
659 								break;
660 							case 19: // Only send scrolling region to printer
661 								break;
662 							case 25: // Invisible cursor
663 								break;
664 							case 66: // Numeric keypad
665 								break;
666 							default:
667 #ifdef DEBUG
668 								Console.Beep();
669 #endif
670 								break;
671 						}
672 					} else {
673 						switch(iParam[i]) {
674 							case 2: // Unlock keyboard
675 								break;
676 							case 3: // Display control codes (PB 12/5/98)
677 								print_ctrl = 1;
678 								break;
679 							case 4: // Set overtype mode
680 								Console.InsertMode(0);
681 								break;
682 							case 12: // Local echo on
683 								break;
684 							case 20: // sends lf only
685 								KeyTrans.unset_ext_mode(APP4_KEY);
686 								newline_mode = false;
687 								break;
688 							default:
689 #ifdef DEBUG
690 								Console.Beep();
691 #endif
692 								break;
693 						}
694 					}
695 				}
696 			}
697 			break;
698 		// Set color
699 		case 'm':
700 			if(missing_param) Console.Normal();
701 			if(iCurrentParam == 0) {
702 				Console.Normal();
703 			} else {
704 				for(int i = 0; i < iCurrentParam; i++)
705 					ConSetAttribute(iParam[i]);
706 			}
707 			break;
708 		// report cursor position Row X Col
709 		case 'n':
710 			if (iCurrentParam == 1 && iParam[0]==5) {
711 				// report the cursor position
712 				Network.WriteString("\x1B[0n", 4);
713 				break;
714 			}
715 			if (iCurrentParam == 1 && iParam[0]==6){
716 				// report the cursor position
717 				// The cursor position needs to be sent as a single string
718 				// (Paul Brannan 6/27/98)
719 				char szCursorReport[40] = "\x1B[";
720 
721 				itoa(Console.GetCursorY() + 1,
722 					&szCursorReport[strlen(szCursorReport)], 10);
723 				strcat(szCursorReport, ";");
724 				itoa(Console.GetCursorX() + 1,
725 					&szCursorReport[strlen(szCursorReport)], 10);
726 				strcat(szCursorReport, "R");
727 
728 				Network.WriteString(szCursorReport, strlen(szCursorReport));
729 
730 			}
731 			break;
732 		// Miscellaneous weird sequences (Paul Brannan 6/27/98)
733 		case 'p':
734 			// Set conformance level
735 			if(flag & FLAG_QUOTE) {
736 				break;
737 			}
738 			// Soft terminal reset
739 			if(flag & FLAG_EXCLAM) {
740 				break;
741 			}
742 			// Report mode settings
743 			if(flag & FLAG_DOLLAR) {
744 				break;
745 			}
746 			break;
747 		// Scroll Screen
748 		case 'r':
749 			if (iCurrentParam < 1) {
750 				// Enable scrolling for entire display
751 				Console.SetScroll(-1, -1);
752 				break;
753 			}
754 			if (iCurrentParam >1) {
755 				// Enable scrolling from row1 to row2
756 				Console.SetScroll(iParam[0] - 1, iParam[1] - 1);
757 				// If the cursor is outside the scrolling range, fix it
758 				// (Paul Brannan 6/26/98)
759 				// if(Console.GetRawCursorY() < iParam[0] - 1) {
760 				// 	Console.SetRawCursorPosition(Console.GetCursorX(),
761 				// 		iParam[0] - 1);
762 				// }
763 				// if(Console.GetRawCursorY() > iParam[1] - 1) {
764 				// 	Console.SetRawCursorPosition(Console.GetCursorX(),
765 				// 		iParam[1] - 1);
766 				// }
767 			}
768 			// Move the cursor to the home position (Paul Brannan 12/2/98)
769 			Console.SetCursorPosition(0, 0);
770 			break;
771 		// Save cursor position
772 		case 's':
773 			SaveCurY(Console.GetRawCursorY());
774 			SaveCurX(Console.GetRawCursorX());
775 			break;
776 		// Restore cursor position
777 		case 'u':
778 			Console.SetRawCursorPosition(iSavedCurX, iSavedCurY);
779 			break;
780 		// DEC terminal report (Paul Brannan 6/28/98)
781 		case 'x':
782 			if(iParam[0])
783 				Network.WriteString("\033[3;1;1;128;128;1;0x", 20);
784 			else
785 				Network.WriteString("\033[2;1;1;128;128;1;0x", 20);
786 			break;
787 		default:
788 #ifdef DEBUG
789 			Console.Beep();
790 #endif
791 			break;
792 	}
793 
794 	return pszBuffer;
795 }
796 
797 #ifdef MTE_SUPPORT
798 // Added by Frediano Ziglio, 5/31/2000
799 // MTE extension
800 // initially copied from ParseEscapeANSI
801 char* TANSIParser::ParseEscapeMTE(char* pszBuffer, char* pszBufferEnd)
802 {
803 	//      The buffer contains something like <ESC>~pA
804 	//      where p is an optional decimal number specifying the count by which the
805 	//      appropriate action should take place.
806 	//      The pointer pszBuffer points us to the p, <ESC> and ~ are
807 	//      already 'consumed'
808 	//      TITUS: Simplification of the code: Assume default count of 1 in case
809 	//      there are no parameters.
810 	char tmpc;
811 	const int nParam = 10;  // Maximum number of parameters
812 	int     iParam[nParam] = {1, 0, 0, 0, 0};       // Assume 1 parameter, Default 1
813 	int iCurrentParam = 0;
814 	char sRepeat[2];
815 
816 	// Get parameters from escape sequence.
817 	while ((tmpc = *pszBuffer) <= '?') {
818 		if(tmpc < '0' || tmpc > '9') {
819 			// Check for parameter delimiter.
820 			if(tmpc == ';') {
821 				pszBuffer++;
822 				continue;
823 			}
824 			pszBuffer++;
825 		}
826 
827 		//  Got Numerical Parameter.
828 		iParam[iCurrentParam] = strtoul(pszBuffer, &pszBuffer, 10);
829 		if (iCurrentParam < nParam)
830 			iCurrentParam++;
831 	}
832 
833 	//~~~ TITUS: Apparently the digit is optional (look at termcap or terminfo)
834 	// So: If there is no digit, assume a count of 1
835 
836 	switch ((unsigned char)*pszBuffer++) {
837 		case 'A':
838 			// set colors
839 			if (iCurrentParam < 2 )
840 				break;
841 			if (iParam[0] <= 15 && iParam[1] <= 15)
842 				Console.SetAttrib( (iParam[1] << 4) | iParam[0] );
843 			break;
844 
845 		case 'R':
846 			// define region
847 			mteRegionXF = -1;
848 			if (iCurrentParam < 2 )
849 				break;
850 			mteRegionXF = iParam[1]-1;
851 			mteRegionYF = iParam[0]-1;
852 			break;
853 
854 		case 'F':
855 			// fill with char
856 			{
857 				if (mteRegionXF == -1 || iCurrentParam < 1)
858 					break;
859 				sRepeat[0] = (char)iParam[0];
860 				sRepeat[1] = '\0';
861 				int xi = Console.GetCursorX(),yi = Console.GetCursorY();
862 				int xf = mteRegionXF;
863 				int yf = mteRegionYF;
864 				mteRegionXF = -1;
865 				for(int y=yi;y<=yf;++y)
866 				{
867 					Console.SetCursorPosition(xi,y);
868 					for(int x=xi;x<=xf;++x)
869 
870 						Console.WriteStringFast(sRepeat,1);
871 				}
872 			}
873 			break;
874 
875 		case 'S':
876 			// Scroll region
877 			{
878 				if (mteRegionXF == -1 || iCurrentParam < 2)
879 					break;
880 				int /*x = Console.GetCursorX(),*/y = Console.GetCursorY();
881 				// int xf = mteRegionXF;
882 				int yf = mteRegionYF;
883 				mteRegionXF = -1;
884 				// !!! don't use x during scroll
885 				int diff = (iParam[0]-1)-y;
886 				if (diff<0)
887 					Console.ScrollDown(y-1,yf,diff);
888 				else
889 					Console.ScrollDown(y,yf+1,diff);
890 			}
891 			break;
892 			// Meridian main version ??
893 		case 'x':
894 			// disable echo and line mode
895 			Network.set_local_echo(0);
896 			Network.set_line_mode(0);
897 			// Meridian Server handle cursor itself
898 			Console.SetCursorSize(0);
899 			break;
900 			// query ??
901 		case 'Q':
902 			if (iParam[0] == 1)
903 				Network.WriteString("\033vga.",5);
904 			break;
905 		default:
906 #ifdef DEBUG
907 			Console.Beep();
908 #endif
909 			break;
910 	}
911 
912 	return pszBuffer;
913  }
914 #endif
915 
916 char* TANSIParser::ParseEscape(char* pszBuffer, char* pszBufferEnd) {
917 	char *pszChar;
918 
919 	// Check if we have enough characters in buffer.
920 	if ((pszBufferEnd - pszBuffer) < 2)
921 		return pszBuffer;
922 
923 	//  I.Ioannou 04 Sep 1997
924 	// there is no need for pszBuffer++; after each command
925 
926 	// Decode the command.
927 	pszBuffer++;
928 
929 	switch (*pszBuffer++) {
930 		case 'A': // Cursor up
931 			Console.MoveCursorPosition(0, -1);
932 			break;
933 		// Cursor down
934 		case 'B':
935   			Console.MoveCursorPosition(0, 1);
936 			break;
937 		// Cursor right
938 		case 'C':
939   			Console.MoveCursorPosition(1, 0);
940 			break;
941 		// LF *or* cursor left (Paul Brannan 6/27/98)
942 		case 'D':
943 			if(vt52_mode)
944 				Console.MoveCursorPosition(-1, 0);
945 			else
946 				Console.index();
947 			break;
948 		// CR/LF (Paul Brannan 6/26/98)
949 		case 'E':
950 			Console.WriteCtrlString("\r\n", 2);
951 			break;
952 		// Special graphics char set (Paul Brannan 6/27/98)
953 		case 'F':
954 			Charmap.setmap('0');
955 			break;
956 		// ASCII char set (Paul Brannan 6/27/98)
957 		case 'G':
958 			Charmap.setmap('B');
959 			break;
960 		// Home cursor/tab set
961 		case 'H':
962 			if(ini.get_vt100_mode()) {
963 				int x = Console.GetCursorX();
964 				if(x != 0) {
965 					int t = tab_stops[x - 1];
966 					for(int j = x - 1; j >= 0 && tab_stops[j] == t; j--)
967 						tab_stops[j] = x;
968 				}
969 			} else {
970 				//  I.Ioannou 04 Sep 1997 (0,0) not (1,1)
971 				ConSetCursorPos(0, 0);
972 			}
973 			break;
974 		// Reverse line feed (Paul Brannan 6/27/98)
975 		// FIX ME!!!  reverse_index is wrong to be calling here
976 		// (Paul Brannan 12/2/98)
977 		case 'I':
978 			Console.reverse_index();
979 			break;
980 		// Erase end of screen
981 		case 'J':
982 			Console.ClearEOScreen();
983 			break;
984 		// Erase EOL
985 		case 'K':
986 			Console.ClearEOLine();
987 			break;
988 		// Scroll Up one line //Reverse index
989 		case 'M':
990 			Console.reverse_index();
991 			break;
992 		// Direct cursor addressing
993 		case 'Y':
994 			if ((pszBufferEnd - pszBuffer) >= 2){
995 				// if we subtract '\x1F', then we may end up with a negative
996 				// cursor position! (Paul Brannan 6/26/98)
997 				ConSetCursorPos(pszBuffer[1] - ' ',	pszBuffer[0] - ' ');
998 				pszBuffer+=2;
999 			} else {
1000 				pszBuffer--; // Paul Brannan 6/26/98
1001 			}
1002 			break;
1003 		// Terminal ID Request
1004 		case 'Z':
1005 			{
1006 				const char* szTerminalId = GetTerminalID();
1007 				Network.WriteString(szTerminalId, strlen(szTerminalId));
1008 				break;
1009 			}
1010 		// reset terminal to defaults
1011 		case 'c':
1012 			ResetTerminal();
1013 			break;
1014 		// Enter alternate keypad mode
1015 		case '=':
1016 			KeyTrans.set_ext_mode(APP3_KEY);
1017 			break;
1018 		// Exit alternate keypad mode
1019 		case '>':
1020 			KeyTrans.unset_ext_mode(APP3_KEY);
1021 			break;
1022 		// Enter ANSI mode
1023 		case '<':
1024 			KeyTrans.unset_ext_mode(APP2_KEY); // exit vt52 mode
1025 			break;
1026 		// Graphics processor on (See note 3)
1027 		case '1':
1028 			break;
1029 		// Line size commands
1030 		case '#':        //Line size commands
1031 			// (Paul Brannan 6/26/98)
1032 			if(pszBuffer < pszBufferEnd) {
1033 				switch(*pszBuffer++) {
1034 				case '3': break; // top half of a double-height line
1035 				case '4': break; // bottom half of a double-height line
1036 				case '6': break; // current line becomes double-width
1037 				case '8': Console.ClearScreen('E'); break;
1038 				}
1039 			} else {
1040 				pszBuffer--;
1041 			}
1042 			break;
1043 		// Graphics processor off (See note 3)
1044 		case '2':
1045 			break;
1046 		// Save cursor and attribs
1047 		case '7':
1048 			SaveCurY(Console.GetRawCursorY());
1049 			SaveCurX(Console.GetRawCursorX());
1050 			iSavedAttributes = Console.GetAttrib();
1051 			break;
1052 			// Restore cursor position and attribs
1053 		case '8':
1054 			Console.SetRawCursorPosition(iSavedCurX, iSavedCurY);
1055 			Console.SetAttrib(iSavedAttributes);
1056 			break;
1057 		// Set G0 map (Paul Brannan 6/25/98)
1058 		case '(':
1059 			if (pszBuffer < pszBufferEnd) {
1060 				map_G0 = *pszBuffer;
1061 				if(current_map == 0) Charmap.setmap(map_G0);
1062 				pszBuffer++;
1063 			} else {
1064 				pszBuffer--;
1065 			}
1066 			break;
1067 		// Set G1 map (Paul Brannan 6/25/98)
1068 		case ')':
1069 			if (pszBuffer < pszBufferEnd) {
1070 				map_G1 = *pszBuffer;
1071 				if(current_map == 1) Charmap.setmap(map_G1);
1072 				pszBuffer++;
1073 			} else {
1074 				pszBuffer--;
1075 			}
1076 			break;
1077 		// This doesn't do anything, as far as I can tell, but it does take
1078 		// a parameter (Paul Brannan 6/27/98)
1079 		case '%':
1080 			if (pszBuffer < pszBufferEnd) {
1081 				pszBuffer++;
1082 			} else {
1083 				pszBuffer--;
1084 			}
1085 			break;
1086 		// ANSI escape sequence
1087 		case '[':
1088 			// Check if we have whole escape sequence in buffer.
1089 			// This should not be isalpha anymore (Paul Brannan 9/1/98)
1090 			pszChar = pszBuffer;
1091 			while ((pszChar < pszBufferEnd) && (*pszChar <= '?'))
1092 				pszChar++;
1093 			if (pszChar == pszBufferEnd)
1094 				pszBuffer -= 2;
1095 			else
1096 				pszBuffer = ParseEscapeANSI(pszBuffer, pszBufferEnd);
1097 			break;
1098 #ifdef MTE_SUPPORT
1099 		case '~':
1100 			// Frediano Ziglio, 5/31/2000
1101 			// Meridian Terminal Emulator extension
1102 			// !!! same as ANSI
1103 			// !!! should put in MTE procedure
1104 			pszChar = pszBuffer;
1105 			while ((pszChar < pszBufferEnd) && (*pszChar <= '?'))
1106 				pszChar++;
1107 			if (pszChar == pszBufferEnd)
1108 				pszBuffer -= 2;
1109 			else
1110 				pszBuffer = ParseEscapeMTE(pszBuffer, pszBufferEnd);
1111 			break;
1112 #endif
1113 		default:
1114 #ifdef DEBUG
1115 			Console.Beep();
1116 #endif
1117 			break;
1118 	}
1119 
1120 	return pszBuffer;
1121 }
1122 
1123 // This function now only parses the ANSI buffer and does not do anything
1124 // with IAC sequences.  That code has been moved to TTelHndl.cpp.
1125 // The scroller update routines have been moved to TScroll.cpp.
1126 // (Paul Brannan 6/15/98)
1127 char* TANSIParser::ParseBuffer(char* pszHead, char* pszTail){
1128 	// copy into ANSI buffer
1129 	char * pszResult;
1130 
1131 	// Parse the buffer for ANSI or display
1132 	while (pszHead < pszTail) {
1133 		if(!ini.get_output_redir()) {
1134 			pszResult = ParseANSIBuffer(pszHead, pszTail);
1135 		} else {
1136 			// Output is being redirected
1137 			if(ini.get_strip_redir()) {
1138 				// Skip the WriteFile() altogether and pass the buffer to a filter
1139 				// Mark Miesfield 09/24/2000
1140 				pszResult = PrintGoodChars(pszHead, pszTail);
1141 			} else {
1142 				DWORD Result;
1143 				// Paul Brannan 7/29/98
1144 				// Note that this has the unforunate effect of printing out
1145 				// NULL (ascii 0) characters onto the screen
1146 				if (!WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),	pszHead,
1147 					pszTail - pszHead, &Result,	NULL)) pszResult = pszHead;
1148 				pszResult = pszHead + Result;
1149 			}
1150 		}
1151 		if (dumpfile)
1152 			fwrite( pszHead, sizeof (char), pszResult-pszHead, dumpfile);
1153 		if(ini.get_scroll_enable()) Scroller.update(pszHead, pszResult);
1154 		if (pszResult == pszHead) break;
1155 		pszHead = pszResult;
1156 	}
1157 	// return the new head to the buffer
1158 	return pszHead;
1159 }
1160 
1161 // A simple routine to strip ANSI sequences
1162 // This isn't perfect, but it does an okay job (Paul Brannan 7/5/98)
1163 // Fixed a line counting bug (Paul Brannan 12/4/98)
1164 int TANSIParser::StripBuffer(char* pszHead, char* pszTail, int width) {
1165 	int lines = 0, c = 0;
1166 	char *pszBuf = pszHead;
1167 
1168 	while(pszHead < pszTail) {
1169 		if(iscntrl(*pszHead)) {
1170 			switch(*(pszHead++)) {
1171 			case 8:
1172 			case 127:
1173 				if(c>0) {
1174 					if(!(c%width)) lines--;
1175 					c--;
1176 					pszBuf--;
1177 				}
1178 				break;
1179 			case 10: lines++;
1180 			case 13:
1181 				*(pszBuf++) = *(pszHead - 1);
1182 				c = 0;
1183 				break;
1184 			case 27:
1185 				switch(*(pszHead++)) {
1186 				case 'Y': pszHead += 2; break;
1187 				case '#':
1188 				case '(':
1189 				case ')':
1190 				case '%': pszHead++; break;
1191 				case '[':
1192 					while((pszHead < pszTail) && (*pszHead < '?'))
1193 						pszHead++;
1194 					pszHead++;
1195 					break;
1196 				}
1197 			}
1198 		} else {
1199 			*(pszBuf++) = *(pszHead++);
1200 			c++;
1201 		}
1202 		if(c != 0 && !(c%width))
1203 			lines++;
1204 	}
1205 
1206 	// Fill in the end of the buffer with blanks
1207 	while(pszBuf <= pszTail) *pszBuf++ = ' ';
1208 
1209 	return lines;
1210 }
1211 
1212 char* TANSIParser::ParseANSIBuffer(char* pszBuffer, char* pszBufferEnd)
1213 {
1214 	if(InPrintMode) {
1215 		return PrintBuffer(pszBuffer, pszBufferEnd);
1216 	}
1217 
1218 	unsigned char tmpc = *(unsigned char *)pszBuffer;
1219 
1220 	if(tmpc == 27) {
1221 		return ParseEscape(pszBuffer, pszBufferEnd);
1222 	}
1223 
1224 //	if((fast_write && tmpc < 32) ||
1225 //		!print_ctrl && (tmpc < 32 || (EightBit_Ansi &&
1226 //		(tmpc > 128 && tmpc < 128 + ' ')))) {
1227 
1228 	// We shouldn't print ctrl characters when fast write is enabled
1229 	// and ctrl chars are disabled (Paul Brannan 9/1/98)
1230 	if(tmpc < 32) {
1231 		// From the Linux kernel (Paul Brannan 12/5/98):
1232 		/* A bitmap for codes <32. A bit of 1 indicates that the code
1233 		 * corresponding to that bit number invokes some special action
1234 		 * (such as cursor movement) and should not be displayed as a
1235 		 * glyph unless the disp_ctrl mode is explicitly enabled.
1236 		 */
1237 		const long CTRL_ACTION = 0x0d00ff81;
1238 		const long CTRL_ALWAYS = 0x0800f501;
1239 		if(!(((print_ctrl?CTRL_ALWAYS:CTRL_ACTION)>>tmpc)&1)) {
1240 
1241 			Console.WriteString((char *)&tmpc, 1);
1242 			pszBuffer++;
1243 			return pszBuffer;
1244 		}
1245 
1246 		switch (tmpc) {
1247 		case 0:
1248 			pszBuffer++;
1249 			break;
1250 
1251 		// I.Ioannou 5/30/98
1252 		case 7:
1253 			Console.Beep();
1254 			pszBuffer++;
1255 			break;
1256 
1257 		// destructive backspace
1258 		case 8:
1259 			// Added option for destructive backspace (Paul Brannan 5/13/98)
1260 			// Changed to ConWriteCtrlString so that the cursor position can be
1261 			// updated (Paul Brannan 5/25/98)
1262 			if(ini.get_dstrbksp()) {
1263 				Console.WriteCtrlChar('\b');
1264 				Console.WriteString(" ", 1);
1265 				Console.WriteCtrlChar('\b');
1266 			}
1267 			else Console.WriteCtrlChar('\b');
1268 			pszBuffer++;
1269 			break;
1270 
1271 		// horizontal tab
1272 		case 9:
1273 			{
1274 				pszBuffer++;
1275 				int x = Console.GetCursorX();
1276 				if(x != -1)
1277 					Console.SetCursorPosition(tab_stops[x], Console.GetCursorY());
1278 			}
1279 			break;
1280 
1281 		// Line Feed Char
1282 		case 10:
1283 			// Test for local echo (Paul Brannan 8/25/98)
1284 			if(Network.get_local_echo() || newline_mode) // &&
1285 				Console.WriteCtrlChar('\x0d');
1286 			Console.WriteCtrlChar('\x0a');
1287 			pszBuffer++;
1288 			break;
1289 
1290 		// form feed
1291 		case 12:
1292 			pszBuffer++;
1293 			Console.ClearScreen();
1294 			Console.SetRawCursorPosition(Console.GetCursorX(), 1); // changed fm 1
1295 			break;
1296 
1297 		case 13:
1298 			Console.WriteCtrlChar('\x0d');
1299 			pszBuffer++;
1300 
1301 			break;
1302 
1303 		case 14:  // shift out of alternate chararcter set
1304 			pszBuffer++;
1305 			Charmap.setmap(map_G1); // Paul Brannan 6/25/98
1306 			current_map = 1;
1307 			break;
1308 
1309 		case 15:  // shift in
1310 			pszBuffer++;
1311 			Charmap.setmap(map_G0); // Paul Brannan 6/25/98
1312 			current_map = 0;
1313 			break;
1314 
1315 		// Paul Brannan 9/1/98 - Is this okay?
1316 		default:
1317 			pszBuffer++;
1318 		}
1319 
1320 		return pszBuffer;
1321 	}
1322 
1323 	//  added by I.Ioannou 06 April, 1997
1324 	//  In 8 bit systems the server may send 0x9b instead of ESC[
1325 	//  Well, this will produce troubles in Greek 737 Code page
1326 	//  which uses 0x9b as the small "delta" - and I thing that there
1327 	//  is another European country with the same problem.
1328 	//  If we have to stay 8-bit clean we may have to
1329 	//  give the ability of ROM characters (ESC[11m),
1330 	//  for striped 8'th bit (ESC[12m) as SCO does,
1331 	//  or a parameter at compile (or run ?) time.
1332 	// We now check for a flag in the ini file (Paul Brannan 5/13/98)
1333 	// We also handle any 8-bit ESC sequence (Paul Brannan 6/28/98)
1334 	if(ini.get_eightbit_ansi() && (tmpc > 128 && tmpc < 128 + ' ')) {
1335 		// There's a chance the sequence might not parse.  If this happens
1336 		// then pszBuffer will be one character too far back, since
1337 		// ParseEscape is expecting two characters, not one.
1338 		// In that case we must handle it.
1339 		char *pszCurrent = pszBuffer;
1340 		pszBuffer = ParseEscape(pszBuffer, pszBufferEnd);
1341 		if(pszBuffer < pszCurrent) pszBuffer = pszCurrent;
1342 	}
1343 
1344 	char* pszCurrent = pszBuffer + 1;
1345 	// I.Ioannou 04 Sep 1997 FIXME with ESC[11m must show chars < 32
1346 	// Fixed (Paul Brannan 6/28/98)
1347 	while ((pszCurrent < pszBufferEnd) && (!iscntrl(*pszCurrent))) {
1348 		// I.Ioannou 04 Sep 1997 strip on high bit
1349 		if ( (inGraphMode) && (*pszCurrent > (char)32) )
1350 			*pszCurrent |= 0x80 ;
1351 		pszCurrent++;
1352 	}
1353 
1354 	// Note that this may break dumpfiles slightly.
1355 	// If 'B' is set to anything other than ASCII, this will cause problems
1356 	// (Paul Brannan 6/28/98)
1357 	if(current_map != 'B' && Charmap.enabled)
1358 		Charmap.translate_buffer(pszBuffer, pszCurrent);
1359 
1360 	last_char = *(pszCurrent-1);    // TITUS++: Remember last char
1361 
1362 	if(fast_write) {
1363 		pszBuffer += Console.WriteStringFast(pszBuffer,
1364 			pszCurrent - pszBuffer);
1365 	} else {
1366 		pszBuffer += Console.WriteString(pszBuffer,
1367 			pszCurrent - pszBuffer);
1368 	}
1369 
1370 	return pszBuffer;
1371 }
1372 
1373 // Added by I.Ioannou 06 April, 1997
1374 // Print the buffer until you reach ESC[4i
1375 char* TANSIParser::PrintBuffer(char* pszBuffer, char* pszBufferEnd) {
1376 	// Check if we have enough characters in buffer.
1377 	if ((pszBufferEnd - pszBuffer) < 4)
1378 		return pszBuffer;
1379 	char *tmpChar;
1380 
1381 	tmpChar = pszBuffer;
1382 	if ( *tmpChar == 27 ) {
1383 		tmpChar++;
1384 		if ( *tmpChar == '[' ) {
1385 			tmpChar++;
1386 			if ( *tmpChar == '4' ) {
1387 				tmpChar++;
1388 				if ( *tmpChar == 'i' ) {
1389 					InPrintMode = 0; // Stop Print Log
1390 					if ( printfile != NULL )
1391 						fclose(printfile);
1392 					pszBuffer += 4;
1393 					return pszBuffer;
1394 				}
1395 			}
1396 		}
1397 	}
1398 
1399 	if (printfile != NULL) {
1400 		fputc( *pszBuffer, printfile);
1401 		pszBuffer++;
1402 	} else
1403 		InPrintMode = 0;
1404 
1405 	return pszBuffer;
1406 }
1407 
1408 /* - PrintGoodChars( pszHead, pszTail ) - - - - - - - - - - - - - - - - - - -
1409 -*
1410 
1411   Mark Miesfield 09/24/2000
1412 
1413   Prints the characters in a buffer, from the specified head to the specified
1414   tail, to standard out, skipping any control characters or ANSI escape
1415   sequences.
1416 
1417   Parameters on entry:
1418     pszHead  ->  Starting point in buffer.
1419 
1420     pszTail  ->  Ending point in buffer.
1421 
1422   Returns:
1423     Pointer to the first character in the buffer that was not output to
1424     standard out.  (Since no error checking is done, this is in effect
1425     pszTail.)
1426 
1427   Side Effects:
1428     None.
1429 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1430 */
1431 char * TANSIParser::PrintGoodChars( char * pszHead, char * pszTail )  {
1432 
1433   while ( pszHead < pszTail )  {
1434     if ( iscntrl( *pszHead ) )  {
1435       switch ( *(pszHead++) )  {
1436         case 10 :
1437           putc( 10, stdout );
1438           break;
1439 
1440         case 13 :
1441           putc( 13, stdout );
1442           break;
1443 
1444         case 27:
1445           switch ( *(pszHead++) )  {
1446             case 'Y':
1447               pszHead += 2;
1448               break;
1449 
1450             case '#':
1451             case '(':
1452             case ')':
1453             case '%': pszHead++; break;
1454             case '[':
1455               while ( (pszHead < pszTail) && (*pszHead < '?') )
1456                 pszHead++;
1457               pszHead++;
1458               break;
1459 
1460             default :
1461               break;
1462           }
1463           break;
1464 
1465         default :
1466           break;
1467       }
1468     }
1469     else
1470       putc( *(pszHead++), stdout );
1471   }
1472   return ( pszTail );
1473 }
1474 // End of function:  PrintGoodChars( pszHead, pszTail )
1475