mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-14 03:30:17 +01:00
883 lines
27 KiB
C
883 lines
27 KiB
C
|
/* ***** BEGIN LICENSE BLOCK *****
|
||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||
|
*
|
||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||
|
* the License. You may obtain a copy of the License at
|
||
|
* http://www.mozilla.org/MPL/
|
||
|
*
|
||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
|
* for the specific language governing rights and limitations under the
|
||
|
* License.
|
||
|
*
|
||
|
* The Original Code is lineterm.
|
||
|
*
|
||
|
* The Initial Developer of the Original Code is
|
||
|
* Ramalingam Saravanan.
|
||
|
* Portions created by the Initial Developer are Copyright (C) 1999
|
||
|
* the Initial Developer. All Rights Reserved.
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
*
|
||
|
* Alternatively, the contents of this file may be used under the terms of
|
||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||
|
* of those above. If you wish to allow use of your version of this file only
|
||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||
|
* use your version of this file under the terms of the MPL, indicate your
|
||
|
* decision by deleting the provisions above and replace them with the notice
|
||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||
|
* the provisions above, a recipient may use your version of this file under
|
||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||
|
*
|
||
|
* ***** END LICENSE BLOCK ***** */
|
||
|
|
||
|
/* ltermInput.c: LTERM PTY input data processing
|
||
|
*/
|
||
|
|
||
|
/* public declarations */
|
||
|
#include "lineterm.h"
|
||
|
|
||
|
/* private declarations */
|
||
|
#include "ltermPrivate.h"
|
||
|
|
||
|
|
||
|
static int ltermLineInput(struct lterms *lts, const UNICHAR *buf, int count,
|
||
|
int *opcodes);
|
||
|
static int ltermMetaInput(struct lterms *lts);
|
||
|
static int ltermRequestCompletion(struct lterms *lts, UNICHAR uch);
|
||
|
|
||
|
|
||
|
/** Processes plain text input data and returns
|
||
|
* OPCODES ::= LINEDATA ( INPUT ( NEWLINE HIDE? )?
|
||
|
* | INPUT META ( COMPLETION | NEWLINE HIDE? ) )
|
||
|
* if echoable input data was processed.
|
||
|
* (OPCODES is set to zero if raw input data was processed)
|
||
|
* Called from ltermWrite
|
||
|
* @return 0 on success,
|
||
|
* -1 on error, and
|
||
|
* -2 if pseudo-TTY has been closed.
|
||
|
*/
|
||
|
int ltermPlainTextInput(struct lterms *lts,
|
||
|
const UNICHAR *buf, int count, int *opcodes)
|
||
|
{
|
||
|
struct LtermInput *lti = &(lts->ltermInput);
|
||
|
int returnCode;
|
||
|
|
||
|
LTERM_LOG(ltermPlainTextInput,20,
|
||
|
("count=%d, lti->inputMode=%d\n", count, lti->inputMode));
|
||
|
|
||
|
if (lti->inputMode == LTERM0_RAW_MODE) {
|
||
|
/* Transmit characters immediately to child process; no buffering */
|
||
|
|
||
|
LTERM_LOG(ltermPlainTextInput,29,
|
||
|
("Raw mode, transmitting %d characters\n",
|
||
|
count));
|
||
|
|
||
|
if (ltermSendData(lts, buf, count) != 0)
|
||
|
return -1;
|
||
|
|
||
|
*opcodes = 0;
|
||
|
|
||
|
} else {
|
||
|
/* Not raw input mode; process line mode input */
|
||
|
int processTrailingTab = 0;
|
||
|
|
||
|
LTERM_LOG(ltermPlainTextInput,21,
|
||
|
("Line mode, lts->commandNumber=%d, inputMode=%d\n",
|
||
|
lts->commandNumber, lti->inputMode));
|
||
|
|
||
|
if ((lti->inputMode >= LTERM3_COMPLETION_MODE) &&
|
||
|
(lts->commandNumber == 0)) {
|
||
|
/* Downgrade input mode */
|
||
|
lti->inputMode = LTERM2_EDIT_MODE;
|
||
|
|
||
|
LTERM_LOG(ltermPlainTextInput,21,
|
||
|
("------------ Downgraded input mode=%d\n\n",
|
||
|
lti->inputMode));
|
||
|
|
||
|
} else if ((lti->inputMode < lts->maxInputMode) &&
|
||
|
(lts->commandNumber != 0)) {
|
||
|
/* Upgrade input mode */
|
||
|
int priorInputMode = lti->inputMode;
|
||
|
|
||
|
/* Set input mode (possibly allowing completion) */
|
||
|
lti->inputMode = lts->maxInputMode;
|
||
|
|
||
|
/* Do not allow command completion without TTY echo */
|
||
|
if ( (lts->disabledInputEcho || lts->noTTYEcho) &&
|
||
|
(lti->inputMode > LTERM2_EDIT_MODE) )
|
||
|
lti->inputMode = LTERM2_EDIT_MODE;
|
||
|
|
||
|
if ((lti->inputChars > 0) &&
|
||
|
(priorInputMode < LTERM3_COMPLETION_MODE) &&
|
||
|
(lti->inputMode >= LTERM3_COMPLETION_MODE)) {
|
||
|
/* Process prior input TABs before switching to completion mode */
|
||
|
int j;
|
||
|
|
||
|
if ((count == 0) &&
|
||
|
(lti->inputCursorGlyph == lti->inputGlyphs) &&
|
||
|
(lti->inputGlyphColIndex[lti->inputGlyphs] == lti->inputCols) &&
|
||
|
(lti->inputColCharIndex[lti->inputCols] == lti->inputChars) &&
|
||
|
(lti->inputLine[lti->inputChars] == U_TAB)) {
|
||
|
/* Trailing TAB in prior input; delete it, and process it later */
|
||
|
if (ltermDeleteGlyphs(lti, 1) != 0)
|
||
|
return -1;
|
||
|
processTrailingTab = 1;
|
||
|
}
|
||
|
|
||
|
/* Replace all input TABs with spaces */
|
||
|
for (j=0; j < lti->inputChars; j++) {
|
||
|
if (lti->inputLine[j] == U_TAB)
|
||
|
lti->inputLine[j] = U_SPACE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LTERM_LOG(ltermPlainTextInput,21,
|
||
|
("------------ Upgraded input mode=%d, trailingTab=%d\n\n",
|
||
|
lti->inputMode, processTrailingTab));
|
||
|
|
||
|
}
|
||
|
|
||
|
if (processTrailingTab) {
|
||
|
/* Re-process trailing TAB */
|
||
|
UNICHAR uch = U_TAB;
|
||
|
|
||
|
assert(count == 0);
|
||
|
|
||
|
LTERM_LOG(ltermPlainTextInput,21,("Reprocessing trailing TAB\n"));
|
||
|
|
||
|
returnCode= ltermLineInput(lts, &uch, 1, opcodes);
|
||
|
if (returnCode < 0)
|
||
|
return returnCode;
|
||
|
|
||
|
} else {
|
||
|
/* Process new input characters */
|
||
|
returnCode = ltermLineInput(lts, buf, count, opcodes) != 0;
|
||
|
if (returnCode < 0)
|
||
|
return returnCode;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** Cancels a prior command line completion request
|
||
|
* @return 0 on success,
|
||
|
* -1 on error
|
||
|
*/
|
||
|
int ltermCancelCompletion(struct lterms *lts)
|
||
|
{
|
||
|
|
||
|
LTERM_LOG(ltermCancelCompletion,40,
|
||
|
("++++++++++++ CANCELED COMPLETION REQUEST\n\n"));
|
||
|
|
||
|
if (lts->completionRequest != LTERM_NO_COMPLETION) {
|
||
|
|
||
|
/* Kill input line transmitted to process */
|
||
|
if (ltermSendData(lts, lts->control+TTYKILL, 1) != 0)
|
||
|
return -1;
|
||
|
|
||
|
lts->completionRequest = LTERM_NO_COMPLETION;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** Inserts plain text character UCH as a single-column glyph at the
|
||
|
* current input cursor location, translating to escape sequence if needed.
|
||
|
* @return 0 on success, -1 on error.
|
||
|
*/
|
||
|
int ltermInsertChar(struct LtermInput *lti, UNICHAR uch)
|
||
|
{
|
||
|
UNICHAR* escapeSequence;
|
||
|
int insChars, insertColIndex, insertCharIndex, j;
|
||
|
|
||
|
LTERM_LOG(ltermInsertChar,40,("inserting character 0x%x at glyph %d\n",
|
||
|
uch, lti->inputCursorGlyph));
|
||
|
|
||
|
/* Ignore null character */
|
||
|
if (uch == 0)
|
||
|
return 0;
|
||
|
|
||
|
escapeSequence = NULL;
|
||
|
insChars = 1;
|
||
|
|
||
|
#if 0
|
||
|
/* COMMENTED OUT: Plain text not escaped; use code later in HTML insert */
|
||
|
/* Check if plain text character needs to be escaped for XML/HTML */
|
||
|
for (j=0; j<LTERM_PLAIN_ESCAPES; j++) {
|
||
|
|
||
|
if (uch == ltermGlobal.escapeChars[j]) {
|
||
|
/* Insert escape sequence rather than character */
|
||
|
escapeSequence = ltermGlobal.escapeSeq[j];
|
||
|
insChars = ltermGlobal.escapeLen[j];
|
||
|
LTERM_LOG(ltermInsertChar,42,("escape index=%d\n", j));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
#endif /* 0 */
|
||
|
|
||
|
if (lti->inputChars+insChars > MAXCOLM1) {
|
||
|
/* Input buffer overflow; ignore insert character */
|
||
|
LTERM_WARNING("ltermInsertChar: Warning - input line buffer overflow\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
assert(lti->inputChars >= 0);
|
||
|
assert(lti->inputCols >= 0);
|
||
|
assert(lti->inputGlyphs >= 0);
|
||
|
assert(lti->inputCursorGlyph >= 0);
|
||
|
|
||
|
assert(lti->inputCols <= lti->inputChars);
|
||
|
assert(lti->inputGlyphs <= lti->inputCols);
|
||
|
|
||
|
insertColIndex = lti->inputGlyphColIndex[lti->inputCursorGlyph];
|
||
|
insertCharIndex = lti->inputColCharIndex[insertColIndex];
|
||
|
|
||
|
LTERM_LOG(ltermInsertChar,41,("insertColIndex=%d, insertCharIndex=%d, insChars=%d\n",
|
||
|
insertColIndex, insertCharIndex, insChars));
|
||
|
|
||
|
/* Shift portion of input line to the right;
|
||
|
remember that the column/glyph index arrays have an extra element */
|
||
|
for (j=lti->inputChars - 1; j >= insertCharIndex; j--)
|
||
|
lti->inputLine[j+insChars] = lti->inputLine[j];
|
||
|
|
||
|
for (j=lti->inputCols; j >= insertColIndex; j--)
|
||
|
lti->inputColCharIndex[j+1] = lti->inputColCharIndex[j]+insChars;
|
||
|
|
||
|
for (j=lti->inputGlyphs; j >= lti->inputCursorGlyph; j--) {
|
||
|
lti->inputGlyphCharIndex[j+1] = lti->inputGlyphCharIndex[j]+insChars;
|
||
|
lti->inputGlyphColIndex[j+1] = lti->inputGlyphColIndex[j]+1;
|
||
|
}
|
||
|
|
||
|
/* Insert character(s) in input line */
|
||
|
if (escapeSequence == NULL) {
|
||
|
lti->inputLine[insertCharIndex] = uch;
|
||
|
} else {
|
||
|
for (j=0; j < insChars; j++)
|
||
|
lti->inputLine[j+insertCharIndex] = escapeSequence[j];
|
||
|
}
|
||
|
|
||
|
/* Insert column/glyph */
|
||
|
lti->inputColCharIndex[insertColIndex] = insertCharIndex;
|
||
|
|
||
|
lti->inputGlyphCharIndex[lti->inputCursorGlyph] = insertCharIndex;
|
||
|
lti->inputGlyphColIndex[lti->inputCursorGlyph] = insertColIndex;
|
||
|
|
||
|
lti->inputChars += insChars; /* Increment character count */
|
||
|
|
||
|
lti->inputCols++; /* Increment column count */
|
||
|
|
||
|
lti->inputGlyphs++; /* Increment glyph count */
|
||
|
|
||
|
lti->inputCursorGlyph++; /* Reposition cursor */
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** switches to raw input mode */
|
||
|
void ltermSwitchToRawMode(struct lterms *lts)
|
||
|
{
|
||
|
struct LtermInput *lti = &(lts->ltermInput);
|
||
|
|
||
|
LTERM_LOG(ltermSwitchToRawMode,40,("\n"));
|
||
|
|
||
|
if (lti->inputMode != LTERM0_RAW_MODE) {
|
||
|
/* Do other things ... */
|
||
|
lti->inputMode = LTERM0_RAW_MODE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/** clears input line buffer and switches to regular input mode */
|
||
|
void ltermClearInputLine(struct lterms *lts)
|
||
|
{
|
||
|
struct LtermInput *lti = &(lts->ltermInput);
|
||
|
|
||
|
LTERM_LOG(ltermClearInputLine,40,("\n"));
|
||
|
|
||
|
lti->inputChars = 0;
|
||
|
|
||
|
lti->inputCols = 0;
|
||
|
lti->inputColCharIndex[0] = 0;
|
||
|
|
||
|
lti->inputGlyphs = 0;
|
||
|
lti->inputGlyphCharIndex[0] = 0;
|
||
|
lti->inputGlyphColIndex[0] = 0;
|
||
|
|
||
|
lti->inputCursorGlyph = 0;
|
||
|
|
||
|
if (lts->maxInputMode >= LTERM2_EDIT_MODE)
|
||
|
lti->inputMode = LTERM2_EDIT_MODE;
|
||
|
else
|
||
|
lti->inputMode = lts->maxInputMode;
|
||
|
|
||
|
lti->escapeFlag = 0;
|
||
|
lti->escapeCSIFlag = 0;
|
||
|
lti->escapeCSIArg = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** Processes an input string in canonical or higher mode and returns
|
||
|
* OPCODES ::= LINEDATA ( INPUT (NEWLINE HIDE?)?
|
||
|
* | INPUT META (COMPLETION|NEWLINE HIDE?) )
|
||
|
* if echoable input data was processed.
|
||
|
* (OPCODES is set to zero if raw input data was processed)
|
||
|
* Called from ltermPlainTextInput
|
||
|
* @return 0 on success,
|
||
|
* -1 on error, and
|
||
|
* -2 if pseudo-TTY has been closed.
|
||
|
*/
|
||
|
static int ltermLineInput(struct lterms *lts,
|
||
|
const UNICHAR *buf, int count, int *opcodes)
|
||
|
{
|
||
|
struct LtermInput *lti = &(lts->ltermInput);
|
||
|
UNICHAR uch;
|
||
|
int charIndex, metaInput;
|
||
|
|
||
|
/* Default returned opcodes (maybe overridden) */
|
||
|
*opcodes = LTERM_LINEDATA_CODE | LTERM_INPUT_CODE;
|
||
|
|
||
|
charIndex = 0;
|
||
|
|
||
|
LTERM_LOG(ltermLineInput,30,
|
||
|
("count=%d, lti->inputMode=%d, inputCursorGlyph=%d\n",
|
||
|
count, lti->inputMode, lti->inputCursorGlyph));
|
||
|
LTERM_LOGUNICODE(ltermLineInput,31,(buf, count));
|
||
|
LTERM_LOG(ltermLineInput,31,("Glyphs=%d,Cols=%d,Chars=%d\n",
|
||
|
lti->inputGlyphs, lti->inputCols, lti->inputChars));
|
||
|
|
||
|
while (charIndex < count) {
|
||
|
uch = buf[charIndex];
|
||
|
|
||
|
if (uch == U_ESCAPE) {
|
||
|
/* Escape */
|
||
|
lti->escapeFlag = 1;
|
||
|
uch = U_NUL;
|
||
|
|
||
|
} else if (lti->escapeFlag) {
|
||
|
/* Escaped character */
|
||
|
lti->escapeFlag = 0;
|
||
|
|
||
|
switch (uch) {
|
||
|
|
||
|
case U_LBRACKET:
|
||
|
/* Start of escape code sequence */
|
||
|
lti->escapeCSIFlag = 1;
|
||
|
lti->escapeCSIArg = 0;
|
||
|
uch = U_NUL;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
uch = U_NUL;
|
||
|
}
|
||
|
|
||
|
} else if (lti->escapeCSIFlag) {
|
||
|
/* Character part of escape code sequence */
|
||
|
|
||
|
LTERM_LOG(ltermLineInput,38,("Escape code sequence - 0x%x\n", uch));
|
||
|
|
||
|
if ((uch >= (UNICHAR)U_ZERO && uch <= (UNICHAR)U_NINE)) {
|
||
|
/* Process numerical argument to escape code sequence */
|
||
|
lti->escapeCSIArg = lti->escapeCSIArg*10 + (uch - U_ZERO);
|
||
|
uch = U_NUL;
|
||
|
|
||
|
} else {
|
||
|
/* End of escape code sequence */
|
||
|
lti->escapeCSIFlag = 0;
|
||
|
|
||
|
/* NOTE: Input CSI escape sequence; may not be portable */
|
||
|
|
||
|
/* SUN arrow key bindings */
|
||
|
switch (uch) {
|
||
|
case U_A_CHAR:
|
||
|
uch = U_CTL_P;
|
||
|
break;
|
||
|
case U_B_CHAR:
|
||
|
uch = U_CTL_N;
|
||
|
break;
|
||
|
case U_C_CHAR:
|
||
|
uch = U_CTL_F;
|
||
|
break;
|
||
|
case U_D_CHAR:
|
||
|
uch = U_CTL_B;
|
||
|
break;
|
||
|
default:
|
||
|
uch = U_NUL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( ((uch >= (UNICHAR)U_SPACE) && (uch != (UNICHAR)U_DEL)) ||
|
||
|
((uch == (UNICHAR)U_TAB) && (lti->inputMode <= LTERM2_EDIT_MODE)) ) {
|
||
|
/* printable character or non-completion mode TAB; insert in buffer */
|
||
|
/* (NEED TO UPDATE THIS CHECK FOR UNICODE PRINTABILITY) */
|
||
|
|
||
|
LTERM_LOG(ltermLineInput,39,("inserting printable character - %c\n",
|
||
|
(char) uch));
|
||
|
|
||
|
/* Insert character */
|
||
|
if (ltermInsertChar(lti, uch) != 0)
|
||
|
return -1;
|
||
|
|
||
|
} else {
|
||
|
/* Control character */
|
||
|
|
||
|
/* Translate carriage returns to linefeeds in line mode
|
||
|
(***NOTE*** may not be portable out of *nix) */
|
||
|
if (uch == U_CRETURN)
|
||
|
uch = U_LINEFEED;
|
||
|
|
||
|
/* Line break control characters */
|
||
|
if ( (uch == U_LINEFEED) ||
|
||
|
(uch == lts->control[TTYDISCARD]) ||
|
||
|
(uch == lts->control[TTYSUSPEND]) ||
|
||
|
(uch == lts->control[TTYINTERRUPT])) {
|
||
|
/* Newline/TTYdiscard/TTYsuspend/TTYinterrupt character */
|
||
|
|
||
|
/* Assert that linebreak character occurs at end of buffer;
|
||
|
* enforced by lterm_write.
|
||
|
*/
|
||
|
assert(charIndex == count-1);
|
||
|
|
||
|
/* Check if meta input line */
|
||
|
metaInput = ltermMetaInput(lts);
|
||
|
|
||
|
if ((uch == lts->control[TTYDISCARD]) && !metaInput
|
||
|
&& (lts->commandNumber == 0)) {
|
||
|
/* Not meta/command line; simply transmit discard character */
|
||
|
if (ltermSendData(lts, lts->control+TTYDISCARD, 1) != 0)
|
||
|
return -1;
|
||
|
|
||
|
} else {
|
||
|
/* Newline behaviour, with hide option */
|
||
|
LTERM_LOG(ltermLineInput,31,("------------ NEWLINE (0x%x)\n\n",
|
||
|
uch));
|
||
|
LTERM_LOGUNICODE(ltermLineInput,31,( lti->inputLine,
|
||
|
lti->inputChars));
|
||
|
|
||
|
/* The NEWLINE code tells ltermReturnInputLine to clear
|
||
|
* the input line buffer after copying it
|
||
|
*/
|
||
|
*opcodes = LTERM_LINEDATA_CODE | LTERM_INPUT_CODE
|
||
|
| LTERM_NEWLINE_CODE;
|
||
|
|
||
|
if (uch == lts->control[TTYDISCARD]) {
|
||
|
*opcodes |= LTERM_HIDE_CODE;
|
||
|
uch = U_LINEFEED; /* essentially newline behaviour otherwise */
|
||
|
}
|
||
|
|
||
|
if (uch == lts->control[TTYINTERRUPT]) {
|
||
|
/* Interrupt output operations, if necessary */
|
||
|
if (ltermInterruptOutput(lts) != 0)
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (metaInput) {
|
||
|
/* meta input; do not send line */
|
||
|
*opcodes |= LTERM_META_CODE;
|
||
|
} else {
|
||
|
/* Send line and copy to echo buffer */
|
||
|
if (ltermSendLine(lts, uch, (uch != U_LINEFEED),
|
||
|
LTERM_NO_COMPLETION) != 0)
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
} else if (uch == lts->control[TTYKILL]) {
|
||
|
/* kill line */
|
||
|
ltermClearInputLine(lts);
|
||
|
|
||
|
LTERM_LOG(ltermLineInput,31,("TTYKILL\n"));
|
||
|
|
||
|
} else if ((uch == U_BACKSPACE) || (uch == U_DEL) ||
|
||
|
(uch == lts->control[TTYERASE])) {
|
||
|
/* erase glyph */
|
||
|
if (ltermDeleteGlyphs(lti, 1) != 0)
|
||
|
return -1;
|
||
|
|
||
|
LTERM_LOG(ltermLineInput,39,("TTYERASE=0x%x/0x%x\n",
|
||
|
lts->control[TTYERASE], uch ));
|
||
|
|
||
|
} else {
|
||
|
/* other control characters */
|
||
|
|
||
|
LTERM_LOG(ltermLineInput,32,("^%c\n", uch+U_ATSIGN));
|
||
|
|
||
|
if (lti->inputMode < LTERM2_EDIT_MODE) {
|
||
|
/* Non-edit mode; simply transmit control character */
|
||
|
if (ltermSendData(lts, &uch, 1) != 0)
|
||
|
return -1;
|
||
|
|
||
|
} else {
|
||
|
/* Edit input mode */
|
||
|
|
||
|
if (uch == U_CTL_D) {
|
||
|
/* Special handling for ^D */
|
||
|
|
||
|
if (lti->inputChars == 0) {
|
||
|
/* Lone ^D in input line, simply transmit it */
|
||
|
if (ltermSendData(lts, &uch, 1) != 0)
|
||
|
return -1;
|
||
|
uch = U_NUL;
|
||
|
|
||
|
} else if (lti->inputCursorGlyph < lti->inputGlyphs) {
|
||
|
/* Cursor not at end of line; delete to right */
|
||
|
if (ltermDeleteGlyphs(lti, -1) != 0)
|
||
|
return -1;
|
||
|
uch = U_NUL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch (uch) {
|
||
|
|
||
|
case U_NUL: /* Null character; ignore */
|
||
|
break;
|
||
|
|
||
|
case U_CTL_B: /* move cursor backward */
|
||
|
if (lti->inputCursorGlyph > 0) {
|
||
|
lti->inputCursorGlyph--;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case U_CTL_F: /* move cursor forward */
|
||
|
if (lti->inputCursorGlyph < lti->inputGlyphs) {
|
||
|
lti->inputCursorGlyph++;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case U_CTL_A: /* position cursor at beginning of line */
|
||
|
lti->inputCursorGlyph = 0;
|
||
|
break;
|
||
|
|
||
|
case U_CTL_E: /* position cursor at end of line */
|
||
|
lti->inputCursorGlyph = lti->inputGlyphs;
|
||
|
break;
|
||
|
|
||
|
case U_CTL_K: /* delete to end of line */
|
||
|
if (ltermDeleteGlyphs(lti,-(lti->inputGlyphs-lti->inputCursorGlyph))
|
||
|
!= 0)
|
||
|
return -1;
|
||
|
break;
|
||
|
|
||
|
case U_CTL_L: /* form feed */
|
||
|
case U_CTL_R: /* redisplay */
|
||
|
break;
|
||
|
|
||
|
case U_CTL_D: /* ^D at end of non-null input line */
|
||
|
case U_CTL_N: /* dowN history */
|
||
|
case U_CTL_P: /* uP history */
|
||
|
case U_CTL_Y: /* yank */
|
||
|
case U_TAB: /* command completion */
|
||
|
|
||
|
/* Assert that completion character occurs at end of buffer;
|
||
|
* enforced by lterm_write.
|
||
|
*/
|
||
|
assert(charIndex == count-1);
|
||
|
|
||
|
metaInput = ltermMetaInput(lts);
|
||
|
|
||
|
if (metaInput) {
|
||
|
/* Meta input command completion */
|
||
|
LTERM_LOG(ltermLineInput,40,
|
||
|
("++++++++++++ meta COMPLETION uch=0x%X\n\n", uch));
|
||
|
if (uch == U_TAB) {
|
||
|
*opcodes = LTERM_LINEDATA_CODE | LTERM_INPUT_CODE
|
||
|
| LTERM_META_CODE
|
||
|
| LTERM_COMPLETION_CODE;
|
||
|
} else {
|
||
|
LTERM_WARNING("ltermLineInput: Warning - meta command completion not yet implemented for uch=0x%x\n", uch);
|
||
|
}
|
||
|
|
||
|
} else if (lti->inputMode >= LTERM3_COMPLETION_MODE) {
|
||
|
/* Completion mode; non-completion TABs already processed */
|
||
|
|
||
|
if (ltermRequestCompletion(lts, uch) != 0)
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
default: /* Transmit any other control character */
|
||
|
if (ltermSendData(lts, &uch, 1) != 0)
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Increment character index */
|
||
|
charIndex++;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** Check if input line contains a meta delimiter;
|
||
|
* @return 1 if it does, 0 otherwise.
|
||
|
*/
|
||
|
static int ltermMetaInput(struct lterms *lts)
|
||
|
{
|
||
|
struct LtermInput *lti = &(lts->ltermInput);
|
||
|
UNICHAR *delimLoc, *ustr, *ustr2;
|
||
|
|
||
|
LTERM_LOG(ltermMetaInput,40,("\n"));
|
||
|
|
||
|
if (lts->options & LTERM_NOMETA_FLAG)
|
||
|
return 0;
|
||
|
|
||
|
/* Assert that there is at least one free character position in the buffer */
|
||
|
assert(lti->inputChars < MAXCOL);
|
||
|
|
||
|
/* Insert null character at the end of the input buffer */
|
||
|
lti->inputLine[lti->inputChars] = U_NUL;
|
||
|
|
||
|
/* Locate first occurrence of meta delimiter in input line */
|
||
|
delimLoc = ucschr(lti->inputLine, ltermGlobal.metaDelimiter);
|
||
|
|
||
|
if (delimLoc == NULL)
|
||
|
return 0;
|
||
|
|
||
|
for (ustr=lti->inputLine; ustr<delimLoc; ustr++) /* skip spaces/TABs */
|
||
|
if ((*ustr != U_SPACE) && (*ustr != U_TAB)) break;
|
||
|
|
||
|
if (ustr == delimLoc) {
|
||
|
/* Nameless meta command */
|
||
|
LTERM_LOG(ltermMetaInput,41,("Nameless meta command\n"));
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
if (!IS_ASCII_LETTER(*ustr)) /* meta command must start with a letter */
|
||
|
return 0;
|
||
|
|
||
|
for (ustr2=ustr+1; ustr2<delimLoc; ustr2++)
|
||
|
if (!IS_ASCII_LETTER(*ustr2) && !IS_ASCII_DIGIT(*ustr2))
|
||
|
return 0;
|
||
|
|
||
|
LTERM_LOG(ltermMetaInput,41,("Named meta command\n"));
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** Requests command line completion from process corresponding to
|
||
|
* control character UCH.
|
||
|
* @return 0 on success,
|
||
|
* -1 on error
|
||
|
*/
|
||
|
static int ltermRequestCompletion(struct lterms *lts, UNICHAR uch)
|
||
|
{
|
||
|
LTERM_LOG(ltermRequestCompletion,40,
|
||
|
("++++++++++++ COMPLETION REQUEST uch=0x%X\n\n", uch));
|
||
|
|
||
|
switch (uch) {
|
||
|
case U_TAB:
|
||
|
/* Send line and copy to echo buffer */
|
||
|
if (ltermSendLine(lts, uch, 0, LTERM_TAB_COMPLETION) != 0)
|
||
|
return -1;
|
||
|
break;
|
||
|
case U_CTL_P:
|
||
|
case U_CTL_N:
|
||
|
/* Send line and copy to echo buffer */
|
||
|
if (ltermSendLine(lts, uch, 0, LTERM_HISTORY_COMPLETION) != 0)
|
||
|
return -1;
|
||
|
break;
|
||
|
default:
|
||
|
LTERM_WARNING("ltermCompletionRequest: Warning - command completion not yet implemented for uch=0x%x\n", uch);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** Deletes glyphs from the input line.
|
||
|
* If COUNT > 0, glyphs are deleted to the left of the cursor.
|
||
|
* If COUNT < 0, glyphs are deleted to the right of the cursor.
|
||
|
* Called from ltermLineInput.
|
||
|
* @return 0 on success, -1 on error.
|
||
|
*/
|
||
|
int ltermDeleteGlyphs(struct LtermInput *lti, int count)
|
||
|
{
|
||
|
int leftGlyph, leftColIndex, leftCharIndex;
|
||
|
int rightGlyph, rightColIndex, rightCharIndex;
|
||
|
int deleteGlyphs, deleteCols, deleteChars, j;
|
||
|
|
||
|
LTERM_LOG(ltermDeleteGlyphs,40,("deleting %d glyphs from glyph %d\n",
|
||
|
count, lti->inputCursorGlyph));
|
||
|
|
||
|
if (count >= 0) {
|
||
|
/* Delete to the left */
|
||
|
deleteGlyphs = count;
|
||
|
|
||
|
/* Limit the number of glyphs deleted to that present to the left */
|
||
|
if (deleteGlyphs > lti->inputCursorGlyph)
|
||
|
deleteGlyphs = lti->inputCursorGlyph;
|
||
|
|
||
|
rightGlyph = lti->inputCursorGlyph;
|
||
|
leftGlyph = rightGlyph - deleteGlyphs;
|
||
|
|
||
|
} else {
|
||
|
/* Delete to the right */
|
||
|
deleteGlyphs = -count;
|
||
|
|
||
|
/* Limit the number of glyphs deleted to that present to the right */
|
||
|
if (deleteGlyphs > (lti->inputGlyphs - lti->inputCursorGlyph))
|
||
|
deleteGlyphs = lti->inputGlyphs - lti->inputCursorGlyph;
|
||
|
|
||
|
leftGlyph = lti->inputCursorGlyph;
|
||
|
rightGlyph = leftGlyph + deleteGlyphs;
|
||
|
}
|
||
|
|
||
|
leftColIndex = lti->inputGlyphColIndex[leftGlyph];
|
||
|
leftCharIndex = lti->inputGlyphCharIndex[leftGlyph];
|
||
|
|
||
|
rightColIndex = lti->inputGlyphColIndex[rightGlyph];
|
||
|
rightCharIndex = lti->inputGlyphCharIndex[rightGlyph];
|
||
|
|
||
|
deleteCols = rightColIndex - leftColIndex;
|
||
|
deleteChars = rightCharIndex - leftCharIndex;
|
||
|
|
||
|
LTERM_LOG(ltermDeleteGlyphs,41,("deleteCols=%d, deleteChars=%d\n",
|
||
|
deleteCols, deleteChars));
|
||
|
|
||
|
LTERM_LOG(ltermDeleteGlyphs,42,("leftGlyph=%d, leftCol=%d, leftChar=%d\n",
|
||
|
leftGlyph, leftColIndex, leftCharIndex));
|
||
|
|
||
|
LTERM_LOG(ltermDeleteGlyphs,42,("rightGlyph=%d, rightCol=%d, rightChar=%d\n",
|
||
|
rightGlyph, rightColIndex, rightCharIndex));
|
||
|
|
||
|
/* Shift portion of input line to the left;
|
||
|
remember that the column/glyph index arrays have an extra element */
|
||
|
for (j = leftCharIndex; j < lti->inputChars-deleteChars; j++)
|
||
|
lti->inputLine[j] = lti->inputLine[j+deleteChars];
|
||
|
|
||
|
for (j = leftColIndex; j <= lti->inputCols-deleteCols; j++)
|
||
|
lti->inputColCharIndex[j] = lti->inputColCharIndex[j+deleteCols]
|
||
|
- deleteChars;
|
||
|
|
||
|
for (j = leftGlyph; j <= lti->inputGlyphs-deleteGlyphs; j++)
|
||
|
lti->inputGlyphColIndex[j] = lti->inputGlyphColIndex[j+deleteGlyphs]
|
||
|
- deleteCols;
|
||
|
|
||
|
lti->inputChars -= deleteChars; /* Decrement character count */
|
||
|
|
||
|
lti->inputCols -= deleteCols; /* Decrement column count */
|
||
|
|
||
|
lti->inputGlyphs -= deleteGlyphs; /* Decrement glyph count */
|
||
|
|
||
|
if (count > 0)
|
||
|
lti->inputCursorGlyph -= deleteGlyphs; /* Reposition glyph cursor */
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** Transmits COUNT Unicode characters from BUF to child process
|
||
|
* after translating Unicode to UTF8 or Latin1, as appropriate.
|
||
|
* The data is transmitted in smallish chunks so as not to overflow the
|
||
|
* PTY input buffer.
|
||
|
* @return 0 on successful write, -1 on error.
|
||
|
*/
|
||
|
int ltermSendData(struct lterms *lts, const UNICHAR *buf, int count)
|
||
|
{
|
||
|
char ch, ptyBuf[MAXPTYIN];
|
||
|
int remainingChars, chunkSize, success;
|
||
|
|
||
|
assert(lts != NULL);
|
||
|
assert(count >= 0);
|
||
|
|
||
|
LTERM_LOG(ltermSendData,40,("count=%d\n", count));
|
||
|
LTERM_LOGUNICODE(ltermSendData,41,(buf, count));
|
||
|
|
||
|
if ((count == 1) && (*buf < 0x80)) {
|
||
|
/* Optimized code to transmit single ASCII character */
|
||
|
ch = (char) *buf;
|
||
|
|
||
|
if (lts->ptyMode)
|
||
|
#ifndef USE_NSPR_IO
|
||
|
success = (write(lts->pty.ptyFD, &ch, 1) == 1);
|
||
|
#else
|
||
|
assert(0);
|
||
|
#endif
|
||
|
else
|
||
|
success = (WRITE(lts->ltermProcess.processIN, &ch, 1) == 1);
|
||
|
|
||
|
if (!success) {
|
||
|
#if defined(DEBUG_LTERM) && !defined(USE_NSPR_IO)
|
||
|
int errcode = errno;
|
||
|
perror("ltermSendData");
|
||
|
#else
|
||
|
int errcode = 0;
|
||
|
#endif
|
||
|
LTERM_ERROR("ltermSendData: Error %d in writing to child STDIN\n",
|
||
|
errcode);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
remainingChars = count;
|
||
|
while (remainingChars > 0) {
|
||
|
/* Convert Unicode to UTF8 */
|
||
|
ucstoutf8(&buf[count-remainingChars], remainingChars,
|
||
|
ptyBuf, MAXPTYIN,
|
||
|
&remainingChars, &chunkSize);
|
||
|
|
||
|
assert(chunkSize > 0);
|
||
|
|
||
|
LTERM_LOG(ltermSendData,42,("remainingChars=%d, chunkSize=%d\n",
|
||
|
remainingChars, chunkSize));
|
||
|
|
||
|
/* Send UTF8 to process */
|
||
|
if (ltermSendChar(lts, ptyBuf, chunkSize) != 0)
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** Transmits COUNT characters from BUF to child process.
|
||
|
* @return 0 on successful write, -1 on error.
|
||
|
*/
|
||
|
int ltermSendChar(struct lterms *lts, const char *buf, int count)
|
||
|
{
|
||
|
int success;
|
||
|
|
||
|
LTERM_LOG(ltermSendChar,50,("count=%d\n", count));
|
||
|
|
||
|
if (lts->ptyMode)
|
||
|
#ifndef USE_NSPR_IO
|
||
|
success = (write(lts->pty.ptyFD, buf,
|
||
|
(SIZE_T) count) == count);
|
||
|
#else
|
||
|
assert(0);
|
||
|
#endif
|
||
|
else
|
||
|
success = (WRITE(lts->ltermProcess.processIN, buf,
|
||
|
(SIZE_T) count) == count);
|
||
|
|
||
|
if (!success) {
|
||
|
#if defined(DEBUG_LTERM) && !defined(USE_NSPR_IO)
|
||
|
int errcode = errno;
|
||
|
perror("ltermSendChar");
|
||
|
#else
|
||
|
int errcode = 0;
|
||
|
#endif
|
||
|
LTERM_ERROR("ltermSendChar: Error %d in writing to child STDIN\n",
|
||
|
errcode);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|