RetroZilla/extensions/xmlterm/lineterm/ltermOutput.c
2015-10-20 23:03:22 -04:00

976 lines
30 KiB
C

/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is lineterm.
*
* The Initial Developer of the Original Code is
* Ramalingam Saravanan.
* Portions created by the Initial Developer are Copyright (C) 1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/* ltermOutput.c: LTERM PTY output processing
*/
/* public declarations */
#include "lineterm.h"
/* private declarations */
#include "ltermPrivate.h"
static int ltermAppendOutput(struct lterms *lts, const char *cbuf, int count,
UNISTYLE style, int interleaveCheck,
int *interleavedBytes, int rawIncompleteMax,
int *rawIncompleteBytes, char *rawIncompleteBuf);
static int ltermDecode(const char *rawBuf, int n_total,
UNICHAR* decodedBuf, int decodeMax,
int decodeNUL,
int *rawIncompleteBytes);
static int ltermPromptLocate(struct lterms *lts);
/** Processes output from decoded output buffer and returns *opcodes:
* OPCODES ::= SCREENDATA BELL? ( CLEAR | INSERT | DELETE | SCROLL )?
* if ScreenMode data is being returned.
* OPCODES ::= LINEDATA BELL? ( CLEAR
* | OUTPUT NEWLINE?)
* if LineMode data is being returned.
* OPVALS contains return value(s) for specific screen operations
* such as INSERT, DELETE, and SCROLL.
* OPROW contains the row value for specific screen operations such as
* INSERT, DELETE, and SCROLL (or -1, cursor row is to be used).
* @return 0 on success and -1 on error.
*/
int ltermProcessOutput(struct lterms *lts, int *opcodes, int *opvals,
int *oprow)
{
struct LtermOutput *lto = &(lts->ltermOutput);
UNICHAR uch;
UNISTYLE ustyle;
int charIndex, returnCode, consumedChars, remainingChars, j;
int bellFlag;
LTERM_LOG(ltermProcessOutput,30,("lto->outputMode=%d, cursorChar=%d, Chars=%d\n",
lto->outputMode, lto->outputCursorChar, lto->outputChars));
LTERM_LOG(ltermProcessOutput,32, ("lts->commandNumber=%d\n",
lts->commandNumber));
/* Set default returned opcodes, opvals, oprow */
*opcodes = 0;
*opvals = 0;
*oprow = -1;
charIndex = 0;
bellFlag = 0;
while ((*opcodes == 0) && (charIndex < lto->decodedChars)) {
uch = lto->decodedOutput[charIndex];
ustyle = lto->decodedStyle[charIndex] | lto->styleMask;
consumedChars = 1;
if (uch == U_ESCAPE) {
/* Process escape sequence */
int savedOutputMode = lto->outputMode;
LTERM_LOG(ltermProcessOutput,31,("ESCAPE sequence\n"));
returnCode = ltermProcessEscape(lts, lto->decodedOutput+charIndex,
lto->decodedChars-charIndex,
lto->decodedStyle+charIndex,
&consumedChars, opcodes, opvals, oprow);
if (returnCode < 0)
return -1;
if (returnCode == 1) {
/* Incomplete escape sequence */
lto->incompleteEscapeSequence = 1;
if (lto->outputMode == LTERM1_SCREEN_MODE)
*opcodes = LTERM_SCREENDATA_CODE;
else
*opcodes = LTERM_LINEDATA_CODE | LTERM_OUTPUT_CODE;
}
/* Assert that change in output mode is a loop terminating condition */
if (lto->outputMode != savedOutputMode)
assert(*opcodes != 0);
} else if (lto->outputMode == LTERM1_SCREEN_MODE) {
/* Screen mode processing */
if ((uch >= (UNICHAR)U_SPACE) && (uch != (UNICHAR)U_DEL)) {
/* Printable non-TAB character */
LTERM_LOG(ltermProcessOutput,39,("Screen mode, printable char - %c\n",
(char) uch));
if (lto->insertMode) {
/* Insert blank character at cursor position */
if (ltermInsDelEraseChar(lts, 1, LTERM_INSERT_ACTION) != 0)
return -1;
}
/* Mark column as being modified */
if ((lto->modifiedCol[lto->cursorRow] == -1) ||
(lto->modifiedCol[lto->cursorRow] > lto->cursorCol))
lto->modifiedCol[lto->cursorRow] = lto->cursorCol;
/* Replace character and style info at current cursor location */
j = lto->cursorRow*lts->nCols + lto->cursorCol;
assert(j < (lts->nRows * lts->nCols));
lto->screenChar[j] = uch;
lto->screenStyle[j] = ustyle;
if (lto->cursorCol < lts->nCols-1) {
/* Move cursor right */
lto->cursorCol++;
}
} else {
/* Control character */
switch (uch) {
case U_BACKSPACE: /* Backspace */
LTERM_LOG(ltermProcessOutput,32,("Screen mode, BACKSPACE\n"));
if (lto->cursorCol > 0)
lto->cursorCol--;
break;
case U_TAB: /* Tab */
LTERM_LOG(ltermProcessOutput,32,("Screen mode, TAB\n"));
lto->cursorCol = ((lto->cursorCol/8)+1)*8;
if (lto->cursorCol > lts->nCols-1)
lto->cursorCol = lts->nCols-1;
break;
case U_BEL: /* Bell */
LTERM_LOG(ltermProcessOutput,32,("************ Screen mode, BELL\n"));
bellFlag = 1;
break;
case U_CRETURN: /* Carriage return */
lto->cursorCol = 0;
break;
case U_LINEFEED: /* Newline */
LTERM_LOG(ltermProcessOutput,32,("************ Screen mode, NEWLINE\n\n"));
*opcodes = LTERM_SCREENDATA_CODE;
if (lto->cursorRow > lto->botScrollRow) {
/* Not bottom scrolling line; simply move cursor down one row */
lto->cursorRow--;
} else {
/* Delete top scrollable line, scrolling up */
if (ltermInsDelEraseLine(lts, 1, lto->topScrollRow, LTERM_DELETE_ACTION) != 0)
return -1;
*opcodes |= LTERM_DELETE_CODE;
*opvals = 1;
*oprow = lto->topScrollRow;
}
break;
default:
/* Ignore other control characters (including NULs) */
break;
}
}
} else {
/* Line mode processing */
if ( ((uch >= (UNICHAR)U_SPACE) && (uch != (UNICHAR)U_DEL)) ||
(uch == (UNICHAR)U_TAB)) {
/* Printable/TAB character; replace/insert at current cursor location
or append to end of line */
LTERM_LOG(ltermProcessOutput,39,("Line mode, printable char - %c\n",
(char) uch));
if (lto->outputCursorChar == lto->outputChars) {
/* Append single character to end of line */
if (lto->outputChars+1 > MAXCOLM1) {
/* Output buffer overflow; ignore character */
LTERM_WARNING("ltermProcessOutput: Warning - output line buffer overflow\n");
}
lto->outputLine[lto->outputChars] = uch;
lto->outputStyle[lto->outputChars] = ustyle;
lto->outputChars++; /* Insert character in output line */
lto->outputCursorChar++; /* Reposition cursor */
} else {
/* Replace/insert single character in the middle of line */
if (lto->insertMode) {
/* Insert blank character at cursor position */
if (ltermInsDelEraseChar(lts, 1, LTERM_INSERT_ACTION) != 0)
return -1;
}
/* Overwrite single character in the middle of line */
lto->outputLine[lto->outputCursorChar] = uch;
lto->outputStyle[lto->outputCursorChar] = ustyle;
/* Note modifications */
if (lto->outputCursorChar < lto->outputModifiedChar)
lto->outputModifiedChar = lto->outputCursorChar;
lto->outputCursorChar++; /* Reposition cursor */
}
} else {
/* Control character */
switch (uch) {
case U_BACKSPACE: /* Backspace */
LTERM_LOG(ltermProcessOutput,32,("Line mode, BACKSPACE\n"));
if (lto->outputCursorChar > 0)
lto->outputCursorChar--;
break;
case U_CRETURN: /* Carriage return */
LTERM_LOG(ltermProcessOutput,32,("Line mode, CRETURN\n"));
lto->outputCursorChar = 0;
break;
case U_BEL: /* Bell */
LTERM_LOG(ltermProcessOutput,32,("************ Line mode, BELL\n"));
bellFlag = 1;
break;
case U_CTL_L: /* Formfeed; clear line and return */
LTERM_LOG(ltermProcessOutput,32,("************ Line mode, FORMFEED\n\n"));
ltermClearOutputLine(lts);
*opcodes = LTERM_LINEDATA_CODE | LTERM_CLEAR_CODE;
break;
case U_LINEFEED: /* Newline; return complete line */
LTERM_LOG(ltermProcessOutput,32,("************ Line mode, NEWLINE\n\n"));
*opcodes = LTERM_LINEDATA_CODE
| LTERM_OUTPUT_CODE
| LTERM_NEWLINE_CODE;
break;
default:
/* Ignore other control characters (including NULs) */
break;
}
}
}
/* Increment character index */
charIndex += consumedChars;
}
/* Determine count of unprocessed characters */
remainingChars = lto->decodedChars - charIndex;
if (remainingChars > 0) {
/* Move unprocessed output to beginning of decode buffer */
LTERM_LOG(ltermProcessOutput,32,("Moved %d chars to beginning of decodedOutput\n", remainingChars));
for (j=0; j<remainingChars; j++) {
lto->decodedOutput[j] = lto->decodedOutput[j+charIndex];
lto->decodedStyle[j] = lto->decodedStyle[j+charIndex];
}
}
/* Update remaining decoded character count */
lto->decodedChars = remainingChars;
if (*opcodes == 0) {
/* All output processed; without any special terminating condition */
if (lto->outputMode == LTERM1_SCREEN_MODE) {
/* Full screen mode */
*opcodes = LTERM_SCREENDATA_CODE;
} else {
/* Line mode */
*opcodes = LTERM_LINEDATA_CODE | LTERM_OUTPUT_CODE;
}
}
/* Set bell code */
if (bellFlag)
*opcodes |= LTERM_BELL_CODE;
if (*opcodes & LTERM_LINEDATA_CODE) {
/* Returning line mode data; check for prompt */
if ((lts->commandNumber == 0) ||
(lto->outputModifiedChar < lto->promptChars)) {
/* If not command line or if "prompt string" may have been modified,
* search for prompt
*/
int promptLen;
LTERM_LOG(ltermProcessOutput,39,("Prompt? modifiedChar=%d, promptChars=%d\n",
lto->outputModifiedChar, lto->promptChars));
/* Reset modification marker */
lto->outputModifiedChar = lto->outputChars;
/* Check if prompt string is present in output */
promptLen = ltermPromptLocate(lts);
if (promptLen > 0) {
/* Prompt string found */
lto->promptChars = promptLen;
if (lts->commandNumber == 0) {
/* Set command number */
/* New command number */
if (lts->lastCommandNum == 0xFFFF)
lts->lastCommandNum = 0;
lts->lastCommandNum++;
lts->commandNumber = lts->lastCommandNum;
LTERM_LOG(ltermProcessOutput,32,
("************ Prompt found; commandNumber=%d\n\n",
lts->commandNumber));
}
} else {
/* No prompt string */
if (lts->commandNumber != 0) {
/* Unset command number and prompt columns */
UNICHAR temLine[2] = {0, LTERM_WRITE_PLAIN_INPUT};
lts->commandNumber = 0;
lto->promptChars = 0;
/* Notify "input thread" by writing null input record */
WRITE(lts->writeBUFFER, temLine, 2*sizeof(UNICHAR));
LTERM_LOG(ltermProcessOutput,32,
("************ No prompt found; not command line\n\n"));
}
}
}
}
if (lto->outputMode == LTERM1_SCREEN_MODE) {
char modifiedRows[81];
int showRows = (lts->nRows < 80) ? lts->nRows : 80;
for (j=0; j<showRows; j++) {
if (lto->modifiedCol[j] > -1)
modifiedRows[j] = 'M';
else
modifiedRows[j] = '.';
}
modifiedRows[showRows] = '\0';
LTERM_LOG(ltermProcessOutput,38,("modifiedRows=%s\n", modifiedRows));
}
LTERM_LOG(ltermProcessOutput,31,("returned opcodes=0x%X\n", *opcodes));
return 0;
}
/** Clears output line buffer */
void ltermClearOutputLine(struct lterms *lts)
{
struct LtermOutput *lto = &(lts->ltermOutput);
LTERM_LOG(ltermClearOutputLine,40,("\n"));
lto->outputChars = 0;
lto->outputCursorChar = 0;
lto->outputModifiedChar = 0;
lto->promptChars = 0;
lts->commandNumber = 0;
}
/** Clears output screen buffer (allocating memory, if first time/resized) */
int ltermClearOutputScreen(struct lterms *lts)
{
struct LtermOutput *lto = &(lts->ltermOutput);
int j;
LTERM_LOG(ltermClearOutputScreen,40,("\n"));
if (lto->screenChar == NULL) {
/* Allocate memory for full screen */
int screenSize = lts->nRows * lts->nCols;
lto->screenChar = (UNICHAR *) MALLOC(screenSize * sizeof(UNICHAR));
if (lto->screenChar == NULL) {
LTERM_ERROR("ltermClearOutputScreen: Error - failed to allocate memory for chars\n");
return -1;
}
assert(lto->screenStyle == NULL);
lto->screenStyle = (UNISTYLE *) MALLOC(screenSize * sizeof(UNISTYLE));
if (lto->screenStyle == NULL) {
LTERM_ERROR("ltermClearOutputScreen: Error - failed to allocate memory for style\n");
return -1;
}
}
/* Position cursor at home */
lto->cursorRow = lts->nRows - 1;
lto->cursorCol = 0;
/* Blank out entire screen */
if (ltermInsDelEraseLine(lts, lts->nRows, lts->nRows-1, LTERM_ERASE_ACTION) != 0)
return -1;
/* No rows modified yet */
for (j=0; j<lts->nRows; j++) {
lto->modifiedCol[j] = -1;
}
return 0;
}
/** Saves current output mode value and switches to stream output mode,
* with specified opcodes and terminator string.
* @return 0 on success and -1 on error.
*/
int ltermSwitchToStreamMode(struct lterms *lts, int streamOpcodes,
const UNICHAR *streamTerminator)
{
struct LtermOutput *lto = &(lts->ltermOutput);
int strLength;
LTERM_LOG(ltermSwitchToStreamMode,40,("streamOpcodes=0x%x\n",streamOpcodes));
if (streamTerminator != NULL) {
/* Save terminator string (may be null) */
strLength = ucslen(streamTerminator);
ucsncpy( lto->streamTerminator, streamTerminator, MAXSTREAMTERM);
LTERM_LOGUNICODE(ltermSwitchToStreamMode,41,(streamTerminator,
(int) strLength));
} else {
/* Null terminator */
strLength = 0;
lto->streamTerminator[0] = U_NUL;
}
if (strLength > MAXSTREAMTERM-1) {
LTERM_ERROR("ltermSwitchToStreamMode: Error - terminator string too long\n");
return -1;
}
if (lts->options & LTERM_NONUL_FLAG) {
/* No decoding of NUL characters */
if (strLength == 0) {
LTERM_ERROR("ltermSwitchToStreamMode: Error - null terminator string not allowed\n");
return -1;
}
} else {
/* Decoding NUL characters */
if (strLength > 0) {
LTERM_ERROR("ltermSwitchToStreamMode: Error - terminator string must be NUL\n");
return -1;
}
}
lto->savedOutputMode = lto->outputMode;
lto->outputMode = LTERM0_STREAM_MODE;
lto->streamOpcodes = streamOpcodes;
return 0;
}
/** Switches to screen output mode.
* @return 0 on success and -1 on error.
*/
int ltermSwitchToScreenMode(struct lterms *lts)
{
struct LtermOutput *lto = &(lts->ltermOutput);
LTERM_LOG(ltermSwitchToScreenMode,40,("\n"));
if (lto->outputMode == LTERM2_LINE_MODE) {
/* Switching from line mode to screen mode */
/* Clear styleMask */
lto->styleMask = 0;
/* Clear screen */
if (ltermClearOutputScreen(lts) != 0)
return -1;
/* Reset returned cursor location */
lto->returnedCursorRow = -1;
lto->returnedCursorCol = -1;
/* Scrolling region */
lto->topScrollRow = lts->nRows-1;
lto->botScrollRow = 0;
/* Disable input echo */
lts->restoreInputEcho = !lts->disabledInputEcho;
lts->disabledInputEcho = 1;
/* Switch to raw input mode */
ltermSwitchToRawMode(lts);
}
lto->outputMode = LTERM1_SCREEN_MODE;
return 0;
}
/** Switches to line output mode.
* @return 0 on success and -1 on error.
*/
int ltermSwitchToLineMode(struct lterms *lts)
{
struct LtermOutput *lto = &(lts->ltermOutput);
int j;
LTERM_LOG(ltermSwitchToLineMode,40,("\n"));
if (lto->outputMode == LTERM1_SCREEN_MODE) {
/* Switching from screen mode to line mode */
/* Switch to line input mode */
ltermClearInputLine(lts);
if (lts->restoreInputEcho) {
/* Re-enable input echo */
lts->disabledInputEcho = 0;
lts->restoreInputEcho = 0;
}
/* Clear styleMask */
lto->styleMask = 0;
/* Clear output line */
ltermClearOutputLine(lts);
/* Copy bottom line to line output buffer ??? */
lto->outputChars = lts->nCols;
assert(lts->nCols < MAXCOL);
for (j=0; j<lts->nCols; j++) {
lto->outputLine[j] = lto->screenChar[j];
lto->outputStyle[j] = lto->screenStyle[j];
}
}
lto->outputMode = LTERM2_LINE_MODE;
return 0;
}
/** Reads data from process STDOUT and/or STDERR using file descriptors
* from the LTERM POLL structure, and converts to Unicode, saving
* incomplete character encodings in corresponding incomplete raw buffers.
* Decoded characters are appended to the decodedOutput buffer,
* with appropriate style settings.
* If READERR is false, STDERR is never read.
* @return the number of decoded characters appended (>=0) if successful,
* -1 if an error occurred during processing, or
* -2 if pseudo-TTY has been closed.
*/
int ltermReceiveData(struct lterms *lts, int readERR)
{
struct LtermOutput *lto = &(lts->ltermOutput);
char temERRBuf[MAXCOL], temOUTBuf[MAXCOL];
int readERRMax, readOUTMax;
int nReadERR, nTotalERR, nReadOUT, nTotalOUT;
int interleavedBytes, n_decoded, n_decoded_tot, j;
LTERM_LOG(ltermReceiveData,30,("\n"));
nTotalERR = 0;
if (readERR && (lto->pollFD[POLL_STDERR].POLL_REVENTS != 0)) {
/* Read data from STDERR */
/* Read maximum number of bytes that will all fit into decodedOutput
when decoded, assuming number of decoded characters will not exceed
the number of encoded bytes */
readERRMax = MAXCOLM1 - lto->decodedChars - lto->rawERRBytes;
/* Reduce by half to leave some space for STDOUT data */
readERRMax = readERRMax / 2;
if (readERRMax <= 0) {
/* Decoded buffer overflow */
LTERM_WARNING("ltermReceiveData: Warning - decoded buffer overflow\n");
/* Non-fatal error recovery; return without reading */
return 0;
}
/* Copy any incomplete raw output to beginning of buffer */
for (j=0; j<lto->rawERRBytes; j++)
temERRBuf[j] = lto->rawERRBuf[j];
/* Read STDERRdata (blocking mode) */
nReadERR = READ(lto->pollFD[POLL_STDERR].fd,
temERRBuf + lto->rawERRBytes, (SIZE_T) readERRMax);
if (nReadERR < 0) {
#if defined(DEBUG_LTERM) && !defined(USE_NSPR_IO)
int errcode = errno;
/* perror("ltermReceiveData"); */
#else
int errcode = 0;
#endif
LTERM_ERROR( "ltermReceiveData: Error %d in reading from process STDERR\n",
errcode);
return -1;
}
if (nReadERR == 0) {
/* End of file => pseudo-TTY has been closed */
LTERM_LOG(ltermReceiveData,31,("pseudo-TTY has been closed\n"));
/* Suspend LTERM */
lts->suspended = 1;
return -2;
}
LTERM_LOG(ltermReceiveData,32,("Read %d raw bytes from STDERR\n", nReadERR));
nTotalERR = nReadERR + lto->rawERRBytes;
}
nTotalOUT = 0;
if (lto->pollFD[POLL_STDOUT].POLL_REVENTS != 0) {
/* Read data from STDOUT */
/* Read maximum number of bytes that will all fit into decodedOutput
when decoded, assuming number of decoded characters will not exceed
the number of encoded bytes */
readOUTMax = MAXCOLM1 - lto->decodedChars - lto->rawOUTBytes - nTotalERR;
if (readOUTMax <= 0) {
/* Decoded buffer overflow */
LTERM_WARNING("ltermReceiveData: Warning - decoded buffer overflow\n");
/* Non-fatal error recovery; return without reading */
return 0;
}
/* Copy any incomplete raw output to beginning of buffer */
for (j=0; j<lto->rawOUTBytes; j++)
temOUTBuf[j] = lto->rawOUTBuf[j];
/* Read STDOUTdata (blocking mode) */
nReadOUT = READ(lto->pollFD[POLL_STDOUT].fd,
temOUTBuf + lto->rawOUTBytes, (SIZE_T) readOUTMax);
if (nReadOUT < 0) {
#if defined(DEBUG_LTERM) && !defined(USE_NSPR_IO)
int errcode = errno;
/* perror("ltermReceiveData"); */
#else
int errcode = 0;
#endif
LTERM_ERROR( "ltermReceiveData: Error %d in reading from process STDOUT\n",
errcode);
return -1;
}
if (nReadOUT == 0) {
/* End of file => pseudo-TTY has been closed */
LTERM_LOG(ltermReceiveData,31,("pseudo-TTY has been closed\n"));
/* Suspend LTERM */
lts->suspended = 1;
return -2;
}
LTERM_LOG(ltermReceiveData,32,("Read %d raw bytes from STDOUT\n", nReadOUT));
nTotalOUT = nReadOUT + lto->rawOUTBytes;
}
n_decoded_tot = 0;
if (lts->readERRfirst) {
/* Decode STDERR data first */
interleavedBytes = 0;
n_decoded = ltermAppendOutput(lts, temERRBuf, nTotalERR,
LTERM_STDERR_STYLE,
lts->interleave, &interleavedBytes,
MAXRAWINCOMPLETE,
&(lto->rawERRBytes), lto->rawERRBuf);
if (n_decoded < 0)
return -1;
n_decoded_tot += n_decoded;
/* Decode STDOUT data next */
n_decoded = ltermAppendOutput(lts, temOUTBuf, nTotalOUT,
LTERM_STDOUT_STYLE,
0, NULL,
MAXRAWINCOMPLETE,
&(lto->rawOUTBytes), lto->rawOUTBuf);
if (n_decoded < 0)
return -1;
n_decoded_tot += n_decoded;
if (interleavedBytes > 0) {
/* Decode remaining STDERR data */
n_decoded = ltermAppendOutput(lts, temERRBuf+interleavedBytes,
nTotalERR-interleavedBytes,
LTERM_STDERR_STYLE,
0, NULL,
MAXRAWINCOMPLETE,
&(lto->rawERRBytes), lto->rawERRBuf);
if (n_decoded < 0)
return -1;
n_decoded_tot += n_decoded;
}
} else {
/* Decode STDOUT data first */
interleavedBytes = 0;
n_decoded = ltermAppendOutput(lts, temOUTBuf, nTotalOUT,
LTERM_STDOUT_STYLE,
lts->interleave, &interleavedBytes,
MAXRAWINCOMPLETE,
&(lto->rawOUTBytes), lto->rawOUTBuf);
if (n_decoded < 0)
return -1;
n_decoded_tot += n_decoded;
/* Decode STDERR data next */
n_decoded = ltermAppendOutput(lts, temERRBuf, nTotalERR,
LTERM_STDERR_STYLE,
0, NULL,
MAXRAWINCOMPLETE,
&(lto->rawERRBytes), lto->rawERRBuf);
if (n_decoded < 0)
return -1;
n_decoded_tot += n_decoded;
if (interleavedBytes > 0) {
/* Decode remaining STDOUT data */
n_decoded = ltermAppendOutput(lts, temOUTBuf+interleavedBytes,
nTotalOUT-interleavedBytes,
LTERM_STDOUT_STYLE,
0, NULL,
MAXRAWINCOMPLETE,
&(lto->rawOUTBytes), lto->rawOUTBuf);
if (n_decoded < 0)
return -1;
n_decoded_tot += n_decoded;
}
}
if (n_decoded_tot > 0) {
/* New characters have been decoded; no longer incomplete escape seq. */
lto->incompleteEscapeSequence = 0;
}
return n_decoded_tot;
}
/** Process COUNT bytes raw data from CBUF, converting complete
* character encodings to Unicode, saving RAWINCOMPLETEBYTES in
* RAWINCOMPLETEBUF, whose maximum size is specified by RAWINCOMPLETEMAX.
* If INTERLEAVECHECK, check for starting NEWLINE and other interleavable
* characters, and if found "decode" just those characters and set
* INTERLEAVEDBYTES to the number of bytes consumed on return;
* otherwise INTERLEAVEDBYTES is set to 0.
* The decoded characters are appended to the decodedOutput buffer,
* with style STYLE.
* @return the number of characters appended (>=0) if successful,
* -1 if an error occurred during processing, or
* -2 if pseudo-TTY has been closed.
*/
static int ltermAppendOutput(struct lterms *lts, const char *cbuf, int count,
UNISTYLE style, int interleaveCheck,
int *interleavedBytes, int rawIncompleteMax,
int *rawIncompleteBytes, char *rawIncompleteBuf)
{
struct LtermOutput *lto = &(lts->ltermOutput);
int decodeMax, n_decoded, decodeNUL, j;
LTERM_LOG(ltermAppendOutput,30,("count=%d, style=0x%X, iCheck=%d, rawIncMax=%d\n",
count, style, interleaveCheck, rawIncompleteMax));
if (interleaveCheck && (count > 0) && (cbuf[0] == '\n')) {
/* Raw buffer starts with NEWLINE character; interleave */
assert(lto->decodedChars < MAXCOLM1);
/* "Decode" just the NEWLINE character */
lto->decodedOutput[lto->decodedChars] = U_LINEFEED;
lto->decodedStyle[lto->decodedChars] = LTERM_STDOUT_STYLE;
lto->decodedChars++;
*interleavedBytes = 1;
LTERM_LOG(ltermAppendOutput,32,("INTERLEAVED %d bytes\n",
*interleavedBytes));
return 1;
}
if (interleavedBytes != NULL)
*interleavedBytes = 0;
if (count == 0)
return 0;
/* Decode all complete encoded character sequences */
decodeMax = MAXCOLM1 - lto->decodedChars;
decodeNUL = (lts->options & LTERM_NONUL_FLAG) == 0;
n_decoded = ltermDecode(cbuf, count,
lto->decodedOutput+lto->decodedChars,
decodeMax, decodeNUL,
rawIncompleteBytes);
if (n_decoded < 0)
return -1;
/* Save any incomplete raw byte encodings */
if (*rawIncompleteBytes > rawIncompleteMax) {
LTERM_ERROR("ltermAppendOutput: Error - too many incomplete raw characters\n");
return -1;
}
for (j=0; j<*rawIncompleteBytes; j++)
rawIncompleteBuf[j] = cbuf[j+count-*rawIncompleteBytes];
/* Set decoded character styles */
for (j=lto->decodedChars; j<lto->decodedChars+n_decoded; j++)
lto->decodedStyle[j] = style;
/* Increment decoded character count */
lto->decodedChars += n_decoded;
LTERM_LOG(ltermAppendOutput,32,("Appended %d bytes\n", n_decoded));
return n_decoded;
}
/** Decodes N_TOTAL bytes in RAWBUF, returning upto DECODEMAX Unicode
* characters in DECODEDBUF, leaving *RAWINCOMPLETEBYTES at the end of
* RAWBUF undecoded.
* Any NUL (zero) characters in RAWBUF are skipped, unless
* decodeNUL is true.
* If there is not enough space in DECODEDBUF for all characters to be
* decoded, an error is returned.
* @return the number of Unicoded characters decoded (>=0) or -1 on error.
*/
static int ltermDecode(const char *rawBuf, int n_total,
UNICHAR* decodedBuf, int decodeMax,
int decodeNUL,
int *rawIncompleteBytes)
{
int n_decoded, result;
LTERM_LOG(ltermDecode,40,("\n"));
if (decodeMax < n_total) {
LTERM_ERROR("ltermDecode: Error - decode buffer overflow\n");
return -1;
}
result = utf8toucs(rawBuf, n_total, decodedBuf, decodeMax,
!decodeNUL, rawIncompleteBytes, &n_decoded);
if (result != 0) {
LTERM_WARNING("ltermDecode: Warning - Invalid UTF8 data encountered\n");
}
LTERM_LOG(ltermDecode,41,("result=%d, incomplete=%d, n_decoded=%d\n",
result, *rawIncompleteBytes, n_decoded));
LTERM_LOGUNICODE(ltermDecode,42,(decodedBuf, n_decoded));
return n_decoded;
}
/** Search for prompt string in the output line buffer.
* TEMPORARY implementation: assume promptRegexp is just a list of delimiters
* @return the length of the matching prompt string, or
* 0 if there is no match.
*/
static int ltermPromptLocate(struct lterms *lts)
{
struct LtermOutput *lto = &(lts->ltermOutput);
int prefixCount, promptLen;
LTERM_LOG(ltermPromptLocate,49,("lto->outputChars=%d\n",
lto->outputChars));
/* Assert that there is at least one free character in the buffer */
assert(lto->outputChars < MAXCOL);
if (lto->outputChars == 0)
return 0;
/* Insert null character at the end of the output buffer */
lto->outputLine[lto->outputChars] = U_NUL;
/* Determine length of initial non-delimiter prefix */
prefixCount = ucscspn(lto->outputLine, lts->promptRegexp);
if (prefixCount+1 >= lto->outputChars) {
promptLen = 0;
} else {
/* Skip any whitespace following the delimiter */
const UNICHAR spaceStr[3] = {U_SPACE, U_TAB, U_NUL};
int spaceCount = ucsspn(lto->outputLine+prefixCount+1, spaceStr);
promptLen = prefixCount + 1 + spaceCount;
LTERM_LOGUNICODE(ltermPromptLocate,41,(lto->outputLine, promptLen));
}
return promptLen;
}