View Javadoc
1   /*
2    * Copyright (C) 2012, Robin Rosenberg
3    * Copyright (C) 2010, 2013 Marc Strapetz <marc.strapetz@syntevo.com> and others
4    *
5    * This program and the accompanying materials are made available under the
6    * terms of the Eclipse Distribution License v. 1.0 which is available at
7    * https://www.eclipse.org/org/documents/edl-v10.php.
8    *
9    * SPDX-License-Identifier: BSD-3-Clause
10   */
11  
12  package org.eclipse.jgit.util.io;
13  
14  import java.io.IOException;
15  import java.io.InputStream;
16  
17  import org.eclipse.jgit.diff.RawText;
18  
19  /**
20   * An InputStream that expands LF to CRLF.
21   *
22   * Existing CRLF are not expanded to CRCRLF, but retained as is.
23   *
24   * Optionally, a binary check on the first {@link RawText#getBufferSize()} bytes
25   * is performed and in case of binary files, canonicalization is turned off (for
26   * the complete file).
27   */
28  public class AutoCRLFInputStream extends InputStream {
29  
30  	private final byte[] single = new byte[1];
31  
32  	private final byte[] buf = new byte[RawText.getBufferSize()];
33  
34  	private final InputStream in;
35  
36  	private int cnt;
37  
38  	private int ptr;
39  
40  	private boolean isBinary;
41  
42  	private boolean detectBinary;
43  
44  	private byte last;
45  
46  	/**
47  	 * Creates a new InputStream, wrapping the specified stream
48  	 *
49  	 * @param in
50  	 *            raw input stream
51  	 * @param detectBinary
52  	 *            whether binaries should be detected
53  	 * @since 2.0
54  	 */
55  	public AutoCRLFInputStream(InputStream in, boolean detectBinary) {
56  		this.in = in;
57  		this.detectBinary = detectBinary;
58  	}
59  
60  	/** {@inheritDoc} */
61  	@Override
62  	public int read() throws IOException {
63  		final int read = read(single, 0, 1);
64  		return read == 1 ? single[0] & 0xff : -1;
65  	}
66  
67  	/** {@inheritDoc} */
68  	@Override
69  	public int read(byte[] bs, int off, int len) throws IOException {
70  		if (len == 0)
71  			return 0;
72  
73  		if (cnt == -1)
74  			return -1;
75  
76  		int i = off;
77  		final int end = off + len;
78  
79  		while (i < end) {
80  			if (ptr == cnt && !fillBuffer())
81  				break;
82  
83  			byte b = buf[ptr++];
84  			if (isBinary || b != '\n') {
85  				// Logic for binary files ends here
86  				bs[i++] = last = b;
87  				continue;
88  			}
89  
90  			if (b == '\n') {
91  				if (last == '\r') {
92  					bs[i++] = last = b;
93  					continue;
94  				}
95  				bs[i++] = last = '\r';
96  				ptr--;
97  			} else
98  				bs[i++] = last = b;
99  		}
100 		int n = i == off ? -1 : i - off;
101 		if (n > 0)
102 			last = bs[i - 1];
103 		return n;
104 	}
105 
106 	/** {@inheritDoc} */
107 	@Override
108 	public void close() throws IOException {
109 		in.close();
110 	}
111 
112 	private boolean fillBuffer() throws IOException {
113 		cnt = 0;
114 		while (cnt < buf.length) {
115 			int n = in.read(buf, cnt, buf.length - cnt);
116 			if (n < 0) {
117 				break;
118 			}
119 			cnt += n;
120 		}
121 		if (cnt < 1) {
122 			cnt = -1;
123 			return false;
124 		}
125 		if (detectBinary) {
126 			isBinary = RawText.isBinary(buf, cnt, cnt < buf.length);
127 			detectBinary = false;
128 		}
129 		ptr = 0;
130 		return true;
131 	}
132 }