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