1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.util.io;
12
13 import java.io.BufferedOutputStream;
14 import java.io.IOException;
15 import java.io.OutputStream;
16
17 import org.eclipse.jgit.diff.RawText;
18
19
20
21
22
23
24
25
26
27
28 public class AutoCRLFOutputStream extends OutputStream {
29
30 private final OutputStream out;
31
32 private int buf = -1;
33
34 private byte[] binbuf = new byte[RawText.getBufferSize()];
35
36 private byte[] onebytebuf = new byte[1];
37
38 private int binbufcnt = 0;
39
40 private boolean detectBinary;
41
42 private boolean isBinary;
43
44
45
46
47
48
49 public AutoCRLFOutputStream(OutputStream out) {
50 this(out, true);
51 }
52
53
54
55
56
57
58
59
60
61 public AutoCRLFOutputStream(OutputStream out, boolean detectBinary) {
62
63 this.out = out instanceof BufferedOutputStream ? out
64 : new BufferedOutputStream(out);
65 this.detectBinary = detectBinary;
66 }
67
68
69 @Override
70 public void write(int b) throws IOException {
71 onebytebuf[0] = (byte) b;
72 write(onebytebuf, 0, 1);
73 }
74
75
76 @Override
77 public void write(byte[] b) throws IOException {
78 int overflow = buffer(b, 0, b.length);
79 if (overflow > 0)
80 write(b, b.length - overflow, overflow);
81 }
82
83
84 @Override
85 public void write(byte[] b, int startOff, int startLen)
86 throws IOException {
87 final int overflow = buffer(b, startOff, startLen);
88 if (overflow < 0)
89 return;
90 final int off = startOff + startLen - overflow;
91 final int len = overflow;
92 if (len == 0)
93 return;
94 int lastw = off;
95 if (isBinary) {
96 out.write(b, off, len);
97 return;
98 }
99 for (int i = off; i < off + len; ++i) {
100 final byte c = b[i];
101 switch (c) {
102 case '\r':
103 buf = '\r';
104 break;
105 case '\n':
106 if (buf != '\r') {
107 if (lastw < i) {
108 out.write(b, lastw, i - lastw);
109 }
110 out.write('\r');
111 lastw = i;
112 }
113 buf = -1;
114 break;
115 default:
116 buf = -1;
117 break;
118 }
119 }
120 if (lastw < off + len) {
121 out.write(b, lastw, off + len - lastw);
122 }
123 if (b[off + len - 1] == '\r')
124 buf = '\r';
125 }
126
127 private int buffer(byte[] b, int off, int len) throws IOException {
128 if (binbufcnt > binbuf.length) {
129 return len;
130 }
131 int copy = Math.min(binbuf.length - binbufcnt, len);
132 System.arraycopy(b, off, binbuf, binbufcnt, copy);
133 binbufcnt += copy;
134 int remaining = len - copy;
135 if (remaining > 0) {
136 decideMode(false);
137 }
138 return remaining;
139 }
140
141 private void decideMode(boolean complete) throws IOException {
142 if (detectBinary) {
143 isBinary = RawText.isBinary(binbuf, binbufcnt, complete);
144 if (!isBinary) {
145 isBinary = RawText.isCrLfText(binbuf, binbufcnt, complete);
146 }
147 detectBinary = false;
148 }
149 int cachedLen = binbufcnt;
150 binbufcnt = binbuf.length + 1;
151 write(binbuf, 0, cachedLen);
152 }
153
154
155 @Override
156 public void flush() throws IOException {
157 if (binbufcnt <= binbuf.length) {
158 decideMode(true);
159 }
160 buf = -1;
161 out.flush();
162 }
163
164
165 @Override
166 public void close() throws IOException {
167 flush();
168 out.close();
169 }
170 }