001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: *
041: * Contributor(s): Ivan Soleimanipour.
042: */
043:
044: /*
045: * "LineDiscipline.java"
046: * LineDiscipline.java 1.8 01/07/10
047: */
048:
049: package org.netbeans.lib.terminalemulator;
050:
051: /**
052: * Do the sort of stuff pty's normally do:
053: * <ul>
054: * <li> echoing
055: * <li> CR/NL mappings
056: * <li> BS processing
057: * <li> Line buffering.
058: * </ul>
059: * <p>
060: * Currently the settings are hardcoded to simulate a pty setup for running
061: * shells.
062: * <p>
063: * This class is not complete by any means and is merely an example of
064: * a TermStream. Things that it might do:
065: * <ul>
066: * <li> TAB processing
067: * <li> conversion of control characters to "signals".
068: * </ul>
069: */
070:
071: public class LineDiscipline extends TermStream {
072:
073: private static final char bs_sequence[] = { (char) 8, (char) 32,
074: (char) 8 };
075:
076: // input line main buffer
077: private StringBuffer line = new StringBuffer();
078:
079: // auto-growing buffer for sending lines accumulated in 'line'.
080: private int send_buf_sz = 2;
081: private char send_buf[] = new char[send_buf_sz];
082:
083: char[] send_buf(int n) {
084: if (n >= send_buf_sz) {
085: send_buf_sz = n + 1;
086: send_buf = new char[send_buf_sz];
087: }
088: return send_buf;
089: }
090:
091: // buffer for processing incoming characters
092: private int put_capacity = 16;
093: private int put_length = 0;
094: private char put_buf[] = new char[put_capacity];
095:
096: public void flush() {
097: toDTE.flush();
098: }
099:
100: public void putChar(char c) {
101: // Even though we dealing with one character, as the processing on it
102: // may get more complicated we will want to use the code factored in
103: // processChar()
104:
105: // reset buffer
106: put_length = 0;
107:
108: // fill it
109: processChar(c);
110:
111: // flush it
112: toDTE.putChars(put_buf, 0, put_length);
113: }
114:
115: public void putChars(char buf[], int offset, int count) {
116:
117: // reset buffer
118: put_length = 0;
119:
120: // fill it
121: for (int bx = 0; bx < count; bx++)
122: processChar(buf[offset + bx]);
123:
124: // flush it
125: toDTE.putChars(put_buf, 0, put_length);
126: }
127:
128: private void processChar(char c) {
129: // Any actual mapping and processing gets done here
130: appendChar(c);
131:
132: // Map NL to NLCR *stty onlcr)
133: if (c == 10)
134: appendChar((char) 13);
135: }
136:
137: private void appendChar(char c) {
138:
139: // Play StringBuffer
140:
141: if (put_length >= put_capacity) {
142: int new_capacity = put_capacity * 2;
143: if (new_capacity < 0)
144: new_capacity = Integer.MAX_VALUE;
145: char new_buf[] = new char[new_capacity];
146: System.arraycopy(put_buf, 0, new_buf, 0, put_length);
147: put_buf = new_buf;
148: put_capacity = new_capacity;
149: }
150:
151: put_buf[put_length++] = c;
152: }
153:
154: public void sendChar(char c) {
155: // keystroke -> world (DCE)
156:
157: // map CR to NL (stty icrnl)
158: if (c == 13) {
159: toDTE.putChar(c); // echo
160: toDTE.flush();
161:
162: c = (char) 10;
163: toDTE.putChar(c); // echo the newline too
164: toDTE.flush();
165:
166: line.append(c);
167:
168: int nchars = line.length();
169: char[] tmp = send_buf(nchars);
170: line.getChars(0, nchars, tmp, 0);
171: toDCE.sendChars(tmp, 0, nchars);
172: line.delete(0, 99999); // clear the line
173:
174: } else if (c == 10) {
175: toDTE.putChar((char) 13); // echo carriage return too
176: toDTE.flush();
177:
178: toDTE.putChar(c); // echo
179: toDTE.flush();
180:
181: line.append(c);
182:
183: int nchars = line.length();
184: char[] tmp = send_buf(nchars);
185: line.getChars(0, nchars, tmp, 0);
186: toDCE.sendChars(tmp, 0, nchars);
187: line.delete(0, 99999); // clear the line
188:
189: } else if (c == 8) {
190: // BS
191: int nchars = line.length();
192:
193: if (nchars == 0)
194: return; // nothing left to BS over
195:
196: char erased_char = ' '; // The char we're going to erase
197: try {
198: erased_char = line.charAt(nchars - 1);
199: } catch (Exception x) {
200: return; // apparently the 'nchars == 0' test failed above ;-)
201: }
202: int cwidth = getTerm().charWidth(erased_char);
203:
204: // remove from line buffer
205: line.delete(nchars - 1, nchars);
206:
207: // HACK
208: // If you play a bit with DtTerm on Solaris in a non-latin locale
209: // you'll see that when you BS over a multi-cell character it
210: // doesn't erase the whole character. The character is erased but the
211: // cursor moves back only one column. So you usually need to BS twice
212: // to get rid of it. If you "fix" Term to do something more reasonable
213: // you'll find out that as you backspace you'll run over the cursor.
214: // that's because the kernel linebuffer accounting assumes the above setup.
215: // I"m not sure how all of this came about but we have to mimic similar
216: // acounting and we do it by padding the buffer (only) with a bunch of spaces.
217: //
218: // NOTE: There are two strong invariants you have to keep in mind:
219: // - Solaris, and I assume other unixes, stick to the BS-SP-BS echo
220: // even though they seem to know about character widths.
221: // - BS from Term's point of view is _strictly_ a cursor motion operation!
222: // The fact that it erases things has to do with the line discipline
223: // (kernels or this class 'ere)
224: //
225: // Now I know non-unix people will want BS to behave sanely in non-unix
226: // environments, so perhapws we SHOULD have a property to control whether
227: // things get erased the unix way or some other way.
228:
229: while (--cwidth > 0)
230: line.append(' ');
231:
232: // erase character on screen
233: toDTE.putChars(bs_sequence, 0, 3);
234: toDTE.flush();
235:
236: } else {
237: toDTE.putChar(c); // echo
238: toDTE.flush();
239: line.append(c);
240: }
241: }
242:
243: public void sendChars(char c[], int offset, int count) {
244: for (int cx = 0; cx < count; cx++)
245: sendChar(c[offset + cx]);
246: }
247: }
|