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