Back to index

lightning-sunbird  0.9+nobinonly
ltermIO.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 /* ltermIO.c: LTERM PTY input/output data processing
00038  */
00039 
00040 /* public declarations */
00041 #include "lineterm.h"
00042 
00043 /* private declarations */
00044 #include "ltermPrivate.h"
00045 
00046 
00047 static int ltermWrite(struct lterms *lts, int *opcodes);
00048 
00049 static int ltermReturnStreamData(struct lterms *lts, struct LtermRead *ltr);
00050 static int ltermReturnScreenData(struct lterms *lts, struct LtermRead *ltr,
00051                                  int opcodes, int opvals, int oprow);
00052 static int ltermReturnInputLine(struct lterms *lts, struct LtermRead *ltr,
00053                                 int completionRequested);
00054 static int ltermReturnOutputLine(struct lterms *lts, struct LtermRead *ltr);
00055 
00056 
00069 int ltermSendLine(struct lterms *lts, UNICHAR uch,
00070                   int echoControl, int completionCode)
00071 {
00072   struct LtermInput *lti = &(lts->ltermInput);
00073   struct LtermOutput *lto = &(lts->ltermOutput);
00074   UNICHAR insch;
00075   int glyphCount, prefixChars, charCount, sendCount;
00076   int j, k, startCol, nCols, charIndex;
00077 
00078   LTERM_LOG(ltermSendLine,40,
00079        ("uch=0x%x, echoControl=%d, completionCode=%d, completionRequest=%d\n",
00080          uch, echoControl, completionCode, lts->completionRequest));
00081 
00082   if ((lts->completionRequest == LTERM_HISTORY_COMPLETION) &&
00083       (lts->completionChars > 0)) {
00084     /* Delete prior completion inserts */
00085     if (ltermDeleteGlyphs(lti, lts->completionChars) != 0)
00086       return -1;
00087   }
00088 
00089   if (completionCode != LTERM_NO_COMPLETION) {
00090     /* Send only glyphs to left of cursor */
00091     glyphCount = lti->inputCursorGlyph;
00092 
00093   } else {
00094     /* Send all glyphs in input line */
00095     glyphCount = lti->inputGlyphs;
00096   }
00097 
00098   if (lto->promptChars > 0) {
00099     /* Insert output prompt into echo buffer */
00100     prefixChars = lto->promptChars;
00101   } else {
00102     /* Insert entire output line into echo buffer */
00103     prefixChars = lto->outputChars;
00104   }
00105 
00106   charCount = prefixChars;
00107 
00108   LTERM_LOG(ltermSendLine,42,
00109             ("lto->promptChars=%d, outputChars=%d, glyphCount=%d\n",
00110              lto->promptChars, lto->outputChars, glyphCount));
00111 
00112   if (charCount >= MAXCOLM1) {
00113     /* Buffer overflow; error return */
00114     LTERM_ERROR("ltermSendLine: Error - character buffer overflow\n");
00115     return -1;
00116   }
00117 
00118   for (j=0; j<charCount; j++)
00119     lts->echoLine[j] = lto->outputLine[j];
00120 
00121   /* Append input line */
00122   for (j=0; j<glyphCount; j++) {
00123     startCol = lti->inputGlyphColIndex[j];
00124     nCols = lti->inputGlyphColIndex[j+1] - startCol;
00125 
00126     for (k=startCol; k<startCol+nCols; k++) {
00127       charIndex = lti->inputColCharIndex[k];
00128 
00129       insch = lti->inputLine[charIndex];
00130 
00131 #if 0
00132       /* COMMENTED OUT: Plain text not escaped; use later in HTML insert */
00133       if (insch == U_AMPERSAND) {
00134         /* Escaped character */
00135 
00136         /* Assert that there is one free character position in buffer */
00137         assert(lti->inputChars < MAXCOL);
00138 
00139         /* Insert null character at the end of the input buffer */
00140         lti->inputLine[lti->inputChars] = U_NUL;
00141 
00142         /* Match escape sequence against known escape sequences */
00143         for (m=0; m<LTERM_XML_ESCAPES; m++) {
00144           if (ucsncmp(lti->inputLine+charIndex,
00145                       ltermGlobal.escapeSeq[m],
00146                       (unsigned int) ltermGlobal.escapeLen[m]) == 0)
00147             break;
00148         }
00149 
00150         if (m < LTERM_XML_ESCAPES) {
00151           /* Subsitute single character for escape sequence */
00152           insch = (UNICHAR) ltermGlobal.escapeChars[m];
00153         } else {
00154           LTERM_WARNING("ltermSendLine: Warning - unknown XML character escape sequence\n");
00155         }
00156       }
00157 #endif /* 0 */
00158 
00159       if (charCount >= MAXCOLM1) {
00160         /* Buffer overflow; error return */
00161         LTERM_ERROR("ltermSendLine: Error - character buffer overflow\n");
00162         return -1;
00163 
00164       } else {
00165         /* Insert character into echo line buffer */
00166         lts->echoLine[charCount++] = insch;
00167       }
00168     }
00169   }
00170 
00171 
00172   /* Transmit data to process */
00173   sendCount = charCount - prefixChars;
00174 
00175   if (lts->completionRequest != LTERM_NO_COMPLETION) {
00176     /* Completion already requested */
00177 
00178     /* Send only control character */
00179     if (uch != U_NUL) {
00180       if (ltermSendData(lts, &uch, 1) != 0)
00181         return -1;
00182     }
00183 
00184   } else {
00185     if (uch != U_NUL) {
00186       /* Send line+control character to child process */
00187 
00188       /* Insert control character at the end of the buffer (temporarily) */
00189       lts->echoLine[charCount] = uch;
00190 
00191       sendCount++;
00192     }
00193 
00194     if (ltermSendData(lts, lts->echoLine+prefixChars, sendCount) != 0)
00195       return -1;
00196   }
00197 
00198   if (completionCode != LTERM_NO_COMPLETION) {
00199     /* Request completion */
00200     lts->completionRequest = completionCode;
00201 
00202     /* No completion text inserted yet */
00203     lts->completionChars = 0;
00204 
00205   } else {
00206     /* Set input line break flag;
00207      * cleared only by output line break processing
00208      */
00209     lts->inputLineBreak = 1;
00210   }
00211 
00212   if (echoControl && (charCount+2 < MAXCOLM1)) {
00213     /* Echo control character in printable form */
00214     lts->echoLine[charCount++] = U_CARET;
00215     lts->echoLine[charCount++] = U_ATSIGN + uch;
00216   }
00217 
00218   /* Set count of echo characters (excluding control character) */
00219   lts->echoChars = charCount;
00220 
00221   LTERM_LOG(ltermSendLine,41,("glyphCount=%d, sendCount=%d\n",
00222                                  glyphCount, sendCount));
00223   LTERM_LOGUNICODE(ltermSendLine,41,(lts->echoLine, charCount));
00224 
00225   return 0;
00226 }
00227 
00228 
00243 int ltermWrite(struct lterms *lts, int *opcodes)
00244 {
00245   struct LtermInput *lti = &(lts->ltermInput);
00246   struct LtermOutput *lto = &(lts->ltermOutput);
00247   char *byteBuf = (char *) lti->inputBuf;
00248   int returnCode, availableChars, processedChars, headerChars, totalChars, j;
00249 
00250   LTERM_LOG(ltermWrite,20, ("inputLineBreak=%d, inputBufRecord=%d\n",
00251                                lts->inputLineBreak, lts->inputBufRecord));
00252 
00253   *opcodes = 0;
00254 
00255   if (!lts->inputBufRecord) {
00256     /* Complete record not available in buffer; read data from input pipe */
00257     int readMax, n_read;
00258 
00259     readMax = (PIPEHEADER+MAXCOL)*sizeof(UNICHAR) - lti->inputBufBytes;
00260 
00261     assert(readMax > 0);
00262 
00263     n_read = READ(lts->readBUFFER, byteBuf + lti->inputBufBytes,
00264                   (SIZE_T) readMax);
00265 
00266     if (n_read < 0) {
00267       LTERM_WARNING("ltermWrite: Warning - error %d in reading from input buffer pipe\n", n_read);
00268       return -1;
00269     }
00270 
00271     LTERM_LOG(ltermWrite,22,
00272               ("read %d bytes from input buffer pipe\n", n_read));
00273 
00274     if (n_read == 0) {
00275       /* End of file */
00276       LTERM_LOG(ltermWrite,21,("input buffer pipe has been closed\n"));
00277 
00278       /* Suspend LTERM */
00279       lts->suspended = 1;
00280 
00281       return -2;
00282     }
00283 
00284     /* Update count of bytes read so far */
00285     lti->inputBufBytes += n_read;
00286 
00287     LTERM_LOGUNICODE(ltermWrite,12,(lti->inputBuf,
00288                                     lti->inputBufBytes/sizeof(UNICHAR)));
00289 
00290     /* Check if one or more complete records is now available in buffer */
00291     lts->inputBufRecord = (lti->inputBufBytes >= (int)sizeof(UNICHAR)) &&
00292   (lti->inputBufBytes >= (PIPEHEADER+lti->inputBuf[0])*((int)sizeof(UNICHAR)));
00293 
00294     if (!lts->inputBufRecord) {
00295       /* Incomplete input record; return */
00296       return 0;
00297     }
00298   }
00299 
00300   /* availableChars may be zero; such zero length input data records
00301    * are used to notify the "input thread" about output processing events,
00302    * such as the detection of a command line prompt (which would
00303    * enable the command completion input mode)
00304    */
00305   availableChars = lti->inputBuf[0];
00306   processedChars = 0;
00307 
00308   LTERM_LOG(ltermWrite,22, ("inputBuf[0]=%d, inputBuf[1]=%d\n",
00309                               lti->inputBuf[0], lti->inputBuf[1]));
00310 
00311   if (lti->inputBuf[1] == LTERM_WRITE_CLOSE_MESSAGE) {
00312     /* LTERM being closed */
00313     return -2;
00314   }
00315 
00316   if (lti->inputBuf[1] == LTERM_WRITE_PLAIN_INPUT) {
00317     /* Process plain text input record */
00318     int doNotCancelCompletion = 0;
00319 
00320     if (lti->inputMode >= LTERM3_COMPLETION_MODE) {
00321 
00322       if (availableChars == 1) {
00323         /* Single control character completion requests */
00324 
00325         doNotCancelCompletion =
00326          ( (lts->completionRequest == LTERM_TAB_COMPLETION) &&
00327            (lti->inputBuf[2] == U_TAB) ) ||
00328          ( (lts->completionRequest == LTERM_HISTORY_COMPLETION) &&
00329             ((lti->inputBuf[2] == U_CTL_P) || (lti->inputBuf[2] == U_CTL_N)) );
00330 
00331       } else if (availableChars == 3) {
00332         /* Three character escape sequences for up/down arrow keys */
00333         /* NOTE: Input CSI escape sequence; may not be portable */
00334 
00335         doNotCancelCompletion =
00336        ( (lts->completionRequest == LTERM_HISTORY_COMPLETION) &&
00337          (lti->inputBuf[2] == U_ESCAPE) && (lti->inputBuf[3] == U_LBRACKET) &&
00338          ((lti->inputBuf[4] == U_A_CHAR) || (lti->inputBuf[4] == U_B_CHAR)) );
00339       }
00340     }
00341 
00342     if (!doNotCancelCompletion &&
00343         (lts->completionRequest != LTERM_NO_COMPLETION)) {
00344       /* Cancel any completion request */
00345 
00346       if (ltermCancelCompletion(lts) != 0)
00347         return -1;
00348     }
00349 
00350     LTERM_LOGUNICODE(ltermWrite,12,(lti->inputBuf+PIPEHEADER, availableChars));
00351 
00352     returnCode = ltermPlainTextInput(lts, lti->inputBuf+PIPEHEADER,
00353                                      availableChars, opcodes);
00354     if (returnCode < 0)
00355       return returnCode;
00356 
00357     processedChars = availableChars;
00358 
00359   } else if (lti->inputBuf[1] == LTERM_WRITE_XML_INPUT) {
00360     /* Insert complete XML element into input line, after checks */
00361 
00362     if (lts->completionRequest != LTERM_NO_COMPLETION) {
00363       /* Cancel command completion request */
00364       if (ltermCancelCompletion(lts) != 0)
00365         return -1;
00366     }
00367 
00368     LTERM_WARNING("ltermWrite: Warning - LTERM_WRITE_XML_INPUT not yet implemented\n");
00369 
00370     processedChars = availableChars;
00371 
00372   } else if (lti->inputBuf[1] == LTERM_WRITE_PLAIN_OUTPUT) {
00373     /* Insert plain text in decoded character buffer */
00374     int decodeMax = MAXCOLM1 - lto->decodedChars;
00375 
00376     processedChars = availableChars;
00377 
00378     if (processedChars > decodeMax)
00379       processedChars = decodeMax;
00380 
00381     if (processedChars > 0) {
00382       /* Copy characters to decoded character buffer */
00383       int offset;
00384 
00385       if ((lto->decodedChars > 0) &&
00386           lto->incompleteEscapeSequence &&
00387           (lto->decodedStyle[0] != LTERM_ALTOUT_STYLE)) {
00388         /* Move incomplete escape sequence to end of decoded buffer */
00389         for (j=lto->decodedChars-1; j>=0; j--) {
00390           lto->decodedOutput[j+processedChars] = lto->decodedOutput[j];
00391           lto->decodedStyle[j+processedChars] = lto->decodedStyle[j];
00392         }
00393 
00394         /* Incomplete escape sequence no longer at beginning of buffer */
00395         lto->incompleteEscapeSequence = 0;
00396 
00397         /* Insert new characters at beginning of decoded buffer */
00398         offset = 0;
00399 
00400       } else {
00401         /* Append new characters at the end of the decoded buffer */
00402         offset = lto->decodedChars;
00403       }
00404 
00405       /* Insert new characters in decoded buffer, with appropriate offset */
00406       for (j=0; j<processedChars; j++) {
00407         lto->decodedOutput[j+offset] = lti->inputBuf[PIPEHEADER+j];
00408         lto->decodedStyle[j+offset] = LTERM_ALTOUT_STYLE;
00409       }
00410 
00411       /* Increment decoded character count */
00412       lto->decodedChars += processedChars;
00413     }
00414 
00415     returnCode = 0;
00416   }
00417 
00418   if (processedChars == availableChars) {
00419     /* Delete current record completely from buffer */
00420     processedChars += PIPEHEADER;
00421     headerChars = 0;
00422 
00423   } else {
00424     /* Delete processed characters from current record */
00425     headerChars = PIPEHEADER;
00426 
00427     /* Decrement character count in record header */
00428     lti->inputBuf[0] -= processedChars;
00429   }
00430 
00431   LTERM_LOG(ltermWrite,23, ("processedChars=%d, headerChars=%d\n",
00432                                processedChars, headerChars));
00433 
00434   /* Shift remaining data in buffer */
00435   totalChars = lti->inputBufBytes/sizeof(UNICHAR);
00436 
00437   for (j=headerChars; j<(totalChars-processedChars); j++)
00438     lti->inputBuf[j] = lti->inputBuf[j+processedChars];
00439 
00440   /* Decrement buffer count */
00441   lti->inputBufBytes -= processedChars*sizeof(UNICHAR);
00442 
00443   /* Check if one or more complete records is now left in buffer */
00444   lts->inputBufRecord = (lti->inputBufBytes >= (int)sizeof(UNICHAR)) &&
00445   (lti->inputBufBytes >= (PIPEHEADER+lti->inputBuf[0])*((int)sizeof(UNICHAR)));
00446 
00447   LTERM_LOG(ltermWrite,21,
00448             ("return opcodes=0x%x, inputBufBytes=%d, inputBufRecord=%d\n",
00449              *opcodes, lti->inputBufBytes, lts->inputBufRecord));
00450 
00451   return 0;
00452 }
00453 
00454 
00477 int ltermRead(struct lterms *lts, struct LtermRead *ltr, int timeout)
00478 {
00479   struct LtermInput *lti = &(lts->ltermInput);
00480   struct LtermOutput *lto = &(lts->ltermOutput);
00481   int waitTime, nfds_all, pollCode, rePoll;
00482   int inputAvailable, newOutputAvailable;
00483   int outOpcodes, outOpvals, outOprow, returnCode;
00484   int j;
00485 
00486   LTERM_LOG(ltermRead,20,("start outputMode=%d\n", lto->outputMode));
00487 
00488   if (lto->outputMode == LTERM1_SCREEN_MODE) {
00489     char modifiedRows[81];
00490     int showRows = (lts->nRows < 80) ? lts->nRows : 80;
00491     for (j=0; j<showRows; j++) {
00492       if (lto->modifiedCol[j] > -1)
00493         modifiedRows[j] = 'M';
00494       else
00495         modifiedRows[j] = '.';
00496     }
00497     modifiedRows[showRows] = '\0';
00498     LTERM_LOG(ltermRead,28,("modifiedRows=%s\n", modifiedRows));
00499   }
00500 
00501   waitTime = timeout;
00502 
00503   if (lts->inputBufRecord) {
00504     /* Complete input record available in buffer */
00505 
00506     LTERM_LOG(ltermRead,21,("inputBufRecord=%d\n", lts->inputBufRecord));
00507     waitTime = 0;
00508 
00509   } else if ((lto->decodedChars > 0) && !lto->incompleteEscapeSequence) {
00510     /* Do not wait if there are decoded characters to be processed that are
00511        not part of an incomplete ESCAPE sequence */
00512 
00513     LTERM_LOG(ltermRead,21,("decodedChars=%d,incompleteEscapeSequence=%d\n",
00514                   lto->decodedChars, lto->incompleteEscapeSequence));
00515 
00516     waitTime = 0;
00517 
00518   } else if (lto->outputMode == LTERM1_SCREEN_MODE) {
00519     /* Screen mode: do not wait if there are any modified rows */
00520     for (j=0; j<lts->nRows; j++)
00521       if (lto->modifiedCol[j] >= 0) {
00522         waitTime = 0;
00523         break;
00524       }
00525 
00526     if (j<lts->nRows) {
00527       LTERM_LOG(ltermRead,21,("j=%d, modifiedCol[j] = %d\n",
00528                     j, lto->modifiedCol[j]));
00529     }
00530   }
00531 
00532   /* If stream mode, do not poll STDERR output */
00533   if (lto->outputMode == LTERM0_STREAM_MODE)
00534     nfds_all = POLL_COUNT-1;
00535   else
00536     nfds_all = lto->nfds;
00537 
00538   do {
00539     /* Do not re-poll unless something special occurs */
00540     rePoll = 0;
00541 
00542     /* Default return values in LtermRead structure */
00543     ltr->read_count = 0;
00544     ltr->opcodes = 0;
00545     ltr->opvals = 0;
00546     ltr->buf_row = -1;
00547     ltr->buf_col = -1;
00548     ltr->cursor_row = -1;
00549     ltr->cursor_col = -1;
00550 
00551     LTERM_LOG(ltermRead,21,("polling nfds = %d, waitTime = %d\n",
00552                 lto->nfds, waitTime));
00553 
00554     /* Poll all files for data to read */
00555     pollCode = POLL(lto->pollFD, (SIZE_T) lto->nfds, waitTime);
00556 
00557     if (pollCode == -1) {
00558 #if defined(DEBUG_LTERM) && !defined(USE_NSPR_IO)
00559       int errcode = errno;
00560       perror("ltermRead");
00561 #else
00562       int errcode = 0;
00563 #endif
00564       LTERM_ERROR( "ltermRead: Error return from poll, code=%d\n", errcode);
00565       return -1;
00566     }
00567 
00568     /* Do not wait for polling the second time around */
00569     waitTime = 0;
00570 
00571     /* Check if input data is available */
00572     inputAvailable = lts->inputBufRecord ||
00573                      ( (pollCode > 0) &&
00574                        (lto->pollFD[POLL_INPUTBUF].POLL_REVENTS != 0) );
00575 
00576     /* Check if STDOUT/STDERR data is available */
00577     newOutputAvailable = (pollCode > 0) &&
00578                           ( (lto->pollFD[POLL_STDOUT].POLL_REVENTS != 0) ||
00579                             (lto->pollFD[POLL_STDERR].POLL_REVENTS != 0) );
00580 
00581     LTERM_LOG(ltermRead,21,
00582             ("inputAvailable=%d, newOutputAvailable=%d, completionRequest=%d\n",
00583             inputAvailable, newOutputAvailable, lts->completionRequest));
00584 
00585     if (inputAvailable && (!newOutputAvailable ||
00586                            (lts->completionRequest == LTERM_NO_COMPLETION)) ) {
00587       /* Process an input data record; either no new output is available, or
00588        * output is available but command completion has not been requested
00589        */
00590       int inOpcodes;
00591 
00592       returnCode = ltermWrite(lts, &inOpcodes);
00593       if (returnCode < 0)
00594         return returnCode;
00595 
00596       if (inOpcodes != 0) {
00597         /* Echoable input data processed; return input line */
00598         returnCode = ltermReturnInputLine(lts, ltr, 0);
00599         if (returnCode < 0)
00600           return returnCode;
00601 
00602         if (inOpcodes & LTERM_META_CODE) {
00603           ltr->opcodes |= LTERM_META_CODE;
00604 
00605           if (inOpcodes & LTERM_COMPLETION_CODE)
00606             ltr->opcodes |= LTERM_COMPLETION_CODE;
00607         }
00608 
00609         if (inOpcodes & LTERM_NEWLINE_CODE) {
00610           /* Input line break */
00611           ltr->opcodes |= LTERM_NEWLINE_CODE;
00612 
00613           if (inOpcodes & LTERM_HIDE_CODE)
00614             ltr->opcodes |= LTERM_HIDE_CODE;
00615 
00616           /* Clear input line buffer */
00617           ltermClearInputLine(lts);
00618         }
00619 
00620         if (lts->disabledInputEcho) {
00621           /* Input echo is disabled, re-poll */
00622           rePoll = 1;
00623           LTERM_LOG(ltermRead,32,("Input echo disabled; RE-POLLING\n\n"));
00624           continue;
00625         }
00626 
00627         return 0;
00628       }
00629 
00630       inputAvailable = 0;
00631     }
00632 
00633     if (newOutputAvailable) {
00634       /* Read data from STDOUT/STDERR */
00635       int outChars;
00636 
00637       outChars = ltermReceiveData(lts, nfds_all==POLL_COUNT);
00638       if (outChars < 0)
00639         return outChars;
00640     }
00641 
00642     if (lto->outputMode == LTERM0_STREAM_MODE) {
00643       /* Stream mode */
00644       return ltermReturnStreamData(lts, ltr);
00645     }
00646 
00647     /* If there is some complete decoded output, process it */
00648     if ((lto->decodedChars > 0) && !lto->incompleteEscapeSequence) {
00649       returnCode = ltermProcessOutput(lts, &outOpcodes, &outOpvals, &outOprow);
00650       if (returnCode < 0)
00651         return -1;
00652 
00653     } else {
00654       /* No new output to process */
00655       if (lto->outputMode == LTERM1_SCREEN_MODE) {
00656         outOpcodes = LTERM_SCREENDATA_CODE;
00657         outOpvals = 0;
00658         outOprow = -1;
00659       } else {
00660         /* No new output in line mode; return nothing */
00661         return 0;
00662       }
00663     }
00664 
00665     if (outOpcodes & LTERM_SCREENDATA_CODE) {
00666       /* Return screen mode output */
00667       return ltermReturnScreenData(lts, ltr, outOpcodes, outOpvals, outOprow);
00668     }
00669 
00670     if (outOpcodes & LTERM_LINEDATA_CODE) {
00671 
00672       LTERM_LOG(ltermRead,32,
00673        ("inputLineBreak=%d, completionRequest=%d, completionChars=%d\n",
00674         lts->inputLineBreak, lts->completionRequest, lts->completionChars));
00675 
00676       if (outOpcodes & LTERM_OUTPUT_CODE) {
00677         /* Return output line */
00678         int echoMatch = 0;
00679 
00680         returnCode = ltermReturnOutputLine(lts, ltr);
00681         if (returnCode < 0)
00682           return returnCode;
00683 
00684         LTERM_LOG(ltermRead,33, ("read_count=%d, echoChars=%d\n",
00685                                  ltr->read_count, lts->echoChars));
00686 
00687         if (lts->inputLineBreak) {
00688           /* Compare output line to initial portion of echo line */
00689           echoMatch = 1;
00690 
00691           if ( (ltr->read_count > lts->echoChars) ||
00692                ((ltr->read_count != lts->echoChars) &&
00693                 (outOpcodes & LTERM_NEWLINE_CODE)) ) {
00694             echoMatch = 0;
00695           } else {
00696             int k;
00697             for (k=0; k<ltr->read_count; k++) {
00698               if (ltr->buf[k] != lts->echoLine[k]) {
00699                 echoMatch = 0;
00700                 break;
00701               }
00702             }
00703           }
00704         }
00705 
00706         if (outOpcodes & LTERM_NEWLINE_CODE) {
00707           /* Complete output line */
00708           if (!echoMatch)
00709             ltr->opcodes |= LTERM_NEWLINE_CODE;
00710 
00711           /* Clear output line buffer */
00712           ltermClearOutputLine(lts);
00713 
00714           /* Clear input line break flag */
00715           lts->inputLineBreak = 0;
00716 
00717         } else {
00718           /* Incomplete output line */
00719 
00720           if (lts->completionRequest != LTERM_NO_COMPLETION) {
00721             /* Completion requested;
00722              * Compare first/last part of output line to parts of echo line
00723              */
00724             int k;
00725             int extraChars = ltr->read_count - lts->echoChars;
00726             int compChars = ltr->read_count;
00727             int preMatch = 1;
00728 
00729             if (extraChars > 0)
00730               compChars = lts->echoChars;
00731 
00732             for (k=0; k<compChars; k++) {
00733               if (ltr->buf[k] != lts->echoLine[k]) {
00734                 preMatch = 0;
00735                 break;
00736               }
00737             }
00738 
00739             if (!preMatch) {
00740               /* No match; cancel completion request */
00741               if (ltermCancelCompletion(lts) != 0)
00742                 return -1;
00743 
00744             } else if (extraChars >= 0) {
00745               /* Matched complete echo line and perhaps more */
00746 
00747               if ((extraChars > 0) && (extraChars <= MAXCOLM1)) {
00748                 /* Insert extra characters into input line */
00749                 lts->completionChars = extraChars;
00750 
00751                 for (k=0; k<extraChars; k++) {
00752                   if (ltermInsertChar(lti, ltr->buf[compChars+k]) != 0)
00753                     return -1;
00754                 }
00755 
00756                 LTERM_LOG(ltermRead,32,
00757                           ("++++++++++++ COMPLETION SUCCESSFUL\n"));
00758 
00759                 LTERM_LOGUNICODE(ltermRead,32,
00760                                  (ltr->buf+compChars, extraChars));
00761               }
00762 
00763               /* Return updated input line, prefixed with prompt */
00764               returnCode = ltermReturnInputLine(lts, ltr, 1);
00765               if (returnCode < 0)
00766                 return returnCode;
00767 
00768             } else {
00769               /* Partial match; do not echo */
00770               echoMatch = 1;
00771             }
00772 
00773           } else if (lto->promptChars == lto->outputChars) {
00774             /* Incomplete output line with just a prompt */
00775 
00776             LTERM_LOG(ltermRead,32,("Prompt-only line\n"));
00777 
00778             /* Return updated input line, prefixed with prompt */
00779             returnCode = ltermReturnInputLine(lts, ltr, 0);
00780             if (returnCode < 0)
00781               return returnCode;
00782           }
00783         }
00784 
00785         if (echoMatch) {
00786           /* Do not display output line; re-poll */
00787           rePoll = 1;
00788           LTERM_LOG(ltermRead,32,("Echo match; RE-POLLING\n\n"));
00789           continue;
00790         } else {
00791           return 0;
00792         }
00793 
00794       } else {
00795         /* Line mode data with modifier flags; return just cursor position */
00796         ltr->opcodes = outOpcodes;
00797         ltr->opvals = 0;
00798 
00799         /* Copy full screen cursor position */
00800         ltr->cursor_row = -1;
00801         ltr->cursor_col = 0;
00802 
00803         ltr->read_count = 0;
00804 
00805         return 0;
00806       }
00807     }
00808 
00809   } while (rePoll);
00810 
00811   return -1;
00812 }
00813 
00818 int ltermInterruptOutput(struct lterms *lts)
00819 {
00820   struct LtermOutput *lto = &(lts->ltermOutput);
00821 
00822   if (lto->outputMode == LTERM0_STREAM_MODE) {
00823     /* Abnormally terminate output stream */
00824     lto->streamOpcodes |= LTERM_ERROR_CODE;
00825   }
00826 
00827   return 0;
00828 }
00829 
00830 
00839 static int ltermReturnStreamData(struct lterms *lts, struct LtermRead *ltr)
00840 {
00841   struct LtermOutput *lto = &(lts->ltermOutput);
00842   UNICHAR *locTerminator;
00843   int streamTerminated, charCount, deleteCount, j;
00844 
00845   LTERM_LOG(ltermReturnStreamData,30,("start\n"));
00846 
00847   if (lto->streamOpcodes & LTERM_ERROR_CODE) {
00848     /* Error in stream output; terminate stream abnormally */
00849     LTERM_LOG(ltermReturnStreamData,32,("Error termination of STREAM mode\n"));
00850 
00851     if (lto->savedOutputMode == LTERM1_SCREEN_MODE) {
00852       if (ltermSwitchToScreenMode(lts) != 0)
00853         return -1;
00854     } else {
00855       if (ltermSwitchToLineMode(lts) != 0)
00856         return -1;
00857     }
00858 
00859     ltr->opcodes = lto->streamOpcodes | LTERM_STREAMDATA_CODE
00860                                       | LTERM_NEWLINE_CODE;
00861     ltr->read_count = 0;
00862     return 0;
00863 
00864   } else if (lto->decodedChars == 0) {
00865     /* No output data available; return null opcode */
00866     return 0;
00867   }
00868 
00869   ltr->opcodes = LTERM_STREAMDATA_CODE | lto->streamOpcodes;
00870 
00871   if (ucslen(lto->streamTerminator) == 0) {
00872     /* Null terminated stream */
00873     locTerminator = NULL;
00874     for (j=0; j<lto->decodedChars; j++) {
00875       if (lto->decodedOutput[j] == 0) {
00876         locTerminator = &lto->decodedOutput[j];
00877         break;
00878       }
00879     }
00880 
00881   } else {
00882     /* Non-null terminated stream */
00883     assert(lto->decodedChars < MAXCOL);
00884 
00885     /* Insert terminating null character in decoded buffer */
00886     lto->decodedOutput[lto->decodedChars] = U_NUL;
00887 
00888     /* There should be no NULs in decoded output */
00889     assert((int)ucslen(lto->decodedOutput) == lto->decodedChars);
00890 
00891     /* Search for stream terminator string in decoded output */
00892     locTerminator = ucsstr(lto->decodedOutput, lto->streamTerminator);
00893   }
00894 
00895   /* Stream termination flag */
00896   streamTerminated = 0;
00897 
00898   if (locTerminator == NULL) {
00899     /* No terminator found; return decoded stream data */
00900     charCount = lto->decodedChars;
00901     if (charCount > ltr->max_count)
00902       charCount = ltr->max_count;
00903 
00904   } else {
00905     /* Count stream portion of decoded output (excluding terminator) */
00906     charCount = locTerminator - lto->decodedOutput;
00907 
00908     if (charCount > ltr->max_count) {
00909       /* Return only portion of stream data that fits into output buffer,
00910        * without terminating stream mode
00911        */
00912       charCount = ltr->max_count;
00913 
00914     } else {
00915       /* Return all remaining stream data, terminate stream mode and
00916        * revert to saved output mode
00917        */
00918       streamTerminated = 1;
00919       if (lto->savedOutputMode == LTERM1_SCREEN_MODE) {
00920         if (ltermSwitchToScreenMode(lts) != 0)
00921           return -1;
00922       } else {
00923         if (ltermSwitchToLineMode(lts) != 0)
00924           return -1;
00925       }
00926       LTERM_LOG(ltermReturnStreamData,32,("terminating STREAM mode\n"));
00927     }
00928   }
00929 
00930   /* Copy stream data to output buffer */
00931   for (j=0; j<charCount; j++) {
00932     ltr->buf[j] = lto->decodedOutput[j];
00933     ltr->style[j] = LTERM_STDOUT_STYLE;
00934   }
00935 
00936   /* Count of characters read */
00937   ltr->read_count = charCount;
00938 
00939   /* Count of characters to be deleted */
00940   deleteCount = charCount;
00941 
00942   if (streamTerminated) {
00943     /* Stream terminated */
00944     ltr->opcodes |= LTERM_NEWLINE_CODE;
00945 
00946     /* Delete terminator string from decoded buffer */
00947     if (ucslen(lto->streamTerminator) == 0) {
00948       deleteCount += 1;
00949     } else {
00950       deleteCount += ucslen(lto->streamTerminator);
00951     }
00952   }
00953 
00954   /* Shift remaining characters in decoded buffer */
00955   for (j=deleteCount; j<lto->decodedChars; j++)
00956     lto->decodedOutput[j-deleteCount] = lto->decodedOutput[j];
00957 
00958   lto->decodedChars -= deleteCount;
00959 
00960   LTERM_LOG(ltermReturnStreamData,31,("returning STREAM data (%d bytes)\n",
00961                                          ltr->read_count));
00962 
00963   return 0;
00964 }
00965 
00966 
00976 static int ltermReturnScreenData(struct lterms *lts, struct LtermRead *ltr,
00977                                  int opcodes, int opvals, int oprow)
00978 {
00979   struct LtermOutput *lto = &(lts->ltermOutput);
00980   int cursorMoved, returnRow, charCount, returnCode;
00981   int jOffset, j;
00982 
00983   cursorMoved = (lto->returnedCursorRow != lto->cursorRow) ||
00984                 (lto->returnedCursorCol != lto->cursorCol);
00985 
00986   lto->returnedCursorRow = lto->cursorRow;
00987   lto->returnedCursorCol = lto->cursorCol;
00988 
00989   LTERM_LOG(ltermReturnScreenData,30,("cursorMoved=%d\n", cursorMoved));
00990 
00991   /* Screen mode data with possible modifier flags */
00992   ltr->opcodes = opcodes;
00993   ltr->opvals = opvals;
00994 
00995   /* Copy full screen cursor position */
00996   ltr->cursor_row = lto->returnedCursorRow;
00997   ltr->cursor_col = lto->returnedCursorCol;
00998 
00999   if (opcodes &
01000       (LTERM_CLEAR_CODE|LTERM_INSERT_CODE|LTERM_DELETE_CODE|LTERM_SCROLL_CODE)) {
01001     /* Screen modifier flags; return no character data */
01002     if (oprow >= 0) {
01003       ltr->buf_row = oprow;
01004     } else {
01005       ltr->buf_row = lto->cursorRow;
01006     }
01007 
01008     ltr->buf_col = 0;
01009 
01010     ltr->read_count = 0;
01011 
01012     return 0;
01013   }
01014 
01015   /* Check if any row has been modified */
01016   returnRow = -1;
01017 
01018   for (j=0; j<lts->nRows; j++)
01019     if (lto->modifiedCol[j] >= 0) {
01020       returnRow = j;
01021       break;
01022     }
01023 
01024   if (returnRow < 0) {
01025     /* No modified rows */
01026     ltr->read_count = 0;
01027     ltr->buf_row = 0;
01028     ltr->buf_col = 0;
01029 
01030     if (!cursorMoved && (ltr->opcodes == LTERM_SCREENDATA_CODE)) {
01031       /* Return no data (***IMPORTANT ACTION, TO PREVENT LOOPING***) */
01032       ltr->opcodes = 0;
01033     }
01034 
01035     return 0;
01036   }
01037 
01038   /* Returning modified row */
01039   ltr->opcodes |= LTERM_OUTPUT_CODE;
01040 
01041   /* Copy entire row */
01042   ltr->buf_row = returnRow;
01043   ltr->buf_col = 0;
01044 
01045   charCount = lts->nCols;
01046 
01047   if (charCount <= ltr->max_count) {
01048     returnCode = 0;
01049   } else {
01050     /* Too much data to fit into output buffer; truncate */
01051     charCount = ltr->max_count;
01052     returnCode = -3;
01053   }
01054 
01055   jOffset = ltr->buf_row * lts->nCols + ltr->buf_col;
01056   for (j=0; j<charCount; j++) {
01057     ltr->buf[j]   = lto->screenChar[jOffset+j];
01058     ltr->style[j] = lto->screenStyle[jOffset+j];
01059   }
01060 
01061   /* Clear modified flag in returned row */
01062   lto->modifiedCol[returnRow] = -1;
01063 
01064   ltr->read_count = charCount;
01065 
01066   LTERM_LOG(ltermReturnScreenData,31,("returning SCREEN data\n"));
01067   LTERM_LOGUNICODE(ltermReturnScreenData,31,(ltr->buf, ltr->read_count));
01068 
01069   return returnCode;
01070 }
01071 
01072 
01083 static int ltermReturnInputLine(struct lterms *lts, struct LtermRead *ltr,
01084                                 int completionRequested)
01085 {
01086   struct LtermOutput *lto = &(lts->ltermOutput);
01087   struct LtermInput *lti = &(lts->ltermInput);
01088   int outChars, charCount, inputCursorCol, returnCode;
01089   int j;
01090 
01091   LTERM_LOG(ltermReturnInputLine,30, ("outputChars=%d, promptChars=%d\n",
01092                                         lto->outputChars, lto->promptChars));
01093 
01094   if (lto->promptChars > 0) {
01095     /* Prefix with prompt output data */
01096     ltr->opcodes = LTERM_LINEDATA_CODE;
01097 
01098     outChars = lto->promptChars;
01099 
01100     if (!completionRequested) {
01101       /* Hack to handle misidentified prompts;
01102        *  if output characters following the prompt differ from input line,
01103        *  display them.
01104        */
01105       for (j=0; j<lti->inputChars; j++) {
01106         if (((j+outChars) < lto->outputChars) && 
01107             (lto->outputLine[j+outChars] != lti->inputLine[j])) {
01108           outChars = lto->outputChars;
01109           break;
01110         }
01111       }
01112     }
01113 
01114     if (outChars > ltr->max_count)
01115       outChars = 0;
01116 
01117     if (outChars > 0) {
01118       ltr->opcodes |= LTERM_PROMPT_CODE;
01119       for (j=0; j<outChars; j++) {
01120         ltr->buf[j] = lto->outputLine[j];
01121         ltr->style[j] = LTERM_PROMPT_STYLE;
01122       }
01123     }
01124 
01125   } else {
01126     /* Prefix with entire output line */
01127     returnCode = ltermReturnOutputLine(lts, ltr);
01128     if (returnCode < 0)
01129       return returnCode;
01130 
01131     outChars = ltr->read_count;
01132   }
01133 
01134   ltr->opcodes |= LTERM_INPUT_CODE;
01135 
01136   charCount = outChars + lti->inputChars;
01137 
01138   if (charCount <= ltr->max_count) {
01139     returnCode = 0;
01140   } else {
01141     /* Too much data to fit into output buffer; truncate */
01142     charCount = ltr->max_count;
01143     returnCode = -3;
01144   }
01145 
01146   /* Append STDIN data, with markup and escape sequences */
01147   for (j=outChars; j<charCount; j++) {
01148     ltr->buf[j] = lti->inputLine[j-outChars];
01149     ltr->style[j] = LTERM_STDIN_STYLE;
01150   }
01151 
01152   inputCursorCol = lti->inputGlyphColIndex[lti->inputCursorGlyph];
01153 
01154   ltr->buf_row = -1;
01155   ltr->buf_col = 0;
01156 
01157   ltr->cursor_row = -1;
01158   ltr->cursor_col = outChars + lti->inputColCharIndex[inputCursorCol];
01159 
01160   ltr->read_count = charCount;
01161 
01162   LTERM_LOG(ltermReturnInputLine,32,("returning INPUT LINE data\n"));
01163   LTERM_LOGUNICODE(ltermReturnInputLine,32,(ltr->buf, ltr->read_count));
01164 
01165   return returnCode;
01166 }
01167 
01168 
01178 static int ltermReturnOutputLine(struct lterms *lts, struct LtermRead *ltr)
01179 {
01180   struct LtermOutput *lto = &(lts->ltermOutput);
01181   int charCount, returnCode, j;
01182 
01183   LTERM_LOG(ltermReturnOutputLine,30,
01184             ("outputChars=%d, promptChars=%d, CursorChar=%d\n",
01185              lto->outputChars, lto->promptChars, lto->outputCursorChar));
01186 
01187   ltr->opcodes = LTERM_LINEDATA_CODE;
01188 
01189   charCount = lto->outputChars;
01190 
01191   if (charCount <= ltr->max_count) {
01192     returnCode = 0;
01193   } else {
01194     /* Too much data to fit into output buffer; truncate */
01195     charCount = ltr->max_count;
01196     returnCode = -3;
01197   }
01198 
01199   /* Copy output characters (plain text; no markup or escape sequences) */
01200   ltr->opcodes |= LTERM_OUTPUT_CODE;
01201   for (j=0; j<charCount; j++) {
01202     ltr->buf[j] = lto->outputLine[j];
01203     ltr->style[j] = lto->outputStyle[j];
01204   }
01205 
01206   if ((lto->promptChars > 0) && (lto->promptChars <= charCount)) {
01207     /* Set prompt style */
01208     ltr->opcodes |= LTERM_PROMPT_CODE;
01209     for (j=0; j<lto->promptChars; j++) {
01210       ltr->style[j] = LTERM_PROMPT_STYLE;
01211     }
01212   }
01213 
01214   ltr->buf_row = -1;
01215   ltr->buf_col = 0;
01216 
01217   ltr->cursor_row = -1;
01218   ltr->cursor_col = lto->outputCursorChar;
01219 
01220   ltr->read_count = charCount;
01221 
01222   LTERM_LOG(ltermReturnOutputLine,31,("returning OUTPUT LINE data\n"));
01223   LTERM_LOGUNICODE(ltermReturnOutputLine,31,(ltr->buf, ltr->read_count));
01224 
01225   return returnCode;
01226 }