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
45
46
47
48
49
50
51
52 package org.eclipse.jgit.lib;
53
54 import static java.nio.charset.StandardCharsets.UTF_8;
55
56 import java.text.MessageFormat;
57 import java.util.ArrayList;
58 import java.util.Collections;
59 import java.util.List;
60 import java.util.Locale;
61 import java.util.Set;
62 import java.util.concurrent.TimeUnit;
63 import java.util.concurrent.atomic.AtomicReference;
64
65 import org.eclipse.jgit.errors.ConfigInvalidException;
66 import org.eclipse.jgit.events.ConfigChangedEvent;
67 import org.eclipse.jgit.events.ConfigChangedListener;
68 import org.eclipse.jgit.events.ListenerHandle;
69 import org.eclipse.jgit.events.ListenerList;
70 import org.eclipse.jgit.internal.JGitText;
71 import org.eclipse.jgit.transport.RefSpec;
72 import org.eclipse.jgit.util.RawParseUtils;
73
74
75
76
77 public class Config {
78
79 private static final String[] EMPTY_STRING_ARRAY = {};
80
81 static final long KiB = 1024;
82 static final long MiB = 1024 * KiB;
83 static final long GiB = 1024 * MiB;
84 private static final int MAX_DEPTH = 10;
85
86 private static final TypedConfigGetter DEFAULT_GETTER = new DefaultTypedConfigGetter();
87
88 private static TypedConfigGetter typedGetter = DEFAULT_GETTER;
89
90
91 private final ListenerListhtml#ListenerList">ListenerList listeners = new ListenerList();
92
93
94
95
96
97
98
99 private final AtomicReference<ConfigSnapshot> state;
100
101 private final Config baseConfig;
102
103
104
105
106
107
108
109
110 static final String MAGIC_EMPTY_VALUE = new String();
111
112
113
114
115 public Config() {
116 this(null);
117 }
118
119
120
121
122
123
124
125
126 public Configig" href="../../../../org/eclipse/jgit/lib/Config.html#Config">Config(Config defaultConfig) {
127 baseConfig = defaultConfig;
128 state = new AtomicReference<>(newState());
129 }
130
131
132
133
134
135
136
137
138
139 public static void setTypedConfigGetter(TypedConfigGetter getter) {
140 typedGetter = getter == null ? DEFAULT_GETTER : getter;
141 }
142
143
144
145
146
147
148
149
150 static String escapeValue(String x) {
151 if (x.isEmpty()) {
152 return "";
153 }
154
155 boolean needQuote = x.charAt(0) == ' ' || x.charAt(x.length() - 1) == ' ';
156 StringBuilder r = new StringBuilder(x.length());
157 for (int k = 0; k < x.length(); k++) {
158 char c = x.charAt(k);
159
160
161
162
163
164
165
166
167
168
169
170
171 switch (c) {
172 case '\0':
173
174
175
176 throw new IllegalArgumentException(
177 JGitText.get().configValueContainsNullByte);
178
179 case '\n':
180 r.append('\\').append('n');
181 break;
182
183 case '\t':
184 r.append('\\').append('t');
185 break;
186
187 case '\b':
188
189
190
191 r.append('\\').append('b');
192 break;
193
194 case '\\':
195 r.append('\\').append('\\');
196 break;
197
198 case '"':
199 r.append('\\').append('"');
200 break;
201
202 case '#':
203 case ';':
204 needQuote = true;
205 r.append(c);
206 break;
207
208 default:
209 r.append(c);
210 break;
211 }
212 }
213
214 return needQuote ? '"' + r.toString() + '"' : r.toString();
215 }
216
217 static String escapeSubsection(String x) {
218 if (x.isEmpty()) {
219 return "\"\"";
220 }
221
222 StringBuilder r = new StringBuilder(x.length() + 2).append('"');
223 for (int k = 0; k < x.length(); k++) {
224 char c = x.charAt(k);
225
226
227
228 switch (c) {
229 case '\0':
230 throw new IllegalArgumentException(
231 JGitText.get().configSubsectionContainsNullByte);
232
233 case '\n':
234 throw new IllegalArgumentException(
235 JGitText.get().configSubsectionContainsNewline);
236
237 case '\\':
238 case '"':
239 r.append('\\').append(c);
240 break;
241
242 default:
243 r.append(c);
244 break;
245 }
246 }
247
248 return r.append('"').toString();
249 }
250
251
252
253
254
255
256
257
258
259
260
261
262 public int getInt(final String section, final String name,
263 final int defaultValue) {
264 return typedGetter.getInt(this, section, null, name, defaultValue);
265 }
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280 public int getInt(final String section, String subsection,
281 final String name, final int defaultValue) {
282 return typedGetter.getInt(this, section, subsection, name,
283 defaultValue);
284 }
285
286
287
288
289
290
291
292
293
294
295
296
297 public long getLong(String section, String name, long defaultValue) {
298 return typedGetter.getLong(this, section, null, name, defaultValue);
299 }
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314 public long getLong(final String section, String subsection,
315 final String name, final long defaultValue) {
316 return typedGetter.getLong(this, section, subsection, name,
317 defaultValue);
318 }
319
320
321
322
323
324
325
326
327
328
329
330
331
332 public boolean getBoolean(final String section, final String name,
333 final boolean defaultValue) {
334 return typedGetter.getBoolean(this, section, null, name, defaultValue);
335 }
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351 public boolean getBoolean(final String section, String subsection,
352 final String name, final boolean defaultValue) {
353 return typedGetter.getBoolean(this, section, subsection, name,
354 defaultValue);
355 }
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370 public <T extends Enum<?>> T getEnum(final String section,
371 final String subsection, final String name, final T defaultValue) {
372 final T[] all = allValuesOf(defaultValue);
373 return typedGetter.getEnum(this, all, section, subsection, name,
374 defaultValue);
375 }
376
377 @SuppressWarnings("unchecked")
378 private static <T> T[] allValuesOf(T value) {
379 try {
380 return (T[]) value.getClass().getMethod("values").invoke(null);
381 } catch (Exception err) {
382 String typeName = value.getClass().getName();
383 String msg = MessageFormat.format(
384 JGitText.get().enumValuesNotAvailable, typeName);
385 throw new IllegalArgumentException(msg, err);
386 }
387 }
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405 public <T extends Enum<?>> T getEnum(final T[] all, final String section,
406 final String subsection, final String name, final T defaultValue) {
407 return typedGetter.getEnum(this, all, section, subsection, name,
408 defaultValue);
409 }
410
411
412
413
414
415
416
417
418
419
420
421
422 public String getString(final String section, String subsection,
423 final String name) {
424 return getRawString(section, subsection, name);
425 }
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441 public String[] getStringList(final String section, String subsection,
442 final String name) {
443 String[] base;
444 if (baseConfig != null)
445 base = baseConfig.getStringList(section, subsection, name);
446 else
447 base = EMPTY_STRING_ARRAY;
448
449 String[] self = getRawStringList(section, subsection, name);
450 if (self == null)
451 return base;
452 if (base.length == 0)
453 return self;
454 String[] res = new String[base.length + self.length];
455 int n = base.length;
456 System.arraycopy(base, 0, res, 0, n);
457 System.arraycopy(self, 0, res, n, self.length);
458 return res;
459 }
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480 public long getTimeUnit(String section, String subsection, String name,
481 long defaultValue, TimeUnit wantUnit) {
482 return typedGetter.getTimeUnit(this, section, subsection, name,
483 defaultValue, wantUnit);
484 }
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500 public List<RefSpec> getRefSpecs(String section, String subsection,
501 String name) {
502 return typedGetter.getRefSpecs(this, section, subsection, name);
503 }
504
505
506
507
508
509
510
511
512
513
514
515
516
517 public Set<String> getSubsections(String section) {
518 return getState().getSubsections(section);
519 }
520
521
522
523
524
525
526
527
528
529 public Set<String> getSections() {
530 return getState().getSections();
531 }
532
533
534
535
536
537
538
539
540 public Set<String> getNames(String section) {
541 return getNames(section, null);
542 }
543
544
545
546
547
548
549
550
551
552
553 public Set<String> getNames(String section, String subsection) {
554 return getState().getNames(section, subsection);
555 }
556
557
558
559
560
561
562
563
564
565
566
567
568 public Set<String> getNames(String section, boolean recursive) {
569 return getState().getNames(section, null, recursive);
570 }
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585 public Set<String> getNames(String section, String subsection,
586 boolean recursive) {
587 return getState().getNames(section, subsection, recursive);
588 }
589
590
591
592
593
594
595
596
597
598
599
600
601
602 @SuppressWarnings("unchecked")
603 public <T> T get(SectionParser<T> parser) {
604 final ConfigSnapshot myState = getState();
605 T obj = (T) myState.cache.get(parser);
606 if (obj == null) {
607 obj = parser.parse(this);
608 myState.cache.put(parser, obj);
609 }
610 return obj;
611 }
612
613
614
615
616
617
618
619
620
621
622
623 public void uncache(SectionParser<?> parser) {
624 state.get().cache.remove(parser);
625 }
626
627
628
629
630
631
632
633
634
635
636
637
638 public ListenerHandle addChangeListener(ConfigChangedListener listener) {
639 return listeners.addConfigChangedListener(listener);
640 }
641
642
643
644
645
646
647
648
649
650
651
652
653
654 protected boolean notifyUponTransientChanges() {
655 return true;
656 }
657
658
659
660
661 protected void fireConfigChangedEvent() {
662 listeners.dispatch(new ConfigChangedEvent());
663 }
664
665 String getRawString(final String section, final String subsection,
666 final String name) {
667 String[] lst = getRawStringList(section, subsection, name);
668 if (lst != null) {
669 return lst[lst.length - 1];
670 } else if (baseConfig != null) {
671 return baseConfig.getRawString(section, subsection, name);
672 } else {
673 return null;
674 }
675 }
676
677 private String[] getRawStringList(String section, String subsection,
678 String name) {
679 return state.get().get(section, subsection, name);
680 }
681
682 private ConfigSnapshot getState() {
683 ConfigSnapshot cur, upd;
684 do {
685 cur = state.get();
686 final ConfigSnapshot base = getBaseState();
687 if (cur.baseState == base)
688 return cur;
689 upd = new ConfigSnapshot(cur.entryList, base);
690 } while (!state.compareAndSet(cur, upd));
691 return upd;
692 }
693
694 private ConfigSnapshot getBaseState() {
695 return baseConfig != null ? baseConfig.getState() : null;
696 }
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716 public void setInt(final String section, final String subsection,
717 final String name, final int value) {
718 setLong(section, subsection, name, value);
719 }
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739 public void setLong(final String section, final String subsection,
740 final String name, final long value) {
741 final String s;
742
743 if (value >= GiB && (value % GiB) == 0)
744 s = String.valueOf(value / GiB) + "g";
745 else if (value >= MiB && (value % MiB) == 0)
746 s = String.valueOf(value / MiB) + "m";
747 else if (value >= KiB && (value % KiB) == 0)
748 s = String.valueOf(value / KiB) + "k";
749 else
750 s = String.valueOf(value);
751
752 setString(section, subsection, name, s);
753 }
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773 public void setBoolean(final String section, final String subsection,
774 final String name, final boolean value) {
775 setString(section, subsection, name, value ? "true" : "false");
776 }
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796 public <T extends Enum<?>> void setEnum(final String section,
797 final String subsection, final String name, final T value) {
798 String n;
799 if (value instanceof ConfigEnum)
800 n = ((ConfigEnum) value).toConfigValue();
801 else
802 n = value.name().toLowerCase(Locale.ROOT).replace('_', ' ');
803 setString(section, subsection, name, n);
804 }
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824 public void setString(final String section, final String subsection,
825 final String name, final String value) {
826 setStringList(section, subsection, name, Collections
827 .singletonList(value));
828 }
829
830
831
832
833
834
835
836
837
838
839
840 public void unset(final String section, final String subsection,
841 final String name) {
842 setStringList(section, subsection, name, Collections
843 .<String> emptyList());
844 }
845
846
847
848
849
850
851
852
853
854 public void unsetSection(String section, String subsection) {
855 ConfigSnapshot src, res;
856 do {
857 src = state.get();
858 res = unsetSection(src, section, subsection);
859 } while (!state.compareAndSet(src, res));
860 }
861
862 private ConfigSnapshotonfigSnapshot">ConfigSnapshot unsetSection(final ConfigSnapshot srcState,
863 final String section,
864 final String subsection) {
865 final int max = srcState.entryList.size();
866 final ArrayList<ConfigLine> r = new ArrayList<>(max);
867
868 boolean lastWasMatch = false;
869 for (ConfigLine e : srcState.entryList) {
870 if (e.includedFrom == null && e.match(section, subsection)) {
871
872 lastWasMatch = true;
873 continue;
874 }
875
876 if (lastWasMatch && e.section == null && e.subsection == null)
877 continue;
878 r.add(e);
879 }
880
881 return newState(r);
882 }
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902 public void setStringList(final String section, final String subsection,
903 final String name, final List<String> values) {
904 ConfigSnapshot src, res;
905 do {
906 src = state.get();
907 res = replaceStringList(src, section, subsection, name, values);
908 } while (!state.compareAndSet(src, res));
909 if (notifyUponTransientChanges())
910 fireConfigChangedEvent();
911 }
912
913 private ConfigSnapshotSnapshot">ConfigSnapshot replaceStringList(final ConfigSnapshot srcState,
914 final String section, final String subsection, final String name,
915 final List<String> values) {
916 final List<ConfigLine> entries = copy(srcState, values);
917 int entryIndex = 0;
918 int valueIndex = 0;
919 int insertPosition = -1;
920
921
922
923 while (entryIndex < entries.size() && valueIndex < values.size()) {
924 final ConfigLine e = entries.get(entryIndex);
925 if (e.includedFrom == null && e.match(section, subsection, name)) {
926 entries.set(entryIndex, e.forValue(values.get(valueIndex++)));
927 insertPosition = entryIndex + 1;
928 }
929 entryIndex++;
930 }
931
932
933
934 if (valueIndex == values.size() && entryIndex < entries.size()) {
935 while (entryIndex < entries.size()) {
936 final ConfigLine e = entries.get(entryIndex++);
937 if (e.includedFrom == null
938 && e.match(section, subsection, name))
939 entries.remove(--entryIndex);
940 }
941 }
942
943
944
945 if (valueIndex < values.size() && entryIndex == entries.size()) {
946 if (insertPosition < 0) {
947
948
949
950
951 insertPosition = findSectionEnd(entries, section, subsection,
952 true);
953 }
954 if (insertPosition < 0) {
955
956
957
958 final ConfigLineLine.html#ConfigLine">ConfigLine e = new ConfigLine();
959 e.section = section;
960 e.subsection = subsection;
961 entries.add(e);
962 insertPosition = entries.size();
963 }
964 while (valueIndex < values.size()) {
965 final ConfigLineLine.html#ConfigLine">ConfigLine e = new ConfigLine();
966 e.section = section;
967 e.subsection = subsection;
968 e.name = name;
969 e.value = values.get(valueIndex++);
970 entries.add(insertPosition++, e);
971 }
972 }
973
974 return newState(entries);
975 }
976
977 private static List<ConfigLine> copy(final ConfigSnapshot src,
978 final List<String> values) {
979
980
981
982 final int max = src.entryList.size() + values.size() + 1;
983 final ArrayList<ConfigLine> r = new ArrayList<>(max);
984 r.addAll(src.entryList);
985 return r;
986 }
987
988 private static int findSectionEnd(final List<ConfigLine> entries,
989 final String section, final String subsection,
990 boolean skipIncludedLines) {
991 for (int i = 0; i < entries.size(); i++) {
992 ConfigLine e = entries.get(i);
993 if (e.includedFrom != null && skipIncludedLines) {
994 continue;
995 }
996
997 if (e.match(section, subsection, null)) {
998 i++;
999 while (i < entries.size()) {
1000 e = entries.get(i);
1001 if (e.match(section, subsection, e.name))
1002 i++;
1003 else
1004 break;
1005 }
1006 return i;
1007 }
1008 }
1009 return -1;
1010 }
1011
1012
1013
1014
1015
1016
1017 public String toText() {
1018 final StringBuilder out = new StringBuilder();
1019 for (ConfigLine e : state.get().entryList) {
1020 if (e.includedFrom != null)
1021 continue;
1022 if (e.prefix != null)
1023 out.append(e.prefix);
1024 if (e.section != null && e.name == null) {
1025 out.append('[');
1026 out.append(e.section);
1027 if (e.subsection != null) {
1028 out.append(' ');
1029 String escaped = escapeValue(e.subsection);
1030
1031 boolean quoted = escaped.startsWith("\"")
1032 && escaped.endsWith("\"");
1033 if (!quoted)
1034 out.append('"');
1035 out.append(escaped);
1036 if (!quoted)
1037 out.append('"');
1038 }
1039 out.append(']');
1040 } else if (e.section != null && e.name != null) {
1041 if (e.prefix == null || "".equals(e.prefix))
1042 out.append('\t');
1043 out.append(e.name);
1044 if (MAGIC_EMPTY_VALUE != e.value) {
1045 out.append(" =");
1046 if (e.value != null) {
1047 out.append(' ');
1048 out.append(escapeValue(e.value));
1049 }
1050 }
1051 if (e.suffix != null)
1052 out.append(' ');
1053 }
1054 if (e.suffix != null)
1055 out.append(e.suffix);
1056 out.append('\n');
1057 }
1058 return out.toString();
1059 }
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070 public void fromText(String text) throws ConfigInvalidException {
1071 state.set(newState(fromTextRecurse(text, 1, null)));
1072 }
1073
1074 private List<ConfigLine> fromTextRecurse(String text, int depth,
1075 String includedFrom) throws ConfigInvalidException {
1076 if (depth > MAX_DEPTH) {
1077 throw new ConfigInvalidException(
1078 JGitText.get().tooManyIncludeRecursions);
1079 }
1080 final List<ConfigLine> newEntries = new ArrayList<>();
1081 final StringReader in = new StringReader(text);
1082 ConfigLine last = null;
1083 ConfigLine e = new ConfigLine();
1084 e.includedFrom = includedFrom;
1085 for (;;) {
1086 int input = in.read();
1087 if (-1 == input) {
1088 if (e.section != null)
1089 newEntries.add(e);
1090 break;
1091 }
1092
1093 final char c = (char) input;
1094 if ('\n' == c) {
1095
1096 newEntries.add(e);
1097 if (e.section != null)
1098 last = e;
1099 e = new ConfigLine();
1100 e.includedFrom = includedFrom;
1101 } else if (e.suffix != null) {
1102
1103 e.suffix += c;
1104
1105 } else if (';' == c || '#' == c) {
1106
1107 e.suffix = String.valueOf(c);
1108
1109 } else if (e.section == null && Character.isWhitespace(c)) {
1110
1111 if (e.prefix == null)
1112 e.prefix = "";
1113 e.prefix += c;
1114
1115 } else if ('[' == c) {
1116
1117 e.section = readSectionName(in);
1118 input = in.read();
1119 if ('"' == input) {
1120 e.subsection = readSubsectionName(in);
1121 input = in.read();
1122 }
1123 if (']' != input)
1124 throw new ConfigInvalidException(JGitText.get().badGroupHeader);
1125 e.suffix = "";
1126
1127 } else if (last != null) {
1128
1129 e.section = last.section;
1130 e.subsection = last.subsection;
1131 in.reset();
1132 e.name = readKeyName(in);
1133 if (e.name.endsWith("\n")) {
1134 e.name = e.name.substring(0, e.name.length() - 1);
1135 e.value = MAGIC_EMPTY_VALUE;
1136 } else
1137 e.value = readValue(in);
1138
1139 if (e.section.equalsIgnoreCase("include")) {
1140 addIncludedConfig(newEntries, e, depth);
1141 }
1142 } else
1143 throw new ConfigInvalidException(JGitText.get().invalidLineInConfigFile);
1144 }
1145
1146 return newEntries;
1147 }
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160 protected byte[] readIncludedConfig(String relPath)
1161 throws ConfigInvalidException {
1162 return null;
1163 }
1164
1165 private void addIncludedConfig(final List<ConfigLine> newEntries,
1166 ConfigLine line, int depth) throws ConfigInvalidException {
1167 if (!line.name.equalsIgnoreCase("path") ||
1168 line.value == null || line.value.equals(MAGIC_EMPTY_VALUE)) {
1169 throw new ConfigInvalidException(MessageFormat.format(
1170 JGitText.get().invalidLineInConfigFileWithParam, line));
1171 }
1172 byte[] bytes = readIncludedConfig(line.value);
1173 if (bytes == null) {
1174 return;
1175 }
1176
1177 String decoded;
1178 if (isUtf8(bytes)) {
1179 decoded = RawParseUtils.decode(UTF_8, bytes, 3, bytes.length);
1180 } else {
1181 decoded = RawParseUtils.decode(bytes);
1182 }
1183 try {
1184 newEntries.addAll(fromTextRecurse(decoded, depth + 1, line.value));
1185 } catch (ConfigInvalidException e) {
1186 throw new ConfigInvalidException(MessageFormat
1187 .format(JGitText.get().cannotReadFile, line.value), e);
1188 }
1189 }
1190
1191 private ConfigSnapshot newState() {
1192 return new ConfigSnapshot(Collections.<ConfigLine> emptyList(),
1193 getBaseState());
1194 }
1195
1196 private ConfigSnapshot newState(List<ConfigLine> entries) {
1197 return new ConfigSnapshot(Collections.unmodifiableList(entries),
1198 getBaseState());
1199 }
1200
1201
1202
1203
1204 protected void clear() {
1205 state.set(newState());
1206 }
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216 protected boolean isUtf8(final byte[] bytes) {
1217 return bytes.length >= 3 && bytes[0] == (byte) 0xEF
1218 && bytes[1] == (byte) 0xBB && bytes[2] == (byte) 0xBF;
1219 }
1220
1221 private static String readSectionName(StringReader in)
1222 throws ConfigInvalidException {
1223 final StringBuilder name = new StringBuilder();
1224 for (;;) {
1225 int c = in.read();
1226 if (c < 0)
1227 throw new ConfigInvalidException(JGitText.get().unexpectedEndOfConfigFile);
1228
1229 if (']' == c) {
1230 in.reset();
1231 break;
1232 }
1233
1234 if (' ' == c || '\t' == c) {
1235 for (;;) {
1236 c = in.read();
1237 if (c < 0)
1238 throw new ConfigInvalidException(JGitText.get().unexpectedEndOfConfigFile);
1239
1240 if ('"' == c) {
1241 in.reset();
1242 break;
1243 }
1244
1245 if (' ' == c || '\t' == c)
1246 continue;
1247 throw new ConfigInvalidException(MessageFormat.format(JGitText.get().badSectionEntry, name));
1248 }
1249 break;
1250 }
1251
1252 if (Character.isLetterOrDigit((char) c) || '.' == c || '-' == c)
1253 name.append((char) c);
1254 else
1255 throw new ConfigInvalidException(MessageFormat.format(JGitText.get().badSectionEntry, name));
1256 }
1257 return name.toString();
1258 }
1259
1260 private static String readKeyName(StringReader in)
1261 throws ConfigInvalidException {
1262 final StringBuilder name = new StringBuilder();
1263 for (;;) {
1264 int c = in.read();
1265 if (c < 0)
1266 throw new ConfigInvalidException(JGitText.get().unexpectedEndOfConfigFile);
1267
1268 if ('=' == c)
1269 break;
1270
1271 if (' ' == c || '\t' == c) {
1272 for (;;) {
1273 c = in.read();
1274 if (c < 0)
1275 throw new ConfigInvalidException(JGitText.get().unexpectedEndOfConfigFile);
1276
1277 if ('=' == c)
1278 break;
1279
1280 if (';' == c || '#' == c || '\n' == c) {
1281 in.reset();
1282 break;
1283 }
1284
1285 if (' ' == c || '\t' == c)
1286 continue;
1287 throw new ConfigInvalidException(JGitText.get().badEntryDelimiter);
1288 }
1289 break;
1290 }
1291
1292 if (Character.isLetterOrDigit((char) c) || c == '-') {
1293
1294
1295
1296 name.append((char) c);
1297 } else if ('\n' == c) {
1298 in.reset();
1299 name.append((char) c);
1300 break;
1301 } else
1302 throw new ConfigInvalidException(MessageFormat.format(JGitText.get().badEntryName, name));
1303 }
1304 return name.toString();
1305 }
1306
1307 private static String readSubsectionName(StringReader in)
1308 throws ConfigInvalidException {
1309 StringBuilder r = new StringBuilder();
1310 for (;;) {
1311 int c = in.read();
1312 if (c < 0) {
1313 break;
1314 }
1315
1316 if ('\n' == c) {
1317 throw new ConfigInvalidException(
1318 JGitText.get().newlineInQuotesNotAllowed);
1319 }
1320 if ('\\' == c) {
1321 c = in.read();
1322 switch (c) {
1323 case -1:
1324 throw new ConfigInvalidException(JGitText.get().endOfFileInEscape);
1325
1326 case '\\':
1327 case '"':
1328 r.append((char) c);
1329 continue;
1330
1331 default:
1332
1333
1334 r.append((char) c);
1335 continue;
1336 }
1337 }
1338 if ('"' == c) {
1339 break;
1340 }
1341
1342 r.append((char) c);
1343 }
1344 return r.toString();
1345 }
1346
1347 private static String readValue(StringReader in)
1348 throws ConfigInvalidException {
1349 StringBuilder value = new StringBuilder();
1350 StringBuilder trailingSpaces = null;
1351 boolean quote = false;
1352 boolean inLeadingSpace = true;
1353
1354 for (;;) {
1355 int c = in.read();
1356 if (c < 0) {
1357 break;
1358 }
1359 if ('\n' == c) {
1360 if (quote) {
1361 throw new ConfigInvalidException(
1362 JGitText.get().newlineInQuotesNotAllowed);
1363 }
1364 in.reset();
1365 break;
1366 }
1367
1368 if (!quote && (';' == c || '#' == c)) {
1369 if (trailingSpaces != null) {
1370 trailingSpaces.setLength(0);
1371 }
1372 in.reset();
1373 break;
1374 }
1375
1376 char cc = (char) c;
1377 if (Character.isWhitespace(cc)) {
1378 if (inLeadingSpace) {
1379 continue;
1380 }
1381 if (trailingSpaces == null) {
1382 trailingSpaces = new StringBuilder();
1383 }
1384 trailingSpaces.append(cc);
1385 continue;
1386 } else {
1387 inLeadingSpace = false;
1388 if (trailingSpaces != null) {
1389 value.append(trailingSpaces);
1390 trailingSpaces.setLength(0);
1391 }
1392 }
1393
1394 if ('\\' == c) {
1395 c = in.read();
1396 switch (c) {
1397 case -1:
1398 throw new ConfigInvalidException(JGitText.get().endOfFileInEscape);
1399 case '\n':
1400 continue;
1401 case 't':
1402 value.append('\t');
1403 continue;
1404 case 'b':
1405 value.append('\b');
1406 continue;
1407 case 'n':
1408 value.append('\n');
1409 continue;
1410 case '\\':
1411 value.append('\\');
1412 continue;
1413 case '"':
1414 value.append('"');
1415 continue;
1416 default:
1417 throw new ConfigInvalidException(MessageFormat.format(
1418 JGitText.get().badEscape,
1419 Character.valueOf(((char) c))));
1420 }
1421 }
1422
1423 if ('"' == c) {
1424 quote = !quote;
1425 continue;
1426 }
1427
1428 value.append(cc);
1429 }
1430 return value.length() > 0 ? value.toString() : null;
1431 }
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447 public static interface SectionParser<T> {
1448
1449
1450
1451
1452
1453
1454
1455 T parse(Config cfg);
1456 }
1457
1458 private static class StringReader {
1459 private final char[] buf;
1460
1461 private int pos;
1462
1463 StringReader(String in) {
1464 buf = in.toCharArray();
1465 }
1466
1467 int read() {
1468 if (pos >= buf.length) {
1469 return -1;
1470 }
1471 return buf[pos++];
1472 }
1473
1474 void reset() {
1475 pos--;
1476 }
1477 }
1478
1479
1480
1481
1482
1483
1484 public static interface ConfigEnum {
1485
1486
1487
1488
1489
1490 String toConfigValue();
1491
1492
1493
1494
1495
1496
1497
1498
1499 boolean matchConfigValue(String in);
1500 }
1501 }