reactos/rosapps/net/telnet/src/ansiprsr.cpp
Steven Edwards c98929fa7e New Telnet Client
svn path=/trunk/; revision=2458
2001-12-30 10:05:48 +00:00

1474 lines
39 KiB
C++

///////////////////////////////////////////////////////////////////////////////
//Telnet Win32 : an ANSI telnet client.
//Copyright (C) 1998-2000 Paul Brannan
//Copyright (C) 1998 I.Ioannou
//Copyright (C) 1997 Brad Johnson
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either version 2
//of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//I.Ioannou
//roryt@hol.gr
//
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
// Module: ansiprsr.cpp
//
// Contents: ANSI parser base class
//
// Product: telnet
//
// Revisions: August 30, 1998 Paul Brannan <pbranna@clemson.edu>
// July 29, 1998 pbranna@clemson.edu
// June 15, 1998 pbranna@clemson.edu
// May 19, 1998 pbranna@clemson.edu
// 24 Dec, 1997 Andrey.V.Smilianets
// 05. Sep.1997 roryt@hol.gr (I.Ioannou)
// 11.May.1997 roryt@hol.gr (I.Ioannou)
// 6.April.1997 roryt@hol.gr (I.Ioannou)
// 5.April.1997 jbj@nounname.com
// 30.M„rz.1997 Titus_Boxberg@public.uni-hamburg.de
// 14.Sept.1996 jbj@nounname.com
// Version 2.0
//
// 13.Jul.1995 igor.milavec@uni-lj.si
// Original code
//
///////////////////////////////////////////////////////////////////////////////
#include <windows.h>
#include <string.h>
#include "ansiprsr.h"
// The constructor now takes different arguments and initializes different
// variables (Paul Brannan 6/15/98)
TANSIParser::TANSIParser(TConsole &RefConsole, KeyTranslator &RefKeyTrans,
TScroller &RefScroller, TNetwork &RefNetwork,
TCharmap &RefCharmap):
TParser(RefConsole, RefKeyTrans, RefScroller, RefNetwork, RefCharmap) {
Init();
iSavedAttributes = (unsigned char) 7;
// must also check to make sure the string is non-NULL
// (Paul Brannan 5/8/98)
if ((ini.get_dumpfile() != NULL) && (*ini.get_dumpfile() != '\0')){
dumpfile = fopen(ini.get_dumpfile(), "wb");
}else {
dumpfile = NULL;
}
InPrintMode = 0;
printfile = NULL;
fast_write = ini.get_fast_write(); // Paul Brannan 6/28/98
Scroller.init(&StripBuffer);
}
TANSIParser::~TANSIParser(){
if (dumpfile) fclose (dumpfile);
// Added I.Ioannou 06 April, 1997
if (printfile != NULL) fclose (printfile);
}
// Created Init() function to initialize the parser but not clear the screen
// (Paul Brannan 9/23/98)
void TANSIParser::Init() {
// Paul Brannan 6/25/98
map_G0 = 'B'; map_G1 = 'B';
Charmap.setmap(map_G0);
current_map = 'B';
ignore_margins = 0;
vt52_mode = 0;
print_ctrl = 0;
newline_mode = false;
KeyTrans.clear_ext_mode();
iSavedCurY = 0; // Reset Variables
iSavedCurX = 0;
inGraphMode = 0;
Console.SetScroll(-1, -1);
Console.Normal(); // Reset Attributes
// Set tabs stops
resetTabStops();
}
void TANSIParser::ResetTerminal() {
Init();
Console.ClearScreen(); // Clear Screen
Console.SetRawCursorPosition(0,0); // Home Cursor
}
void TANSIParser::SaveCurY(int iY){
iSavedCurY=iY;
}
void TANSIParser::SaveCurX(int iX){
iSavedCurX=iX;
}
void TANSIParser::resetTabStops() {
for(int j = 0; j < MAX_TAB_POSITIONS; j++) {
tab_stops[j] = 8 + j - (j%8);
}
}
void TANSIParser::ConSetAttribute(unsigned char TextAttrib){
// Paul Brannan 5/8/98
// Made this go a little bit faster by changing from switch{} to an array
// for the colors
if(TextAttrib >= 30) {
if(TextAttrib <= 37) {
Console.SetForeground(ANSIColors[TextAttrib-30]);
return;
} else if((TextAttrib >= 40) && (TextAttrib <= 47)) {
Console.SetBackground(ANSIColors[TextAttrib-40]);
return;
}
}
switch (TextAttrib){
// Text Attributes
case 0: Console.Normal(); break; // Normal video
case 1: Console.HighVideo(); break; // High video
case 2: Console.LowVideo(); break; // Low video
case 4: Console.UnderlineOn(); break; // Underline on (I.Ioannou)
case 5: Console.BlinkOn(); break; // Blink video
// Corrected by I.Ioannou 11 May, 1997
case 7: Console.ReverseOn(); break; // Reverse video
case 8: break; // hidden
// All from 10 thru 27 are hacked from linux kernel
// I.Ioannou 06 April, 1997
case 10:
// I.Ioannou 04 Sep 1997 turn on/off high bit
inGraphMode = 0;
print_ctrl = 0;
Charmap.setmap(current_map ? map_G1:map_G0); // Paul Brannan 6/25/98
break; // ANSI X3.64-1979 (SCO-ish?)
// Select primary font,
// don't display control chars
// if defined, don't set
// bit 8 on output (normal)
case 11:
inGraphMode = 0;
print_ctrl = 1;
Charmap.setmap(0); // Paul Brannan 6/25/98
break; // ANSI X3.64-1979 (SCO-ish?)
// Select first alternate font,
// let chars < 32 be displayed
// as ROM chars
case 12:
inGraphMode = 1;
print_ctrl = 1;
Charmap.setmap(0); // Paul Brannan 6/25/98
break; // ANSI X3.64-1979 (SCO-ish?)
// Select second alternate font,
// toggle high bit before
// displaying as ROM char.
case 21: // not really Low video
case 22: Console.LowVideo(); break; // but this works good also
case 24: Console.UnderlineOff(); break; // Underline off
case 25: Console.BlinkOff(); break; // blink off
// Corrected by I.Ioannou 11 May, 1997
case 27: Console.ReverseOff(); break; //Reverse video off
// Mutt needs this (Paul Brannan, Peter Jordan 12/31/98)
// This is from the Linux kernel source
case 38: /* ANSI X3.64-1979 (SCO-ish?)
* Enables underscore, white foreground
* with white underscore (Linux - use
* default foreground).
*/
Console.UnderlineOn();
Console.SetForeground(ini.get_normal_fg());
break;
case 39: /* ANSI X3.64-1979 (SCO-ish?)
* Disable underline option.
* Reset colour to default? It did this
* before...
*/
Console.UnderlineOff();
Console.SetForeground(ini.get_normal_fg());
break;
case 49:
Console.SetBackground(ini.get_normal_bg());
break;
}
}
void TANSIParser::ConSetCursorPos(int x, int y) {
if(ignore_margins)
Console.SetRawCursorPosition(x, y);
else
Console.SetCursorPosition(x, y);
}
char* TANSIParser::GetTerminalID()
{
return "\033[?1;2c";
}
// All of the Telnet protocol stuff has been moved to TTelHndl.cpp
// This is more consistent with what OO should be
// (Paul Brannan 6/15/98)
#ifdef __BORLANDC__
// argsused doesn't work on MSVC++
#pragma argsused
#endif
// Use this for the VT100 flags (Paul Brannan 12/2/98)
#define FLAG_DOLLAR 0x0001
#define FLAG_QMARK 0x0002
#define FLAG_GREATER 0x0004
#define FLAG_LESS 0x0008
#define FLAG_EXCLAM 0x0010
#define FLAG_AMPERSAND 0x0020
#define FLAG_SLASH 0x0040
#define FLAG_EQUAL 0x0080
#define FLAG_QUOTE 0x0100
#define FLAG_OTHER 0x8000
char* TANSIParser::ParseEscapeANSI(char* pszBuffer, char* pszBufferEnd)
{
// The buffer contains something like <ESC>[pA
// where p is an optional decimal number specifying the count by which the
// appropriate action should take place.
// The pointer pszBuffer points us to the p, <ESC> and [ are
// already 'consumed'
// TITUS: Simplification of the code: Assume default count of 1 in case
// there are no parameters.
char tmpc;
const int nParam = 10; // Maximum number of parameters
int iParam[nParam] = {1, 0, 0, 0, 0}; // Assume 1 Parameter, Default 1
int iCurrentParam = 0;
DWORD flag = 0;
int missing_param = 0;
// Get parameters from escape sequence.
while ((tmpc = *pszBuffer) <= '?') {
if(tmpc < '0' || tmpc > '9') {
// Check for parameter delimiter.
if(tmpc == ';') {
// This is a hack (Paul Brannan 6/27/98)
if(*(pszBuffer - 1) == '[') missing_param = iCurrentParam+1;
pszBuffer++;
continue;
}
// It is legal to have control characters inside ANSI sequences
// (Paul Brannan 6/26/98)
if(tmpc < ' ') {
Console.WriteCtrlChar(tmpc);
pszBuffer++;
continue;
}
// A new way of handling flags (Paul Brannan 12/2/98)
switch(tmpc) {
case '$': flag |= FLAG_DOLLAR; break;
case '?': flag |= FLAG_QMARK; break;
case '>': flag |= FLAG_GREATER; break;
case '<': flag |= FLAG_LESS; break;
case '!': flag |= FLAG_EXCLAM; break;
case '&': flag |= FLAG_AMPERSAND; break;
case '/': flag |= FLAG_SLASH; break;
case '=': flag |= FLAG_EQUAL; break;
case '\"': flag |= FLAG_QUOTE; break;
default: flag |= FLAG_OTHER; break;
}
pszBuffer++;
}
// Got Numerical Parameter.
iParam[iCurrentParam] = strtoul(pszBuffer, &pszBuffer, 10);
if (iCurrentParam < nParam)
iCurrentParam++;
}
//~~~ TITUS: Apparently the digit is optional (look at termcap or terminfo)
// So: If there is no digit, assume a count of 1
switch ((unsigned char)*pszBuffer++) {
// Insert Character
case '@':
if(iParam[0] == 0) iParam[0] = 1; // Paul Brannan 9/1/98
Console.InsertCharacter(iParam[0]); break;
// Move cursor up.
case 'A':
if(iParam[0] == 0) iParam[0] = 1;
Console.MoveCursorPosition(0, -iParam[0]); break;
// Move cursor down.
// Added by I.Ioannou 06 April, 1997
case 'B':
case 'e':
if(iParam[0] == 0) iParam[0] = 1;
Console.MoveCursorPosition(0, iParam[0]);
break;
// Move cursor right.
// Added by I.Ioannou 06 April, 1997
case 'C':
case 'a':
// Handle cursor size sequences (Jose Cesar Otero Rodriquez and
// Paul Brannan, 3/27/1999)
if(flag & FLAG_EQUAL) {
switch(iParam[0]) {
case 7: Console.SetCursorSize(50); break;
case 11: Console.SetCursorSize(6); break;
case 32: Console.SetCursorSize(0); break;
default: Console.SetCursorSize(13);
}
} else {
if(iParam[0] == 0) iParam[0] = 1;
Console.MoveCursorPosition(iParam[0], 0);
break;
}
// Move cursor left.
case 'D':
if(iParam[0] == 0) iParam[0] = 1;
Console.MoveCursorPosition(-iParam[0], 0);
break;
// Move cursor to beginning of line, p lines down.
// Added by I.Ioannou 06 April, 1997
case 'E':
Console.MoveCursorPosition(-Console.GetCursorX(), iParam[0]);
break;
// Moves active position to beginning of line, p lines up
// Added by I.Ioannou 06 April, 1997
// With '=' this changes the default fg color (Paul Brannan 6/27/98)
case 'F':
if(flag & FLAG_EQUAL)
Console.setDefaultFg(iParam[0]);
else
Console.MoveCursorPosition(-Console.GetCursorX(), -iParam[0]);
break;
// Go to column p
// Added by I.Ioannou 06 April, 1997
// With '=' this changes the default bg color (Paul Brannan 6/27/98)
case '`':
case 'G': // 'G' is from Linux kernel sources
if(flag & FLAG_EQUAL) {
Console.setDefaultBg(iParam[0]);
} else {
if (iCurrentParam < 1) // Alter Default
iParam[0] = 0;
// this was backward, and we should subtract 1 from x
// (Paul Brannan 5/27/98)
ConSetCursorPos(iParam[0] - 1, Console.GetCursorY());
}
break;
// Set cursor position.
case 'f':
case 'H':
if (iCurrentParam < 2 || iParam[1] < 1)
iParam[1] = 1;
ConSetCursorPos(iParam[1] - 1, iParam[0] - 1);
break;
// Clear screen
case 'J':
if ( iCurrentParam < 1 ) iParam[0] = 0; // Alter Default
switch (iParam[0]) {
case 0: Console.ClearEOScreen(); break;
case 1: Console.ClearBOScreen(); break;
case 2:
Console.ClearScreen();
Console.SetRawCursorPosition(0, 0);
break;
}
break;
// Clear line
case 'K':
if (iCurrentParam < 1) // Alter Default
iParam[0] = 0;
switch (iParam[0]) {
case 0: Console.ClearEOLine(); break;
case 1: Console.ClearBOLine(); break;
case 2: Console.ClearLine(); break;
}
break;
// Insert p new, blank lines.
// Added by I.Ioannou 06 April, 1997
case 'L':
{
// for (int i = 1; i <= iParam[0]; i++)
// This should speed things up a bit (Paul Brannan 9/2/98)
Console.ScrollDown(Console.GetRawCursorY(), -1, iParam[0]);
break;
}
// Delete p lines.
// Added by I.Ioannou 06 April, 1997
case 'M':
{
for (int i = 1; i <= iParam[0]; i++)
// This should speed things up a bit (Paul Brannan 9/2/98)
Console.ScrollDown(Console.GetRawCursorY(), -1, -1);
break;
}
// DELETE CHAR
case 'P':
Console.DeleteCharacter(iParam[0]);
break;
// Scrolls screen up (down? -- PB) p lines,
// Added by I.Ioannou 06 April, 1997
// ANSI X3.64-1979 references this but I didn't
// found it in any telnet implementation
// note 05 Oct 97 : but SCO terminfo uses them, so uncomment them !!
case 'S':
{
//for (int i = 1; i <= iParam[0]; i++)
// This should speed things up a bit (Paul Brannan 9/2/98)
Console.ScrollDown(-1, -1, -iParam[0]);
break;
}
// Scrolls screen up p lines,
// Added by I.Ioannou 06 April, 1997
// ANSI X3.64-1979 references this but I didn't
// found it in any telnet implementation
// note 05 Oct 97 : but SCO terminfo uses them, so uncomment them !!
case 'T':
{
// for (int i = 1; i <= iParam[0]; i++)
// This should speed things up a bit (Paul Brannan 9/2/98)
Console.ScrollDown(-1, -1, iParam[0]);
break;
}
// Erases p characters up to the end of line
// Added by I.Ioannou 06 April, 1997
case 'X':
{
int iKeepX = Console.GetRawCursorX();
int iKeepY = Console.GetRawCursorY();
if (iParam[0] > Console.GetWidth())
iParam[0] = Console.GetWidth(); // up to the end of line
for ( int i = 1; i <= iParam[0]; i++ )
Console.WriteString(" ", 1);
Console.SetRawCursorPosition(iKeepX , iKeepY);
break;
}
// Go back p tab stops
// Added by I.Ioannou 06 April, 1997
// Implemented by Paul Brannan, 4/13/2000
case 'Z':
{
int x = Console.GetCursorX();
for(int j = 0; x > 0 && j < iParam[0]; j++)
while(x > 0 && tab_stops[j] == tab_stops[x]) x--;
Console.SetCursorPosition(x, Console.GetCursorY());
}
break;
// Get Terminal ID
case 'c':
{
char* szTerminalId = GetTerminalID();
Network.WriteString(szTerminalId, strlen(szTerminalId));
break;
}
// TITUS++ 2. November 1998: Repeat Character.
case 'b':
// isprint may be causing problems (Paul Brannan 3/27/99)
// if ( isprint(last_char) ) {
char buf[150]; // at most 1 line (max 132 chars)
if ( iParam[0] > 149 ) iParam[0] = 149;
memset(buf, last_char, iParam[0]);
buf[iParam[0]] = 0;
if ( fast_write )
Console.WriteStringFast(buf, iParam[0]);
else
Console.WriteString(buf, iParam[0]);
// } /* IF */
break;
// Go to line p
// Added by I.Ioannou 06 April, 1997
case 'd':
if (iCurrentParam < 1) // Alter Default
iParam[0] = 0;
// this was backward, and we should subtract 1 from y
// (Paul Brannan 5/27/98)
ConSetCursorPos(Console.GetCursorX(), iParam[0] - 1);
break;
// iBCS2 tab erase
// Added by I.Ioannou 06 April, 1997
case 'g':
if (iCurrentParam < 1) // Alter Default
iParam[0] = 0;
switch (iParam[0]) {
case 0:
{
// Clear the horizontal tab stop at the current active position
for(int j = 0; j < MAX_TAB_POSITIONS; j++) {
int x = Console.GetCursorX();
if(tab_stops[j] == x) tab_stops[j] = tab_stops[x + 1];
}
}
break;
case 2:
// I think this might be "set as default?"
break;
case 3:
{
// Clear all tab stops
for(int j = 0; j < MAX_TAB_POSITIONS; j++)
tab_stops[j] = -1;
}
break;
}
break;
// Set extended mode
case 'h':
{
for (int i = 0; i < iCurrentParam; i++) {
// Changed to a switch statement (Paul Brannan 5/27/98)
if(flag & FLAG_QMARK) {
switch(iParam[i]) {
case 1: // App cursor keys
KeyTrans.set_ext_mode(APP_KEY);
break;
case 2: // VT102 mode
vt52_mode = 0;
KeyTrans.unset_ext_mode(APP2_KEY);
break;
case 3: // 132 columns
if(ini.get_wide_enable()) {
Console.SetWindowSize(132, -1);
}
break;
case 4: // smooth scrolling
break;
case 5: // Light background
Console.Lightbg();
break;
case 6: // Stay in margins
ignore_margins = 0;
break;
case 7:
Console.setLineWrap(true);
break;
case 8: // Auto-repeat keys
break;
case 18: // Send FF to printer
break;
case 19: // Entire screen legal for printer
break;
case 25: // Visible cursor
break;
case 66: // Application numeric keypad
break;
default:
#ifdef DEBUG
Console.Beep();
#endif
break;
}
} else {
switch(iParam[i]) {
case 2: // Lock keyboard
break;
case 3: // Act upon control codes (PB 12/5/98)
print_ctrl = 0;
break;
case 4: // Set insert mode
Console.InsertMode(1);
break;
case 12: // Local echo off
break;
case 20: // Newline sends cr/lf
KeyTrans.set_ext_mode(APP4_KEY);
newline_mode = true;
break;
default:
#ifdef DEBUG
Console.Beep();
#endif
break;
}
}
}
}
break;
// Print Screen
case 'i':
if (iCurrentParam < 1)
iParam[0]=0;
switch (iParam[0]){
case 0: break; // Print Screen
case 1: break; // Print Line
// Added I.Ioannou 06 April, 1997
case 4:
// Stop Print Log
InPrintMode = 0;
if ( printfile != NULL )
fclose(printfile);
break;
case 5:
// Start Print Log
printfile = fopen(ini.get_printer_name(), "ab");
if (printfile != NULL) InPrintMode = 1;
break;
}
break;
// Unset extended mode
case 'l':
{
for (int i = 0; i < iCurrentParam; i++) {
// Changed to a switch statement (Paul Brannan 5/27/98)
if(flag & FLAG_QMARK) {
switch(iParam[i]) {
case 1: // Numeric cursor keys
KeyTrans.unset_ext_mode(APP_KEY);
break;
case 2: // VT52 mode
vt52_mode = 1;
KeyTrans.set_ext_mode(APP2_KEY);
break;
case 3: // 80 columns
if(ini.get_wide_enable()) {
Console.SetWindowSize(80, -1);
}
break;
case 4: // jump scrolling
break;
case 5: // Dark background
Console.Darkbg();
break;
case 6: // Ignore margins
ignore_margins = 1;
break;
case 7:
Console.setLineWrap(false);
break;
case 8: // Auto-repeat keys
break;
case 19: // Only send scrolling region to printer
break;
case 25: // Invisible cursor
break;
case 66: // Numeric keypad
break;
default:
#ifdef DEBUG
Console.Beep();
#endif
break;
}
} else {
switch(iParam[i]) {
case 2: // Unlock keyboard
break;
case 3: // Display control codes (PB 12/5/98)
print_ctrl = 1;
break;
case 4: // Set overtype mode
Console.InsertMode(0);
break;
case 12: // Local echo on
break;
case 20: // sends lf only
KeyTrans.unset_ext_mode(APP4_KEY);
newline_mode = false;
break;
default:
#ifdef DEBUG
Console.Beep();
#endif
break;
}
}
}
}
break;
// Set color
case 'm':
if(missing_param) Console.Normal();
if(iCurrentParam == 0) {
Console.Normal();
} else {
for(int i = 0; i < iCurrentParam; i++)
ConSetAttribute(iParam[i]);
}
break;
// report cursor position Row X Col
case 'n':
if (iCurrentParam == 1 && iParam[0]==5) {
// report the cursor position
Network.WriteString("\x1B[0n", 6);
break;
}
if (iCurrentParam == 1 && iParam[0]==6){
// report the cursor position
// The cursor position needs to be sent as a single string
// (Paul Brannan 6/27/98)
char szCursorReport[40] = "\x1B[";
itoa(Console.GetCursorY() + 1,
&szCursorReport[strlen(szCursorReport)], 10);
strcat(szCursorReport, ";");
itoa(Console.GetCursorX() + 1,
&szCursorReport[strlen(szCursorReport)], 10);
strcat(szCursorReport, "R");
Network.WriteString(szCursorReport, strlen(szCursorReport));
}
break;
// Miscellaneous weird sequences (Paul Brannan 6/27/98)
case 'p':
// Set conformance level
if(flag & FLAG_QUOTE) {
break;
}
// Soft terminal reset
if(flag & FLAG_EXCLAM) {
break;
}
// Report mode settings
if(flag & FLAG_DOLLAR) {
break;
}
break;
// Scroll Screen
case 'r':
if (iCurrentParam < 1) {
// Enable scrolling for entire display
Console.SetScroll(-1, -1);
break;
}
if (iCurrentParam >1) {
// Enable scrolling from row1 to row2
Console.SetScroll(iParam[0] - 1, iParam[1] - 1);
// If the cursor is outside the scrolling range, fix it
// (Paul Brannan 6/26/98)
// if(Console.GetRawCursorY() < iParam[0] - 1) {
// Console.SetRawCursorPosition(Console.GetCursorX(),
// iParam[0] - 1);
// }
// if(Console.GetRawCursorY() > iParam[1] - 1) {
// Console.SetRawCursorPosition(Console.GetCursorX(),
// iParam[1] - 1);
// }
}
// Move the cursor to the home position (Paul Brannan 12/2/98)
Console.SetCursorPosition(0, 0);
break;
// Save cursor position
case 's':
SaveCurY(Console.GetRawCursorY());
SaveCurX(Console.GetRawCursorX());
break;
// Restore cursor position
case 'u':
Console.SetRawCursorPosition(iSavedCurX, iSavedCurY);
break;
// DEC terminal report (Paul Brannan 6/28/98)
case 'x':
if(iParam[0])
Network.WriteString("\033[3;1;1;128;128;1;0x", 20);
else
Network.WriteString("\033[2;1;1;128;128;1;0x", 20);
break;
default:
#ifdef DEBUG
Console.Beep();
#endif
break;
}
return pszBuffer;
}
#ifdef MTE_SUPPORT
// Added by Frediano Ziglio, 5/31/2000
// MTE extension
// initially copied from ParseEscapeANSI
char* TANSIParser::ParseEscapeMTE(char* pszBuffer, char* pszBufferEnd)
{
// The buffer contains something like <ESC>~pA
// where p is an optional decimal number specifying the count by which the
// appropriate action should take place.
// The pointer pszBuffer points us to the p, <ESC> and ~ are
// already 'consumed'
// TITUS: Simplification of the code: Assume default count of 1 in case
// there are no parameters.
char tmpc;
const int nParam = 10; // Maximum number of parameters
int iParam[nParam] = {1, 0, 0, 0, 0}; // Assume 1 parameter, Default 1
int iCurrentParam = 0;
char sRepeat[2];
// Get parameters from escape sequence.
while ((tmpc = *pszBuffer) <= '?') {
if(tmpc < '0' || tmpc > '9') {
// Check for parameter delimiter.
if(tmpc == ';') {
pszBuffer++;
continue;
}
pszBuffer++;
}
// Got Numerical Parameter.
iParam[iCurrentParam] = strtoul(pszBuffer, &pszBuffer, 10);
if (iCurrentParam < nParam)
iCurrentParam++;
}
//~~~ TITUS: Apparently the digit is optional (look at termcap or terminfo)
// So: If there is no digit, assume a count of 1
switch ((unsigned char)*pszBuffer++) {
case 'A':
// set colors
if (iCurrentParam < 2 )
break;
if (iParam[0] <= 15 && iParam[1] <= 15)
Console.SetAttrib( (iParam[1] << 4) | iParam[0] );
break;
case 'R':
// define region
mteRegionXF = -1;
if (iCurrentParam < 2 )
break;
mteRegionXF = iParam[1]-1;
mteRegionYF = iParam[0]-1;
break;
case 'F':
// fill with char
{
if (mteRegionXF == -1 || iCurrentParam < 1)
break;
sRepeat[0] = (char)iParam[0];
sRepeat[1] = '\0';
int xi = Console.GetCursorX(),yi = Console.GetCursorY();
int xf = mteRegionXF;
int yf = mteRegionYF;
mteRegionXF = -1;
for(int y=yi;y<=yf;++y)
{
Console.SetCursorPosition(xi,y);
for(int x=xi;x<=xf;++x)
Console.WriteStringFast(sRepeat,1);
}
}
break;
case 'S':
// Scroll region
{
if (mteRegionXF == -1 || iCurrentParam < 2)
break;
int /*x = Console.GetCursorX(),*/y = Console.GetCursorY();
// int xf = mteRegionXF;
int yf = mteRegionYF;
mteRegionXF = -1;
// !!! don't use x during scroll
int diff = (iParam[0]-1)-y;
if (diff<0)
Console.ScrollDown(y-1,yf,diff);
else
Console.ScrollDown(y,yf+1,diff);
}
break;
// Meridian main version ??
case 'x':
// disable echo and line mode
Network.set_local_echo(0);
Network.set_line_mode(0);
// Meridian Server handle cursor itself
Console.SetCursorSize(0);
break;
// query ??
case 'Q':
if (iParam[0] == 1)
Network.WriteString("\033vga.",5);
break;
default:
#ifdef DEBUG
Console.Beep();
#endif
break;
}
return pszBuffer;
}
#endif
char* TANSIParser::ParseEscape(char* pszBuffer, char* pszBufferEnd) {
char *pszChar;
// Check if we have enough characters in buffer.
if ((pszBufferEnd - pszBuffer) < 2)
return pszBuffer;
// I.Ioannou 04 Sep 1997
// there is no need for pszBuffer++; after each command
// Decode the command.
pszBuffer++;
switch (*pszBuffer++) {
case 'A': // Cursor up
Console.MoveCursorPosition(0, -1);
break;
// Cursor down
case 'B':
Console.MoveCursorPosition(0, 1);
break;
// Cursor right
case 'C':
Console.MoveCursorPosition(1, 0);
break;
// LF *or* cursor left (Paul Brannan 6/27/98)
case 'D':
if(vt52_mode)
Console.MoveCursorPosition(-1, 0);
else
Console.index();
break;
// CR/LF (Paul Brannan 6/26/98)
case 'E':
Console.WriteCtrlString("\r\n", 2);
break;
// Special graphics char set (Paul Brannan 6/27/98)
case 'F':
Charmap.setmap('0');
break;
// ASCII char set (Paul Brannan 6/27/98)
case 'G':
Charmap.setmap('B');
break;
// Home cursor/tab set
case 'H':
if(ini.get_vt100_mode()) {
int x = Console.GetCursorX();
if(x != 0) {
int t = tab_stops[x - 1];
for(int j = x - 1; j >= 0 && tab_stops[j] == t; j--)
tab_stops[j] = x;
}
} else {
// I.Ioannou 04 Sep 1997 (0,0) not (1,1)
ConSetCursorPos(0, 0);
}
break;
// Reverse line feed (Paul Brannan 6/27/98)
// FIX ME!!! reverse_index is wrong to be calling here
// (Paul Brannan 12/2/98)
case 'I':
Console.reverse_index();
break;
// Erase end of screen
case 'J':
Console.ClearEOScreen();
break;
// Erase EOL
case 'K':
Console.ClearEOLine();
break;
// Scroll Up one line //Reverse index
case 'M':
Console.reverse_index();
break;
// Direct cursor addressing
case 'Y':
if ((pszBufferEnd - pszBuffer) >= 2){
// if we subtract '\x1F', then we may end up with a negative
// cursor position! (Paul Brannan 6/26/98)
ConSetCursorPos(pszBuffer[1] - ' ', pszBuffer[0] - ' ');
pszBuffer+=2;
} else {
pszBuffer--; // Paul Brannan 6/26/98
}
break;
// Terminal ID Request
case 'Z':
{
char* szTerminalId = GetTerminalID();
Network.WriteString(szTerminalId, strlen(szTerminalId));
break;
}
// reset terminal to defaults
case 'c':
ResetTerminal();
break;
// Enter alternate keypad mode
case '=':
KeyTrans.set_ext_mode(APP3_KEY);
break;
// Exit alternate keypad mode
case '>':
KeyTrans.unset_ext_mode(APP3_KEY);
break;
// Enter ANSI mode
case '<':
KeyTrans.unset_ext_mode(APP2_KEY); // exit vt52 mode
break;
// Graphics processor on (See note 3)
case '1':
break;
// Line size commands
case '#': //Line size commands
// (Paul Brannan 6/26/98)
if(pszBuffer < pszBufferEnd) {
switch(*pszBuffer++) {
case '3': break; // top half of a double-height line
case '4': break; // bottom half of a double-height line
case '6': break; // current line becomes double-width
case '8': Console.ClearScreen('E'); break;
}
} else {
pszBuffer--;
}
break;
// Graphics processor off (See note 3)
case '2':
break;
// Save cursor and attribs
case '7':
SaveCurY(Console.GetRawCursorY());
SaveCurX(Console.GetRawCursorX());
iSavedAttributes = Console.GetAttrib();
break;
// Restore cursor position and attribs
case '8':
Console.SetRawCursorPosition(iSavedCurX, iSavedCurY);
Console.SetAttrib(iSavedAttributes);
break;
// Set G0 map (Paul Brannan 6/25/98)
case '(':
if (pszBuffer < pszBufferEnd) {
map_G0 = *pszBuffer;
if(current_map == 0) Charmap.setmap(map_G0);
pszBuffer++;
} else {
pszBuffer--;
}
break;
// Set G1 map (Paul Brannan 6/25/98)
case ')':
if (pszBuffer < pszBufferEnd) {
map_G1 = *pszBuffer;
if(current_map == 1) Charmap.setmap(map_G1);
pszBuffer++;
} else {
pszBuffer--;
}
break;
// This doesn't do anything, as far as I can tell, but it does take
// a parameter (Paul Brannan 6/27/98)
case '%':
if (pszBuffer < pszBufferEnd) {
pszBuffer++;
} else {
pszBuffer--;
}
break;
// ANSI escape sequence
case '[':
// Check if we have whole escape sequence in buffer.
// This should not be isalpha anymore (Paul Brannan 9/1/98)
pszChar = pszBuffer;
while ((pszChar < pszBufferEnd) && (*pszChar <= '?'))
pszChar++;
if (pszChar == pszBufferEnd)
pszBuffer -= 2;
else
pszBuffer = ParseEscapeANSI(pszBuffer, pszBufferEnd);
break;
#ifdef MTE_SUPPORT
case '~':
// Frediano Ziglio, 5/31/2000
// Meridian Terminal Emulator extension
// !!! same as ANSI
// !!! should put in MTE procedure
pszChar = pszBuffer;
while ((pszChar < pszBufferEnd) && (*pszChar <= '?'))
pszChar++;
if (pszChar == pszBufferEnd)
pszBuffer -= 2;
else
pszBuffer = ParseEscapeMTE(pszBuffer, pszBufferEnd);
break;
#endif
default:
#ifdef DEBUG
Console.Beep();
#endif
break;
}
return pszBuffer;
}
// This function now only parses the ANSI buffer and does not do anything
// with IAC sequences. That code has been moved to TTelHndl.cpp.
// The scroller update routines have been moved to TScroll.cpp.
// (Paul Brannan 6/15/98)
char* TANSIParser::ParseBuffer(char* pszHead, char* pszTail){
// copy into ANSI buffer
char * pszResult;
// Parse the buffer for ANSI or display
while (pszHead < pszTail) {
if(!ini.get_output_redir()) {
pszResult = ParseANSIBuffer(pszHead, pszTail);
} else {
// Output is being redirected
if(ini.get_strip_redir()) {
// Skip the WriteFile() altogether and pass the buffer to a filter
// Mark Miesfield 09/24/2000
pszResult = PrintGoodChars(pszHead, pszTail);
} else {
DWORD Result;
// Paul Brannan 7/29/98
// Note that this has the unforunate effect of printing out
// NULL (ascii 0) characters onto the screen
if (!WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), pszHead,
pszTail - pszHead, &Result, NULL)) pszResult = pszHead;
pszResult = pszHead + Result;
}
}
if (dumpfile)
fwrite( pszHead, sizeof (char), pszResult-pszHead, dumpfile);
if(ini.get_scroll_enable()) Scroller.update(pszHead, pszResult);
if (pszResult == pszHead) break;
pszHead = pszResult;
}
// return the new head to the buffer
return pszHead;
}
// A simple routine to strip ANSI sequences
// This isn't perfect, but it does an okay job (Paul Brannan 7/5/98)
// Fixed a line counting bug (Paul Brannan 12/4/98)
int TANSIParser::StripBuffer(char* pszHead, char* pszTail, int width) {
int lines = 0, c = 0;
char *pszBuf = pszHead;
while(pszHead < pszTail) {
if(iscntrl(*pszHead)) {
switch(*(pszHead++)) {
case 8:
case 127:
if(c>0) {
if(!(c%width)) lines--;
c--;
pszBuf--;
}
break;
case 10: lines++;
case 13:
*(pszBuf++) = *(pszHead - 1);
c = 0;
break;
case 27:
switch(*(pszHead++)) {
case 'Y': pszHead += 2; break;
case '#':
case '(':
case ')':
case '%': pszHead++; break;
case '[':
while((pszHead < pszTail) && (*pszHead < '?'))
pszHead++;
pszHead++;
break;
}
}
} else {
*(pszBuf++) = *(pszHead++);
c++;
}
if(c != 0 && !(c%width))
lines++;
}
// Fill in the end of the buffer with blanks
while(pszBuf <= pszTail) *pszBuf++ = ' ';
return lines;
}
char* TANSIParser::ParseANSIBuffer(char* pszBuffer, char* pszBufferEnd)
{
if(InPrintMode) {
return PrintBuffer(pszBuffer, pszBufferEnd);
}
unsigned char tmpc = *(unsigned char *)pszBuffer;
if(tmpc == 27) {
return ParseEscape(pszBuffer, pszBufferEnd);
}
// if((fast_write && tmpc < 32) ||
// !print_ctrl && (tmpc < 32 || (EightBit_Ansi &&
// (tmpc > 128 && tmpc < 128 + ' ')))) {
// We shouldn't print ctrl characters when fast write is enabled
// and ctrl chars are disabled (Paul Brannan 9/1/98)
if(tmpc < 32) {
// From the Linux kernel (Paul Brannan 12/5/98):
/* A bitmap for codes <32. A bit of 1 indicates that the code
* corresponding to that bit number invokes some special action
* (such as cursor movement) and should not be displayed as a
* glyph unless the disp_ctrl mode is explicitly enabled.
*/
const long CTRL_ACTION = 0x0d00ff81;
const long CTRL_ALWAYS = 0x0800f501;
if(!(((print_ctrl?CTRL_ALWAYS:CTRL_ACTION)>>tmpc)&1)) {
Console.WriteString((char *)&tmpc, 1);
pszBuffer++;
return pszBuffer;
}
switch (tmpc) {
case 0:
pszBuffer++;
break;
// I.Ioannou 5/30/98
case 7:
Console.Beep();
pszBuffer++;
break;
// destructive backspace
case 8:
// Added option for destructive backspace (Paul Brannan 5/13/98)
// Changed to ConWriteCtrlString so that the cursor position can be
// updated (Paul Brannan 5/25/98)
if(ini.get_dstrbksp()) {
Console.WriteCtrlChar('\b');
Console.WriteString(" ", 1);
Console.WriteCtrlChar('\b');
}
else Console.WriteCtrlChar('\b');
pszBuffer++;
break;
// horizontal tab
case 9:
{
pszBuffer++;
int x = Console.GetCursorX();
if(x != -1)
Console.SetCursorPosition(tab_stops[x], Console.GetCursorY());
}
break;
// Line Feed Char
case 10:
// Test for local echo (Paul Brannan 8/25/98)
if(Network.get_local_echo() || newline_mode) // &&
Console.WriteCtrlChar('\x0d');
Console.WriteCtrlChar('\x0a');
pszBuffer++;
break;
// form feed
case 12:
pszBuffer++;
Console.ClearScreen();
Console.SetRawCursorPosition(Console.GetCursorX(), 1); // changed fm 1
break;
case 13:
Console.WriteCtrlChar('\x0d');
pszBuffer++;
break;
case 14: // shift out of alternate chararcter set
pszBuffer++;
Charmap.setmap(map_G1); // Paul Brannan 6/25/98
current_map = 1;
break;
case 15: // shift in
pszBuffer++;
Charmap.setmap(map_G0); // Paul Brannan 6/25/98
current_map = 0;
break;
// Paul Brannan 9/1/98 - Is this okay?
default:
pszBuffer++;
}
return pszBuffer;
}
// added by I.Ioannou 06 April, 1997
// In 8 bit systems the server may send 0x9b instead of ESC[
// Well, this will produce troubles in Greek 737 Code page
// which uses 0x9b as the small "delta" - and I thing that there
// is another European country with the same problem.
// If we have to stay 8-bit clean we may have to
// give the ability of ROM characters (ESC[11m),
// for striped 8'th bit (ESC[12m) as SCO does,
// or a parameter at compile (or run ?) time.
// We now check for a flag in the ini file (Paul Brannan 5/13/98)
// We also handle any 8-bit ESC sequence (Paul Brannan 6/28/98)
if(ini.get_eightbit_ansi() && (tmpc > 128 && tmpc < 128 + ' ')) {
// There's a chance the sequence might not parse. If this happens
// then pszBuffer will be one character too far back, since
// ParseEscape is expecting two characters, not one.
// In that case we must handle it.
char *pszCurrent = pszBuffer;
pszBuffer = ParseEscape(pszBuffer, pszBufferEnd);
if(pszBuffer < pszCurrent) pszBuffer = pszCurrent;
}
char* pszCurrent = pszBuffer + 1;
// I.Ioannou 04 Sep 1997 FIXME with ESC[11m must show chars < 32
// Fixed (Paul Brannan 6/28/98)
while ((pszCurrent < pszBufferEnd) && (!iscntrl(*pszCurrent))) {
// I.Ioannou 04 Sep 1997 strip on high bit
if ( (inGraphMode) && (*pszCurrent > (char)32) )
*pszCurrent |= 0x80 ;
pszCurrent++;
}
// Note that this may break dumpfiles slightly.
// If 'B' is set to anything other than ASCII, this will cause problems
// (Paul Brannan 6/28/98)
if(current_map != 'B' && Charmap.enabled)
Charmap.translate_buffer(pszBuffer, pszCurrent);
last_char = *(pszCurrent-1); // TITUS++: Remember last char
if(fast_write) {
pszBuffer += Console.WriteStringFast(pszBuffer,
pszCurrent - pszBuffer);
} else {
pszBuffer += Console.WriteString(pszBuffer,
pszCurrent - pszBuffer);
}
return pszBuffer;
}
// Added by I.Ioannou 06 April, 1997
// Print the buffer until you reach ESC[4i
char* TANSIParser::PrintBuffer(char* pszBuffer, char* pszBufferEnd) {
// Check if we have enough characters in buffer.
if ((pszBufferEnd - pszBuffer) < 4)
return pszBuffer;
char *tmpChar;
tmpChar = pszBuffer;
if ( *tmpChar == 27 ) {
tmpChar++;
if ( *tmpChar == '[' ) {
tmpChar++;
if ( *tmpChar == '4' ) {
tmpChar++;
if ( *tmpChar == 'i' ) {
InPrintMode = 0; // Stop Print Log
if ( printfile != NULL )
fclose(printfile);
pszBuffer += 4;
return pszBuffer;
}
}
}
}
if (printfile != NULL) {
fputc( *pszBuffer, printfile);
pszBuffer++;
} else
InPrintMode = 0;
return pszBuffer;
}
/* - PrintGoodChars( pszHead, pszTail ) - - - - - - - - - - - - - - - - - - -
-*
Mark Miesfield 09/24/2000
Prints the characters in a buffer, from the specified head to the specified
tail, to standard out, skipping any control characters or ANSI escape
sequences.
Parameters on entry:
pszHead -> Starting point in buffer.
pszTail -> Ending point in buffer.
Returns:
Pointer to the first character in the buffer that was not output to
standard out. (Since no error checking is done, this is in effect
pszTail.)
Side Effects:
None.
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
char * TANSIParser::PrintGoodChars( char * pszHead, char * pszTail ) {
while ( pszHead < pszTail ) {
if ( iscntrl( *pszHead ) ) {
switch ( *(pszHead++) ) {
case 10 :
putc( 10, stdout );
break;
case 13 :
putc( 13, stdout );
break;
case 27:
switch ( *(pszHead++) ) {
case 'Y':
pszHead += 2;
break;
case '#':
case '(':
case ')':
case '%': pszHead++; break;
case '[':
while ( (pszHead < pszTail) && (*pszHead < '?') )
pszHead++;
pszHead++;
break;
default :
break;
}
break;
default :
break;
}
}
else
putc( *(pszHead++), stdout );
}
return ( pszTail );
}
// End of function: PrintGoodChars( pszHead, pszTail )