/////////////////////////////////////////////////////////////////////////////// //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 // 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 "precomp.h" const int ANSIColors[] = {BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE}; // 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); } const 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 [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, 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': { const 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", 4); 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 ~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, 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': { const 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 )