1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 package org.eclipse.jgit.util;
45
46 import java.util.Arrays;
47
48 import org.eclipse.jgit.lib.Constants;
49
50
51 public abstract class QuotedString {
52
53 public static final GitPathStyle GIT_PATH = new GitPathStyle();
54
55
56
57
58
59
60
61
62 public static final BourneStyle BOURNE = new BourneStyle();
63
64
65 public static final BourneUserPathStyle BOURNE_USER_PATH = new BourneUserPathStyle();
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 public abstract String quote(String in);
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 public String dequote(final String in) {
101 final byte[] b = Constants.encode(in);
102 return dequote(b, 0, b.length);
103 }
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127 public abstract String dequote(byte[] in, int offset, int end);
128
129
130
131
132
133
134
135
136 public static class BourneStyle extends QuotedString {
137 @Override
138 public String quote(final String in) {
139 final StringBuilder r = new StringBuilder();
140 r.append('\'');
141 int start = 0, i = 0;
142 for (; i < in.length(); i++) {
143 switch (in.charAt(i)) {
144 case '\'':
145 case '!':
146 r.append(in, start, i);
147 r.append('\'');
148 r.append('\\');
149 r.append(in.charAt(i));
150 r.append('\'');
151 start = i + 1;
152 break;
153 }
154 }
155 r.append(in, start, i);
156 r.append('\'');
157 return r.toString();
158 }
159
160 @Override
161 public String dequote(final byte[] in, int ip, final int ie) {
162 boolean inquote = false;
163 final byte[] r = new byte[ie - ip];
164 int rPtr = 0;
165 while (ip < ie) {
166 final byte b = in[ip++];
167 switch (b) {
168 case '\'':
169 inquote = !inquote;
170 continue;
171 case '\\':
172 if (inquote || ip == ie)
173 r[rPtr++] = b;
174 else
175 r[rPtr++] = in[ip++];
176 continue;
177 default:
178 r[rPtr++] = b;
179 continue;
180 }
181 }
182 return RawParseUtils.decode(Constants.CHARSET, r, 0, rPtr);
183 }
184 }
185
186
187 public static class BourneUserPathStyle extends BourneStyle {
188 @Override
189 public String quote(final String in) {
190 if (in.matches("^~[A-Za-z0-9_-]+$")) {
191
192
193
194 return in + "/";
195 }
196
197 if (in.matches("^~[A-Za-z0-9_-]*/.*$")) {
198
199
200
201 final int i = in.indexOf('/') + 1;
202 if (i == in.length())
203 return in;
204 return in.substring(0, i) + super.quote(in.substring(i));
205 }
206
207 return super.quote(in);
208 }
209 }
210
211
212 public static final class GitPathStyle extends QuotedString {
213 private static final byte[] quote;
214 static {
215 quote = new byte[128];
216 Arrays.fill(quote, (byte) -1);
217
218 for (int i = '0'; i <= '9'; i++)
219 quote[i] = 0;
220 for (int i = 'a'; i <= 'z'; i++)
221 quote[i] = 0;
222 for (int i = 'A'; i <= 'Z'; i++)
223 quote[i] = 0;
224 quote[' '] = 0;
225 quote['$'] = 0;
226 quote['%'] = 0;
227 quote['&'] = 0;
228 quote['*'] = 0;
229 quote['+'] = 0;
230 quote[','] = 0;
231 quote['-'] = 0;
232 quote['.'] = 0;
233 quote['/'] = 0;
234 quote[':'] = 0;
235 quote[';'] = 0;
236 quote['='] = 0;
237 quote['?'] = 0;
238 quote['@'] = 0;
239 quote['_'] = 0;
240 quote['^'] = 0;
241 quote['|'] = 0;
242 quote['~'] = 0;
243
244 quote['\u0007'] = 'a';
245 quote['\b'] = 'b';
246 quote['\f'] = 'f';
247 quote['\n'] = 'n';
248 quote['\r'] = 'r';
249 quote['\t'] = 't';
250 quote['\u000B'] = 'v';
251 quote['\\'] = '\\';
252 quote['"'] = '"';
253 }
254
255 @Override
256 public String quote(final String instr) {
257 if (instr.length() == 0)
258 return "\"\"";
259 boolean reuse = true;
260 final byte[] in = Constants.encode(instr);
261 final StringBuilder r = new StringBuilder(2 + in.length);
262 r.append('"');
263 for (int i = 0; i < in.length; i++) {
264 final int c = in[i] & 0xff;
265 if (c < quote.length) {
266 final byte style = quote[c];
267 if (style == 0) {
268 r.append((char) c);
269 continue;
270 }
271 if (style > 0) {
272 reuse = false;
273 r.append('\\');
274 r.append((char) style);
275 continue;
276 }
277 }
278
279 reuse = false;
280 r.append('\\');
281 r.append((char) (((c >> 6) & 03) + '0'));
282 r.append((char) (((c >> 3) & 07) + '0'));
283 r.append((char) (((c >> 0) & 07) + '0'));
284 }
285 if (reuse)
286 return instr;
287 r.append('"');
288 return r.toString();
289 }
290
291 @Override
292 public String dequote(final byte[] in, final int inPtr, final int inEnd) {
293 if (2 <= inEnd - inPtr && in[inPtr] == '"' && in[inEnd - 1] == '"')
294 return dq(in, inPtr + 1, inEnd - 1);
295 return RawParseUtils.decode(Constants.CHARSET, in, inPtr, inEnd);
296 }
297
298 private static String dq(final byte[] in, int inPtr, final int inEnd) {
299 final byte[] r = new byte[inEnd - inPtr];
300 int rPtr = 0;
301 while (inPtr < inEnd) {
302 final byte b = in[inPtr++];
303 if (b != '\\') {
304 r[rPtr++] = b;
305 continue;
306 }
307
308 if (inPtr == inEnd) {
309
310
311 r[rPtr++] = '\\';
312 break;
313 }
314
315 switch (in[inPtr++]) {
316 case 'a':
317 r[rPtr++] = 0x07 ;
318 continue;
319 case 'b':
320 r[rPtr++] = '\b';
321 continue;
322 case 'f':
323 r[rPtr++] = '\f';
324 continue;
325 case 'n':
326 r[rPtr++] = '\n';
327 continue;
328 case 'r':
329 r[rPtr++] = '\r';
330 continue;
331 case 't':
332 r[rPtr++] = '\t';
333 continue;
334 case 'v':
335 r[rPtr++] = 0x0B;
336 continue;
337
338 case '\\':
339 case '"':
340 r[rPtr++] = in[inPtr - 1];
341 continue;
342
343 case '0':
344 case '1':
345 case '2':
346 case '3': {
347 int cp = in[inPtr - 1] - '0';
348 for (int n = 1; n < 3 && inPtr < inEnd; n++) {
349 final byte c = in[inPtr];
350 if ('0' <= c && c <= '7') {
351 cp <<= 3;
352 cp |= c - '0';
353 inPtr++;
354 } else {
355 break;
356 }
357 }
358 r[rPtr++] = (byte) cp;
359 continue;
360 }
361
362 default:
363
364
365 r[rPtr++] = '\\';
366 r[rPtr++] = in[inPtr - 1];
367 continue;
368 }
369 }
370
371 return RawParseUtils.decode(Constants.CHARSET, r, 0, rPtr);
372 }
373
374 private GitPathStyle() {
375
376 }
377 }
378 }