1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.net.io;
19
20 import java.io.FilterInputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23
24 import org.apache.commons.net.util.NetConstants;
25
26 /**
27 * This class wraps an input stream, replacing all singly occurring <LF> (linefeed) characters with <CR><LF> (carriage return followed by
28 * linefeed), which is the NETASCII standard for representing a newline. You would use this class to implement ASCII file transfers requiring conversion to
29 * NETASCII.
30 */
31 public final class ToNetASCIIInputStream extends FilterInputStream {
32 private static final int NOTHING_SPECIAL = 0;
33 private static final int LAST_WAS_CR = 1;
34 private static final int LAST_WAS_NL = 2;
35 private int status;
36
37 /**
38 * Creates a ToNetASCIIInputStream instance that wraps an existing InputStream.
39 *
40 * @param input The InputStream to wrap.
41 */
42 public ToNetASCIIInputStream(final InputStream input) {
43 super(input);
44 status = NOTHING_SPECIAL;
45 }
46
47 @Override
48 public int available() throws IOException {
49 final int result;
50
51 result = in.available();
52
53 if (status == LAST_WAS_NL) {
54 return result + 1;
55 }
56
57 return result;
58 }
59
60 /** Returns false. Mark is not supported. */
61 @Override
62 public boolean markSupported() {
63 return false;
64 }
65
66 /**
67 * Reads and returns the next byte in the stream. If the end of the message has been reached, returns -1.
68 *
69 * @return The next character in the stream. Returns -1 if the end of the stream has been reached.
70 * @throws IOException If an error occurs while reading the underlying stream.
71 */
72 @Override
73 public int read() throws IOException {
74 final int ch;
75
76 if (status == LAST_WAS_NL) {
77 status = NOTHING_SPECIAL;
78 return '\n';
79 }
80
81 ch = in.read();
82
83 switch (ch) {
84 case '\r':
85 status = LAST_WAS_CR;
86 return '\r';
87 case '\n':
88 if (status != LAST_WAS_CR) {
89 status = LAST_WAS_NL;
90 return '\r';
91 }
92 // falls through$
93 default:
94 status = NOTHING_SPECIAL;
95 return ch;
96 }
97 // statement not reached
98 // return ch;
99 }
100
101 /**
102 * Reads the next number of bytes from the stream into an array and returns the number of bytes read. Returns -1 if the end of the stream has been reached.
103 *
104 * @param buffer The byte array in which to store the data.
105 * @return The number of bytes read. Returns -1 if the end of the message has been reached.
106 * @throws IOException If an error occurs in reading the underlying stream.
107 */
108 @Override
109 public int read(final byte[] buffer) throws IOException {
110 return read(buffer, 0, buffer.length);
111 }
112
113 /**
114 * Reads the next number of bytes from the stream into an array and returns the number of bytes read. Returns -1 if the end of the message has been reached.
115 * The characters are stored in the array starting from the given offset and up to the length specified.
116 *
117 * @param buffer The byte array in which to store the data.
118 * @param offset The offset into the array at which to start storing data.
119 * @param length The number of bytes to read.
120 * @return The number of bytes read. Returns -1 if the end of the stream has been reached.
121 * @throws IOException If an error occurs while reading the underlying stream.
122 */
123 @Override
124 public int read(final byte[] buffer, int offset, int length) throws IOException {
125 int ch;
126 final int off;
127
128 if (length < 1) {
129 return 0;
130 }
131
132 ch = available();
133
134 if (length > ch) {
135 length = ch;
136 }
137
138 // If nothing is available, block to read only one character
139 if (length < 1) {
140 length = 1;
141 }
142
143 if ((ch = read()) == NetConstants.EOS) {
144 return NetConstants.EOS;
145 }
146
147 off = offset;
148
149 do {
150 buffer[offset++] = (byte) ch;
151 } while (--length > 0 && (ch = read()) != NetConstants.EOS);
152
153 return offset - off;
154 }
155 }