Back to index

lightning-sunbird  0.9+nobinonly
ltermOutput.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is lineterm.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Ramalingam Saravanan.
00018  * Portions created by the Initial Developer are Copyright (C) 1999
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 /* ltermOutput.c: LTERM PTY output processing
00038  */
00039 
00040 /* public declarations */
00041 #include "lineterm.h"
00042 
00043 /* private declarations */
00044 #include "ltermPrivate.h"
00045 
00046 
00047 static int ltermAppendOutput(struct lterms *lts, const char *cbuf, int count,
00048                              UNISTYLE style, int interleaveCheck,
00049                              int *interleavedBytes, int rawIncompleteMax,
00050                              int *rawIncompleteBytes, char *rawIncompleteBuf);
00051 static int ltermDecode(const char *rawBuf, int n_total,
00052                        UNICHAR* decodedBuf, int decodeMax,
00053                        int decodeNUL,
00054                        int *rawIncompleteBytes);
00055 static int ltermPromptLocate(struct lterms *lts);
00056 
00057 
00070 int ltermProcessOutput(struct lterms *lts, int *opcodes, int *opvals,
00071                        int *oprow)
00072 {
00073   struct LtermOutput *lto = &(lts->ltermOutput);
00074   UNICHAR uch;
00075   UNISTYLE ustyle;
00076   int charIndex, returnCode, consumedChars, remainingChars, j;
00077   int bellFlag;
00078 
00079   LTERM_LOG(ltermProcessOutput,30,("lto->outputMode=%d, cursorChar=%d, Chars=%d\n",
00080                 lto->outputMode, lto->outputCursorChar, lto->outputChars));
00081 
00082   LTERM_LOG(ltermProcessOutput,32, ("lts->commandNumber=%d\n",
00083                                       lts->commandNumber));
00084 
00085   /* Set default returned opcodes, opvals, oprow */
00086   *opcodes = 0;
00087   *opvals = 0;
00088   *oprow = -1;
00089 
00090   charIndex = 0;
00091   bellFlag = 0;
00092 
00093   while ((*opcodes == 0) && (charIndex < lto->decodedChars)) {
00094     uch = lto->decodedOutput[charIndex];
00095     ustyle = lto->decodedStyle[charIndex] | lto->styleMask;
00096 
00097     consumedChars = 1;
00098 
00099     if (uch == U_ESCAPE) {
00100       /* Process escape sequence */
00101       int savedOutputMode = lto->outputMode;
00102 
00103       LTERM_LOG(ltermProcessOutput,31,("ESCAPE sequence\n"));
00104 
00105       returnCode = ltermProcessEscape(lts, lto->decodedOutput+charIndex,
00106                                       lto->decodedChars-charIndex,
00107                                       lto->decodedStyle+charIndex,
00108                                       &consumedChars, opcodes, opvals, oprow);
00109       if (returnCode < 0)
00110         return -1;
00111 
00112       if (returnCode == 1) {
00113         /* Incomplete escape sequence */
00114         lto->incompleteEscapeSequence = 1;
00115         if (lto->outputMode == LTERM1_SCREEN_MODE)
00116           *opcodes = LTERM_SCREENDATA_CODE;
00117         else
00118           *opcodes = LTERM_LINEDATA_CODE | LTERM_OUTPUT_CODE;
00119       }
00120 
00121       /* Assert that change in output mode is a loop terminating condition */
00122       if (lto->outputMode != savedOutputMode)
00123         assert(*opcodes != 0);
00124 
00125     } else if (lto->outputMode == LTERM1_SCREEN_MODE) {
00126       /* Screen mode processing */
00127 
00128       if ((uch >= (UNICHAR)U_SPACE) && (uch != (UNICHAR)U_DEL)) {
00129         /* Printable non-TAB character */
00130 
00131         LTERM_LOG(ltermProcessOutput,39,("Screen mode, printable char - %c\n",
00132                       (char) uch));
00133 
00134         if (lto->insertMode) {
00135           /* Insert blank character at cursor position */
00136           if (ltermInsDelEraseChar(lts, 1, LTERM_INSERT_ACTION) != 0)
00137             return -1;
00138         }
00139 
00140         /* Mark column as being modified */
00141         if ((lto->modifiedCol[lto->cursorRow] == -1) ||
00142             (lto->modifiedCol[lto->cursorRow] > lto->cursorCol))
00143           lto->modifiedCol[lto->cursorRow] = lto->cursorCol;
00144 
00145         /* Replace character and style info at current cursor location */
00146         j = lto->cursorRow*lts->nCols + lto->cursorCol;
00147 
00148         assert(j < (lts->nRows * lts->nCols));
00149 
00150         lto->screenChar[j] = uch;
00151         lto->screenStyle[j] = ustyle;
00152 
00153         if (lto->cursorCol < lts->nCols-1) {
00154           /* Move cursor right */
00155           lto->cursorCol++;
00156         }
00157 
00158       } else {
00159         /* Control character */
00160 
00161         switch (uch) {
00162         case U_BACKSPACE:                 /* Backspace */
00163           LTERM_LOG(ltermProcessOutput,32,("Screen mode, BACKSPACE\n"));
00164           if (lto->cursorCol > 0)
00165             lto->cursorCol--;
00166           break;
00167 
00168         case U_TAB:                       /* Tab */
00169           LTERM_LOG(ltermProcessOutput,32,("Screen mode, TAB\n"));
00170           lto->cursorCol = ((lto->cursorCol/8)+1)*8;
00171           if (lto->cursorCol > lts->nCols-1)
00172             lto->cursorCol = lts->nCols-1;
00173           break;
00174 
00175         case U_BEL:                       /* Bell */
00176           LTERM_LOG(ltermProcessOutput,32,("************ Screen mode, BELL\n"));
00177           bellFlag = 1;
00178           break;
00179 
00180         case U_CRETURN:                   /* Carriage return */
00181           lto->cursorCol = 0;
00182           break;
00183 
00184         case U_LINEFEED:                  /* Newline */
00185           LTERM_LOG(ltermProcessOutput,32,("************ Screen mode, NEWLINE\n\n"));
00186 
00187           *opcodes =  LTERM_SCREENDATA_CODE;
00188 
00189           if (lto->cursorRow > lto->botScrollRow) {
00190             /* Not bottom scrolling line; simply move cursor down one row */
00191             lto->cursorRow--;
00192 
00193           } else {
00194             /* Delete top scrollable line, scrolling up */
00195             if (ltermInsDelEraseLine(lts, 1, lto->topScrollRow, LTERM_DELETE_ACTION) != 0)
00196               return -1;
00197             *opcodes |= LTERM_DELETE_CODE;
00198             *opvals = 1;
00199             *oprow = lto->topScrollRow;
00200           }
00201           break;
00202 
00203         default:
00204           /* Ignore other control characters (including NULs) */
00205           break;
00206         }
00207       }
00208 
00209     } else {
00210       /* Line mode processing */
00211 
00212       if ( ((uch >= (UNICHAR)U_SPACE) && (uch != (UNICHAR)U_DEL)) ||
00213            (uch == (UNICHAR)U_TAB)) {
00214         /* Printable/TAB character; replace/insert at current cursor location
00215            or append to end of line */
00216 
00217         LTERM_LOG(ltermProcessOutput,39,("Line mode, printable char - %c\n",
00218                       (char) uch));
00219 
00220         if (lto->outputCursorChar == lto->outputChars) {
00221           /* Append single character to end of line */
00222 
00223           if (lto->outputChars+1 > MAXCOLM1) {
00224             /* Output buffer overflow; ignore character */
00225             LTERM_WARNING("ltermProcessOutput: Warning - output line buffer overflow\n");
00226           }
00227 
00228           lto->outputLine[lto->outputChars] = uch;
00229           lto->outputStyle[lto->outputChars] = ustyle;
00230           lto->outputChars++;           /* Insert character in output line */
00231 
00232           lto->outputCursorChar++;      /* Reposition cursor */
00233 
00234         } else {
00235           /* Replace/insert single character in the middle of line */
00236 
00237           if (lto->insertMode) {
00238             /* Insert blank character at cursor position */
00239             if (ltermInsDelEraseChar(lts, 1, LTERM_INSERT_ACTION) != 0)
00240               return -1;
00241           }
00242 
00243           /* Overwrite single character in the middle of line */
00244           lto->outputLine[lto->outputCursorChar] = uch;
00245           lto->outputStyle[lto->outputCursorChar] = ustyle;
00246 
00247           /* Note modifications */
00248           if (lto->outputCursorChar < lto->outputModifiedChar)
00249             lto->outputModifiedChar = lto->outputCursorChar;
00250 
00251           lto->outputCursorChar++;      /* Reposition cursor */
00252         }
00253 
00254       } else {
00255         /* Control character */
00256 
00257         switch (uch) {
00258         case U_BACKSPACE:                /* Backspace */
00259           LTERM_LOG(ltermProcessOutput,32,("Line mode, BACKSPACE\n"));
00260           if (lto->outputCursorChar > 0)
00261             lto->outputCursorChar--;
00262           break;
00263 
00264         case U_CRETURN:                  /* Carriage return */
00265           LTERM_LOG(ltermProcessOutput,32,("Line mode, CRETURN\n"));
00266           lto->outputCursorChar = 0;
00267           break;
00268 
00269         case U_BEL:                      /* Bell */
00270           LTERM_LOG(ltermProcessOutput,32,("************ Line mode, BELL\n"));
00271           bellFlag = 1;
00272           break;
00273 
00274         case U_CTL_L:                    /* Formfeed; clear line and return */
00275           LTERM_LOG(ltermProcessOutput,32,("************ Line mode, FORMFEED\n\n"));
00276 
00277           ltermClearOutputLine(lts);
00278           *opcodes = LTERM_LINEDATA_CODE | LTERM_CLEAR_CODE;
00279           break;
00280 
00281         case U_LINEFEED:                  /* Newline; return complete line */
00282           LTERM_LOG(ltermProcessOutput,32,("************ Line mode, NEWLINE\n\n"));
00283 
00284           *opcodes =  LTERM_LINEDATA_CODE
00285                      | LTERM_OUTPUT_CODE
00286                      | LTERM_NEWLINE_CODE;
00287           break;
00288 
00289         default:
00290           /* Ignore other control characters (including NULs) */
00291           break;
00292         }
00293       }
00294     }
00295 
00296     /* Increment character index */
00297     charIndex += consumedChars;
00298   }
00299 
00300   /* Determine count of unprocessed characters */
00301   remainingChars = lto->decodedChars - charIndex;
00302 
00303   if (remainingChars > 0) {
00304     /* Move unprocessed output to beginning of decode buffer */
00305 
00306     LTERM_LOG(ltermProcessOutput,32,("Moved %d chars to beginning of decodedOutput\n", remainingChars));
00307     for (j=0; j<remainingChars; j++) {
00308       lto->decodedOutput[j] = lto->decodedOutput[j+charIndex];
00309       lto->decodedStyle[j] = lto->decodedStyle[j+charIndex];
00310     }
00311   }
00312 
00313   /* Update remaining decoded character count */
00314   lto->decodedChars = remainingChars;
00315 
00316   if (*opcodes == 0) {
00317     /* All output processed; without any special terminating condition */
00318     if (lto->outputMode == LTERM1_SCREEN_MODE) {
00319       /* Full screen mode */
00320       *opcodes = LTERM_SCREENDATA_CODE;
00321 
00322     } else {
00323       /* Line mode */
00324       *opcodes = LTERM_LINEDATA_CODE | LTERM_OUTPUT_CODE;
00325     }
00326   }
00327 
00328   /* Set bell code */
00329   if (bellFlag)
00330     *opcodes |= LTERM_BELL_CODE;
00331 
00332   if (*opcodes & LTERM_LINEDATA_CODE) {
00333     /* Returning line mode data; check for prompt */
00334 
00335     if ((lts->commandNumber == 0) ||
00336         (lto->outputModifiedChar < lto->promptChars)) {
00337       /* If not command line or if "prompt string" may have been modified,
00338        * search for prompt
00339        */
00340       int promptLen;
00341 
00342       LTERM_LOG(ltermProcessOutput,39,("Prompt? modifiedChar=%d, promptChars=%d\n",
00343                      lto->outputModifiedChar, lto->promptChars));
00344 
00345       /* Reset modification marker */
00346       lto->outputModifiedChar = lto->outputChars;
00347 
00348       /* Check if prompt string is present in output */
00349       promptLen = ltermPromptLocate(lts);
00350 
00351       if (promptLen > 0) {
00352         /* Prompt string found */
00353         lto->promptChars = promptLen;
00354 
00355         if (lts->commandNumber == 0) {
00356           /* Set command number */
00357 
00358           /* New command number */
00359           if (lts->lastCommandNum == 0xFFFF)
00360             lts->lastCommandNum = 0;
00361           lts->lastCommandNum++;
00362 
00363           lts->commandNumber = lts->lastCommandNum;
00364 
00365           LTERM_LOG(ltermProcessOutput,32,
00366                     ("************ Prompt found; commandNumber=%d\n\n",
00367                      lts->commandNumber));
00368         }
00369 
00370       } else {
00371         /* No prompt string */
00372         if (lts->commandNumber != 0) {
00373           /* Unset command number and prompt columns */
00374           UNICHAR temLine[2] = {0, LTERM_WRITE_PLAIN_INPUT};
00375 
00376           lts->commandNumber = 0;
00377           lto->promptChars = 0;
00378 
00379           /* Notify "input thread" by writing null input record */
00380           WRITE(lts->writeBUFFER, temLine, 2*sizeof(UNICHAR));
00381 
00382           LTERM_LOG(ltermProcessOutput,32,
00383                     ("************  No prompt found; not command line\n\n"));
00384         }
00385       }
00386     }
00387   }
00388 
00389   if (lto->outputMode == LTERM1_SCREEN_MODE) {
00390     char modifiedRows[81];
00391     int showRows = (lts->nRows < 80) ? lts->nRows : 80;
00392     for (j=0; j<showRows; j++) {
00393       if (lto->modifiedCol[j] > -1)
00394         modifiedRows[j] = 'M';
00395       else
00396         modifiedRows[j] = '.';
00397     }
00398     modifiedRows[showRows] = '\0';
00399     LTERM_LOG(ltermProcessOutput,38,("modifiedRows=%s\n", modifiedRows));
00400   }
00401 
00402   LTERM_LOG(ltermProcessOutput,31,("returned opcodes=0x%X\n", *opcodes));
00403 
00404   return 0;
00405 }
00406 
00407 
00409 void ltermClearOutputLine(struct lterms *lts)
00410 {
00411   struct LtermOutput *lto = &(lts->ltermOutput);
00412 
00413   LTERM_LOG(ltermClearOutputLine,40,("\n"));
00414 
00415   lto->outputChars = 0;
00416   lto->outputCursorChar = 0;
00417   lto->outputModifiedChar = 0;
00418   lto->promptChars = 0;
00419 
00420   lts->commandNumber = 0;
00421 }
00422 
00423 
00425 int ltermClearOutputScreen(struct lterms *lts)
00426 {
00427   struct LtermOutput *lto = &(lts->ltermOutput);
00428   int j;
00429 
00430   LTERM_LOG(ltermClearOutputScreen,40,("\n"));
00431 
00432   if (lto->screenChar == NULL) {
00433     /* Allocate memory for full screen */
00434     int screenSize = lts->nRows * lts->nCols;
00435 
00436     lto->screenChar = (UNICHAR *) MALLOC(screenSize * sizeof(UNICHAR));
00437 
00438     if (lto->screenChar == NULL) {
00439       LTERM_ERROR("ltermClearOutputScreen: Error - failed to allocate memory for chars\n");
00440       return -1;
00441     }
00442 
00443     assert(lto->screenStyle == NULL);
00444     lto->screenStyle = (UNISTYLE *) MALLOC(screenSize * sizeof(UNISTYLE));
00445 
00446     if (lto->screenStyle == NULL) {
00447       LTERM_ERROR("ltermClearOutputScreen: Error - failed to allocate memory for style\n");
00448       return -1;
00449     }
00450   }  
00451 
00452   /* Position cursor at home */
00453   lto->cursorRow = lts->nRows - 1;
00454   lto->cursorCol = 0;
00455 
00456   /* Blank out entire screen */
00457   if (ltermInsDelEraseLine(lts, lts->nRows, lts->nRows-1, LTERM_ERASE_ACTION) != 0)
00458     return -1;
00459 
00460   /* No rows modified yet */
00461   for (j=0; j<lts->nRows; j++) {
00462     lto->modifiedCol[j] = -1;
00463   }
00464 
00465   return 0;
00466 }
00467 
00468 
00473 int ltermSwitchToStreamMode(struct lterms *lts, int streamOpcodes,
00474                                     const UNICHAR *streamTerminator)
00475 {
00476   struct LtermOutput *lto = &(lts->ltermOutput);
00477   int strLength;
00478 
00479   LTERM_LOG(ltermSwitchToStreamMode,40,("streamOpcodes=0x%x\n",streamOpcodes));
00480 
00481   if (streamTerminator != NULL) {
00482     /* Save terminator string (may be null) */
00483     strLength = ucslen(streamTerminator);
00484     ucsncpy( lto->streamTerminator, streamTerminator, MAXSTREAMTERM);
00485 
00486     LTERM_LOGUNICODE(ltermSwitchToStreamMode,41,(streamTerminator,
00487                                                  (int) strLength));
00488   } else {
00489     /* Null terminator */
00490     strLength = 0;
00491     lto->streamTerminator[0] = U_NUL;
00492   }
00493 
00494   if (strLength > MAXSTREAMTERM-1) {
00495     LTERM_ERROR("ltermSwitchToStreamMode: Error - terminator string too long\n");
00496     return -1;
00497   }
00498 
00499   if (lts->options & LTERM_NONUL_FLAG) {
00500     /* No decoding of NUL characters */
00501     if (strLength == 0) {
00502       LTERM_ERROR("ltermSwitchToStreamMode: Error - null terminator string not allowed\n");
00503       return -1;
00504     }
00505   } else {
00506     /* Decoding NUL characters */
00507     if (strLength > 0) {
00508       LTERM_ERROR("ltermSwitchToStreamMode: Error - terminator string must be NUL\n");
00509       return -1;
00510     }
00511   }
00512 
00513   lto->savedOutputMode = lto->outputMode;
00514   lto->outputMode = LTERM0_STREAM_MODE;
00515   lto->streamOpcodes = streamOpcodes;
00516   return 0;
00517 }
00518 
00519 
00523 int ltermSwitchToScreenMode(struct lterms *lts)
00524 {
00525   struct LtermOutput *lto = &(lts->ltermOutput);
00526 
00527   LTERM_LOG(ltermSwitchToScreenMode,40,("\n"));
00528 
00529   if (lto->outputMode == LTERM2_LINE_MODE) {
00530     /* Switching from line mode to screen mode */
00531 
00532     /* Clear styleMask */
00533     lto->styleMask = 0;
00534 
00535     /* Clear screen */
00536     if (ltermClearOutputScreen(lts) != 0)
00537       return -1;
00538 
00539     /* Reset returned cursor location */
00540     lto->returnedCursorRow = -1;
00541     lto->returnedCursorCol = -1;
00542 
00543     /* Scrolling region */
00544     lto->topScrollRow = lts->nRows-1;
00545     lto->botScrollRow = 0;
00546 
00547     /* Disable input echo */
00548     lts->restoreInputEcho = !lts->disabledInputEcho;
00549     lts->disabledInputEcho = 1;
00550 
00551     /* Switch to raw input mode */
00552     ltermSwitchToRawMode(lts);
00553 
00554   }
00555 
00556   lto->outputMode = LTERM1_SCREEN_MODE;
00557   return 0;
00558 }
00559 
00560 
00564 int ltermSwitchToLineMode(struct lterms *lts)
00565 {
00566   struct LtermOutput *lto = &(lts->ltermOutput);
00567   int j;
00568 
00569   LTERM_LOG(ltermSwitchToLineMode,40,("\n"));
00570 
00571   if (lto->outputMode == LTERM1_SCREEN_MODE) {
00572     /* Switching from screen mode to line mode */
00573 
00574     /* Switch to line input mode */
00575     ltermClearInputLine(lts);
00576 
00577     if (lts->restoreInputEcho) {
00578       /* Re-enable input echo */
00579       lts->disabledInputEcho = 0;
00580       lts->restoreInputEcho = 0;
00581     }
00582 
00583     /* Clear styleMask */
00584     lto->styleMask = 0;
00585 
00586     /* Clear output line */
00587     ltermClearOutputLine(lts);
00588 
00589     /* Copy bottom line to line output buffer ??? */
00590     lto->outputChars = lts->nCols;
00591 
00592     assert(lts->nCols < MAXCOL);
00593 
00594     for (j=0; j<lts->nCols; j++) {
00595       lto->outputLine[j] =  lto->screenChar[j];
00596       lto->outputStyle[j] = lto->screenStyle[j];
00597     }
00598 
00599   }
00600 
00601   lto->outputMode = LTERM2_LINE_MODE;
00602   return 0;
00603 }
00604 
00605 
00616 int ltermReceiveData(struct lterms *lts, int readERR)
00617 {
00618   struct LtermOutput *lto = &(lts->ltermOutput);
00619   char temERRBuf[MAXCOL], temOUTBuf[MAXCOL];
00620   int readERRMax, readOUTMax;
00621   int nReadERR, nTotalERR, nReadOUT, nTotalOUT;
00622   int interleavedBytes, n_decoded, n_decoded_tot, j;
00623 
00624   LTERM_LOG(ltermReceiveData,30,("\n"));
00625 
00626   nTotalERR = 0;
00627   if (readERR && (lto->pollFD[POLL_STDERR].POLL_REVENTS != 0)) {
00628     /* Read data from STDERR */
00629 
00630     /* Read maximum number of bytes that will all fit into decodedOutput
00631        when decoded, assuming number of decoded characters will not exceed
00632        the number of encoded bytes */
00633 
00634     readERRMax = MAXCOLM1 - lto->decodedChars - lto->rawERRBytes;
00635 
00636     /* Reduce by half to leave some space for STDOUT data */
00637     readERRMax = readERRMax / 2;
00638 
00639     if (readERRMax <= 0) {
00640       /* Decoded buffer overflow */
00641       LTERM_WARNING("ltermReceiveData: Warning - decoded buffer overflow\n");
00642 
00643       /* Non-fatal error recovery; return without reading */
00644       return 0;
00645     }
00646 
00647     /* Copy any incomplete raw output to beginning of buffer */
00648     for (j=0; j<lto->rawERRBytes; j++)
00649       temERRBuf[j] = lto->rawERRBuf[j];
00650 
00651     /* Read STDERRdata (blocking mode) */
00652     nReadERR = READ(lto->pollFD[POLL_STDERR].fd,
00653                     temERRBuf + lto->rawERRBytes, (SIZE_T) readERRMax);
00654 
00655     if (nReadERR < 0) {
00656 #if defined(DEBUG_LTERM) && !defined(USE_NSPR_IO)
00657       int errcode = errno;
00658       /*    perror("ltermReceiveData"); */
00659 #else
00660       int errcode = 0;
00661 #endif
00662       LTERM_ERROR( "ltermReceiveData: Error %d in reading from process STDERR\n",
00663                    errcode);
00664       return -1;
00665     }
00666 
00667     if (nReadERR == 0) {
00668       /* End of file => pseudo-TTY has been closed */
00669       LTERM_LOG(ltermReceiveData,31,("pseudo-TTY has been closed\n"));
00670 
00671       /* Suspend LTERM */
00672       lts->suspended = 1;
00673 
00674       return -2;
00675     }
00676 
00677 
00678     LTERM_LOG(ltermReceiveData,32,("Read %d raw bytes from STDERR\n", nReadERR));
00679     nTotalERR = nReadERR + lto->rawERRBytes;
00680   }
00681 
00682   nTotalOUT = 0;
00683   if (lto->pollFD[POLL_STDOUT].POLL_REVENTS != 0) {
00684     /* Read data from STDOUT */
00685 
00686     /* Read maximum number of bytes that will all fit into decodedOutput
00687        when decoded, assuming number of decoded characters will not exceed
00688        the number of encoded bytes */
00689     readOUTMax = MAXCOLM1 - lto->decodedChars - lto->rawOUTBytes - nTotalERR;
00690 
00691     if (readOUTMax <= 0) {
00692       /* Decoded buffer overflow */
00693       LTERM_WARNING("ltermReceiveData: Warning - decoded buffer overflow\n");
00694 
00695       /* Non-fatal error recovery; return without reading */
00696       return 0;
00697     }
00698 
00699     /* Copy any incomplete raw output to beginning of buffer */
00700     for (j=0; j<lto->rawOUTBytes; j++)
00701       temOUTBuf[j] = lto->rawOUTBuf[j];
00702 
00703     /* Read STDOUTdata (blocking mode) */
00704     nReadOUT = READ(lto->pollFD[POLL_STDOUT].fd,
00705                     temOUTBuf + lto->rawOUTBytes, (SIZE_T) readOUTMax);
00706 
00707     if (nReadOUT < 0) {
00708 #if defined(DEBUG_LTERM) && !defined(USE_NSPR_IO)
00709       int errcode = errno;
00710       /*    perror("ltermReceiveData"); */
00711 #else
00712       int errcode = 0;
00713 #endif
00714       LTERM_ERROR( "ltermReceiveData: Error %d in reading from process STDOUT\n",
00715                    errcode);
00716       return -1;
00717     }
00718 
00719     if (nReadOUT == 0) {
00720       /* End of file => pseudo-TTY has been closed */
00721       LTERM_LOG(ltermReceiveData,31,("pseudo-TTY has been closed\n"));
00722 
00723       /* Suspend LTERM */
00724       lts->suspended = 1;
00725 
00726       return -2;
00727     }
00728 
00729     LTERM_LOG(ltermReceiveData,32,("Read %d raw bytes from STDOUT\n", nReadOUT));
00730 
00731     nTotalOUT = nReadOUT + lto->rawOUTBytes;
00732   }
00733 
00734   n_decoded_tot = 0;
00735 
00736   if (lts->readERRfirst) {
00737     /* Decode STDERR data first */
00738     interleavedBytes = 0;
00739     n_decoded = ltermAppendOutput(lts, temERRBuf, nTotalERR,
00740                                   LTERM_STDERR_STYLE,
00741                                   lts->interleave, &interleavedBytes,
00742                                   MAXRAWINCOMPLETE,
00743                                   &(lto->rawERRBytes), lto->rawERRBuf);
00744     if (n_decoded < 0)
00745       return -1;
00746     n_decoded_tot += n_decoded;
00747 
00748     /* Decode STDOUT data next */
00749     n_decoded = ltermAppendOutput(lts, temOUTBuf, nTotalOUT,
00750                                   LTERM_STDOUT_STYLE,
00751                                   0, NULL,
00752                                   MAXRAWINCOMPLETE,
00753                                   &(lto->rawOUTBytes), lto->rawOUTBuf);
00754     if (n_decoded < 0)
00755       return -1;
00756     n_decoded_tot += n_decoded;
00757 
00758     if (interleavedBytes > 0) {
00759       /* Decode remaining STDERR data */
00760       n_decoded = ltermAppendOutput(lts, temERRBuf+interleavedBytes,
00761                                     nTotalERR-interleavedBytes,
00762                                     LTERM_STDERR_STYLE,
00763                                     0, NULL,
00764                                     MAXRAWINCOMPLETE,
00765                                     &(lto->rawERRBytes), lto->rawERRBuf);
00766       if (n_decoded < 0)
00767         return -1;
00768       n_decoded_tot += n_decoded;
00769     }
00770 
00771   } else {
00772     /* Decode STDOUT data first */
00773     interleavedBytes = 0;
00774     n_decoded = ltermAppendOutput(lts, temOUTBuf, nTotalOUT,
00775                                   LTERM_STDOUT_STYLE,
00776                                   lts->interleave, &interleavedBytes,
00777                                   MAXRAWINCOMPLETE,
00778                                   &(lto->rawOUTBytes), lto->rawOUTBuf);
00779     if (n_decoded < 0)
00780       return -1;
00781     n_decoded_tot += n_decoded;
00782 
00783     /* Decode STDERR data next */
00784     n_decoded = ltermAppendOutput(lts, temERRBuf, nTotalERR,
00785                                   LTERM_STDERR_STYLE,
00786                                   0, NULL,
00787                                   MAXRAWINCOMPLETE,
00788                                   &(lto->rawERRBytes), lto->rawERRBuf);
00789     if (n_decoded < 0)
00790       return -1;
00791     n_decoded_tot += n_decoded;
00792 
00793     if (interleavedBytes > 0) {
00794       /* Decode remaining STDOUT data */
00795       n_decoded = ltermAppendOutput(lts, temOUTBuf+interleavedBytes,
00796                                     nTotalOUT-interleavedBytes,
00797                                     LTERM_STDOUT_STYLE,
00798                                     0, NULL,
00799                                     MAXRAWINCOMPLETE,
00800                                     &(lto->rawOUTBytes), lto->rawOUTBuf);
00801       if (n_decoded < 0)
00802         return -1;
00803       n_decoded_tot += n_decoded;
00804     }
00805   }
00806 
00807   if (n_decoded_tot > 0) {
00808     /* New characters have been decoded; no longer incomplete escape seq. */
00809     lto->incompleteEscapeSequence = 0;
00810   }
00811 
00812   return n_decoded_tot;
00813 }
00814 
00815 
00829 static int ltermAppendOutput(struct lterms *lts, const char *cbuf, int count,
00830                              UNISTYLE style, int interleaveCheck,
00831                              int *interleavedBytes, int rawIncompleteMax,
00832                              int *rawIncompleteBytes, char *rawIncompleteBuf)
00833 {
00834   struct LtermOutput *lto = &(lts->ltermOutput);
00835   int decodeMax, n_decoded, decodeNUL, j;
00836 
00837   LTERM_LOG(ltermAppendOutput,30,("count=%d, style=0x%X, iCheck=%d, rawIncMax=%d\n",
00838             count, style, interleaveCheck, rawIncompleteMax));
00839 
00840   if (interleaveCheck && (count > 0) && (cbuf[0] == '\n')) {
00841     /* Raw buffer starts with NEWLINE character; interleave */
00842 
00843     assert(lto->decodedChars < MAXCOLM1);
00844 
00845     /* "Decode" just the NEWLINE character */
00846     lto->decodedOutput[lto->decodedChars] = U_LINEFEED;
00847     lto->decodedStyle[lto->decodedChars] = LTERM_STDOUT_STYLE;
00848 
00849     lto->decodedChars++;
00850 
00851     *interleavedBytes = 1;
00852 
00853     LTERM_LOG(ltermAppendOutput,32,("INTERLEAVED %d bytes\n",
00854                                     *interleavedBytes));
00855 
00856     return 1;
00857   }
00858 
00859   if (interleavedBytes != NULL)
00860     *interleavedBytes = 0;
00861 
00862   if (count == 0)
00863     return 0;
00864 
00865   /* Decode all complete encoded character sequences */
00866   decodeMax = MAXCOLM1 - lto->decodedChars;
00867   decodeNUL = (lts->options & LTERM_NONUL_FLAG) == 0;
00868   n_decoded = ltermDecode(cbuf, count,
00869                           lto->decodedOutput+lto->decodedChars,
00870                           decodeMax, decodeNUL,
00871                           rawIncompleteBytes);
00872 
00873   if (n_decoded < 0)
00874     return -1;
00875 
00876   /* Save any incomplete raw byte encodings */
00877   if (*rawIncompleteBytes > rawIncompleteMax) {
00878     LTERM_ERROR("ltermAppendOutput: Error - too many incomplete raw characters\n");
00879     return -1;
00880   }
00881 
00882   for (j=0; j<*rawIncompleteBytes; j++)
00883     rawIncompleteBuf[j] = cbuf[j+count-*rawIncompleteBytes];
00884 
00885   /* Set decoded character styles */
00886   for (j=lto->decodedChars; j<lto->decodedChars+n_decoded; j++)
00887     lto->decodedStyle[j] = style;
00888 
00889   /* Increment decoded character count */
00890   lto->decodedChars += n_decoded;
00891 
00892   LTERM_LOG(ltermAppendOutput,32,("Appended %d bytes\n", n_decoded));
00893 
00894   return n_decoded;
00895 }
00896 
00897 
00907 static int ltermDecode(const char *rawBuf, int n_total,
00908                        UNICHAR* decodedBuf, int decodeMax,
00909                        int decodeNUL,
00910                        int *rawIncompleteBytes)
00911 {
00912   int n_decoded, result;
00913 
00914   LTERM_LOG(ltermDecode,40,("\n"));
00915 
00916   if (decodeMax < n_total) {
00917     LTERM_ERROR("ltermDecode: Error - decode buffer overflow\n");
00918     return -1;
00919   }
00920 
00921   result = utf8toucs(rawBuf, n_total, decodedBuf, decodeMax,
00922                      !decodeNUL, rawIncompleteBytes, &n_decoded);
00923 
00924   if (result != 0) {
00925     LTERM_WARNING("ltermDecode: Warning - Invalid UTF8 data encountered\n");
00926   }
00927 
00928   LTERM_LOG(ltermDecode,41,("result=%d, incomplete=%d, n_decoded=%d\n",
00929                             result, *rawIncompleteBytes, n_decoded));
00930   LTERM_LOGUNICODE(ltermDecode,42,(decodedBuf, n_decoded));
00931 
00932   return n_decoded;
00933 }
00934 
00935 
00941 static int ltermPromptLocate(struct lterms *lts)
00942 {
00943   struct LtermOutput *lto = &(lts->ltermOutput);
00944   int prefixCount, promptLen;
00945 
00946   LTERM_LOG(ltermPromptLocate,49,("lto->outputChars=%d\n",
00947                 lto->outputChars));
00948 
00949   /* Assert that there is at least one free character in the buffer */
00950   assert(lto->outputChars < MAXCOL);
00951 
00952   if (lto->outputChars == 0)
00953     return 0;
00954 
00955   /* Insert null character at the end of the output buffer */
00956   lto->outputLine[lto->outputChars] = U_NUL;
00957 
00958   /* Determine length of initial non-delimiter prefix */
00959   prefixCount = ucscspn(lto->outputLine, lts->promptRegexp);
00960 
00961   if (prefixCount+1 >= lto->outputChars) {
00962     promptLen = 0;
00963 
00964   } else {
00965     /* Skip any whitespace following the delimiter */
00966     const UNICHAR spaceStr[3] = {U_SPACE, U_TAB, U_NUL};
00967     int spaceCount = ucsspn(lto->outputLine+prefixCount+1, spaceStr);
00968 
00969     promptLen = prefixCount + 1 + spaceCount;
00970 
00971     LTERM_LOGUNICODE(ltermPromptLocate,41,(lto->outputLine, promptLen));
00972   }
00973 
00974   return promptLen;
00975 }