1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
|
.nf
ECL: Enhanced CL Release Notes and User's Guide
================================================
Michael Fitzpatrick
NOAO/IRAF Group
12/12/04
Revised: 5/28/05
********************************************************************************
Release History:
02/10/05 ** Alpha Release for testing
05/06/05 ** 2nd Alpha Release for testing
06/07/05 ** 1st Beta Release for testing
********************************************************************************
Table of Contents
-----------------
Introduction
Installation and Use
To Install the CL
Determine CL Version Type
Error Handling
Introduction and Cautions
Example Descriptions
Reporting Errors
Traceback
Trapping Errors
The 'iferr' Syntax
The 'erract' Environment Variable
Error Handling: Then and Now
New CL parameters
What Errors Are NOT Trapped
Command-line History and BackSpace Revisions
Input Command Summary
New Builtin Functions and Variables
Error Functions
String Functions
Trig Functions
Utility Functions
Bitwise Operations
Defined Constants
Post-Release Notes
********************************************************************************
============
Introduction
============
The primary goals of the ECL project were to
o add an error-handling capability to the existing IRAF CL,
o include other functionality which could improve the
scripting environment (e.g. pre-defined language constants
such as 'PI') and add any other features we found lacking
(e.g. missing trig functions and string utilities), and
o add commonly requested features.
Where possible, small enhancements such as a new utility builtin function
will be implemented in the "old" CL as well, however as scripts begin to
use the more advanced features scripts will naturally become less backward
compatible. Future work will build on the version presented here with
the hope that users will migrate to the new system over a short time.
This is a work in progress. Users are encouraged to experiment with
features, request future enhancements, and to please report any errors or
problems to
iraf@noao.edu
New releases will be announced on the IRAF website (http://iraf.noao.edu)
following the addition of any new features or when critical bugs have been
fixed.
====================
Installation and Use
====================
The ECL is being distributed in a self-extracting script file
rather than the traditional IRAF external package since it is meant to
overlay an existing IRAF system until the time when it becomes part of
the core distribution. Since the script creates a new command link in
the unix system "local bin directory" and adds files to the IRAF source
tree, it MUST be run as the root user (the script will terminate or ask
if you wish to proceed with a no-op installation otherwise).
The installation script does the following to your system:
1) Replaces the existing hlib$cl.csh script with a modified
version after creating a hlib$cl.csh.ORIG backup file
2) Creates an "ecl" command link in the same directory as the
current "cl" IRAF command link. Both links point to the same
hlib$cl.csh script which checks for how it was called an
invokes the proper binary.
3) Moves the "ecl.e" binary to the proper iraf$bin.<arch> directory,
changing the ownership to the 'iraf' user and setting the execute
permissions on the file.
4) Creates a iraf$pkg/ecl directory and moves all ECL sources there.
The install script may be run from any directory on the system, it is
unpacked in /tmp and cleans up temp files when complete. A "personal
installation" option is not implemented at this time but could be considered
later for users who don't have write permission on their IRAF tree. Please
contact iraf@noao.edu for instructions on how to manually setup such a
system for personal use.
To Install the ECL
------------------
Step 1) Download the distribution file appropriate for your system. For
example,
% ftp iraf.noao.edu (140.252.1.1)
login: anonymous
password: [your email address]
ftp> cd pub
ftp> binary
ftp> get ecl_install_redhat.csh
ftp> quit
Step 2) Execute the script AS ROOT:
% su # become the root user
# ./ecl_install_redhat.csh
The script will prompt you for the local bin directory or any
iraf paths needed, simply accept the default values determined for
your system or override them with others.
Once executed, the ECL source and binaries will be installed in
the system as described above. The file you are reading right
now is available as iraf$pkg/ecl/Notes.ecl and will be updated
with post-release notes at the end of the file with each new
release.
Step 3) Start the ECL from your normal IRAF login directory as either
% ecl
or
% cl -ecl
The second form of the command is needed on systems which mount
IRAF from another machine since the CL command links are created
at IRAF install time. One reason for replacing the hlib$cl.csh
script is to allow for the "-ecl" argument to override the binary
to be used on systems where only the 'cl' command is available and
so that the installation isn't required on all machines mounting
a common IRAF.
The default ECL prompt is now "ecl>" in the new version as a visual
clue that the new system is being used. Additionally, package prompts
default to using the complete package name rather than the familiar
2-character prefix as another clue. This behavior can be changed
by adding the string "nolongprompt" to the CL 'ehinit' parameter,
e.g.
cl> cl.ehinit = cl.ehinit // " nolongprompt"
Except as described below, use of the ECL should be identical to the
traditional CL for most users.
Determining CL Version
----------------------
As users begin to make regular use of features found only in the
ECL, the first error to be checked is that the script is running using the
proper version of the CL. This needs to be done using features found in
both the ECL and traditional CL languages. The simplest test, for either
package loading scripts or within tasks, is something like
if (defpar ("$errno")) {
print ("You are using the ECL")
} else {
print ("You are using the old CL")
}
==============
Error Handling
==============
Introduction and Cautions
=========================
The error-handling enhancements are composed of two elements:
o the reporting of errors within scripts, and
o the ability to trap and recover those errors.
The first case addresses the long-standing problem in which an error message
returned by a script gives a line number that has no basis in reality, and
which gives no useful information about the underlying task that created it.
In the second case, one often wants scripts to be able to trap errors from
compiled tasks so that some sort of cleanup can be done in order to allow
the script to continue, or so that an error status code can be examined
and some specific action taken (which may simply be to ignore the error).
In the ECL, messages are now printed with the correct line number and
with a detailed traceback to the user's command-line showing more precisely
what was called at the time of the error. New language constructs are
available which allow scripts to conditionally check for errors from
tasks they call and branch to code to deal with those errors. Finally,
new ECL environment variables and builtin functions allow for limited
error-handling control over scripts already in the system which have not
been retrofitted to specifically trap errors. Details of each of these
capabilities and examples of how they may be used by developers and users
are given below. It is also worth discussing the types of errors which
can occur in a script task before getting into details about how they
might be handled by the user or script programmer.
Error conditions in the CL break down into roughly the following types:
Error Type Examples
---------- --------
Compiled Task Errors 1) A call to a compiled task in the system
dies unexpectedly with an exception (e.g.
FPE, segmentation violation, etc)
2) A task aborts due to an error condition the
task has trapped and cannot recover (e.g.
invalid parameters, out of memory, etc).
CL Internal Errors 1) Script code performs an illegal operation
causing an exception (e.g. "i = j / k"
where 'k' is zero.
2) Script code triggers a runtime error within
the CL itself (e.g. "log (string_value)")
CL Error Assertions 1) Script programmer forces the task to exit
with a call to the CL error() builtin.
2) Script programmer simply prints and error
message indicating a problem and returns
without further processing.
All of these errors can be detected at some level, however not all of
them can be handled in a way which allows a calling script to recover
and continue executing, nor would it always make sense to do so.
Errors such as a floating-point-exception (FPE) may be data-dependent,
a segmentation violation may indicate a coding error in a compiled task
or a platform-specific bug, or an error in another script task may be
beyond the control of the scripter to fix. Error assertions by a script
programmer are not meant to be recoverable, and in the second example
an arbitrary problem message cannot be trapped by the system.
An error-handling capability in the ECL (or any language) is not a
panacea for all error conditions one might encounter, the best a script
programmer can hope to do is to trap an error and take some reasonable
action at the time. The ECL offers a way for a script to print a more
meaningful error message, or at least abort gracefully after cleaning
itself up. However, depending on the type of error, *your* script may
still never run to completion until somebody else fixes *their* code.
Lastly, it is also important to note that trapping an error means the
script finds itself in an unnatural state. Proper recovery requires
that the script programmer understand the error condition as well as
the state of the script at that point of execution. The error-handling
code must restore the script to a state where it can continue running
(if possible) and avoid potential side-effects caused by e.g. forgetting
to clean up intermediate files or reset counter variables. New language
features mean new types of bugs can be introduced into a script, even if
the irony is that these new features are meant to trap bugs!
Example Descriptions
--------------------
In the examples to follow we will make use of an ERRTEST package
distributed with the ECL source and containing the following tasks used
in the examples to follow:
nested -- Test various error conditions from layered scripts
nest0 -- Dummy layer for nested testing
errtype -- Low-level script to test compiled and CL error conditions
fpe -- Compiled task producing an arithmetic exception
segvio -- Compiled task producing a segmentation violation
spperr -- Compiled task invoking the SPP error() function
Reporting of Errors
===================
Traceback
---------
The most obvious change to users will be in the traceback of errors
reported by the ECL. As an example, suppose we have a test script
called NESTED that calls several layers of other scripts until it gets
to a compiled task called FPE which simply triggers a divide-by-zero
arithmetic exception. The calling sequence we use is
NESTED (type) # toplevel test task
NEST0 (type) # hidden script task
ERRTYPE (type) # script task
FPE () # compiled task giving the error
(The 'type' argument here is a code used to test various types of system
errors but its value isn't important to the current discussion.) In the
traditional CL, executing this script results in the following and familiar
message:
cl> nested 1
ERROR on line 72: floating point divide by zero
errtype (type=1)
nested (type=1)
There are a number of issues with the error report here we wish to correct:
1) The error is reported to be on line 72, but none of the scripts
called invoke any task on that line, or even have that many lines,
and so it is clearly wrong.
2) Was it the ERRTYPE script that caused an error or something else?
3) There is no mention of the FPE task we know to be the culprit.
These problems are resolved in the ECL where the error report now looks like:
cl> nested 1
ERROR: floating point divide by zero
"fpe ()"
line 15: errtest$errtype.cl
called as: `errtype (type=1)'
"errtype (type)"
line 13: errtest$nest0.cl (hidden task)
called as: `nest0 (type=1)'
"nest0 (type)"
line 11: errtest$nested.cl
called as: `nested (type=1)'
The traceback is more complete and begins with the task which actually
throws the error. Checking the line numbers of the ERRTEST package
scripts we find that indeed FPE is called on line 15 of 'errtype.cl',
ERRTYPE is called from line 13 of 'nest0.cl', and so on.
For each task in the calling sequence the format of the traceback is
<script code fragment executing at the time of error>
LINE <number>: <script file containing line>
CALLED AS: <how this script was called>
The length of the traceback may be controlled with the new 'erract'
environment variable discussed in more detail below. In short, 'erract'
allows the traceback to be suppressed entirely, to print information only
at the level where the error occurred, or to print a full calling stack
trace (default).
Trapping Errors
===================
The 'iferr' Syntax
------------------
The ECL provides new language constructs to enable error actions, error
handling and recovery. This syntax will already be familiar to SPP programmers
and will quickly become obvious to even novice script programmers.
Error recovery is implemented using the IFERR and IFNOERR statements
to "post" an error handler that is called at the end of a block of code and
which checks for error conditions that may have occurred in that block.
The syntax for these statements is of the form:
iferr { <statement> } ifnoerr { <statement> }
<error action statement> <success action statement>
iferr { ifnoerr {
<block of statements> <block of statements>
} then } then
<error action statement> <success action statement>
iferr { ifnoerr {
<block of statements> <block of statements>
} then { } then {
<block of error stmts> <block of success action stmts>
} }
The IFERR is grammatically equivalent to the IF statement and means "if an
error occurs during the processing of the enclosed code, execute the error
action statement to follow". IFNOERR is the same except that the sense
of the test is reversed and the action statements are executed only if the
enclosed code completes without error. Additionally, these statements take
an ELSE clause allowing both forms of the test to be combined. For example,
iferr { ifnoerr {
<block of statements> <block of statements>
} then { } then {
<error stmts> <success stmts>
} else { } else {
<success stmts> <error stmts>
} }
In all cases
o Curly braces around the code to be checked are required,
o Curly braces are required when any action is a compound block
o The THEN statement is optional if a single statement is executed
as part of the action block
o The THEN statement is required for a compound action or when using
an ELSE clause
o It is a syntax error for a condition block to itself directly contain
an IFERR or IFNOERR statement and action statements, i.e. IFERR
statements may not be nested
To make effective use of these statements a few points need to be
kept in mind:
o The check for errors happens only after ALL statements in the
condition block are executed;
o Statements which generate errors did not execute to completion,
subsequent code relying on that result cannot be trusted
o Code in the condition block which executes following an initial
error may itself trigger errors due to the failure of a previous
statement or the resulting side-effects;
This implies that IFERR statements should be used to target only critical
pieces of code where a particular error condition might be expected, and/or
where an action block could reasonably react to that error. As an example
of how ignoring these points could be problematic consider the code snippet:
iferr {
task_a ()
task_b () | scan (x)
task_c (x)
} then {
error (99, "An error occurred\n")
}
All three tasks in the condition block will be executed, however the
behavior of the code being check depends on which task in the block fails;
If 'task_a' fails there may be no consequences for the remaining calls,
however if 'task_b' fails the value of 'x' may never be set and 'task_c'
may also fail (or at least produce spurious results). Cascading errors like
this will also be trapped and the action statement will still execute, but
the system error message strings will be incomplete (more about that below).
While it is possible to have a failure from each statement in a condition
block branch immediately to the action block by checking each statement
individually, doing so would permit poor programming practices such as
iteratively testing for the name of the failed task and taking different
recovery methods in the action block. If this is actually required for the
script to recover cleanly, the recommended method is to put an IFERR block
around smaller pieces of code where the recovery statements relate more
directly to the code being checked.
Errors trapped by IFERR statements include:
o System exceptions (FPE, segfault, etc) thrown by compiled tasks
o SPP error() returns from compiled tasks
o CL script error() assertions
Below we discuss errors which cannot be trapped using the IFERR syntax as
well as strategies for how to handle those errors which can be detected.
We'll also see how to determine which task in a condition block failed
and why.
The 'erract' Environment Variable
----------------------------------
The ECL has a new 'erract' environment variable used to control the
different aspects of the error handling. This is a whitespace-delimited
string comprised of the following options:
abort Script task should abort at an error and begin error
recovery back to the command-line
noabort Task should not abort, but continue execution if possible
trace Print a traceback of the calling sequence including all
line numbers and calling statements
notrace Print only the error message, no linenumbers or calls
clear Clear the error params (i.e. $errmsg, $errnum, $errtask)
at each new task call. This reseets the params with each
task invocation allowing them to be examined after each
call regardless of whether the code is in an IFERR block.
noclear Do not clear the CL error params at each new task call,
the params are only reset when an error is encountered.
flpr Automatically issue a 'flpr' when an error is seen. This
is used to flush any failed task from the process cache to
avoid potential future problems caused by a corrupted task.
noflpr Do not issue a 'flpr' when an error is seen, tasks remain
in the process cache, possibly in an error state.
full Print a complete traceback of the calling sequence.
nofull Print only the error report for the task causing the error
and none of its parents.
The default value is set as:
set erract = "abort trace flpr clear full"
Note that erract is implemented as an environment variable rather than
as a new CL parameter (similar to the ehinit/epinit params) in order to
minimize changes in the CL parameter file itself during the transition
to the ECL. The difference is that the 'set' (ore 'reset') command must
be used to define the values, whereas with ehinit/epinit they may be
assigned directly. For this variable it is also possible to (re)define
a single parameter without affecting other options, e.g.
cl> show erract # print options
abort trace flpr clear full
cl> set erract = "noabort" # reset one of them
cl> show erract # print options again
noabort trace flpr clear full
Error Handling: Then and Now
----------------------------
To better understand the new error detection and recovery behavior
(and to document this for future reference), let's look at the old error
mechanisms of the CL language: Any command called from the CL executes in a
context defined by the task hierarchy initiating the command, i.e. from the
command-line CL prompt one has a "first" task context, scripts calling child
(compiled or script) tasks push a new CL context inheriting the current
CL environment and who's 'parent' is the context that invoked the task.
In the traditional CL with an error occurring in a compiled task,
recovery first takes place in the SPP code who may choose to either handle
the error itself or may abort completely by doing a long-jump back to the
IRAF main() procedure (i.e. an EA_FATAL error type). In this latter case,
the process binary (running as a detached process from the CL) sends an
error() command back to the CL telling it the task has terminated abnormally
(a normal task shutdown leaves the executable simply waiting for more input
from the CL, e.g. another task to execute). This returned error() statement
is the same CL error() command one would use to abort a script task, and its
effect is to tell the CL to abort the current context and long-jump back
to the command-line after cleaning up running processes and freeing the
dictionary space (what the CL uses to catalog tasks/packages, parameters,
etc). [NOTE: Whether it is a system exception or a programmer-posted error,
the error sent back to the CL has always included both the error code and
message, it is just that the CL has never made use of these until now.]
Similarly, errors which occur while running script tasks (e.g. 'task not
found' errors, invalid use of string values, divide-by-zero from local
script variables, etc) also end up in the same CL error() procedure via
internal procedure calls made while executing the script.
Syntax errors are caught when the script is 'compiled' into the opcode
execution stack and are reported before the script begins to execute.
A script calling a child script containing a syntax error cannot trap
that error even though it will not be reported until the child script is
'compiled' just prior to execution. We assume that all script tasks are
well-formed and free of ntax errors.
ECL error recovery is somewhat simplified by the fact that errors,
either from external tasks or the execution of scripts, all converge in
a single procedure in the CL source code. The trick is to modify the
runtime behavior of the CL so that once we know we have an error we can
branch to conditional code instead of simply jumping all the way back to the
command line. Since we also wish to improve the error reporting we'd also
like make better use of information about how the failed code was called.
The first step is to realize that when executing a script the CL
language is "compiled" into a series of 'opcode' instructions comprising an
intermediate runtime language (similar to assembly language). Scripts are
run by executing the opcode instruction at the current 'program counter'
location, pushing task arguments or getting labels for jumps from the
dictionary, restoring a previous CL context, etc. The compilation stage
already has information about the script line being parsed so by adding
this line-number to the opcode instruction it is now possible to trace a
fault in any opcode back to the originating line of the script, and from
there back up to the command line through the calling tree. This extra
information makes the runtime size of the script slightly larger so
extremely large scripts may experience "dictionary full" problems not
previously seen (various CL buffer sizes were increased to help offset
this problem). This relatively minor change is all that is required to
address the problems mentioned above in error reporting.
Error trapping and recovery is done in a manner similar to the
implementation in SPP: The IFERR statement isn't actually an instruction
in the runtime script, rather it is used to tell the parser to insert
code around the block to be checked using traditional IF statements.
As an example, consider
iferr {
task1 (arg1, arg2)
task2 (arg1)
} then {
recovery ()
}
When compiled this is the equivalent of writing
_errpsh ()
task1 (arg1, arg2)
task2 (arg1)
if (_errpop () != 0) {
recovery ()
}
The _errpsh() is a hidden builtin function which "pushes" an error
structure onto the runtime stack, the _errpop() test at the end then
queries that structure to see whether any statement since the previous
push set the error flag and filled in the structure with the task name,
line number and other information. The push also temporarily deactivates
the behavior of the error() function so it no longer aborts entirely,
allowing the script to continue after cleaning up the current error.
In order to keep the model simple, nested iferr statements within
the same script are not currently implemented but are a possible future
enhancement. Complications arise from examples such as
iferr {
task1 (arg1, arg2)
iferr { task2 (arg1) } then
recovery2 ()
} then {
recovery1 ()
}
Consider the case where task1() succeeds and task2() fails and is
recovered properly with the recovery2() procedure. As far as the outer
IFERR block is concerned, did an error occur or not? If the remainder of
the script depends on task2() succeeding then the answer is possibly 'no'
(depending on what the recovery does) and we should additionally call
the recovery1() procedure (who is responsible for dealing with an error
condition in that block), if there is no dependency then we may want
*any* failure to be considered, or perhaps even have a way to "clear"
error conditions within the block. Now assume instead it is the first
task which fails and that triggers the second to fail because we depend
on the first succeeding, how should we post the error number/message for
the script? We simply disallow nested IFERR statements for the moment
to avoid dealing with these complex interactions
New CL parameters
===================
On order for script programmers to make use of errors that have
been trapped by the ECL, one generally needs access to the details of
that error, e.g. the message, task name, error number, etc. To this end
the ECL implements several new pseudo-parameters and builtin functions
containing this information. These include
Param Function Meaning
----- -------- -------
$errno errno() The system error number
$errmsg errmsg() The system error message string
$errtask errtask() Task which created the error
By default these parameters are re-defined as each task is called, in theory
allowing a script to trap errors without the IFERR by doing something like
mytask1 ()
if ($errno != 0) <statement>
mytask2 ()
if ($errno != 0) <statement>
:
This behavior can be modified by the 'erract' environment variable 'clear'
or 'noclear' settings so that they only change when an error condition is
found (i.e. erract set to 'noclear', tasks which complete successfully
do not modify variables).
Additionally, a new $err_dzvalue pseudo-parameter is defined to
be used by the CL interpreter when a divide-by-zero condition is encountered
in the CL itself. (This value has no builtin function equivalent.)
This is an integer and will be cast to floating-point automatically if
needed, the default value of 1 (one) was chosen to allow the script to
continue executing but it should be noted that this value is only used
when an error is found within an IFERR block. For example,
ecl> = 1 / 0
ERROR: integer divide by zero
ecl> = 1. / 0.
ERROR: floating divide by zero
However,
ecl> iferr {
>>> = 1 / 0
>>> } then ;;
Warning on line 31 of : integer divide by zero - using $err_dzvalue = 1
1
Note the warning message indicating the use of the parameter followed by the
result.
What Errors Are NOT Trapped
===========================
As mentioned above, not all CL errors can or should be trapped
by the new system. The (incomplete) list of error conditions which
CANNOT be trapped during task execution using the IFERR or other new
features includes:
o CL-language syntax errors
o CL internal errors, for example
- invalid procedure arguments (e.g. "parameter not found")
- improper usage of intrinsic procedures (e.g. log(-10) )
- operand type mis-matches (e.g. "s1 + x")
- parser errors (e.g. newline in string)
o CL runtime errors
- too many background jobs (e.g. "no available job slots")
- insufficient resource messages (e.g. out of memory)
- can't read/write/create files (e.g. permissions problem on uparm$)
- ambiguous task name
- scan/print string exceeds max length
o User-defined error messages and returns (i.e. the script writer
outputs an error message and returns from the procedure but
does not use something like thea CL error() function to abort.
For instance, a script prints "I can't run this on Tuesdays" and
returns to the command-line but does not otherwise post an error
condition for the calling context.
============================================
Command-line History and BackSpace Revisions
============================================
The ECL now implements the common GNU Readline interface for input
handing meaning that many familiar tcsh-like features such as Up/Down-Arrow
history, Left/Right cursor-position movement, and tab-filename completion
are now understood in the IRAF environment. It follows that many of
the problems encountered with the DEL/BS key to erase characters when
entering input on the commandline have also been eliminated on most
systems since the readline interface internally handles the delete-key
mappings imposed on most systems. Tab-completion of task/params names
was not implemented in this initial release but could be added later.
It is important to note that this implementation was done so as
to not interfere with the native IRAF ehist/epar cursor and history
mechanism. From the ECL prompt, all commands recognized by readline()
interface (including user mappings defined in an ".inputrc" file) will
be honored. If that command is ehist/epar or one of the recognized
IRAF history editing meta-characters then these will be processed in the
traditional IRAF manner.
Should a problem with readline input be found, it can be disabled
from the user's session by adding the string "noreadline" to the CL
'ehinit' parameter, e.g.
ecl> cl.ehinit = cl.ehinit // " noreadline"
Input Command Summary
---------------------
The following Control/Meta key sequences are understood by the
readline() interface for command input:
Basic Commands
Ctrl-b Move cursor back one character.
Ctrl-f Move cursor forward one character.
DEL Delete the character to the left of the cursor.
Backspace Delete the character to the left of the cursor.
Ctrl-d Delete the character underneath the cursor.
Ctrl-_ Undo the last editing command
Ctrl-x Ctrl-u Undo the last editing command
Up-Arrow Move up through the command-history list
Down-Arrow Move down through the command-history list
Left-Arrow Move cursor left one character on command line
Right-Arrow Move cursor right one character on command line
Cursor Movement Commands
Ctrl-a Move to the start of the line.
Ctrl-e Move to the end of the line.
Meta-f Move forward a word, where a word is composed of letters/digits.
Meta-b Move backward a word.
Ctrl-l Clear the screen, reprinting the current line at the top.
Text Deletion Commands
Ctrl-k Kill the text from the current cursor position to the end of
the line.
Meta-d Kill from the cursor to the end of the current word, or, if
between words, to the end of the next word. Word boundaries
are the same as those used by Meta-f.
Meta-DEL Kill from the cursor the start of the current word, or, if
between words, to the start of the previous word. Word
boundaries are the same as those used by Meta-b.
Ctrl-w Kill from the cursor to the previous whitespace. This is
different than Meta-DEL because the word boundaries differ.
To yank (copy the most-recently-killed text from the kill buffer) the text
back into the line:
Ctrl-y Yank the most recently killed text back into the buffer at
the cursor.
Meta-y Rotate the kill-ring, and yank the new top. You can only do
this if the prior command is Ctrl-y or Meta-y.
History Searching Commands
Ctrl-r Search backward through the history for a particular string
Ctrl-s Search forward through the history for a particular string
ESC Terminate the search
Ctrl-g Terminate the search and restore original line
As each character of the search string is typed, Readline displays
the next entry from the history matching the string typed so far. An
incremental search requires only as many characters as needed to
find the desired history entry. To find other matching entries in
the history list, type Ctrl-r or Ctrl-s as appropriate from the current
search position.
NOTE: In many terminal settings the Ctrl-s key is mapped to the tty
'stop' character and the window will appear to no longer accept
input. In these cases a Ctrl-q will normally return the terminal
to proper function and so the forward search mechanism isn't
generally recommended.
=====================
New Builtin Functions
=====================
Error-Handling Functions
------------------------
The following builtin functions were added as alternatives to the
matching CL parameters. The difference is almost entirely stylistic and
the rules about the longevity of the values described above apply in either
case.
errmsg () Return last error message string (i.e. cl.$errmsg)
errcode () Return last integer error code (i.e. cl.$errno)
errtask () Return taskname posting fatal error (i.e. cl.$errtask)
Examples:
iferr {
sometask (par1, ....)
} then {
printf ("Error in '%s': %s\n", errtask(), errmsg())
# or equivalently
printf ("Error in '%s': %s\n", $errtask, $errmsg)
}
String Functions
----------------
Beginning with V2.12.2 several new functions were added to the
CL to improve string handling and the provide complementary functions
to those which already exist. Items marked with a '*' first appeared in
V2.12.2, all others are new to this release.
New functions include:
isindef (expr) (*)
Can be used to check for INDEF values in expressions. INDEF
values may be tested for equality, however when otherwise used
in a boolean expression the result of the boolean is also
INDEF. This function can be used to trap this particular
case, or for INDEF strings/variable directly. Result is a
boolean yes/no.
Example:
cl> junk = fscan (threshold, tval)
cl> if (isindef (tval) == yes)
error (0, "INDEF 'threshold' parameter value")
strlwr (str) (*)
strupr (str) (*)
Convert the string to lower/upper case, returns a string.
Example:
cl> s1 = "test" ; s2 = "TEST"
cl> = strupr (s1) ; = strlwr (s2)
TEST
test
strstr (str1, str2) (*)
Search for first occurance of 'str1' in 'str2', returns index
of the start of 'str1' or zero if not found.
Example:
cl> = strstr ("imh", "imhead.imh")
1
cl> = strstr ("head", "imhead.imh")
3
strldx (chars, str) (*)
Complement to the stridx() which returns the last occurance of
any of 'chars' in 'str'. Returns index of last char or zero
if not found.
Example:
cl> = strldx (".", "junk.fits")
5
strlstr (str1, str2) (*)
Search for last occurance of 'str1' in 'str2', returns index
of the start of 'str1' or zero if not found.
Example:
cl> = strlstr ("imh", "imhead.imh")
8
[NOTE: String indices are 1-indexed in the CL]
trim (str [, trimchars])
triml (str [, trimchars])
trimr (str [, trimchars])
Trim any of the chars in 'trimchars' from the ends of 'str'.
The trim() function removes chars from both the front and back
of the string, triml() removes only from the left side of the
string, and trimr() removes only from the right side. If the
'trimchars' argument is not specified the whitespace chars
(tab and space) are assumed.
Example:
cl> printf ("'%s'\n", trim (" test "))
'test'
cl> = trimr ("/iraf/iraf///////", "/")
/iraf/iraf
To check for strings containing only whitespace:
if (trim (foo) == "")
error (0, "no legal value specified for 'foo'")
The new string functions are particularly useful for dealing with
pathnames where one needs to find and extension, separate a file from a
path prefix, trim trailing slashes. and so on.
Additionally, the existing substr() function has been modified to
allow a 'last' index greater than a 'first' index, in which case the return
string is reversed.
Trig Functions
--------------
The following trigonometric functions have been added as new builtins
to the CL. These complement existing functions as well as provide utility
versions to simplify degree/radian conversion.
asin (arg) Inverse SIN, result in radians
acos (arg) Inverse COS, result in radians
rad (rad_arg) Convert arg in radians to degrees
deg (deg_arg) Convert arg in degrees to radians
dsin (deg_arg) Sine function, arg in degrees
dcos (deg_arg) Cosine function, arg in degrees
dtan (deg_arg) Tangent function, arg in degrees
dasin (arg) Inverse sine function, result in degrees
dacos (arg) Inverse cosine function, result in degrees
datan2 (y, x) Inverse tangent function, result in degrees
Utility Functions
-----------------
The following utility functions have been added.
fp_equal (arg1, arg2) Floating point compare (w/in machine precision)
hypot (x, y) Euclidean distance (i.e. sqrt (x*x + y*y))
sign (arg) Sign of argument (-1 or 1)
Examples:
cl> = fp_equal (1.2345, 1.234)
0
cl> = hypot (3, 4) # may also take real arguments
5
cl> = sign (-23) # may also take real arguments
-1
Bitwise Operations
------------------
The following bitwise operands have been added in the V2.12.2b.
Note that these are bitwise operands and not logical operands. While there
is presently no direct need for these they are seen as potentially useful
in e.g. evaluating bit-flags stored in image header keywords and support the
goal of providing a richer scripting language.
not (arg1) Bitwise boolean NOT of an integer
and (arg1, arg2) Bitwise boolean AND of two integers
or (arg1, arg2) Bitwise boolean OR of two integers
xor (arg1, arg2) Bitwise exclusive OR of two integers
Examples:
cl> = radix (12, 2) # print bit pattern of number 12
1100
cl> = radix (13, 2) # print bit pattern of number 13
1101
cl> = and (12, 13) # 1100 & 1101 == 1100
12
cl> = or (12, 13) # 1100 | 1101 == 1101
13
cl> = xor (12, 13) # (1100 & ~1101) | (~1100 & 1101) == 1
1
cl> = not (12)
-13
cl> = radix (not(12), 2)
11111111111111111111111111110011
=================
Defined Constants
=================
The ECL also introduces the ability to use common numerical and
physical constants in scripts as part of the language keyword set. Constants
are, by convention, always upper case identifiers and are listed in the table
below:
Numerical constants
+---------------------------------------------------------------------+
| Name | Value | Units |
+---------------------------------------------------------------------+
| BASE_E | 2.7182818284590452353 | |
| FOURPI | 12.566370614359172953 | |
| GAMMA | .57721566490153286061 | |
| HALFPI | 1.5707963267948966192 | |
| LN_10 | 2.3025850929940456840 | |
| LN_2 | .69314718055994530942 | |
| LN_PI | 1.1447298858494001741 | |
| LOG_E | .43429448190325182765 | |
| PI | 3.1415926535897932385 | |
| RADIAN | 57.295779513082320877 | |
| SQRTOF2 | 1.4142135623730950488 | |
| SQRTOFPI | 1.7724538509055160273 | |
| TWOPI | 6.2831853071795864769 | |
+---------------------------------------------------------------------+
Physical constants
+---------------------------------------------------------------------+
| Name | Value | Units |
+---------------------------------------------------------------------+
| AU | 1.49597870691e11 | m |
| GRAV_ACCEL | 9.80665e0 | m / sec^2 |
| GRAV_CONST | 6.673e-11 | m^3 / kg s^2 |
| LIGHT_YEAR | 9.46053620707e15 | m |
| PARSEC | 3.08567758135e16 | m |
| SPEED_OF_LIGHT | 299792458.0 | m / sec |
| SOLAR_MASS | 1.98892e30 | kg |
+---------------------------------------------------------------------+
For example, these may be used in scripts as:
area = (PI * radius ** 2) # Compute area of circle.
rad = degrees / RADIAN # Convert degrees to radians
===============================================================================
# Post-Release Notes
===============================================================================
.fi
|