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 8000 bytes is performed and in case
25   * of binary files, canonicalization is turned off (for the complete file).
26   */
27  public class AutoCRLFInputStream extends InputStream {
28  
29  	static final int BUFFER_SIZE = 8096;
30  
31  	private final byte[] single = new byte[1];
32  
33  	private final byte[] buf = new byte[BUFFER_SIZE];
34  
35  	private final InputStream in;
36  
37  	private int cnt;
38  
39  	private int ptr;
40  
41  	private boolean isBinary;
42  
43  	private boolean detectBinary;
44  
45  	private byte last;
46  
47  	/**
48  	 * Creates a new InputStream, wrapping the specified stream
49  	 *
50  	 * @param in
51  	 *            raw input stream
52  	 * @param detectBinary
53  	 *            whether binaries should be detected
54  	 * @since 2.0
55  	 */
56  	public AutoCRLFInputStream(InputStream in, boolean detectBinary) {
57  		this.in = in;
58  		this.detectBinary = detectBinary;
59  	}
60  
61  	/** {@inheritDoc} */
62  	@Override
63  	public int read() throws IOException {
64  		final int read = read(single, 0, 1);
65  		return read == 1 ? single[0] & 0xff : -1;
66  	}
67  
68  	/** {@inheritDoc} */
69  	@Override
70  	public int read(byte[] bs, int off, int len) throws IOException {
71  		if (len == 0)
72  			return 0;
73  
74  		if (cnt == -1)
75  			return -1;
76  
77  		int i = off;
78  		final int end = off + len;
79  
80  		while (i < end) {
81  			if (ptr == cnt && !fillBuffer())
82  				break;
83  
84  			byte b = buf[ptr++];
85  			if (isBinary || b != '\n') {
86  				// Logic for binary files ends here
87  				bs[i++] = last = b;
88  				continue;
89  			}
90  
91  			if (b == '\n') {
92  				if (last == '\r') {
93  					bs[i++] = last = b;
94  					continue;
95  				}
96  				bs[i++] = last = '\r';
97  				ptr--;
98  			} else
99  				bs[i++] = last = b;
100 		}
101 		int n = i == off ? -1 : i - off;
102 		if (n > 0)
103 			last = bs[i - 1];
104 		return n;
105 	}
106 
107 	/** {@inheritDoc} */
108 	@Override
109 	public void close() throws IOException {
110 		in.close();
111 	}
112 
113 	private boolean fillBuffer() throws IOException {
114 		cnt = 0;
115 		while (cnt < buf.length) {
116 			int n = in.read(buf, cnt, buf.length - cnt);
117 			if (n < 0) {
118 				break;
119 			}
120 			cnt += n;
121 		}
122 		if (cnt < 1) {
123 			cnt = -1;
124 			return false;
125 		}
126 		if (detectBinary) {
127 			isBinary = RawText.isBinary(buf, cnt);
128 			detectBinary = false;
129 		}
130 		ptr = 0;
131 		return true;
132 	}
133 }