aboutsummaryrefslogtreecommitdiff
path: root/pkg/ecl/Notes.ecl
blob: e9ed3ceabcac03b16231b1b3d213138dd0ebed38 (plain) (blame)
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

		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 occuring 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 metacharacters 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
===============================================================================