98727277a8b47e6a5397d3ac3e77e131424edce8
2 * Jexer - Java Text User Interface
4 * License: LGPLv3 or later
6 * This module is licensed under the GNU Lesser General Public License
7 * Version 3. Please see the file "COPYING" in this directory for more
8 * information about the GNU Lesser General Public License Version 3.
10 * Copyright (C) 2015 Kevin Lamonte
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public License
14 * as published by the Free Software Foundation; either version 3 of
15 * the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this program; if not, see
24 * http://www.gnu.org/licenses/, or write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
28 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
33 import java
.io
.OutputStream
;
34 import java
.io
.IOException
;
37 * TelnetOutputStream works with TelnetSocket to perform the telnet protocol.
39 public final class TelnetOutputStream
extends OutputStream
{
42 * The root TelnetSocket that has my telnet protocol state.
44 private TelnetSocket master
;
47 * The raw socket's OutputStream.
49 private OutputStream output
;
52 * Package private constructor.
54 * @param master the master TelnetSocket
55 * @param output the underlying socket's OutputStream
57 TelnetOutputStream(TelnetSocket master
, OutputStream output
) {
62 // OutputStream interface -------------------------------------------------
65 * Closes this output stream and releases any system resources associated
69 public void close() throws IOException
{
77 * Flushes this output stream and forces any buffered output bytes to be
81 public void flush() throws IOException
{
82 if ((master
.nvt
.binaryMode
== false) && (master
.nvt
.writeCR
== true)) {
83 // The last byte sent to this.write() was a CR, which was never
84 // actually sent. So send the CR in ascii mode, then flush.
85 // CR <anything> -> CR NULL
86 output
.write(master
.C_CR
);
87 output
.write(master
.C_NUL
);
88 master
.nvt
.writeCR
= false;
94 * Writes b.length bytes from the specified byte array to this output
98 public void write(byte[] b
) throws IOException
{
99 writeImpl(b
, 0, b
.length
);
103 * Writes len bytes from the specified byte array starting at offset off
104 * to this output stream.
107 public void write(byte[] b
, int off
, int len
) throws IOException
{
108 writeImpl(b
, off
, len
);
112 * Writes the specified byte to this output stream.
115 public void write(int b
) throws IOException
{
116 byte [] bytes
= new byte[1];
118 writeImpl(bytes
, 0, 1);
122 * Writes b.length bytes from the specified byte array to this output
123 * stream. Note package private access.
125 void rawWrite(byte[] b
) throws IOException
{
126 output
.write(b
, 0, b
.length
);
130 * Writes len bytes from the specified byte array starting at offset off
131 * to this output stream.
133 private void writeImpl(final byte[] b
, final int off
,
134 final int len
) throws IOException
{
136 byte [] writeBuffer
= new byte[Math
.max(len
, 4)];
137 int writeBufferI
= 0;
139 for (int i
= 0; i
< len
; i
++) {
140 if (writeBufferI
>= writeBuffer
.length
- 4) {
141 // Flush what we have generated so far and reset the buffer,
142 // because the next byte could generate up to 4 output bytes
143 // (CR <something> <IAC> <IAC>).
144 super.write(writeBuffer
, 0, writeBufferI
);
148 // Pull the next byte
149 byte ch
= b
[i
+ off
];
151 if (master
.nvt
.binaryMode
== true) {
153 if (ch
== master
.TELNET_IAC
) {
155 writeBuffer
[writeBufferI
++] = (byte)master
.TELNET_IAC
;
156 writeBuffer
[writeBufferI
++] = (byte)master
.TELNET_IAC
;
158 // Anything else -> just send
159 writeBuffer
[writeBufferI
++] = ch
;
164 // Non-binary mode: more complicated. We use writeCR to handle
165 // the case that the last byte of b was a CR.
167 // Bare carriage return -> CR NUL
168 if (ch
== master
.C_CR
) {
169 if (master
.nvt
.writeCR
== true) {
170 // Flush the previous CR to the stream.
171 // CR <anything> -> CR NULL
172 writeBuffer
[writeBufferI
++] = (byte)master
.C_CR
;
173 writeBuffer
[writeBufferI
++] = (byte)master
.C_NUL
;
175 master
.nvt
.writeCR
= true;
176 } else if (ch
== master
.C_LF
) {
177 if (master
.nvt
.writeCR
== true) {
179 writeBuffer
[writeBufferI
++] = (byte)master
.C_CR
;
180 writeBuffer
[writeBufferI
++] = (byte)master
.C_LF
;
181 master
.nvt
.writeCR
= false;
184 writeBuffer
[writeBufferI
++] = ch
;
186 } else if (ch
== master
.TELNET_IAC
) {
187 if (master
.nvt
.writeCR
== true) {
188 // CR <anything> -> CR NULL
189 writeBuffer
[writeBufferI
++] = (byte)master
.C_CR
;
190 writeBuffer
[writeBufferI
++] = (byte)master
.C_NUL
;
191 master
.nvt
.writeCR
= false;
194 writeBuffer
[writeBufferI
++] = (byte)master
.TELNET_IAC
;
195 writeBuffer
[writeBufferI
++] = (byte)master
.TELNET_IAC
;
197 if (master
.nvt
.writeCR
== true) {
198 // CR <anything> -> CR NULL
199 writeBuffer
[writeBufferI
++] = (byte)master
.C_CR
;
200 writeBuffer
[writeBufferI
++] = (byte)master
.C_NUL
;
201 master
.nvt
.writeCR
= false;
203 // Normal character */
204 writeBuffer
[writeBufferI
++] = ch
;
208 } // while (i < userbuf.length)
210 if (writeBufferI
> 0) {
211 // Flush what we have generated so far and reset the buffer.
212 super.write(writeBuffer
, 0, writeBufferI
);