1 package io.jawk.jrt;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 import java.io.FileOutputStream;
26 import java.io.FileInputStream;
27 import java.io.IOException;
28 import java.io.InputStreamReader;
29 import java.io.PrintStream;
30 import java.nio.charset.StandardCharsets;
31 import java.text.DecimalFormatSymbols;
32 import java.util.ArrayList;
33 import java.util.Date;
34 import java.util.Enumeration;
35 import java.util.HashMap;
36 import java.util.HashSet;
37 import java.util.IllegalFormatException;
38 import java.util.List;
39 import java.util.Locale;
40 import java.util.Map;
41 import java.util.Objects;
42 import java.util.Set;
43 import java.util.StringTokenizer;
44 import java.util.regex.Matcher;
45 import java.util.regex.Pattern;
46 import io.jawk.Awk;
47 import io.jawk.intermediate.UninitializedObject;
48 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84 public class JRT {
85
86 private static final boolean IS_WINDOWS = System.getProperty("os.name").indexOf("Windows") >= 0;
87
88 private final VariableManager vm;
89
90 private IoState ioState;
91
92 private AwkSink awkSink;
93
94 private PrintStream error;
95
96 private Object inputLine = null;
97
98 private RecordState recordState;
99
100 private InputSource activeSource;
101 private static final UninitializedObject BLANK = new UninitializedObject();
102
103 private static final Integer ONE = Integer.valueOf(1);
104 private static final Integer ZERO = Integer.valueOf(0);
105 private static final Integer MINUS_ONE = Integer.valueOf(-1);
106 private String jrtInputString;
107
108
109 private long nr;
110 private long fnr;
111 private int rstart;
112 private int rlength;
113 private Object filename;
114 private String fs;
115 private String rs;
116 private String ofs;
117 private String ors;
118 private String convfmt;
119 private String ofmt;
120 private String subsep;
121 private final Locale locale;
122 private final char decimalSeparator;
123
124 private static final class FileOutputState {
125
126 private final AwkSink sink;
127
128 private FileOutputState(AwkSink sinkParam) {
129 this.sink = Objects.requireNonNull(sinkParam, "sink");
130 }
131 }
132
133 private static final class CommandInputState {
134
135 private final Process process;
136 private final PartitioningReader reader;
137 private final Thread errorPump;
138
139 private CommandInputState(Process processParam, PartitioningReader readerParam, Thread errorPumpParam) {
140 this.process = Objects.requireNonNull(processParam, "process");
141 this.reader = Objects.requireNonNull(readerParam, "reader");
142 this.errorPump = errorPumpParam;
143 }
144 }
145
146 private static final class ProcessOutputState {
147
148 private final Process process;
149 private final AwkSink sink;
150 private final PrintStream processOutput;
151 private final Thread stdoutPump;
152 private final Thread stderrPump;
153
154 private ProcessOutputState(
155 Process processParam,
156 AwkSink sinkParam,
157 PrintStream processOutputParam,
158 Thread stdoutPumpParam,
159 Thread stderrPumpParam) {
160 this.process = Objects.requireNonNull(processParam, "process");
161 this.sink = Objects.requireNonNull(sinkParam, "sink");
162 this.processOutput = Objects.requireNonNull(processOutputParam, "processOutput");
163 this.stdoutPump = stdoutPumpParam;
164 this.stderrPump = stderrPumpParam;
165 }
166 }
167
168 private static final class IoState {
169
170 private final Map<String, PartitioningReader> fileReaders = new HashMap<String, PartitioningReader>();
171 private final Map<String, CommandInputState> commandInputs = new HashMap<String, CommandInputState>();
172 private final Map<String, FileOutputState> fileOutputs = new HashMap<String, FileOutputState>();
173 private final Map<String, ProcessOutputState> processOutputs = new HashMap<String, ProcessOutputState>();
174 }
175
176
177
178
179
180
181
182
183
184 @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "JRT must hold the provided runtime collaborators for later use")
185 public JRT(VariableManager vm, Locale locale, AwkSink awkSink, PrintStream error) {
186 this.vm = vm;
187 this.locale = locale == null ? Locale.US : locale;
188 this.decimalSeparator = DecimalFormatSymbols.getInstance(this.locale).getDecimalSeparator();
189 this.awkSink = Objects.requireNonNull(awkSink, "awkSink");
190 this.error = error == null ? System.err : error;
191 this.nr = 0L;
192 this.fnr = 0L;
193 this.rstart = 0;
194 this.rlength = 0;
195 this.filename = "";
196 this.fs = Awk.DEFAULT_FS;
197 this.rs = Awk.DEFAULT_RS;
198 this.ofs = Awk.DEFAULT_OFS;
199 this.ors = Awk.DEFAULT_ORS;
200 this.convfmt = Awk.DEFAULT_CONVFMT;
201 this.ofmt = Awk.DEFAULT_OFMT;
202 this.subsep = Awk.DEFAULT_SUBSEP;
203 }
204
205
206
207
208
209
210
211 public void setAwkSink(AwkSink sink) {
212 awkSink = Objects.requireNonNull(sink, "awkSink");
213 }
214
215
216
217
218
219
220
221 public void setErrorStream(PrintStream errorStream) {
222 this.error = Objects.requireNonNull(errorStream, "errorStream");
223 }
224
225
226
227
228
229
230 public AwkSink getAwkSink() {
231 return awkSink;
232 }
233
234
235
236
237
238
239 public Locale getLocale() {
240 return locale;
241 }
242
243 private IoState getIoState() {
244 if (ioState == null) {
245 ioState = new IoState();
246 }
247 return ioState;
248 }
249
250
251
252
253
254
255
256
257 public static boolean isJrtManagedSpecialVariable(String name) {
258 return "FS".equals(name)
259 || "RS".equals(name)
260 || "OFS".equals(name)
261 || "ORS".equals(name)
262 || "CONVFMT".equals(name)
263 || "OFMT".equals(name)
264 || "SUBSEP".equals(name)
265 || "FILENAME".equals(name)
266 || "NF".equals(name)
267 || "NR".equals(name)
268 || "FNR".equals(name)
269 || "ARGC".equals(name);
270 }
271
272
273
274
275
276
277
278 public static Map<String, Object> copySpecialVariables(Map<String, Object> variableMap) {
279 Map<String, Object> specialVariables = new HashMap<String, Object>();
280 if (variableMap == null || variableMap.isEmpty()) {
281 return specialVariables;
282 }
283 for (Map.Entry<String, Object> entry : variableMap.entrySet()) {
284 if (isJrtManagedSpecialVariable(entry.getKey())) {
285 specialVariables.put(entry.getKey(), entry.getValue());
286 }
287 }
288 return specialVariables;
289 }
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306 public void prepareForExecution(String defaultFs, String defaultRs) {
307
308 jrtCloseAll();
309
310
311 ioState = null;
312 inputLine = null;
313 recordState = null;
314 activeSource = null;
315 jrtInputString = null;
316 nr = 0L;
317 fnr = 0L;
318 rstart = 0;
319 rlength = 0;
320 filename = "";
321
322
323 setFS(defaultFs == null ? Awk.DEFAULT_FS : defaultFs);
324 setRS(defaultRs);
325 setOFS(Awk.DEFAULT_OFS);
326 setORS(Awk.DEFAULT_ORS);
327 setCONVFMT(Awk.DEFAULT_CONVFMT);
328 setOFMT(Awk.DEFAULT_OFMT);
329 setSUBSEP(Awk.DEFAULT_SUBSEP);
330 setFILENAMEViaJrt("");
331 setNR(0);
332 setFNR(0);
333 setRSTART(0);
334 setRLENGTH(0);
335 }
336
337
338
339
340
341
342
343 public final void assignInitialVariables(Map<String, Object> initialVarMap) {
344 for (Map.Entry<String, Object> var : initialVarMap.entrySet()) {
345 String name = var.getKey();
346 Object value = var.getValue();
347 if ("FS".equals(name)) {
348 setFS(value);
349 continue;
350 }
351 if ("RS".equals(name)) {
352 setRS(value);
353 continue;
354 }
355 if ("OFS".equals(name)) {
356 setOFS(value);
357 continue;
358 }
359 if ("ORS".equals(name)) {
360 setORS(value);
361 continue;
362 }
363 if ("CONVFMT".equals(name)) {
364 setCONVFMT(value);
365 continue;
366 }
367 if ("OFMT".equals(name)) {
368 setOFMT(value);
369 continue;
370 }
371 if ("SUBSEP".equals(name)) {
372 setSUBSEP(value);
373 continue;
374 }
375 if ("FILENAME".equals(name)) {
376 setFILENAMEViaJrt(value);
377 continue;
378 }
379 if ("NF".equals(name)) {
380 setNF(value);
381 continue;
382 }
383 if ("NR".equals(name)) {
384 setNR(value);
385 continue;
386 }
387 if ("FNR".equals(name)) {
388 setFNR(value);
389 continue;
390 }
391 if ("ARGC".equals(name)) {
392 setARGC(value);
393 continue;
394 }
395 vm.assignVariable(name, value);
396 }
397 }
398
399
400
401
402
403
404
405
406
407
408 public final void applySpecialVariables(Map<String, Object> variableMap) {
409 if (variableMap == null || variableMap.isEmpty()) {
410 return;
411 }
412 for (Map.Entry<String, Object> var : variableMap.entrySet()) {
413 String name = var.getKey();
414 Object value = var.getValue();
415 if ("FS".equals(name)) {
416 setFS(value);
417 } else if ("RS".equals(name)) {
418 setRS(value);
419 } else if ("OFS".equals(name)) {
420 setOFS(value);
421 } else if ("ORS".equals(name)) {
422 setORS(value);
423 } else if ("CONVFMT".equals(name)) {
424 setCONVFMT(value);
425 } else if ("OFMT".equals(name)) {
426 setOFMT(value);
427 } else if ("SUBSEP".equals(name)) {
428 setSUBSEP(value);
429 } else if ("FILENAME".equals(name)) {
430 setFILENAMEViaJrt(value);
431 } else if ("NF".equals(name)) {
432 setNF(value);
433 } else if ("NR".equals(name)) {
434 setNR(value);
435 } else if ("FNR".equals(name)) {
436 setFNR(value);
437 } else if ("ARGC".equals(name)) {
438 setARGC(value);
439 }
440
441
442 }
443 }
444
445
446
447
448
449
450
451
452
453
454 public static void assignEnvironmentVariables(AssocArray aa) {
455 Map<String, String> env = System.getenv();
456 for (Map.Entry<String, String> var : env.entrySet()) {
457 aa.put(var.getKey(), new StrNum(var.getValue()));
458 }
459 }
460
461
462
463
464
465
466
467
468 public static Map<Object, Object> createAwkMap(boolean sortedArrayKeys) {
469 return AssocArray.create(sortedArrayKeys);
470 }
471
472
473
474
475
476
477
478
479
480
481 public static boolean containsAwkKey(Map<Object, Object> map, Object key) {
482 if (map instanceof AssocArray) {
483 return ((AssocArray) map).isIn(key);
484 }
485 return map.containsKey(key);
486 }
487
488
489
490
491
492
493
494
495
496
497
498
499 public static Object getAwkValue(Map<Object, Object> map, Object key) {
500 if (map instanceof AssocArray) {
501 return map.get(key);
502 }
503 Object value = map.get(key);
504 return value != null ? value : BLANK;
505 }
506
507
508
509
510
511
512
513
514 public String toAwkString(Object o) {
515 return AwkSink.formatOutputValue(o, this.convfmt, this.locale);
516 }
517
518
519
520
521
522
523
524 public static double toDouble(final Object o) {
525 if (o == null) {
526 return 0;
527 }
528
529 if (o instanceof Number) {
530 return ((Number) o).doubleValue();
531 }
532
533 if (o instanceof Character) {
534 return (double) ((Character) o).charValue();
535 }
536
537 if (o instanceof StrNum) {
538 StrNum strNum = (StrNum) o;
539 if (strNum.isNumber()) {
540 return strNum.doubleValue();
541 }
542 }
543
544
545 String s = o.toString();
546 int length = s.length();
547
548
549
550 if (length > 26) {
551 length = 26;
552 }
553
554
555
556
557 while (length > 0) {
558 try {
559 return Double.parseDouble(s.substring(0, length));
560 } catch (NumberFormatException nfe) {
561 length--;
562 }
563 }
564
565
566 return 0;
567 }
568
569
570
571
572
573
574
575
576 public static boolean isActuallyLong(double d) {
577 double r = Math.rint(d);
578 return Math.abs(d - r) < Math.ulp(d);
579 }
580
581
582
583
584
585
586
587 public static long toLong(final Object o) {
588 if (o == null) {
589 return 0;
590 }
591
592 if (o instanceof Number) {
593 return ((Number) o).longValue();
594 }
595
596 if (o instanceof Character) {
597 return (long) ((Character) o).charValue();
598 }
599
600
601 String s = o.toString();
602 int length = s.length();
603
604
605
606 if (length > 20) {
607 length = 20;
608 }
609
610
611
612
613 while (length > 0) {
614 try {
615 return Long.parseLong(s.substring(0, length));
616 } catch (NumberFormatException nfe) {
617 length--;
618 }
619 }
620
621 return 0;
622 }
623
624
625
626
627
628
629
630
631
632 public static long parseFieldNumber(Object obj) {
633 long num = toLong(obj);
634 if (num < 0) {
635 throw new AwkRuntimeException(
636 "Field $(" + obj.toString()
637 + ") is incorrect.");
638 }
639 return num;
640 }
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657 public static boolean compare2(Object o1, Object o2, int mode) {
658 if (o1 instanceof Number && o2 instanceof Number) {
659 return compareNumbers(((Number) o1).doubleValue(), ((Number) o2).doubleValue(), mode);
660 }
661
662 String o1String = o1 == null ? "" : o1.toString();
663 String o2String = o2 == null ? "" : o2.toString();
664
665 if (o1 instanceof UninitializedObject) {
666 if (isBlankOrZero(o2, o2String)) {
667 return mode == 0;
668 } else {
669 return mode < 0;
670 }
671 }
672 if (o2 instanceof UninitializedObject) {
673 if (isBlankOrZero(o1, o1String)) {
674 return mode == 0;
675 } else {
676 return mode > 0;
677 }
678 }
679
680 if (isNumericComparisonOperand(o1) && isNumericComparisonOperand(o2)) {
681 return compareNumbers(getDoubleForComparison(o1), getDoubleForComparison(o2), mode);
682 }
683
684 if (mode == 0) {
685 return o1String.equals(o2String);
686 } else if (mode < 0) {
687 return o1String.compareTo(o2String) < 0;
688 } else {
689 return o1String.compareTo(o2String) > 0;
690 }
691 }
692
693 private static boolean isBlankOrZero(Object value, String stringValue) {
694 if (value instanceof UninitializedObject) {
695 return true;
696 }
697 if (value instanceof Number) {
698 return ((Number) value).doubleValue() == 0.0D;
699 }
700 if (value instanceof StrNum && ((StrNum) value).isNumber()) {
701 return ((StrNum) value).doubleValue() == 0.0D;
702 }
703 return "".equals(stringValue) || "0".equals(stringValue);
704 }
705
706 private static boolean isNumericComparisonOperand(Object value) {
707 return value instanceof Number || value instanceof StrNum && ((StrNum) value).isNumber();
708 }
709
710 private static double getDoubleForComparison(Object value) {
711 if (value instanceof Number) {
712 return ((Number) value).doubleValue();
713 }
714 return ((StrNum) value).doubleValue();
715 }
716
717 private static boolean compareNumbers(double o1Number, double o2Number, int mode) {
718 if (mode < 0) {
719 return o1Number < o2Number;
720 } else if (mode == 0) {
721 return o1Number == o2Number;
722 } else {
723 return o1Number > o2Number;
724 }
725 }
726
727
728
729
730
731
732
733 public static Object toJavaScalar(Object value) {
734 if (value instanceof StrNum) {
735 return value.toString();
736 }
737 if (value instanceof Double || value instanceof Float) {
738 double number = ((Number) value).doubleValue();
739 if (isActuallyLong(number)) {
740 return Long.valueOf((long) Math.rint(number));
741 }
742 }
743 return value;
744 }
745
746 static boolean isParseableNumber(String value, char decimalSeparator) {
747 int index = 0;
748 int length = value.length();
749
750 if (length == 0) {
751 return false;
752 }
753
754 char current = value.charAt(index);
755 if (current == '+' || current == '-') {
756 index++;
757 if (index == length) {
758 return false;
759 }
760 }
761
762 boolean digitFound = false;
763 while (index < length && value.charAt(index) >= '0' && value.charAt(index) <= '9') {
764 index++;
765 digitFound = true;
766 }
767
768 if (index < length && value.charAt(index) == decimalSeparator) {
769 index++;
770 while (index < length && value.charAt(index) >= '0' && value.charAt(index) <= '9') {
771 index++;
772 digitFound = true;
773 }
774 }
775
776 if (!digitFound) {
777 return false;
778 }
779
780 if (index < length && (value.charAt(index) == 'e' || value.charAt(index) == 'E')) {
781 index++;
782 if (index < length && (value.charAt(index) == '+' || value.charAt(index) == '-')) {
783 index++;
784 }
785
786 boolean exponentDigitFound = false;
787 while (index < length && value.charAt(index) >= '0' && value.charAt(index) <= '9') {
788 index++;
789 exponentDigitFound = true;
790 }
791 if (!exponentDigitFound) {
792 return false;
793 }
794 }
795
796 return index == length;
797 }
798
799 static String normalizeNumberForComparison(String value, char decimalSeparator) {
800 return decimalSeparator == '.' ? value : value.replace(decimalSeparator, '.');
801 }
802
803
804
805
806
807
808
809
810
811
812
813
814 public static Object inc(Object o) {
815 return toDouble(o) + 1;
816 }
817
818
819
820
821
822
823
824
825
826
827
828
829 public static Object dec(Object o) {
830 return toDouble(o) - 1;
831 }
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850 public final boolean toBoolean(Object o) {
851 boolean val;
852 if (o instanceof Integer) {
853 val = ((Integer) o).intValue() != 0;
854 } else if (o instanceof Long) {
855 val = ((Long) o).longValue() != 0;
856 } else if (o instanceof Double) {
857 val = ((Double) o).doubleValue() != 0;
858 } else if (o instanceof StrNum) {
859 StrNum strNum = (StrNum) o;
860 val = strNum.isNumber() ? strNum.doubleValue() != 0 : strNum.toString().length() > 0;
861 } else if (o instanceof String) {
862 val = (o.toString().length() > 0);
863 } else if (o instanceof UninitializedObject) {
864 val = false;
865 } else if (o instanceof Pattern) {
866
867
868 Pattern pattern = (Pattern) o;
869 Object inputField = jrtGetInputField(0);
870 String s = inputField instanceof UninitializedObject ? "" : inputField.toString();
871 Matcher matcher = pattern.matcher(s);
872 val = matcher.find();
873 } else {
874 throw new Error("Unknown operand_stack type: " + o.getClass() + " for value " + o);
875 }
876 return val;
877 }
878
879
880
881
882
883
884
885
886
887
888 public int split(Object array, Object string) {
889 return splitWorker(new StringTokenizer(toAwkString(string)), toArrayMap(array));
890 }
891
892
893
894
895
896
897
898
899
900
901
902
903
904 public int split(Object fieldSeparator, Object array, Object string) {
905 String fsString = toAwkString(fieldSeparator);
906 if (fsString.equals(" ")) {
907 return splitWorker(new StringTokenizer(toAwkString(string)), toArrayMap(array));
908 } else if (fsString.equals("")) {
909 return splitWorker(new CharacterTokenizer(toAwkString(string)), toArrayMap(array));
910 } else if (fsString.length() == 1) {
911 return splitWorker(
912 new SingleCharacterTokenizer(toAwkString(string), fsString.charAt(0)),
913 toArrayMap(array));
914 } else {
915 return splitWorker(new RegexTokenizer(toAwkString(string), fsString), toArrayMap(array));
916 }
917 }
918
919 private static Map<Object, Object> toArrayMap(Object array) {
920 if (!(array instanceof Map)) {
921 throw new IllegalArgumentException("split target must be a Map.");
922 }
923 @SuppressWarnings("unchecked")
924 Map<Object, Object> arrayMap = (Map<Object, Object>) array;
925 return arrayMap;
926 }
927
928 private int splitWorker(Enumeration<Object> e, Map<Object, Object> array) {
929 int cnt = 0;
930 array.clear();
931 while (e.hasMoreElements()) {
932 Object value = e.nextElement();
933 array.put(Long.valueOf(++cnt), toInputScalar(value));
934 }
935 array.put(0L, Long.valueOf(cnt));
936 return cnt;
937 }
938
939
940
941
942
943
944
945
946 public PartitioningReader getPartitioningReader() {
947 if (activeSource instanceof StreamInputSource) {
948 return ((StreamInputSource) activeSource).getPartitioningReader();
949 }
950 return null;
951 }
952
953
954
955
956
957
958
959
960 public Object getInputLine() {
961 if (recordState != null) {
962 return recordState.getField(0);
963 }
964 return inputLine;
965 }
966
967
968
969
970
971
972
973 public Integer getNF() {
974 if (recordState == null) {
975 return Integer.valueOf(0);
976 }
977 return Integer.valueOf(recordState.getNF());
978 }
979
980
981
982
983
984
985 public void setNF(Object nfObject) {
986 jrtSetNF(nfObject);
987 }
988
989
990
991
992
993
994 public Long getNR() {
995 return Long.valueOf(nr);
996 }
997
998
999
1000
1001
1002
1003 public void setNR(Object value) {
1004 this.nr = toLong(value);
1005 }
1006
1007
1008
1009
1010
1011
1012 public Long getFNR() {
1013 return Long.valueOf(fnr);
1014 }
1015
1016
1017
1018
1019
1020
1021 public void setFNR(Object value) {
1022 this.fnr = toLong(value);
1023 }
1024
1025
1026
1027
1028
1029
1030 public Object getFSVar() {
1031 return fs;
1032 }
1033
1034
1035
1036
1037
1038
1039 public String getFSString() {
1040 return fs;
1041 }
1042
1043
1044
1045
1046
1047
1048 public void setFS(Object value) {
1049 this.fs = value == null ? "" : value.toString();
1050 }
1051
1052
1053
1054
1055
1056
1057 public Object getRSVar() {
1058 return rs;
1059 }
1060
1061
1062
1063
1064
1065
1066 public String getRSString() {
1067 return rs;
1068 }
1069
1070
1071
1072
1073
1074
1075 public void setRS(Object value) {
1076 this.rs = value == null ? "" : value.toString();
1077 applyRS(this.rs);
1078 }
1079
1080
1081
1082
1083
1084
1085 public Object getOFSVar() {
1086 return ofs;
1087 }
1088
1089
1090
1091
1092
1093
1094 public String getOFSString() {
1095 return ofs;
1096 }
1097
1098
1099
1100
1101
1102
1103 public void setOFS(Object value) {
1104 this.ofs = value == null ? "" : value.toString();
1105 }
1106
1107
1108
1109
1110
1111
1112 public Object getORSVar() {
1113 return ors;
1114 }
1115
1116
1117
1118
1119
1120
1121 public String getORSString() {
1122 return ors;
1123 }
1124
1125
1126
1127
1128
1129
1130 public void setORS(Object value) {
1131 this.ors = value == null ? "" : value.toString();
1132 }
1133
1134
1135
1136
1137
1138
1139 public Integer getRSTART() {
1140 return Integer.valueOf(rstart);
1141 }
1142
1143
1144
1145
1146
1147
1148 public void setRSTART(Object value) {
1149 this.rstart = (int) toLong(value);
1150 }
1151
1152
1153
1154
1155
1156
1157 public Integer getRLENGTH() {
1158 return Integer.valueOf(rlength);
1159 }
1160
1161
1162
1163
1164
1165
1166 public void setRLENGTH(Object value) {
1167 this.rlength = (int) toLong(value);
1168 }
1169
1170
1171
1172
1173
1174
1175 public Object getFILENAME() {
1176 return filename == null ? "" : filename;
1177 }
1178
1179
1180
1181
1182
1183
1184 public void setFILENAMEViaJrt(Object name) {
1185 this.filename = normalizeRecordValue(name);
1186 }
1187
1188
1189
1190
1191
1192
1193 public Object getSUBSEPVar() {
1194 return subsep;
1195 }
1196
1197
1198
1199
1200
1201
1202 public String getSUBSEPString() {
1203 return subsep;
1204 }
1205
1206
1207
1208
1209
1210
1211 public void setSUBSEP(Object value) {
1212 this.subsep = value == null ? "" : value.toString();
1213 }
1214
1215
1216
1217
1218
1219
1220 public Object getCONVFMTVar() {
1221 return convfmt;
1222 }
1223
1224
1225
1226
1227
1228
1229 public String getCONVFMTString() {
1230 return convfmt;
1231 }
1232
1233
1234
1235
1236
1237
1238 public void setCONVFMT(Object value) {
1239 this.convfmt = value == null ? "" : value.toString();
1240 }
1241
1242
1243
1244
1245
1246
1247 public String getOFMTString() {
1248 return ofmt;
1249 }
1250
1251
1252
1253
1254
1255
1256 public void setOFMT(Object value) {
1257 this.ofmt = value == null ? "" : value.toString();
1258 }
1259
1260
1261
1262
1263
1264
1265 public Object getARGCVar() {
1266 return vm.getARGC();
1267 }
1268
1269
1270
1271
1272
1273
1274 public void setARGC(Object value) {
1275 vm.assignVariable("ARGC", value);
1276 }
1277
1278
1279
1280
1281
1282
1283
1284
1285 public void setInputLine(Object inputLineParam) {
1286 Object inputValue = normalizeRecordValue(inputLineParam);
1287 this.inputLine = inputValue;
1288 recordState = new RecordState(inputValue, null);
1289 }
1290
1291
1292
1293
1294
1295
1296
1297 public Object toInputScalar(Object value) {
1298 if (value instanceof String) {
1299 return new StrNum((String) value, decimalSeparator);
1300 }
1301 if (value instanceof StrNum) {
1302 return value;
1303 }
1304 if (value == null || value instanceof UninitializedObject) {
1305 return new StrNum("", decimalSeparator);
1306 }
1307 return new StrNum(value.toString(), decimalSeparator);
1308 }
1309
1310 private static Object normalizeRecordValue(Object value) {
1311 if (value == null || value instanceof UninitializedObject) {
1312 return "";
1313 }
1314 return value;
1315 }
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327 public boolean consumeInput(final InputSource source) throws IOException {
1328 Objects.requireNonNull(source, "source");
1329 activeSource = source;
1330 if (!source.nextRecord()) {
1331 return false;
1332 }
1333
1334 inputLine = null;
1335 recordState = new RecordState(source);
1336
1337 this.nr++;
1338 if (source.isFromFilenameList()) {
1339 this.fnr++;
1340 }
1341 return true;
1342 }
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355 public Object consumeInputToTarget(final InputSource source) throws IOException {
1356 Objects.requireNonNull(source, "source");
1357 activeSource = source;
1358 materializeCurrentRecord();
1359 if (!source.nextRecord()) {
1360 return null;
1361 }
1362
1363 RecordState inputState = new RecordState(source);
1364 this.nr++;
1365 if (source.isFromFilenameList()) {
1366 this.fnr++;
1367 }
1368 return new StrNum(inputState.getRecordText(), decimalSeparator);
1369 }
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380 public boolean consumeInputForEval(InputSource source) throws IOException {
1381 return consumeInput(source);
1382 }
1383
1384
1385
1386
1387
1388
1389
1390 protected void initializeInputFields(String record, List<String> preFields) {
1391 recordState = new RecordState(toInputScalar(record), preFields);
1392 }
1393
1394
1395
1396
1397
1398 public void jrtParseFields() {
1399 RecordState state = ensureRecordStateForTextMutation();
1400 state.ensureFieldsMaterialized();
1401 }
1402
1403
1404
1405
1406 public boolean hasInputFields() {
1407 return recordState != null;
1408 }
1409
1410
1411
1412
1413
1414
1415
1416
1417 public void jrtSetNF(Object nfObj) {
1418 int nf = (int) toDouble(nfObj);
1419 if (nf < 0) {
1420 nf = 0;
1421 }
1422
1423 RecordState state = ensureRecordStateForFieldMutation();
1424 int currentNF = state.getNF();
1425
1426 if (nf < currentNF) {
1427 for (int i = currentNF; i > nf; i--) {
1428 state.removeField(i - 1);
1429 }
1430 } else if (nf > currentNF) {
1431 for (int i = currentNF + 1; i <= nf; i++) {
1432 state.addField("");
1433 }
1434 }
1435
1436 state.markRecordTextDirty();
1437 }
1438
1439
1440
1441
1442
1443
1444
1445 public Object jrtGetInputField(Object fieldnumObj) {
1446 return jrtGetInputField(parseFieldNumber(fieldnumObj));
1447 }
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457 public Object jrtGetInputField(long fieldnum) {
1458 if (fieldnum < 0 || fieldnum > Integer.MAX_VALUE) {
1459 throw new AwkRuntimeException("Field $(" + Long.valueOf(fieldnum) + ") is incorrect.");
1460 }
1461 if (recordState == null) {
1462 return BLANK;
1463 }
1464 return recordState.getField((int) fieldnum);
1465 }
1466
1467
1468
1469
1470
1471
1472
1473
1474 public String jrtSetInputField(Object valueObj, long fieldNum) {
1475 if (fieldNum > Integer.MAX_VALUE) {
1476 throw new AwkRuntimeException("Field $(" + Long.valueOf(fieldNum) + ") is incorrect.");
1477 }
1478 String value = valueObj == null ? "" : valueObj.toString();
1479 int fieldIndex = (int) fieldNum;
1480 RecordState state = ensureRecordStateForFieldMutation();
1481 if (valueObj instanceof UninitializedObject) {
1482 if (fieldIndex <= state.getNF()) {
1483 state.setField(fieldIndex - 1, "");
1484 }
1485 } else {
1486 while (state.getNF() < fieldIndex) {
1487 state.addField("");
1488 }
1489 state.setField(fieldIndex - 1, valueObj);
1490 }
1491 state.markRecordTextDirty();
1492 return value;
1493 }
1494
1495 protected void rebuildDollarZeroFromFields() {
1496 if (recordState != null) {
1497 recordState.markRecordTextDirty();
1498 inputLine = recordState.getField(0);
1499 }
1500 }
1501
1502 private void materializeCurrentRecord() {
1503 if (recordState != null) {
1504 recordState.materialize();
1505 }
1506 }
1507
1508 private RecordState ensureRecordStateForTextMutation() {
1509 if (recordState == null) {
1510 recordState = new RecordState(inputLine, null);
1511 }
1512 return recordState;
1513 }
1514
1515 private RecordState ensureRecordStateForFieldMutation() {
1516 RecordState state = ensureRecordStateForTextMutation();
1517 state.ensureFieldsMaterialized();
1518 return state;
1519 }
1520
1521 private List<Object> sanitizeFields(List<String> rawFields) {
1522 List<Object> copy = new ArrayList<Object>(rawFields.size());
1523 for (String field : rawFields) {
1524 String value = field == null ? "" : field;
1525 copy.add(new StrNum(value, decimalSeparator));
1526 }
1527 return copy;
1528 }
1529
1530 private List<Object> splitRecordText(String recordText, String fieldSeparator) {
1531 List<Object> fields = new ArrayList<Object>();
1532 if (recordText == null || recordText.isEmpty()) {
1533 return fields;
1534 }
1535
1536 Enumeration<Object> tokenizer;
1537 if (fieldSeparator.equals(" ")) {
1538 tokenizer = new StringTokenizer(recordText);
1539 } else if (fieldSeparator.length() == 1) {
1540 tokenizer = new SingleCharacterTokenizer(recordText, fieldSeparator.charAt(0));
1541 } else if (fieldSeparator.equals("")) {
1542 tokenizer = new CharacterTokenizer(recordText);
1543 } else {
1544 tokenizer = new RegexTokenizer(recordText, fieldSeparator);
1545 }
1546
1547 while (tokenizer.hasMoreElements()) {
1548 fields.add(new StrNum((String) tokenizer.nextElement(), decimalSeparator));
1549 }
1550 return fields;
1551 }
1552
1553 private static String joinFieldsWithLiteralSeparator(List<Object> fields, String separator) {
1554 StringBuilder sb = new StringBuilder();
1555 for (int i = 0; i < fields.size(); i++) {
1556 if (i > 0) {
1557 sb.append(separator);
1558 }
1559 Object field = fields.get(i);
1560 sb.append(field == null ? "" : field.toString());
1561 }
1562 return sb.toString();
1563 }
1564
1565 private String rebuildRecordTextFromFields(List<Object> fields) {
1566 return joinFieldsWithLiteralSeparator(fields, ofs);
1567 }
1568
1569 private final class RecordState {
1570
1571 private final String fieldSeparatorAtRead;
1572 private final InputSource source;
1573 private String recordText;
1574 private Object recordScalar;
1575 private List<Object> fields;
1576 private boolean recordTextAvailable;
1577 private boolean fieldsAvailable;
1578 private boolean recordTextDirty;
1579 private boolean fieldsDirty;
1580 private boolean recordTextLoadedFromSource;
1581 private boolean fieldsLoadedFromSource;
1582
1583 private RecordState(InputSource source) {
1584 this(null, null, source);
1585 }
1586
1587 private RecordState(Object recordValue, List<String> rawFields) {
1588 this(recordValue, rawFields, null);
1589 }
1590
1591 private RecordState(Object recordValue, List<String> rawFields, InputSource source) {
1592 this.fieldSeparatorAtRead = fs;
1593 this.source = source;
1594 if (recordValue != null) {
1595 this.recordScalar = normalizeRecordValue(recordValue);
1596 this.recordText = this.recordScalar.toString();
1597 this.recordTextAvailable = true;
1598 } else if (rawFields == null && source == null) {
1599 this.recordScalar = "";
1600 this.recordText = "";
1601 this.recordTextAvailable = true;
1602 }
1603 if (rawFields != null) {
1604 this.fields = sanitizeFields(rawFields);
1605 this.fieldsAvailable = true;
1606 this.fieldsDirty = false;
1607 } else {
1608 this.fieldsAvailable = false;
1609 this.fieldsDirty = true;
1610 }
1611 this.recordTextDirty = false;
1612 }
1613
1614 private void ensureFieldsMaterialized() {
1615 if (fieldsAvailable && !fieldsDirty) {
1616 return;
1617 }
1618 if (!recordTextDirty) {
1619 loadFieldsFromSource();
1620 if (fieldsAvailable && !fieldsDirty) {
1621 return;
1622 }
1623 }
1624 fields = splitRecordText(getRecordText(), fieldSeparatorAtRead);
1625 fieldsAvailable = true;
1626 fieldsDirty = false;
1627 }
1628
1629 private String getRecordText() {
1630 if (!recordTextAvailable || recordTextDirty) {
1631 if (recordTextDirty) {
1632 recordText = rebuildRecordTextFromFields(fields);
1633 recordScalar = recordText;
1634 } else {
1635 loadRecordTextFromSource();
1636 if (!recordTextAvailable) {
1637 loadFieldsFromSource();
1638 if (!fieldsAvailable) {
1639 throw new IllegalStateException(
1640 "InputSource must provide record text, fields, or both after nextRecord()");
1641 }
1642 recordText = joinFieldsWithLiteralSeparator(fields, fieldSeparatorAtRead);
1643 recordScalar = new StrNum(recordText, decimalSeparator);
1644 }
1645 }
1646 recordTextAvailable = true;
1647 recordTextDirty = false;
1648 }
1649 return recordText;
1650 }
1651
1652 private int getNF() {
1653 ensureFieldsMaterialized();
1654 return fields.size();
1655 }
1656
1657 private Object getField(int fieldIndex) {
1658 if (fieldIndex == 0) {
1659 String value = getRecordText();
1660 if (recordScalar == null) {
1661 recordScalar = value;
1662 }
1663 return recordScalar;
1664 }
1665 ensureFieldsMaterialized();
1666 int zeroBasedIndex = fieldIndex - 1;
1667 if (zeroBasedIndex < 0 || zeroBasedIndex >= fields.size()) {
1668 return BLANK;
1669 }
1670 return fields.get(zeroBasedIndex);
1671 }
1672
1673 private void setField(int zeroBasedIndex, Object value) {
1674 ensureFieldsMaterialized();
1675 fields.set(zeroBasedIndex, normalizeFieldValue(value));
1676 markRecordTextDirty();
1677 }
1678
1679 private void addField(Object value) {
1680 ensureFieldsMaterialized();
1681 fields.add(normalizeFieldValue(value));
1682 markRecordTextDirty();
1683 }
1684
1685 private Object normalizeFieldValue(Object value) {
1686 if (value == null || value instanceof UninitializedObject) {
1687 return "";
1688 }
1689 return value;
1690 }
1691
1692 private void removeField(int zeroBasedIndex) {
1693 ensureFieldsMaterialized();
1694 fields.remove(zeroBasedIndex);
1695 markRecordTextDirty();
1696 }
1697
1698 private void markRecordTextDirty() {
1699 recordTextDirty = true;
1700 recordTextAvailable = fieldsAvailable;
1701 recordScalar = null;
1702 }
1703
1704 private void materialize() {
1705 getRecordText();
1706 ensureFieldsMaterialized();
1707 }
1708
1709 private void loadRecordTextFromSource() {
1710 if (source == null || recordTextLoadedFromSource) {
1711 return;
1712 }
1713 recordText = source.getRecordText();
1714 recordTextAvailable = recordText != null;
1715 if (recordTextAvailable) {
1716 recordScalar = new StrNum(recordText, decimalSeparator);
1717 }
1718 recordTextLoadedFromSource = true;
1719 }
1720
1721 private void loadFieldsFromSource() {
1722 if (source == null || fieldsLoadedFromSource) {
1723 return;
1724 }
1725 List<String> rawFields = source.getFields();
1726 fieldsLoadedFromSource = true;
1727 if (rawFields != null) {
1728 fields = sanitizeFields(rawFields);
1729 fieldsAvailable = true;
1730 fieldsDirty = false;
1731 }
1732 }
1733 }
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743 public Integer jrtConsumeFileInputForGetline(String fileNameParam) {
1744 try {
1745 if (jrtConsumeFileInput(fileNameParam)) {
1746 return ONE;
1747 } else {
1748 jrtInputString = "";
1749 return ZERO;
1750 }
1751 } catch (IOException ioe) {
1752 jrtInputString = "";
1753 return MINUS_ONE;
1754 }
1755 }
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765 public Integer jrtConsumeCommandInputForGetline(String cmdString) {
1766 try {
1767 if (jrtConsumeCommandInput(cmdString)) {
1768 return ONE;
1769 } else {
1770 jrtInputString = "";
1771 return ZERO;
1772 }
1773 } catch (IOException ioe) {
1774 jrtInputString = "";
1775 return MINUS_ONE;
1776 }
1777 }
1778
1779
1780
1781
1782
1783
1784 public String jrtGetInputString() {
1785 return jrtInputString;
1786 }
1787
1788
1789
1790
1791
1792
1793
1794
1795 public Map<String, PrintStream> getOutputFiles() {
1796 Map<String, PrintStream> outputFiles = new HashMap<String, PrintStream>();
1797 for (Map.Entry<String, FileOutputState> entry : getIoState().fileOutputs.entrySet()) {
1798 outputFiles.put(entry.getKey(), entry.getValue().sink.getPrintStream());
1799 }
1800 return outputFiles;
1801 }
1802
1803
1804
1805
1806
1807
1808
1809
1810 protected AwkSink getFileAwkSink(String fileNameParam, boolean append) {
1811 return getOrCreateFileOutputState(fileNameParam, append).sink;
1812 }
1813
1814
1815
1816
1817
1818
1819
1820 protected AwkSink getPipeAwkSink(String cmd) {
1821 return getOrCreateProcessOutputState(cmd).sink;
1822 }
1823
1824
1825
1826
1827
1828
1829
1830 public void printDefault(Object[] values) throws IOException {
1831 awkSink.print(ofs, ors, ofmt, values);
1832 }
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842 public void printToFile(String fileNameParam, boolean append, Object[] values) throws IOException {
1843 getFileAwkSink(fileNameParam, append).print(ofs, ors, ofmt, values);
1844 }
1845
1846
1847
1848
1849
1850
1851
1852
1853 public void printToProcess(String cmd, Object[] values) throws IOException {
1854 AwkSink sink = getPipeAwkSink(cmd);
1855 sink.print(ofs, ors, ofmt, values);
1856 sink.flush();
1857 }
1858
1859
1860
1861
1862
1863
1864
1865
1866 public void printfDefault(String format, Object[] values) throws IOException {
1867 awkSink.printf(ofs, ors, ofmt, format, values);
1868 }
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879 public void printfToFile(String fileNameParam, boolean append, String format, Object[] values)
1880 throws IOException {
1881 AwkSink sink = getFileAwkSink(fileNameParam, append);
1882 sink.printf(ofs, ors, ofmt, format, values);
1883 }
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893 public void printfToProcess(String cmd, String format, Object[] values) throws IOException {
1894 AwkSink sink = getPipeAwkSink(cmd);
1895 sink.printf(ofs, ors, ofmt, format, values);
1896 sink.flush();
1897 }
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907 public PrintStream jrtGetPrintStream(String fileNameParam, boolean append) {
1908 return getFileAwkSink(fileNameParam, append).getPrintStream();
1909 }
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920 public boolean jrtConsumeFileInput(String fileNameParam) throws IOException {
1921 Map<String, PartitioningReader> fileReaders = getIoState().fileReaders;
1922 PartitioningReader pr = fileReaders.get(fileNameParam);
1923 if (pr == null) {
1924 try {
1925 pr = new PartitioningReader(
1926 new InputStreamReader(new FileInputStream(fileNameParam), StandardCharsets.UTF_8),
1927 this.rs);
1928 fileReaders.put(fileNameParam, pr);
1929 this.filename = fileNameParam;
1930 } catch (IOException ioe) {
1931 fileReaders.remove(fileNameParam);
1932 throw ioe;
1933 }
1934 }
1935
1936 String recordText = pr.readRecord();
1937 if (recordText == null) {
1938 return false;
1939 } else {
1940 jrtInputString = recordText;
1941 inputLine = toInputScalar(recordText);
1942 recordState = new RecordState(inputLine, null);
1943 this.nr++;
1944 return true;
1945 }
1946 }
1947
1948 private static Process spawnProcess(String cmd) throws IOException {
1949 Process p;
1950
1951 if (IS_WINDOWS) {
1952
1953 ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/c", cmd);
1954 p = pb.start();
1955 } else {
1956
1957 ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c", cmd);
1958 p = pb.start();
1959 }
1960
1961 return p;
1962 }
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973 public boolean jrtConsumeCommandInput(String cmd) throws IOException {
1974 CommandInputState commandInput = getOrCreateCommandInputState(cmd);
1975 String recordText = commandInput.reader.readRecord();
1976 if (recordText == null) {
1977 return false;
1978 } else {
1979 jrtInputString = recordText;
1980 inputLine = toInputScalar(recordText);
1981 recordState = new RecordState(inputLine, null);
1982 this.nr++;
1983 return true;
1984 }
1985 }
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996 public PrintStream jrtSpawnForOutput(String cmd) {
1997 return getPipeAwkSink(cmd).getPrintStream();
1998 }
1999
2000 private FileOutputState getOrCreateFileOutputState(String fileNameParam, boolean append) {
2001 IoState state = getIoState();
2002 FileOutputState outputState = state.fileOutputs.get(fileNameParam);
2003 if (outputState == null) {
2004 outputState = createFileOutputState(fileNameParam, append);
2005 state.fileOutputs.put(fileNameParam, outputState);
2006 }
2007 return outputState;
2008 }
2009
2010 private FileOutputState createFileOutputState(String fileNameParam, boolean append) {
2011 try {
2012 PrintStream printStream = new PrintStream(
2013 new FileOutputStream(fileNameParam, append),
2014 true,
2015 StandardCharsets.UTF_8.name());
2016 return new FileOutputState(new OutputStreamAwkSink(printStream, locale));
2017 } catch (IOException ioe) {
2018 throw new AwkRuntimeException("Cannot open " + fileNameParam + " for writing: " + ioe);
2019 }
2020 }
2021
2022 private CommandInputState getOrCreateCommandInputState(String cmd) throws IOException {
2023 IoState state = getIoState();
2024 CommandInputState commandInput = state.commandInputs.get(cmd);
2025 if (commandInput == null) {
2026 commandInput = createCommandInputState(cmd);
2027 state.commandInputs.put(cmd, commandInput);
2028 this.filename = "";
2029 }
2030 return commandInput;
2031 }
2032
2033 private CommandInputState createCommandInputState(String cmd) throws IOException {
2034 Process process = null;
2035 Thread errorPump = null;
2036 try {
2037 process = spawnProcess(cmd);
2038 process.getOutputStream().close();
2039 errorPump = DataPump.dumpAndReturnThread(cmd + " stderr", process.getErrorStream(), error);
2040 PartitioningReader reader = new PartitioningReader(
2041 new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8),
2042 this.rs);
2043 return new CommandInputState(process, reader, errorPump);
2044 } catch (IOException ioe) {
2045 if (process != null) {
2046 process.destroy();
2047 }
2048 joinDataPump(errorPump);
2049 throw ioe;
2050 }
2051 }
2052
2053 private ProcessOutputState getOrCreateProcessOutputState(String cmd) {
2054 IoState state = getIoState();
2055 ProcessOutputState outputState = state.processOutputs.get(cmd);
2056 if (outputState == null) {
2057 outputState = createProcessOutputState(cmd);
2058 state.processOutputs.put(cmd, outputState);
2059 }
2060 return outputState;
2061 }
2062
2063 private ProcessOutputState createProcessOutputState(String cmd) {
2064 Process process = null;
2065 Thread stderrPump = null;
2066 Thread stdoutPump = null;
2067 PrintStream processOutput = null;
2068 try {
2069 processOutput = awkSink.getPrintStream();
2070 process = spawnProcess(cmd);
2071 stderrPump = DataPump.dumpAndReturnThread(cmd + " stderr", process.getErrorStream(), error);
2072 stdoutPump = DataPump.dumpAndReturnThread(cmd + " stdout", process.getInputStream(), processOutput);
2073 PrintStream processInput = new PrintStream(process.getOutputStream(), true, StandardCharsets.UTF_8.name());
2074 return new ProcessOutputState(
2075 process,
2076 new OutputStreamAwkSink(processInput, locale),
2077 processOutput,
2078 stdoutPump,
2079 stderrPump);
2080 } catch (IOException ioe) {
2081 if (process != null) {
2082 process.destroy();
2083 }
2084 joinDataPump(stdoutPump);
2085 joinDataPump(stderrPump);
2086 throw new AwkRuntimeException("Can't spawn " + cmd + ": " + ioe);
2087 }
2088 }
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105 public Integer jrtClose(String fileNameParam) {
2106 boolean b1 = jrtCloseFileReader(fileNameParam);
2107 boolean b2 = jrtCloseCommandReader(fileNameParam);
2108 boolean b3 = jrtCloseOutputFile(fileNameParam);
2109 boolean b4 = jrtCloseOutputStream(fileNameParam);
2110
2111 return (b1 || b2 || b3 || b4) ? ZERO : MINUS_ONE;
2112 }
2113
2114
2115
2116
2117
2118
2119 public void jrtCloseAll() {
2120 IoState state = ioState;
2121 if (state == null) {
2122 return;
2123 }
2124 Set<String> set = new HashSet<String>();
2125 for (String s : state.fileReaders.keySet()) {
2126 set.add(s);
2127 }
2128 for (String s : state.commandInputs.keySet()) {
2129 set.add(s);
2130 }
2131 for (String s : state.fileOutputs.keySet()) {
2132 set.add(s);
2133 }
2134 for (String s : state.processOutputs.keySet()) {
2135 set.add(s);
2136 }
2137 for (String s : set) {
2138 jrtClose(s);
2139 }
2140 }
2141
2142 private boolean jrtCloseOutputFile(String fileNameParam) {
2143 IoState state = ioState;
2144 if (state == null) {
2145 return false;
2146 }
2147 FileOutputState outputState = state.fileOutputs.remove(fileNameParam);
2148 if (outputState != null) {
2149 outputState.sink.getPrintStream().close();
2150 }
2151 return outputState != null;
2152 }
2153
2154 private boolean jrtCloseOutputStream(String cmd) {
2155 IoState state = ioState;
2156 if (state == null) {
2157 return false;
2158 }
2159 ProcessOutputState outputState = state.processOutputs.remove(cmd);
2160 if (outputState == null) {
2161 return false;
2162 }
2163 outputState.sink.getPrintStream().close();
2164 try {
2165
2166
2167 outputState.process.waitFor();
2168 outputState.process.exitValue();
2169 } catch (InterruptedException ie) {
2170 Thread.currentThread().interrupt();
2171 outputState.process.destroyForcibly();
2172 throw new AwkRuntimeException(
2173 "Caught exception while waiting for process exit: " + ie);
2174 } finally {
2175 joinDataPump(outputState.stdoutPump);
2176 joinDataPump(outputState.stderrPump);
2177 outputState.processOutput.flush();
2178 error.flush();
2179 }
2180 return true;
2181 }
2182
2183 private boolean jrtCloseFileReader(String fileNameParam) {
2184 IoState state = ioState;
2185 if (state == null) {
2186 return false;
2187 }
2188 PartitioningReader pr = state.fileReaders.get(fileNameParam);
2189 if (pr == null) {
2190 return false;
2191 }
2192 state.fileReaders.remove(fileNameParam);
2193 try {
2194 pr.close();
2195 return true;
2196 } catch (IOException ioe) {
2197 return false;
2198 }
2199 }
2200
2201 private boolean jrtCloseCommandReader(String cmd) {
2202 IoState state = ioState;
2203 if (state == null) {
2204 return false;
2205 }
2206 CommandInputState commandInput = state.commandInputs.remove(cmd);
2207 if (commandInput == null) {
2208 return false;
2209 }
2210 try {
2211 commandInput.reader.close();
2212 try {
2213
2214
2215 commandInput.process.waitFor();
2216 commandInput.process.exitValue();
2217 } catch (InterruptedException ie) {
2218 Thread.currentThread().interrupt();
2219 commandInput.process.destroyForcibly();
2220 throw new AwkRuntimeException(
2221 "Caught exception while waiting for process exit: " + ie);
2222 }
2223 return true;
2224 } catch (IOException ioe) {
2225 return false;
2226 } finally {
2227 joinDataPump(commandInput.errorPump);
2228 error.flush();
2229 }
2230 }
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245 public Integer jrtSystem(String cmd) {
2246 try {
2247 PrintStream processOutput = awkSink.getPrintStream();
2248 Process p = spawnProcess(cmd);
2249
2250 p.getOutputStream().close();
2251 Thread errorPump = DataPump.dumpAndReturnThread(cmd + " stderr", p.getErrorStream(), error);
2252 Thread outputPump = DataPump.dumpAndReturnThread(cmd + " stdout", p.getInputStream(), processOutput);
2253 boolean interrupted = false;
2254 int retcode;
2255 while (true) {
2256 try {
2257 retcode = p.waitFor();
2258 break;
2259 } catch (InterruptedException ie) {
2260
2261 interrupted = true;
2262 }
2263 }
2264 joinDataPump(outputPump);
2265 joinDataPump(errorPump);
2266 processOutput.flush();
2267 error.flush();
2268 if (interrupted) {
2269 Thread.currentThread().interrupt();
2270 }
2271 return Integer.valueOf(retcode);
2272 } catch (IOException ioe) {
2273 return MINUS_ONE;
2274 }
2275 }
2276
2277 private static void joinDataPump(Thread pump) {
2278 if (pump == null) {
2279 return;
2280 }
2281 boolean interrupted = false;
2282 while (true) {
2283 try {
2284 pump.join();
2285 break;
2286 } catch (InterruptedException ie) {
2287 interrupted = true;
2288 }
2289 }
2290 if (interrupted) {
2291 Thread.currentThread().interrupt();
2292 }
2293 }
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306 public static String sprintfNoCatch(Locale locale, String fmtArg, Object... arr) throws IllegalFormatException {
2307 return String.format(locale, fmtArg, arr);
2308 }
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319 public static void printfNoCatch(Locale locale, String fmtArg, Object... arr) {
2320 System.out.print(sprintfNoCatch(locale, fmtArg, arr));
2321 }
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333 public static void printfNoCatch(PrintStream ps, Locale locale, String fmtArg, Object... arr) {
2334 ps.print(sprintfNoCatch(locale, fmtArg, arr));
2335 }
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346 public static String substr(Object startposObj, String str) {
2347 int startpos = (int) toDouble(startposObj);
2348 if (startpos <= 0) {
2349 throw new AwkRuntimeException("2nd arg to substr must be a positive integer");
2350 }
2351 if (startpos > str.length()) {
2352 return "";
2353 } else {
2354 return str.substring(startpos - 1);
2355 }
2356 }
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368 public static String substr(Object sizeObj, Object startposObj, String str) {
2369 int startpos = (int) toDouble(startposObj);
2370 if (startpos <= 0) {
2371 throw new AwkRuntimeException("2nd arg to substr must be a positive integer");
2372 }
2373 if (startpos > str.length()) {
2374 return "";
2375 }
2376 int size = (int) toDouble(sizeObj);
2377 if (size < 0) {
2378 throw new AwkRuntimeException("3nd arg to substr must be a non-negative integer");
2379 }
2380 if (startpos + size > str.length()) {
2381 return str.substring(startpos - 1);
2382 } else {
2383 return str.substring(startpos - 1, startpos + size - 1);
2384 }
2385 }
2386
2387
2388
2389
2390
2391
2392
2393
2394 public static int timeSeed() {
2395 long l = new Date().getTime();
2396 long l2 = l % (1000 * 60 * 60 * 24);
2397 int seed = (int) l2;
2398 return seed;
2399 }
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409 public static BSDRandom newRandom(int seed) {
2410 return new BSDRandom(seed);
2411 }
2412
2413
2414
2415
2416
2417
2418
2419
2420 public void applyRS(Object rsObj) {
2421 if (activeSource instanceof StreamInputSource) {
2422 ((StreamInputSource) activeSource).setRecordSeparator(rsObj.toString());
2423 }
2424 }
2425 }