1 package org.sentrysoftware.jawk.backend;
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.IOException;
27 import java.io.PrintStream;
28 import java.io.StringReader;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.Collections;
32 import java.util.Enumeration;
33 import java.util.HashMap;
34 import java.util.List;
35 import java.util.Locale;
36 import java.util.Map;
37 import java.util.Random;
38 import java.util.Set;
39 import java.util.StringTokenizer;
40 import java.util.regex.Matcher;
41 import java.util.regex.Pattern;
42
43 import org.sentrysoftware.jawk.ExitException;
44 import org.sentrysoftware.jawk.ext.JawkExtension;
45 import org.sentrysoftware.jawk.frontend.AwkSyntaxTree;
46 import org.sentrysoftware.jawk.intermediate.Address;
47 import org.sentrysoftware.jawk.intermediate.AwkTuples;
48 import org.sentrysoftware.jawk.intermediate.Position;
49 import org.sentrysoftware.jawk.intermediate.PositionForInterpretation;
50 import org.sentrysoftware.jawk.intermediate.UninitializedObject;
51 import org.sentrysoftware.jawk.jrt.AssocArray;
52 import org.sentrysoftware.jawk.jrt.AwkRuntimeException;
53 import org.sentrysoftware.jawk.jrt.BlockManager;
54 import org.sentrysoftware.jawk.jrt.BlockObject;
55 import org.sentrysoftware.jawk.jrt.CharacterTokenizer;
56 import org.sentrysoftware.jawk.jrt.JRT;
57 import org.sentrysoftware.jawk.jrt.KeyList;
58 import org.sentrysoftware.jawk.jrt.KeyListImpl;
59 import org.sentrysoftware.jawk.jrt.ConditionPair;
60 import org.sentrysoftware.jawk.jrt.RegexTokenizer;
61 import org.sentrysoftware.jawk.jrt.SingleCharacterTokenizer;
62 import org.sentrysoftware.jawk.jrt.VariableManager;
63 import org.sentrysoftware.jawk.util.ArrayStackImpl;
64 import org.sentrysoftware.jawk.util.AwkLogger;
65 import org.sentrysoftware.jawk.util.AwkSettings;
66 import org.sentrysoftware.jawk.util.LinkedListStackImpl;
67 import org.sentrysoftware.jawk.util.MyStack;
68 import org.sentrysoftware.jawk.util.ScriptSource;
69 import org.sentrysoftware.printf4j.Printf4J;
70 import org.slf4j.Logger;
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101 public class AVM implements AwkInterpreter, VariableManager {
102
103 private static final Logger LOG = AwkLogger.getLogger(AVM.class);
104 private static final boolean IS_WINDOWS = (System.getProperty("os.name").indexOf("Windows") >= 0);
105
106 private RuntimeStack runtime_stack = new RuntimeStack();
107
108
109
110
111
112 private MyStack<Object> operand_stack = new ArrayStackImpl<Object>();
113 private java.util.List<String> arguments;
114 private boolean sorted_array_keys;
115 private Map<String, Object> initial_variables;
116 private String initial_fs_value;
117 private boolean trap_illegal_format_exceptions;
118 private JRT jrt;
119 final private Locale locale;
120 private Map<String, JawkExtension> extensions;
121
122
123
124
125 private Object pop() { return operand_stack.pop(); }
126 private void push(Object o) { operand_stack.push(o); }
127
128 private final AwkSettings settings;
129
130
131
132
133
134
135
136 public AVM() {
137 settings = null;
138 arguments = new ArrayList<String>();
139 sorted_array_keys = false;
140 initial_variables = new HashMap<String, Object>();
141 initial_fs_value = null;
142 trap_illegal_format_exceptions = false;
143 jrt = new JRT(this);
144 locale = Locale.getDefault();
145 this.extensions = Collections.emptyMap();
146 }
147
148
149
150
151
152
153
154
155
156 public AVM(final AwkSettings parameters, final Map<String, JawkExtension> extensions) {
157 if (parameters == null) {
158 throw new IllegalArgumentException("AwkSettings can not be null");
159 }
160 this.settings = parameters;
161 locale = settings.getLocale();
162 arguments = parameters.getNameValueOrFileNames();
163 sorted_array_keys = parameters.isUseSortedArrayKeys();
164 initial_variables = parameters.getVariables();
165 initial_fs_value = parameters.getFieldSeparator();
166 trap_illegal_format_exceptions = parameters.isCatchIllegalFormatExceptions();
167 jrt = new JRT(this);
168 this.extensions = extensions;
169 for (JawkExtension ext : extensions.values()) {
170 ext.init(this, jrt, settings);
171 }
172 }
173
174 private long nf_offset = NULL_OFFSET;
175 private long nr_offset = NULL_OFFSET;
176 private long fnr_offset = NULL_OFFSET;
177 private long fs_offset = NULL_OFFSET;
178 private long rs_offset = NULL_OFFSET;
179 private long ofs_offset = NULL_OFFSET;
180 private long ors_offset = NULL_OFFSET;
181 private long rstart_offset = NULL_OFFSET;
182 private long rlength_offset = NULL_OFFSET;
183 private long filename_offset = NULL_OFFSET;
184 private long subsep_offset = NULL_OFFSET;
185 private long convfmt_offset = NULL_OFFSET;
186 private long ofmt_offset = NULL_OFFSET;
187 private long environ_offset = NULL_OFFSET;
188 private long argc_offset = NULL_OFFSET;
189 private long argv_offset = NULL_OFFSET;
190
191 private static final Integer ZERO = Integer.valueOf(0);
192 private static final Integer ONE = Integer.valueOf(1);
193
194 private Random random_number_generator;
195 private int oldseed;
196
197 private Address exit_address = null;
198
199
200
201
202
203 private boolean within_end_blocks = false;
204
205
206
207
208 private int exit_code = 0;
209
210
211
212
213 private boolean throw_exit_exception = false;
214
215
216
217
218
219
220 private Map<String, Integer> global_variable_offsets;
221
222
223
224
225 private Map<String, Boolean> global_variable_arrays;
226 private Set<String> function_names;
227
228 private static int parseIntField(Object obj, PositionForInterpretation position) {
229
230 int fieldVal;
231
232 if (obj instanceof Number) {
233 fieldVal = ((Number) obj).intValue();
234 } else {
235 try {
236 fieldVal = (int) Double.parseDouble(obj.toString());
237 } catch (NumberFormatException nfe) {
238 throw new AwkRuntimeException(position.lineNumber(), "Field $(" + obj.toString() + ") is incorrect.");
239 }
240 }
241
242 return fieldVal;
243 }
244
245 private void setNumOnJRT(int fieldNum, double num) {
246
247 String numString;
248 if (num == (long) num) {
249 numString = Long.toString((long) num);
250 } else {
251 numString = Double.toString(num);
252 }
253
254
255 if (fieldNum == 0) {
256 jrt.setInputLine(numString.toString());
257 jrt.jrtParseFields();
258 } else {
259 jrt.jrtSetInputField(numString, fieldNum);
260 }
261 }
262
263 private String execSubOrGSub(PositionForInterpretation position, int gsubArgPos) {
264
265 String newString;
266
267
268
269
270
271 boolean is_gsub = position.boolArg(gsubArgPos);
272 String convfmt = getCONVFMT().toString();
273 String orig = JRT.toAwkString(pop(), convfmt, locale);
274 String repl = JRT.toAwkString(pop(), convfmt, locale);
275 String ere = JRT.toAwkString(pop(), convfmt, locale);
276 if (is_gsub) {
277 newString = replaceAll(orig, ere, repl);
278 } else {
279 newString = replaceFirst(orig, ere, repl);
280 }
281
282 return newString;
283 }
284
285
286
287
288
289
290
291
292 @Override
293 public void interpret(AwkTuples tuples)
294 throws ExitException, IOException
295 {
296 Map<String, Pattern> regexps = new HashMap<String, Pattern>();
297 Map<Integer, ConditionPair> condition_pairs = new HashMap<Integer, ConditionPair>();
298
299 global_variable_offsets = tuples.getGlobalVariableOffsetMap();
300 global_variable_arrays = tuples.getGlobalVariableAarrayMap();
301 function_names = tuples.getFunctionNameSet();
302
303 PositionForInterpretation position = (PositionForInterpretation) tuples.top();
304
305 try {
306 while (!position.isEOF()) {
307
308 int opcode = position.opcode();
309
310 switch (opcode) {
311 case AwkTuples._PRINT_: {
312
313
314
315
316 long num_args = position.intArg(0);
317 printTo(settings.getOutputStream(), num_args);
318 position.next();
319 break;
320 }
321 case AwkTuples._PRINT_TO_FILE_: {
322
323
324
325
326
327
328 long num_args = position.intArg(0);
329 boolean append = position.boolArg(1);
330 String key = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
331 PrintStream ps = jrt.getOutputFiles().get(key);
332 if (ps == null) {
333 try {
334 jrt.getOutputFiles().put(key, ps = new PrintStream(new FileOutputStream(key, append), true));
335 } catch (IOException ioe) {
336 throw new AwkRuntimeException(position.lineNumber(), "Cannot open " + key + " for writing: " + ioe);
337 }
338 }
339 printTo(ps, num_args);
340 position.next();
341 break;
342 }
343 case AwkTuples._PRINT_TO_PIPE_: {
344
345
346
347
348
349 long num_args = position.intArg(0);
350 String cmd = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
351 PrintStream ps = jrt.jrtSpawnForOutput(cmd);
352 printTo(ps, num_args);
353 position.next();
354 break;
355 }
356 case AwkTuples._PRINTF_: {
357
358
359
360
361 long num_args = position.intArg(0);
362 printfTo(settings.getOutputStream(), num_args);
363 position.next();
364 break;
365 }
366 case AwkTuples._PRINTF_TO_FILE_: {
367
368
369
370
371
372
373 long num_args = position.intArg(0);
374 boolean append = position.boolArg(1);
375 String key = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
376 PrintStream ps = jrt.getOutputFiles().get(key);
377 if (ps == null) {
378 try {
379 jrt.getOutputFiles().put(key, ps = new PrintStream(new FileOutputStream(key, append), true));
380 } catch (IOException ioe) {
381 throw new AwkRuntimeException(position.lineNumber(), "Cannot open " + key + " for writing: " + ioe);
382 }
383 }
384 printfTo(ps, num_args);
385 position.next();
386 break;
387 }
388 case AwkTuples._PRINTF_TO_PIPE_: {
389
390
391
392
393
394 long num_args = position.intArg(0);
395 String cmd = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
396 PrintStream ps = jrt.jrtSpawnForOutput(cmd);
397 printfTo(ps, num_args);
398 position.next();
399 break;
400 }
401 case AwkTuples._SPRINTF_: {
402
403
404
405
406 long num_args = position.intArg(0);
407 push(sprintfFunction(num_args));
408 position.next();
409 break;
410 }
411 case AwkTuples._LENGTH_: {
412
413
414
415
416
417
418 long num = position.intArg(0);
419 if (num == 0) {
420
421 push(jrt.jrtGetInputField(0).toString().length());
422 } else {
423 push(pop().toString().length());
424 }
425 position.next();
426 break;
427 }
428 case AwkTuples._PUSH_: {
429
430 push(position.arg(0));
431 position.next();
432 break;
433 }
434 case AwkTuples._POP_: {
435
436 pop();
437 position.next();
438 break;
439 }
440 case AwkTuples._IFFALSE_: {
441
442
443
444
445
446
447 boolean jump = !jrt.toBoolean(pop());
448 if (jump) {
449 position.jump(position.addressArg());
450 } else {
451 position.next();
452 }
453 break;
454 }
455 case AwkTuples._TO_NUMBER_: {
456
457
458
459
460
461 boolean val = jrt.toBoolean(pop());
462 push(val ? ONE : ZERO);
463 position.next();
464 break;
465 }
466 case AwkTuples._IFTRUE_: {
467
468
469
470
471
472
473 boolean jump = jrt.toBoolean(pop());
474 if (jump) {
475 position.jump(position.addressArg());
476 } else {
477 position.next();
478 }
479 break;
480 }
481 case AwkTuples._NOT_: {
482
483
484
485
486
487 Object o = pop();
488 boolean result;
489 if (o instanceof Integer) {
490 result = ((Integer)o).intValue() != 0;
491 } else if (o instanceof Long) {
492 result = ((Long)o).longValue() != 0;
493 } else if (o instanceof Double) {
494 result = ((Double)o).doubleValue() != 0;
495 } else if (o instanceof String) {
496 result = (o.toString().length() > 0);
497 } else if (o instanceof UninitializedObject) {
498 result = false;
499 } else {
500 throw new Error("Unknown operand_stack type: "+o.getClass()+" for value "+o);
501 }
502 if (result) {
503 push(0);
504 } else {
505 push(1);
506 }
507 position.next();
508 break;
509 }
510 case AwkTuples._NEGATE_: {
511
512
513 double d = JRT.toDouble(pop());
514 if (d == (long) d) {
515 push((long) -d);
516 } else {
517 push(-d);
518 }
519 position.next();
520 break;
521 }
522 case AwkTuples._UNARY_PLUS_: {
523
524 double d = JRT.toDouble(pop());
525 if (d == (long) d) {
526 push((long)d);
527 } else {
528 push(d);
529 }
530 position.next();
531 break;
532 }
533 case AwkTuples._GOTO_: {
534
535
536 position.jump(position.addressArg());
537 break;
538 }
539 case AwkTuples._NOP_: {
540
541 position.next();
542 break;
543 }
544 case AwkTuples._CONCAT_: {
545
546
547 String convfmt = getCONVFMT().toString();
548 String s2 = JRT.toAwkString(pop(), convfmt, locale);
549 String s1 = JRT.toAwkString(pop(), convfmt, locale);
550 String result_string = s1 + s2;
551 push(result_string);
552 position.next();
553 break;
554 }
555 case AwkTuples._ASSIGN_: {
556
557
558
559 Object value = pop();
560 boolean is_global = position.boolArg(1);
561 assign(position.intArg(0), value, is_global, position);
562 position.next();
563 break;
564 }
565 case AwkTuples._ASSIGN_ARRAY_: {
566
567
568
569
570 Object arr_idx = pop();
571 Object rhs = pop();
572 if (rhs == null) {
573 rhs = BLANK;
574 }
575 long offset = position.intArg(0);
576 boolean is_global = position.boolArg(1);
577 assignArray(offset, arr_idx, rhs, is_global);
578 position.next();
579 break;
580 }
581 case AwkTuples._PLUS_EQ_ARRAY_:
582 case AwkTuples._MINUS_EQ_ARRAY_:
583 case AwkTuples._MULT_EQ_ARRAY_:
584 case AwkTuples._DIV_EQ_ARRAY_:
585 case AwkTuples._MOD_EQ_ARRAY_:
586 case AwkTuples._POW_EQ_ARRAY_: {
587
588
589
590
591 Object arr_idx = pop();
592 Object rhs = pop();
593 if (rhs == null) {
594 rhs = BLANK;
595 }
596 long offset = position.intArg(0);
597 boolean is_global = position.boolArg(1);
598
599 double val = JRT.toDouble(rhs);
600
601
602
603
604 Object o1 = runtime_stack.getVariable(offset, is_global);
605 if (o1 == null || o1 instanceof UninitializedObject) {
606 runtime_stack.setVariable(offset, o1 = new AssocArray(sorted_array_keys), is_global);
607 } else {
608 assert o1 instanceof AssocArray;
609 }
610
611 AssocArray array = (AssocArray) o1;
612 Object o = array.get(arr_idx);
613 assert o != null;
614 double orig_val = JRT.toDouble(o);
615
616 double new_val;
617
618 switch (opcode) {
619 case AwkTuples._PLUS_EQ_ARRAY_:
620 new_val = orig_val + val;
621 break;
622 case AwkTuples._MINUS_EQ_ARRAY_:
623 new_val = orig_val - val;
624 break;
625 case AwkTuples._MULT_EQ_ARRAY_:
626 new_val = orig_val * val;
627 break;
628 case AwkTuples._DIV_EQ_ARRAY_:
629 new_val = orig_val / val;
630 break;
631 case AwkTuples._MOD_EQ_ARRAY_:
632 new_val = orig_val % val;
633 break;
634 case AwkTuples._POW_EQ_ARRAY_:
635 new_val = Math.pow(orig_val, val);
636 break;
637 default:
638 throw new Error("Invalid op code here: " + opcode);
639 }
640
641 if (new_val == (long) new_val) {
642 assignArray(offset, arr_idx, (long) new_val, is_global);
643 } else {
644 assignArray(offset, arr_idx, new_val, is_global);
645 }
646 position.next();
647 break;
648 }
649 case AwkTuples._ASSIGN_AS_INPUT_: {
650
651 jrt.setInputLine(pop().toString());
652 jrt.jrtParseFields();
653 push(jrt.getInputLine());
654 position.next();
655 break;
656 }
657 case AwkTuples._ASSIGN_AS_INPUT_FIELD_: {
658
659
660 Object field_num_obj = pop();
661 int field_num;
662 if (field_num_obj instanceof Number) {
663 field_num = ((Number) field_num_obj).intValue();
664 } else {
665 try {
666 field_num = Integer.parseInt(field_num_obj.toString());
667 } catch (NumberFormatException nfe) {
668 field_num = 0;
669 }
670 }
671 String value = pop().toString();
672 push(value);
673 if (field_num == 0) {
674 jrt.setInputLine(value);
675 jrt.jrtParseFields();
676 } else {
677 jrt.jrtSetInputField(value, field_num);
678 }
679 position.next();
680 break;
681 }
682 case AwkTuples._PLUS_EQ_:
683 case AwkTuples._MINUS_EQ_:
684 case AwkTuples._MULT_EQ_:
685 case AwkTuples._DIV_EQ_:
686 case AwkTuples._MOD_EQ_:
687 case AwkTuples._POW_EQ_: {
688
689
690
691 boolean is_global = position.boolArg(1);
692 Object o1 = runtime_stack.getVariable(position.intArg(0), is_global);
693 if (o1 == null) {
694 o1 = BLANK;
695 }
696 Object o2 = pop();
697 double d1 = JRT.toDouble(o1);
698 double d2 = JRT.toDouble(o2);
699 double ans;
700 switch (opcode) {
701 case AwkTuples._PLUS_EQ_:
702 ans = d1 + d2;
703 break;
704 case AwkTuples._MINUS_EQ_:
705 ans = d1 - d2;
706 break;
707 case AwkTuples._MULT_EQ_:
708 ans = d1 * d2;
709 break;
710 case AwkTuples._DIV_EQ_:
711 ans = d1 / d2;
712 break;
713 case AwkTuples._MOD_EQ_:
714 ans = d1 % d2;
715 break;
716 case AwkTuples._POW_EQ_:
717 ans = Math.pow(d1, d2);
718 break;
719 default:
720 throw new Error("Invalid opcode here: " + opcode);
721 }
722 if (ans == (long) ans) {
723 push((long) ans);
724 runtime_stack.setVariable(position.intArg(0), (int) ans, is_global);
725 } else {
726 push(ans);
727 runtime_stack.setVariable(position.intArg(0), ans, is_global);
728 }
729 position.next();
730 break;
731 }
732 case AwkTuples._PLUS_EQ_INPUT_FIELD_:
733 case AwkTuples._MINUS_EQ_INPUT_FIELD_:
734 case AwkTuples._MULT_EQ_INPUT_FIELD_:
735 case AwkTuples._DIV_EQ_INPUT_FIELD_:
736 case AwkTuples._MOD_EQ_INPUT_FIELD_:
737 case AwkTuples._POW_EQ_INPUT_FIELD_: {
738
739
740
741
742 int fieldnum = parseIntField(pop(), position);
743 double incval = JRT.toDouble(pop());
744
745
746 Object num_obj = jrt.jrtGetInputField(fieldnum);
747 double num;
748 switch (opcode) {
749 case AwkTuples._PLUS_EQ_INPUT_FIELD_:
750 num = JRT.toDouble(num_obj) + incval;
751 break;
752 case AwkTuples._MINUS_EQ_INPUT_FIELD_:
753 num = JRT.toDouble(num_obj) - incval;
754 break;
755 case AwkTuples._MULT_EQ_INPUT_FIELD_:
756 num = JRT.toDouble(num_obj) * incval;
757 break;
758 case AwkTuples._DIV_EQ_INPUT_FIELD_:
759 num = JRT.toDouble(num_obj) / incval;
760 break;
761 case AwkTuples._MOD_EQ_INPUT_FIELD_:
762 num = JRT.toDouble(num_obj) % incval;
763 break;
764 case AwkTuples._POW_EQ_INPUT_FIELD_:
765 num = Math.pow(JRT.toDouble(num_obj), incval);
766 break;
767 default:
768 throw new Error("Invalid opcode here: " + opcode);
769 }
770 setNumOnJRT(fieldnum, num);
771
772
773 push(num);
774 position.next();
775
776 break;
777 }
778 case AwkTuples._INC_: {
779
780
781 inc(position.intArg(0), position.boolArg(1));
782 position.next();
783 break;
784 }
785 case AwkTuples._DEC_: {
786
787
788 dec(position.intArg(0), position.boolArg(1));
789 position.next();
790 break;
791 }
792 case AwkTuples._POSTINC_: {
793
794
795 pop();
796 push(inc(position.intArg(0), position.boolArg(1)));
797 position.next();
798 break;
799 }
800 case AwkTuples._POSTDEC_: {
801
802
803 pop();
804 push(dec(position.intArg(0), position.boolArg(1)));
805 position.next();
806 break;
807 }
808 case AwkTuples._INC_ARRAY_REF_: {
809
810
811
812 boolean is_global = position.boolArg(1);
813 Object o1 = runtime_stack.getVariable(position.intArg(0), is_global);
814 if (o1 == null || o1 instanceof UninitializedObject) {
815 runtime_stack.setVariable(position.intArg(0), o1 = new AssocArray(sorted_array_keys), is_global);
816 }
817 AssocArray aa = (AssocArray) o1;
818 Object key = pop();
819 Object o = aa.get(key);
820 assert o != null;
821 double ans = JRT.toDouble(o) + 1;
822 if (ans == (long) ans) {
823 aa.put(key, (long) ans);
824 } else {
825 aa.put(key, ans);
826 }
827 position.next();
828 break;
829 }
830 case AwkTuples._DEC_ARRAY_REF_: {
831
832
833
834 boolean is_global = position.boolArg(1);
835 Object o1 = runtime_stack.getVariable(position.intArg(0), is_global);
836 if (o1 == null || o1 instanceof UninitializedObject) {
837 runtime_stack.setVariable(position.intArg(0), o1 = new AssocArray(sorted_array_keys), is_global);
838 }
839 AssocArray aa = (AssocArray) o1;
840 Object key = pop();
841 Object o = aa.get(key);
842 assert o != null;
843 double ans = JRT.toDouble(o) - 1;
844 if (ans == (long) ans) {
845 aa.put(key, (long) ans);
846 } else {
847 aa.put(key, ans);
848 }
849 position.next();
850 break;
851 }
852 case AwkTuples._INC_DOLLAR_REF_: {
853
854
855 int fieldnum = parseIntField(pop(), position);
856
857
858 Object num_obj = jrt.jrtGetInputField(fieldnum);
859 double num = JRT.toDouble(num_obj) + 1;
860 setNumOnJRT(fieldnum, num);
861
862 position.next();
863 break;
864 }
865 case AwkTuples._DEC_DOLLAR_REF_: {
866
867
868 int fieldnum = parseIntField(pop(), position);
869
870
871 Object num_obj = jrt.jrtGetInputField(fieldnum);
872 double num = JRT.toDouble(num_obj) - 1;
873 setNumOnJRT(fieldnum, num);
874
875 position.next();
876 break;
877 }
878 case AwkTuples._DEREFERENCE_: {
879
880
881 boolean is_global = position.boolArg(2);
882 Object o = runtime_stack.getVariable(position.intArg(0), is_global);
883 if (o == null) {
884 if (position.boolArg(1)) {
885
886 push(runtime_stack.setVariable(position.intArg(0), new AssocArray(sorted_array_keys), is_global));
887 } else {
888 push(runtime_stack.setVariable(position.intArg(0), BLANK, is_global));
889 }
890 } else {
891 push(o);
892 }
893 position.next();
894 break;
895 }
896 case AwkTuples._DEREF_ARRAY_: {
897
898
899 Object idx = pop();
900 Object array = pop();
901 if (!(array instanceof AssocArray)) {
902 throw new AwkRuntimeException("Attempting to index a non-associative-array.");
903 }
904 Object o = ((AssocArray) array).get(idx);
905 assert o != null;
906 push(o);
907 position.next();
908 break;
909 }
910 case AwkTuples._SRAND_: {
911
912
913 long numargs = position.intArg(0);
914 int seed;
915 if (numargs == 0) {
916
917 seed = JRT.timeSeed();
918 } else {
919 Object o = pop();
920 if (o instanceof Double) {
921 seed = ((Double) o).intValue();
922 } else if (o instanceof Long) {
923 seed = ((Long) o).intValue();
924 } else if (o instanceof Integer) {
925 seed = ((Integer) o).intValue();
926 } else {
927 try {
928 seed = Integer.parseInt(o.toString());
929 } catch (NumberFormatException nfe) {
930 seed = 0;
931 }
932 }
933 }
934 random_number_generator = new Random(seed);
935 push(oldseed);
936 oldseed = seed;
937 position.next();
938 break;
939 }
940 case AwkTuples._RAND_: {
941 if (random_number_generator == null) {
942 int seed = JRT.timeSeed();
943 random_number_generator = new Random(seed);
944 oldseed = seed;
945 }
946 push(random_number_generator.nextDouble());
947 position.next();
948 break;
949 }
950 case AwkTuples._INTFUNC_:
951 case AwkTuples._CAST_INT_: {
952
953 push((long) JRT.toDouble(pop()));
954 position.next();
955 break;
956 }
957 case AwkTuples._SQRT_: {
958
959 push(Math.sqrt(JRT.toDouble(pop())));
960 position.next();
961 break;
962 }
963 case AwkTuples._LOG_: {
964
965 push(Math.log(JRT.toDouble(pop())));
966 position.next();
967 break;
968 }
969 case AwkTuples._EXP_: {
970
971 push(Math.exp(JRT.toDouble(pop())));
972 position.next();
973 break;
974 }
975 case AwkTuples._SIN_: {
976
977 push(Math.sin(JRT.toDouble(pop())));
978 position.next();
979 break;
980 }
981 case AwkTuples._COS_: {
982
983 push(Math.cos(JRT.toDouble(pop())));
984 position.next();
985 break;
986 }
987 case AwkTuples._ATAN2_: {
988
989
990 double d2 = JRT.toDouble(pop());
991 double d1 = JRT.toDouble(pop());
992 push(Math.atan2(d1, d2));
993 position.next();
994 break;
995 }
996 case AwkTuples._MATCH_: {
997
998
999 String convfmt = getCONVFMT().toString();
1000 String ere = JRT.toAwkString(pop(), convfmt, locale);
1001 String s = JRT.toAwkString(pop(), convfmt, locale);
1002
1003
1004 int flags = 0;
1005
1006 if (global_variable_offsets.containsKey("IGNORECASE")) {
1007 Integer offset_obj = global_variable_offsets.get("IGNORECASE");
1008 Object ignorecase = runtime_stack.getVariable(offset_obj, true);
1009
1010 if (JRT.toDouble(ignorecase) != 0) {
1011 flags |= Pattern.CASE_INSENSITIVE;
1012 }
1013 }
1014
1015 Pattern pattern = Pattern.compile(ere, flags);
1016 Matcher matcher = pattern.matcher(s);
1017 boolean result = matcher.find();
1018 if (result) {
1019 assign(rstart_offset, matcher.start() + 1, true, position);
1020 assign(rlength_offset, matcher.end() - matcher.start(), true, position);
1021 pop();
1022
1023 } else {
1024 assign(rstart_offset, ZERO, true, position);
1025 assign(rlength_offset, -1, true, position);
1026 pop();
1027
1028 }
1029 position.next();
1030 break;
1031 }
1032 case AwkTuples._INDEX_: {
1033
1034
1035 String convfmt = getCONVFMT().toString();
1036 String s2 = JRT.toAwkString(pop(), convfmt, locale);
1037 String s1 = JRT.toAwkString(pop(), convfmt, locale);
1038 push(s1.indexOf(s2) + 1);
1039 position.next();
1040 break;
1041 }
1042 case AwkTuples._SUB_FOR_DOLLAR_0_: {
1043
1044
1045
1046 boolean is_gsub = position.boolArg(0);
1047
1048
1049
1050 String convfmt = getCONVFMT().toString();
1051 String repl = JRT.toAwkString(pop(), convfmt, locale);
1052 String ere = JRT.toAwkString(pop(), convfmt, locale);
1053 String orig = JRT.toAwkString(jrt.jrtGetInputField(0), convfmt, locale);
1054 String newstring;
1055 if (is_gsub) {
1056 newstring = replaceAll(orig, ere, repl);
1057 } else {
1058 newstring = replaceFirst(orig, ere, repl);
1059 }
1060
1061 jrt.setInputLine(newstring);
1062 jrt.jrtParseFields();
1063 position.next();
1064 break;
1065 }
1066 case AwkTuples._SUB_FOR_DOLLAR_REFERENCE_: {
1067
1068
1069
1070
1071
1072
1073 String newString = execSubOrGSub(position, 0);
1074 int fieldNum = (int) JRT.toDouble(pop());
1075
1076 if (fieldNum == 0) {
1077 jrt.setInputLine(newString);
1078 jrt.jrtParseFields();
1079 } else {
1080 jrt.jrtSetInputField(newString, fieldNum);
1081 }
1082 position.next();
1083 break;
1084 }
1085 case AwkTuples._SUB_FOR_VARIABLE_: {
1086
1087
1088
1089
1090
1091
1092 long offset = position.intArg(0);
1093 boolean is_global = position.boolArg(1);
1094 String newString = execSubOrGSub(position, 2);
1095
1096 assign(offset, newString, is_global, position);
1097 pop();
1098 position.next();
1099 break;
1100 }
1101 case AwkTuples._SUB_FOR_ARRAY_REFERENCE_: {
1102
1103
1104
1105
1106
1107
1108
1109
1110 long offset = position.intArg(0);
1111 boolean is_global = position.boolArg(1);
1112 Object arr_idx = pop();
1113 String newString = execSubOrGSub(position, 2);
1114
1115 assignArray(offset, arr_idx, newString, is_global);
1116 pop();
1117 position.next();
1118 break;
1119 }
1120 case AwkTuples._SPLIT_: {
1121
1122
1123
1124
1125 String convfmt = getCONVFMT().toString();
1126 long numargs = position.intArg(0);
1127 String fs_string;
1128 if (numargs == 2) {
1129 fs_string = JRT.toAwkString(getFS(), convfmt, locale);
1130 } else if (numargs == 3) {
1131 fs_string = JRT.toAwkString(pop(), convfmt, locale);
1132 } else {
1133 throw new Error("Invalid # of args. split() requires 2 or 3. Got: " + numargs);
1134 }
1135 Object o = pop();
1136 if (!(o instanceof AssocArray)) {
1137 throw new AwkRuntimeException(position.lineNumber(), o + " is not an array.");
1138 }
1139 String s = JRT.toAwkString(pop(), convfmt, locale);
1140 Enumeration<Object> tokenizer;
1141 if (fs_string.equals(" ")) {
1142 tokenizer = new StringTokenizer(s);
1143 } else if (fs_string.length() == 1) {
1144 tokenizer = new SingleCharacterTokenizer(s, fs_string.charAt(0));
1145 } else if (fs_string.isEmpty()) {
1146 tokenizer = new CharacterTokenizer(s);
1147 } else {
1148 tokenizer = new RegexTokenizer(s, fs_string);
1149 }
1150
1151 AssocArray assoc_array = (AssocArray) o;
1152 assoc_array.clear();
1153 int cnt = 0;
1154 while (tokenizer.hasMoreElements()) {
1155 assoc_array.put(++cnt, tokenizer.nextElement());
1156 }
1157 push(cnt);
1158 position.next();
1159 break;
1160 }
1161 case AwkTuples._SUBSTR_: {
1162
1163
1164
1165
1166 long numargs = position.intArg(0);
1167 int startPos, length;
1168 String s;
1169 if (numargs == 3)
1170 {
1171 length = (int) JRT.toLong(pop());
1172 startPos = (int) JRT.toDouble(pop());
1173 s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1174 } else if (numargs == 2) {
1175 startPos = (int) JRT.toDouble(pop());
1176 s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1177 length = s.length() - startPos + 1;
1178 } else {
1179 throw new Error("numargs for _SUBSTR_ must be 2 or 3. It is " + numargs);
1180 }
1181 if (startPos <= 0) {
1182 startPos = 1;
1183 }
1184 if (length <= 0 || startPos > s.length()) {
1185 push(BLANK);
1186 } else {
1187 if (startPos + length > s.length()) {
1188 push(s.substring(startPos - 1));
1189 } else {
1190 push(s.substring(startPos - 1, startPos + length - 1));
1191 }
1192 }
1193 position.next();
1194 break;
1195 }
1196 case AwkTuples._TOLOWER_: {
1197
1198 push(JRT.toAwkString(pop(), getCONVFMT().toString(), locale).toLowerCase());
1199 position.next();
1200 break;
1201 }
1202 case AwkTuples._TOUPPER_: {
1203
1204 push(JRT.toAwkString(pop(), getCONVFMT().toString(), locale).toUpperCase());
1205 position.next();
1206 break;
1207 }
1208 case AwkTuples._SYSTEM_: {
1209
1210 String s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1211 push(JRT.jrtSystem(s));
1212 position.next();
1213 break;
1214 }
1215 case AwkTuples._SWAP_: {
1216
1217
1218 swapOnStack();
1219 position.next();
1220 break;
1221 }
1222 case AwkTuples._CMP_EQ_: {
1223
1224
1225 Object o2 = pop();
1226 Object o1 = pop();
1227 push(JRT.compare2(o1, o2, 0) ? ONE : ZERO);
1228 position.next();
1229 break;
1230 }
1231 case AwkTuples._CMP_LT_: {
1232
1233
1234 Object o2 = pop();
1235 Object o1 = pop();
1236 push(JRT.compare2(o1, o2, -1) ? ONE : ZERO);
1237 position.next();
1238 break;
1239 }
1240 case AwkTuples._CMP_GT_: {
1241
1242
1243 Object o2 = pop();
1244 Object o1 = pop();
1245 push(JRT.compare2(o1, o2, 1) ? ONE : ZERO);
1246 position.next();
1247 break;
1248 }
1249 case AwkTuples._MATCHES_: {
1250
1251
1252 Object o2 = pop();
1253 Object o1 = pop();
1254
1255 String s = o1.toString();
1256
1257 if (o2 instanceof Pattern) {
1258 Pattern p = (Pattern) o2;
1259 Matcher m = p.matcher(s);
1260
1261
1262 boolean result = m.find();
1263 push(result ? 1 : 0);
1264 } else {
1265 String r = JRT.toAwkString(o2, getCONVFMT().toString(), locale);
1266 boolean result = Pattern.compile(r).matcher(s).find();
1267 push(result ? 1 : 0);
1268 }
1269 position.next();
1270 break;
1271 }
1272 case AwkTuples._SLEEP_: {
1273
1274
1275
1276
1277
1278
1279 long seconds;
1280 long numargs = position.intArg(0);
1281 if (numargs == 0) {
1282 seconds = 1;
1283 } else {
1284 seconds = (long) JRT.toDouble(pop());
1285 }
1286 try {
1287 Thread.sleep(seconds * 1000);
1288 } catch (InterruptedException ie) {
1289 throw new AwkRuntimeException(position.lineNumber(), "Caught exception while waiting for process exit: " + ie);
1290 }
1291 position.next();
1292 break;
1293 }
1294 case AwkTuples._DUMP_: {
1295
1296
1297
1298
1299
1300
1301 long numargs = position.intArg(0);
1302 AssocArray[] aa_array;
1303 if (numargs == 0) {
1304 aa_array = null;
1305 } else {
1306 aa_array = new AssocArray[(int) numargs];
1307 for (int i = 0; i < numargs; ++i) {
1308 aa_array[i] = (AssocArray) pop();
1309 }
1310 }
1311 avmDump(aa_array);
1312 position.next();
1313 break;
1314 }
1315 case AwkTuples._ADD_: {
1316
1317
1318 Object o2 = pop();
1319 Object o1 = pop();
1320 double d1 = JRT.toDouble(o1);
1321 double d2 = JRT.toDouble(o2);
1322 double ans = d1 + d2;
1323 if (ans == (long) ans) {
1324 push((long) ans);
1325 } else {
1326 push(ans);
1327 }
1328 position.next();
1329 break;
1330 }
1331 case AwkTuples._SUBTRACT_: {
1332
1333
1334 Object o2 = pop();
1335 Object o1 = pop();
1336 double d1 = JRT.toDouble(o1);
1337 double d2 = JRT.toDouble(o2);
1338 double ans = d1 - d2;
1339 if (ans == (long) ans) {
1340 push((long) ans);
1341 } else {
1342 push(ans);
1343 }
1344 position.next();
1345 break;
1346 }
1347 case AwkTuples._MULTIPLY_: {
1348
1349
1350 Object o2 = pop();
1351 Object o1 = pop();
1352 double d1 = JRT.toDouble(o1);
1353 double d2 = JRT.toDouble(o2);
1354 double ans = d1 * d2;
1355 if (ans == (long) ans) {
1356 push((long) ans);
1357 } else {
1358 push(ans);
1359 }
1360 position.next();
1361 break;
1362 }
1363 case AwkTuples._DIVIDE_: {
1364
1365
1366 Object o2 = pop();
1367 Object o1 = pop();
1368 double d1 = JRT.toDouble(o1);
1369 double d2 = JRT.toDouble(o2);
1370 double ans = d1 / d2;
1371 if (ans == (long) ans) {
1372 push((long) ans);
1373 } else {
1374 push(ans);
1375 }
1376 position.next();
1377 break;
1378 }
1379 case AwkTuples._MOD_: {
1380
1381
1382 Object o2 = pop();
1383 Object o1 = pop();
1384 double d1 = JRT.toDouble(o1);
1385 double d2 = JRT.toDouble(o2);
1386 double ans = d1 % d2;
1387 if (ans == (long) ans) {
1388 push((long) ans);
1389 } else {
1390 push(ans);
1391 }
1392 position.next();
1393 break;
1394 }
1395 case AwkTuples._POW_: {
1396
1397
1398 Object o2 = pop();
1399 Object o1 = pop();
1400 double d1 = JRT.toDouble(o1);
1401 double d2 = JRT.toDouble(o2);
1402 double ans = Math.pow(d1, d2);
1403 if (ans == (long) ans) {
1404 push((long) ans);
1405 } else {
1406 push(ans);
1407 }
1408 position.next();
1409 break;
1410 }
1411 case AwkTuples._DUP_: {
1412
1413 Object o = pop();
1414 push(o);
1415 push(o);
1416 position.next();
1417 break;
1418 }
1419 case AwkTuples._KEYLIST_: {
1420
1421 Object o = pop();
1422 assert o != null;
1423 if (!(o instanceof AssocArray)) {
1424 throw new AwkRuntimeException(position.lineNumber(), "Cannot get a keylist (via 'in') of a non associative array. arg = " + o.getClass() + ", " + o);
1425 }
1426 AssocArray aa = (AssocArray) o;
1427 push(new KeyListImpl(aa.keySet()));
1428 position.next();
1429 break;
1430 }
1431 case AwkTuples._IS_EMPTY_KEYLIST_: {
1432
1433
1434 Object o = pop();
1435 if (o == null || !(o instanceof KeyList)) {
1436 throw new AwkRuntimeException(position.lineNumber(), "Cannot get a keylist (via 'in') of a non associative array. arg = " + o.getClass() + ", " + o);
1437 }
1438 KeyList keylist = (KeyList) o;
1439 if (keylist.size() == 0) {
1440 position.jump(position.addressArg());
1441 } else {
1442 position.next();
1443 }
1444 break;
1445 }
1446 case AwkTuples._GET_FIRST_AND_REMOVE_FROM_KEYLIST_: {
1447
1448 Object o = pop();
1449 if (o == null || !(o instanceof KeyList)) {
1450 throw new AwkRuntimeException(position.lineNumber(), "Cannot get a keylist (via 'in') of a non associative array. arg = " + o.getClass() + ", " + o);
1451 }
1452
1453 KeyList keylist = (KeyList) o;
1454 assert keylist.size() > 0;
1455 push(keylist.getFirstAndRemove());
1456 position.next();
1457 break;
1458 }
1459 case AwkTuples._CHECK_CLASS_: {
1460
1461
1462 Object o = pop();
1463 if (!(position.classArg().isInstance(o))) {
1464 throw new AwkRuntimeException(position.lineNumber(), "Verification failed. Top-of-stack = " + o.getClass() + " isn't an instance of " + position.classArg());
1465 }
1466 push(o);
1467 position.next();
1468 break;
1469 }
1470 case AwkTuples._CONSUME_INPUT_: {
1471
1472
1473
1474 if (avmConsumeInput(false)) {
1475 position.next();
1476 } else {
1477 position.jump(position.addressArg());
1478 }
1479 break;
1480 }
1481 case AwkTuples._GETLINE_INPUT_: {
1482 avmConsumeInputForGetline();
1483 position.next();
1484 break;
1485 }
1486 case AwkTuples._USE_AS_FILE_INPUT_: {
1487
1488 String s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1489 avmConsumeFileInputForGetline(s);
1490 position.next();
1491 break;
1492 }
1493 case AwkTuples._USE_AS_COMMAND_INPUT_: {
1494
1495 String s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1496 avmConsumeCommandInputForGetline(s);
1497 position.next();
1498 break;
1499 }
1500 case AwkTuples._NF_OFFSET_: {
1501
1502 nf_offset = position.intArg(0);
1503 assert nf_offset != NULL_OFFSET;
1504 assign(nf_offset, 0, true, position);
1505 pop();
1506 position.next();
1507 break;
1508 }
1509 case AwkTuples._NR_OFFSET_: {
1510
1511 nr_offset = position.intArg(0);
1512 assert nr_offset != NULL_OFFSET;
1513 assign(nr_offset, 0, true, position);
1514 pop();
1515 position.next();
1516 break;
1517 }
1518 case AwkTuples._FNR_OFFSET_: {
1519
1520 fnr_offset = position.intArg(0);
1521 assert fnr_offset != NULL_OFFSET;
1522 assign(fnr_offset, 0, true, position);
1523 pop();
1524 position.next();
1525 break;
1526 }
1527 case AwkTuples._FS_OFFSET_: {
1528
1529 fs_offset = position.intArg(0);
1530 assert fs_offset != NULL_OFFSET;
1531 if (initial_fs_value == null) {
1532 assign(fs_offset, " ", true, position);
1533 } else {
1534 assign(fs_offset, initial_fs_value, true, position);
1535 }
1536 pop();
1537 position.next();
1538 break;
1539 }
1540 case AwkTuples._RS_OFFSET_: {
1541
1542 rs_offset = position.intArg(0);
1543 assert rs_offset != NULL_OFFSET;
1544 assign(rs_offset, settings.getDefaultRS(), true, position);
1545 pop();
1546 position.next();
1547 break;
1548 }
1549 case AwkTuples._OFS_OFFSET_: {
1550
1551 ofs_offset = position.intArg(0);
1552 assert ofs_offset != NULL_OFFSET;
1553 assign(ofs_offset, " ", true, position);
1554 pop();
1555 position.next();
1556 break;
1557 }
1558 case AwkTuples._ORS_OFFSET_: {
1559
1560 ors_offset = position.intArg(0);
1561 assert ors_offset != NULL_OFFSET;
1562 assign(ors_offset, settings.getDefaultORS(), true, position);
1563 pop();
1564 position.next();
1565 break;
1566 }
1567 case AwkTuples._RSTART_OFFSET_: {
1568
1569 rstart_offset = position.intArg(0);
1570 assert rstart_offset != NULL_OFFSET;
1571 assign(rstart_offset, "", true, position);
1572 pop();
1573 position.next();
1574 break;
1575 }
1576 case AwkTuples._RLENGTH_OFFSET_: {
1577
1578 rlength_offset = position.intArg(0);
1579 assert rlength_offset != NULL_OFFSET;
1580 assign(rlength_offset, "", true, position);
1581 pop();
1582 position.next();
1583 break;
1584 }
1585 case AwkTuples._FILENAME_OFFSET_: {
1586
1587 filename_offset = position.intArg(0);
1588 assert filename_offset != NULL_OFFSET;
1589 assign(filename_offset, "", true, position);
1590 pop();
1591 position.next();
1592 break;
1593 }
1594 case AwkTuples._SUBSEP_OFFSET_: {
1595
1596 subsep_offset = position.intArg(0);
1597 assert subsep_offset != NULL_OFFSET;
1598 assign(subsep_offset, new String(new byte[] {28}), true, position);
1599 pop();
1600 position.next();
1601 break;
1602 }
1603 case AwkTuples._CONVFMT_OFFSET_: {
1604
1605 convfmt_offset = position.intArg(0);
1606 assert convfmt_offset != NULL_OFFSET;
1607 assign(convfmt_offset, "%.6g", true, position);
1608 pop();
1609 position.next();
1610 break;
1611 }
1612 case AwkTuples._OFMT_OFFSET_: {
1613
1614 ofmt_offset = position.intArg(0);
1615 assert ofmt_offset != NULL_OFFSET;
1616 assign(ofmt_offset, "%.6g", true, position);
1617 pop();
1618 position.next();
1619 break;
1620 }
1621 case AwkTuples._ENVIRON_OFFSET_: {
1622
1623
1624 environ_offset = position.intArg(0);
1625 assert environ_offset != NULL_OFFSET;
1626
1627 Map<String, String> env = System.getenv();
1628 for (Map.Entry<String, String> var : env.entrySet()) {
1629 assignArray(environ_offset, var.getKey(), var.getValue(), true);
1630 pop();
1631 }
1632 position.next();
1633 break;
1634 }
1635 case AwkTuples._ARGC_OFFSET_: {
1636
1637 argc_offset = position.intArg(0);
1638 assert argc_offset != NULL_OFFSET;
1639
1640
1641 assign(argc_offset, arguments.size() + 1, true, position);
1642 pop();
1643 position.next();
1644 break;
1645 }
1646 case AwkTuples._ARGV_OFFSET_: {
1647
1648 argv_offset = position.intArg(0);
1649 assert argv_offset != NULL_OFFSET;
1650
1651 int argc = (int) JRT.toDouble(runtime_stack.getVariable(argc_offset, true));
1652 assignArray(argv_offset, 0, "java Awk", true);
1653 pop();
1654 for (int i = 1; i < argc; i++) {
1655
1656 assignArray(argv_offset, i, arguments.get(i - 1), true);
1657 pop();
1658 }
1659 position.next();
1660 break;
1661 }
1662 case AwkTuples._GET_INPUT_FIELD_: {
1663
1664 int fieldnum = parseIntField(pop(), position);
1665 push(jrt.jrtGetInputField(fieldnum));
1666 position.next();
1667 break;
1668 }
1669 case AwkTuples._APPLY_RS_: {
1670 assert rs_offset != NULL_OFFSET;
1671 Object rs_obj = runtime_stack.getVariable(rs_offset, true);
1672 if (jrt.getPartitioningReader() != null) {
1673 jrt.getPartitioningReader().setRecordSeparator(rs_obj.toString());
1674 }
1675 position.next();
1676 break;
1677 }
1678 case AwkTuples._CALL_FUNCTION_: {
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688 Address func_addr = position.addressArg();
1689
1690 long num_formal_params = position.intArg(2);
1691 long num_actual_params = position.intArg(3);
1692 assert num_formal_params >= num_actual_params;
1693 runtime_stack.pushFrame(num_formal_params, position.current());
1694
1695 for (long i = num_actual_params - 1 ; i >= 0 ; i--) {
1696 runtime_stack.setVariable(i, pop(), false);
1697 }
1698 position.jump(func_addr);
1699
1700 break;
1701 }
1702 case AwkTuples._FUNCTION_: {
1703
1704
1705
1706
1707 position.next();
1708 break;
1709 }
1710 case AwkTuples._SET_RETURN_RESULT_: {
1711
1712 runtime_stack.setReturnValue(pop());
1713 position.next();
1714 break;
1715 }
1716 case AwkTuples._RETURN_FROM_FUNCTION_: {
1717 position.jump(runtime_stack.popFrame());
1718 push(runtime_stack.getReturnValue());
1719 position.next();
1720 break;
1721 }
1722 case AwkTuples._SET_NUM_GLOBALS_: {
1723
1724 assert position.intArg(0) == global_variable_offsets.size();
1725 runtime_stack.setNumGlobals(position.intArg(0));
1726
1727
1728
1729
1730
1731 for (String key : initial_variables.keySet()) {
1732 if (function_names.contains(key)) {
1733 throw new IllegalArgumentException("Cannot assign a scalar to a function name (" + key + ").");
1734 }
1735 Integer offset_obj = global_variable_offsets.get(key);
1736 Boolean array_obj = global_variable_arrays.get(key);
1737 if (offset_obj != null) {
1738 assert array_obj != null;
1739 if (array_obj.booleanValue()) {
1740 throw new IllegalArgumentException("Cannot assign a scalar to a non-scalar variable (" + key + ").");
1741 } else {
1742 Object obj = initial_variables.get(key);
1743 runtime_stack.setFilelistVariable(offset_obj.intValue(), obj);
1744 }
1745 }
1746 }
1747
1748 position.next();
1749 break;
1750 }
1751 case AwkTuples._CLOSE_: {
1752
1753 String s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1754 push(jrt.jrtClose(s));
1755 position.next();
1756 break;
1757 }
1758 case AwkTuples._APPLY_SUBSEP_: {
1759
1760
1761
1762
1763 long count = position.intArg(0);
1764 assert count >= 1;
1765
1766 String convfmt = getCONVFMT().toString();
1767 if (count == 1) {
1768
1769 } else {
1770 StringBuilder sb = new StringBuilder();
1771 sb.append(JRT.toAwkString(pop(), convfmt, locale));
1772 String subsep = JRT.toAwkString(runtime_stack.getVariable(subsep_offset, true), convfmt, locale);
1773 for (int i = 1; i < count; i++) {
1774 sb.insert(0, subsep);
1775 sb.insert(0, JRT.toAwkString(pop(), convfmt, locale));
1776 }
1777 push(sb.toString());
1778 }
1779 position.next();
1780 break;
1781 }
1782 case AwkTuples._DELETE_ARRAY_ELEMENT_: {
1783
1784
1785
1786 long offset = position.intArg(0);
1787 boolean is_global = position.boolArg(1);
1788 AssocArray aa = (AssocArray) runtime_stack.getVariable(offset, is_global);
1789 Object key = pop();
1790 if (aa != null) {
1791 aa.remove(key);
1792 }
1793 position.next();
1794 break;
1795 }
1796 case AwkTuples._DELETE_ARRAY_: {
1797
1798
1799
1800 long offset = position.intArg(0);
1801 boolean is_global = position.boolArg(1);
1802 runtime_stack.removeVariable(offset, is_global);
1803 position.next();
1804 break;
1805 }
1806 case AwkTuples._SET_EXIT_ADDRESS_: {
1807
1808 exit_address = position.addressArg();
1809 position.next();
1810 break;
1811 }
1812 case AwkTuples._SET_WITHIN_END_BLOCKS_: {
1813
1814 within_end_blocks = position.boolArg(0);
1815 position.next();
1816 break;
1817 }
1818 case AwkTuples._EXIT_WITHOUT_CODE_:
1819 case AwkTuples._EXIT_WITH_CODE_: {
1820 if (opcode == AwkTuples._EXIT_WITH_CODE_) {
1821
1822 exit_code = (int) JRT.toDouble(pop());
1823 }
1824 throw_exit_exception = true;
1825
1826
1827 if (!within_end_blocks) {
1828
1829 runtime_stack.popAllFrames();
1830
1831 operand_stack.clear();
1832 position.jump(exit_address);
1833 } else {
1834
1835 jrt.jrtCloseAll();
1836
1837 operand_stack.clear();
1838 throw new ExitException(exit_code, "The AWK script requested an exit");
1839
1840 }
1841 break;
1842 }
1843 case AwkTuples._REGEXP_: {
1844
1845 String key = JRT.toAwkString(position.arg(0), getCONVFMT().toString(), locale);
1846 Pattern pattern = regexps.get(key);
1847 if (pattern == null) {
1848 regexps.put(key, pattern = Pattern.compile(key));
1849 }
1850 push(pattern);
1851 position.next();
1852 break;
1853 }
1854 case AwkTuples._CONDITION_PAIR_: {
1855
1856
1857 ConditionPair cp = condition_pairs.get(position.current());
1858 if (cp == null) {
1859 cp = new ConditionPair();
1860 condition_pairs.put(position.current(), cp);
1861 }
1862 boolean end = jrt.toBoolean(pop());
1863 boolean start = jrt.toBoolean(pop());
1864 push(cp.update(start, end) ? ONE : ZERO);
1865 position.next();
1866 break;
1867 }
1868 case AwkTuples._IS_IN_: {
1869
1870
1871 Object arr = pop();
1872 Object arg = pop();
1873 AssocArray aa = (AssocArray) arr;
1874 boolean result = aa.isIn(arg);
1875 push(result ? ONE : ZERO);
1876 position.next();
1877 break;
1878 }
1879 case AwkTuples._CAST_DOUBLE_: {
1880 push(JRT.toDouble(pop()));
1881 position.next();
1882 break;
1883 }
1884 case AwkTuples._CAST_STRING_: {
1885 push(pop().toString());
1886 position.next();
1887 break;
1888 }
1889 case AwkTuples._THIS_: {
1890
1891
1892
1893
1894 position.next();
1895 break;
1896 }
1897 case AwkTuples._EXEC_: {
1898
1899
1900
1901
1902 String awk_code = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1903 List<ScriptSource> scriptSources = new ArrayList<ScriptSource>(1);
1904 scriptSources.add(new ScriptSource(ScriptSource.DESCRIPTION_COMMAND_LINE_SCRIPT, new StringReader(awk_code), false));
1905
1906 org.sentrysoftware.jawk.frontend.AwkParser ap = new org.sentrysoftware.jawk.frontend.AwkParser(
1907
1908 settings.isAdditionalFunctions(),
1909 settings.isAdditionalTypeFunctions(),
1910 extensions);
1911 try {
1912 AwkSyntaxTree ast = ap.parse(scriptSources);
1913 if (ast != null) {
1914 ast.semanticAnalysis();
1915 ast.semanticAnalysis();
1916 AwkTuples new_tuples = new AwkTuples();
1917 int result = ast.populateTuples(new_tuples);
1918 assert result == 0;
1919 new_tuples.postProcess();
1920 ap.populateGlobalVariableNameToOffsetMappings(new_tuples);
1921 AVM new_avm = new AVM(settings, extensions);
1922 int subScriptExitCode = 0;
1923 try {
1924 new_avm.interpret(new_tuples);
1925 } catch (ExitException ex) {
1926 subScriptExitCode = ex.getCode();
1927 }
1928 push(subScriptExitCode);
1929 } else {
1930 push(-1);
1931 }
1932 } catch (IOException ioe) {
1933 throw new AwkRuntimeException(position.lineNumber(), "IO Exception caught : " + ioe);
1934 }
1935
1936 position.next();
1937 break;
1938 }
1939 case AwkTuples._EXTENSION_: {
1940
1941
1942
1943
1944
1945
1946
1947 String extension_keyword = position.arg(0).toString();
1948 long num_args = position.intArg(1);
1949 boolean is_initial = position.boolArg(2);
1950
1951 JawkExtension extension = extensions.get(extension_keyword);
1952 if (extension == null) {
1953 throw new AwkRuntimeException("Extension for '" + extension_keyword + "' not found.");
1954 }
1955
1956 Object[] args = new Object[(int) num_args];
1957 for (int i = (int)num_args - 1 ; i >=0 ; i--) {
1958 args[i] = pop();
1959 }
1960
1961 Object retval = extension.invoke(extension_keyword, args);
1962
1963
1964
1965
1966 if (is_initial && retval != null && retval instanceof BlockObject) {
1967 retval = new BlockManager().block((BlockObject) retval);
1968 }
1969
1970
1971 if (retval == null) {
1972 retval = "";
1973 } else if (retval instanceof Integer) {
1974 } else if (retval instanceof Long) {
1975 } else if (retval instanceof Double) {
1976 } else if (retval instanceof String) {
1977 } else if (retval instanceof AssocArray) {
1978 } else if (retval instanceof BlockObject) {
1979
1980 } else {
1981
1982
1983 retval = retval.toString();
1984 }
1985 push(retval);
1986
1987 position.next();
1988 break;
1989 }
1990 default:
1991 throw new Error("invalid opcode: " + AwkTuples.toOpcodeString(position.opcode()));
1992 }
1993 }
1994
1995
1996 jrt.jrtCloseAll();
1997
1998 } catch (RuntimeException re) {
1999 LOG.error("", re);
2000 LOG.error("operand_stack = {}", operand_stack);
2001 LOG.error("position = {}", position);
2002 LOG.error("line number = {}", position.lineNumber());
2003
2004
2005 runtime_stack.popAllFrames();
2006
2007 operand_stack.clear();
2008
2009 throw re;
2010 } catch (AssertionError ae) {
2011 LOG.error("", ae);
2012 LOG.error("operand_stack = {}", operand_stack);
2013 try {
2014 LOG.error("position = {}", position);
2015 } catch (Throwable t) {
2016 LOG.error("{ could not report on position", t);
2017 }
2018 try {
2019 LOG.error("line number = {}", position.lineNumber());
2020 } catch (Throwable t) {
2021 LOG.error("{ could not report on line number", t);
2022 }
2023 throw ae;
2024 }
2025
2026
2027 if (throw_exit_exception) {
2028 throw new ExitException(exit_code, "The AWK script requested an exit");
2029 }
2030
2031 }
2032
2033
2034
2035
2036 public void waitForIO() {
2037 jrt.jrtCloseAll();
2038 }
2039
2040 private void avmDump(AssocArray[] aa_array) {
2041 if (aa_array == null) {
2042
2043 Object[] globals = runtime_stack.getNumGlobals();
2044 for (String name : global_variable_offsets.keySet()) {
2045 int idx = global_variable_offsets.get(name);
2046 Object value = globals[idx];
2047 if (value instanceof AssocArray) {
2048 AssocArray aa = (AssocArray) value;
2049 value = aa.mapString();
2050 }
2051 LOG.info("{} = {}", name, value);
2052 }
2053 } else {
2054
2055 for (AssocArray aa : aa_array) {
2056 LOG.info(aa.mapString());
2057 }
2058 }
2059 }
2060
2061 private void printTo(PrintStream ps, long num_args) {
2062
2063
2064 if (num_args == 0) {
2065
2066 ps.print(jrt.jrtGetInputField(0));
2067 ps.print(getORS().toString());
2068 } else {
2069
2070
2071 String ofs_string = getOFS().toString();
2072
2073
2074 Object[] args = new Object[(int)num_args];
2075 for (int i = (int)num_args - 1 ; i >=0 ; i--) {
2076 args[i] = pop();
2077 }
2078
2079
2080 for (int i = 0 ; i < num_args ; i++) {
2081 ps.print(JRT.toAwkStringForOutput(args[i], getOFMT().toString(), locale));
2082
2083 if (i < num_args - 1) {
2084
2085 ps.print(ofs_string);
2086 }
2087 }
2088 ps.print(getORS().toString());
2089 }
2090
2091 if (IS_WINDOWS) {
2092 ps.flush();
2093 }
2094 }
2095
2096 private void printfTo(PrintStream ps, long num_args) {
2097
2098 ps.print(sprintfFunction(num_args));
2099
2100 if (IS_WINDOWS) {
2101 ps.flush();
2102 }
2103 }
2104
2105
2106
2107
2108 private String sprintfFunction(long num_args) {
2109
2110
2111 if (num_args == 0) return "";
2112
2113
2114 Object[] arg_array = new Object[(int) (num_args - 1)];
2115
2116
2117
2118
2119 for (int i = (int)num_args - 2 ; i >= 0 ; i--) {
2120 arg_array[i] = pop();
2121 }
2122
2123
2124 String fmt = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
2125
2126 if (trap_illegal_format_exceptions) {
2127 return Printf4J.sprintf(locale, fmt, arg_array);
2128 } else {
2129 return JRT.sprintfNoCatch(locale, fmt, arg_array);
2130 }
2131 }
2132
2133 private StringBuffer replace_first_sb = new StringBuffer();
2134
2135
2136
2137
2138 private String replaceFirst(String orig, String ere, String repl) {
2139 replace_first_sb.setLength(0);
2140 push(JRT.replaceFirst(orig, repl, ere, replace_first_sb, getCONVFMT().toString(), locale));
2141 return replace_first_sb.toString();
2142 }
2143
2144 private StringBuffer replace_all_sb = new StringBuffer();
2145
2146
2147
2148
2149 private String replaceAll(String orig, String ere, String repl) {
2150 replace_all_sb.setLength(0);
2151 push(JRT.replaceAll(orig, repl, ere, replace_all_sb, getCONVFMT().toString(), locale));
2152 return replace_all_sb.toString();
2153 }
2154
2155
2156
2157
2158 private void assign(long l, Object value, boolean is_global, Position position) {
2159
2160 if (runtime_stack.getVariable(l, is_global) instanceof AssocArray) {
2161 throw new AwkRuntimeException(position.lineNumber(), "cannot assign anything to an unindexed associative array");
2162 }
2163 push(value);
2164 runtime_stack.setVariable(l, value, is_global);
2165 }
2166
2167
2168
2169
2170 private void assignArray(long offset, Object arr_idx, Object rhs, boolean is_global) {
2171 Object o1 = runtime_stack.getVariable(offset, is_global);
2172 if (o1 == null || o1.equals(BLANK)) {
2173 runtime_stack.setVariable(offset, o1 = new AssocArray(sorted_array_keys), is_global);
2174 }
2175 assert o1 != null;
2176
2177
2178
2179
2180
2181
2182
2183
2184 assert o1 instanceof AssocArray;
2185 AssocArray array = (AssocArray) o1;
2186
2187
2188
2189 array.put(arr_idx, rhs);
2190 push(rhs);
2191 }
2192
2193
2194
2195
2196
2197 private Object inc(long l, boolean is_global) {
2198 Object o = runtime_stack.getVariable(l, is_global);
2199 if (o == null || o instanceof UninitializedObject) {
2200 runtime_stack.setVariable(l, o = ZERO, is_global);
2201 }
2202 runtime_stack.setVariable(l, JRT.inc(o), is_global);
2203 return o;
2204 }
2205
2206
2207
2208
2209
2210 private Object dec(long l, boolean is_global) {
2211 Object o = runtime_stack.getVariable(l, is_global);
2212 if (o == null) {
2213 runtime_stack.setVariable(l, o = ZERO, is_global);
2214 }
2215 runtime_stack.setVariable(l, JRT.dec(o), is_global);
2216 return o;
2217 }
2218
2219
2220 @Override
2221 public final Object getRS() {
2222 assert rs_offset != NULL_OFFSET;
2223 Object rs_obj = runtime_stack.getVariable(rs_offset, true);
2224 return rs_obj;
2225 }
2226
2227
2228 @Override
2229 public final Object getOFS() {
2230 assert ofs_offset != NULL_OFFSET;
2231 Object ofs_obj = runtime_stack.getVariable(ofs_offset, true);
2232 return ofs_obj;
2233 }
2234
2235 public final Object getORS() {
2236 return runtime_stack.getVariable(ors_offset, true);
2237 }
2238
2239
2240 @Override
2241 public final Object getSUBSEP() {
2242 assert subsep_offset != NULL_OFFSET;
2243 Object subsep_obj = runtime_stack.getVariable(subsep_offset, true);
2244 return subsep_obj;
2245 }
2246
2247
2248
2249
2250
2251
2252
2253
2254 @SuppressWarnings("unused")
2255 private void setFilelistVariable(String name_value) {
2256 int eq_idx = name_value.indexOf('=');
2257
2258 assert eq_idx >= 0;
2259 if (eq_idx == 0) {
2260 throw new IllegalArgumentException("Must have a non-blank variable name in a name=value variable assignment argument.");
2261 }
2262 String name = name_value.substring(0, eq_idx);
2263 String value = name_value.substring(eq_idx + 1);
2264 Object obj;
2265 try {
2266 obj = Integer.parseInt(value);
2267 } catch (NumberFormatException nfe) {
2268 try {
2269 obj = Double.parseDouble(value);
2270 } catch (NumberFormatException nfe2) {
2271 obj = value;
2272 }
2273 }
2274
2275
2276 if (function_names.contains(name)) {
2277 throw new IllegalArgumentException("Cannot assign a scalar to a function name (" + name + ").");
2278 }
2279
2280 Integer offset_obj = global_variable_offsets.get(name);
2281 Boolean array_obj = global_variable_arrays.get(name);
2282
2283 if (offset_obj != null) {
2284 assert array_obj != null;
2285 if (array_obj.booleanValue()) {
2286 throw new IllegalArgumentException("Cannot assign a scalar to a non-scalar variable (" + name + ").");
2287 } else {
2288 runtime_stack.setFilelistVariable(offset_obj.intValue(), obj);
2289 }
2290 }
2291
2292 }
2293
2294
2295 @Override
2296 public final void assignVariable(String name, Object obj) {
2297
2298 if (function_names.contains(name)) {
2299 throw new IllegalArgumentException("Cannot assign a scalar to a function name (" + name + ").");
2300 }
2301
2302 Integer offset_obj = global_variable_offsets.get(name);
2303 Boolean array_obj = global_variable_arrays.get(name);
2304
2305 if (offset_obj != null) {
2306 assert array_obj != null;
2307 if (array_obj.booleanValue()) {
2308 throw new IllegalArgumentException("Cannot assign a scalar to a non-scalar variable (" + name + ").");
2309 } else {
2310 runtime_stack.setFilelistVariable(offset_obj.intValue(), obj);
2311 }
2312 }
2313
2314 }
2315
2316 private void swapOnStack() {
2317 Object o1 = pop();
2318 Object o2 = pop();
2319 push(o1);
2320 push(o2);
2321 }
2322
2323 private void avmConsumeInputForGetline() throws IOException {
2324 if (avmConsumeInput(true)) {
2325 push(1);
2326 } else {
2327 push("");
2328 push(0);
2329 }
2330 swapOnStack();
2331 }
2332
2333 private void avmConsumeFileInputForGetline(String filename) throws IOException {
2334 if (avmConsumeFileInput(filename)) {
2335 push(1);
2336 } else {
2337 push(0);
2338 }
2339 swapOnStack();
2340 }
2341
2342 private void avmConsumeCommandInputForGetline(String cmd) throws IOException {
2343 if (avmConsumeCommandInput(cmd)) {
2344 push(1);
2345 } else {
2346 push(0);
2347 }
2348 swapOnStack();
2349 }
2350
2351 private boolean avmConsumeFileInput(String filename)
2352 throws IOException
2353 {
2354 boolean retval = jrt.jrtConsumeFileInput(filename);
2355 if (retval) {
2356 push(jrt.getInputLine());
2357 } else {
2358 push("");
2359 }
2360 return retval;
2361 }
2362
2363 private boolean avmConsumeCommandInput(String cmd)
2364 throws IOException
2365 {
2366 boolean retval = jrt.jrtConsumeCommandInput(cmd);
2367 if (retval) {
2368 push(jrt.getInputLine());
2369 } else {
2370 push("");
2371 }
2372 return retval;
2373 }
2374
2375 private boolean avmConsumeInput(boolean for_getline)
2376 throws IOException
2377 {
2378 boolean retval = jrt.jrtConsumeInput(settings.getInput(), for_getline, locale);
2379 if (retval && for_getline) {
2380 push(jrt.getInputLine());
2381 }
2382 return retval;
2383 }
2384
2385
2386 @Override
2387 public Object getFS() {
2388 assert fs_offset != NULL_OFFSET;
2389 Object fs_string = runtime_stack.getVariable(fs_offset, true);
2390 return fs_string;
2391 }
2392
2393
2394 @Override
2395 public Object getCONVFMT() {
2396 assert convfmt_offset != NULL_OFFSET : "convfmt_offset not defined";
2397 Object convfmt_string = runtime_stack.getVariable(convfmt_offset, true);
2398 return convfmt_string;
2399 }
2400
2401
2402 @Override
2403 public void resetFNR() {
2404 runtime_stack.setVariable(fnr_offset, ZERO, true);
2405 }
2406
2407
2408 @Override
2409 public void incFNR() {
2410 inc(fnr_offset, true);
2411 }
2412
2413
2414 @Override
2415 public void incNR() {
2416 inc(nr_offset, true);
2417 }
2418
2419
2420 @Override
2421 public void setNF(Integer I) {
2422 runtime_stack.setVariable(nf_offset, I, true);
2423 }
2424
2425
2426 @Override
2427 public void setFILENAME(String filename) {
2428 runtime_stack.setVariable(filename_offset, filename, true);
2429 }
2430
2431
2432 @Override
2433 public Object getARGV() {
2434 return runtime_stack.getVariable(argv_offset, true);
2435 }
2436
2437
2438 @Override
2439 public Object getARGC() {
2440 return runtime_stack.getVariable(argc_offset, true);
2441 }
2442
2443 private String getOFMT() {
2444 assert ofmt_offset != NULL_OFFSET;
2445 String ofmt_string = runtime_stack.getVariable(ofmt_offset, true).toString();
2446 return ofmt_string;
2447 }
2448
2449 private static final UninitializedObject BLANK = new UninitializedObject();
2450
2451
2452
2453
2454 public static final int NULL_OFFSET = -1;
2455
2456 private static class RuntimeStack {
2457
2458 private Object[] globals = null;
2459 private Object[] locals = null;
2460 private MyStack<Object[]> locals_stack = new ArrayStackImpl<Object[]>();
2461 private MyStack<Integer> return_indexes = new LinkedListStackImpl<Integer>();
2462
2463 @SuppressWarnings("unused")
2464 public void dump() {
2465 LOG.info("globals = " + Arrays.toString(globals));
2466 LOG.info("locals = " + Arrays.toString(locals));
2467 LOG.info("locals_stack = " + locals_stack);
2468 LOG.info("return_indexes = " + return_indexes);
2469 }
2470
2471 Object[] getNumGlobals() {
2472 return globals;
2473 }
2474
2475
2476
2477
2478 void setNumGlobals(long l) {
2479 assert l >= 0;
2480 assert globals == null;
2481 globals = new Object[(int) l];
2482 for (int i = 0 ; i < l ; i++) {
2483 globals[i] = null;
2484 }
2485
2486
2487 }
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503 Object getVariable(long offset, boolean is_global) {
2504 assert globals != null;
2505 assert offset != NULL_OFFSET;
2506 if (is_global) {
2507 return globals[(int) offset];
2508 } else {
2509 return locals[(int) offset];
2510 }
2511 }
2512
2513 Object setVariable(long offset, Object val, boolean is_global) {
2514 assert globals != null;
2515 assert offset != NULL_OFFSET;
2516 if (is_global) {
2517 return globals[(int) offset] = val;
2518 } else {
2519 return locals[(int) offset] = val;
2520 }
2521 }
2522
2523
2524 void removeVariable(long offset, boolean is_global) {
2525 assert globals != null;
2526 assert offset != NULL_OFFSET;
2527 if (is_global) {
2528 assert globals[(int) offset] == null || globals[(int) offset] instanceof AssocArray;
2529 globals[(int) offset] = null;
2530 } else {
2531 assert locals[(int) offset] == null || locals[(int) offset] instanceof AssocArray;
2532 locals[(int) offset] = null;
2533 }
2534 }
2535
2536 void setFilelistVariable(int offset, Object value) {
2537 assert globals != null;
2538 assert offset != NULL_OFFSET;
2539 globals[offset] = value;
2540 }
2541
2542 void pushFrame(long num_formal_params, int position_idx) {
2543 locals_stack.push(locals);
2544 locals = new Object[(int) num_formal_params];
2545 return_indexes.push(position_idx);
2546 }
2547
2548
2549 int popFrame() {
2550 locals = locals_stack.pop();
2551 return return_indexes.pop();
2552 }
2553
2554 void popAllFrames() {
2555 int sz = locals_stack.size();
2556 while (--sz >= 0) {
2557 locals = locals_stack.pop();
2558 return_indexes.pop();
2559 }
2560 }
2561 private Object return_value;
2562
2563 void setReturnValue(Object obj) {
2564 assert return_value == null;
2565 return_value = obj;
2566 }
2567
2568 Object getReturnValue() {
2569 Object retval;
2570 if (return_value == null) {
2571 retval = BLANK;
2572 } else {
2573 retval = return_value;
2574 }
2575 return_value = null;
2576 return retval;
2577 }
2578 }
2579 }