/* ***** 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; jdecodedOutput[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; jmodifiedCol[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; jnRows; 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; jnCols; 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; jrawERRBytes; 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; jrawOUTBytes; 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; jdecodedChars+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; }