001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.net.io;
019
020import java.io.FilterInputStream;
021import java.io.IOException;
022import java.io.InputStream;
023
024import org.apache.commons.net.util.NetConstants;
025
026/**
027 * This class wraps an input stream, replacing all singly occurring <LF> (linefeed) characters with <CR><LF> (carriage return followed by
028 * linefeed), which is the NETASCII standard for representing a newline. You would use this class to implement ASCII file transfers requiring conversion to
029 * NETASCII.
030 */
031
032public final class ToNetASCIIInputStream extends FilterInputStream {
033    private static final int NOTHING_SPECIAL = 0;
034    private static final int LAST_WAS_CR = 1;
035    private static final int LAST_WAS_NL = 2;
036    private int status;
037
038    /**
039     * Creates a ToNetASCIIInputStream instance that wraps an existing InputStream.
040     *
041     * @param input The InputStream to wrap.
042     */
043    public ToNetASCIIInputStream(final InputStream input) {
044        super(input);
045        status = NOTHING_SPECIAL;
046    }
047
048    @Override
049    public int available() throws IOException {
050        final int result;
051
052        result = in.available();
053
054        if (status == LAST_WAS_NL) {
055            return result + 1;
056        }
057
058        return result;
059    }
060
061    /** Returns false. Mark is not supported. */
062    @Override
063    public boolean markSupported() {
064        return false;
065    }
066
067    /**
068     * Reads and returns the next byte in the stream. If the end of the message has been reached, returns -1.
069     *
070     * @return The next character in the stream. Returns -1 if the end of the stream has been reached.
071     * @throws IOException If an error occurs while reading the underlying stream.
072     */
073    @Override
074    public int read() throws IOException {
075        final int ch;
076
077        if (status == LAST_WAS_NL) {
078            status = NOTHING_SPECIAL;
079            return '\n';
080        }
081
082        ch = in.read();
083
084        switch (ch) {
085        case '\r':
086            status = LAST_WAS_CR;
087            return '\r';
088        case '\n':
089            if (status != LAST_WAS_CR) {
090                status = LAST_WAS_NL;
091                return '\r';
092            }
093            //$FALL-THROUGH$
094        default:
095            status = NOTHING_SPECIAL;
096            return ch;
097        }
098        // statement not reached
099        // return ch;
100    }
101
102    /**
103     * 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.
104     *
105     * @param buffer The byte array in which to store the data.
106     * @return The number of bytes read. Returns -1 if the end of the message has been reached.
107     * @throws IOException If an error occurs in reading the underlying stream.
108     */
109    @Override
110    public int read(final byte[] buffer) throws IOException {
111        return read(buffer, 0, buffer.length);
112    }
113
114    /**
115     * 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.
116     * The characters are stored in the array starting from the given offset and up to the length specified.
117     *
118     * @param buffer The byte array in which to store the data.
119     * @param offset The offset into the array at which to start storing data.
120     * @param length The number of bytes to read.
121     * @return The number of bytes read. Returns -1 if the end of the stream has been reached.
122     * @throws IOException If an error occurs while reading the underlying stream.
123     */
124    @Override
125    public int read(final byte[] buffer, int offset, int length) throws IOException {
126        int ch;
127        final int off;
128
129        if (length < 1) {
130            return 0;
131        }
132
133        ch = available();
134
135        if (length > ch) {
136            length = ch;
137        }
138
139        // If nothing is available, block to read only one character
140        if (length < 1) {
141            length = 1;
142        }
143
144        if ((ch = read()) == NetConstants.EOS) {
145            return NetConstants.EOS;
146        }
147
148        off = offset;
149
150        do {
151            buffer[offset++] = (byte) ch;
152        } while (--length > 0 && (ch = read()) != NetConstants.EOS);
153
154        return offset - off;
155    }
156}