1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package gnu.inet.ftp;
21
22 import java.io.*;
23
24 /***
25 * The <code>AsciiInputStream</code> class acts as a filter to convert incoming
26 * ASCII FTP streams to the system's local ASCII format by filtering and
27 * converting line termination strings. Note that the class as currently
28 * written will only handle <code>\r\n</code> or single character line
29 * termination strings; a more technically correct implementation would be
30 * preferable.
31 */
32 public class AsciiInputStream extends FilterInputStream {
33
34 protected boolean active;
35 protected int eol;
36 protected int buffer;
37 protected int markBuffer;
38
39 /***
40 * Creates an AsciiInputStream by passing <code>in</code> to its superclass'
41 * constructor and determining the system-specific line termination string.
42 *
43 * @param in the underlying input stream.
44 *
45 * @throws Exception if system line separator is longer than 1 char and is
46 * not <code>\r\n</code>
47 */
48 public AsciiInputStream(InputStream in) throws Exception {
49 super(in);
50
51 String lineSeparator = System.getProperty("line.separator");
52 if (lineSeparator.equals("\r\n")) {
53 active = false;
54 } else if (lineSeparator.length() > 1) {
55 throw new Exception("System line separator longer than 1 char");
56 } else {
57 active = true;
58 eol = lineSeparator.charAt(0);
59 buffer = -1;
60 }
61 }
62
63 /***
64 * Reads the next byte of data.
65 *
66 * @return the next byte of data, or -1 for EOF
67 * @throws IOException if an I/O error occurs.
68 */
69 public int read() throws IOException {
70 if (!active)
71 return in.read();
72
73 int cur;
74
75 if (buffer < 0) {
76 cur = in.read();
77 } else {
78 cur = buffer;
79 buffer = -1;
80 }
81
82 if (cur != '\r') {
83 return cur;
84 } else if ((buffer = in.read()) != '\n') {
85 return cur;
86 } else {
87 buffer = -1;
88 return eol;
89 }
90 }
91
92 /***
93 * Reads up to <code>byte.length</code> bytes of data from this input stream
94 * into an array of bytes.
95 *
96 * @param b the buffer into which the data is read.
97 *
98 * @return the total number of bytes read into the buffer, or -1 for EOF.
99 * @throws IOException if an I/O error occurs.
100 */
101 public int read(byte[] b) throws IOException {
102 return read(b, 0, b.length);
103 }
104
105 /***
106 * Reads up to <code>len</code> bytes of data from this input stream into an
107 * array of bytes.
108 *
109 * @param b the buffer into which the data is read.
110 * @param off the start offset of the data.
111 * @param len the maximum number of bytes read.
112 *
113 * @return the total number of bytes read into the buffer, or -1 for EOF.
114 * @throws IOException if an I/O error occurs.
115 */
116 public int read(byte[] b, int off, int len) throws IOException {
117 if (!active)
118 return in.read(b, off, len);
119
120 if (off < 0 || len < 0 || off+len > b.length)
121 throw new IndexOutOfBoundsException();
122
123 if (len == 0)
124 return 0;
125
126 int cur = read();
127 if (cur < 0)
128 return 0;
129
130 b[off] = (byte)cur;
131 int count = 1;
132
133 while (count < len) {
134 try {
135 cur = read();
136 } catch (IOException e) {
137 cur = -1;
138 }
139
140 if (cur < 0)
141 return count;
142
143 b[off+count] = (byte)cur;
144 count++;
145 }
146
147 return count;
148 }
149
150 /***
151 * Skips over and discards n bytes of data from the input stream.
152 *
153 * @param n the number of bytes to be skipped.
154 *
155 * @return the actual number of bytes skipped.
156 * @throws IOException if an I/O error occurs.
157 */
158 public long skip(long n) throws IOException {
159 if (n <= 0L)
160 return 0;
161
162 if (!active || buffer < 0)
163 return in.skip(n);
164
165 buffer = -1;
166 return 1L + in.skip(n - 1L);
167 }
168
169 /***
170 * Returns the number of bytes that can be read from this input stream
171 * without blocking.
172 *
173 * @return the number of bytes that can be read from the input stream
174 * without blocking.
175 * @throws IOException if an I/O error occurs.
176 */
177 public int available() throws IOException {
178 if (!active || buffer < 0)
179 return in.available();
180
181 return 1 + in.available();
182 }
183
184 /***
185 * Marks the current position in this input stream.
186 *
187 * @param readlimit the maximum limit of bytes that can be read before the
188 * mark position becomes invalid.
189 */
190 public void mark(int readlimit) {
191 if (readlimit <= 0)
192 return;
193
194 if (!active) {
195 in.mark(readlimit);
196 return;
197 }
198
199 markBuffer = buffer;
200 in.mark(readlimit - 1);
201 }
202
203 /***
204 * Repositions this stream to the position at the time the mark method was
205 * last called on this input stream.
206 *
207 * @throws IOException if the stream has not been or cannot be marked or
208 * if the mark has been invalidated.
209 */
210 public void reset() throws IOException {
211 if (!active) {
212 in.reset();
213 return;
214 }
215
216 if (!in.markSupported())
217 throw new IOException();
218
219 buffer = markBuffer;
220 in.reset();
221 }
222
223 }