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

1227 lines
37 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 ***** */
/* ltermIO.c: LTERM PTY input/output data processing
*/
/* public declarations */
#include "lineterm.h"
/* private declarations */
#include "ltermPrivate.h"
static int ltermWrite(struct lterms *lts, int *opcodes);
static int ltermReturnStreamData(struct lterms *lts, struct LtermRead *ltr);
static int ltermReturnScreenData(struct lterms *lts, struct LtermRead *ltr,
int opcodes, int opvals, int oprow);
static int ltermReturnInputLine(struct lterms *lts, struct LtermRead *ltr,
int completionRequested);
static int ltermReturnOutputLine(struct lterms *lts, struct LtermRead *ltr);
/** Transmits input line + optional control character UCH to child process.
* If UCH is 0, no control character is transmitted.
* Copy of the transmitted plain text (without any markup or character escapes)
* is saved in the echo buffer, prefixed by the prompt.
* If echoControl is true, control character is echoed in printable form.
* If completionCode == LTERM_NO_COMPLETION, transmit complete line
* and set input line break flag.
* If completionCode != LTERM_NO_COMPLETION, transmit only characters to
* left of current and set completion request flag.
* Called from ltermLineInput.
* @return 0 on success, -1 on error.
*/
int ltermSendLine(struct lterms *lts, UNICHAR uch,
int echoControl, int completionCode)
{
struct LtermInput *lti = &(lts->ltermInput);
struct LtermOutput *lto = &(lts->ltermOutput);
UNICHAR insch;
int glyphCount, prefixChars, charCount, sendCount;
int j, k, startCol, nCols, charIndex;
LTERM_LOG(ltermSendLine,40,
("uch=0x%x, echoControl=%d, completionCode=%d, completionRequest=%d\n",
uch, echoControl, completionCode, lts->completionRequest));
if ((lts->completionRequest == LTERM_HISTORY_COMPLETION) &&
(lts->completionChars > 0)) {
/* Delete prior completion inserts */
if (ltermDeleteGlyphs(lti, lts->completionChars) != 0)
return -1;
}
if (completionCode != LTERM_NO_COMPLETION) {
/* Send only glyphs to left of cursor */
glyphCount = lti->inputCursorGlyph;
} else {
/* Send all glyphs in input line */
glyphCount = lti->inputGlyphs;
}
if (lto->promptChars > 0) {
/* Insert output prompt into echo buffer */
prefixChars = lto->promptChars;
} else {
/* Insert entire output line into echo buffer */
prefixChars = lto->outputChars;
}
charCount = prefixChars;
LTERM_LOG(ltermSendLine,42,
("lto->promptChars=%d, outputChars=%d, glyphCount=%d\n",
lto->promptChars, lto->outputChars, glyphCount));
if (charCount >= MAXCOLM1) {
/* Buffer overflow; error return */
LTERM_ERROR("ltermSendLine: Error - character buffer overflow\n");
return -1;
}
for (j=0; j<charCount; j++)
lts->echoLine[j] = lto->outputLine[j];
/* Append input line */
for (j=0; j<glyphCount; j++) {
startCol = lti->inputGlyphColIndex[j];
nCols = lti->inputGlyphColIndex[j+1] - startCol;
for (k=startCol; k<startCol+nCols; k++) {
charIndex = lti->inputColCharIndex[k];
insch = lti->inputLine[charIndex];
#if 0
/* COMMENTED OUT: Plain text not escaped; use later in HTML insert */
if (insch == U_AMPERSAND) {
/* Escaped character */
/* Assert that there is one free character position in buffer */
assert(lti->inputChars < MAXCOL);
/* Insert null character at the end of the input buffer */
lti->inputLine[lti->inputChars] = U_NUL;
/* Match escape sequence against known escape sequences */
for (m=0; m<LTERM_XML_ESCAPES; m++) {
if (ucsncmp(lti->inputLine+charIndex,
ltermGlobal.escapeSeq[m],
(unsigned int) ltermGlobal.escapeLen[m]) == 0)
break;
}
if (m < LTERM_XML_ESCAPES) {
/* Subsitute single character for escape sequence */
insch = (UNICHAR) ltermGlobal.escapeChars[m];
} else {
LTERM_WARNING("ltermSendLine: Warning - unknown XML character escape sequence\n");
}
}
#endif /* 0 */
if (charCount >= MAXCOLM1) {
/* Buffer overflow; error return */
LTERM_ERROR("ltermSendLine: Error - character buffer overflow\n");
return -1;
} else {
/* Insert character into echo line buffer */
lts->echoLine[charCount++] = insch;
}
}
}
/* Transmit data to process */
sendCount = charCount - prefixChars;
if (lts->completionRequest != LTERM_NO_COMPLETION) {
/* Completion already requested */
/* Send only control character */
if (uch != U_NUL) {
if (ltermSendData(lts, &uch, 1) != 0)
return -1;
}
} else {
if (uch != U_NUL) {
/* Send line+control character to child process */
/* Insert control character at the end of the buffer (temporarily) */
lts->echoLine[charCount] = uch;
sendCount++;
}
if (ltermSendData(lts, lts->echoLine+prefixChars, sendCount) != 0)
return -1;
}
if (completionCode != LTERM_NO_COMPLETION) {
/* Request completion */
lts->completionRequest = completionCode;
/* No completion text inserted yet */
lts->completionChars = 0;
} else {
/* Set input line break flag;
* cleared only by output line break processing
*/
lts->inputLineBreak = 1;
}
if (echoControl && (charCount+2 < MAXCOLM1)) {
/* Echo control character in printable form */
lts->echoLine[charCount++] = U_CARET;
lts->echoLine[charCount++] = U_ATSIGN + uch;
}
/* Set count of echo characters (excluding control character) */
lts->echoChars = charCount;
LTERM_LOG(ltermSendLine,41,("glyphCount=%d, sendCount=%d\n",
glyphCount, sendCount));
LTERM_LOGUNICODE(ltermSendLine,41,(lts->echoLine, charCount));
return 0;
}
/** Writes data from input buffer pipe to pseudo-TTY,
* processing one complete record read from input buffer pipe
* and returning
* OPCODES ::= LINEDATA ( INPUT ( NEWLINE HIDE? )?
* | INPUT META ( COMPLETION | NEWLINE HIDE? ) )
* NOTE: This call may block to read data from input buffer pipe,
* if lts->inputBufRecord is false.
* Non-zero OPCODES means echoable input line data was processed.
* Zero OPCODES implies that no echoable input data was processed, i.e.,
* an incomplete record or screen/output data processing.
* @return 0 if successful,
* -1 on error, and
* -2 if pseudo-TTY has been closed.
*/
int ltermWrite(struct lterms *lts, int *opcodes)
{
struct LtermInput *lti = &(lts->ltermInput);
struct LtermOutput *lto = &(lts->ltermOutput);
char *byteBuf = (char *) lti->inputBuf;
int returnCode, availableChars, processedChars, headerChars, totalChars, j;
LTERM_LOG(ltermWrite,20, ("inputLineBreak=%d, inputBufRecord=%d\n",
lts->inputLineBreak, lts->inputBufRecord));
*opcodes = 0;
if (!lts->inputBufRecord) {
/* Complete record not available in buffer; read data from input pipe */
int readMax, n_read;
readMax = (PIPEHEADER+MAXCOL)*sizeof(UNICHAR) - lti->inputBufBytes;
assert(readMax > 0);
n_read = READ(lts->readBUFFER, byteBuf + lti->inputBufBytes,
(SIZE_T) readMax);
if (n_read < 0) {
LTERM_WARNING("ltermWrite: Warning - error %d in reading from input buffer pipe\n", n_read);
return -1;
}
LTERM_LOG(ltermWrite,22,
("read %d bytes from input buffer pipe\n", n_read));
if (n_read == 0) {
/* End of file */
LTERM_LOG(ltermWrite,21,("input buffer pipe has been closed\n"));
/* Suspend LTERM */
lts->suspended = 1;
return -2;
}
/* Update count of bytes read so far */
lti->inputBufBytes += n_read;
LTERM_LOGUNICODE(ltermWrite,12,(lti->inputBuf,
lti->inputBufBytes/sizeof(UNICHAR)));
/* Check if one or more complete records is now available in buffer */
lts->inputBufRecord = (lti->inputBufBytes >= (int)sizeof(UNICHAR)) &&
(lti->inputBufBytes >= (PIPEHEADER+lti->inputBuf[0])*((int)sizeof(UNICHAR)));
if (!lts->inputBufRecord) {
/* Incomplete input record; return */
return 0;
}
}
/* availableChars may be zero; such zero length input data records
* are used to notify the "input thread" about output processing events,
* such as the detection of a command line prompt (which would
* enable the command completion input mode)
*/
availableChars = lti->inputBuf[0];
processedChars = 0;
LTERM_LOG(ltermWrite,22, ("inputBuf[0]=%d, inputBuf[1]=%d\n",
lti->inputBuf[0], lti->inputBuf[1]));
if (lti->inputBuf[1] == LTERM_WRITE_CLOSE_MESSAGE) {
/* LTERM being closed */
return -2;
}
if (lti->inputBuf[1] == LTERM_WRITE_PLAIN_INPUT) {
/* Process plain text input record */
int doNotCancelCompletion = 0;
if (lti->inputMode >= LTERM3_COMPLETION_MODE) {
if (availableChars == 1) {
/* Single control character completion requests */
doNotCancelCompletion =
( (lts->completionRequest == LTERM_TAB_COMPLETION) &&
(lti->inputBuf[2] == U_TAB) ) ||
( (lts->completionRequest == LTERM_HISTORY_COMPLETION) &&
((lti->inputBuf[2] == U_CTL_P) || (lti->inputBuf[2] == U_CTL_N)) );
} else if (availableChars == 3) {
/* Three character escape sequences for up/down arrow keys */
/* NOTE: Input CSI escape sequence; may not be portable */
doNotCancelCompletion =
( (lts->completionRequest == LTERM_HISTORY_COMPLETION) &&
(lti->inputBuf[2] == U_ESCAPE) && (lti->inputBuf[3] == U_LBRACKET) &&
((lti->inputBuf[4] == U_A_CHAR) || (lti->inputBuf[4] == U_B_CHAR)) );
}
}
if (!doNotCancelCompletion &&
(lts->completionRequest != LTERM_NO_COMPLETION)) {
/* Cancel any completion request */
if (ltermCancelCompletion(lts) != 0)
return -1;
}
LTERM_LOGUNICODE(ltermWrite,12,(lti->inputBuf+PIPEHEADER, availableChars));
returnCode = ltermPlainTextInput(lts, lti->inputBuf+PIPEHEADER,
availableChars, opcodes);
if (returnCode < 0)
return returnCode;
processedChars = availableChars;
} else if (lti->inputBuf[1] == LTERM_WRITE_XML_INPUT) {
/* Insert complete XML element into input line, after checks */
if (lts->completionRequest != LTERM_NO_COMPLETION) {
/* Cancel command completion request */
if (ltermCancelCompletion(lts) != 0)
return -1;
}
LTERM_WARNING("ltermWrite: Warning - LTERM_WRITE_XML_INPUT not yet implemented\n");
processedChars = availableChars;
} else if (lti->inputBuf[1] == LTERM_WRITE_PLAIN_OUTPUT) {
/* Insert plain text in decoded character buffer */
int decodeMax = MAXCOLM1 - lto->decodedChars;
processedChars = availableChars;
if (processedChars > decodeMax)
processedChars = decodeMax;
if (processedChars > 0) {
/* Copy characters to decoded character buffer */
int offset;
if ((lto->decodedChars > 0) &&
lto->incompleteEscapeSequence &&
(lto->decodedStyle[0] != LTERM_ALTOUT_STYLE)) {
/* Move incomplete escape sequence to end of decoded buffer */
for (j=lto->decodedChars-1; j>=0; j--) {
lto->decodedOutput[j+processedChars] = lto->decodedOutput[j];
lto->decodedStyle[j+processedChars] = lto->decodedStyle[j];
}
/* Incomplete escape sequence no longer at beginning of buffer */
lto->incompleteEscapeSequence = 0;
/* Insert new characters at beginning of decoded buffer */
offset = 0;
} else {
/* Append new characters at the end of the decoded buffer */
offset = lto->decodedChars;
}
/* Insert new characters in decoded buffer, with appropriate offset */
for (j=0; j<processedChars; j++) {
lto->decodedOutput[j+offset] = lti->inputBuf[PIPEHEADER+j];
lto->decodedStyle[j+offset] = LTERM_ALTOUT_STYLE;
}
/* Increment decoded character count */
lto->decodedChars += processedChars;
}
returnCode = 0;
}
if (processedChars == availableChars) {
/* Delete current record completely from buffer */
processedChars += PIPEHEADER;
headerChars = 0;
} else {
/* Delete processed characters from current record */
headerChars = PIPEHEADER;
/* Decrement character count in record header */
lti->inputBuf[0] -= processedChars;
}
LTERM_LOG(ltermWrite,23, ("processedChars=%d, headerChars=%d\n",
processedChars, headerChars));
/* Shift remaining data in buffer */
totalChars = lti->inputBufBytes/sizeof(UNICHAR);
for (j=headerChars; j<(totalChars-processedChars); j++)
lti->inputBuf[j] = lti->inputBuf[j+processedChars];
/* Decrement buffer count */
lti->inputBufBytes -= processedChars*sizeof(UNICHAR);
/* Check if one or more complete records is now left in buffer */
lts->inputBufRecord = (lti->inputBufBytes >= (int)sizeof(UNICHAR)) &&
(lti->inputBufBytes >= (PIPEHEADER+lti->inputBuf[0])*((int)sizeof(UNICHAR)));
LTERM_LOG(ltermWrite,21,
("return opcodes=0x%x, inputBufBytes=%d, inputBufRecord=%d\n",
*opcodes, lti->inputBufBytes, lts->inputBufRecord));
return 0;
}
/** Reads a (possibly incomplete) line from LTERM.
* The LtermRead structure *LTR contains both input and output parameters,
* returning opcodes values of
* OPCODES ::= STREAMDATA NEWLINE? ERROR?
* COOKIESTR? DOCSTREAM? XMLSTREAM? WINSTREAM?
* if StreamMode data is being returned.
*
* OPCODES ::= SCREENDATA BELL? ( OUTPUT | CLEAR | INSERT | DELETE | SCROLL )?
* if ScreenMode data is being returned.
*
* OPCODES ::= LINEDATA BELL? ( CLEAR
* | OUTPUT NEWLINE? )
* if LineMode data is being returned.
*
* (Auxiliary procedure for lterm_read.)
* @return 0 if successful,
* -1 if an error occurred while reading,
* -2 if pseudo-TTY has been closed,
* -3 if more than MAX_COUNT characters are present in the line
* (in this case the first MAX_COUNT characters are returned in BUF,
* and the rest are discarded).
*/
int ltermRead(struct lterms *lts, struct LtermRead *ltr, int timeout)
{
struct LtermInput *lti = &(lts->ltermInput);
struct LtermOutput *lto = &(lts->ltermOutput);
int waitTime, nfds_all, pollCode, rePoll;
int inputAvailable, newOutputAvailable;
int outOpcodes, outOpvals, outOprow, returnCode;
int j;
LTERM_LOG(ltermRead,20,("start outputMode=%d\n", lto->outputMode));
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(ltermRead,28,("modifiedRows=%s\n", modifiedRows));
}
waitTime = timeout;
if (lts->inputBufRecord) {
/* Complete input record available in buffer */
LTERM_LOG(ltermRead,21,("inputBufRecord=%d\n", lts->inputBufRecord));
waitTime = 0;
} else if ((lto->decodedChars > 0) && !lto->incompleteEscapeSequence) {
/* Do not wait if there are decoded characters to be processed that are
not part of an incomplete ESCAPE sequence */
LTERM_LOG(ltermRead,21,("decodedChars=%d,incompleteEscapeSequence=%d\n",
lto->decodedChars, lto->incompleteEscapeSequence));
waitTime = 0;
} else if (lto->outputMode == LTERM1_SCREEN_MODE) {
/* Screen mode: do not wait if there are any modified rows */
for (j=0; j<lts->nRows; j++)
if (lto->modifiedCol[j] >= 0) {
waitTime = 0;
break;
}
if (j<lts->nRows) {
LTERM_LOG(ltermRead,21,("j=%d, modifiedCol[j] = %d\n",
j, lto->modifiedCol[j]));
}
}
/* If stream mode, do not poll STDERR output */
if (lto->outputMode == LTERM0_STREAM_MODE)
nfds_all = POLL_COUNT-1;
else
nfds_all = lto->nfds;
do {
/* Do not re-poll unless something special occurs */
rePoll = 0;
/* Default return values in LtermRead structure */
ltr->read_count = 0;
ltr->opcodes = 0;
ltr->opvals = 0;
ltr->buf_row = -1;
ltr->buf_col = -1;
ltr->cursor_row = -1;
ltr->cursor_col = -1;
LTERM_LOG(ltermRead,21,("polling nfds = %d, waitTime = %d\n",
lto->nfds, waitTime));
/* Poll all files for data to read */
pollCode = POLL(lto->pollFD, (SIZE_T) lto->nfds, waitTime);
if (pollCode == -1) {
#if defined(DEBUG_LTERM) && !defined(USE_NSPR_IO)
int errcode = errno;
perror("ltermRead");
#else
int errcode = 0;
#endif
LTERM_ERROR( "ltermRead: Error return from poll, code=%d\n", errcode);
return -1;
}
/* Do not wait for polling the second time around */
waitTime = 0;
/* Check if input data is available */
inputAvailable = lts->inputBufRecord ||
( (pollCode > 0) &&
(lto->pollFD[POLL_INPUTBUF].POLL_REVENTS != 0) );
/* Check if STDOUT/STDERR data is available */
newOutputAvailable = (pollCode > 0) &&
( (lto->pollFD[POLL_STDOUT].POLL_REVENTS != 0) ||
(lto->pollFD[POLL_STDERR].POLL_REVENTS != 0) );
LTERM_LOG(ltermRead,21,
("inputAvailable=%d, newOutputAvailable=%d, completionRequest=%d\n",
inputAvailable, newOutputAvailable, lts->completionRequest));
if (inputAvailable && (!newOutputAvailable ||
(lts->completionRequest == LTERM_NO_COMPLETION)) ) {
/* Process an input data record; either no new output is available, or
* output is available but command completion has not been requested
*/
int inOpcodes;
returnCode = ltermWrite(lts, &inOpcodes);
if (returnCode < 0)
return returnCode;
if (inOpcodes != 0) {
/* Echoable input data processed; return input line */
returnCode = ltermReturnInputLine(lts, ltr, 0);
if (returnCode < 0)
return returnCode;
if (inOpcodes & LTERM_META_CODE) {
ltr->opcodes |= LTERM_META_CODE;
if (inOpcodes & LTERM_COMPLETION_CODE)
ltr->opcodes |= LTERM_COMPLETION_CODE;
}
if (inOpcodes & LTERM_NEWLINE_CODE) {
/* Input line break */
ltr->opcodes |= LTERM_NEWLINE_CODE;
if (inOpcodes & LTERM_HIDE_CODE)
ltr->opcodes |= LTERM_HIDE_CODE;
/* Clear input line buffer */
ltermClearInputLine(lts);
}
if (lts->disabledInputEcho) {
/* Input echo is disabled, re-poll */
rePoll = 1;
LTERM_LOG(ltermRead,32,("Input echo disabled; RE-POLLING\n\n"));
continue;
}
return 0;
}
inputAvailable = 0;
}
if (newOutputAvailable) {
/* Read data from STDOUT/STDERR */
int outChars;
outChars = ltermReceiveData(lts, nfds_all==POLL_COUNT);
if (outChars < 0)
return outChars;
}
if (lto->outputMode == LTERM0_STREAM_MODE) {
/* Stream mode */
return ltermReturnStreamData(lts, ltr);
}
/* If there is some complete decoded output, process it */
if ((lto->decodedChars > 0) && !lto->incompleteEscapeSequence) {
returnCode = ltermProcessOutput(lts, &outOpcodes, &outOpvals, &outOprow);
if (returnCode < 0)
return -1;
} else {
/* No new output to process */
if (lto->outputMode == LTERM1_SCREEN_MODE) {
outOpcodes = LTERM_SCREENDATA_CODE;
outOpvals = 0;
outOprow = -1;
} else {
/* No new output in line mode; return nothing */
return 0;
}
}
if (outOpcodes & LTERM_SCREENDATA_CODE) {
/* Return screen mode output */
return ltermReturnScreenData(lts, ltr, outOpcodes, outOpvals, outOprow);
}
if (outOpcodes & LTERM_LINEDATA_CODE) {
LTERM_LOG(ltermRead,32,
("inputLineBreak=%d, completionRequest=%d, completionChars=%d\n",
lts->inputLineBreak, lts->completionRequest, lts->completionChars));
if (outOpcodes & LTERM_OUTPUT_CODE) {
/* Return output line */
int echoMatch = 0;
returnCode = ltermReturnOutputLine(lts, ltr);
if (returnCode < 0)
return returnCode;
LTERM_LOG(ltermRead,33, ("read_count=%d, echoChars=%d\n",
ltr->read_count, lts->echoChars));
if (lts->inputLineBreak) {
/* Compare output line to initial portion of echo line */
echoMatch = 1;
if ( (ltr->read_count > lts->echoChars) ||
((ltr->read_count != lts->echoChars) &&
(outOpcodes & LTERM_NEWLINE_CODE)) ) {
echoMatch = 0;
} else {
int k;
for (k=0; k<ltr->read_count; k++) {
if (ltr->buf[k] != lts->echoLine[k]) {
echoMatch = 0;
break;
}
}
}
}
if (outOpcodes & LTERM_NEWLINE_CODE) {
/* Complete output line */
if (!echoMatch)
ltr->opcodes |= LTERM_NEWLINE_CODE;
/* Clear output line buffer */
ltermClearOutputLine(lts);
/* Clear input line break flag */
lts->inputLineBreak = 0;
} else {
/* Incomplete output line */
if (lts->completionRequest != LTERM_NO_COMPLETION) {
/* Completion requested;
* Compare first/last part of output line to parts of echo line
*/
int k;
int extraChars = ltr->read_count - lts->echoChars;
int compChars = ltr->read_count;
int preMatch = 1;
if (extraChars > 0)
compChars = lts->echoChars;
for (k=0; k<compChars; k++) {
if (ltr->buf[k] != lts->echoLine[k]) {
preMatch = 0;
break;
}
}
if (!preMatch) {
/* No match; cancel completion request */
if (ltermCancelCompletion(lts) != 0)
return -1;
} else if (extraChars >= 0) {
/* Matched complete echo line and perhaps more */
if ((extraChars > 0) && (extraChars <= MAXCOLM1)) {
/* Insert extra characters into input line */
lts->completionChars = extraChars;
for (k=0; k<extraChars; k++) {
if (ltermInsertChar(lti, ltr->buf[compChars+k]) != 0)
return -1;
}
LTERM_LOG(ltermRead,32,
("++++++++++++ COMPLETION SUCCESSFUL\n"));
LTERM_LOGUNICODE(ltermRead,32,
(ltr->buf+compChars, extraChars));
}
/* Return updated input line, prefixed with prompt */
returnCode = ltermReturnInputLine(lts, ltr, 1);
if (returnCode < 0)
return returnCode;
} else {
/* Partial match; do not echo */
echoMatch = 1;
}
} else if (lto->promptChars == lto->outputChars) {
/* Incomplete output line with just a prompt */
LTERM_LOG(ltermRead,32,("Prompt-only line\n"));
/* Return updated input line, prefixed with prompt */
returnCode = ltermReturnInputLine(lts, ltr, 0);
if (returnCode < 0)
return returnCode;
}
}
if (echoMatch) {
/* Do not display output line; re-poll */
rePoll = 1;
LTERM_LOG(ltermRead,32,("Echo match; RE-POLLING\n\n"));
continue;
} else {
return 0;
}
} else {
/* Line mode data with modifier flags; return just cursor position */
ltr->opcodes = outOpcodes;
ltr->opvals = 0;
/* Copy full screen cursor position */
ltr->cursor_row = -1;
ltr->cursor_col = 0;
ltr->read_count = 0;
return 0;
}
}
} while (rePoll);
return -1;
}
/** Interrupts output operations in response to an input TTY interrupt signal
* @return 0 if successful,
* -1 if an error occurred
*/
int ltermInterruptOutput(struct lterms *lts)
{
struct LtermOutput *lto = &(lts->ltermOutput);
if (lto->outputMode == LTERM0_STREAM_MODE) {
/* Abnormally terminate output stream */
lto->streamOpcodes |= LTERM_ERROR_CODE;
}
return 0;
}
/** Returns stream data from STDOUT
* (Auxiliary procedure for ltermRead.)
* OPCODES ::= STREAMDATA NEWLINE? ERROR?
* COOKIESTR? DOCSTREAM? XMLSTREAM? WINSTREAM?
* The LtermRead structure *LTR contains both input and output parameters.
* @return 0 if successful,
* -1 if an error occurred while reading,
*/
static int ltermReturnStreamData(struct lterms *lts, struct LtermRead *ltr)
{
struct LtermOutput *lto = &(lts->ltermOutput);
UNICHAR *locTerminator;
int streamTerminated, charCount, deleteCount, j;
LTERM_LOG(ltermReturnStreamData,30,("start\n"));
if (lto->streamOpcodes & LTERM_ERROR_CODE) {
/* Error in stream output; terminate stream abnormally */
LTERM_LOG(ltermReturnStreamData,32,("Error termination of STREAM mode\n"));
if (lto->savedOutputMode == LTERM1_SCREEN_MODE) {
if (ltermSwitchToScreenMode(lts) != 0)
return -1;
} else {
if (ltermSwitchToLineMode(lts) != 0)
return -1;
}
ltr->opcodes = lto->streamOpcodes | LTERM_STREAMDATA_CODE
| LTERM_NEWLINE_CODE;
ltr->read_count = 0;
return 0;
} else if (lto->decodedChars == 0) {
/* No output data available; return null opcode */
return 0;
}
ltr->opcodes = LTERM_STREAMDATA_CODE | lto->streamOpcodes;
if (ucslen(lto->streamTerminator) == 0) {
/* Null terminated stream */
locTerminator = NULL;
for (j=0; j<lto->decodedChars; j++) {
if (lto->decodedOutput[j] == 0) {
locTerminator = &lto->decodedOutput[j];
break;
}
}
} else {
/* Non-null terminated stream */
assert(lto->decodedChars < MAXCOL);
/* Insert terminating null character in decoded buffer */
lto->decodedOutput[lto->decodedChars] = U_NUL;
/* There should be no NULs in decoded output */
assert((int)ucslen(lto->decodedOutput) == lto->decodedChars);
/* Search for stream terminator string in decoded output */
locTerminator = ucsstr(lto->decodedOutput, lto->streamTerminator);
}
/* Stream termination flag */
streamTerminated = 0;
if (locTerminator == NULL) {
/* No terminator found; return decoded stream data */
charCount = lto->decodedChars;
if (charCount > ltr->max_count)
charCount = ltr->max_count;
} else {
/* Count stream portion of decoded output (excluding terminator) */
charCount = locTerminator - lto->decodedOutput;
if (charCount > ltr->max_count) {
/* Return only portion of stream data that fits into output buffer,
* without terminating stream mode
*/
charCount = ltr->max_count;
} else {
/* Return all remaining stream data, terminate stream mode and
* revert to saved output mode
*/
streamTerminated = 1;
if (lto->savedOutputMode == LTERM1_SCREEN_MODE) {
if (ltermSwitchToScreenMode(lts) != 0)
return -1;
} else {
if (ltermSwitchToLineMode(lts) != 0)
return -1;
}
LTERM_LOG(ltermReturnStreamData,32,("terminating STREAM mode\n"));
}
}
/* Copy stream data to output buffer */
for (j=0; j<charCount; j++) {
ltr->buf[j] = lto->decodedOutput[j];
ltr->style[j] = LTERM_STDOUT_STYLE;
}
/* Count of characters read */
ltr->read_count = charCount;
/* Count of characters to be deleted */
deleteCount = charCount;
if (streamTerminated) {
/* Stream terminated */
ltr->opcodes |= LTERM_NEWLINE_CODE;
/* Delete terminator string from decoded buffer */
if (ucslen(lto->streamTerminator) == 0) {
deleteCount += 1;
} else {
deleteCount += ucslen(lto->streamTerminator);
}
}
/* Shift remaining characters in decoded buffer */
for (j=deleteCount; j<lto->decodedChars; j++)
lto->decodedOutput[j-deleteCount] = lto->decodedOutput[j];
lto->decodedChars -= deleteCount;
LTERM_LOG(ltermReturnStreamData,31,("returning STREAM data (%d bytes)\n",
ltr->read_count));
return 0;
}
/** Returns processed screen data
* (Auxiliary procedure for ltermRead.)
* The LtermRead structure *LTR contains both input and output parameters.
* @return 0 if successful,
* -1 if an error occurred while reading,
* -3 if more than COUNT characters are present in the line
* (in this case the first COUNT characters are returned in BUF,
* and the rest are discarded).
*/
static int ltermReturnScreenData(struct lterms *lts, struct LtermRead *ltr,
int opcodes, int opvals, int oprow)
{
struct LtermOutput *lto = &(lts->ltermOutput);
int cursorMoved, returnRow, charCount, returnCode;
int jOffset, j;
cursorMoved = (lto->returnedCursorRow != lto->cursorRow) ||
(lto->returnedCursorCol != lto->cursorCol);
lto->returnedCursorRow = lto->cursorRow;
lto->returnedCursorCol = lto->cursorCol;
LTERM_LOG(ltermReturnScreenData,30,("cursorMoved=%d\n", cursorMoved));
/* Screen mode data with possible modifier flags */
ltr->opcodes = opcodes;
ltr->opvals = opvals;
/* Copy full screen cursor position */
ltr->cursor_row = lto->returnedCursorRow;
ltr->cursor_col = lto->returnedCursorCol;
if (opcodes &
(LTERM_CLEAR_CODE|LTERM_INSERT_CODE|LTERM_DELETE_CODE|LTERM_SCROLL_CODE)) {
/* Screen modifier flags; return no character data */
if (oprow >= 0) {
ltr->buf_row = oprow;
} else {
ltr->buf_row = lto->cursorRow;
}
ltr->buf_col = 0;
ltr->read_count = 0;
return 0;
}
/* Check if any row has been modified */
returnRow = -1;
for (j=0; j<lts->nRows; j++)
if (lto->modifiedCol[j] >= 0) {
returnRow = j;
break;
}
if (returnRow < 0) {
/* No modified rows */
ltr->read_count = 0;
ltr->buf_row = 0;
ltr->buf_col = 0;
if (!cursorMoved && (ltr->opcodes == LTERM_SCREENDATA_CODE)) {
/* Return no data (***IMPORTANT ACTION, TO PREVENT LOOPING***) */
ltr->opcodes = 0;
}
return 0;
}
/* Returning modified row */
ltr->opcodes |= LTERM_OUTPUT_CODE;
/* Copy entire row */
ltr->buf_row = returnRow;
ltr->buf_col = 0;
charCount = lts->nCols;
if (charCount <= ltr->max_count) {
returnCode = 0;
} else {
/* Too much data to fit into output buffer; truncate */
charCount = ltr->max_count;
returnCode = -3;
}
jOffset = ltr->buf_row * lts->nCols + ltr->buf_col;
for (j=0; j<charCount; j++) {
ltr->buf[j] = lto->screenChar[jOffset+j];
ltr->style[j] = lto->screenStyle[jOffset+j];
}
/* Clear modified flag in returned row */
lto->modifiedCol[returnRow] = -1;
ltr->read_count = charCount;
LTERM_LOG(ltermReturnScreenData,31,("returning SCREEN data\n"));
LTERM_LOGUNICODE(ltermReturnScreenData,31,(ltr->buf, ltr->read_count));
return returnCode;
}
/** Returns incomplete input line, prefixed with prompt or incomplete output.
* (Auxiliary procedure for ltermRead.)
* The LtermRead structure *LTR contains both input and output parameters.
* Set COMPLETIONREQUESTED to true if this is a completed command line.
* @return 0 if successful,
* -1 if an error occurred while reading,
* -3 if more than COUNT characters are present in the line
* (in this case the first COUNT characters are returned in BUF,
* and the rest are discarded).
*/
static int ltermReturnInputLine(struct lterms *lts, struct LtermRead *ltr,
int completionRequested)
{
struct LtermOutput *lto = &(lts->ltermOutput);
struct LtermInput *lti = &(lts->ltermInput);
int outChars, charCount, inputCursorCol, returnCode;
int j;
LTERM_LOG(ltermReturnInputLine,30, ("outputChars=%d, promptChars=%d\n",
lto->outputChars, lto->promptChars));
if (lto->promptChars > 0) {
/* Prefix with prompt output data */
ltr->opcodes = LTERM_LINEDATA_CODE;
outChars = lto->promptChars;
if (!completionRequested) {
/* Hack to handle misidentified prompts;
* if output characters following the prompt differ from input line,
* display them.
*/
for (j=0; j<lti->inputChars; j++) {
if (((j+outChars) < lto->outputChars) &&
(lto->outputLine[j+outChars] != lti->inputLine[j])) {
outChars = lto->outputChars;
break;
}
}
}
if (outChars > ltr->max_count)
outChars = 0;
if (outChars > 0) {
ltr->opcodes |= LTERM_PROMPT_CODE;
for (j=0; j<outChars; j++) {
ltr->buf[j] = lto->outputLine[j];
ltr->style[j] = LTERM_PROMPT_STYLE;
}
}
} else {
/* Prefix with entire output line */
returnCode = ltermReturnOutputLine(lts, ltr);
if (returnCode < 0)
return returnCode;
outChars = ltr->read_count;
}
ltr->opcodes |= LTERM_INPUT_CODE;
charCount = outChars + lti->inputChars;
if (charCount <= ltr->max_count) {
returnCode = 0;
} else {
/* Too much data to fit into output buffer; truncate */
charCount = ltr->max_count;
returnCode = -3;
}
/* Append STDIN data, with markup and escape sequences */
for (j=outChars; j<charCount; j++) {
ltr->buf[j] = lti->inputLine[j-outChars];
ltr->style[j] = LTERM_STDIN_STYLE;
}
inputCursorCol = lti->inputGlyphColIndex[lti->inputCursorGlyph];
ltr->buf_row = -1;
ltr->buf_col = 0;
ltr->cursor_row = -1;
ltr->cursor_col = outChars + lti->inputColCharIndex[inputCursorCol];
ltr->read_count = charCount;
LTERM_LOG(ltermReturnInputLine,32,("returning INPUT LINE data\n"));
LTERM_LOGUNICODE(ltermReturnInputLine,32,(ltr->buf, ltr->read_count));
return returnCode;
}
/** Returns output line data
* (Auxiliary procedure for ltermRead.)
* The LtermRead structure *LTR contains both input and output parameters.
* @return 0 if successful,
* -1 if an error occurred while reading,
* -3 if more than COUNT characters are present in the line
* (in this case the first COUNT characters are returned in BUF,
* and the rest are discarded).
*/
static int ltermReturnOutputLine(struct lterms *lts, struct LtermRead *ltr)
{
struct LtermOutput *lto = &(lts->ltermOutput);
int charCount, returnCode, j;
LTERM_LOG(ltermReturnOutputLine,30,
("outputChars=%d, promptChars=%d, CursorChar=%d\n",
lto->outputChars, lto->promptChars, lto->outputCursorChar));
ltr->opcodes = LTERM_LINEDATA_CODE;
charCount = lto->outputChars;
if (charCount <= ltr->max_count) {
returnCode = 0;
} else {
/* Too much data to fit into output buffer; truncate */
charCount = ltr->max_count;
returnCode = -3;
}
/* Copy output characters (plain text; no markup or escape sequences) */
ltr->opcodes |= LTERM_OUTPUT_CODE;
for (j=0; j<charCount; j++) {
ltr->buf[j] = lto->outputLine[j];
ltr->style[j] = lto->outputStyle[j];
}
if ((lto->promptChars > 0) && (lto->promptChars <= charCount)) {
/* Set prompt style */
ltr->opcodes |= LTERM_PROMPT_CODE;
for (j=0; j<lto->promptChars; j++) {
ltr->style[j] = LTERM_PROMPT_STYLE;
}
}
ltr->buf_row = -1;
ltr->buf_col = 0;
ltr->cursor_row = -1;
ltr->cursor_col = lto->outputCursorChar;
ltr->read_count = charCount;
LTERM_LOG(ltermReturnOutputLine,31,("returning OUTPUT LINE data\n"));
LTERM_LOGUNICODE(ltermReturnOutputLine,31,(ltr->buf, ltr->read_count));
return returnCode;
}