/* ***** 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; jechoLine[j] = lto->outputLine[j]; /* Append input line */ for (j=0; jinputGlyphColIndex[j]; nCols = lti->inputGlyphColIndex[j+1] - startCol; for (k=startCol; kinputColCharIndex[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; minputLine+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; jdecodedOutput[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; jmodifiedCol[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; jnRows; j++) if (lto->modifiedCol[j] >= 0) { waitTime = 0; break; } if (jnRows) { 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; kread_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; kbuf[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; kbuf[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; jdecodedChars; j++) { if (lto->decodedOutput[j] == 0) { locTerminator = <o->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; jbuf[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; jdecodedChars; 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; jnRows; 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; jbuf[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; jinputChars; 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; jbuf[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; jbuf[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; jbuf[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; jpromptChars; 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; }