reactos/posix/apps/posixw32/vt100.c
2002-10-29 04:45:58 +00:00

1125 lines
31 KiB
C

/* vt100.c
*
* AUTHOR: John L. Miller, johnmil@cs.cmu.edu / johnmil@jprc.com
* DATE: 8/4/96
*
* Copyright (c) 1996 John L. Miller
*
* Full permission is granted to use, modify and distribute
* this code, provided:
* 1) This comment field is included in its entirity
* 2) No money is charged for any work including or based on
* portions of this code.
*
* If you're a nice person and find this useful, I'd appreciate a
* note letting me know about it. e-mail is usually what spurs me
* on to improve and support software I've written.
*
*/
/* This is the main code body for my generic vt-100 emulator. This code
* body provides parsing for most of the vt-100 escape sequences, but it
* doesn't actually do anything with some of them. The idea behind this
* parser is to provide a generic front-end that you can initialize, then
* send all of your output to. The output is parsed by the routines in this
* program, then spit out to a back-end.
*
* What back-end you say? Well, the one you have to supply yourself. There's a
* dozen or so routines you have to provide for character-based I/O, cursor
* movement, erasing and deleting text, and setting text and terminal attributes.
*
* For a list of the routines your back end must supply, read vt100.h closely.
*
* In case it's not obvious, these routines were written for a system running win32.
* for vt100.c and vt100.h, most of the code should be easily portable to other
* operating systems. Yeah, like they NEED a vt-100 emulator :p
*/
#include <windows.h>
#include <stdio.h>
#include <stdarg.h>
#include "vt100.h"
/* NOTE - many of the functions look like they should
* take X-Y pairs. Bear in mind this is a text display,
* so for this we're talking ROWS and COLUMNS. So parm
* lists go (row, column), which is the opposite of (x, y).
*/
char cBuffer[MAXVTBUFLEN+1]; /* terminal output buffer for unprocessed characters */
int BufLen; /* Number of characters in cBuffer waiting for output */
/* List of all device-independant colors. These colors will be transmitted to a
* back-end routine responsible for setting the foreground and background text
* colors appropriately. Note that the color combinations are ordered to correspond
* with combinations of red (0x1), green (0x2) and blue (0x4) bit flags.
*/
int ScColorTrans[8]= { 0,
SC_RED,
SC_GREEN,
SC_RED|SC_GREEN,
SC_BLUE,
SC_RED|SC_BLUE,
SC_RED|SC_GREEN,
SC_RED|SC_GREEN|SC_BLUE
};
/* List of terminal attributes which we track (used by <esc>[?#h and <esc>[?#l) */
int termAttrMode[NUM_TERM_ATTR_MODES] = { 0,
CURSORAPPL_MODE,
ANSI_MODE,
COL132_MODE,
SMOOTHSCROLL_MODE,
REVSCREEN_MODE,
ORIGINREL_MODE,
WRAP_MODE,
REPEAT_MODE,
};
/* FORWARD FUNCTION DECLARATIONS -
* these functions are intended for use only in this module */
static int ProcessBracket(int Start);
static int ProcessEscape(int Start);
static int ProcessControl(int Start);
static int ProcessBuffer(void);
/* END FORWARD FUNCTION DECLARATIONS */
/* vtputs() -
*
* front-end 'puts()' substitute. Call this routine instead
* of 'puts()', and it'll pass the output through the vt100 emulator.
*/
vtputs(char *f)
{
char cbuf[1024];
strcpy(cbuf,f);
strcat(cbuf,"\n");
vtProcessedTextOut(cbuf, strlen(cbuf));
return(0);
}
/* vtprintf -
*
* This routine is a substitute for printf(). Call this routine with the
* same parameters you would use for printf, and output will be channelled
* through the vt-100 emulator.
*/
vtprintf(char *format, ...)
{
char cbuf[1024];
va_list va;
va_start(va, format);
vsprintf(cbuf,format, va);
va_end(va);
vtProcessedTextOut(cbuf, strlen(cbuf));
return(0);
}
/* vtInitVT100 -
*
* Set the initial state of the VT-100 emulator, and call the initialization
* routine for the back end. This routine MUST be invoked before any other, or
* the VT-100 emulator will most likely roll over and die.
*/
vtInitVT100(void)
{
int i=0;
cBuffer[0]='\0';
BufLen=0;
beInitVT100Terminal(); /* call the back-end initialization. */
return(0);
}
/* ProcessBracket -
*
* Helper routine for processing escape sequences. By the time this
* routine is invoked, '<esc>[' has already been read in the input
* stream. 'Start' is an index in cBuffer to the '<esc>'.
*
* If an escape sequence is successfully parsed, return the index of the
* first character AFTER the escape sequence. Otherwise, return 'Start'.
*
*/
static int ProcessBracket(int Start)
{
int End; /* Current character being examined in cBuffer */
int args[10], numargs=0; /* numerical args after <esc>[ */
int iMode;
int i;
int itmp=0;
int left;
int iForeground, iBackground;
/* If there's no valid escape sequence, return as we were called. */
if ((cBuffer[Start+1] != '[')||(Start+2 >= BufLen))
return(Start);
End = Start + 2;
/* Loop through the buffer, hacking out all numeric
* arguments (consecutive string of digits and
* semicolons
*/
do {
itmp = 0; /* itmp will hold the current arguments integer value */
/* the semicolon is a delimiter */
if (cBuffer[End] == ';')
{
End++;
if (End >= BufLen)
return(Start);
}
/* Parse this argument into a number. */
while (isdigit(cBuffer[End]))
{
itmp = itmp*10 + (cBuffer[End]-'0');
End++;
if (End >= BufLen)
return(Start);
}
/* Save the numeric argument if we actually
* parsed a number.
*/
if (End != Start + 2)
args[numargs++] = itmp;
} while (cBuffer[End] == ';');
/* At this point, we've come across a character that isn't a number
* and isn't a semicolon. This means it is the command specifier.
*/
/* Number of characters left in the input stream. I don't use
* this as rigorously as I should here.
*/
left = BufLen - End;
/* Return if there's definitely not enough characters for a
* full escape sequence.
*/
if (left <= 0)
return(Start);
/* Giant switch statement, parsing the command specifier that followed
* up <esc>[arg;arg;...arg
*/
switch (cBuffer[End])
{
/* Cursor Up: Esc [ Pn A */
case 'A':
beOffsetCursor(numargs ? -args[0] : -1, 0);
End += 1;
break;
/* Cursor Down: Esc [ Pn B */
case 'B':
beOffsetCursor(numargs ? args[0] : 1, 0);
End += 1;
break;
/* Cursor Right: Esc [ Pn C */
case 'C':
beOffsetCursor(0, numargs ? args[0] : 1);
End += 1;
break;
/* Cursor Left: Esc [ Pn D */
case 'D':
beOffsetCursor(0, numargs ? -args[0] : -1);
End += 1;
break;
/* Direct Addressing : Esc [ Pn(row);Pn(col)H or
* Esc [ Pn(row);Pn(col)f
*/
case 'H':
case 'f':
if (numargs == 0)
beAbsoluteCursor(1,1);
else if (numargs == 1)
beAbsoluteCursor(args[0] > 0 ? args[0] : 1,1);
else if (numargs == 2)
beAbsoluteCursor(args[0] > 0 ? args[0] : 1, args[1] > 0 ? args[1] : 1);
End += 1;
break;
/* Erase from Cursor to end of screen Esc [ J
* Erase from Beginning of screen to cursor Esc [ 1 J
* Erase Entire screen Esc [ 2 J
*/
case 'J':
if (numargs == 0)
beEraseText(CUR_ROW, CUR_COL, BOTTOM_EDGE, RIGHT_EDGE);
else if (args[0] == 1)
beEraseText(TOP_EDGE, LEFT_EDGE, CUR_ROW, CUR_COL);
else
beEraseText(TOP_EDGE, LEFT_EDGE, BOTTOM_EDGE, RIGHT_EDGE);
End += 1;
break;
/* Erase from Cursor to end of line Esc [ K
* Erase from Beginning of line to cursor Esc [ 1 K
* Erase Entire line Esc [ 2 K
*/
case 'K':
if (numargs == 0)
beEraseText(CUR_ROW, CUR_COL, CUR_ROW, RIGHT_EDGE);
else if (args[0] == 1)
beEraseText(CUR_ROW, LEFT_EDGE, CUR_ROW, CUR_COL);
else
beEraseText(CUR_ROW, LEFT_EDGE, CUR_ROW, RIGHT_EDGE);
End += 1;
break;
/* Set Graphics Rendition:
* ESC[#;#;....;#m
* The graphics rendition is basically foreground and background
* color and intensity.
*/
case 'm':
beGetTextAttributes(&iForeground, &iBackground);
if (numargs < 1)
{
/* If we just get ESC[m, treat it as though
* we should shut off all extra text
* attributes
*/
iForeground &= ~(SC_BOLD|SC_UL|SC_BL|SC_RV|SC_GRAPHICS|SC_G0|SC_G1);
iForeground |= SC_ASCII;
beSetTextAttributes(iForeground, iBackground);
End += 1;
break;
}
/* Loop through all the color specs we got, and combine them
* together. Note that things like 'reverse video', 'bold',
* and 'blink' are only applied to the foreground. The back end
* is responsible for applying these properties to all text.
*/
for (i=0; i < numargs; i++)
{
switch(args[i])
{
/* 0 for normal display */
case 0:
iForeground &= ~SC_BOLD;
break;
/* 1 for bold on */
case 1:
iForeground |= SC_BOLD;
break;
/* 4 underline (mono only) */
case 4:
iForeground |= SC_UL;
break;
/* 5 blink on */
case 5:
iForeground |= SC_BL;
break;
/* 7 reverse video on */
case 7:
iForeground |= SC_RV;
break;
/* 8 nondisplayed (invisible) BUGBUG - not doing this. */
/* 30-37 is bit combination of 30+ red(1) green(2) blue(4)
* 30 black foreground
*/
case 30:
case 31:
case 32:
case 33:
case 34:
case 35:
case 36:
case 37:
iForeground &= ~(SC_RED|SC_GREEN|SC_BLUE);
iForeground |= ScColorTrans[args[i]-30];
break;
/* 40-47 is bit combo similar to 30-37, but for background. */
case 40:
case 41:
case 42:
case 43:
case 44:
case 45:
case 46:
case 47:
iBackground &= ~(SC_RED|SC_GREEN|SC_BLUE);
iBackground |= ScColorTrans[args[i]-30];
break;
}
}
beSetTextAttributes(iForeground, iBackground);
End += 1;
break;
/*
* Set with Esc [ Ps h
* Reset with Esc [ Ps l
* Mode name Ps Set Reset
* -------------------------------------------------------------------
* Keyboard action 2 Locked Unlocked
* Insertion 4 Insert Overwrite
* Send - Receive 12 Full Echo
* Line feed/New line 20 New line Line feed
*/
case 'h':
case 'l':
/* BUGBUG - many of the terminal modes are set with '?' as the
* first character rather than a number sign. These are dealt with
* later in this switch statement because they must be. Most other
* settings are just ignored, however.
*/
End += 1;
break;
/* Insert line Esc [ Pn L */
case 'L':
beInsertRow(CUR_ROW);
End += 1;
break;
/* Delete line Esc [ Pn M */
case 'M':
do {
beDeleteText(CUR_ROW,LEFT_EDGE, CUR_ROW, RIGHT_EDGE);
} while (--args[0] > 0);
End += 1;
break;
/* Delete character Esc [ Pn P */
case 'P':
do {
beDeleteText(CUR_ROW, CUR_COL, CUR_ROW, CUR_COL);
} while (--args[0] > 0);
End += 1;
break;
/* Set the Scrolling region Esc [ Pn(top);Pn(bot) r */
case 'r':
if (numargs == 0)
beSetScrollingRows(TOP_EDGE,BOTTOM_EDGE);
else if (numargs == 2)
beSetScrollingRows(args[0],args[1]);
End += 1;
break;
/* Print screen or region Esc [ i
* Print cursor line Esc [ ? 1 i
* Enter auto print Esc [ ? 5 i
* Exit auto print Esc [ ? 4 i
* Enter print controller Esc [ 5 i
* Exit print controller Esc [ 4 i
*/
case 'i':
/* BUGBUG - print commands are not acted upon. */
End += 1;
break;
/* Clear tab at current column Esc [ g
* Clear all tabs Esc [ 3 g
*/
case 'g':
if (numargs == 0)
beClearTab(CUR_COL);
else
if ((numargs == 1) && (args[0] == 3))
beClearTab(ALL_TABS);
End += 1;
break;
/* BUGBUG - queries which require responses are ignored. */
/* Esc [ c DA:Device Attributes
* or
*
* Esc [ <sol> x DECREQTPARM: Request Terminal Parameters
* * <sol> values other than 1 are ignored. Upon
* receipt of a <sol> value of 1, the following
* response is sent:
* Esc[3;<par>;<nbits>;<xspeed>;<rspeed>;1;0x
*
* * Where <par>, <nbits>, <xspeed>, and <rspeed>
* are as for VT100s with the following
* exceptions:
* <nbits> Values of 5 and 6 bits per
* character are sent as 7 bits.
* <xspeed>,<rspeed>
* These two numbers will always
* be the same. 9600 baud is
* sent for 7200 baud.
*
* Esc [ Ps n DSR: Device Status Report
* * Parameter values other than 5, 6, are ignored.
* If the parameter value is 5, the sequence
* Esc [ O n is returned. If the parameter value is
* 6, the CPR: Cursor Position Report sequence
* Esc [ Pn ; Pn R is returned with the Pn set to
* cursor row and column numbers.
*
* Cursor Controls:
* ESC[#;#R Reports current cursor line & column
*/
/* spec <esc>[<spec>h <esc>[<spec>l
* Cursor key ?1 Application Cursor
* ANSI/VT52 ?2 ANSI VT52
* Column ?3 132 80
* Scrolling ?4 Smooth Jump
* Screen ?5 Reverse Normal
* Origin ?6 Relative Absolute
* Wraparound ?7 Wrap Truncate
* Auto key repeat ?8 Repeating No repeat
*/
case '?':
/* We didn't catch the numeric argument because the '?' stopped
* it before. Parse it now.
*/
args[0]=0;
while (isdigit(cBuffer[++End]))
{
if (End >= BufLen)
return(Start);
args[0] = 10*args[0] + (cBuffer[End]-'0');
}
/* If we don't handle this particular '?' command (and
* there are plenty we don't) then just ignore it.
*/
if ( (args[0] > 8)
||( (cBuffer[End] != 'l') && (cBuffer[End] != 'h'))
)
{
End++;
if (End >= BufLen)
return(Start);
break;
}
/* This command sets terminal status. Get the current status,
* determine what needs to be changed, and send it back.
*/
iMode = beGetTermMode();
/* If we need a given mode and it's not already set, set it. */
if ((cBuffer[End] == 'h') && (!(iMode & termAttrMode[args[0]])))
{
beSetTermMode(iMode | termAttrMode[args[0]]);
}
/* likewise, clear it as appropriate */
if ((cBuffer[End] == 'l') && (iMode & termAttrMode[args[0]]))
{
beSetTermMode(iMode & ~termAttrMode[args[0]]);
}
End++;
break;
/* If it's an escape sequence we don't treat, pretend as though we never saw
* it.
*/
default:
End += 1;
break;
}
return(End);
}
/* ProcessEscape -
*
* At this point, <esc> has been seen. Start points to the escape
* itself, and then this routine is responsible for parsing the
* rest of the escape sequence, and either pawning off further parsing,
* or acting upon it as appropriate.
*
* Note that the escape sequences being parsed are still contained in cBuffer.
*/
static int ProcessEscape(int Start)
{
int End;
int left;
int fore, back;
int i;
/* if it's definitely not a full escape sequence, return that we haven't
* seen it.
*/
if ((cBuffer[Start] != 27)||(Start+1 >= BufLen))
return(Start);
End = Start + 1;
/* At this point, if the sequence is <esc> x, 'End' points at
* x
*/
/* left = number of characters left unparsed in the buffer. */
left = BufLen - End -1;
/* Main switch statement - parse the escape sequence according to the
* next character we see.
*/
switch (cBuffer[End])
{
/* Hard Reset Esc c BUGBUG - not imp'd. */
case 'c':
End += 1;
break;
/* Cursor up Esc A */
case 'A':
beOffsetCursor(-1,0);
End += 1;
break;
/* Cursor down Esc B */
case 'B':
beOffsetCursor(1,0);
End += 1;
break;
/* Cursor right Esc C */
case 'C':
beOffsetCursor(0,1);
End += 1;
break;
/* Cursor left Esc D */
case 'D':
beOffsetCursor(0,-1);
End += 1;
break;
/* Newline command: Esc E */
case 'E':
beRawTextOut("\n",strlen("\n"));
End += 1;
break;
/* Invoke the Graphics character set Esc F */
case 'F':
beGetTextAttributes(&fore, &back);
if (! (fore & SC_GRAPHICS))
{
fore &= ~(SC_ASCII|SC_G0|SC_G1);
fore |= SC_GRAPHICS;
beSetTextAttributes(fore, back);
}
End += 1;
break;
/* Invoke the ASCII character set Esc G */
case 'G':
beGetTextAttributes(&fore, &back);
if (! (fore & SC_ASCII))
{
fore &= ~(SC_G0|SC_G1|SC_GRAPHICS);
fore |= SC_ASCII;
beSetTextAttributes(fore, back);
}
End += 1;
break;
/* Move the cursor to (1,1): Home cursor Esc H */
case 'H':
beAbsoluteCursor(TOP_EDGE,LEFT_EDGE);
End += 1;
break;
/* Reverse line feed Esc I */
case 'I':
beOffsetCursor(-1,0);
End += 1;
break;
/* Erase to end of screen Esc J */
case 'J':
beEraseText(CUR_ROW, CUR_COL, BOTTOM_EDGE, RIGHT_EDGE);
End += 1;
break;
/* Erase to end of line Esc K */
case 'K':
beEraseText(CUR_ROW, CUR_COL, CUR_ROW, RIGHT_EDGE);
End += 1;
break;
/* Reverse Line: Esc M */
case 'M':
beAbsoluteCursor(CUR_ROW, LEFT_EDGE);
beOffsetCursor(-1,0);
End += 1;
break;
/* Switch to G1 graphics character set. Esc N */
case 'N':
beGetTextAttributes(&fore, &back);
if (! (fore & SC_G1))
{
fore &= ~(SC_G0|SC_ASCII|SC_GRAPHICS);
fore |= SC_G1;
beSetTextAttributes(fore, back);
}
End += 1;
break;
/* Switch to G0 graphics character set Esc O */
case 'O':
beGetTextAttributes(&fore, &back);
if (! (fore & SC_G0))
{
fore &= ~(SC_G1|SC_ASCII|SC_GRAPHICS);
fore |= SC_G0;
beSetTextAttributes(fore, back);
}
End += 1;
break;
/* Print cursor line Esc V BUGBUG - unimp'd */
case 'V':
End += 1;
break;
/* Enter print controller Esc W BUGBUG - unimp'd */
case 'W':
End += 1;
break;
/* Exit print controller Esc X BUGBUG - unimp'd */
case 'X':
End += 1;
break;
/* Cursor address Esc Y row col BUGBUG - unimp'd and needed */
case 'Y':
End += 1;
break;
/* Identify terminal type Esc Z */
case 'Z':
beTransmitText(ANSWERBACK_MESSAGE,strlen(ANSWERBACK_MESSAGE));
End += 1;
break;
/* One of dozens of escape sequences starting <esc>[. Parse further */
case '[':
/* pass in the escape as the starting point */
End = ProcessBracket(End-1);
break;
/* Print screen Esc ] BUGBUG - unimp'd */
case ']':
End += 1;
break;
/* Enter auto print Esc ^ BUGBUG - unimpd' */
case '^':
End += 1;
break;
/* Exit auto print Esc - BUGBUG - unimpd' */
case '-':
End += 1;
break;
/* Alternate keypad Esc = BUGBUG - unimpd' */
case '=':
End += 1;
break;
/* Numeric keypad Esc > BUGBUG - unimpd' */
case '>':
End += 1;
break;
/* Enter ANSI mode Esc < BUGBUG - unimpd' */
case '<':
End += 1;
break;
/* Save cursor position & Attributes: Esc 7 */
case '7':
beSaveCursor();
End += 1;
break;
/* Restore cursor position & attributes: Esc 8 */
case '8':
beRestoreCursor();
End += 1;
break;
/* Set character size - BUGBUG - unimp'd.
* # 1 Double ht, single width top half chars
* # 2 Double ht, single width lower half chars
* # 3 Double ht, double width top half chars
* # 4 Double ht, double width lower half chars
* # 5 Single ht, single width chars
* # 6 Single ht, double width chars
*/
case '#':
End += 1;
break;
/* Select character set
* ESC ( A British
* ESC ( C Finnish
* ESC ( E Danish or Norwegian
* ESC ( H Swedish
* ESC ( K German
* ESC ( Q French Canadian
* ESC ( R Flemish or French/Belgian
* ESC ( Y Italian
* ESC ( Z Spanish
* ESC ( 1 Alternative Character
* ESC ( 4 Dutch
* ESC ( 5 Finnish
* ESC ( 6 Danish or Norwegian
* ESC ( 7 Swedish
* ESC ( = Swiss (French or German)
*/
case '(':
case ')':
/* BUGBUG - most character sets aren't implemented. */
beGetTextAttributes(&fore, &back);
switch (cBuffer[++End])
{
case 'B': /* ESC ( B North American ASCII set */
i=SC_ASCII;
break;
case '0': /* ESC ( 0 Line Drawing */
i=SC_G1;
break;
case '2': /* ESC ( 2 Alternative Line drawing */
i=SC_G0;
break;
default:
/* Make sure the screen mode isn't set. */
i = 0xFFFFFFFF;
break;
}
if (! (fore & i))
{
fore &= ~(SC_ASCII|SC_G0|SC_G1|SC_GRAPHICS);
fore |= i;
beSetTextAttributes(fore, back);
}
End += 1;
break;
/* Unknown escape sequence */
default:
{
char cbuf[80];
sprintf(cbuf,"<esc>%c", cBuffer[End]);
beRawTextOut(cbuf+Start,6);
End += 1;
}
}
return(End);
}
/* ProcessControl -
*
* Process a probable escape or control sequence
* starting at the supplied index. Return the index
* of the first character *after* the escape sequence we
* process.
* In the case of an incomplete sequence, ie one
* that isn't completed by the end of the buffer, return
* 'Start'.
* In the case of an invalid sequence,
* note the invalid escape sequence, and return Start+1
*/
static int ProcessControl(int Start)
{
int fore, back;
int End = Start;
/* Check to make sure we at least have enough characters
* for a control character
*/
if (Start >= BufLen)
return(Start);
switch (cBuffer[Start])
{
/* NUL 0 Fill character; ignored on input.
* DEL 127 Fill character; ignored on input.
*/
case 0:
case 127:
End += 1;
break;
/* ENQ 5 Transmit answerback message. */
case 5:
beTransmitText(ANSWERBACK_MESSAGE,strlen(ANSWERBACK_MESSAGE));
End += 1;
break;
/* BEL 7 Ring the bell. */
case 7:
beRingBell();
End += 1;
break;
/* BS 8 Move cursor left. */
case 8:
beOffsetCursor(0,-1);
End += 1;
break;
/* HT 9 Move cursor to next tab stop. */
case 9:
beAdvanceToTab();
End += 1;
break;
/* LF 10 Line feed; causes print if in autoprint. */
case 10:
beOffsetCursor(1,0);
End += 1;
break;
/* VT 11 Same as LF.
* FF 12 Same as LF.
*/
case 11:
case 12:
beOffsetCursor(1,0);
End += 1;
break;
/* CR 13 Move cursor to left margin or newline. */
case 13:
beOffsetCursor(1,0);
beAbsoluteCursor(CUR_ROW,LEFT_EDGE);
End += 1;
break;
/* SO 14 Invoke G1 character set. */
case 14:
beGetTextAttributes(&fore, &back);
if (! (fore & SC_G1))
{
fore &= ~(SC_ASCII|SC_G0|SC_G1|SC_GRAPHICS);
fore |= SC_G1;
beSetTextAttributes(fore, back);
}
End += 1;
break;
/* SI 15 Invoke G0 character set. */
case 15:
beGetTextAttributes(&fore, &back);
if (! (fore & SC_G0))
{
fore &= ~(SC_ASCII|SC_G0|SC_G1|SC_GRAPHICS);
fore |= SC_G0;
beSetTextAttributes(fore, back);
}
End += 1;
break;
/* CAN 24 Cancel escape sequence and display checkerboard. BUGBUG - not imp'd.
* SUB 26 Same as CAN.
*/
case 24:
End += 1;
break;
/* ESC 27 Introduce a control sequence. */
case 27:
End = ProcessEscape(Start);
break;
/* Print any other control character received. */
default:
{
char buf[4];
sprintf(buf,"^%c",'A' + cBuffer[Start] - 1);
beRawTextOut(buf, 2);
End += 1;
}
break;
}
return(End);
}
/* ProcessBuffer -
*
* Process the current contents of the terminal character buffer.
*/
static int ProcessBuffer(void)
{
int Start=0,End=0;
/* Null-terminate the buffer. Why? Heck, why not? */
cBuffer[BufLen] = '\0';
/* Loop through the entire buffer. Start will be incremented
* to point at the start of unprocessed text in the buffer as
* we go.
*/
while (Start < BufLen)
{
End = Start;
/* Since we null-terminated, null < 27 and we have a termination condition */
while ((cBuffer[End] > 27)||(cBuffer[End] == 10)||(cBuffer[End] == 13))
End++;
/* At this point, if Start < End, we have a string of characters which
* doesn't have any control sequences, and should be printed raw.
*/
if (End > Start)
beRawTextOut(cBuffer+Start, End-Start);
if (End >= BufLen)
{
break;
}
/* At this point, 'End' points to the beginning of an escape
* sequence. We'll reset 'start' to be AFTER parsing the
* escape sequence. Note that if the escape sequence
* is incomplete, ProcessControl should return the
* same value passed in. Otherwise, it'll return the
* index of the first character after the valid
* escape sequence.
*/
Start = ProcessControl(End);
if (Start == End)
{
/* The escape sequence was incomplete.
* Move the unprocessed portion of the input buffer
* to start at the beginning of the buffer, then
* return.
*/
while (End < BufLen)
{
cBuffer[End-Start] = cBuffer[End];
End += 1;
}
BufLen = End-Start;
return(0);
}
}
/* If we made it this far, Start == Buflen, and so we've finished
* processing the buffer completely.
*/
BufLen = 0;
cBuffer[BufLen] = '\0';
return(0);
}
/* vtProcessedTextOut -
*
* Output characters to terminal, passing them through the vt100 emulator.
* Return -1 on error
*/
int
vtProcessedTextOut(char *cbuf, int count)
{
/* If we have a buffer overflow, error out if we haven't already crashed. */
if ((count + BufLen) > MAXVTBUFLEN)
{
beRawTextOut("ERROR: VT-100 internal buffer overflow!",39);
return(-1);
}
/* Otherwise, add our requested information to the
* output buffer, and attempt to parse it.
*/
memcpy(cBuffer + BufLen, cbuf, count);
BufLen += count;
return(ProcessBuffer());
}