A Beta release of the new Error-Handling CL (ECL) is now available for testing on machines which get their IRAF from the downtown Tucson servers (ursa/gemini), or as an add-on distribution for laptop systems. So that no special setup is required and because no current functionality is affected, we are taking the unusual step of adding this test release to the standard IRAF V2.12.2a system rather than to just the development version. ECL is now available for Solaris, RedHat, Linux, and Mac OS X systems and will be part of the next IRAF patch release.
New Features of the ECL Include:
ftp://iraf.noao.edu/pub/ecl.readme
or within the ECL using the command
ecl> page pkg$ecl/Notes.ecl
To start the new version of the CL simply issue the command
% cl -ecl
The default prompt for the ECL is now "ecl>" as a visual clue to which
version is being used, the above command can be aliased by users who wish
to make the ECL their default.
ftp://iraf.noao.edu/pub/ecl_install_<arch>.csh
where <arch> is the standard iraf architecture name for each platform
(i.e. 'redhat', 'linux', 'macosx' or 'ssun').
The installer will currently overlay the IRAF system on the machine and create a symlink in a system bin directory and so requires root permission to execute, but it may be run from any directory. To install the files simply download the installer in binary mode, reset the execute permissions if needed, and run the script. A "personal installation" version for users who do not have permissions to overwrite their IRAF installation will be available with the first public release.
Note this is still a test release and problems can be expected. The familiar 'cl' command will still instantiate the traditional CL and users must purposefully start the ECL to take advantage of new features. No other changes to the system or external packages have been made or are required, users of the old CL will see no change as a result of this test release.
Future updates to the Tucson servers will be done during off-hours to minimize interference with currently running sessions. Please report any problems, comments or suggestions to
iraf@noao.edu
as soon as they are discovered.
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
Personal Installation Instructions
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.
Note: The installer scripts require the 'uudecode' command which may be
in an optional package under some operating systems. Linux users
should be sure the 'uudecode' command exists (e.g. in /bin, /usr/bin
or using the "which uudecode" command) and/or that the 'sharutils'
package is installed on their system.
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.
Personal Installation Instructions
----------------------------------
At this time the auto-installer doesn't implement a personal
install option, however this can be done pretty easily by hand. If
you do not have root permission on your machine, you can install manually
using the following procedure:
1) Download the source tarball from
ftp://iraf.noao.edu/pub/ecl.tar.gz
2) Build and install:
If you have 'iraf' permissions and want to install in the IRAF tree:
a) Create a iraf$pkg/ecl directory
% mkdir /iraf/iraf/pkg/ecl
b) Unpack the tar file in the new directory
% cd /iraf/iraf/pkg/ecl # go there
% cat /<path>/ecl.tar.gz | gunzip | tar -xvf -
c) Build the executable (assumes $iraf/$IRAFARCH defined)
% mkpkg relink
This creates an 'ecl.e' executable in the directory.
d) Install the files
% cp ecl.e $iraf/bin.<arch>/ # install binary
% cp $hlib/cl.csh $hlib/cl.csh.ORIG # backup cl.csh
% cp cl.csh $hlib/cl.csh # replace w/ new one
e) Run the ECL using the command
% cl -ecl
This install ECL for use by anyone on the system, however it
does not create an 'ecl' command link so the above is required.
If you want to do a personal installtion (i.e. no root or iraf
permissions)
a) Create a directory for the ECL sources:
% mkdir ~/iraf/ecl
b) Unpack the tar file in the new directory
% cd ~/iraf/ecl # go there
% cat /<path>/ecl.tar.gz | gunzip | tar -xvf -
c) Build the executable (assumes $iraf/$IRAFARCH defined)
% mkpkg relink
This creates an 'ecl.e' executable in the directory.
d) Since the 'cl.csh' script assumes the binary is in the IRAF
tree it cannot be used for a personal install, however one can
define a unix environment to use the binary directly, e.g.
% setenv iraf /iraf/iraf/ # trailing '/' required
% setenv IRAFARCH redhat # or ssun, or macosx, or
% ...
% setenv arch .$IRAFARCH # Note '.' !!!
% source $iraf/unix/hlib/irafuser.csh # additional definitions
These lines should be put in the user's .cshrc file.
e) Run the ECL by calling the binary directly, i.e.
% /home/fred/iraf/ecl/ecl.e
Or simply create a command alias to do it, i.e.
alias ecl /home/fred/iraf/ecl/ecl.e
Note that $iraf path and IRAFARCH settings need to be correct for the
machine. The ECL requires a login.cl/uparm just as the old CL does.
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' (or '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> command-line
completion of tasks, filenames and parameters 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.
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
===============================================================================
IRAF
NOAO
Last updated: 06Mar2000