blob: 342a15bf832393c08eecbe5afaa093546ed15b77 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* "LineDiscipline.java"
* LineDiscipline.java 1.8 01/07/10
*/
package org.netbeans.lib.terminalemulator;
/**
* Do the sort of stuff pty's normally do:
* <ul>
* <li> echoing
* <li> CR/NL mappings
* <li> BS processing
* <li> Line buffering.
* </ul>
* <p>
* Currently the settings are hardcoded to simulate a pty setup for running
* shells.
* <p>
* This class is not complete by any means and is merely an example of
* a TermStream. Things that it might do:
* <ul>
* <li> TAB processing
* <li> conversion of control characters to "signals".
* </ul>
*/
public class LineDiscipline extends TermStream {
private static final char bs_sequence[] = {(char)8, (char)32, (char)8};
// input line main buffer
private final StringBuffer line = new StringBuffer();
// auto-growing buffer for sending lines accumulated in 'line'.
private int send_buf_sz = 2;
private char send_buf[] = new char[send_buf_sz];
char [] send_buf(int n) {
if (n >= send_buf_sz) {
send_buf_sz = n+1;
send_buf = new char[send_buf_sz];
}
return send_buf;
}
// buffer for processing incoming characters
private int put_capacity = 16;
private int put_length = 0;
private char put_buf[] = new char[put_capacity];
@Override
public void flush() {
toDTE.flush();
}
@Override
public void putChar(char c) {
// Even though we dealing with one character, as the processing on it
// may get more complicated we will want to use the code factored in
// processChar()
// reset buffer
put_length = 0;
// fill it
processChar(c);
// flush it
toDTE.putChars(put_buf, 0, put_length);
}
@Override
public void putChars(char buf[], int offset, int count) {
// reset buffer
put_length = 0;
// fill it
for (int bx = 0; bx < count; bx++)
processChar(buf[offset+bx]);
// flush it
toDTE.putChars(put_buf, 0, put_length);
}
private void processChar(char c) {
// Any actual mapping and processing gets done here
appendChar(c);
// Map NL to NLCR *stty onlcr)
if (c == 10)
appendChar((char) 13);
}
private void appendChar(char c) {
// Play StringBuffer
if (put_length >= put_capacity) {
int new_capacity = put_capacity * 2;
if (new_capacity < 0)
new_capacity = Integer.MAX_VALUE;
char new_buf[] = new char[new_capacity];
System.arraycopy(put_buf, 0, new_buf, 0, put_length);
put_buf = new_buf;
put_capacity = new_capacity;
}
put_buf[put_length++] = c;
}
@Override
@SuppressWarnings({"AssignmentToMethodParameter", "ValueOfIncrementOrDecrementUsed"})
public void sendChar(char c) {
// keystroke -> world (DCE)
// map CR to NL (stty icrnl)
if (c == 13) {
toDTE.putChar(c); // echo
toDTE.flush();
c = (char) 10;
toDTE.putChar(c); // echo the newline too
toDTE.flush();
line.append(c);
int nchars = line.length();
char [] tmp = send_buf(nchars);
line.getChars(0, nchars, tmp, 0);
toDCE.sendChars(tmp, 0, nchars);
line.delete(0, 99999); // clear the line
} else if (c == 10) {
toDTE.putChar((char) 13); // echo carriage return too
toDTE.flush();
toDTE.putChar(c); // echo
toDTE.flush();
line.append(c);
int nchars = line.length();
char [] tmp = send_buf(nchars);
line.getChars(0, nchars, tmp, 0);
toDCE.sendChars(tmp, 0, nchars);
line.delete(0, 99999); // clear the line
} else if (c == 8) {
// BS
int nchars = line.length();
if (nchars == 0)
return; // nothing left to BS over
char erased_char; // The char we're going to erase
try {
erased_char = line.charAt(nchars-1);
} catch (Exception x) {
return; // apparently the 'nchars == 0' test failed above ;-)
}
int cwidth = getTerm().charWidth(erased_char);
// remove from line buffer
line.delete(nchars-1, nchars);
// HACK
// If you play a bit with DtTerm on Solaris in a non-latin locale
// you'll see that when you BS over a multi-cell character it
// doesn't erase the whole character. The character is erased but the
// cursor moves back only one column. So you usually need to BS twice
// to get rid of it. If you "fix" Term to do something more reasonable
// you'll find out that as you backspace you'll run over the cursor.
// that's because the kernel linebuffer accounting assumes the above setup.
// I"m not sure how all of this came about but we have to mimic similar
// acounting and we do it by padding the buffer (only) with a bunch of spaces.
//
// NOTE: There are two strong invariants you have to keep in mind:
// - Solaris, and I assume other unixes, stick to the BS-SP-BS echo
// even though they seem to know about character widths.
// - BS from Term's point of view is _strictly_ a cursor motion operation!
// The fact that it erases things has to do with the line discipline
// (kernels or this class 'ere)
//
// Now I know non-unix people will want BS to behave sanely in non-unix
// environments, so perhapws we SHOULD have a property to control whether
// things get erased the unix way or some other way.
while(--cwidth > 0 ) {
line.append(' ');
}
// erase character on screen
toDTE.putChars(bs_sequence, 0, 3);
toDTE.flush();
} else {
toDTE.putChar(c); // echo
toDTE.flush();
line.append(c);
}
}
@Override
public void sendChars(char c[], int offset, int count) {
for (int cx = 0; cx < count; cx++)
sendChar(c[offset+cx]);
}
}