PSXSS: I try again.
svn path=/trunk/; revision=3029
--- /dev/null
+08/08/96 - John L. Miller, johnmil@cs.cmu.edu, johnmil@jprc.com
+
+FILES INCLUDED:
+ 00readme.txt - this file
+ VT100.H - Definitions for VT-100 emulator.
+ VT100.C - Front end parsing code for VT-100 emulator
+ CONSOLE.C - Back-end code to allow VT-100 in WinNt/Win95 console
+
+Many UNIX users take terminals for granted, as something you get for free
+with the operating system. Unfortunately, this isn't the case for many
+non-unix operating systems, especially PC-based ones. After a number of
+projects, I decided it would be nice if there was source publicly available
+for doing VT-100 emulation.
+
+The files included with this distribution are not a complete implementation
+of VT-100 terminal emulation, but do provide complete enough coverage to
+use many vt-100 functions over the network. For instance, its enough to
+use EMACS to edit, or to connect up to your favorite mud with ANSI color
+and graphics characters.
+
+The VT-100 emulator is broken into two parts. The first is the front end,
+vt100.c and vt100.h. These files were written to be fairly device-independant,
+though admittedly if you're running under a 16-bit operating system instead
+of a 32-bit, you might need to change some of the 'int' values to 'long.'
+Otherwise, it should work 'as-is'.
+
+The second part is a back-end. The back-end is responsible for doing the
+workhorse activities. The front-end parses a character stream, and decides
+whether to clear a part of the screen, or move the cursor, or switch fonts.
+Then it calls routines in the back-end to perform these activities.
+
+The back-end functions are, for the most part, very straight forward, and
+quite easy to implement compared to writing a vt-100 emulator from scratch.
+CONSOLE.C is a back-end for use in console (command, dos) windows under
+Windows 95 and Windows NT. This console vt-100 emulator is also being used
+in my TINTIN-III port and kerberized encrypted telnet port.
+
+
+TO USE THIS VT-100 EMULATOR:
+
+First, it's intended to be linked directly into source code. You'll need
+to change printf's and puts' in your source code to call vtprintf() and
+vtputs() instead. You can add additional functions to vt100.c as you see
+fit to handle other output functions like putchar() and write(). Another
+routine you may want to use is vtProcessedTextOut(), which accepts a
+buffer to output, and a count of characters in that buffer.
+
+Second, you need to make sure that your source code calls vtInitVT100()
+before it does ANYTHING else. This initializes the vt-100 emulator.
+
+Third, if you want to use this VT-100 emulator with anything besides
+Windows NT and Windows 95 consoles, you'll need to implement your own
+back end. The list of functions you will need to supply, as well as what
+they need to do is contained in vt100.h. The list (minus descriptions)
+is as follows:
+
+ int beInitVT100Terminal();
+ int beAbsoluteCursor(int row, int col);
+ int beOffsetCursor(int row, int column);
+ int beRestoreCursor(void);
+ int beSaveCursor(void);
+ int beSetTextAttributes(int fore, int back);
+ int beRawTextOut(char *text, int len);
+ int beEraseText(int rowFrom, int colFrom, int rowTo, int colTo);
+ int beDeleteText(int rowFrom, int colFrom, int rowTo, int colTo);
+ int beInsertRow(int row);
+ int beTransmitText(char *text, int len);
+ int beAdvanceToTab(void);
+ int beClearTab(int col);
+ int beSetScrollingRows(int fromRow, int toRow);
+ int beRingBell(void);
+ int beGetTermMode();
+ int beSetTermMode(int newMode);
+
+For details on what each of these does, read the descriptions of each
+function included in vt100.h, and read over CONSOLE.C for examples. I've
+included copious comments in all of these files to try to make them as
+easy to use as possible.
+
+In any case, it should be easier than writing a VT-100 emulator from
+scratch.
+
+KNOWN BUGS -
+
+o Many features of VT-100 emulation aren't implemented. This includes
+ support for graphics character set 0 and many of the
+ answerback functions.
+
+Well, good luck!
+
--- /dev/null
+# $Id: Makefile,v 1.1 2002/06/09 08:37:07 ea Exp $
+#
+# Win32 Terminal Emulator for the POSIX+ subsystem.
+#
+PATH_TO_TOP=../../../reactos
+PATH_TO_TOP_PSX=../..
+
+TARGET_NAME=posixw32
+TARGET_TYPE=program
+TARGET_APPTYPE=console
+TARGET_CFLAGS =-I$(PATH_TO_TOP_PSX)/include
+TARGET_SDKLIBS=ntdll.a kernel32.a
+TARGET_OBJECTS=\
+ $(TARGET_NAME).o \
+ console.o \
+ vt100.o \
+ $(TARGET_NAME).coff
+
+include $(PATH_TO_TOP)/rules.mak
+
+include $(TOOLS_PATH)/helper.mk
+
+# EOF
--- /dev/null
+/* console.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 file contains functions intended to provide the back
+ * end to a console window for my semi-vt100 emulator.
+ */
+
+/* Note - one HUGE difference between console windows and terminal
+ * windows. Console windows displays start at (0,0). Terminal displays
+ * start at (1,1). YUCK!
+ */
+
+#include <windows.h>
+#include "vt100.h"
+
+int topScrollRow=TOP_EDGE;
+int bottomScrollRow=BOTTOM_EDGE;
+
+/* This variable will contain terminal configuration flags, such as
+ * reverse/standard video, whether wrapping is enabled, and so on.
+ */
+int conTermMode;
+
+/* Variable to hold the cursor position for save/restore cursor calls */
+COORD cursorPosSave={1,1};
+
+/* Handles to the current console for input and output */
+HANDLE hConIn, hConOut;
+
+/* Array of all the tabs which are currently set. Ironically, I think the
+ * primary emulator can CLEAR tags, but not set them.
+ */
+int tabSet[132]={0};
+int numTabs = 0;
+
+
+/* This section contains console-specific color information. NT consoles can
+ * have Red, blue, green, and intensity flags set. Hence, 4 con_colors.
+ */
+#define NUM_CON_COLORS 4
+
+/* Foreground and background colors are separated out */
+int conForeColors, conBackColors;
+
+/* mapping between foreground and background console colors: needed
+ * when reverse video is being used
+ */
+int conColorMapping[NUM_CON_COLORS][2] =
+{
+ {FOREGROUND_RED, BACKGROUND_RED},
+ {FOREGROUND_BLUE, BACKGROUND_BLUE},
+ {FOREGROUND_GREEN, BACKGROUND_GREEN},
+ {FOREGROUND_INTENSITY, BACKGROUND_INTENSITY}
+};
+
+
+/* Device-independant foreground and background flags stored here.
+ * probably a bad division of labor, but hey, since we don't use
+ * all of their flags in our console stuff (and hence can't retrieve
+ * them), the information has to live SOMEWHERE.
+ */
+
+int scForeFlags, scBackFlags;
+
+/* Defines for array indexing for translation of flags */
+#define SC_FLAG 0
+#define CONSOLE_FLAG 1
+
+/* Color mapping between SC (the vt-100 emulator device independant
+ * flags) and NT console character specific flags. Flags which have no analog
+ * are set to 0. Note that all global character attributes (character set
+ * underline, bold, reverse) are all stored in foreground only
+ */
+const int scForeMapping[NUM_SC_ATTRIBUTES][2] =
+{
+ {SC_RED,FOREGROUND_RED},
+ {SC_GREEN,FOREGROUND_GREEN},
+ {SC_BLUE,FOREGROUND_BLUE},
+ {SC_BOLD,FOREGROUND_INTENSITY},
+ {SC_UL,0},
+ {SC_BL,0},
+ {SC_RV,0},
+ {SC_ASCII,0},
+ {SC_G0,0},
+ {SC_G1,0},
+ {SC_GRAPHICS,0}
+};
+
+/* Background color mapping between SC and console */
+const int scBackMapping[NUM_SC_ATTRIBUTES][2] =
+{
+ {SC_RED,BACKGROUND_RED},
+ {SC_GREEN,BACKGROUND_GREEN},
+ {SC_BLUE,BACKGROUND_BLUE},
+ {SC_BOLD,BACKGROUND_INTENSITY},
+ {SC_UL,0},
+ {SC_BL,0},
+ {SC_RV,0},
+ {SC_ASCII,0},
+ {SC_G0,0},
+ {SC_G1,0},
+ {SC_GRAPHICS,0}
+};
+
+/* These arrays map character vals 0-255 to new values.
+ * Since the G0 and G1 character sets don't have a direct analog in
+ * NT, I'm settling for replacing the ones I know what to set them
+ * to.
+ */
+char G0Chars[256];
+char G1Chars[256];
+
+/* These four sets of variables are just precomputed combinations of
+ * all the possible flags to save time for masking.
+ */
+int allFore[2], allBack[2];
+int bothFore[2], bothBack[2];
+
+
+/* FORWARD DECLARATIONS */
+int
+RawPrintLine(
+ char *text,
+ int len,
+ int scrollAtEnd
+ );
+
+int
+Scroll(
+ int row
+ );
+/* END FORWARD DECLARATIONS */
+
+
+
+/* beInitVT100Terminal() -
+ *
+ * This function is called by the VT100 emulator as soon as the
+ * front-end terminal is initialized. It's responsible for setting
+ * initial state of the terminal, and initing our many wacky variables.
+ */
+
+int
+beInitVT100Terminal()
+{
+ int i;
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+ /* Set tabs to every 8 spaces initially */
+ numTabs = 0;
+ for (numTabs=0; numTabs < 132/8; numTabs++)
+ tabSet[numTabs] = (numTabs+1)*8;
+
+ /* Init the cursor save position to HOME */
+ cursorPosSave.X = 1;
+ cursorPosSave.Y = 1;
+
+ /* Disable scrolling window limits */
+ topScrollRow=TOP_EDGE;
+ bottomScrollRow=BOTTOM_EDGE;
+
+ conTermMode = ANSI_MODE|WRAP_MODE|REPEAT_MODE;
+
+ hConIn = GetStdHandle(STD_INPUT_HANDLE);
+ hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ /* Init our time-saving mask variables */
+ allFore[SC_FLAG] = allBack[SC_FLAG] = 0;
+ allFore[CONSOLE_FLAG] = allBack[CONSOLE_FLAG] = 0;
+ bothFore[SC_FLAG] = bothBack[SC_FLAG] = 0;
+ bothFore[CONSOLE_FLAG] = bothBack[CONSOLE_FLAG] = 0;
+
+ for (i=0; i<NUM_SC_ATTRIBUTES; i++)
+ {
+ allFore[SC_FLAG] |= scForeMapping[i][SC_FLAG];
+ allFore[CONSOLE_FLAG] |= scForeMapping[i][CONSOLE_FLAG];
+ allBack[SC_FLAG] |= scBackMapping[i][SC_FLAG];
+ allBack[CONSOLE_FLAG] |= scBackMapping[i][CONSOLE_FLAG];
+
+ if (scForeMapping[i][SC_FLAG] && scForeMapping[i][CONSOLE_FLAG])
+ {
+ bothFore[SC_FLAG] |= scForeMapping[i][SC_FLAG];
+ bothFore[CONSOLE_FLAG] |= scForeMapping[i][CONSOLE_FLAG];
+ }
+
+ if (scBackMapping[i][SC_FLAG] && scBackMapping[i][CONSOLE_FLAG])
+ {
+ bothBack[SC_FLAG] |= scBackMapping[i][SC_FLAG];
+ bothBack[CONSOLE_FLAG] |= scBackMapping[i][CONSOLE_FLAG];
+ }
+ }
+
+ conForeColors = conBackColors = 0;
+
+ for (i=0; i<NUM_CON_COLORS; i++)
+ {
+ conForeColors |= conColorMapping[i][0];
+ conBackColors |= conColorMapping[i][1];
+ }
+
+
+ /* Do initial settings for device-independant flags */
+ scForeFlags = SC_ASCII;
+ scBackFlags = 0;
+
+ GetConsoleScreenBufferInfo(hConOut, &csbi);
+
+ for (i=0; i<NUM_SC_ATTRIBUTES; i++)
+ {
+ if (csbi.wAttributes & scForeMapping[i][CONSOLE_FLAG])
+ scForeFlags |= scForeMapping[i][SC_FLAG];
+
+ if (csbi.wAttributes & scBackMapping[i][CONSOLE_FLAG])
+ scBackFlags |= scBackMapping[i][SC_FLAG];
+ }
+
+
+ /* Since the G0/G1 character sets don't really map to
+ * USASCII, So come as close as we can. By default, it'll
+ * just print the ascii character. For the graphics characters
+ * I was able to identify, change that mapping.
+ */
+
+ for (i=0; i<256; i++)
+ {
+ G0Chars[i] = i;
+ G1Chars[i] = i;
+ }
+
+ G1Chars['a']=(char)177;
+ G1Chars['f']=(char)248;
+ G1Chars['g']=(char)241;
+ G1Chars['j']=(char)217;
+ G1Chars['k']=(char)191;
+ G1Chars['l']=(char)218;
+ G1Chars['m']=(char)192;
+ G1Chars['n']=(char)197;
+ G1Chars['o']=(char)196;
+ G1Chars['p']=(char)196;
+ G1Chars['q']=(char)196;
+ G1Chars['r']=(char)196;
+ G1Chars['s']=(char)196;
+ G1Chars['t']=(char)195;
+ G1Chars['u']=(char)180;
+ G1Chars['v']=(char)193;
+ G1Chars['w']=(char)194;
+ G1Chars['x']=(char)179;
+ G1Chars['y']=(char)243;
+ G1Chars['z']=(char)242;
+
+ return(0);
+}
+
+
+
+/* beAbsoluteCursor -
+ *
+ * Given an input row and column, move the cursor to the
+ * absolute screen coordinates requested. Note that if the
+ * display window has scrollbars, the column is adjusted
+ * to take that into account, but the row is not. This allows
+ * for large scrollback in terminal windows.
+ *
+ * ROW must be able to accept CUR_ROW, TOP_EDGE, BOTTOM_EDGE,
+ * or a row number.
+ *
+ * COLUMN must be able to accept CUR_COL, LEFT_EDGE, RIGHT_EDGE,
+ * or a column number.
+ */
+
+int
+beAbsoluteCursor(
+ int row,
+ int col
+ )
+{
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ COORD cursorPos;
+
+ GetConsoleScreenBufferInfo(hConOut, &csbi);
+
+ if (row == CUR_ROW)
+ row = csbi.dwCursorPosition.Y;
+ else if (row == TOP_EDGE)
+ row = csbi.srWindow.Top;
+ else if (row == BOTTOM_EDGE)
+ row = csbi.srWindow.Bottom;
+ else
+ row += csbi.srWindow.Top - 1;
+
+ if (col == CUR_COL)
+ col = csbi.dwCursorPosition.X;
+ else if (col == LEFT_EDGE)
+ col = 0;
+ else if (col == RIGHT_EDGE)
+ col = csbi.dwSize.X-1;
+ else
+ col -= 1;
+
+ cursorPos.X = col;
+ cursorPos.Y = row;
+
+ SetConsoleCursorPosition(hConOut, cursorPos);
+
+ return(0);
+}
+
+
+/* beOffsetCursor -
+ *
+ * Given an input row and column offset, move the cursor by that
+ * many positions. For instance, row=0 and column=-1 would move
+ * the cursor left a single column.
+ *
+ * If the cursor can't move the requested amount, results are
+ * unpredictable.
+ */
+
+int
+beOffsetCursor(
+ int row,
+ int column
+ )
+{
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ COORD cursorPos;
+
+ GetConsoleScreenBufferInfo(hConOut, &csbi);
+
+ cursorPos = csbi.dwCursorPosition;
+ cursorPos.X += column;
+ cursorPos.Y += row;
+
+ if (cursorPos.X < 0)
+ cursorPos.X = 0;
+
+ if (cursorPos.X >= csbi.dwSize.X)
+ {
+ cursorPos.X -= csbi.dwSize.X;
+ cursorPos.Y += 1;
+ }
+
+ if (cursorPos.Y < 0)
+ cursorPos.Y = 0;
+
+ SetConsoleCursorPosition(hConOut, cursorPos);
+
+ return(0);
+}
+
+
+/* beRestoreCursor -
+ *
+ * Saved cursor position should be stored in a static
+ * variable in the back end. This function restores the
+ * cursor to the position stored in that variable.
+ */
+
+int
+beRestoreCursor(void)
+{
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ COORD cursorPos;
+
+ GetConsoleScreenBufferInfo(hConOut, &csbi);
+
+ cursorPos = csbi.dwCursorPosition;
+
+ cursorPos.Y += cursorPosSave.Y;
+
+ SetConsoleCursorPosition(hConOut, cursorPos);
+
+ return(0);
+}
+
+/* beSaveCursor -
+ *
+ * The back-end should maintain a static variable with the
+ * last STORED cursor position in it. This function replaces
+ * the contents of that variable with the current cursor position.
+ * The cursor may be restored to this position by using the
+ * beRestoreCursor function.
+ */
+
+int
+beSaveCursor(void)
+{
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+ GetConsoleScreenBufferInfo(hConOut, &csbi);
+ cursorPosSave = csbi.dwCursorPosition;
+
+ cursorPosSave.Y -= csbi.srWindow.Top;
+
+ return(0);
+}
+
+
+/* beGetTextAttributes -
+ *
+ * given a pointer to 'fore'ground and 'back'ground ints,
+ * fill them with a device-independant description of the
+ * current foreground and background colors, as well as any
+ * font information in the foreground variable.
+ */
+
+int
+beGetTextAttributes(
+ int *fore,
+ int *back
+ )
+{
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ int i;
+
+ /* Since it's entirely possible that the text attributes were
+ * changed without our terminal being notified, we might as well
+ * make sure they're accurate.
+ */
+
+ /* First, strip out everything in the screen buffer variables
+ * that we can detect
+ */
+
+ scForeFlags &= ~bothFore[SC_FLAG];
+ scBackFlags &= ~bothBack[SC_FLAG];
+
+ /* Now, find out what the real settings are, and set the
+ * flag values accordingly.
+ */
+
+ GetConsoleScreenBufferInfo(hConOut, &csbi);
+
+
+ /* If reverse video is set, we need to reverse our color mappings
+ * before any calculations get made.
+ */
+
+ if (scForeFlags & SC_RV)
+ {
+ int tmpFore, tmpBack;
+
+ tmpFore = csbi.wAttributes & conForeColors;
+ tmpBack = csbi.wAttributes & conBackColors;
+
+ csbi.wAttributes &= ~(conForeColors | conBackColors);
+
+ for (i=0; i<NUM_CON_COLORS; i++)
+ {
+ if (tmpFore & conColorMapping[i][0])
+ csbi.wAttributes |= conColorMapping[i][1];
+
+ if (tmpBack & conColorMapping[i][1])
+ csbi.wAttributes |= conColorMapping[i][0];
+ }
+ }
+
+ /* Now, do the actual translation between our detectable
+ * console text attributes and the corresponding device-independant
+ * attributes.
+ */
+
+ for (i=0; i<NUM_SC_ATTRIBUTES; i++)
+ {
+ if (csbi.wAttributes & scForeMapping[i][CONSOLE_FLAG])
+ scForeFlags |= scForeMapping[i][SC_FLAG];
+
+ if (csbi.wAttributes & scBackMapping[i][CONSOLE_FLAG])
+ scBackFlags |= scBackMapping[i][SC_FLAG];
+ }
+
+ /* Finally, copy our updated sc flags into the variables
+ * passed in
+ */
+
+ if (fore != NULL)
+ *fore = scForeFlags;
+
+ if (back != NULL)
+ *back = scBackFlags;
+
+ return(0);
+}
+
+
+/* beSetTextAttributes -
+ *
+ * Given a foreground and a background device independant (SC) color and font
+ * specification, apply these to the display, and save the state in the
+ * static screen variables.
+ *
+ * Note that many font-specific constants (bold/underline/reverse, G0/G1/ASCII)
+ * are stored ONLY in the foreground specification.
+ */
+
+int
+beSetTextAttributes(
+ int fore,
+ int back
+ )
+{
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ int i;
+ WORD wAttributes;
+
+ /* First off, let's assign these settings into our
+ * device-independant holder.
+ */
+
+ scForeFlags = fore;
+ scBackFlags = back;
+
+ /* Next, determine the console's actual current settings */
+
+ GetConsoleScreenBufferInfo(hConOut, &csbi);
+
+ /* Mask out any of the attributes which can be set via
+ * our device-independant options. Since the console settings
+ * have additional options, we need to retain those so we don't
+ * do something unpleasant to our I/O abilities, for instance.
+ */
+
+ wAttributes = csbi.wAttributes;
+
+ wAttributes &= ~(bothFore[CONSOLE_FLAG] | bothBack[CONSOLE_FLAG]);
+
+ /* Now, loop through the device-independant possibilities for
+ * flags, and modify our console flags as appropriate.
+ */
+
+ for (i=0; i<NUM_SC_ATTRIBUTES; i++)
+ {
+ if (scForeFlags & scForeMapping[i][SC_FLAG])
+ wAttributes |= scForeMapping[i][CONSOLE_FLAG];
+
+ if (scBackFlags & scBackMapping[i][SC_FLAG])
+ wAttributes |= scBackMapping[i][CONSOLE_FLAG];
+ }
+
+ /* One last unpleasantry: if reverse video is set, then we should
+ * reverse the foreground and background colors
+ */
+
+ if (scForeFlags & SC_RV)
+ {
+ int tmpFore, tmpBack;
+
+ tmpFore = wAttributes & conForeColors;
+ tmpBack = wAttributes & conBackColors;
+
+ wAttributes &= ~(conForeColors | conBackColors);
+
+ for (i=0; i<NUM_CON_COLORS; i++)
+ {
+ if (tmpFore & conColorMapping[i][0])
+ wAttributes |= conColorMapping[i][1];
+
+ if (tmpBack & conColorMapping[i][1])
+ wAttributes |= conColorMapping[i][0];
+ }
+ }
+
+ /* The appropriate colors, etc. should be set in
+ * the wAttributes variable now. Apply them to the
+ * current console.
+ */
+
+ SetConsoleTextAttribute(hConOut, wAttributes);
+
+ return(0);
+}
+
+
+/* beRawTextOut-
+ *
+ * The name of this function is misleading. Given a pointer to
+ * ascii text and a count of bytes to print, print them to the
+ * display device. If wrapping is enabled, wrap text. If there is a
+ * scrolling region set and the cursor is in it,
+ * scroll only within that region. 'beRawTextOut' means that it's guaranteed
+ * not to have control sequences within the text.
+ */
+
+int
+beRawTextOut(
+ char *text,
+ int len
+ )
+{
+ int i,j;
+
+ /* If there's no work to do, return immediately. */
+ if ((text == NULL)||(len == 0))
+ return(0);
+
+ i=0;
+
+ /* Otherwise, loop through the text until all of it has been output */
+ while (i < len)
+ {
+ /* This inner loop serves to divide the raw text to output into
+ * explicit lines. While the 'RawPrintLine' may still have to
+ * break lines to do text wrapping, explicit line breaks are
+ * handled right here.
+ */
+ j=i;
+ while ((text[j] != '\n')&&(j<len))
+ {
+ j++;
+ }
+
+ RawPrintLine(text+i, j-i, (text[j] == '\n'));
+
+ i = j+1;
+ }
+
+ return(0);
+}
+
+
+/* RawPrintLine -
+ *
+ * This routine is a helper for beRawTextOut. It is given a
+ * line of text which is guaranteed not to have any newlines
+ * or control characters (which need to be interpreted) in it.
+ * It prints out the text, wrapping if necessary, and handles
+ * scrolling or truncation.
+ *
+ * If scrollAtEnd is true, an extra carriage return (scroll) is
+ * performed after the text has been printed out.
+ */
+
+int
+RawPrintLine(
+ char *text,
+ int len,
+ int scrollAtEnd
+ )
+{
+ int i, end;
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ DWORD dwWritten;
+
+ if ((scrollAtEnd == FALSE) && ((text == NULL)||(len == 0)))
+ return(0);
+
+ GetConsoleScreenBufferInfo(hConOut, &csbi);
+
+ /* find out how far to the first tab or end of text */
+
+ if (text != NULL)
+ {
+ for (end=0; end<len; end++)
+ {
+ if (text[end] == '\t')
+ break;
+ }
+
+ if (end > (csbi.dwSize.X - csbi.dwCursorPosition.X))
+ end = (csbi.dwSize.X - csbi.dwCursorPosition.X);
+
+ /* If we're in non-ascii mode, we need to do a little
+ * magic to get the right characters out.
+ */
+
+ if (scForeFlags & SC_G1)
+ {
+ for (i=0; i<end; i++)
+ {
+ text[i] = G1Chars[text[i]];
+ }
+ }
+
+ /* actually print out the text. */
+
+ WriteConsole(hConOut,text,(DWORD)end,&dwWritten,NULL);
+
+ if (end == (csbi.dwSize.X - csbi.dwCursorPosition.X))
+ Scroll(CUR_ROW);
+
+ if ( (!(conTermMode & WRAP_MODE))
+ && (end == (csbi.dwSize.X - csbi.dwCursorPosition.X))
+ )
+ end = len;
+
+ if (end != len)
+ {
+ if (text[end] == '\t')
+ {
+ beAdvanceToTab();
+ RawPrintLine(text+end+1,len - (end+1), FALSE);
+ }
+ else
+ {
+ RawPrintLine(text+end, len-end, FALSE);
+ beAbsoluteCursor(CUR_ROW,1);
+ beOffsetCursor(1,0);
+ }
+ }
+ }
+
+ /* Now that we've printed this line, scroll if we need to.
+ * Note that a scroll implies a newline.
+ */
+
+ if (scrollAtEnd)
+ {
+ Scroll(CUR_ROW);
+ beAbsoluteCursor(CUR_ROW,1);
+ beOffsetCursor(1,0);
+ }
+
+ return(0);
+}
+
+
+/* Scroll -
+ *
+ * Given a row specification, calculate a scroll executed in that
+ * row. It could be within a scroll range, or outside of it.
+ *
+ * For some ungodly reason, I made this routine handle the TOP_EDGE,
+ * BOTTOM_EDGE, and CUR_ROW specifiers as well as a real row.
+ */
+
+int
+Scroll(
+ int row
+ )
+{
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ SMALL_RECT scrollRect;
+ COORD dest;
+ CHAR_INFO fillChar;
+
+ GetConsoleScreenBufferInfo(hConOut, &csbi);
+
+ if (row == TOP_EDGE)
+ row = csbi.srWindow.Top;
+ else if (row == BOTTOM_EDGE)
+ row = csbi.srWindow.Bottom;
+ else if (row == CUR_ROW)
+ row = csbi.dwCursorPosition.Y;
+ else
+ row += csbi.srWindow.Top;
+
+ /* Escape out if we don't really need to scroll */
+
+ if ( (row < (csbi.dwSize.Y-1))
+ && ((row-csbi.srWindow.Top + 1) < bottomScrollRow)
+ )
+ return(0);
+
+ /* NT console requires a fill character for scrolling. */
+
+ fillChar.Char.AsciiChar=' ';
+ fillChar.Attributes = csbi.wAttributes;
+
+ /* Determine the rectangle of text to scroll. Under NT this
+ * is actually an overlap-safe block-copy.
+ */
+ scrollRect.Left = 0;
+ scrollRect.Top = 1;
+ scrollRect.Right = csbi.dwSize.X;
+ scrollRect.Bottom = row;
+
+ if (topScrollRow != TOP_EDGE)
+ {
+ scrollRect.Top = csbi.srWindow.Top + topScrollRow + 1;
+ }
+
+ if ( (bottomScrollRow != BOTTOM_EDGE)
+ && ((csbi.srWindow.Top+bottomScrollRow) < scrollRect.Bottom)
+ )
+ {
+ scrollRect.Bottom = csbi.srWindow.Top + bottomScrollRow;
+ }
+
+ dest.X = 0;
+ dest.Y = scrollRect.Top - 1;
+
+ ScrollConsoleScreenBuffer(hConOut,&scrollRect,NULL,
+ dest, &fillChar);
+
+ return(0);
+}
+
+
+/* beEraseText -
+ *
+ * Given a 'from' and a 'to' position in display coordinates,
+ * this function will fill in all characters between the two
+ * (inclusive) with spaces. Note that the coordinates do NOT
+ * specify a rectangle. erasing from (1,1) to (2,2) erases
+ * all of the first row, and the first two characters of the
+ * second.
+ *
+ * Note that this routine must be able to handle TOP_EDGE,
+ * BOTTOM_EDGE, LEFT_EDGE, RIGHT_EDGE, CUR_ROW, and CUR_COL
+ * in the appropriate parameters.
+ */
+
+int
+beEraseText(
+ int rowFrom,
+ int colFrom,
+ int rowTo,
+ int colTo
+ )
+{
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ COORD dest, saveCursor;
+ DWORD nLength, dwWritten;
+
+ GetConsoleScreenBufferInfo(hConOut, &csbi);
+
+ saveCursor = csbi.dwCursorPosition;
+
+ /* Convert the row and column specifications into
+ * buffer coordinates
+ */
+ if (rowFrom == CUR_ROW)
+ rowFrom = csbi.dwCursorPosition.Y;
+ else if (rowFrom == TOP_EDGE)
+ rowFrom = csbi.srWindow.Top;
+ else
+ rowFrom += csbi.srWindow.Top -1;
+
+ if (colFrom == CUR_COL)
+ colFrom = csbi.dwCursorPosition.X;
+ else if (colFrom == LEFT_EDGE)
+ colFrom = 0;
+ else
+ colFrom -= 1;
+
+ if (rowTo == CUR_ROW)
+ rowTo = csbi.dwCursorPosition.Y;
+ else if (rowTo == BOTTOM_EDGE)
+ rowTo = csbi.srWindow.Bottom;
+ else
+ rowTo += csbi.srWindow.Top-1;
+
+ if (colTo == CUR_COL)
+ colTo = csbi.dwCursorPosition.X;
+ else if (colTo == RIGHT_EDGE)
+ colTo = csbi.dwSize.X;
+ else
+ colTo -= 1;
+
+ /* We're going to erase by filling a continuous range of
+ * character cells with spaces. Note that this has displeasing
+ * asthetics under NT, as highlighting appears to be immune.
+ */
+ nLength = (rowTo - rowFrom)*csbi.dwSize.X;
+ nLength += colTo - colFrom;
+
+ dest.X = colFrom;
+ dest.Y = rowFrom;
+
+ FillConsoleOutputCharacter(hConOut, ' ', nLength, dest, &dwWritten);
+ FillConsoleOutputAttribute(hConOut, csbi.wAttributes, nLength, dest, &dwWritten);
+
+ SetConsoleCursorPosition(hConOut, saveCursor);
+
+ return(0);
+}
+
+
+/* beDeleteText -
+ *
+ * Given a screen cursor 'from' and 'to' position, this function
+ * will delete all text between the two. Text will be scrolled
+ * up as appropriate to fill the deleted space. Note that, as in
+ * beEraseText, the two coordinates don't specify a rectangle, but
+ * rather a starting position and ending position. In other words,
+ * deleting from (1,1) to (2,2) should move the text from (2,3) to the
+ * end of the second row to (1,1), move line 3 up to line 2, and so on.
+ *
+ * This function must be able to process TOP_EDGE, BOTTOM_EDGE, LEFT_EDGE,
+ * RIGHT_EDGE, CUR_ROW, and CUR_COL specifications in the appropriate
+ * variables as well as regular row and column specifications.
+ */
+
+int
+beDeleteText(
+ int rowFrom,
+ int colFrom,
+ int rowTo,
+ int colTo
+ )
+{
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ COORD dest, saveCursor;
+ CHAR_INFO fillChar;
+
+ GetConsoleScreenBufferInfo(hConOut, &csbi);
+
+ saveCursor = csbi.dwCursorPosition;
+
+ if (rowFrom == CUR_ROW)
+ rowFrom = csbi.dwCursorPosition.Y;
+ else if (rowFrom == TOP_EDGE)
+ rowFrom = csbi.srWindow.Top;
+ else
+ rowFrom += csbi.srWindow.Top -1;
+
+ if (colFrom == CUR_COL)
+ colFrom = csbi.dwCursorPosition.X;
+ else if (colFrom == LEFT_EDGE)
+ colFrom = 0;
+ else
+ colFrom -= 1;
+
+ if (rowTo == CUR_ROW)
+ rowTo = csbi.dwCursorPosition.Y;
+ else if (rowTo == BOTTOM_EDGE)
+ rowTo = csbi.srWindow.Bottom;
+ else
+ rowTo += csbi.srWindow.Top-1;
+
+ if (colTo == CUR_COL)
+ colTo = csbi.dwCursorPosition.X;
+ else if (colTo == RIGHT_EDGE)
+ colTo = csbi.dwSize.X;
+ else
+ colTo -= 1;
+
+ fillChar.Char.AsciiChar=' ';
+ fillChar.Attributes = csbi.wAttributes;
+
+ /* Now that we've got the from and to positions
+ * set correctly, we need to delete appropriate
+ * rows and columns.
+ */
+
+ dest.X = colFrom;
+ dest.Y = rowFrom;
+
+ /* BUGBUG - need to implement this. What can I say, I'm lazy :) */
+
+ return(0);
+}
+
+
+/* beInsertRow -
+ *
+ * Given a row number or CUR_ROW, TOP_EDGE or BOTTOM_EDGE as an input,
+ * this function will scroll all text from the current row down down by one,
+ * and create a blank row under the cursor.
+ */
+
+int
+beInsertRow(
+ int row
+ )
+{
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ COORD dest;
+ CHAR_INFO fillChar;
+ SMALL_RECT scrollRect;
+
+ GetConsoleScreenBufferInfo(hConOut, &csbi);
+
+ fillChar.Char.AsciiChar=' ';
+ fillChar.Attributes = csbi.wAttributes;
+
+ if (row == CUR_ROW)
+ row = csbi.dwCursorPosition.Y;
+ else if (row == TOP_EDGE)
+ row = csbi.srWindow.Top;
+ else if (row == BOTTOM_EDGE)
+ row = csbi.srWindow.Bottom;
+ else
+ row += csbi.srWindow.Top-1;
+
+ dest.X = 0;
+ dest.Y = row+1;
+
+ scrollRect.Top = row;
+ scrollRect.Left = 0;
+ scrollRect.Right = csbi.dwSize.X;
+ scrollRect.Bottom = csbi.srWindow.Bottom;
+
+ ScrollConsoleScreenBuffer(hConOut, &scrollRect, NULL, dest, &fillChar);
+
+ return(0);
+}
+
+
+/* beTransmitText -
+ *
+ * Given a pointer to text and byte count, this routine should transmit data
+ * to whatever host made the request it's responding to. Typically this routin
+ * should transmit data as though the user had typed it in.
+ */
+
+int
+beTransmitText(
+ char *text,
+ int len
+ )
+{
+ if ((text == NULL) || (len < 1))
+ return(0);
+
+ /* BUGBUG - need to implement this. */
+
+ return(0);
+}
+
+
+/* beAdvanceToTab -
+ *
+ * This routine will destructively advance the cursor to the
+ * next set tab, or to the end of the line if there are no
+ * more tabs to the right of the cursor.
+ */
+
+int
+beAdvanceToTab(void)
+{
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ int i, col, tocol;
+ COORD dest;
+ DWORD dwWritten;
+
+ GetConsoleScreenBufferInfo(hConOut,&csbi);
+
+ col = csbi.dwCursorPosition.X+1;
+
+ dest = csbi.dwCursorPosition;
+
+ for(i=0; i<numTabs; i++)
+ {
+ if (col < tabSet[i])
+ {
+ tocol = tabSet[i];
+ break;
+ }
+ }
+
+ if (i == numTabs)
+ {
+ tocol = csbi.dwSize.X;
+ }
+
+ FillConsoleOutputCharacter(hConOut, ' ', tocol-col,
+ dest, &dwWritten);
+
+ beOffsetCursor(0,tocol-col);
+
+ return(0);
+}
+
+
+/* beClearTab -
+ *
+ * This function accepts a constant, and will try to clear tabs
+ * appropriately. Its argument is either
+ * ALL_TABS, meaning all tabs should be removed
+ * CUR_COL, meaning the tab in the current column should be wiped, or
+ * a column value, meaning if there's a tab there it should be wiped.
+ *
+ */
+
+int
+beClearTab(
+ int col
+ )
+{
+ int i, j;
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+ if (col == ALL_TABS)
+ {
+ tabSet[0] = 0;
+ numTabs = 0;
+ return(0);
+ }
+
+ if (col == CUR_COL)
+ {
+ GetConsoleScreenBufferInfo(hConOut,&csbi);
+ col = csbi.dwCursorPosition.X+1;
+ }
+
+ for (i=0; i<numTabs; i++)
+ {
+ if (tabSet[i] == col)
+ {
+ numTabs -= 1;
+
+ for (j=i; j<numTabs; j++)
+ tabSet[j]=tabSet[j+1];
+
+ break;
+ }
+ }
+
+ return(0);
+}
+
+/* beSetScrollingRows -
+ *
+ * Given a pair of row numbers, this routine will set the scrolling
+ * rows to those values. Note that this routine will accept
+ * TOP_ROW and BOTTOM_ROW as values, meaning that scrolling should
+ * be enabled for the entire display, regardless of resizing.
+ */
+
+int
+beSetScrollingRows(
+ int fromRow,
+ int toRow
+ )
+{
+ if (fromRow > toRow)
+ return(-1);
+
+ topScrollRow = fromRow;
+ bottomScrollRow = toRow;
+
+ return(0);
+}
+
+
+/* beRingBell -
+ *
+ * Ring the system bell once.
+ */
+
+int
+beRingBell(void)
+{
+ MessageBeep((UINT)-1);
+ return(0);
+}
+
+
+/* beGetTermMode -
+ *
+ * Return the value of conTermMode, which is the terminal settings which
+ * can be queried/set by <esc>[?#h/l.
+ */
+
+int
+beGetTermMode()
+{
+ return(conTermMode);
+}
+
+
+/* beSetTermMode -
+ *
+ * Set the terminal as requested, assuming we can. Right now we only handle a
+ * couple of the possible flags, but we store many of the others.
+ */
+
+int beSetTermMode(
+ int newMode
+ )
+{
+ int i, changes;
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ COORD newSize;
+ SMALL_RECT newWindowRect;
+ DWORD dwConMode;
+
+ changes = conTermMode ^ newMode;
+
+ /* For each bit set in 'changes', determine the
+ * appropriate course of action.
+ */
+
+ for (i=0; i < NUM_TERM_ATTR_MODES; i++)
+ {
+ if (termAttrMode[i] & changes)
+ {
+ switch(termAttrMode[i])
+ {
+ case COL132_MODE:
+ GetConsoleScreenBufferInfo(hConOut, &csbi);
+ newSize.Y = csbi.dwSize.Y;
+ newSize.X = (newMode & COL132_MODE) ? 132 : 80;
+ if (newSize.X != csbi.dwSize.X)
+ {
+ newWindowRect.Top = csbi.srWindow.Top;
+ newWindowRect.Bottom = csbi.srWindow.Bottom;
+ newWindowRect.Left = 0;
+ newWindowRect.Right = csbi.dwSize.X - 1;
+ SetConsoleScreenBufferSize(hConOut, newSize);
+ SetConsoleWindowInfo(hConOut, TRUE, &newWindowRect);
+ }
+ break;
+
+ case WRAP_MODE:
+ GetConsoleMode(hConOut,&dwConMode);
+ if ( (newMode & WRAP_MODE)
+ && (! (dwConMode & ENABLE_WRAP_AT_EOL_OUTPUT))
+ )
+ {
+ dwConMode |= ENABLE_WRAP_AT_EOL_OUTPUT;
+ SetConsoleMode(hConOut, dwConMode);
+ }
+ if ( (!(newMode & WRAP_MODE))
+ && (dwConMode & ENABLE_WRAP_AT_EOL_OUTPUT)
+ )
+ {
+ dwConMode &= ~ENABLE_WRAP_AT_EOL_OUTPUT;
+ SetConsoleMode(hConOut, dwConMode);
+ }
+ break;
+
+ case CURSORAPPL_MODE:
+ case ANSI_MODE:
+ case SMOOTHSCROLL_MODE:
+ case REVSCREEN_MODE:
+ case ORIGINREL_MODE:
+ case REPEAT_MODE:
+ /* bugbug - we don't handle any of these. */
+ break;
+ }
+ }
+ }
+
+ conTermMode = newMode;
+
+ return(0);
+}
--- /dev/null
+/* $Id: posixw32.c,v 1.1 2002/06/09 08:37:07 ea Exp $
+ *
+ * PROJECT : ReactOS Operating System / POSIX+ Environment Subsystem
+ * DESCRIPTION: POSIXW32 - A DEC VT-100 terminal emulator for the PSX subsystem
+ * DESCRIPTION: that runs in the Win32 subsystem.
+ * COPYRIGHT : Copyright (c) 2001-2002 Emanuele Aliberti
+ * LICENSE : GNU GPL v2
+ * DATE : 2001-05-05
+ * AUTHOR : Emanuele Aliberti <ea@iol.it>
+ * NOTE : This IS a Win32 program, but will fail if the PSX subsystem
+ * NOTE : is not installed. The PSX subsystem consists of two more
+ * NOTE : files: PSXSS.EXE, PSXDLL.DLL.
+ * WARNING : If you use this program under a real NT descendant, be
+ * WARNING : sure to have disabled the PSX subsystem.
+ * --------------------------------------------------------------------
+ *
+ * This software 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 software 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 software; see the file COPYING. If not, write
+ * to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
+ * MA 02139, USA.
+ *
+ * --------------------------------------------------------------------
+ * 2002-03-16 EA Today it actually compiled.
+ * 2002-06-08 EA Renamed (old name was CSRTERM)
+ */
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define NTOS_MODE_USER
+#include <ntos.h>
+#include <psx/lpcproto.h>
+
+#include "vt100.h"
+#include "posixw32.h"
+
+/*** OPTIONS *********************************************************/
+
+#define PRIVATE static
+
+#define INPUT_QUEUE_SIZE 32
+
+#ifdef NDEBUG
+#define TRACE
+#else
+#define TRACE OutputDebugString(__FUNCTION__)
+#endif
+
+/*** GLOBALS *********************************************************/
+
+PRIVATE LPCSTR MyName = "POSIXW32";
+PRIVATE CSRTERM_SESSION Session =
+{
+ 0, //Identifier
+ { //ServerPort
+ {0,0,NULL},
+ L"\\"PSX_NS_SUBSYSTEM_DIRECTORY_NAME"\\"PSX_NS_SESSIONAPI_PORT_NAME,
+ INVALID_HANDLE_VALUE
+ }
+};
+
+/*** PRIVATE FUNCTIONS ***********************************************/
+VOID STDCALL Debug_Print (LPCSTR Format, ...)
+{
+ CHAR Buffer [512];
+ va_list ArgumentPointer;
+
+ va_start(ArgumentPointer, Format);
+ vsprintf(Buffer, Format, ArgumentPointer);
+ va_end(ArgumentPointer);
+ OutputDebugStringA (Buffer);
+}
+/**********************************************************************
+ * OutPort/2 PRIVATE
+ *
+ * DESCRIPTION
+ * Notify to PSXSS that input data is ready by sending a
+ * software interrupt on the \POSIX+\SessionPort port.
+ */
+PRIVATE DWORD STDCALL OutPort (PCHAR Buffer, ULONG Size)
+{
+ NTSTATUS Status;
+ PSX_TERMINAL_READ TerminalRead;
+TRACE;
+ if (Size > 0)
+ {
+ /* LPC */
+ TerminalRead.Header.MessageType = LPC_NEW_MESSAGE;
+ TerminalRead.PsxHeader.Context = PSX_CONNECTION_TYPE_TERMINAL;
+ TerminalRead.PsxHeader.Procedure = PSX_TERMINAL_INTERRUPT;
+ /* Terminal I/O */
+ TerminalRead.Size = Size;
+#if 0
+ RtlCopyMemory (TerminalRead.Buffer, Buffer, Size);
+ Status = NtRequestWaitReplyPort (
+ Session.ServerPort.Handle,
+ & TerminalRead
+ /* FIXME */
+ );
+#endif
+ if (!NT_SUCCESS(Status))
+ {
+ vtprintf ("%s: %s: NtRequestWaitReplyPort failed with %08x\n",
+ MyName, __FUNCTION__, Status);
+ return 0;
+ }
+ }
+ return Size;
+}
+/**********************************************************************
+ * ProcessConnectionRequest/1 PRIVATE
+ *
+ * DESCRIPTION
+ * Initialize our data for managing the control connection
+ * initiated by the PSXSS.EXE process.
+ */
+PRIVATE NTSTATUS STDCALL ProcessConnectionRequest (PLPC_MAX_MESSAGE Request)
+{
+ PPSX_CONNECT_PORT_DATA ConnectData = (PPSX_CONNECT_PORT_DATA) & Request->Data;
+
+TRACE;
+ if (PSX_CONNECTION_TYPE_SERVER != ConnectData->ConnectionType)
+ {
+
+ return STATUS_UNSUCCESSFUL;
+ }
+ if (PSX_LPC_PROTOCOL_VERSION != ConnectData->Version)
+ {
+
+ return STATUS_UNSUCCESSFUL;
+ }
+ Session.SsLinkIsActive = TRUE;
+ return STATUS_SUCCESS;
+}
+/**********************************************************************
+ * ProcessRequest/1 PRIVATE
+ *
+ * DESCRIPTION
+ *
+ */
+PRIVATE NTSTATUS STDCALL ProcessRequest (PPSX_MAX_MESSAGE Request)
+{
+TRACE;
+ /* TODO */
+ vtprintf("TEST VT-100\n");
+
+ return STATUS_SUCCESS;
+}
+/**********************************************************************
+ * PsxSessionPortListener/1 PRIVATE
+ *
+ * DESCRIPTION
+ * Manage messages from the PSXSS, that is LPC messages we get
+ * from the PSXSS process to our \POSIX+\Sessions\P<pid> port.
+ *
+ * NOTE
+ * This function is the thread 's entry point created in
+ * CreateSessionObiects().
+ */
+PRIVATE DWORD STDCALL PsxSessionPortListener (LPVOID Arg)
+{
+ NTSTATUS Status;
+ LPC_TYPE RequestType;
+ PSX_MAX_MESSAGE Request;
+ PPSX_MAX_MESSAGE Reply = NULL;
+ BOOL NullReply = FALSE;
+
+TRACE;
+
+ while (TRUE)
+ {
+ Reply = NULL;
+ NullReply = FALSE;
+ while (!NullReply)
+ {
+ Status = NtReplyWaitReceivePort (
+ Session.Port.Handle,
+ 0,
+ (PLPC_MESSAGE) Reply,
+ (PLPC_MESSAGE) & Request
+ );
+ if (!NT_SUCCESS(Status))
+ {
+ break;
+ }
+ RequestType = PORT_MESSAGE_TYPE(Request);
+ switch (RequestType)
+ {
+ case LPC_CONNECTION_REQUEST:
+ ProcessConnectionRequest ((PLPC_MAX_MESSAGE) & Request);
+ NullReply = TRUE;
+ continue;
+ case LPC_CLIENT_DIED:
+ case LPC_PORT_CLOSED:
+ case LPC_DEBUG_EVENT:
+ case LPC_ERROR_EVENT:
+ case LPC_EXCEPTION:
+ NullReply = TRUE;
+ continue;
+ default:
+ if (RequestType != LPC_REQUEST)
+ {
+ NullReply = TRUE;
+ continue;
+ }
+ }
+ Reply = & Request;
+ Reply->PsxHeader.Status = ProcessRequest (& Request);
+ NullReply = FALSE;
+ }
+ if ((STATUS_INVALID_HANDLE == Status) ||
+ (STATUS_OBJECT_TYPE_MISMATCH == Status))
+ {
+ break;
+ }
+ }
+ Session.SsLinkIsActive = FALSE;
+ TerminateThread (GetCurrentThread(), Status);
+}
+/**********************************************************************
+ * CreateSessionObiects/1 PRIVATE
+ *
+ * DESCRIPTION
+ * Create the session objects which are mananged by our side:
+ *
+ * \POSIX+\Sessions\P<pid>
+ * \POSIX+\Sessions\D<pid>
+ */
+PRIVATE NTSTATUS STDCALL CreateSessionObjects (DWORD Pid)
+{
+ NTSTATUS Status;
+ ULONG Id = 0;
+ OBJECT_ATTRIBUTES Oa;
+ LARGE_INTEGER SectionSize = {PSX_TERMINAL_SECTION_SIZE,0};
+
+TRACE;
+
+
+ /* Critical section */
+ Status = RtlInitializeCriticalSection (& Session.Lock);
+ if (!NT_SUCCESS(Status))
+ {
+ vtprintf (
+ "%s: %s: RtlInitializeCriticalSection failed with %08x\n",
+ MyName, __FUNCTION__, Status);
+ return Status;
+ }
+ /* Port and port management thread */
+ swprintf (
+ Session.Port.NameBuffer,
+ PSX_NS_SESSION_PORT_TEMPLATE,
+ PSX_NS_SUBSYSTEM_DIRECTORY_NAME,
+ PSX_NS_SESSION_DIRECTORY_NAME,
+ Pid
+ );
+ OutputDebugStringW(Session.Port.NameBuffer);
+ RtlInitUnicodeString (& Session.Port.Name, Session.Port.NameBuffer);
+ InitializeObjectAttributes (& Oa, & Session.Port.Name, 0, NULL, NULL);
+ Status = NtCreatePort (& Session.Port.Handle, & Oa, 0, 0, 0x10000);
+ if (!NT_SUCCESS(Status))
+ {
+ RtlDeleteCriticalSection (& Session.Lock);
+ vtprintf ("%s: %s: NtCreatePort failed with %08x\n",
+ MyName, __FUNCTION__, Status);
+ return Status;
+ }
+ Session.Port.Thread.Handle =
+ CreateThread (
+ NULL,
+ 0,
+ PsxSessionPortListener,
+ 0,
+ CREATE_SUSPENDED,
+ & Session.Port.Thread.Id
+ );
+ if ((HANDLE) NULL == Session.Port.Thread.Handle)
+ {
+ Status = (NTSTATUS) GetLastError();
+ NtClose (Session.Port.Handle);
+ RtlDeleteCriticalSection (& Session.Lock);
+ vtprintf ("%s: %s: CreateThread failed with %d\n",
+ MyName, __FUNCTION__, Status);
+ return Status;
+ }
+ /* Section */
+ swprintf (
+ Session.Section.NameBuffer,
+ PSX_NS_SESSION_DATA_TEMPLATE,
+ PSX_NS_SUBSYSTEM_DIRECTORY_NAME,
+ PSX_NS_SESSION_DIRECTORY_NAME,
+ Pid
+ );
+ OutputDebugStringW(Session.Section.NameBuffer);
+ RtlInitUnicodeString (& Session.Section.Name, Session.Section.NameBuffer);
+ InitializeObjectAttributes (& Oa, & Session.Section.Name, 0, 0, 0);
+ Status = NtCreateSection (
+ & Session.Section.Handle,
+ SECTION_ALL_ACCESS, /* DesiredAccess */
+ & Oa,
+ & SectionSize,
+ PAGE_READWRITE, /* Protect 4 */
+ SEC_COMMIT, /* Attributes */
+ 0 /* FileHandle: 0=pagefile.sys */
+ );
+ if (!NT_SUCCESS(Status))
+ {
+ NtClose (Session.Port.Handle);
+ NtTerminateThread (Session.Port.Thread.Handle, Status);
+ RtlDeleteCriticalSection (& Session.Lock);
+ vtprintf ("%s: %s: NtCreateSection failed with %08x\n",
+ MyName, __FUNCTION__, Status);
+ return Status;
+ }
+ Session.Section.BaseAddress = NULL;
+ Session.Section.ViewSize = SectionSize.u.LowPart;
+ Status = NtMapViewOfSection (
+ Session.Section.Handle,
+ NtCurrentProcess(),
+ & Session.Section.BaseAddress,
+ 0, /* ZeroBits */
+ 0, /* Commitsize */
+ 0, /* SectionOffset */
+ & Session.Section.ViewSize,
+ ViewUnmap,
+ 0, /* AllocationType */
+ PAGE_READWRITE /* Protect 4 */
+ );
+ if (!NT_SUCCESS(Status))
+ {
+ NtClose (Session.Port.Handle);
+ NtTerminateThread (Session.Port.Thread.Handle, Status);
+ NtClose (Session.Section.Handle);
+ RtlDeleteCriticalSection (& Session.Lock);
+ vtprintf ("%s: %s: NtMapViewOfSection failed with %08x\n",
+ MyName, __FUNCTION__, Status);
+ return Status;
+ }
+ return Status;
+}
+
+/**********************************************************************
+ * CreateTerminalToPsxChannel/0 PRIVATE
+ *
+ * DESCRIPTION
+ *
+ */
+PRIVATE NTSTATUS STDCALL CreateTerminalToPsxChannel (VOID)
+{
+ PSX_CONNECT_PORT_DATA ConnectData;
+ ULONG ConnectDataLength = sizeof ConnectData;
+ SECURITY_QUALITY_OF_SERVICE Sqos;
+ NTSTATUS Status;
+
+TRACE;
+
+
+ /*
+ * Initialize the connection data object before
+ * calling PSXSS.
+ */
+ ConnectData.ConnectionType = PSX_CONNECTION_TYPE_TERMINAL;
+ ConnectData.Version = PSX_LPC_PROTOCOL_VERSION;
+ /*
+ * Try connecting to \POSIX+\SessionPort.
+ */
+ RtlInitUnicodeString (& Session.ServerPort.Name, Session.ServerPort.NameBuffer);
+ OutputDebugStringW(Session.ServerPort.Name.Buffer);
+ Status = NtConnectPort (
+ & Session.ServerPort.Handle,
+ & Session.ServerPort.Name,
+ & Sqos,
+ NULL,
+ NULL,
+ 0,
+ & ConnectData,
+ & ConnectDataLength
+ );
+ if (STATUS_SUCCESS != Status)
+ {
+ vtprintf ("%s: %s: NtConnectPort failed with %08x\n",
+ MyName, __FUNCTION__, Status);
+ return Status;
+ }
+ Session.Identifier = ConnectData.PortIdentifier;
+ return STATUS_SUCCESS;
+}
+
+/**********************************************************************
+ * InitializeSsIoChannel PRIVATE
+ *
+ * DESCRIPTION
+ * Create our objects in the system name space
+ * (CreateSessionObjects) and then connect to the session port
+ * (CreateControChannel).
+ */
+PRIVATE NTSTATUS STDCALL InitializeSsIoChannel (VOID)
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ DWORD Pid = GetCurrentProcessId();
+
+TRACE;
+
+
+ Status = CreateSessionObjects (Pid);
+ if (STATUS_SUCCESS != Status)
+ {
+ vtprintf ("%s: %s: CreateSessionObjects failed with %08x\n",
+ MyName, __FUNCTION__, Status);
+ return Status;
+ }
+ Status = CreateTerminalToPsxChannel ();
+ if (STATUS_SUCCESS != Status)
+ {
+ vtprintf ("%s: %s: CreateTerminalToPsxChannel failed with %08x\n",
+ MyName, __FUNCTION__, Status);
+ return Status;
+ }
+ return STATUS_SUCCESS;
+}
+/**********************************************************************
+ * PsxCreateLeaderProcess/1 PRIVATE
+ *
+ * DESCRIPTION
+ * Create a new PSXSS process.
+ */
+PRIVATE NTSTATUS STDCALL PsxCreateLeaderProcess (char * Command)
+{
+ NTSTATUS Status;
+TRACE;
+
+ if (NULL == Command)
+ {
+ Command = "sh";
+ }
+ /* TODO: request PSXSS to init the process slot */
+ vtprintf ("%s: %s: calling CSRSS not implemented!", MyName, __FUNCTION__);
+ return STATUS_SUCCESS;
+}
+/**********************************************************************
+ * PrintInformationProcess/0
+ *
+ * DESCRIPTION
+ */
+PRIVATE VOID STDCALL PrintInformationProcess (VOID)
+{
+
+TRACE;
+
+ vtputs ("Leader:");
+ vtprintf (" UniqueProcess %08x\n", Session.Client.UniqueProcess);
+ vtprintf (" UniqueThread %08x\n", Session.Client.UniqueThread);
+}
+/**********************************************************************
+ * PostMortem/0
+ *
+ * DESCRIPTION
+ */
+PRIVATE INT STDCALL PostMortem (VOID)
+{
+ DWORD ExitCode;
+
+TRACE;
+
+
+ PrintInformationProcess ();
+ if (TRUE == GetExitCodeProcess (Session.Client.UniqueProcess, & ExitCode))
+ {
+ vtprintf (" ExitCode %d\n", ExitCode);
+ }
+ return 0;
+}
+/**********************************************************************
+ * InputTerminalEmulator/0
+ *
+ * DESCRIPTION
+ * Process user terminal input.
+ *
+ * NOTE
+ * This code is run in the main thread.
+ */
+PRIVATE BOOL STDCALL InputTerminalEmulator (VOID)
+{
+ HANDLE StandardInput;
+ INPUT_RECORD InputRecord [INPUT_QUEUE_SIZE];
+ DWORD NumberOfEventsRead = 0;
+ INT CurrentEvent;
+
+
+TRACE;
+
+ StandardInput = GetStdHandle (STD_INPUT_HANDLE);
+ if (INVALID_HANDLE_VALUE == StandardInput)
+ {
+ return FALSE;
+ }
+ while ((TRUE == Session.SsLinkIsActive) &&
+ ReadConsoleInput (
+ StandardInput,
+ InputRecord,
+ (sizeof InputRecord) / sizeof (INPUT_RECORD),
+ & NumberOfEventsRead
+ ))
+ {
+ for ( CurrentEvent = 0;
+ (CurrentEvent < NumberOfEventsRead);
+ CurrentEvent ++
+ )
+ {
+ switch (InputRecord [CurrentEvent].EventType)
+ {
+ case KEY_EVENT:
+ OutPort (& InputRecord [CurrentEvent].Event.KeyEvent.uChar.AsciiChar, 1);
+ break;
+ case MOUSE_EVENT:
+ /* TODO: send a sequence of move cursor codes */
+ /* InputRecord [CurrentEvent].Event.MouseEvent; */
+ break;
+ case WINDOW_BUFFER_SIZE_EVENT:
+ /* TODO: send a SIGWINCH signal to the leader process. */
+ /* InputRecord [CurrentEvent].Event.WindowBufferSizeEvent.dwSize; */
+ break;
+ /* Next events should be ignored. */
+ case MENU_EVENT:
+ vtprintf ("%s: %s: MENU_EVENT received from CSRSS\n", MyName, __FUNCTION__);
+ case FOCUS_EVENT:
+ vtprintf ("%s: %s: FOCUS_EVENT received from CSRSS\n", MyName, __FUNCTION__);
+ break;
+ }
+ }
+ NumberOfEventsRead = 0;
+ }
+ return TRUE;
+}
+/**********************************************************************
+ * Startup/1
+ *
+ * DESCRIPTION
+ * Initialize the program.
+ */
+PRIVATE VOID STDCALL Startup (LPSTR Command)
+{
+ NTSTATUS Status;
+ DWORD ThreadId;
+
+
+TRACE;
+
+ /* PSX process info */
+ Session.Client.UniqueProcess = INVALID_HANDLE_VALUE;
+ Session.Client.UniqueThread = INVALID_HANDLE_VALUE;
+ /* Initialize the VT-100 emulator */
+ vtInitVT100 ();
+ /* Connect to PSXSS */
+ Status = InitializeSsIoChannel ();
+ if (!NT_SUCCESS(Status))
+ {
+ vtprintf ("%s: failed to connect to PSXSS (Status=%08x)!\n",
+ MyName, Status);
+ exit (EXIT_FAILURE);
+ }
+ /* Create the leading process for this session */
+ Status = PsxCreateLeaderProcess (Command);
+ if (!NT_SUCCESS(Status))
+ {
+ vtprintf ("%s: failed to create the PSX process (Status=%08x)!\n",
+ MyName, Status);
+ exit (EXIT_FAILURE);
+ }
+}
+/**********************************************************************
+ * Shutdown/0 PRIVATE
+ *
+ * DESCRIPTION
+ * Shutdown the program.
+ */
+PRIVATE INT STDCALL Shutdown (VOID)
+{
+
+TRACE;
+
+ /* TODO: try exiting cleanly: close any open resource */
+ /* TODO: notify PSXSS the session is terminating */
+ RtlDeleteCriticalSection (& Session.Lock);
+ return PostMortem ();
+}
+/**********************************************************************
+ *
+ * ENTRY POINT PUBLIC
+ *
+ *********************************************************************/
+int main (int argc, char * argv [])
+{
+
+TRACE;
+
+ Startup (argv[1]); /* Initialization */
+ InputTerminalEmulator (); /* Process user input */
+ return Shutdown ();
+}
+/* EOF */
--- /dev/null
+#ifndef _CSRTERM_H
+#define _CSRTERM_H
+
+/* PSX session: CSR terminal emulator side */
+
+#define NAME_BUFFER_SIZE 64
+
+typedef struct _PSXSS_PORT
+{
+ UNICODE_STRING Name;
+ WCHAR NameBuffer [NAME_BUFFER_SIZE];
+ HANDLE Handle;
+
+} PSXSS_PORT, * PPSXSS_PORT;
+
+typedef struct _CSRTERM_SESSION_PORT
+{
+ UNICODE_STRING Name;
+ WCHAR NameBuffer [NAME_BUFFER_SIZE];
+ HANDLE Handle;
+ struct {
+ HANDLE Handle;
+ DWORD Id;
+ } Thread;
+
+} CSRTERM_SESSION_PORT;
+
+typedef struct _CSRTERM_SESSION_SECTION
+{
+ UNICODE_STRING Name;
+ WCHAR NameBuffer [NAME_BUFFER_SIZE];
+ HANDLE Handle;
+ ULONG Size;
+ PVOID BaseAddress;
+ ULONG ViewSize;
+
+} CSRTERM_SESSION_SECTION;
+
+typedef struct _CSRTERM_SESSION
+{
+ ULONG Identifier; /* PortID for ServerPort in PSXSS */
+ PSXSS_PORT ServerPort; /* \POSIX+\SessionPort */
+ CSRTERM_SESSION_PORT Port; /* \POSIX+\Sessions\P<pid> */
+ CSRTERM_SESSION_SECTION Section; /* \POSIX+\Sessions\D<pid> */
+ CLIENT_ID Client;
+ CRITICAL_SECTION Lock;
+ BOOL SsLinkIsActive;
+
+} CSRTERM_SESSION, * PCSRTERM_SESSION;
+
+#define LOCK_SESSION RtlEnterCriticalSection(& Session.Lock)
+#define UNLOCK_SESSION RtlLeaveCriticalSection(& Session.Lock)
+#endif /* ndef _CSRTERM_H */
--- /dev/null
+#include <defines.h>
+#include <reactos/resource.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION RES_UINT_FV_MAJOR,RES_UINT_FV_MINOR,RES_UINT_FV_REVISION,RES_UINT_FV_BUILD
+ PRODUCTVERSION RES_UINT_PV_MAJOR,RES_UINT_PV_MINOR,RES_UINT_PV_REVISION,RES_UINT_PV_BUILD
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", RES_STR_COMPANY_NAME
+ VALUE "FileDescription", "CSR Terminal Emulator for POSIX+\0"
+ VALUE "FileVersion", RES_STR_FILE_VERSION
+ VALUE "InternalName", "csrterm\0"
+ VALUE "LegalCopyright", RES_STR_LEGAL_COPYRIGHT
+ VALUE "OriginalFilename", "csrterm.exe\0"
+ VALUE "ProductName", RES_STR_PRODUCT_NAME
+ VALUE "ProductVersion", RES_STR_PRODUCT_VERSION
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
--- /dev/null
+$Id: readme.txt,v 1.1 2002/06/09 08:37:07 ea Exp $
+
+posixw32 - a Win32 client terminal emulator for the POSIX+ subsystem
+
+SYNOPSYS
+
+ posixw32 [program]
+
+ program program to be run in the terminal; if none is given,
+ the shell for the current user (W32 session's) is
+ used.
+
+DESCRIPTION
+
+ posixw32 emulates a DEC VT-100 terminal (on top of the CSRSS
+ subsystem, hence the name) which is the controlling terminal
+ for a process [program] running in the context of the PSX
+ subsystem. posixw32 is a Win32 console application, not a PSX
+ application. The process created by the PSX subsystem on behalf
+ of posixw32 is not the child of the posixw32 instance that
+ requested it. posixw32 simply performs terminal I/O in the CSRSS
+ world (the W32 world!) for [program].
+
+NOTES
+
+ The role of posixw32 is creating a session in the PSX subsystem
+ managing any I/O for it. This is how it works:
+
+ 1. posixw32 creates two well known named objects in the system
+ name space that will allow the PSX subsystem server to build
+ the I/O channel for the session. To let the PSX subsystem
+ process recognize the objects, they contain a numeric suffix
+ which is the process identifier (n) the system gives to each
+ instance of posixw32:
+
+ \POSIX+\Session\Pn LPC port (IPC rendez-vous object)
+ \POSIX+\Session\Dn section (shared memory object)
+
+ posixw32 also creates a new thread to manage the calls though
+ the LPC port. Port Pn is used by the subsystem to control the
+ terminal which posixw32 emulates.
+
+ 2. posixw32 connects to the PSX subsystem session port
+
+ \POSIX+\SessionPort
+
+ and asks the subsystem to create a new session.
+
+ 3. The PSX subsystem, if it decides to accept the request, creates
+ a new session for that calling instance of posixw32 (n), and in
+ turn connects back to the terminal control port
+
+ \POSIX+\Session\Pn
+
+ 4. When posixw32 makes the PSX subsystem create the new session, it
+ also tells the subsystem what program should be the session
+ leader process. The PSX subsystem creates that process (the
+ image file to start must be marked IMAGE_SUBSYSTEM_POSIX_GUI or
+ IMAGE_SUBSYSTEM_POSIX_CUI).
+
+ 5. The requested process [program] runs in the context of the
+ PSX subsystem and performs any terminal I/O via the channel
+ posixw32 and the PSX susbstem created.
+
+REVISIONS
+ 2001-05-05 created
+ 2002-03-03 simplified
+ 2002-06-08 renamed to avoid future name clash
+
+AUTHOR
+
+ Emanuele Aliberti <ea@iol.it>
+
+CREDITS
+
+ John L. Miller (johnmil@cs.cmu.edu, johnmil@jprc.com) code for
+ a basic VT-100 emulator for Win32 consoles is used to process
+ tc* calls output.
+
+EOF
--- /dev/null
+/* 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());
+}
+
--- /dev/null
+/* vt100.h
+ *
+ * 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 identifier should be spit back to the computer when a terminal
+ * id is requested.
+ */
+#define ANSWERBACK_MESSAGE "vt100"
+
+/* Various terminal-related modes Entries are as follows:
+ * Identification esc. ID If set, if clear
+ */
+
+ /* Keyboard action 2 Locked Unlocked */
+#define CAPS_MODE 0x00000001
+ /* Insertion 4 Insert Overwrite */
+#define INSERT_MODE 0x00000002
+ /* Send - Receive 12 Full Echo */
+#define FULLDUPLEX_MODE 0x00000004
+ /* Line feed/New line 20 New line Line feed */
+#define NEWLINE_MODE 0x00000008
+
+
+#define NUM_TERM_ATTR_MODES 9 /* We only track eight '?' escape sequences */
+
+ /* Cursor key ?1 Application Cursor */
+#define CURSORAPPL_MODE 0x00000010
+ /* ANSI/VT52 ?2 ANSI VT52 */
+#define ANSI_MODE 0x00000020
+ /* Column ?3 132 80 */
+#define COL132_MODE 0x00000040
+ /* Scrolling ?4 Smooth Jump */
+#define SMOOTHSCROLL_MODE 0x00000080
+ /* Screen ?5 Reverse Normal */
+#define REVSCREEN_MODE 0x00000100
+ /* Origin ?6 Relative Absolute */
+#define ORIGINREL_MODE 0x00000200
+ /* Wraparound ?7 Wrap Truncate */
+#define WRAP_MODE 0x00000400
+ /* Auto key repeat ?8 Repeating No repeat */
+#define REPEAT_MODE 0x00000800
+
+
+ /* Print form feed ?18 Yes No */
+#define PRINTFF_MODE 0x00001000
+ /* Print extent ?19 Full screen Scrolling region */
+#define PRINTFULLSCR_MODE 0x00002000
+ /* Keypad application 'Esc =' numeric 'Esc >' */
+#define KEYPADNUMERIC_MODE 0x00004000
+ /* default mode that we start the emulator with */
+#define DEFAULT_MODE (NEWLINE_MODE|ANSI_MODE|REPEAT_MODE)
+
+ /* This constant is VERY important - the size of the buffer for
+ * unprocessed vt-100 prints!
+ */
+#define MAXVTBUFLEN 4096
+
+ /* Constants used in place of actual row and column numbers
+ * for the cursor movement and text erasing and deleting functions.
+ */
+#define CUR_ROW -999
+#define CUR_COL -999
+#define ALL_TABS -1999
+
+#define LEFT_EDGE 0
+#define RIGHT_EDGE 12000
+#define TOP_EDGE 0
+#define BOTTOM_EDGE 12000
+
+ /* Text attribute definitions; color, font, bold. */
+#define NUM_SC_ATTRIBUTES 11
+
+#define SC_RED 0x0001
+#define SC_GREEN 0x0002
+#define SC_BLUE 0x0004
+#define SC_BOLD 0x0010
+#define SC_UL 0x0020 /* Underlined */
+#define SC_BL 0x0040 /* Blinking */
+#define SC_RV 0x0080 /* Reverse video */
+#define SC_ASCII 0x0100 /* Normal ASCII (USASCII) */
+#define SC_G0 0x0200 /* graphics set G0 */
+#define SC_G1 0x0400 /* Graphics set G1 */
+#define SC_GRAPHICS 0x0800 /* Good question */
+
+
+/* forward variable declarations */
+
+extern int termAttrMode[NUM_TERM_ATTR_MODES];
+extern int alltermAttrModes;
+
+
+/* prototypes from vt100.c */
+
+/* functions meant for use outside of the emulator */
+
+int vtputs(char *f);
+int vtprintf(char *format, ...);
+int vtInitVT100(void);
+int vtProcessedTextOut(char *cbuf, int count);
+
+
+/* Prototype for functions which MUST BE SUPPLIED BY THE BACK END!!! */
+
+/* Back-end specific initialization is performed in this function.
+ * this is gauranteed to be called before any other requests are made
+ * of the back end.
+ */
+
+/* beInitVT100Terminal() -
+ *
+ * This function is called by the VT100 emulator as soon as the
+ * front-end terminal is initialized. It's responsible for setting
+ * initial state of the terminal, and initing our many wacky variables.
+ */
+int beInitVT100Terminal();
+
+
+/* beAbsoluteCursor -
+ *
+ * Given an input row and column, move the cursor to the
+ * absolute screen coordinates requested. Note that if the
+ * display window has scrollbars, the column is adjusted
+ * to take that into account, but the row is not. This allows
+ * for large scrollback in terminal windows.
+ *
+ * ROW must be able to accept CUR_ROW, TOP_EDGE, BOTTOM_EDGE,
+ * or a row number.
+ *
+ * COLUMN must be able to accept CUR_COL, LEFT_EDGE, RIGHT_EDGE,
+ * or a column number.
+ */
+int beAbsoluteCursor(int row, int col);
+
+
+/* beOffsetCursor -
+ *
+ * Given an input row and column offset, move the cursor by that
+ * many positions. For instance, row=0 and column=-1 would move
+ * the cursor left a single column.
+ *
+ * If the cursor can't move the requested amount, results are
+ * unpredictable.
+ */
+int beOffsetCursor(int row, int column);
+
+
+/* beRestoreCursor -
+ *
+ * Saved cursor position should be stored in a static
+ * variable in the back end. This function restores the
+ * cursor to the position stored in that variable.
+ */
+int beRestoreCursor(void);
+
+
+/* beSaveCursor -
+ *
+ * The back-end should maintain a static variable with the
+ * last STORED cursor position in it. This function replaces
+ * the contents of that variable with the current cursor position.
+ * The cursor may be restored to this position by using the
+ * beRestoreCursor function.
+ */
+int beSaveCursor(void);
+
+
+/* beGetTextAttributes -
+ *
+ * given a pointer to 'fore'ground and 'back'ground ints,
+ * fill them with a device-independant description of the
+ * current foreground and background colors, as well as any
+ * font information in the foreground variable.
+ */
+int beGetTextAttributes(int *fore, int *back);
+
+
+/* beSetTextAttributes -
+ *
+ * Given a foreground and a background device independant (SC) color and font
+ * specification, apply these to the display, and save the state in the
+ * static screen variables.
+ *
+ * Note that many font-specific constants (bold/underline/reverse, G0/G1/ASCII)
+ * are stored ONLY in the foreground specification.
+ */
+int beSetTextAttributes(int fore, int back);
+
+
+/* beRawTextOut-
+ *
+ * The name of this function is misleading. Given a pointer to
+ * ascii text and a count of bytes to print, print them to the
+ * display device. If wrapping is enabled, wrap text. If there is a
+ * scrolling region set and the cursor is in it,
+ * scroll only within that region. 'beRawTextOut' means that it's guaranteed
+ * not to have control sequences within the text.
+ */
+int beRawTextOut(char *text, int len);
+
+
+/* beEraseText -
+ *
+ * Given a 'from' and a 'to' position in display coordinates,
+ * this function will fill in all characters between the two
+ * (inclusive) with spaces. Note that the coordinates do NOT
+ * specify a rectangle. erasing from (1,1) to (2,2) erases
+ * all of the first row, and the first two characters of the
+ * second.
+ *
+ * Note that this routine must be able to handle TOP_EDGE,
+ * BOTTOM_EDGE, LEFT_EDGE, RIGHT_EDGE, CUR_ROW, and CUR_COL
+ * in the appropriate parameters.
+ */
+int beEraseText(int rowFrom, int colFrom, int rowTo, int colTo);
+
+
+/* beDeleteText -
+ *
+ * Given a screen cursor 'from' and 'to' position, this function
+ * will delete all text between the two. Text will be scrolled
+ * up as appropriate to fill the deleted space. Note that, as in
+ * beEraseText, the two coordinates don't specify a rectangle, but
+ * rather a starting position and ending position. In other words,
+ * deleting from (1,1) to (2,2) should move the text from (2,3) to the
+ * end of the second row to (1,1), move line 3 up to line 2, and so on.
+ *
+ * This function must be able to process TOP_EDGE, BOTTOM_EDGE, LEFT_EDGE,
+ * RIGHT_EDGE, CUR_ROW, and CUR_COL specifications in the appropriate
+ * variables as well as regular row and column specifications.
+ */
+int beDeleteText(int rowFrom, int colFrom, int rowTo, int colTo);
+
+
+/* beInsertRow -
+ *
+ * Given a row number or CUR_ROW, TOP_EDGE or BOTTOM_EDGE as an input,
+ * this function will scroll all text from the current row down down by one,
+ * and create a blank row under the cursor.
+ */
+int beInsertRow(int row);
+
+
+/* beTransmitText -
+ *
+ * Given a pointer to text and byte count, this routine should transmit data
+ * to whatever host made the request it's responding to. Typically this routin
+ * should transmit data as though the user had typed it in.
+ */
+int beTransmitText(char *text, int len);
+
+
+/* beAdvanceToTab -
+ *
+ * This routine will destructively advance the cursor to the
+ * next set tab, or to the end of the line if there are no
+ * more tabs to the right of the cursor.
+ */
+
+int beAdvanceToTab(void);
+
+
+/* beClearTab -
+ *
+ * This function accepts a constant, and will try to clear tabs
+ * appropriately. Its argument is either
+ * ALL_TABS, meaning all tabs should be removed
+ * CUR_COL, meaning the tab in the current column should be wiped, or
+ * a column value, meaning if there's a tab there it should be wiped.
+ *
+ */
+int beClearTab(int col);
+
+
+/* beSetScrollingRows -
+ *
+ * Given a pair of row numbers, this routine will set the scrolling
+ * rows to those values. Note that this routine will accept
+ * TOP_ROW and BOTTOM_ROW as values, meaning that scrolling should
+ * be enabled for the entire display, regardless of resizing.
+ */
+int beSetScrollingRows(int fromRow, int toRow);
+
+
+/* beRingBell -
+ *
+ * Ring the system bell once.
+ */
+int beRingBell(void);
+
+
+/* beGetTermMode -
+ *
+ * Return the value of conTermMode, which is the terminal settings which
+ * can be queried/set by <esc>[?#h/l.
+ */
+int beGetTermMode();
+
+
+/* beSetTermMode -
+ *
+ * Set the terminal as requested, assuming we can. Right now we only handle a
+ * couple of the possible flags, but we store many of the others.
+ */
+int beSetTermMode(int newMode);