Copyright: IDL Adv.Dev.Guide 1.0 (c)1995 Research Systems,Inc


IDL Online Help Navigator
IDL Advanced Development Guide Navigator

Chapters in the Advanced Development Guide:

1 Overview

2 Types

3 Variables

4 Keyword Processing

5 String Processing

6 Error Handling

7 Type Conversion

8 Files and Input/Output

9 Signals

10 Timers

11 Miscellaneous Information

12 Adding System Routines

13 Introduction to Callable IDL

14 Using Callable IDL under Unix and VMS

15 Using Callable IDL under Windows

16 AppleScript Support

17 Adding External Widgets to IDL

18 Obsolete Internal Interfaces


Overview
1	Overview

Chapter Overview

This document describes the internal implementation of IDL version 4 in sufficient detail to allow the user to write code in other languages and link it with IDL. It explains how to write code that will be called directly from IDL (CALL_EXTERNAL), how to add built-in system routines and functions (LINKIMAGE), and how to call IDL as a subroutine from other programs ("Callable IDL").

Using this Document with Previous Versions of IDL

This document describes the interface to the IDL internals introduced with IDL version 4.0. Those using CALL_EXTERNAL or LINKIMAGE with previous versions of IDL should consult Chapter 18, "Obsolete Internal Interfaces" for compatibility information.


Topics in Overview Chapter
Overview

This chapter provides an overview of the Advanced Development Guide and an introduction to IDL's inter-language communication techniques.

Topics Covered:

Inter-language Communication Techniques Supported by IDL

When is it Appropriate to Add Code to IDL?

Skills Required to Add Code to IDL

Recommended Reading

IDL Organization

External Definitions

Linking Details

Reading the Remainder of this Book

Jump forward to Types


Inter-language Communication Techniques Supported by IDL

IDL supports a number of different techniques for communicating with the operating system and programs written in other languages. These methods are described, in brief, below.

SPAWN

The simplest (but most limited) way to access programs external to IDL is to use the SPAWN procedure. Calling SPAWN spawns a child process that executes a specified command. Under Unix and VMS, the output from SPAWN can be captured in an IDL string variable. Under Unix, IDL can communicate with a child process through a bi-directional pipe using SPAWN. More information about SPAWN can be found in Chapter 18 of the IDL User's Guide, "Operating System Access", or in the documentation for SPAWN in volume 2 of the IDL Reference Guide. SPAWN is not discussed further in this book.

Remote Procedure Calls (RPCs)

Unix platforms can use Remote Procedure Calls (RPCs) to facilitate communication between IDL and other programs. IDL is run as an RPC server and your own program is run as a client. IDL's RPC functionality is documented in Chapter 24 of the IDL User's Guide, "Remote Procedure Calls". Remote Procedure Calls are not discussed further in this book.

CALL_EXTERNAL

IDL's CALL_EXTERNAL function loads and calls routines contained in shareable object libraries. IDL and the called routine share the same memory and data space. CALL_EXTERNAL is much easier to use than either LINKIMAGE or Callable IDL and is often the best (and simplest) way to communicate with other programs. CALL_EXTERNAL is also supported on all IDL platforms, except for Ultrix.

While many of the topics in this book can enhance your understanding of CALL_EXTERNAL, more complete documentation and examples can be found in Chapter 18 of the IDL User's Guide, "Operating System Access" and the documentation for CALL_EXTERNAL in volume 1 of the IDL Reference Guide.

LINKIMAGE

IDL's LINKIMAGE procedure merges routines written in other languages with IDL at run-time. LINKIMAGE is only available under VMS, Windows NT, and on Unix systems that support the dynamic loading of object modules. Note that LINKIMAGE is substantially more difficult to use than CALL_EXTERNAL, but can be more flexible and powerful. LINKIMAGE provides access to variables and other objects inside of IDL.

This book contains the information necessary to successfully add your own code to IDL using LINKIMAGE. Especially important is Chapter 12, "Adding System Routines". Additional information about LINKIMAGE can be found in the documentation for LINKIMAGE in volume 1 of the IDL Reference Guide and in Chapter 18 of the IDL User's Guide, "Operating System Access".

Callable IDL

IDL for Windows, IDL for Unix, and IDL for VMS are packaged in a shareable form that allows other programs to call IDL as a subroutine. This shareable portion of IDL can be linked into your own programs. This use of IDL is called "Callable IDL" to distinguish it from the more usual case of calling your code from IDL via CALL_EXTERNAL or LINKIMAGE. IDL for Macintosh supports "calls" to IDL via AppleScript.

This book contains the information necessary to successfully call IDL from your own code. Introductory material and caveats about Callable IDL can be found in Chapter 13, "Introduction to Callable IDL".


When is it Appropriate to Add Code to IDL?

IDL is an interactive program that runs across numerous operating systems and hardware platforms. The IDL user enjoys a large amount of portability across these platforms because IDL provides access to system abilities at a relatively high level of abstraction. The large majority of IDL users have no need to understand its inner workings or to link their own code into it and thus no particular reason to read this document other than to satisfy their own natural curiosity.

There are, however, some reasons to add code to IDL:

•	Many sites have an existing investment in other code that they would prefer to use from IDL rather than incurring the cost of rewriting it in the IDL language.

•	It is often best to use the reference implementation of a software package rather than re-implement it in another language, risk adding incorrect behaviors to it, and incur the ongoing maintenance costs of supporting it.

•	IDL is largely suitable for a task, but requires the addition of an operation that cannot be performed efficiently in the IDL language.

A programmer considering adding compiled code to IDL should understand the following caveats:

•	Research Systems attempts to keep the interfaces described in this document stable, and we endeavor to minimize gratuitous change. However, we reserve the right to make any changes required by the future evolution of the system. Code linked with IDL is more likely to require updates and changes to work with new releases of IDL than programs written in the IDL language.

•	The act of linking compiled code to IDL is inherently less portable than use of IDL at the user level.

•	Troubleshooting and debugging such applications can be very difficult. With standard IDL, malfunctions in the program are clearly the fault of Research Systems, and given a reproducible bug report, we attempt to fix them promptly. A program that combines IDL with other code makes it difficult to unambiguously determine where the problem lies. The level of support Research Systems can provide in such troubleshooting is minimal. The programmer is responsible for locating the source of the difficulty. If the problem is in IDL, a simple program demonstrating the problem must be provided before we can address the issue.


Skills Required to Add Code to IDL

There is a large difference between the level at which a typical user sees IDL compared to that of the internals programmer. To the user, IDL is an easy-to-use, array-oriented language that combines numerical and graphical abilities, and runs on many platforms. Internally, IDL is a large, complicated C language program that includes a compiler, an interpreter, graphics, mathematical computation, user interface, and a large amount of operating system-dependant code.

The amount of knowledge required to effectively write internals code for IDL can come as a surprise to the user who is only familiar with IDL's external face. To be successful, the programmer must have experience and proficiency in the following areas:

ANSI C

IDL is written in ANSI C. To understand the data structures and routines described in this document, you must have a complete understanding of this language.

System C Compiler, Linker, and Libraries

In order to successfully integrate IDL with your code, you must fully understand the compilation tools being used as well as those used to build IDL and how they might interact. IDL is usually built with the vendor-supplied C compiler.

Inter-language Calling Conventions

It is possible to link IDL with code written in languages other than C, although the details differ depending on the machine, language, and compiler used. It is the programmer's responsibility to understand the inter-language calling conventions and rules for the target environment—there are too many possibilities for Research Systems to actively support them all. ANSI C is a standard system programming language on all systems supported by IDL, so it is usually straightforward to combine it with code written in other compiled languages. You need to understand:

•	The conventions used to pass parameters to functions in both languages. For example, C uses call by value while FORTRAN uses call by reference. It is easy to compensate for such conventions, but they must be taken into account.

•	Any systematic name changes applied by the compilers. For example, some compilers add underscores at the beginning or ends of names of functions and global data.

•	Any runtime initialization that must be performed. On many systems, the real initial entry point for the program is not main(), but a different function that performs some initialization work and then calls your main() function. If you call IDL from a program written in a language other than C, has the necessary initialization occurred? If you use IDL to call code written in a language other than C, do you need to take steps to initialize the runtime system for that language? Are the two runtime systems compatible? Usually these issues have been addressed by the system vendor, who has a large interest in allowing such inter-language usage.

Operating System Features And Conventions

With the exception of purely numerical code, the programmer must usually fully understand the target operating system environment in which IDL is running in order to write code to link with it.

Microsoft Windows

You must be an accomplished Windows programmer with an understanding of 32­bit applications, WIN32, WIN32S, and DLLs.

Unix

You should understand system calls, signals, processes, standard C libraries, and possibly even X Windows depending on the scope of the code being linked.

VMS

You should understand system services, the Run Time Libraries, file I/O, and processes.


Recommended Reading

There are many books written on the topics discussed in the previous section. The following list includes books we have found to be the most useful over the years in the development and maintenance of IDL. There are thousands of books not mentioned here. Some of them are also excellent. The absence of a book from this list should not be taken as a negative recommendation.

The C Language

Kernighan, Brian W. and Dennis M. Ritchie. The C Programming Language, Second Edition. Englewood Cliffs, New Jersey: Prentice Hall, 1988. ISBN 0-13-110370-9. This is the original C language reference, and is essential reading for this subject.

In addition, you should study the vendor supplied documentation for your compiler.

Microsoft Windows

The following books will be useful to anyone building IDL system routines or applications that call IDL in the Microsoft Windows environment.

Petzold, Charles. Programming Windows 3.1. Redmond, Washington: Microsoft Press, 1992. ISBN 1-55615-395-3

Richter, Jeffery. Advanced Windows NT. Redmond, Washington: Microsoft Press, 1994. ISBN 1-55615-567-0

Microsoft, 1993. Win32 Programmers Reference Volumes 1-5. Redmond, Washington: Microsoft Press, 1993. ISBN 1-55615-515-8 (v.1), ISBN 1-55615-516-6 (v.2), ISBN 1-55615-517-4 (v.3), ISBN 1-55615-518-2 (v.4), ISBN 1-55615-519-0 (v.5),

Unix

Stevens, W. Richard. Advanced Programming in the UNIX Environment. Reading, Massachusetts: Addison Wesley, 1992. ISBN 0-201-56317-7. This is the definitive reference for Unix system programmers. It covers all the important Unix concepts and covers the major Unix variants in complete detail.

Rochkind, Marc J. Advanced UNIX Programming. Englewood Cliffs, New Jersey: Prentice Hall, 1985. ISBN 0-13-011818-4. This volume is also extremely well written and does an excellent job of explaining and motivating the Unix concepts that underlie the Unix system calls. This book suffers in comparison to the Stevens book in that it discusses older Unix systems rather than current systems and lacks discussion of networking. However, what it does cover is correct and very readable, and it is much shorter than Stevens.

The vendor-supplied documentation and manual pages should be used in combination with the books listed above.

VMS

The standard VMS reference manuals published by Digital Equipment Corporation cover the material needed by VMS programmers. Of special importance are the System Services, RunTime Library, C Language, and Linker documentation.

X Windows

The X Windows series by O'Reilly & Associates contains all the information needed to program for the X Window system. There are several volumes—the ones you will need depend on the type of programming you are doing.

Scheifler, Robert W. and James Gettys. X Window System. Digital Press. This is purely a reference manual, as opposed to the O'Reilly books which contain a large amount of tutorial as well as reference information. This book is primarily useful for those using XLIB to draw graphics into Motif Draw Widgets and for those who need to understand the base layers of X Windows. Motif programmers may not require this information since Motif hides many of these details.

There are many other X Windows books on the market with varying levels of quality and usefulness. Note that most X Windows books are updated with each version of the system. (X Version 11, Release 6 is the current version at this printing.)


IDL Organization

In order to properly write code to be linked with IDL, it is necessary to understand a little about its internal operation. This section is intended to give just enough background to understand the material that follows. Traditional interpreted languages work according to the following algorithm:

while (statements remaining) {
  Get next statement.
  Perform lexical analysis and parse statement.
  Execute statement.
}

This description is accurate at a conceptual level, and most early interpreters did their work in exactly this way due to its simplicity. However, this scheme is inefficient for the reasons stated below.

•	The meaning of each statement is determined by the relatively expensive operations of lexical analysis, parsing, and semantic analysis each and every time the statement is encountered.

•	Since each statement is considered in isolation, any statement that requires jumping to a different location in the program will require an expensive search for the target location. Usually, this search starts at the top of the file and moves forward until the target is found.

To avoid these problems, the IDL system uses a two-step process in which compilation and interpretation are separate. The core of the system is the interpreter. The interpreter implements a simple, stack-based postfix language, in which each instruction corresponds to a primitive of the IDL language. This internal form is a compact binary version of the IDL language routine. Routines written in the IDL language are compiled into this internal form by the IDL compiler when the .RUN executive command is issued, or when any other command requires a new routine to be executed. Once the IDL routine is compiled, the original version is ignored, and all references to the routine are to the compiled version. Some of the advantages of this organization are:

•	The expensive compilation process is only performed once, no matter how often the resulting code is executed.

•	Statements are not considered in isolation, so the compiler keeps track of the information required to make jumping to a new location in the program fast.

•	The binary internal form is much faster to interpret than the original form.

•	The internal form is compact, leading to better use of main memory, and allowing more code to fit in any memory cache the computer might be using.

The Interpreter Stack

The primary data structure in the interpreter is the stack. The stack contains pointers to variables, which are implemented by IDL_VARIABLE structures (see "The IDL_VARIABLE Structure" on page 3-1). Pointers to IDL_VARIABLEs are referred to as IDL_VPTRs. Most interpreter instructions work by removing a predefined number of elements from the stack, performing their function, and then pushing the IDL_VPTR to the resulting IDL_VARIABLE back onto the stack. The removed items are the arguments to the instruction, and the new element represents the result. In this sense, the IDL interpreter is no different from any other postfix language interpreter. When an IDL routine is compiled, the compiler checks the number of arguments passed to each system routine against the minimum and maximum number specified in an internal table of routines, and signals an error if an invalid number of arguments is specified.

At execution time, the interpreter instructions that execute system procedures and functions operate as follows:

1.	Look up the requested routine in the internal table of routines.

2.	Execute the routine that implements the desired routine.

3.	Remove the arguments from the stack.

4.	If the routine was a function, push its result onto the stack.

Thus, the compiler checks for the proper number of arguments, and the interpreter does all the work related to pushing and popping elements from the stack. The called function need only worry about executing its operation and providing a result.


External Definitions

The file export.h, found in the external subdirectory of the IDL distribution, supplies all the IDL-specific definitions required to write code for inclusion with IDL. As such, this file defines the interface between IDL and your code. It will be worth your while to examine this file, reading the comments and getting a general idea of what is available. If you are not writing in C, you will have to translate the definitions in this file to suit the language you are using.

CAUTION: export.h contains some declarations which are necessary to the compilation process, but which are still considered private to Research Systems. Such declarations are likely to be changed in the future and should not be depended on. In particular, many of the structure data types discussed in this document have more fields than are discussed here—such fields should not be used. For this reason, you should always include export.h rather than entering the type definitions from this document. This will also protect you from changes to these data structures in future releases of IDL. Anything in export.h that is not explicitly discussed in this document should not be relied upon.

The following two lines should be included near the top of every C program file that is
to become part of IDL:

#include <stdio.h>
#include "export.h"


Linking Details

Once you've written your code, you need to compile it and link it into IDL before it can be run. Information on how to do this is available in several places:

•	The IDL Reference Manual contains descriptions of CALL_EXTERNAL and LINKIMAGE.

•	Chapter 18, "Operating System Access", of the IDL User's Guide contains several examples of how to compile and link code for use with CALL_EXTERNAL. This discussion is equally relevant for LINKIMAGE. Callable IDL users will find it useful as well.

•	The Unix IDL distribution has a bin subdirectory that contains platform specific directories that in turn hold the actual IDL binary and related files. Included with these files is a Makefile that shows how to build IDL from the shareable libraries present in the directory. The link line in this makefile should be used as a starting point when linking your code with Callable IDL—simply omit main.o and include your own object files, containing your own main program.

•	A more detailed description of the issues involved in compiling and linking your code can be found in this book under "Compiling and Linking Programs that Call IDL" on page 14-5.


Reading the Remainder of this Book

After reading this chapter, the remainder of this book can be read in many different orders. Chapters 2 through 11 can be read in any order and are largely independent of each other. Those wishing to add system routines to IDL will need to read Chapter 12, "Adding System Routines", while those wishing to use Callable IDL should start with Chapter 13, "Introduction to Callable IDL".


Types
2	Types

Chapter Overview

This chapter describes the implementation of data types in IDL.


Topics in Types Chapter
Types

This chapter describes the implementation of data types in IDL.

Topics Covered:

Type Codes

Mapping Of Basic Types

Jump forward to Variables

Jump backward to Overview


Type Codes

Every IDL variable has a data type. The possible type codes and their mapping to C language types are listed in Table 2.1. The undefined type code (IDL_TYP_UNDEF) will always have the value zero.

Although it is unlikely, the number of types could change someday. Therefore, you should always use the symbolic names when referring to any type except IDL_TYP_UNDEF. Even in the case of IDL_TYP_UNDEF, using the symbolic name will add clarity to your code. Note that all IDL structures are considered to be of a single type (IDL_TYP_STRUCT).

Name	Type	C Type
IDL_TYP_UNDEF	Undefined	<None>
IDL_TYP_BYTE	Unsigned byte	UCHAR
IDL_TYP_INT	16­bit integer	short
IDL_TYP_LONG	32­bit integer	IDL_LONG
IDL_TYP_FLOAT	Single precision floating	float
IDL_TYP_DOUBLE	Double precision floating	double
IDL_TYP_COMPLEX	Single precision complex	IDL_COMPLEX
IDL_TYP_STRING	String	IDL_STRING
IDL_TYP_STRUCT	Structure	See "Structure Variables" on page 3-5.
IDL_TYP_DCOMPLEX	Double precision complex	IDL_DCOMPLEX
Table 2.1 IDL Types and Mapping to C

Clearly, distinctions must be made between various structures, but such distinctions are made at a different level. There are a few constants that can be used to make your code easier to read and less likely to break if/when the export.h file changes. These are:

•	IDL_MAX_TYPE—The value of the largest type.

•	IDL_NUM_TYPES—The number of types. Since the types are numbered starting at zero, IDL_NUM_TYPES is one greater than IDL_MAX_TYPE.

Type Masks

There are some situations in which it is necessary to specify types in the form of a bit mask rather than the usual type codes, for example when a single argument to a function can represent more than a single type. For any given type, the bit mask value can be computed as: Mask = 2^TypeCode

The IDL_TYP_MASK preprocessor macro is provided to calculate these masks. Given a type code, it returns the bit mask. For example, to specify a bit mask for all the integer types:

IDL_TYP_MASK(IDL_TYP_BYTE)|IDL_TYP_MASK(IDL_TYP_INT)|
                           IDL_TYP_MASK(IDL_TYP_LONG)

Specifying all the possible types would require a long statement similar to the one above. To avoid having to type so much for this common case, the IDL_TYP_B_ALL constant is provided.


Mapping Of Basic Types

Within IDL, the IDL data types are mapped into data types supported by the C language. Most of the types map directly into C primitives, while IDL_TYP_COMPLEX, IDL_TYP_DCOMPLEX, and IDL_TYP_STRING are defined as C structures. The mappings are given in Table 2.1. Structures are built out of the basic types by laying them out in memory in the specified order using the same alignment rules used by the C compiler for the target machine.

Unsigned Byte Data

UCHAR is defined to be unsigned char in export.h.

Long Integer Data

IDL long integers are defined to be 32-bits in size. The C long data type is not correct on all systems because C compilers for 64­bit architectures usually define long as 64-bits. Hence, the IDL_LONG typedef, declared in export.h is used instead.

Complex Data

The IDL_TYP_COMPLEX and IDL_TYP_DCOMPLEX data types are defined by the following C declarations:

typedef struct { float r, i; } IDL_COMPLEX;

typedef struct { double r, i; } IDL_DCOMPLEX;

This is the same mapping used by FORTRAN compilers to implement their complex data types, which allows sharing binary data with such programs.

String Data

The IDL_TYP_STRING data type is implemented by a string descriptor:

typedef struct {
  unsigned short slen;  /* Length of string */
  short stype;          /* Type of string */
  char *s;              /* Pointer to string */
} IDL_STRING;

The fields of the IDL_STRING struct are defined as follows:

slen

The length of the string, not counting the null termination. For example, the string "Hello" has 5 characters.

stype

If stype is zero, the string pointed at by s (if any) was not allocated from dynamic memory, and should not be freed. If non-zero, s points at a string allocated from dynamic memory, and should be freed before being replaced. For information on dynamic memory, see "Dynamic Memory" on page 11-1 and see "Getting Dynamic Memory" on page 3-10.

s

If slen is non-zero, s is a pointer to a null-terminated string of slen characters. If slen is zero, s should not be used. The use of a string pointer to memory located outside the IDL_STRING structure itself allows IDL strings to have dynamically-variable lengths.

A Note about Strings

Strings are the most complicated basic data type, and as such, are at the root of more coding errors than the other types. See "String Processing" on page 5-1.


Variables
3	Variables

Chapter Overview

This chapter describes how variables are created and managed within IDL.


Topics in Variables Chapter
Variables

This chapter describes how variables are created and managed within IDL.

Topics Covered:

The IDL_VARIABLE Structure

Scalar Variables

Array Variables

Structure Variables

Temporary Variables

Creating an Array from Existing Data

Getting Dynamic Memory

Copying Variables

Storing Scalar Values

Obtaining the Name of a Variable

Looking Up Main Program Variables by Name

Looking Up Variables in Current Scope by Name

Jump forward to Keyword Processing

Jump backward to Types


The IDL_VARIABLE Structure

IDL variables are represented by IDL_VARIABLE structures. The definition of IDL_VARIABLE is as follows:

typedef struct {
  UCHAR type;
  UCHAR flags;
  IDL_ALLTYPES value;
}  IDL_VARIABLE;

where IDL_ALLTYPES is a union defined as:

typedef union {
  UCHAR c;           /* Scalar IDL_TYP_BYTE */
  short i;           /* Scalar IDL_TYP_INT */
  IDL_LONG l;        /* Scalar IDL_TYP_LONG */
  float f;           /* Scalar IDL_TYP_FLOAT */
  double d;          /* Scalar IDL_TYP_DOUBLE */
  IDL_COMPLEX cmp;   /* Scalar IDL_TYP_COMPLEX */
  IDL_DCOMPLEX dcmp; /* Scalar IDL_TYP_DCOMPLEX */
  IDL_STRING str;    /* Scalar IDL_TYP_STRING */
  IDL_ARRAY *arr;    /* Pointer to array descriptor */
  IDL_SREF s;        /* Pointer to structure descriptor */
}IDL_ALLTYPES;

The basic scalar types are contained directly in this union, while arrays and structures are represented by the IDL_ARRAY and IDL_SREF structures that are discussed later in this chapter. The type field of the IDL_VARIABLE struct contains one of the type codes discussed in "Type Codes" on page 2-1. When a variable is initially created, it is given the type code IDL_TYP_UNDEF, indicating that the variable contains no value.

The flags field is a bit mask that specifies information about the variable. As a programmer adding code to the IDL system, you will rarely need to set bits in this mask. These bits are set by whatever portion of IDL created the variable. You can check them to make sure the characteristics of the variable fit the requirements of your routine (see "Checking Arguments" on page 6-5). The defined bits in the mask are:

IDL_V_CONST

If this flag is set, the variable is actually a constant. This means that storage for the IDL_VARIABLE resides inside the code section of the user procedure or function that used it. The IDL compiler generates such IDL_VARIABLEs when an expression involving a constant occurs. For example, the IDL statement:

PRINT, 23 * A

causes the compiler to generate a constant for the "23". You must not change the value of this type of "variable".

IDL_V_TEMP

If this flag is set, the variable is a temporary variable. IDL maintains a pool of nameless IDL_VARIABLEs that can be checked out and returned as needed. Such variables are used by the interpreter to temporarily store the results of expressions on the stack. For example, the statement:

PRINT, 2 * 3

will cause the interpreter to go through a sequence of events similar to:

1.	Push a constant variable for the 2 on the stack.

2.	Push a constant variable for the 3 on the stack.

3.	Allocate a temporary variable, pop the two constants from the stack, perform the multiplication with the result going into the temporary variable.

4.	Push the temporary variable onto the stack.

5.	Call the PRINT system procedure specifying one argument.

6.	Remove the argument to PRINT from the stack, and return the temporary variable.

Temporary variables are also used inside user procedures and functions. See "Temporary Variables" on page 3-7.

IDL_V_ARR

If this flag is set, the variable is an array, and the value field of the IDL_VARIABLE points to an array descriptor.

IDL_V_FILE

If this flag is set, the variable is a file variable, as created by IDL's ASSOC function.

IDL_V_DYNAMIC

If this flag is set, the memory used by this IDL_VARIABLE is dynamically allocated. This bit is set for arrays, structures, and for variables of IDL_TYP_STRING (because the memory referenced via the string pointer is dynamic).

IDL_V_STRUCT

If this flag is set, the variable is a structure, and the value field of the IDL_VARIABLE points to the structure descriptor. For implementation reasons, all structure variables are also arrays, so IDL_V_STRUCT also implies IDL_V_ARR. Therefore, it is impossible to have a scalar structure. However, single-element structure arrays are quite common.

Because structure variables have their type field set to IDL_TYP_STRUCT, the IDL_V_STRUCT bit is redundant. It exists for efficiency reasons.


Scalar Variables

A scalar IDL_VARIABLE is distinguished by not having the IDL_V_ARR bit set in its flags field. A scalar variable must have one of the nine basic data types (IDL structures are never scalar). The data for a scalar variable is stored in the  IDL_VARIABLE itself, using the IDL_ALLTYPES union. Table 3.1 gives the relationship between the data type and the field used.

Scalar Data Type	Field that Stores Data
IDL_TYP_UNDEF	None.
IDL_TYP_BYTE	value.c
IDL_TYP_INT	value.i
IDL_TYP_LONG	value.l
IDL_TYP_FLOAT	value.f
IDL_TYP_DOUBLE	value.d
IDL_TYP_COMPLEX	value.cmp
IDL_TYP_DCOMPLEX	value.dcmp
IDL_TYP_STRING	value.str
Table 3.1 Scalar Variable Data Locations


Array Variables

Array variables have the IDL_V_ARR bit of their flags field set, and the value.arr field points to an array descriptor defined by the IDL_ARRAY structure:

typedef struct {
  IDL_LONG elt_len;
  IDL_LONG arr_len;
  IDL_LONG n_elts;
  char *data;
  UCHAR n_dim;
  UCHAR flags;
  short file_unit;
  IDL_LONG dim[IDL_MAX_ARRAY_DIM];
} IDL_ARRAY;

The meaning of the fields of an array descriptor are:

elt_len

The length of each array element in bytes (chars). The array descriptor does not keep track of the types of the array elements, only their lengths. Single elements can get quite long in the case of structures.

arr_len

The length of the entire array in bytes. This value could be calculated as (elt_len * n_elts), but is used so frequently that it is maintained as a separate field in the IDL_ARRAY struct.

n_elts

The number of elements in the array.

data

A pointer to the data area for the array. This is a region of dynamically allocated memory arr_len bytes long. This pointer should be cast to be a pointer of the correct type for the data being manipulated. For example, if the array variable being processed is pointed at by an IDL_VPTR named v and contains IDL_TYP_INT data:

short *data;        /* Declare a pointer variable */
data = (short *) v->value.arr->data;

n_dim

The number of array dimensions. The constant IDL_MAX_ARRAY_DIM defines the upper limit of this value.

flags

A bit mask that specifies characteristics of the array. Currently, only one bit value is defined for this field:

IDL_A_FILE

This flag indicates that the array is a file variable, as created by the ASSOC function. The variable has an array block to specify the structure of the variable, but it has no data area. The data field of the IDL_ARRAY structure does not contain useful information, and should not be used.

file_unit

When the IDL_A_FILE bit is set in the flags field, file_unit contains the IDL Logical Unit Number associated with the variable.

dim

An array that contains the dimensions of the IDL variable. There can be up to IDL_MAX_ARRAY_DIM dimensions. The number of dimensions in the current array is given by the n_dim field.


Structure Variables

Structure variables have the type code IDL_TYP_STRUCT. They also have the IDL_V_STRUCT bit set in their flags field. The value.s field of such a variable contains a structure descriptor defined by the IDL_SREF structure:

typedef struct {
  IDL_ARRAY *arr;     /* ^ to IDL_ARRAY containing data */
  void *sdef;         /* ^ to structure definition */
} IDL_SREF;

The arr field points at an array block, as described on page 3-3. It is worth noting that in the definition of the IDL_ALLTYPES union (described on page 3-1), the arr field is a pointer to IDL_ARRAY, while the s field is an IDL_SREF, a structure that contains a pointer to IDL_ARRAY as its first member. The resulting definition looks like:

union {
  IDL_ARRAY arr;
  struct {
    IDL_ARRAY arr;
    void *sdef;
  } s;
} value;

Due to the way C lays out fields in structs and unions, value.arr will have the same offset and size within the value union as value.s.arr. Therefore, it is possible to access the array block of a structure variable as var->value.arr rather than the more correct var->value.s->arr. You should avoid use of this shorthand, however, because it is not strictly correct usage and because Research Systems reserves the right to change the  IDL_SREF definition in a way that could cause the memory layout of the ALLTYPES union to change.

Creating Structures

The actual structure definition is accessed through the sdef field, which is a pointer to an opaque IDL structure definition. Although the implementation of structure definitions is not public information, they can be created using the IDL_MakeStruct() function from a structure name and a list of tags:

void *IDL_MakeStruct(char *name, IDL_STRUCT_TAG_DEF *tags)

name

The name of the structure definition, or NULL for anonymous structures.

tags

An array of IDL_STRUCT_TAG_DEF elements, one for each tag.

The result from this function can be passed to IDL_ImportArray() or IDL_ImportNamedArray(), as described on page 3-9.

IDL_STRUCT_TAG_DEF is defined as:

typedef struct {
  char *name;
  IDL_LONG *dims;

  void *type;
} IDL_STRUCT_TAG_DEF;

name

Null-terminated uppercase name of the tag.

dims

An array that contains information about the dimensions of the structure. The first element of this array is the number of dimensions. Following elements contain the size of each dimension.

type

Either a pointer to another structure definition, or a simple IDL type cast to void (e.g., (void *) IDL_TYP_BYTE).

The following example shows how to define an anonymous structure. Suppose that you want to create a structure whose definition in the IDL language is:

{TAG1: 0L, TAG2: FLTARR(2,3,4), TAG3: STRARR(10)}

It can be created with IDL_MakeStruct() as follows:

    static IDL_LONG one = 1;
    static IDL_LONG tag2_dims[] = { 3, 2, 3, 4};
    static IDL_LONG tag3_dims[] = { 1, 10 };
    static IDL_STRUCT_TAG_DEF s_tags[] = {
      { "TAG1", 0, (void *) IDL_TYP_LONG},
      { "TAG2", tag2_dims, (void *) IDL_TYP_FLOAT},
      { "TAG3", tag3_dims, (void *) IDL_TYP_STRING},
      { 0 },
    };

    typedef struct data_struct {
      IDL_LONG tag1_data;
      float tag2_data [2] [3] [4];
      IDL_STRING tag_3_data [10];
    ] DATA_STRUCT;

    static DATA_STRUCT s_data;
    void *s;
    IDL_VPTR v;

    /* Create the structure definition */
    s = IDL_MakeStruct(0, s_tags);
    /* Import the data area s_data into an IDL structure,
       note that no data are moved. */
v = IDL_ImportArray(1, &one, IDL_TYP_STRUCT,
                    (UCHAR *) &s_data, 0, s);


Temporary Variables

As discussed previously, IDL maintains a pool of nameless variables known as temporary variables. These variables are used by the interpreter to hold temporary results from evaluating expressions, and are also used within system procedures and functions that need temporary workspace. In addition, system functions often obtain a temporary variable to return the result of their operation to the interpreter. Temporary variables have the following characteristics:

•	All temporaries, when initially allocated, are of type IDL_TYP_UNDEF.

•	Temporary variables do not have a name associated with them.

•	Routines that check out temporaries must either check them back in or return them as the result of the function. Once you return a temporary variable, you cannot access it again.

•	Temporary variables are reclaimed by the interpreter when it is about to exit after executing a program, so it is not possible to lose them and leak dynamic memory by allocating them and failing to return them. If the interpreter is exiting normally and it detects temporaries that have not been returned, it issues an error message. Such an error message indicates an error in the implementation of your system routine. If the interpreter is exiting because an error was detected, allocated temporaries are expected, and are reclaimed quietly. Hence, your routines need only return temporaries on normal return, not before issuing errors. See Chapter 6, "Error Handling".

The interpreter uses temporary variables to hold values that are the result of evaluating expressions. Such temporaries are pushed on the interpreter stack where they are often passed as arguments to other routines. For example, the IDL statement:

PRINT, MAX(FINDGEN(100))

causes the interpreter to perform the following steps:

1.	Push a constant variable with the value 100 onto the stack.

2.	Call the system function FINDGEN, passing it one argument.

3.	FINDGEN returns a temporary variable which is a 100-element vector with each element set to the value of its index.

4.	The interpreter removes the arguments to FINDGEN from the stack (the constant 100) and pushes the resulting temporary variable onto the stack.

5.	The MAX system function is called with a single argument—the temporary result from FINDGEN.

6.	MAX finds the largest element in its argument (99), places that value into a temporary scalar variable, and returns that temporary variable as its result.

7.	The interpreter removes the argument to MAX from the stack. This was the temporary array from FINDGEN, so it is returned to the pool of temporary variables. The resulting temporary variable from MAX is then pushed onto the stack.

8.	The PRINT system procedure is called with a single argument, which is the temporary scalar variable from MAX. It prints the value of the variable and returns.

9.	The interpreter removes the argument to PRINT from the stack, and returns it to the pool of temporary variables.

Getting a Temporary Variable

Temporary variables are obtained via the IDL_Gettmp() function:

IDL_VPTR IDL_Gettmp(void);

IDL_Gettmp() requires no arguments, and returns an IDL_VPTR to a temporary variable. This variable must be returned to the pool of temporary variables before control returns to the interpreter or be returned as the value of a system function, or an error will occur.

Getting a Temporary Array

Temporary array variables can be obtained via the IDL_MakeTempArray() function:

char *IDL_MakeTempArray(int type, int n_dim, IDL_LONG dim[], int
                      init, IDL_VPTR *var)

where:

type

The type code for the resulting array. See "Type Codes" on page 2-1.

n_dim

The number of array dimensions. The constant IDL_MAX_ARRAY_DIM defines the upper limit of this value.

dim

A C array of IDL_MAX_ARRAY_DIM elements containing the array dimensions. The number of dimensions in the array is given by the n_dim argument.

init

Specifies the sort of initialization that should be applied to the resulting array. The init argument must be one of the following:

IDL_BARR_INI_NOP

No initialization is done. The data area of the array will contain whatever garbage was left behind from its previous use. Experience has shown that IDL_TYP_STRING data should never be left uninitialized due to the risk of dereferencing an invalid string pointer and crashing IDL. Therefore, IDL_TYP_STRING data is zeroed when IDL_BARR_INI_NOP is specified.

IDL_BARR_INI_ZERO

The data area of the array is zeroed.

IDL_BARR_INI_ZERO

Each element of the array is set to the value of its index. The INDGEN family of built-in system functions is implemented using this feature.

var

The address of an IDL_VPTR where the address of the resulting temporary variable will be put.

The data area of an array IDL_VARIABLE is accessible from its IDL_VPTR as var->value.arr->data. However, since most routines that create an array need to access the data area, IDL_MakeTempArray() returns the data area pointer as its value. As with IDL_Gettmp(), the variable allocated via IDL_MakeTempArray() must be returned to the pool of temporary variables or be returned as the value of a system function before control returns to the interpreter, or an error will occur.

Freeing A Temporary Variable

Use IDL_Deltmp() to free a temporary variable:

void IDL_Deltmp(IDL_VPTR p)

where p is an IDL_VPTR to the temporary variable to be returned. IDL_Deltmp() frees the dynamic parts of the temporary variable (if any) and then returns the variable to the pool of available temporaries. Once you have deallocated a temporary variable, you may not access it again. There is also a macro named  IDL_DELTMP which checks its argument to make sure it's a temporary, and if so, calls IDL_Deltmp() to return it.


Creating an Array from Existing Data

There are two functions that allow you to create an IDL array variable whose data points at data you supply rather than having IDL allocate the data space. The routine IDL_ImportArray() returns a temporary variable, while IDL_ImportNamedArray() returns a named variable in the current execution scope, creating the new variable if necessary. Your data must already exist in memory. The data area, which can be quite large, is not copied. These functions simply create variable and array descriptors that point to the data you supply and return the pointer to the resulting variable. Their definitions are:

IDL_VPTR IDL_ImportArray(int n_dim, IDL_LONG dim[], int type,         UCHAR *data, IDL_ARRAY_FREE_CB free_cb, void *s)

IDL_VPTR IDL_ImportNamedArray(char *name, int n_dim,
        IDL_LONG dim[], int type, UCHAR *data,
        IDL_ARRAY_FREE_CB free_cb, void *s)

where:

name

The name of the variable to be created or modified.

n_dim

The number of dimensions in the array.

dim

An array of IDL_MAX_ARRAY_DIM elements, containing the size of each dimension.

type

The IDL type code describing the data. See "Type Codes" on page 2-1.

data

A pointer to your array data. Your data will not be modified unless the user explicitly modifies elements of the array using subscripts.

The temporary variable returned by IDL_ImportArray() can be used immediately in an expression, in which case the descriptors are freed immediately. It can also be assigned to a longer-lived variable using IDL_VarCopy().

free_cb

If non-NULL, free_cb is a pointer to a function that will be called when IDL frees the array. This feature gives the caller a sure way to know when IDL is no longer referencing data. Use the called function to perform any required cleanup such as freeing dynamic memory or releasing shared or mapped memory.

s

If the type of the variable is IDL_TYP_STRUCT, s points to the blind structure definition, as returned by K_MakeStruct().


Getting Dynamic Memory

Many programs need to get dynamic memory for some temporary calculation. In the C language, the functions malloc() and free() are used for this purpose, while other languages have their own facilities. IDL provides its own memory allocation routines (see "Dynamic Memory" on page 11-1). Use of such facilities within the IDL interpreter and the system routines can lead to the loss of usable dynamic memory. The following code fragment demonstrates how this can happen. Assume that there is a need for 100 IDL_LONG integers:

char *c;

c = (char *) IDL_MemAlloc((unsigned) (sizeof(IDL_LONG) * 100)
                         (char *) 0, IDL_MSG_RET);
.
.
.
if (some_error_condition) IDL_Message( , IDL_MSG LONGJMP, );
.
.
.
IDL_MemFree((void *) c, (char *) 0, IDL_MSG_RET);

In the normal case, the allocated memory is returned exactly as it should be. However, if an error causes the IDL_Message() function to be called, execution will return directly to the interpreter and this code will never have a chance to clean up.

The IDL_GetScratch Function

To solve this problem, use a temporary variable to obtain dynamic memory. Then, if an error should cause execution to return to the interpreter, the interpreter will reclaim the temporary variable and no dynamic memory will be lost. This frequently-needed operation is provided by the IDL_GetScratch() function:

char *IDL_GetScratch(IDL_VPTR *p, IDL_LONG n_elts,
                     IDL_LONG elt_size)

where:

p

The address of an IDL_VPTR that should be set to the address of the temporary variable allocated.

n_elts

The number of elements for which memory should be allocated.

elt_size

The length of each element, in bytes.

Once the need for the temporary memory has passed, it should be returned using the IDL_Deltmp() function. Using these functions, the above example becomes:

char *c;
IDL_VPTR v;

c = IDL_GetScratch(&v, 100L, (IDL_LONG) sizeof(IDL_LONG));
.
.
.
if (some error condition) IDL_Message(:::,MSG LONGJMP,:::);
.
.
.
IDL_Deltmp(v);

Using the IDL_GetScratch() and IDL_Deltmp() functions is similar to using IDLMemAlloc() directly. In fact, IDL uses IDL_MemAlloc() and IDL_MemFree() internally to implement array variables. The important difference is that dynamic memory doesn't leak when error conditions occur.

To avoid losing dynamic memory, always use the IDL_GetScratch() function in preference to other ways of allocating dynamic memory, and use IDL_Deltmp() to return it.


Copying Variables

To copy the contents of one variable to another, use the IDL_VarCopy() function:

void IDL_VarCopy(IDL_VPTR src, IDL_VPTR dst)

Arguments src and dst are the source and destination, respectively.

IDL_VarCopy() uses the following rules when copying variables:

•	If the destination variable already has a dynamic part, this dynamic part is released.

•	The destination becomes a copy of the source.

•	If the source is a temporary variable, IDL_VarCopy() does not make a duplicate of the dynamic part for the destination. Instead, the dynamic part of the source is given to the destination, and the source variable itself is returned to the pool of free temporary variables. This is the equivalent of freeing the temporary variable. Therefore, the variable must not be used any further and the caller should not explicitly free the variable. This optimization significantly improves resource utilization and performance because this special case occurs frequently.


Storing Scalar Values

The IDL_StoreScalar() function sets an IDL_VARIABLE to a scalar value:

void IDL_StoreScalar(IDL_VPTR dest, int type,
                     IDL_ALLTYPES value)

where:

dest

An IDL_VPTR to the IDL_VARIABLE in which the scalar should be stored.

type

The type code for the scalar value. See "Type Codes" on page 2-1.

value

The address of the IDL_ALLTYPES union that contains the value to store.

If dest is a location that cannot be stored into (e.g., a temporary variable, constant, etc.), an error is issued and control returns to the interpreter. Otherwise, any dynamic part of dest is freed and value is stored into it.

Using IDL_StoreScalar() to Free Dynamic Resources

In addition to performing its primary function, IDL_StoreScalar() has two very useful side effects:

1.	Storing a scalar value in a variable causes IDL to free any dynamic memory currently used by that variable.

2.	IDL_StoreScalar() does the required error checking to make sure the variable allows a new value to be stored into it before performing the actual storage operation.

Often, a system routine accepts an input argument that will have a new value assigned to it before the routine returns to its caller, and the initial value of that argument is of no interest to the routine. Storing a scalar value into such an argument at the start of the routine will automatically check it for storability and free unnecessary dynamic memory. In one easy operation, the required error checking is done, and you've improved the dynamic memory behavior of the IDL system by minimizing dynamic memory fragmentation. For example:

static IDL_ALLTYPES zero;
.
.
.
IDL_StoreScalar(v, IDL_TYP_LONG, &zero);

Error handling is discussed further in Chapter 6, "Error Handling".


Obtaining the Name of a Variable

The IDL_VarName() function returns the name of a variable, constant, or expression given its address. If the item is a named variable, it must be in the currently active program unit:

char *IDL_VarName(IDL_VPTR v)


Looking Up Main Program Variables by Name

The IDL_GetVarAddr() function returns the address of a main program variable, given its name:

IDL_VPTR IDL_GetVarAddr(char *name)

name

Points to the null terminated name of the variable, which must be in upper case.

The return value is NULL if the variable does not exist, otherwise the pointer to the variable is returned.

Alternatively, IDL_GetVarAddr1() will enter a new variable into the symbol table of the main program if called with the parameter ienter set to TRUE, and the specified variable name does not already exist. Otherwise, its operation is the same as IDL_GetVarAddr(). Note that new variables cannot be created if a user procedure or function is active. IDL_GetVarAdd1() is called as shown below:

IDL_VPTR IDL_GetVarAddr1(char *name, int enter)

name

Points to the null-terminated name of the variable, which must be in upper case.

ienter

Set this parameter to TRUE to create the variable if it does not already exist.

If ienter is TRUE and the specified variable name does not already exist, it will be added to the symbol table of the main program. If ienter is FALSE, IDL_GetVarAddr1() is equivalent to IDL_GetVarAddr().

Note that new variables cannot be created if a user procedure or function is active.


Looking Up Variables in Current Scope by Name

The IDL_FindNamedVariable() function returns the address of a variable in the current execution scope given its name:

IDL_VPTR IDL_FindNamedVariable(char *name, int ienter)

name

Name of the variable to find.

ienter

Set this parameter to TRUE to create the variable if it does not already exist.

If the variable is found (or created if ienter is TRUE), its IDL_VPTR is returned. Otherwise, NULL is returned.

Note that even if ienter is TRUE, this routine can return NULL if creating the variable is not possible due to memory constraints.


Keyword Processing
4	Keyword Processing

Chapter Overview

Keyword arguments are an important IDL language feature. They allow a multitude of options to be specified to a routine in a straightforward, easily understood way. The price of this added power is that it is somewhat more complicated to write a routine that accepts keywords than one that doesn't. However, the additional effort is well worth it.


Topics in Keyword Processing Chapter
Keyword Processing

This chapter describes how to create routines that accept keyword arguments.

Topics Covered:

Creating Routines that Accept Keyword Arguments

The IDL_KW_PAR Structure

The IDL_KW_ARR_DESC Structure

Keyword Processing Options

Processing Keywords

Cleaning Up

Keyword Examples

Jump forward to String Processing

Jump backward to Variables


Creating Routines that Accept Keyword Arguments

As mentioned described in Chapter 12, "Adding System Routines", you must register your system routine before IDL will recognize it. When registering the routine, you indicate that it accepts keyword arguments by ORing the constant IDL_KW_ARGS into its minimum argument count in the IDL_SYSFUN_DEF struct passed to IDL_AddSystemRoutine(), or by setting the KEYWORDS keyword in a call to LINKIMAGE.

Routines defined in this way must be designed to handle keyword processing. A routine that does not allow keyword processing knows that its argc argument gives the number of positional arguments, and argv contains only those positional arguments. In contrast, a routine that accepts keywords receives an argc that gives the total number of positional and keyword arguments, and these arguments are delivered in argv mixed together.

The function IDL_KWGetParams() is used to process keywords and separate the positional and keyword arguments. It is passed an array of IDL_KW_PAR structures that give information about the allowed keywords and their attributes. Finally, the IDL_KWCleanup() function is used in certain circumstances to clean up.

More information about these routines and structures can be found in the following sections.


The IDL_KW_PAR Structure

The IDL_KW_PAR struct provides the basic specification for keyword processing. The IDL_KWGetParams() function is passed a null-terminated array of these structures. IDL_KW_PAR structures specify which keywords a routine accepts, the attributes required of them, and the kinds of processing that should be done to them. IDL_KW_PAR structures must be defined in lexical order according to the value of the keyword field. The definition of IDL_KW_PAR is:

typedef struct {
  char *keyword;
  UCHAR type;
  unsigned short mask;
  unsigned short flags;
  int *specified;
  char *value;
} IDL_KW_PAR;

where:

keyword

A pointer to a null-terminated string. This is the name of the keyword, and must be entirely upper case. The array of IDL_KW_PAR structures passed to IDL_KWGetParams() must be lexically sorted by the strings pointed to by this field. The final element in the array is signified by setting the keyword field to NULL ((char *) 0).

type

IDL_KWGetParams() automatically converts the keywords to a specified type. This field specifies the desired type code. For scalars, the only allowable types are IDL_TYP_LONG, IDL_TYP_FLOAT, IDL_TYP_DOUBLE, and IDL_TYP_STRING. Any type can be specified for arrays, or IDL_TYP_UNDEF (0) for no conversion.

mask

The enable mask. This field is ANDed with the mask argument to IDL_KWGetParams() and if the result is non-zero, the keyword is accepted. If the result is 0, the keyword is ignored. This ability allows you to share an array of IDL_KW_PAR structures between several routines, and enable or disable the keywords used by each one.

For example, the IDL graphics and plotting routines have a large number of keywords in common. In addition, each routine has a few keywords that are unique to it. Keywords are implemented using a single shared array of IDL_KW_PAR with appropriate values of the mask field. This technique dramatically reduces the amount of data that would otherwise be required by graphics keyword processing, and makes IDL easier to maintain.

flags

This field specifies special processing instructions. It is a bit mask made by ORing the following values:

IDL_KW_ARRAY

Set this bit to specify that the keyword must be an array. Otherwise, a scalar is required.

IDL_KW_OUT

Set this bit to indicate that the keyword specifies an output parameter, passed by reference. Expressions and constants are excluded. In other words, the routine is going to change the value of the keyword argument, as opposed to the more usual case of simply reading it. The address of the IDL_VARIABLE will be placed in the IDL_VPTR pointed to by the value field (discussed below). IDL_KW_OUT implies that no type checking or processing will be performed on the keyword—it is up to the routine to perform the same sort of type checking normally carried out for positional arguments.

A standard approach to find out if an IDL_KW_OUT parameter is present in a call to a system routine is to specify IDL_TYP_UNDEF (0) for the type field and IDL_KW_OUT | IDL_KW_ZERO for flags. The IDL_VPTR pointed to by the value field will either contain NULL, or a pointer to the IDL_VARIABLE.

IDL_KW_VIN

Set this bit to indicate that the keyword parameter is an input parameter passed by reference. The address of the IDL_VARIABLE or expression is stored in the value field as with IDL_KW_OUT. Expressions and/or constants are valid. If IDL_KW_VIN is specified, IDL_KWCleanup() must be called with a IDL_KW_MARK parameter before IDL_KWGetParams() is called. IDL_KWCleanup() must be called with a IDL_KW_CLEAN parameter before your routine exits to properly return temporary variables that may have been allocated by IDL_KWGetParams().

IDL_KW_ZERO

Set this bit to zero the C variable pointed to by the value field before parsing the keywords. This means that the object pointed to by value will always be zero unless it was specified by the user. Use this technique to create keywords that have Boolean (on or off) meanings.

IDL_KW_VALUE

If this bit is set and the specified keyword is present, the low 12 bits of this field (flags) will be bitwise ORed with the longword pointed to by the value field. Note that this implies the IDL_TYP_LONG type code, and is incompatible with the IDL_KW_ARRAY and IDL_KW_OUT flags.

specified

The address of a C int variable that will be set to TRUE (non-zero) or FALSE (0) based on whether the routine was called with the keyword present. This field should be set to NULL ((int *) 0) if this information is not needed.

value

If the keyword is a read-only scalar, this field is a pointer to a C variable of the correct type (IDL_LONG, float, double, or IDL_STRING).

In the case of a read-only array, value is a pointer to an IDL_KW_ARR_DESC, which is discussed in "The IDL_KW_ARR_DESC Structure" on page 4-4. In the case of an output variable (i.e., the IDL_KW_OUT flag is set), this field should point to an IDL_VPTR that will be filled by IDL_KWGetParams() with the address of the keyword argument.


The IDL_KW_ARR_DESC Structure

When a keyword is specified to be a read-only array (i.e., the IDL_KW_ARRAY flag is set), the value field of the IDL_KW_PAR struct should be set to point to an IDL_KW_ARR_DESC structure. This structure is defined as:

typedef struct {
  char *data;
  int nmin;
  int nmax;
  int n;
} IDL_KW_ARR_DESC;

where:

data

The address of a C array to receive the data. This array must be of the C type mapped into by the type field of the IDL_KW_PAR struct. For example, IDL_TYP_LONG maps into a C IDL_LONG. There must be nmax elements in the array.

nmin

The minimum number of elements allowed.

nmax

The maximum number of elements allowed.

n

The number of elements actually present. Unlike the other fields, this field is set by IDL_KWGetParams().


Keyword Processing Options

The following cases occur in keyword processing:

Scalar Input-Only

For scalar, input-only keywords, the user never sees the IDL_VARIABLE passed as the keyword argument. Instead, the value of the IDL_VARIABLE is convert ed to the type specified by the type field of the IDL_KW_PAR struct and is placed into the C variable specified by the value field.

Array Input-Only

Array input-only keywords work similarly to the scalar case, except that the value field points to an IDL_KW_ARR_DESC struct that supplies the added information required to convert the passed array elements to the specified type and place them into a C array for easy access. As part of this process, the number of array elements passed is checked to be within the range specified in the IDL_KW_ARR_DESC struct, and if no error results, the number is written into the n field of that struct.

It is worth noticing that input-only array keywords don't pass information about the number of dimensions or their sizes, only the total number of elements. Therefore, they are treated as 1-dimensional vectors. For more flexibility, use an Input/Output keyword instead.

Input/Output

This case occurs if the IDL_KW_OUT or IDL_KW_VIN flag is set in the IDL_KW_PAR struct. In this case, you receive the IDL_VPTR to the actual keyword argument, and you must do all error checking and type conversion yourself, just like with positional arguments. This is certainly the most flexible method. However, the other two cases are much easier to use, and are suitable for the vast majority of keywords.


Processing Keywords

The IDL_KWGetParams() function is used to process keywords. IDL_KWGetParams() performs the following actions on behalf of the calling system routine:

•	Verify that the keywords passed to the routine are all allowed by the routine.

•	Carry out the type checking and conversions required for each keyword.

•	Find the positional (non-keyword) arguments that are scattered among the keyword arguments in argv and copy them in order into the plain_args array.

•	Return the number of plain arguments copied into plain_args.

IDL_KWGetParams() has the form:

int IDL_KWGetParams(int argc, IDL_VPTR *argv,char *argk,
        IDL_KW_PAR *kw_list, IDL_VPTR plain_args[], int mask)

where:

argc

The number of arguments passed to the caller. This is the first parameter to all system routines.

argv

The array of IDL_VPTR to arguments that was passed to the caller. This is the second parameter to all system routines.

argk

The pointer to the keyword list that was passed to the caller. This is the third parameter to all system routines that accept keyword arguments.

kw_list

An array of IDL_KW_PAR structures (see "The IDL_KW_PAR Structure" on page 4-2) that specifies the acceptable keywords for this routine. This array is terminated by setting the keyword field of the final struct to NULL ((char *) 0).

plain_args

An array of IDL_VPTR into which the IDL_VPTRs of the positional arguments will be copied. This array must have enough elements to hold the maximum possible number of positional arguments, as defined in IDL_SYSFUN_DEF. See "Registering Routines using IDL_AddSystemRoutine()" on page 12-17.

mask

Mask enable. This variable is ANDed with the mask field of each IDL_KW_PAR struct in the array given by kw_list. If the result is non-zero, the keyword is accepted as a valid keyword for the called system routine. If the result is zero, the keyword is ignored.

Speeding Keyword Processing

As mentioned above, the kw_list argument to IDL_KWGetParams() is a null terminated list of IDL_KW_PAR structures. The time required to scan each item of the keyword array and zero the required fields (those fields specified, and value fields with IDL_KW_ZERO set), can become significant, especially when more than a few keyword array elements (e.g., 5 to 10 elements) are present.

To speed things up, specify IDL_KW_FAST_SCAN as the first keyword array element. If IDL_KW_FAST_SCAN is the first keyword array element, the keyword array is compiled by IDL_KWGetParams() into a more efficient form the first time it is used. Subsequent calls use this efficient version, greatly speeding keyword processing. Usage of IDL_KW_FAST_SCAN is optional, and is not worthwhile for small lists. For longer lists, however, the improvement in speed is noticeable. For example, the following list does not use fast scanning:

static IDL_KW_PAR  kw_pars[] = {
  { "DOUBLE", IDL_TYP_DOUBLE, 1, 0, &d_there, CHARA(d) },
  { "FLOAT", IDL_TYP_FLOAT, 1, IDL_KW_ZERO, 0, CHARA(f) },
  { NULL }
};

To use fast scanning, it would be written as:

static IDL_KW_PAR  kw_pars[] = {
  IDL_KW_FAST_SCAN,
  { "DOUBLE", IDL_TYP_DOUBLE, 1, 0, &d_there, CHARA(d) },
  {"FLOAT", IDL_TYP_FLOAT, 1, IDL_KW_ZERO, 0, CHARA(f) },
  { NULL }
};


Cleaning Up

The IDL_KWCleanup() function is necessary if the keywords allowed by a system routine include any input-only keywords of type IDL_TYP_STRING, or if the IDL_KW_VIN flag is used by any of the keyword IDL_KW_PAR structures. Such keywords can cause keyword processing to allocate temporary variables that must be cleaned up after they've outlived their usefulness. Call IDL_KWCleanup() as follows:

void IDL_KWCleanup(int fcn)

where fcn specifies the operation to be performed, and must be one of the following values:

IDL_KW_MARK

Mark the stack by placing the statement:

IDL_KWCleanup(IDL_KW_MARK);

above the call to IDL_KWGetParams(). In addition, you will need to make a call with IDL_KW_CLEAN at the end.

IDL_KW_CLEAN

Clean up from the last call to IDL_KWGetParams() by placing the line:

IDL_KWCleanup(IDL_KW_CLEAN);

just above the return statement.


Keyword Examples

The following C function implements KEYWORD_DEMO, a system procedure intended to demonstrate how to write the keyword processing code for a routine. It prints the values of its keywords, changes the value of READWRITE to 42 if it is present, and returns. Each line is numbered to make discussion easier. These numbers are not part of the actual program.

1  void keyword_demo(int argc, IDL_VPTR *argv, char *argk)
2  {
3    int i;
4    IDL_ALLTYPES newval;
5
6    static int d_there, s_there, arr_there;
7    static IDL_LONG l;
8    static float f;
9    static double d;
10   static IDL_STRING s;
11   static IDL_LONG arr_data[10];
12   static KW_ARRAY_DESC arr_d = {(char *) arr_data,3,10,0};
13   static IDL_VPTR var;
14
15   static KW_PAR kw_pars[] = { KW_FAST_SCAN,
16     { "ARRAY", TYP_LONG, 1, KW_ARRAY, &arr_there,
17       CHARA(arr_d) },
18     { "DOUBLE", TYP_DOUBLE, 1, 0, &d_there, CHARA(d) },
19     { "FLOAT", TYP_FLOAT, 1, KW_ZERO, 0, CHARA(f) },
20     { "LONG", TYP_LONG, 1, KW_ZERO--KW_VALUE--15, 0,
21       CHARA(l) },
22     { "READWRITE", TYP_UNDEF, 1, KW_OUT--KW_ZERO,
23       0, CHARA(var) },
24     { "STRING",TYP_STRING, 1, 0, &s_there, CHARA(s) },
25     { NULL }
26   };
27
28   (void) IDL_KWGetParams(argc, argv, argk, kw_pars, NULL, 1);
29
30   printf("LONG: <%spresent>\n", l ? "": "not ");
31   printf("FLOAT: %f\n", f);
32   printf("DOUBLE: <%spresent>\n", d_there ? "": "not ");
33   printf("STRING: %s\n", s_there ?
34          IDL_STRING_STR(&s) : "<not present>");
35   printf("ARRAY: ");
36   if (arr_there)
37     for (i = 0; i < arr_d.n; i++)
38       printf(" %ld", arr_data[i]);
39   else
40     printf("<not present>");
41   printf("\n");
42
43   printf("READWRITE: ");
44   if (var) {
45     IDL_Print(1, &var, (char *) 0);
46     newval.l = 42;
47     store_scalar(var, TYP_LONG, &newval);
48   } else {
49     printf("<not present>");
50   }
51   printf("\n");
52
53    IDL_KWCleanup(KW_CLEAN_ALL);
54 }

Executing this routine from the IDL command line:

KEYWORD_DEMO

gives the output:

LONG: <not present>
FLOAT: 0.000000
DOUBLE: <not present>
STRING: <not present>
ARRAY: <not present>
READWRITE: <not present>

Executing it again with keywords specified:

A = 56
KEYWORD_DEMO, /LONG, FLOAT=2, DOUBLE=34,$
  STRING="hello", ARRAY=FINDGEN(10), READWRITE=A
PRINT, 'Final Value of A: ', A

gives the output:

LONG: <present>
FLOAT: 2.000000
DOUBLE: <present>
STRING: hello
ARRAY: 0 1 2 3 4 5 6 7 8 9
READWRITE:      56
Final Value of A:           42

Those features of this procedure that are interesting in terms of keyword processing are, by line number:

4

The IDL_StoreScalar() function used on line 50 requires the scalar to be provided in an  IDL_ALLTYPES struct.

6

These variables are used to determine if a given keyword is present. Note that all the keyword-related variables are declared static. This is necessary so that the C compiler can build the IDL_KW_PAR structure at compile time.

7­10

C variables to receive the scalar read-only keyword values.

11

C array to be used for the ARRAY read-only array keyword.

12

The array descriptor used for ARRAY. arr_data is the address where the array contents should be copied. The minimum number of elements allowed is 3, the maximum is 10. The value set in the last field (0) is not important, because the keyword processing routine never reads its value. Instead, it puts the number of elements actually seen there.

13

The READWRITE keyword uses the IDL_KW_OUT flag, so the routine receives an IDL_VPTR instead of a processed value.

15

The keyword definition array. Notice that all of the keywords are ordered lexically (ASCII) and that there is a NULL entry at the end (line 37). Also, all of the mask fields are set to 1, as is the mask argument to IDL_KWGetParams() on line 40. This means that all of the keywords in the list are to be considered valid in this routine.

The IDL_KW_FAST_SCAN macro is used to define the first keyword array element, speeding the processing of long keyword arrays.

16 ­ 17

ARRAY is defined to be a read-only array keyword of IDL_TYP_LONG. The arr_there variable will be set to non-zero if the keyword is present. In that case, the array contents will be placed in the variable arr_data and the number of elements will be placed into arr_d.n.

18

DOUBLE is a scalar keyword of IDL_TYP_DOUBLE. It uses the variable d_there to know if the keyword is present.

19

FLOAT is an IDL_TYP_FLOAT scalar keyword. It does not use the specified field of the IDL_KW_PAR struct to get notification of whether the keyword is present. Instead, it uses the IDL_KW_ZERO flag to make sure that the variable f is always zeroed. If the keyword is present, the value will be written into f, otherwise it will remain 0. The important point is that the routine can't tell the difference between the keyword being absent, or being present with a user-supplied value of zero. If this distinction doesn't matter, such as when the keyword is to serve as an on/off toggle, use this method. If it does matter, use the specified field as demonstrated with the DOUBLE keyword, above.

20 ­ 21

LONG is a scalar keyword of IDL_TYP_LONG. It sets the IDL_KW_ZERO flag to get the variable l zeroed prior to keyword parsing. The use of the IDL_KW_VALUE flag indicates that if the keyword is present, the value 15 (the lower 12 bits of the flags field) will be ORed into the variable l.

22 ­ 23

The IDL_KW_OUT flag indicates that the routine wants gets the IDL_VPTR for READWRITE if it is present. Since IDL_KW_ZERO is also set, the variable var will be zero unless the keyword is present. The specification of IDL_TYP_UNDEF here indicates that there is no type conversion or processing applied to IDL_KW_OUT keywords.

24

This keyword is included here to force the need for IDL_KWCleanup() on line 52.

24

Every array of IDL_KW_PAR structs has to end with a NULL entry, or undesirable things will happen.

28

Do the keyword processing. The first three arguments are simply the arguments the interpreter passed to the routine. The plain_args argument is set to NULL because this routine is registered as not accepting any plain arguments. Since no plain arguments will be present, the return value from IDL_KWGetParams() is discarded.

30

The l variable will be 0 if LONG is not present, and 15 if it is.

31

The f variable will always have some usable value, but if it's zero there is no way to know if the keyword was actually specified or not.

32 ­ 34

These keywords use the variables from the specified field of their  IDL_KW_PAR struct to determine if they were specified or not. Use of the IDL_STRING_STR macro is described in "Accessing IDL_STRING Values" on page 5-1.

35 ­4 1

Accessing the ARRAY keyword is simple. The arr_there variable indicates if the keyword is present, and arr_d.n gives the number of elements.

43 ­ 51

Since the READWRITE keyword is accessed via the argument's IDL_VPTR, we use the IDL_Print() function to print its value. This has the same effect as using the user-level PRINT procedure when running IDL. See "Output of IDL Variables" on page 8-12. Then, we change its value to 42 using IDL_StoreScalar().

53

The use of IDL_KWCleanup() is necessitated by the existence of the STRING keyword, which is of IDL_TYP_STRING.


String Processing
5	String Processing

Chapter Overview

A number of functions exist to simplify the processing of IDL_STRING descriptors. By using these functions instead of doing your own string management, you can eliminate a common source of errors.


Topics in String Processing Chapter
String Processing

This chapter describes functions that simplify string processing.

Topics Covered:

Accessing IDL_STRING Values

Copying Strings

Deleting Strings

Setting an IDL_STRING Value

Obtaining an IDL_STRING of a Given Length

Jump forward to Error Handling

Jump backward to Keyword Processing


Accessing IDL_STRING Values

It is important to realize that the s field of an IDL_STRING struct does not contain a valid string pointer in the case of a null string (i.e., when slen is zero). A common error that can cause IDL to crash is illustrated by the following code fragment:

void print_str(IDL_STRING *s)
{
  printf("%s", s->s);
}

The problem with this code is that it fails to consider the case where the argument s describes a null string. The proper way to write this code is as follows:

void print str(IDL_STRING *s)
{
  printf("%s", IDL_STRING_STR(s));
}

The macro IDL_STRING_STR takes as its argument a pointer to an IDL_STRING struct. If the string is null, it returns a pointer to a zero length null-terminated string, otherwise it returns the string pointer from the struct. Consistent use of this macro will avoid the most common sort of error involving strings.


Copying Strings

It is often necessary to copy one string to another. Assume, for example, that there are two string descriptors s_src and s_dst, and that s_dst contains garbage. It would almost suffice to simply copy the contents of s_src into s_dst. The reason this is not quite correct is that both descriptors would then contain a pointer to the same string. This aliasing can cause some strange effects, or even cause IDL to crash if one of the two descriptors is freed and the string from the other is accessed.

IDL_StrDup() takes care of this problem by allocating memory for a second copy of the string, and replacing the string pointer in the descriptor with a pointer to the fresh copy. Naturally, if the string descriptor is for a null string, nothing is done.

void IDL_StrDup(IDL_STRING *str, LONG n)

where:

str

Pointer to one or more IDL_STRING descriptors which need their strings duplicated.

n

The number of descriptors.

The proper way to copy a string is:

s_dst = s_src;            /* Copy the descriptor */
IDL_StrDup(&s_dst, 1L);   /* Duplicate the string */


Deleting Strings

Before an IDL_STRING can be discarded or re-used, it is important to release any dynamic memory it might be using. The IDL_StrDelete() function should be used to delete strings:

void IDL_StrDelete(IDL_STRING *str, LONG n)

where:

str

Pointer to one or more IDL_STRING descriptors which need their contents freed.

n

The number of descriptors.

IDL_StrDelete() deletes all dynamic memory used by the IDL_STRINGs. The descriptors contain garbage once this has been done, and their contents should not be used.

The IDL_Deltmp() function automatically calls IDL_StrDelete() when returning temporary variables of type IDL_TYP_STRING, so it is not necessary or desirable to call IDL_StrDelete() explicitly in this case.


Setting an IDL_STRING Value

The IDL_StrStore() function should be used to store a null-terminated C string into an IDL_STRING descriptor:

void IDL_StrStore(IDL_STRING *s, char *fs)

where:

s

Pointer to an IDL_STRING descriptor. This descriptor is assumed to contain garbage, so call  IDL_StrDelete() on it first if this is not the case.

fs

Pointer to the null-terminated string to be copied into s.

IDL_StrStore() is useful for placing a string value into an IDL_STRING. This IDL_STRING does not need to be a component of a VARIABLE, which makes this function very flexible.

One often needs a temporary, scalar VARIABLE of type IDL_TYP_STRING with a given value. The function IDL_StrToSTRING() fills this need:

VPTR IDL_StrToSTRING(char *s)

where:

s

Pointer to the null-terminated string to be copied into the resulting temporary variable.


Obtaining an IDL_STRING of a Given Length

Sometimes you need to make sure that the string in an IDL_STRING descriptor has a specific length. The IDL_StrEnsureLength() function can be used in this case:

void IDL_StrEnsureLength(IDL_STRING *s, int n)

where:

s

A pointer to the IDL_STRING that will have its length checked.

n

The number of characters the string must be able to contain, not including the terminating null character.

If the IDL_STRING passed already has enough room for the specified number of characters, it is not re-allocated. Otherwise, the existing string is freed and a new string of sufficient length is allocated. In either case, the slen field of the IDL_STRING will be set to the requested length.

If a new dynamic string is allocated, it will contain garbage because IDL_StrEnsureLength() only allocates memory of the specified size, it does not copy a value into it. Therefore, the calling routine must copy a null-terminated string into the new dynamic string.


Error Handling
6	Error Handling

Chapter Overview

An interactive environment such as IDL requires a large amount of error checking to ensure the integrity of the system and provide the user with proper feedback.


Topics in Error Handling Chapter
Error Handling

This chapter describes error handling in IDL.

Topics Covered:

Issuing Error Messages

Specifying errno Explicitly

Issuing VMS Messages

Checking Arguments

Jump forward to Type Conversion

Jump backward to String Processing


Issuing Error Messages

Errors are reported using the IDL_Message() function. IDL_Message() is patterned after the standard C library printf() function.

void IDL_Message(int code, int action,  )

The arguments to IDL_Message() are as follows:

code

This is the error code associated with the error message to be issued. There are two error codes that are available for to programmers adding system routines to IDL. The use of these codes is described below. See "IDL_M_GENERIC" on page 6-3 and "IDL_M_NAMED_GENERIC" on page 6-4.

action

IDL_Message() can take a number of different actions after issuing the error message. The action to take is specified by the action argument:

IDL_MSG_RET

Use this argument to make IDL_Message() return to the caller after issuing the error message. In this case, the calling routine can either continue or return to the interpreter as it sees fit.

IDL_MSG_INFO

Use this argument to issue a message that is not an error, but is simply informational in nature. The message is output and IDL_Message() returns to the caller. Normally, IDL_Message() sets the values of IDL's !ERR, !ERROR, and !ERR_STRING system variables, but not in this case.

IDL_MSG_EXIT

Use this argument to cause the IDL process to exit after the message is issued. This code should never be used in a system function or procedure—it is intended for use in other sections of the system.

IDL_MSG_LONGJMP

Use this argument to cause IDL_Message() to exit directly back to the interpreter after issuing the message. In this case, IDL_Message() does not return to its caller. It is an error to use this action code in code not called by the IDL interpreter since the resulting call to longjmp() will be invalid.

IDL_MSG_IO_LONGJMP

This action code is exactly like IDL_MSG_LONGJMP, except that it is issued in response to an input/output error. This code is only used by the I/O module. User written system routines should use the existing I/O routines, so they do not need to use this action.

In addition, the following modifier codes can be ORed into the action code. They modify the normal behavior of IDL_Message():

IDL_MSG_ATTR_NOPRINT

Suppress the printing of the error message, but do everything else in the normal way.

IDL_MSG_ATTR_MORE

Use paging in the style of the Unix more command to display the output. This option exists primarily for use by the IDL compiler, and is unlikely to be of interest to authors of system routines.

IDL_MSG_ATTR_NOPREFIX

Normally, IDL_Message() prefixes the output message with the string contained in IDL's !MSG_PREFIX system variable. IDL_MSG_ATTR_NOPREFIX suppresses this prefix string.

IDL_MSG_ATTR_QUIET

If the IDL_MSG_INFO action has been specified and this bit mask has been included, and the IDL user has IDL's !QUIET system variable, IDL_Message() returns without issuing a message.

IDL_MSG_ATTR_NOTRACE

Set this code to inhibit the traceback portion of the error message.

IDL_MSG_ATTR_BELL

Set this code to ring the bell when the message is output.

IDL_MSG_ATTR_SYS

IDL_Message() always issues a single-line error message that describes the problem from IDL's point of view. Often, however, there is an underlying system reason for the error that should also be displayed to give the user a complete picture of what went wrong. For example, the IDL view of the problem might be "Unable to open file", while the underlying system reason for the error is "no such directory".

The Unix system provides a global variable named errno for communicating such system level errors. The VMS Standard C Library (stdio) also provides this variable. Whenever a call to a system function fails, it returns a 1, and puts an error code into errno that specifies the reason for the failure. Other functions, such as those provided by the standard C library, do not set errno. Note that the VMS stdio contains emulations of the Unix system calls in addition to the functions normally found in the Unix stdio. These functions do set errno.

Specifying IDL_MSG_ATTR_SYS tells IDL_Message() to check errno, and if it is non-null, to issue a second line containing the text of the system error message.

Specify IDL_MSG_ATTR_SYS only if you are calling IDL_Message() as the result of a failed Unix system call. Under VMS, this applies to those functions that emulate the Unix system calls. Otherwise, errno might contain an unrelated garbage value resulting in an incorrect error message.

The Macintosh and Microsoft Windows operating systems have errno for compatibility with the expectations of C programmers, but typically do not set it. On these operating systems, it is possible to specify IDL_MSG_ATTR_SYS, but it has no effect.

The message format string (specified by the code argument) specifies a format string to be used for the error message. This format string is exactly like those used by the standard C library printf() function. Any arguments following action are taken to be arguments for this format string.

As mentioned above, Research Systems has reserved two error codes for use by writers of system routines. They are:

IDL_M_GENERIC

This message code simply specifies a format string of "%s". The first argument after action is taken to be a null-terminated string that is substituted into the format string. For example, the C statement:

IDL_Message(IDL_M_GENERIC, IDL_MSG_LONGJMP, "Error! Help!")

causes IDL to abort the current routine and issue the message:

% Error! Help!

IDL_M_NAMED_GENERIC

This message code is exactly like the one above, except that it prints the name of the system routine in front of the error string. For example, assuming that the name of the routine is MY_PROC, the C statement:

IDL_Message(IDL_M_NAMED_GENERIC, IDL_MSG_LONGJMP,
            "Error! Help!")

causes IDL to interrupt the current routine and issue the message:

% MY PROC: Error! Help!

The choice of which code to use depends on the context in which the message is issued, but IDL_M_NAMED_GENERIC is usually preferred.

If you wish to include arguments into your message string, you should use the sprintf() function from the C standard library to format a string into a temporary buffer, and then supply the buffer as the argument to IDL_Message(). For example, executing the code:

char buf[128];
int unit = 23;

sprintf(buf, "Help! Error number %d.", unit);
IDL_Message(IDL_M_GENERIC, IDL_MSG_LONGJMP, buf);

interrupts the current routine and issues the message:

% Help! Error number 23.


Specifying errno Explicitly

There are times when specifying the IDL_MSG_ATTR_SYS modifier code in the action argument to IDL_Message() is inadequate. This situation usually occurs when your code attempts to perform some cleanup operation when an operating system call fails before calling IDL_Message() and this cleanup code might alter the value of errno. In such cases, it is preferable to use the IDL_MessageErrno() function to issue the message:

void IDL_MessageErrno(int code, int errno, int action,  )

This function differs from IDL_Message() in two ways:

1.	There is an additional argument used to specify the value of errno. See the discussion of errno in "IDL_MSG_ATTR_SYS" on page 6-3 for additional information about errno and its use.

2.	The IDL_MSG_ATTR_SYS modifier code for the action argument is ignored.


Issuing VMS Messages

The IDL_Message() function is used when issuing general errors, or issuing Unix-specific errors. This includes those errors reported by standard C library functions under any operating system, as such libraries generally emulate the Unix functionality reasonably well. However, IDL_Message() is not adequate for reporting errors that come from VMS system routines (System Services and Run-Time Library). Therefore, VMS-specific errors are reported using the IDL_MessageVMS() function.

The IDL_MessageVMS() function is only available under VMS, and should only be used in code that can't work under other operating systems due to system dependencies. In cases where either would work, always use IDL_Message().

IDL_MessageVMS() is very similar to IDL_Message():

void IDL_MessageVMS(int code, int err1, int err2, int action,  )

The arguments are identical to those for IDL_Message() (see "Issuing Error Messages" on page 6-1) with the following exceptions:

err1

IDL_MessageVMS() always issues a single line error message that describes the problem from the IDL point of view. The err1 argument is used to specify that a second line containing a system error message should also be issued, allowing the user to get a complete picture of what went wrong. If this argument is 0, no system error is issued. Otherwise, it should be set to the VMS status code for the failed operation.

err2

Some VMS system routines return 2 error codes. For example, RMS often does this. If there are 2 error codes, err2 should be used to report the second one. If this argument is 0, no second system error is issued.


Checking Arguments

IDL allows a user to provide any number of arguments, of any type, to system functions and procedures. IDL checks for a valid number of arguments, but the routine itself must check the validity of types. This task consists of examining the argv argument to the routine checking the type and flags field of each argument for suitability. The IDL_StoreScalar() function (see "Storing Scalar Values" on page 3-12) can be very useful in checking write-only arguments.

A number of macros exist in order to simplify testing of variable attributes. All of these macros accept a single argument—the VPTR to the argument in question. The macros check for a desired condition and use the IDL_Message() function with the IDL_MSG_LONGJMP action to return to the interpreter if an argument type doesn't agree. Some of these macros overlap, and some are contradictory. You should select the smallest set that covers your requirements for each argument. For an example that uses one of these macros, see "A Complete Example" on page 12-10.

IDL_EXCLUDE_UNDEF

The argument must not be of type IDL_TYP_UNDEF. This condition is usually imposed if the argument is intended to provide some input information to the routine.

IDL_EXCLUDE_CONST

The argument must not be a constant. This condition should be specified if your routine intends to change the value of the argument.

IDL_EXCLUDE_EXPR

The argument must not be a constant or a temporary variable (i.e., the argument must be a named variable). Specify this condition if you intend to return a value in the argument. Returning a value in a temporary variable is pointless because the interpreter will remove it from the stack as soon as the routine completes, causing it to be freed for re-use.

The IDL_VarCopy() and IDL_StoreScalar() functions automatically check their destination and issue an error if it is an expression. Therefore, if you are using one of these functions to write the new value into the argument variable, you do not need to perform this check first.

IDL_EXCLUDE_FILE

The argument cannot be a file variable (as returned by the IDL ASSOC) function. Most system routines exclude file variables—they are handled by a small set of existing routines. This check is also handled by the IDL_ENSURE_SIMPLE macro, which also excludes structure variables.

IDL_EXCLUDE_STRUCT

The argument cannot be a structure.

IDL_EXCLUDE_COMPLEX

The argument cannot be IDL_TYP_COMPLEX.

IDL_EXCLUDE_STRING

The argument cannot be IDL_TYP_STRING.

IDL_EXCLUDE_SCALAR

The argument cannot be a scalar.

IDL_ENSURE_ARRAY

The argument must be an array.

IDL_ENSURE_SCALAR

The argument must be a scalar.

IDL_ENSURE_STRING

The argument must be IDL_TYP_STRING.

IDL_ENSURE_SIMPLE

The argument cannot be a file variable or a structure variable.

IDL_ENSURE_STRUCTURE

The argument must be IDL_TYP_STRUCT.


Type Conversion
7	Type Conversion

Chapter Overview

One of the more frequent operations performed by a system routine is type conversion. In general, if an argument is not of the correct type, an attempt to convert it should be made, rather than simply issuing an error. The functions documented in this chapter are used to carry out such conversions.


Topics in Type Conversion Chapter
Type Conversion

This chapter describes IDL's internal type conversion functions.

Topics Covered:

Converting Arguments to C Scalars

General Type Conversion

Converting Between Specific Types

Jump forward to Files and Input/Output

Jump backward to Error Handling


Converting Arguments to C Scalars

The IDL_LongScalar() and IDL_DoubleScalar() functions convert the value of their argument to a C scalar. The converted value is returned as the function value. The functions are defined as:

LONG IDL_LongScalar(IDL_VPTR p)

double IDL_DoubleScalar(IDL_VPTR p)

If these functions are unable to perform the conversion (e.g., the argument is a file variable, an array, etc.), they issue a descriptive error and jump back to the interpreter. By using these functions, you avoid having to do any of the type checking described in "Checking Arguments" on page 6-5. IDL_DoubleScalar() works exactly like IDL_LongScalar() except that it returns a double-precision, floating-point value.

For example, the following IDL system function (named PRINT_LONG) prints the value of its first argument, converted to long integer:

IDL_VPTR print_long(int argc, IDL_VPTR argv[], char *argk)
{
  printf("%d\n", IDL_LongScalar(argv[0]));
}

Executing it as:

PRINT LONG, 23D

gives the output:

23

as expected, while the statement:

PRINT LONG, FINDGEN(10)

causes the error:

% PRINT LONG: Expression must be a scalar in this context:
              <FLOAT Array(10)>
% Execution halted at $MAIN$ .

because it is not possible to convert an array (the result of FINDGEN) to a scalar.


General Type Conversion

The IDL_BasicTypeConversion() function provides general purpose type conversion:

IDL_VPTR IDL_BasicTypeConversion(int argc, IDL_VPTR argv
                                 int type)

where:

argc

The number of IDL_VPTRs contained in argv.

argv

An array of pointers to VARIABLE arguments.

type

The desired type code of the result. See "Type Codes" on page 2-1.

If argc is 1, this function returns a pointer to a temporary VARIABLE containing the value of argv[0] converted to the type specified by the type argument. If the variable is already of the correct type, the variable itself is returned.

If argv is greater than 1, argv[1] is taken to be an offset into the variable specified by argv[0], and following arguments are taken as the dimensions to be used for the result. In this case, enough bytes are copied (starting from the offset) to satisfy the requirements of the dimensions given. This second form does not work for variables of type string, so an error is issued in that case.

The IDL BYTE and STRING system routines (implemented by the IDL_CvtByte() and IDL_CvtString() functions, described below) treat conversions between variables of type byte and string in a special way. IDL_BasicTypeConversion() does not handle this special case. Instead, it simply performs a straightforward type conversion between those types.


Converting Between Specific Types

A series of functions exist to convert VARIABLEs to specific types:

IDL_VPTR IDL_CvtByte(int argc, IDL_VPTR argv[])

IDL_VPTR IDL_CvtBytscl(int argc, IDL_VPTR argv[], char *argk)

IDL_VPTR IDL_CvtFix(int argc, IDL_VPTR argv[])

IDL_VPTR IDL_CvtLng(int argc, IDL_VPTR argv[])

IDL_VPTR IDL_CvtFlt(int argc, IDL_VPTR argv[])

IDL_VPTR IDL_CvtDbl(int argc, IDL_VPTR argv[])

IDL_VPTR IDL_CvtComplex(int argc, IDL_VPTR argv[])

IDL_VPTR IDL_CvtDComplex(int argc, IDL_VPTR argv[])

IDL_VPTR IDL_CvtString(int argc, IDL_VPTR argv[], char *argk)

When calling these functions, you should set the argk argument to NULL.

These functions are the direct implementations of the IDL commands BYTE, BYTSCL, FIX, LONG, FLOAT, DOUBLE, COMPLEX, DCOMPLEX, and STRING. See the description of these functions in the IDL Reference Guide for details on their arguments and calling sequences.

The behavior of these functions is the same as IDL_BasicTypeConversion() except when converting between bytes and strings. Calling IDL_CvtByte() with a single argument of string type causes each string to be converted to a byte vector of the same length as the string. Each array element is the character code of the corresponding character in the string. Calling IDL_CvtString() with a single argument of TYP BYTE has the opposite effect.


Files and Input/Output
8	Files and Input/Output

Chapter Overview

On most platforms supported by IDL, file handling is built on the standard C library stream package. For VMS this is true only in the case of stream files—RMS is used in all other cases.

Most system routines should not do Input/Output directly. It is almost always better to write a function or procedure that returns a result, which a user can then print in any format supported by the IDL I/O subsystem. For this reason, only minimal I/O abilities are available. Most stream file abilities are present, but access to RMS files is only marginally supported. One notable exception to this is if you are writing a graphics driver (see "Writing A Graphics Driver" on page 17-115).

If your application must perform I/O, it is best to use stream files. Using stream files gives your application the best chance of working with all operating systems supported by IDL. Most of the routines associated with the standard C library I/O package can be used in the normal manner. Note, however, that the C library routines listed in Table 8.1 should not be used; use the IDL-specific functions instead.

C Library Function	IDL Function
fclose()	IDL_FileClose()
fdopen()	IDL_FileOpen()
feof()	IDL_FileEOF()
fflush()	IDL_FileFlushUnit()
fopen()	IDL_FileOpen()
freopen()	IDL_FileOpen()
Table 8.1 Disallowed C Library Routines and their IDL counterparts


Topics in Files and Input/Output Chapter
Files and Input/Output

This chapter describes IDL's internal file I/O functions.

Topics Covered:

File Information

Opening Files

Closing Files

Preventing File Closing

Checking File Status

Allocating and Freeing File Units

Detecting End of File

Flushing Buffered Data

Output of IDL Variables

Reading a Single Character

Adding to the Journal File

Jump forward to Signals

Jump backward to Type Conversion


File Information

IDL maintains a file table in which it keeps a file descriptor for each file opened with IDL_FileOpen(). This table is indexed by the file Logical Unit Number, or LUN. These are the same LUNs IDL users use.

The IDL_FileStat() function is used to get information about a file:

void IDL_FileStat(int unit, IDL_FILE_STAT *stat_blk)

where:

unit

The Logical Unit Number of the file unit to be checked. This function should only be called on file units that are known to be open.

stat_blk

A pointer to an IDL_FILE_STAT struct to be filled in with information about the file. The information returned is valid only as long as the file remains open. You must not access the fields of an IDL_FILE_STAT once the file it refers to has been closed. This struct has the definition:

typedef struct {
  char *name;
  short access;
  LONG flags;
  FILE *fptr;
  struct {
    unsigned short mrs;
  } rms;
} IDL_FILE_STAT;

where:

name

A pointer to a null-terminated string containing the name the file was opened with.

access

A bit mask describing the access allowed to the file. The allowed bit values are:

IDL_OPEN_R

The file is open for input.

IDL_OPEN_W

The file is open for output.

IDL_OPEN_TRUNC

The file was truncated when it was opened. This implies that IDL_OPEN_W is also set.

IDL_OPEN_APND

The file was opened with the file pointer set just past the last byte of data in the file (the file is open for appending).

flags

A bit mask that gives special information about the file. The defined bits are:

IDL_F_ISATTY

The file is a terminal.

IDL_F_ISAGUI

The file is a Graphical User Interface.

IDL_F_NOCLOSE

The CLOSE command will refuse to close the file.

IDL_F_MORE

If the file is a terminal, output is sent through a pager similar to the Unix more command. Details on this pager are not included in this document, and it is therefore not available for general use.

IDL_F_XDR

The file is read/written using XDR (eXternal Data Representation).

IDL_F_DEL_ON_CLOSE

The file will be deleted when it is closed.

IDL_F_SR

The file is a SAVE/RESTORE file.

IDL_F_UNIX_F77

The file is read/written in the format used by the Unix FORTRAN (f77) compiler for unformatted binary data.

IDL_F_UNIX_PIPE

The file is a bi-directional data path connecting IDL to a child process created by the SPAWN procedure.

IDL_F_UNIX_NOSTDIO

No stdio buffering will be performed for the file and all data transfers will go directly to the operating system for processing. Note that setting this bit does not guarantee that data will be written to the file immediately, because the operating system may buffer the data.

IDL_F_UNIX_SPECIAL

The file is a Unix device special file, most likely a pipe. This differs from IDL_F_UNIX_PIPE because it applies to any file, not only those opened with the SPAWN procedure.

File Information, continued


File Information, continued

IDL_F_VMS_FIXED

The file has fixed-length records.

IDL_F_VMS_VARIABLE

The file has variable-length records.

IDL_F_VMS_SEGMENTED

The file has VMS FORTRAN segmented records.

IDL_F_VMS_STREAM

The file is treated as a VMS stream file, opened via the standard C library, just like under Unix. VMS attempts to convert non-stream files into a logical stream in order to mask the fact that the file is not really a stream file.

IDL_F_VMS_STREAM_STRICT

The file is treated as a VMS stream file, opened via the standard C library, just like under Unix. In the case of non-stream files, no attempt is made to convert the file contents to a logical stream.

IDL_F_VMS_RMSBLK

The file is open for RMS block mode access. New files are created with fixed-length 512-byte records.

IDL_F_VMS_RMSBLKUDF

The file is open for RMS block mode access. New files are created with the UNDEFINED record type. One result of this is that most VMS utilities won't be able to read this file.

IDL_F_VMS_INDEXED

The file has indexed organization.

IDL_F_VMS_PRINT

The file will be sent to the VMS system printer SYS$PRINT when it is closed.

IDL_F_VMS_SUBMIT

The file will be sent to the standard VMS system batch queue SYS$BATCH when it is closed.

IDL_F_VMS_TRCLOSE

When closed, the file allocation will be truncated to the amount actually used.

IDL_F_VMS_CCLIST

The file has carriage return carriage control.

IDL_F_VMS_CCFORTRAN

The file has FORTRAN-style carriage control.

IDL_F_VMS_CCNONE

The file data contains explicit carriage control.

IDL_F_VMS_SHARED

Shared access to the file is allowed.

IDL_F_VMS_SUPERCEED

Supersede existing version on open.

IDL_F_DOS_TEXT

The file is in text mode.

IDL_F_DOS_BINARY

The file is in binary mode.

fptr

The stream file pointer to the file. This field can be used with standard library functions to perform I/O. This field is always valid under non-VMS operating systems although you shouldn't use it if the file is an XDR file. You can check for this by looking for the IDL_F_XDR bit in the flags field. Under VMS, the stream file pointer is only valid if the file is open for stream access. In this case the IDL_F_VMS_STREAM bit will be set in the flags field.

rms.mrs

For RMS (VMS) record oriented files, this field contains the record length.


Opening Files

Files are opened using the IDL_FileOpen() function:

int IDL_FileOpen(int argc, IDL_VPTR argv[], char *argk, int     access_mode, int extra_flags, int longjmp_safe)

where:

argc

The number of arguments in argv. This value should always be 2.

argv

The arguments to IDL_File_Open(). argv[0] should be a scalar integer value giving the file unit number (LUN) to be opened. argv[1] is a scalar string giving the file name.

argk

Keywords. Set this argument to NULL.

access_mode

A bit mask that specifies the access to be allowed to the file being opened. The allowed bit values are:

IDL_OPEN_R

The file is open for input.

IDL_OPEN_W

The file is open for output.

IDL_OPEN_TRUNC

The file was truncated when it was opened. This implies that IDL_OPEN_W is also set.

IDL_OPEN_APND

The file was opened with the file pointer set just past the last byte of data in the file (the file is open for appending).

It is important that conflicting bits not be set together (for example, do not specify IDL_OPEN_TRUNC | IDL_OPEN_APND). Also, one or both of IDL_OPEN_READ and IDL_OPEN_WRITE must always be specified.

extra_flags

Used to specify additional file attributes using the flags defined in the description of the flags field of the IDL_FILE_STAT struct (see "File Information" on page 8-2). Note that it makes no sense to set the IDL_F_ISATTY bit in this mask.

longjmp_safe

If set to TRUE, IDL_FileOpen() is being called in a context where an IDL_MSG_LONGJMP IDL_Message action code is okay. If set to FALSE, the routine won't longjmp().

IDL_FileOpen() returns TRUE if the file has been successfully opened and FALSE otherwise. Of course, if longjmp_safe is TRUE, the usual course is to jump back to the IDL interpreter, in which case the routine won't return.

Special File Units

There are three files that are always open. They are:

IDL_STDIN_UNIT

Unit 0 (zero) is the standard input for the IDL process.

IDL_STDOUT_UNIT

Unit -1 is the standard output.

IDL_STDERR_UNIT

Unit -2 is the standard error.

Under VMS, these units are open as both stream and RMS files—each unit is opened twice, once as a stream and again as an RMS variable length record file. This means that you can always refer to the fptr field for these units without checking the IDL_F_VMS_STREAM bit of the flags field. These are the only three units for which this is true. Finally, the constant IDL_NON_UNIT always has a value which is not a valid file unit.


Closing Files

Files are closed using the IDL_FileClose() function:

void IDL_FileClose(int argc, IDL_VPTR argv[], char *argk)

where:

argc

The number of arguments in argv.

argv

The arguments to the close function. These should be scalar integer values giving the Logical Unit Numbers of the file units to close.

argk

Keywords. Set this argument to NULL.


Preventing File Closing

Use the IDL_FileSetClose() function to prevent files from closing. It does this by setting or clearing the IDL_F_NOCLOSE bit in the flags field of the file descriptor (see "File Information" on page 8-2). This feature is used primarily in graphics drivers for printers. For example, the PostScript driver uses this feature to prevent the user from closing the plot data file prematurely.

void IDL_FileSetClose(int unit, int allow)

where:

unit

The Logical Unit Number (LUN) of the file in question. The file must be open for this function to have effect.

allow

Set this field to TRUE if users are allowed to close the file. Set to FALSE if users should be prevented from closing the file.

There are two macros provided to make preventing/enabling this bit easy:

IDL_FILE_NOCLOSE(unit)

Given the file LUN, this macro sets the IDL_F_NOCLOSE bit.

IDL_FILE_CLOSE(unit)

Given the file LUN this macro clears the IDL_F_NOCLOSE bit.

When IDL exits, it only closes open files that do not have the IDL_F_NOCLOSE bit set. Files with close inhibited are simply left alone. Often, you will want to declare an exit handler which takes care of closing such files.


Checking File Status

System routines that deal with files must verify that the files have the proper attributes for the intended operation. Use the function IDL_FileEnsureStatus() for this:

int IDL_FileEnsureStatus(int action, int unit, int flags)

where:

action

If the file unit does not satisfy the requirements of the flags argument, IDL_FileEnsureStatus() will issue an error using the IDL_Message() function (see "Issuing Error Messages" on page 6-1). action is the action argument to IDL_Message() and should be IDL_MSG_RET, IDL_MSG_LONGJMP, or IDL_MSG_IO_LONGJMP.

unit

The Logical Unit Number of the file to be checked.

flags

IDL_FileEnsureStatus() always checks to make sure unit is a valid logical file unit. In addition, flags is a bit mask specifying the file attributes that should be checked. The possible bit values are:

IDL_EFS_USER

The file must be a user unit. This means that the file is not one of the three special files, stdin, stdout, or stderr.

IDL_EFS_IDL_OPEN

The file unit must be open.

IDL_EFS_CLOSED

The file unit must be closed.

IDL_EFS_READ

The file unit must be open for input.

IDL_EFS_WRITE

The file unit must be open for output.

IDL_EFS_NOTTY

The file unit cannot be a tty.

IDL_EFS_NOGUI

The file unit cannot be a Graphical User Interface.

IDL_EFS_NOPIPE

The file unit cannot be a pipe.

IDL_EFS_NOXDR

The file unit cannot be a XDR file.

IDL_EFS_ASSOC

The file unit can be ASSOC'ed. This implies IDL_EFS_USER, IDL_EFS_OPEN, IDL_EFS_NOTTY, IDL_EFS_NOPIPE, and IDL_EFS_NOXDR, in addition to other operating system specific concerns.

IDL_EFS_NOT_NOSTDIO

The file wasn't opened with IDL_F_UNIX_NOSTDIO attribute under Unix.

Note that some of these values are contradictory. The caller must select a consistent set.

If the file unit meets the desired conditions, IDL_FileEnsureStatus() returns TRUE. If it does not meet the conditions, and action was IDL_MSG_RET, then it returns FALSE.


Allocating and Freeing File Units

System routines must allocate and deallocate file units in order to avoid conflicts. When writing IDL procedures, the GET_LUN and FREE_LUN procedures are used. When writing system-level routines, you can access the same routines by calling  IDL_FileGetUnit() and IDL_FileFreeUnit():

IDL_FileGetUnit()

Use IDL_FileGetUnit() to allocate file units:

void IDL_FileGetUnit(int argc, IDL_VPTR argv[])

argc

argc should always be 1.

argv

argv[0] contains an IDL_VPTR to the IDL_VARIABLE that will be filled in with the resulting unit number.

IDL_FileFreeUnit()

Use IDL_FileFreeUnit() to free file units:

void IDL_FileFreeUnit(int argc, IDL_VPTR argv[])

argc

argc gives the number of elements in argv.

argv

argv should contain scalar integer values giving the Logical Unit Numbers of the file units to be returned.

Read the description of GET_LUN and FREE_LUN in the IDL Reference Guide for additional details about these functions. The following code fragment demonstrates how these functions might be used to open and close a file named junk.dat:

VPTR argv[2];
IDL_VARIABLE unit;
IDL_VARIABLE name;
.
.
.
/* Allocate a file unit. */
argv[0] = &unit;
unit.type = TYP LONG;
unit.flags = 0;
IDL_FileGetUnit(1, argv);

/* Set up the file name */
name.type = TYP STRING;
name.flags = V CONST;
name.value.str.s = "junk.dat";
name.value.str.slen = sizeof("junk.dat") - 1;
name.value.str.stype = 0;
argv[1] = &name;
.
.
.
IDL_FileOpen(2, argv, (char *) 0, IDL_OPEN_R, IDL_F_VMS_STREAM);

/* Perform any required actions. */
.
.
.
/* Free the file unit. This will also close the file. */
IDL_FileFreeUnit(1, argv);


Detecting End of File

The IDL_FileEOF() function returns TRUE if the file specified by the Logical Unit Number unit is at EOF, and FALSE otherwise:

int IDL_FileEOF(int unit)

where:

unit

The Logical Unit Number (LUN) of the file in question.


Flushing Buffered Data

File data might be buffered in system memory in order to boost input/output performance. The IDL_FileFlushUnit() function forces any buffered data for the file specified by the Logical Unit Number unit to be written out:

int IDL_FileFlushUnit(int unit)

where:

unit

The Logical Unit Number (LUN) of the file in question.


Reading a Single Character

The IDL_GetKbrd() function returns the character code of the next available character from IDL_STDIN_UNIT:

int IDL_GetKbrd(int should_wait)

where:

should_wait

Set this argument to TRUE if IDL_GetKbrd() should wait for a key to be struck, FALSE otherwise.

If should_wait is FALSE and no input characters are waiting in the input stream,  IDL_GetKbrd() returns NULL. Otherwise, it waits until a key is struck (if necessary) and then returns its ASCII value. This function will generate an error and return to the interpreter if IDL_STDIN_UNIT is not a terminal.


Output of IDL Variables

The IDL_Print() and IDL_PrintF() functions output the value of IDL_VARIABLEs. IDL_Print() simply outputs to IDL_STDOUT_UNIT, while IDL_PrintF() outputs to a specified unit:

void IDL_Print(int argc, IDL_VPTR argv, char *argk)

void IDL_PrintF(int argc, IDL_VPTR argv[], char *argk)

where:

argc

The number of arguments to argv.

argv

IDL_VPTRs of the IDL_VARIABLEs to be output.

argk

Keywords. Set this argument to NULL ((char *) 0).

These functions are the implementation of the built-in IDL system procedures PRINT and PRINTF. See the discussion in the IDL Reference Guide for additional details.


Adding to the Journal File

The IDL_Logit() function can be used to add lines of output to the journal log file:

void IDL_Logit(char *s)

where:

s

A pointer to a NULL terminated string to be added to the journal log file.

If a journal log file is currently open, this function writes the specified string to it on a new line. If no journal file is open, IDL_Logit() returns quietly. The only way to open or close the journal file is via the JOURNAL user-level system procedure.


Signals
9	Signals

Chapter Overview

Signals pose one of the more difficult challenges faced by the Unix programmer. Although seemingly simple, they are a tough portability problem because there are several variants, and their semantics are subtle, inconvenient, and easily confused. IDL has always done whatever is necessary with signals in order to get its job done, but its signal assumptions can also affect user written code that is linked with it. Although this discussion refers primarily to Unix IDL, signals  are used in minimal ways under the other operating systems supported by IDL.

The following is a brief list of problems and contradictions inherent in Unix signals. For a more complete description, see Chapter 10 of Advanced Programming in the UNIX Environment by W. Richard Stevens.

•	Posix signals (sigaction) promise to unify and simplify signals, but not all platforms support them fully. Also, some platforms that do support Posix signals fail to provide needed information for SIGFPE and SIGTRAP, which are very important to IDL's exception handling.

•	You can only have one signal handler function registered for each signal. This means that if one part of a program uses a signal, the rest of the program must leave that signal alone.

•	In order to meet the needs of programs originally developed under different Unix systems (AT&T System V, BSD, Posix), most Unix implementations provide more than one package of signal functions. Typically, a given program is restricted to one of these libraries. If a programmer using CALL_EXTERNAL, LINKIMAGE, or Callable IDL chooses a library different from that used by IDL itself, unexpected results may occur.

•	The number and exact semantics of some signals differ in different versions.

•	Details of signal blocking differ.

•	Some System V implementations of signals are unreliable, meaning that signals can occur in a process and be missed.

•	Some older System V systems reset the handling action to SIG_DFL before calling the handler. This opens a window in time where two signals in a row can cause the process to be killed. Also, the signal handler must re-establish itself every time it is called.

•	On most platforms, if a signal is generated more than once while it is blocked, the second and subsequent occurrences are lost. In other words, most Unix implementations do not queue signals.

•	Most systems provide extra information for SIGFPE and SIGTRAP that allow the program to deduce what type of arithmetic problem occurred and continue execution. The format of this information differs widely. The details of continuing execution are highly OS- and hardware-dependent, and are often undocumented.

These are among the reasons that most libraries avoid signals, and leave their use to the end programmer. IDL, however, must use signals to function properly. In order to allow users to link their code into IDL while using signals, IDL provides a signal API built on top of the signal mechanism supported by the target platform. The IDL signal API has the following attributes:

•	It disallows use of SIGTRAP and SIGFPE. These signals are reserved to IDL.

•	It disallows use of SIGALRM. Most uses for SIGALRM are provided by the IDL timer API described in Chapter 10.

•	It works with all other signals, including those IDL doesn't currently use, so the interface won't change over time.

•	It allows multiple signal handlers for each signal, so IDL and other code can use the same signal simultaneously.

•	It unifies the signal interface by supplying a constant set of definitions and routines, and by handling details like re-establishing handlers.

•	It keeps IDL in charge of which signal package is used and how.

This is not a perfect solution, it is a compromise between the needs of IDL and programmers wishing to link code with it. Usually, the IDL signal abstraction is sufficient, but it does have the following limitations:

•	The calling program must not attempt to catch SIGTRAP or SIGFPE, either directly or through library routines that use these signals to achieve their ends. Furthermore, the IDL signal abstraction does not allow the caller to catch these signals, so your program must leave exception handling to IDL.

•	The caller loses control over signal package choice and some minor signal abilities.

•	Having multiple signal handler routines for a given signal opens the possibility that one handler might do something anti-social that causes problems for the others (like change the signal mask, or longjmp()). To minimize such problems, user code linked into IDL must not call the actual system signal routines, and must not longjmp() out of signal handlers—a tactic that is usually allowed, but which would seriously damage IDL's signal state.

•	Since there may be more than one signal handler registered for a given signal, the signal dispositions of SIG_IGN and SIG_DFL are not directly available to the caller as they would be if you were allowed to use the system signal facilities directly.

If you find that these restrictions are too limiting for your application, chances are your code is not compatible with IDL and should be executed in a separate process. We encourage you to consider running IDL in a separate process and to use an interprocess communication mechanism such as RPC.


Topics in Signals Chapter
Signals

This chapter describes the handling of signals in IDL.

Topics Covered:

Signal Handlers

Establishing a Signal Handler

Removing a Signal Handler

Unix Signal Masks

Jump forward to Timers

Jump backward to Files and Input/Output


Signal Handlers

IDL signal handler functions are defined as:

typedef void (* IDL_SignalHandler_t)(int signo);

When a signal is delivered to the process, all registered signal handlers are called. signo is the integer number of the signal delivered, as defined by the C language header file signal.h (found in /usr/include/signal.h on most Unix systems). signo can be used by a signal handler registered for more than one signal to tell which signal caused it to be called.


Establishing a Signal Handler

To register a signal handler, use the IDL_SignalRegister() function:

int IDL_SignalRegister(int signo, IDL_SignalHandler_t func,
                       int msg_action)

where:

signo

The numeric value of the signal to register for, as defined in signal.h.

func

The signal handler to be called when the signal specified by signo is raised.

msg_action

One of the IDL_MSG_* action codes for IDL_Message(). If there is an error in registering the signal handler, this action code is passed to IDL_Message() to direct its recovery action. Note that it is incorrect to use any of the message codes that cause IDL_Message() to longjmp() back to the IDL interpreter if your code is running in a context where the IDL interpreter is not active—specifically as part of using Callable IDL.

If func is successfully registered for signo, this routine returns TRUE. Otherwise, FALSE is returned and IDL_Message() is called with msg_action to control its behavior. Note that there are values of msg_action that result in this routine not returning on error. Multiple registration of the same function is allowed, but has no additional effect—the handler will only be called once.


Removing a Signal Handler

To remove a signal handler, use the IDL_SignalUnregister() function:

export int IDL_SignalUnregister(int signo,
                                IDL_SignalHandler_t func,
                                int msg_action)

where:

signo

The signal to unregister.

func

The handler to be unregistered.

msg_action

One of the IDL_MSG_* action codes for IDL_Message(). If there is an error in removing the signal handler, this action code is passed to IDL_Message() to direct its recovery action.

Once IDL_SignalUnregister() has been called, func is unregistered and will no longer be called if the signal is raised. IDL_SignalUnregister() returns TRUE for success, FALSE for failure. Requests to unregister a function that has not been previously registered are ignored.


Unix Signal Masks

Unix processes contain a signal mask that defines which signals can be delivered and which are blocked from delivery at any given time. When a signal arrives, the Unix kernel checks the signal mask: If the signal is in the process mask, it is delivered, otherwise it is noted as undeliverable and nothing further is done until the signal mask changes. Sets of signals are represented within IDL with the opaque type IDL_SignalSet_t. Unix IDL provides several functions that manipulate signal sets to change the process mask and allow/disallow delivery of signals.

IDL_SignalSetInit()

IDL_SignalSetInit() initializes a signal set to be empty, and optionally sets it to contain one signal.

void IDL_SignalSetInit(IDL_SignalSet_t *set, int signo)

where:

set

The signal set to be emptied/initialized.

signo

If non-zero, a signal to be added to the new set. This is provided as a convenience for the large number of cases where a set contains only one signal. Use IDL_SignalSetAdd() to add additional signals to a set.

IDL_SignalSetAdd()

IDL_SignalSetAdd() adds the specified signal to the specified signal set:

void IDL_SignalSetAdd(IDL_SignalSet_t *set, int signo)

where:

set

The signal set to be added to. The signal set must have been initialized by IDL_SignalSetInit().

signo

The signal to be added to the signal set.

IDL_SignalSetDel()

IDL_SignalSetDel() deletes the specified signal from a signal set:

void IDL_SignalSetDel(IDL_SignalSet_t *set, int signo)

where:

set

The signal set to delete from. The signal set must have been initialized by IDL_SignalSetInit().

signo

The signal to be removed from the signal set.

IDL_SignalSetIsMember()

IDL_SignalSetIsMember() tests a signal set for the presence of a specified signal, returning TRUE if the signal is present and FALSE otherwise:

int IDL_SignalSetIsMember(IDL_SignalSet_t *set, int signo)

where:

set

The signal set to test. The signal set must have been initialized by IDL_SignalSetInit().

signo

The signal to be removed from the signal set.

IDL_SignalMaskGet()

IDL_SignalMaskGet() sets a signal set to contain the signals from the current process signal mask:

void IDL_SignalMaskGet(IDL_SignalSet_t *set)

where:

set

The signal set in which the current process signal mask will be stored.

IDL_SignalMaskSet()

IDL_SignalMaskSet() sets the current process signal mask to contain the signals specified in a signal mask:

void IDL_SignalMaskSet(IDL_SignalSet_t *set,
                       IDL_SignalSet_t *omask)

where:

set

The signal set from which the current process signal mask will be set.

omask

If omask is non-NULL, the unmodified process signal mask is stored in it. This is useful for restoring the mask later using IDL_SignalMaskSet().

There are some signals that cannot be blocked. This limitation is silently enforced by the operating system.

IDL_SignalMaskBlock()

IDL_SignalMaskBlock() adds signals to the current process signal mask:

void IDL_SignalMaskBlock(IDL_SignalSet_t *set,
                         IDL_SignalSet_t *oset)

where:

set

The signal set containing the signals that will be added to the current process signal mask.

oset

If oset is non-NULL, the unmodified process signal mask is stored in it. This is useful for restoring the mask later using IDL_SignalMaskSet().

There are some signals that cannot be blocked. This limitation is silently enforced by the operating system.

IDL_SignalBlock()

IDL_SignalBlock() does the same thing as IDL_SignalMaskBlock() except it accepts a single signal number instead of requiring a mask to be built:

void IDL_SignalBlock(int signo, IDL_SignalSet_t *oset)

where:

signo

The signal to be blocked.

There are some signals that cannot be blocked. This limitation is silently enforced by the operating system.

IDL_SignalSuspend()

IDL_SignalSuspend() replaces the process signal mask with the ones in set and then suspends the process until a signal is delivered. On return, the original process signal mask is restored:

void IDL_SignalSuspend(IDL_SignalSet_t *set)

where:

set

The signal set containing the signals that will be added to the current process signal mask.


Timers
10	Timers

Chapter Overview

The details of how timers work varies widely between operating systems and between variants of the same operating system (different "flavors" of Unix, for example). IDL's timer module is intended to provide a constant interface to the rest of IDL, and to isolate the non-portable code in one place.

Under Unix, IDL's timer module performs a more important function. Unix processes contain a single timer that must be shared by all users. When the timer fires, it raises the SIGALRM signal which must be caught and handled by the process. The IDL timer routines transparently multiplex this single timer to provide multiple virtual timers.

Under Unix and VMS, IDL provides both blocking and non-blocking timers. Blocking timers put the calling process to sleep until they go off. Non-blocking timers are delivered asynchronously when they fire.

Under MS Windows and Macintosh OS, only the blocking form of timer requests are supported.


Topics in Timers Chapter
Timers

This chapter describes the handling of timers in IDL.

Topics Covered:

Making Timer Requests

Canceling Asynchronous Timer Requests

Blocking Unix Timers

Jump forward to Miscellaneous Information

Jump backward to Signals


Making Timer Requests

The IDL_TimerSet() function registers a timer request. IDL timer requests are one-shot timers. If you wish to have a timer go off repeatedly, your callback function must make a new request each time it is called to set up the next timer.

void IDL_TimerSet(length, callback, from_callback, context)

where:

length

The length of time to delay before issuing the alarm, in microseconds. You should be aware that other activity on the system, overhead incurred in managing the timers, and non-realtime attributes of the operating system can cause the actual duration of the timer to be longer than requested.

callback

Under Unix and VMS, if callback is non-NULL, the timer request is queued and IDL_TimerSet() returns immediately. When the alarm is due, the function pointed at by callback is called. If callback is NULL (and not from_callback), the request is queued and IDL_TimerSet() blocks until the requested time expires.

Under Windows and the Macintosh OS, callback should always be NULL. IDL_TimerSet() does not support non-blocking timers on these platforms.

from_callback

Set this argument to TRUE if this invocation is from a callback function previously set up via a call to IDL_TimerSet(). Set this argument to FALSE if this is the first invocation. In other words, this argument should only be TRUE if you call IDL_TimerSet() from within a timer callback.

context

This argument is a pointer to a variable of type IDL_TIMER_CONTEXT, an opaque IDL data type that uniquely identifies a timer request. If this is a top level request (if from_callback is FALSE), the context pointed at will be assigned a unique value that identifies the request.

If this request is coming from within a timer callback in order to make another request on the same timer, the context pointed at should contain the value from the previous request.

If context is NULL, no context value is returned.

Note: It is an error to queue more than one request using the same callback. The results are undefined.

For the timer module to perform adequately, the time request must be large compared to the run-time of the called function. Re-queuing an extremely short request repeatedly will cause any other requests to starve.


Canceling Asynchronous Timer Requests

Under Unix and VMS, IDL_TimerCancel() can be used to cancel a timer request that has not yet been delivered:

void IDL_TimerCancel(context)

where:

context

A timer request context returned by a previous call to IDL_TimerSet().


Blocking Unix Timers

Under Unix operating systems, the delivery of signals such as SIGALRM (used to manage timers) can cause system calls to be interrupted. In such cases, the system call returns a status of -1 and the global errno variable is set to the value EINTR. It is the caller's responsibility to check for this result and restart the system call when it occurs.

It is easy enough to handle this case when you make system calls directly, but sometimes the problem surfaces in libraries (even those provided by the system, such as libc) that are not properly coded against this possibility because the author assumed that no interrupts would occur. There is very little that the end user can do about such libraries except take steps that prevent signals from being raised during these critical sections.

If the IDL timer module is being used to deliver asynchronous events, it is inevitable that the delivery of SIGALRM will interfere with this sort of library code. The IDL_TimerBlock() function is available under Unix to suspend the delivery of the timer signal. This can be used to provide a window in which no timer will fire. This routine should always be called in pairs, so the timer doesn't get turned off permanently. It is important to be sure a longjmp() (such as caused by calling IDL_Message() with the IDL_MSG_LONGJMP action code) doesn't happen in the critical region. In addition, this function is not re-entrant.

The effect of blocking timer delivery is that the Unix SIGALRM signal is masked to prevent delivery. If the timer fires during this window of time, the signal will not be delivered until timers are unblocked. At that time, the timer module resumes managing the single real Unix timer. In the meantime, timer requests are arbitrarily delayed from being queued and processed. Clearly, excessive blocking of the timer can lead to poor timer performance and should only be performed when necessary and on the smallest possible critical section of code. Of course, the act of blocking and unblocking signals requires a context switch into the Unix kernel and back, making them relatively computationally expensive operations. It is therefore better to block a longer section of code rather than block and unblock around every critical library call.

It has been our experience that some Unix platforms have more problem with this issue than others. You should let experience guide you in deciding when to block signals and when to let them go. Input/Output to device special files under HP-UX and SGI IRIX are known to be especially vulnerable.

void IDL_TimerBlock(stop)

where:

stop

TRUE if the timer should be suspended, FALSE to restart it.


Miscellaneous Information
11	Miscellaneous Information

Chapter Overview

This chapter contains miscellaneous information related to the internals of IDL.


Topics in Miscellaneous Information Chapter
Miscellaneous Information

This chapter contains miscellaneous information related to the internals of IDL.

Topics Covered:

Dynamic Memory

Exit Handlers

User Interrupts

System Variables

Terminal Information

Type Information

User Information

Constants

Macros

Jump forward to Adding System Routines

Jump backward to Timers


Dynamic Memory

IDL provides access to the dynamic memory allocation routines it uses internally. Use these routines rather than system-provided routines such as malloc()/free() when possible.

Please note that system routines (routines added to IDL using LINKIMAGE or CALL_EXTERNAL) should not use the IDL dynamic memory routines. Instead, use IDL_GetScratch() (see "Getting Dynamic Memory" on page 3-10) which prevents memory from being lost under error conditions.

IDL_MemAlloc()

IDL_MemAlloc() is used to allocate dynamic memory.

void *IDL_MemAlloc(REGISTER unsigned int n, char *err_str,
                   int action)

where:

n

The number of bytes to allocate.

err_str

NULL, or a null terminated text string describing the memory being allocated.

action

An action parameter to be passed to IDL_Message() if IDL_MemAlloc() is unable to allocate the desired memory and err_str is non-NULL.

IDL_MemAlloc() attempts to allocate the desired amount of memory. If the requested amount is allocated, a pointer to the memory is returned. The memory is aligned strictly enough to be suitable for any object.

If the attempt to allocate memory fails and err_str is non-NULL, IDL_Message() is called as:

IDL_Message(M_CNTGETMEM, action|MSG_ATTR_SYS, err_str)

If IDL_Message() returns, or if err_str is NULL and IDL_Message() is not called, IDL_MemAlloc() returns a NULL pointer indicating its failure.

IDL_MemFree()

Memory allocated via IDL_MemAlloc() should only be returned via IDL_MemFree():

void IDL_MemFree(REGISTER void *m, char *err_str, int action)

m

A pointer to memory previously allocated via IDL_MemAlloc().

err_str

NULL, or a null terminated text string describing the memory being freed.

action

An action parameter to be passed to IDL_Message() if unable to free memory and err_str is non-NULL.

IDL_MemFree() attempts to free the specified memory. If the attempt to free memory fails and err_str is non-NULL, IDL_Message() is called as:

IDL_Message(M_CNTFREMEM, action|MSG_ATTR_SYS, err_str)

The following actions have undefined consequences, and should not be done:

•	Returning memory allocated from a source other than IDL_MemAlloc().

•	Freeing the same allocation more than once.

•	Dereferencing memory once it has been freed.

IDL_MemAllocPerm()

Another memory allocation routine, IDL_MemAllocPerm(), exists to allocate dynamic memory that will not be returned for reuse. IDL_MemAllocPerm() allocates memory in moderately large units and carves out pieces of these blocks to satisfy its requests. Use of this routine can help minimize the effects of memory fragmentation.

void *IDL_MemAllocPerm(REGISTER unsigned int n,
                       char *err_str, int action)

IDL_MemAllocPerm() takes the same arguments as IDL_MemAlloc(), differing only in that the memory allocated will not be freed until the process exits. Do not attempt to free memory allocated by IDL_MemAllocPerm(). The results of such an action are undefined.


Exit Handlers

IDL maintains a list of exit handler functions that it calls as part of its shutdown operations. These handlers perform actions such as closing files, wrapping up graphics output, and restoring the user environment to its initial state. Exit handlers accept no arguments and return no value. A typical declaration would be:

void my_exit_handler(void)
{
  /* Cleanup Code Here */

}

IDL_ExitRegister()

To register an exit handler, use the IDL_ExitRegister() function:

void IDL_ExitRegister(IDL_PRO_PTR proc)

proc

IDL will call proc just before it exits.

The order in which exit handlers are called is undefined, and you should not depend on any particular ordering. If you have several exit handlers and the order in which they are called is important, you should register a single handler that calls all the others in the required order.

Note: Under some operating systems, it is possible that the IDL process will die in an abnormal way that prevents the calling of the exit handlers. For example, under Unix, receiving some signals (possibly via the kill(1) command) will cause the process to die immediately. IDL always calls exit handlers when possible, so this is rarely a significant problem.


Access to IDL Global Data Under VAX/VMS"
Access to IDL Global Data Under VAX/VMS

Under VAX/VMS, IDL's global variables are available as linker UNIVERSAL symbols. However, the locations of these symbols within the IDL sharable image change from release to release. Therefore, if your program directly accesses these symbols, you must re-link your application every time you install a new IDL distribution.

However, it is possible to minimize the problem of re-linking with each IDL release by using the functions—described in "Functions for Returning System Variable Values" on page 11-5, "Functions for Returning IDL_FileTerm Variable Values" on page 11-6, and "Functions for Returning Data Type Variable Values" on page 11-7—that also provide access to global data. These functions are found in the IDL.EXE transfer vector. Therefore, if the functions are used, no re-linking is needed between releases as long as the transfer vector is not changed. Although Research Systems cannot always avoid changing the transfer vector, it is less likely to change than the locations of UNIVERSAL symbols.

Under ALPHA/VMS, global variables are found in the SYMBOL_VECTOR just like the exported functions, so the previously-described VAX/VMS problem does not occur with ALPHA/VMS. Under ALPHA/VMS, accessing the global variable is equivalent to using the function.


User Interrupts

IDL catches certain operating system signals including SIGINT, which occurs when the user types the interrupt character (usually Control-C). When the interpreter detects the interrupt character, it sets an internal flag which causes execution of the program to stop at the next sequence statement. The interpreter clears this variable every time it is invoked, and checks to see if it has been set before it executes each statement. This means that when the user presses the interrupt character, the current statement must complete before the interpreter checks the value of the variable and halts execution.

Typical statements do not take long to complete, so this delay is not noticeable. However, some system routines take a long time to complete, and the user can be fooled by the long delay into thinking that IDL is ignoring the interrupt. While the occasional long delay can be annoying, this method of handling interrupts is the only way to maintain acceptable performance in the usual case where no interrupt is pending. Therefore, it is the responsibility of system routines that take a long time to complete to check the value of this internal variable and to clean up and return if SIGINT is seen. IDL's Input/Output and FFT routines, among others, do this.

IDL_BailOut()

The IDL_BailOut() function is used to sense or set the state of IDL's internal interrupt flag. It returns TRUE if the keyboard interrupt character has been typed, otherwise FALSE.

int IDL_BailOut(int stop)

where:

stop

Set to FALSE to sense the state of the keyboard interrupt flag without changing its value. Set to TRUE to set the keyboard interrupt flag.


System Variables

The values of certain system variables are available globally to user programs. In all cases, these variables should be considered READ-ONLY. These variables are summarized in Table 11.1.

IDL System Variable	Internal Variable	Type
!DIR	IDL_SysvDir	IDL_STRING
!VERSION.ARCH	IDL_SysvVersion.arch	IDL_STRING
!VERSION.OS	IDL_SysvVersion.os	IDL_STRING
!VERSION.OS_FAMILY	IDL_SysvVersion.os_family	IDL_STRING
!VERSION.RELEASE	IDL_SysvVersion.release	IDL_STRING
!ERR	IDL_SysvErrCode	IDL_LONG
!ERROR	IDL_SysvErrorCode	IDL_LONG
!ORDER	IDL_SysvOrder	IDL_LONG
Table 11.1 IDL System Variables Available to User Programs

Functions for Returning System Variable Values

The following functions return the same information as the global system variables described above, but in a functional form.

IDL_STRING *IDL_SysVersionArch(void)

This function returns a pointer to IDL_SysVersion.arch.

IDL_STRING *IDL_SysVersionOS(void)

This function returns a pointer to IDL_SysVersion.os.

IDL_STRING *IDL_SysVersionOSFamily(void)

This function returns a pointer to IDL_SysVersion.os_family.

IDL_STRING *IDL_SysVersionRelease(void)

This function returns a pointer to IDL_SysVersion.release.

IDL_STRING *IDL_SysvDirFunc(void)

This function returns a pointer to IDL_SysvDir.

IDL_STRING *IDL_SysvErrStringFunc(void)

This function returns a pointer to IDL_SysvErrString.

IDL_STRING *IDL_SysvSyserrStringFunc(void)

This function returns a pointer to IDL_SysvSyserrString.

IDL_LONG IDL_SysvErrCodeValue(void)

This function returns the value of !ERR.

IDL_LONG IDL_SysvErrorCodeValue(void)

This function returns the value of !ERROR.

IDL_LONG IDL_SysvOrderValue(void)

This function returns the value of !ORDER.


Terminal Information

The global variable IDL_FileTerm is a structure of type IDL_TERMINFO:

typedef struct {
  char *name;     /* Name Of Terminal Type */
  char is_tty;    /* True if stdin is a terminal */
  int lines;      /* Lines on screen */
  int columns;    /* Width of output */
} IDL_TERMINFO;

Note: Under operating systems that do not support the concept of a terminal (the Macintosh OS and Microsoft Windows) the name and is_tty fields are not present.

IDL_FileTerm is initialized when IDL is started. Few, if any, user routines will need this information, because user routines should not do their own I/O. User routines that must do their own I/O should use this variable instead of making assumptions about the output device.

Functions for Returning IDL_FileTerm Variable Values

The following functions can be used to return values from the IDL_FileTerm variable. They return the same information contained in the global variable, but in a functional form.

char *IDL_FileTermName(void)

This function returns the value of IDL_FileTerm.name. This function is only available under Unix and VMS.

int IDL_FileTermIsTty(void)

This function returns the value of IDL_FileTerm.is_tty. This function is only available under Unix and VMS.

int IDL_FileTermLines(void)

This function returns the value of IDL_FileTerm.lines.

int IDL_FileTermColumns(void)

This function returns the value of IDL_FileTerm.columns.


Type Information

The following read-only global variables provide information about IDL data.

IDL_OutputFormat

An array of pointers to character strings. IDL_OutputFormat is indexed by type code, and specifies the default output formats for the different data types (see "Type Codes" on page 2-1). The default formats are used by the PRINT and STRING built-in routines as well as for type conversions.

IDL_OutputFormatLen

An array of integers. IDL_OutputFormatLen gives the length in characters of the corresponding elements of IDL_OutputFormat.

IDL_TypeSize

An array of long integers. IDL_TypeSize is indexed by type code, and gives the size of the data object used to represent each type.

IDL_TypeName

An array of pointers to character strings. IDL_TypeName is indexed by type code, and gives a descriptive string for each type.

Functions for Returning Data Type Variable Values

The following functions can be used to return the values contained in the global variables described above, but in a functional form.

char *IDL_OutputFormatFunc(int type)

Given an IDL type code, this function returns the default output format for that type. This is equivalent to accessing the IDL_OutputFormat array.

int IDL_OutputFormatLenFunc(int type)

Given an IDL type code, this function returns the default output format length for that type. This is equivalent to accessing the IDL_OutputFormatLen array.

int IDL_TypeSizeFunc(int type)

Given an IDL type code, this function returns the size of the data object used to represent that type. This is equivalent to accessing the IDL_TypeSize array.

char *IDL_TypeNameFunc(int type)

Given an IDL type code, this function returns the name of the type as a null terminated character string. This is equivalent to accessing the IDL_TypeName array.


User Information

Use the IDL_GetUserInfo() function to get information about the current session. This is the sort of information that can be used in the header of files produced by graphics drivers. It is used, for example, by the PostScript driver:

void IDL_GetUserInfo(IDL_USER_INFO *user_info)

where the IDL_USER_INFO struct is defined as:

typedef struct {
  char *logname;            /* User's login name */
  char host[64];            /* Machine name */
  char wd[IDL_MAX_PATH];    /* Working Directory */
char date[25];              /* Current System Time */
} IDL_USER_INFO;


Constants

Preprocessor constants defined in the export.h file should be used in preference to hardwired values. To accommodate the needs of various operating systems, some of these constants have different values in different versions of IDL. Those constants that are not discussed elsewhere in this book are listed below.

TRUE

A more readable alternative to the constant 1.

FALSE

A more readable alternative to the constant 0.

REGISTER

Some C compilers are good at allocating registers, and using the C register declaration can cause efficiency to suffer. On the other hand, many C compilers won't put any variables into registers unless register definitions are used. Our solution is to use REGISTER to declare variables we feel should be placed into registers. For machines that we feel have a good register allocation scheme, we define REGISTER to be a null macro. For lesser compilers, it is defined.

IDL_MAX_ARRAY_DIM

The maximum number of dimensions an array can have.

IDL_MAXIDLEN

The maximum number of characters IDL allows in an identifier (variable names, program names, etc.).

IDL_MAXPATH

The maximum number of characters allowed in a filepath.


Macros

The macros defined in export.h handle recurring small jobs. Those macros not discussed elsewhere in this book are covered here.

MIN(x,y) and MAX(x,y)

The arguments can be of any numeric C type as long as they are compatible with each other. MIN() and MAX() return the smaller and larger of their two arguments, respectively. These macros evaluate their arguments more than once, so be careful to avoid unwanted side effects, and for efficiency do not call them with an expression.

ABS(x)

ABS() accepts a single argument of any numeric C type, and returns its absolute value. ABS() evaluates its argument more than once, so do not call it with an expression.

IDL_ROUND_UP(x, m)

IDL_ROUND_UP() returns the value of x rounded up modulo m. m must be a power of 2. This macro is useful for extending data regions out to a specified alignment.

IDL_CHAR(ptr)

IDL_CHAR() casts its argument to be a pointer to char. It is used to convert an existing pointer into a generic pointer to an object.

IDL_CHARA(addr)

IDL_CHARA() takes the address of its argument and casts it to be a pointer to  char. It is used to get a generic pointer to an object.


Adding System Routines
12	Adding System Routines

Chapter Overview

An IDL system routine is an IDL procedure or function that is written in a compiled language and linked into IDL, instead of being written in the IDL language itself. The best way to create an IDL system routine is to compile and link the routine into a Unix or VMS sharable library or a Windows DLL, and then to add the routine to IDL at runtime using the LINKIMAGE procedure. Procedures for using LINKIMAGE to include system routines in IDL are documented in chapter 18 ("Operating System Access") of the IDL User's Guide and in the IDL Reference Guide.

If LINKIMAGE is not supported on your platform, you can add routines to IDL's table of procedures and functions using the IDL_AddSystemRoutine() function. Using IDL_AddSystemRoutine() is less attractive than using LINKIMAGE because it requires that you build a separate IDL program. For more information, see "Registering Routines using IDL_AddSystemRoutine()" on page 12-17.


Topics in Adding System Routines Chapter
Adding System Routines

This chapter contains information on adding system routines to IDL.

Topics Covered:

The System Routine Interface

A Hello World Example

A More Complex Example

Simplified Routine Invocation

A Complete Example

Registering Routines using IDL_AddSystemRoutine()

Jump forward to Introduction to Callable IDL

Jump backward to Miscellaneous Information


The System Routine Interface

Routines that implement IDL system functions must return an IDL_VPTR to the IDL_VARIABLE that contains the result. IDL System procedures do not return anything. Typical system routines are declared as:

IDL_VPTR my_function(int argc, IDL_VPTR argv[], char *argk)

void my_procedure(int argc, IDL_VPTR argv[], char *argk)

System routines that do not accept keywords are called with two arguments:

argc

The number of elements in argv.

argv

An array of IDL_VPTRs. These point to the IDL_VARIABLEs which comprise the arguments to the function.

System routines that accept keywords are called with an additional third argument:

argk

The keywords which were present when the routine was called. argk is an opaque object—the called routine is not intended to understand its contents. argk is provided to the function IDL_KWGetParams(), which processes the keywords in a standard way. For more information on keywords, see Chapter 4, "Keyword Processing".


A Hello World Example

Thanks to the definitive text on the C language (Kernighan and Ritchie, The C Programming Language, Prentice Hall, NJ, Second Edition, 1988), the "Hello World" program is often used as an example of a trivial program. Our version of this program is a system function that returns a scalar string containing the text "Hello World!":

#include <stdio.h>
#include "export.h"

IDL_VPTR hello_world(int argc, IDL_VPTR argv[])
{
  return(IDL_StrToSTRING("Hello World!"));
}

This is about as simple as an IDL system routine can be. The function IDL_StrToSTRING(), returns a temporary variable which contains a scalar string. Since this is exactly what is wanted, hello_world() simply returns the variable.

After compiling this function into a sharable object (named, say, hello_exe), we can link it into IDL with the following LINKIMAGE call:

LINKIMAGE, 'HELLO_WORLD', 'hello_exe', 1, 'hello_world', $             MAX_ARGS=1, MIN_ARGS=1

We can now issue the IDL command:

PRINT, HELLO_WORLD()

In response, IDL writes to the screen:

Hello World!


A More Complex Example

The system function shown below does a little more than the previous one, though not by much. It expects a single argument, which must be an array. It returns a single-precision, floating-point array that contains the values from the argument multiplied by two.

Each line is numbered to make discussion easier. These numbers are not part of the actual program:

1  #include <stdio.h>
2  #include "export.h"
3
4  IDL_VPTR mult2(int argc, IDL_VPTR argv[])
5  {
6    IDL_VPTR dst, src;
7    float *src_d, *dst_d;
8    int n;
9
10   src = dst = argv[0];
11   IDL_ENSURE_SIMPLE(src);
12   IDL_ENSURE_ARRAY(src);
13
14   if (src->type != IDL_TYP_FLOAT)
15     src = dst = IDL_CvtFlt(1, argv);
16
17   src_d = dst_d = (float *) src->value.arr->data;
18
19   if (!(src->flags & IDL_V_TEMP))
20     dst_d = (float *)
21       IDL_MakeTempArray(IDL_TYP_FLOAT,src->value.arr->n_dim,
22                       src->value.arr->dim,
23                       BASICARR_INI_NOP, &dst);
24
25   for (n = src->value.arr->n_elts; n--; )
26     *dst_d++ = 2.0 * *src_d++;
27
28   return(dst);
29 }

Each line of this routine is discussed below.

1 ­ 2

Include the required header files.

4

Every system routine takes the same two or three arguments. argc is the number of arguments, argv is an array of arguments. This routine does not accept keywords, so argk is not present.

6

dst will become a pointer to the resulting variable's descriptor. src points at the input variable which is found in argv[0].

7

src_d and dst_d will point to the source and destination data areas.

8

n will contain the number of elements in src.

10

Assume, for now, that the input variable will serve as both the source and destination. This will only be true if the parameter is a temporary floating-point array.

11 ­ 12

Screen out any input that is not of a basic type, and only allow arrays. A better version of this routine would handle scalar input also, but we want to keep the example simple.

14

If the input is not of IDL_TYP_FLOAT, we call the IDL_CvtFlt() function to create a floating-point copy of the argument (see "Converting Between Specific Types" on page 7-3 for information about converting types).

Note that the routine could also be written, more efficiently, with a C switch statement which would handle each of the eight possible data types, eliminating conversion of the input parameter. This would be more in the spirit of the IDL language, where system routines work with all possible data types and sizes, but is outside the scope of this example.

17

Here, we initialize the pointers to the source and destination data areas from the array block structure pointed to by the input variable descriptor.

19 ­ 23

If the input variable is not a temporary variable, we cannot change its value and return it as the function result. Instead, we allocate a new temporary floating point array into which the result will be placed. Notice how the number of dimensions and their sizes are taken from the source variable array block. See "Array Variables" on page 3-3 and "Temporary Variables" on page 3-7.

25

Loop over each element of the arrays.

26

Do the multiplication for each element.

28

Return the temporary variable containing the result.

Testing the Example

Once we have compiled the function and linked it into IDL using LINKIMAGE, we can use the built-in IDL function INDGEN to test the new function, which we name MULT2. INDGEN returns an array of values with each element set to the value of its array index. Therefore, the statement:

PRINT, INDGEN(5)

prints the following on the screen:

0 1 2 3 4

To test our new function we use INDGEN to provide an input argument:

PRINT, MULT2(INDGEN(5))

The result, as expected, is:

0 2.00000 4.00000 6.00000 8.00000


Simplified Routine Invocation

A great deal of the work involved in writing IDL system routines involves checking positional arguments, screening out illegal combinations of type and structure, and converting them to desired type. The function IDL_EzCall() provides a simplified way to handle this task. It processes an array of IDL_EZ_ARG structs which describe the processing to be applied to each positional argument.

The IDL_EzCall() function is similar to the facility provided for keyword arguments by IDL_KWGetParams():

void IDL_EzCall(int argc, IDL_VPTR argv[],
                IDL_EZ_ARG arg_struct[])

where:

argc

The number of positional arguments present.

argv

An array of pointers to the positional arguments.

arg_struct

An array of IDL_EZ_ARG structures defining the desired characteristics for each possible argument. Note that this array must have a definition for every possible parameter whether that argument is present in the current call or not. The order of the IDL_EZ_ARG structures is the same as the order in which the arguments are specified in the call. (See "The IDL_EZ_ARG struct" on page 12-6.)

There are some things you need to be aware of when using IDL_EzCall():

•	IDL_EzCall() automatically excludes file variables (such as those created by the ASSOC function) so you don't have to take any special action to screen such variables out.

•	Every call to IDL_EzCall() must have a matching call to IDL_EzCallCleanup() before execution returns to the interpreter.

•	IDL_EzCall() does not handle keyword arguments. If the calling routine allows keyword arguments, it must do its own keyword processing using IDL_KWGetParams() (see "Processing Keywords" on page 4-5) and pass an argv containing only positional arguments to IDL_EzCall().

•	If you mark a variable as being write-only, you shouldn't count on anything useful being in the uargv or value fields. This implies that it is not a good idea to set the IDL_EZ_POST_WRITEBACK field in the post field. Instead, you will have to allocate a new temporary variable, place the desired value into it, and use the IDL_VarCopy() function to write its value back into the original argv entry yourself.

Note: IDL_EZ_POST_WRITEBACK is only useful when the access field is set to IDL_EZ_ACCESS_RW.

The IDL_EZ_ARG struct

The IDL_EZ_ARG struct has the following definition:

typedef struct {
  short allowed_dims;
  short allowed_types;
  short access;
  short convert;
  short pre;
  short post;
  IDL_VPTR to_delete;
  IDL_VPTR uargv;
  IDL_ALLTYPES value;
} IDL_EZ_ARG;

where:

allowed_dims

A bit mask that specifies the allowed dimensions. Bit 0 means scalar, bit 1 means one-dimensional, etc. The IDL_EZ_DIM_MASK macro can be used to specify certain bits. It accepts a single argument that specifies the number of dimensions that are accepted, and returns the bit value that represents that number. For example, to specify that the argument can be scalar or have 2 dimensions:

IDL_EZ_DIM_MASK(0) | IDL_EZ_DIM_MASK(2)

In addition, the following constants are defined to simplify the writing of common cases:

IDL_EZ_DIM_ARRAY

Allow all but scalar.

EZ_DIM_ANY

Allow anything.

allowed_types

This is a bit mask defining the allowed data types for the argument. To convert type codes to the appropriate bits, use the formula:

BitMask = 2^TypeCode

or use the IDL_TYP_MASK macro (see "Type Masks" on page 2-1).

Note: If you specify a value for the convert field, its a good idea to specify IDL_TYP_B_ALL or IDL_TYP_B_SIMPLE here. The type conversion will catch any problems and your routine will be more flexible.

access

A bitmask that describes the type of access to be allowed to the argument. The following constants should be OR'd together to set the proper value:

IDL_EZ_ACCESS_R

The value of the argument is used by the system routine.

IDL_EZ_ACCESS_W

The value of the argument is changed by the system routine. This means that it must be a named variable (as opposed to a constant or expression).

IDL_EZ_ACCESS_RW

This is equivalent to IDL_EZ_ACCESS_R | IDL_EZ_ACCESS_W.

convert

The type code for the type to which the argument will be converted. A value of IDL_TYP_UNDEF means that no conversion will be applied.

pre

A bitmask that specifies special purpose processing that should be performed on the variable by IDL_EzCall(). These bits are specified with the following constants:

EZ_PRE_SQMATRIX

The argument must be a square matrix.

EZ_PRE_TRANSPOSE

Transpose the argument.

Note: This processing occurs after any type conversions specified by convert, and is only done if the access field has the IDL_EZ_ACCESS_R bit set.

post

A bit mask that specifies special purpose processing that should be performed on the variable by IDL_EzCallCleanup(). These bits are specified with the following constants:

IDL_EZ_POST_WRITEBACK

Transfer the contents of the uargv field back to the actual argument.

IDL_EZ_POST_TRANSPOSE

Transpose uargv prior to transferring its contents back to the actual argument.

Note: This processing occurs only when the access field has the IDL_EZ_ACCESS_W bit set. If IDL_EZ_POST_WRITEBACK is not present, none of the other actions are considered, since that would imply wasted effort.

to_delete

Do not make use of this field. This field is reserved for use by the EZ module. If IDL_EzCall() allocated a temporary variable to satisfy the conversion requirements given by the convert field, the IDL_VPTR to that temporary is saved here for use by IDL_EzCallCleanup().

uargv

After calling IDL_EzCall(), uargv contains a pointer to the IDL_VARIABLE which is the argument. This is the IDL_VPTR that your routine should use. Depending on the required type conversions, it might be the actual argument, or a temporary variable containing a converted version of the original. This field won't contain anything useful if the IDL_EZ_ACCESS_R bit is not set in the access field.

value

This is a copy of the value field of the IDL_VARIABLE pointed at by uargv. For scalar variables, it contains the value, for arrays it points at the array block. This field is here to make reading read-only variables faster. Note that this is only a copy from uargv, and changing it will not cause the actual value field in uargv to be updated.

Cleaning Up

Every call to IDL_EzCall() must be bracketed by a call to IDL_EzCallCleanup():

void IDL_EzCallCleanup(int argc, IDL_VPTR argv[],
                       IDL_EZ_ARG arg_struct[])

The arguments are exactly the same as those passed to IDL_EzCall().

Example—using IDL_EzCall()

The following function skeleton shows how to use the simplified interface to handle argument processing for an older version of the built-in SVD function. SVD accepts the following positional arguments (in order):

A

An m by n matrix (input, required).

w

An n-element vector (output, required).

U

An n by m matrix (output, optional)

V

An n by n matrix (output, optional)

Each line is numbered to make discussion easier. These numbers are not part of the actual program.

1  void nr_svdcmp(int argc, IDL_VPTR argv[])
2  {
3    .
4    .
5    .
6    static IDL_EZ_ARG arg_struct[] = {
7      { IDL_EZ_DIM_MASK(2), IDL_TYP_B_SIMPLE, IDL_EZ_ACCESS_R,
8        IDL_TYP_FLOAT, 0, 0 }, /* A */
9      { IDL_EZ_DIM_ANY, IDL_TYP_B_ALL,
10       IDL_EZ_ACCESS_W, 0, 0, 0 }, /* w */
11     { IDL_EZ_DIM_ANY, IDL_TYP_B_ALL,
12       IDL_EZ_ACCESS_W, 0, 0, 0 }, /* U */
13     { IDL_EZ_DIM_ANY, IDL_TYP_B_ALL,
14       IDL_EZ_ACCESS_W, 0, 0, 0 } /* V */
15   };
16
17   IDL_EzCall(argc, argv, arg_struct);
18   .
19   .
20   .
21   /* Do the SVD calculation and prepare temporary
22      variables to be returned as w, U, and V */
23   .
24   .
25   .
26   IDL_EzCallCleanup(argc, argv, arg_struct);
27 }

Those features of this procedure that are interesting in terms of plain argument processing are, by line number:

7-8

The settings of the various fields of the IDL_EZ_ARG struct for the first positional argument (A) specifies:

allowed_dims

The argument must be 2-dimensional.

allowed_types

It can have any simple type. Types and type codes are discussed in Chapter 2, "Types".

access

The routine will examine the argument's value, but will not attempt to change it.

convert

The argument should be converted to IDL_TYP_FLOAT if necessary.

pre

No pre-processing is required.

post

No post-processing is required.

The remaining fields are all set by IDL_EzCall() in response to the above.

9-14

Arguments two through four are allowed to have any number of dimensions and are allowed any type. This is because the routine does not intend to examine them, only to change them. For the same reason, a zero (IDL_TYP_UNDEF) is specified for the convert field indicating that no type conversion is desired. No pre or post-processing is specified.

17

Process the positional arguments.

26

Clean up.


A Complete Example

The following is a a complete implementation of the IDL system function FZ_ROOTS, used to find the roots of an m-degree complex polynomial, using Laguerre's method. The result is an m-element complex vector. We call this version FZ_ROOTS2 to avoid a name clash with the real routine. FZ_ROOTS2 has an additional keyword, TC_INPUT, that is not present in the real routine.

FZ_ROOTS2 uses the routine zroots(), described in section 9.5 of Numerical Recipes in C: The Art of Scientific Computing (Second Edition), published by Cambridge University Press:

void zroots(fcomplex a[], int m, fcomplex roots[], int polish)

FZ_ROOTS2 will support both single and double precision complex values as well as give the caller control over the error tolerance, which is hard wired into the Numerical Recipes code as a C preprocessor constant named EPS. In order to support these requirements, we have copied the zroots() function given in the book and altered it to support both data types and make EPS a user specified parameter, giving two functions:

void zroots_f(fcomplex a[], int m, fcomplex roots[], int polish,
             float eps);

void zroots_d(dcomplex a[], int m, dcomplex roots[], int polish,
             double eps);

Note that fcomplex and dcomplex are Numerical Recipes defined types that happen to have the same definition as the IDL types IDL_COMPLEX and IDL_DCOMPLEX, a convenient fact that eliminates some type conversion issues.

The definition of FZ_ROOTS2 from the IDL user perspective is:

Calling Sequence

Result = FZ_ROOTS2(C)

Arguments

C

A vector of length m+1 containing the coefficients of the polynomial, in ascending order.

Keywords

DOUBLE

FZ_ROOTS2 normally uses the type of C to determine the type of the computation. If DOUBLE is specified, it overrides this default. Setting DOUBLE to a non-zero value causes the computation type and the result to be double precision complex. Setting it to zero forces single precision complex.

EPS

The desired fractional accuracy. The default value is 2.0 x 10^-6.

NO_POLISH

Set this keyword to suppress the usual polishing of the roots by Laguerre's method.

TC_INPUT

If present, TC_INPUT specifies a named variable that will be assigned the input value C, with its type converted to the type of the result.

The following is the ANSI C code that implements FZ_ROOTS2 along with a line by line commentary. The line numbers are not part of the code and are present to make the discussion easier to follow.

1  #include <stdio.h>
2  #include <stdarg.h>
3  #include "export.h"
4  #include <nr/nr.h>
5
6
7  IDL_VPTR fzroots2(int argc, IDL_VPTR *argv, char *argk)
8  {
9    static int force_type;
10   static IDL_LONG do_double;
11   static double eps;
12   static IDL_LONG no_polish;
13   static IDL_VPTR tc_input;
14   static IDL_KW_PAR kw_pars[] = {
15     {"DOUBLE", IDL_TYP_LONG, 1, 0, &force_type,
16      IDL_CHARA(do_double) },
17     { "EPS", IDL_TYP_DOUBLE, 1, 0, 0, IDL_CHARA(eps) },
18     { "NO_POLISH", IDL_TYP_LONG, 1, IDL_KW_ZERO, 0,
19       IDL_CHARA(no_polish) },
20     { "TC_INPUT", 0, 1, IDL_KW_OUT|IDL_KW_ZERO, 0,
21       IDL_CHARA(tc_input) },
22     { NULL }
23   };
24
25   IDL_VPTR result;
26   IDL_VPTR c_raw;
27   IDL_VPTR c_tc;
28   int m;
29   void *outdata;
30   IDL_LONG dim[IDL_MAX_ARRAY_DIM];
31   int rtype;
32   static IDL_ALLTYPES zero;
33
34
35   eps = 2.0e-6;
36   (void) IDL_KWGetParams(argc, argv, argk, kw_pars,&c_raw,1);
37
38   IDL_ENSURE_ARRAY(c_raw);
39   IDL_ENSURE_SIMPLE(c_raw);
40   if (c_raw->value.arr->n_dim != 1)
41   IDL_Message(IDL_M_NAMED_GENERIC, IDL_MSG_LONGJMP,
42               "Input argument must be a column vector.");
43   m = c_raw->value.arr->dim[0];
44   if (--m <= 0)
45     IDL_Message(IDL_M_NAMED_GENERIC, IDL_MSG_LONGJMP,
46                 "Input array does not have enough elements");
47
48   if (tc_input)
49     IDL_StoreScalar(tc_input, IDL_TYP_LONG, &zero);
50
51
52   if (force_type) {
53     rtype = do_double ? IDL_TYP_DCOMPLEX : IDL_TYP_COMPLEX;
54   } else {
55     rtype = ((c_raw->type == IDL_TYP_DOUBLE)
56              || (c_raw->type == IDL_TYP_DCOMPLEX))
57       ? IDL_TYP_DCOMPLEX : IDL_TYP_COMPLEX;
58   }
59   dim[0] = m;
60   outdata = (void *)
61     IDL_MakeTempArray(rtype,1,dim,IDL_BARR_INI_NOP,&result);
62
63   if (c_raw->type == rtype) {
64     c_tc = c_raw;
65   } else {
66     c_tc = IDL_BasicTypeConversion(1, &c_raw, rtype);
67   }
68
69   if (rtype == IDL_TYP_COMPLEX) {
70     zroots_f((fcomplex *) c_tc->value.arr->data, m,
71              ((fcomplex *)outdata)-1,!no_polish,(float)eps);
72   } else {
73     zroots_d((dcomplex *) c_tc->value.arr->data, m,
74              ((dcomplex *) outdata) - 1, !no_polish, eps);
75   }
76
77   if (tc_input) IDL_VarCopy(c_tc, tc_input);
78   else if (c_raw != c_tc) IDL_Deltmp(c_tc);
79
80   return result;
81 }

Each line of this routine is discussed below.

4

nr.h is the header file provided with Numerical Recipes in C code.

7

FZROOTS2 has the usual three standard arguments.

9

force_type will be TRUE if the user specifies the DOUBLE keyword. In this case, the value of the DOUBLE keyword will determine the result type without regard for the type of the input argument.

10

If the user specifies DOUBLE, a zero value forces a single precision complex result and non-zero forces double precision complex.

11

The value of the EPS keyword.

12

The value of the NO_POLISH keyword.

13

The value of the TC_INPUT keyword.

14

This array defines the keywords accepted by FZ_ROOTS2.

15

Since setting DOUBLE to 0 has a different meaning than not specifying the keyword at all, force_type is used to detect the fact that the keyword is set independent of its value.

17

The EPS keyword allows the user to specify the eps tolerance parameter. eps is specified as double precision to avoid losing accuracy for double precision computations—it will be converted to single precision if necessary. The default value for this keyword is non-zero, so no zeroing is specified here. If the user includes the EPS keyword, the value will be placed in eps, otherwise eps will not be changed.

18

This keyword lets the user suppress the usual polishing performed by zroots(). Since specifying a value of 0 is equivalent to not specifying the keyword at all, IDL_KW_ZERO is used to initialize the variable.

20

If present, TC_INPUT is an output keyword that will have the type converted value of the input argument stored in it. By specifying IDL_KW_OUT and IDL_KW_ZERO, we ensure that TC_INPUT is either zero or a pointer to a valid IDL variable.

25

This variable will receive the function result.

26

The input argument prior to any type conversion.

27

The type converted input variable. If the input variable is already of the correct type, this will be the same as c_raw, otherwise it will be different.

28

The value of m to be passed to zroots().

29

Pointer to the data area of the result variable. We declare it as  (void *) so that it can point to data of any type.

30

Used to specify dimensions of the result. This will always be a vector of m elements.

31

IDL type code for result variable.

32

Used by IDL_StoreScalar() to type check the TC_INPUT keyword. It is declared as static to ensure it is initialized to zero.

35

Set the default EPS value before doing keyword processing. If the user specifies EPS, the supplied value will override this. Otherwise, this value will still be in eps and will be passed to zroots() unaltered.

36

Perform keyword processing.

38 ­ 39

Ensure that the input argument is an array, and is one of the basic types (not a file variable or structure).

40­ 42

The input variable must be a vector, and therefore should have only a single dimension.

43 ­ 46

Ensure that the input variable is long enough for m to be non-zero. m is one less than the number of elements in the input vector, so this is equivalent to saying that the input must have at least 2 elements.

48

If the TC_INPUT keyword was present, use IDL_StoreScalar() to make sure the named variable specified can receive the converted input value. A nice side effect of this operation is that any dynamic memory currently being used by this variable will be freed now instead of later after we have allocated other dynamic memory. This freed memory might be immediately reusable if it is large enough, which would reduce memory fragmentation and lower overall memory requirements.

52

If the user specified the DOUBLE keyword, it is used to control the resulting type, otherwise the input argument type is used to decide.

53

The DOUBLE keyword was specified. If it is non-zero, use IDL_TYP_DCOMPLEX, otherwise IDL_TYP_COMPLEX.

55 ­ 57

Use the input type to decide the result type. If the input is IDL_TYP_DOUBLE or IDL_TYP_DCOMPLEX, use IDL_TYP_DCOMPLEX, otherwise IDL_TYP_COMPLEX.

59 ­ 61

Create the output variable that will be passed back as the result of FZ_ROOTS2.

63­ 67

If necessary, convert the input argument to the result type. This is done after creation of the output variable because it is likely to have a short lifetime. If it does get freed at the end of this routine, it won't cause memory fragmentation by leaving a hole in the process virtual memory.

69

The version of zroots() to call depends on the data type of the result.

70 ­ 71

Single precision complex. Note that the outdata pointer is decremented by one element. This compensates for the fact that the Numerical Recipe routine will index it from [1..m] rather than [0..m-1] as is the usual C convention. Also, eps is cast to single precision.

73­ 74

Double precision complex case.

77

If the user specified the TC_INPUT keyword, copy the type converted input into the keyword variable. Since VarCopy() frees its source variable if it is a temporary variable, we are relieved of the usual responsibility to call IDL_Deltmp() if c_tc contains a temporary variable created on line 61.

78

The user didn't specify the TC_INPUT keyword. In this case, if we allocated c_tc on line 66, we must free it before returning.

80

Return the result.


Registering Routines using IDL_AddSystemRoutine()

As a supplement to LINKIMAGE or CALL_EXTERNAL, you can use the IDL_AddSystemRoutine() function to add your own system routines to IDL's table of procedures and functions. This function can be useful if you want to add a large number of system routines to IDL. IDL_AddSystemRoutine() is defined as:

int IDL_AddSystemRoutine(IDL_SYSFUN_DEF *defs, int is_function,
                       int cnt)

It returns TRUE if it succeeds in adding the routine or FALSE in the event of an error:

defs

An array of IDL_SYSFUN_DEF structures, one per routine to be declared. This array must be defined with the C language static storage class because IDL keeps pointers to it. defs must be sorted by routine name in ascending lexical order.

is_function

Set this parameter to TRUE if the routines in defs are functions, and FALSE if they are procedures.

cnt

The number of IDL_SYSFUN_DEF structures contained in the defs array.

The definition of IDL_SYSFUN_DEF is:

typedef IDL_VARIABLE *(* IDL_FUN_RET)();

typedef struct {
  IDL_FUN_RET funct_addr;
  char *name;
  UCHAR arg_min;
  UCHAR arg_max;
} IDL_SYSFUN_DEF;

IDL_VARIABLE structures are described in "The IDL_VARIABLE Structure" on page 3-1.

funct_addr

Address of the function implementing the system routine.

name

The name by which the routine is to be invoked from within IDL. This should be a pointer to a null terminated string. The name should be capitalized.

arg_min

The minimum number of arguments allowed for the routine. If the routine allows keywords, the value of arg_min should be OR'd with the value IDL_KW_ARGS. Thus, if the minimum value is 1 and keywords are allowed, define  arg_min as:

1 | IDL_KW_ARGS

If the minimum allowed number is zero, and the routine takes keywords, simply include IDL_KW_ARGS.

arg_max

The maximum number of arguments allowed for the routine. If the routine does not place an upper value on the number of arguments, use the value MAXPARAMS.

Example

The following example shows how to register a system routine without using LINKIMAGE. LINKIMAGE is the preferred way to add system routines to IDL because it does not require building a separate IDL program. For simplicity, everything is placed in a single file. Normally, you would modularize things to allow easier code maintenance.

#include <stdio.h>
#include "export.h"

void prox1(int argc, IDL_VPTR argv[])
{
  printf("prox1 %d\n", IDL_LongScalar(argv[0]));
}

main(int argc, char *argv[])
{
  static IDL_SYSFUN_DEF new_pros[] = {
    {(IDL_FUN_RET) prox1, "PROX1", 1, 1},
  };

  if (!IDL_AddSystemRoutine(new_pros, FALSE, 1))
    IDL_Message(IDL_M_GENERIC, IDL_MSG_RET,
                "Error adding system routine");
  return IDL_Main(0, argc, argv);
}

This adds a system procedure named PROX1 which accepts a single argument. It converts this argument to a scalar longword integer and prints it.


Introduction to Callable IDL
13	Introduction to Callable IDL

Chapter Overview

IDL can be called as a subroutine from other programs. This capability is referred to as Callable IDL to distinguish it from the more common case of calling your code from IDL via CALL_EXTERNAL or LINKIMAGE.

This chapter provides a basic description of Callable IDL. Subsequent chapters discuss the specifics of using Callable IDL under Unix and VMS (Chapter 14, "Using Callable IDL under Unix and VMS") and under Microsoft Windows (Chapter 15, "Using Callable IDL under Windows"). Chapter 16, "AppleScript Support", discusses "calling" IDL for Macintosh via AppleScript.


Topics in Introduction to Callable IDL Chapter
Introduction to Callable IDL

This chapter describes the basics of calling IDL from other programs.

Topics Covered:

How Callable IDL is Implemented on Different Platforms

When is it Appropriate to use Callable IDL?

Licensing Issues and Callable IDL

Using Callable IDL

Platform-specific Documentation for Callable IDL

Jump forward to Using Callable IDL under Unix and VMS

Jump backward to Adding System Routines


How Callable IDL is Implemented on Different Platforms

IDL for Windows, IDL for Unix, and IDL for VMS are packaged in a sharable form that allows other programs to call IDL as a subroutine. The details of packaging differ between platforms:

•	IDL for Windows has a small driver program linked to a Dynamic Link Library (DLL).

•	IDL for Unix has a small driver program linked to a sharable object library.

•	IDL for VMS is a sharable executable.

•	IDL for Macintosh, as mentioned previously, is "callable" using AppleScript. See "AppleScript Support" on page 16-1.

In all three cases, it is possible to link the sharable portion of IDL into your own programs. Note that Callable IDL is not a separate object that implements a library version of IDL—Interactive IDL as seen by the user calls the sharable IDL library itself.


When is it Appropriate to use Callable IDL?

Although Callable IDL is very powerful and convenient, it is not always the best method of communication between IDL and other programs. There are usually easier approaches that will solve a given problem in a more robust and maintainable manner. Consider using CALL_EXTERNAL, LINKIMAGE, or Remote Procedure Calls.

IDL will not integrate with all programs. Understanding the issues described in this section will help you decide when Callable IDL is and is not appropriate.

Technical Issues Relating to Callable IDL

IDL makes computing easier by raising the level at which IDL users interface with the computer. It is natural to think that calling IDL from other programs will have the same effect, and under the correct circumstances this is true. However, using Callable IDL is not as easy as using IDL. Programmers who wish to use Callable IDL need to possess the skills described in "Skills Required to Add Code to IDL" on page 1-3.

Be aware that the same things that make IDL powerful at the user level can make it difficult to include in other programs. As an interactive, interpreted language, IDL is a decidedly non-trivial object to add to a process. Unlike a simple mathematical subroutine, IDL includes a compiler, a language interpreter, and related code that the caller must work around. As an interactive program, IDL must control the process to a high degree, which can conflict with the caller's wishes. The following (certainly incomplete) list summarizes some of the issues that must be dealt with.

IDL Signal API

IDL uses Unix signals to manage many of its features, including exception handling, user interrupts, and child processes. The exact signals used and the manner in which they are used can change from IDL release to release as necessary. Although the IDL signal API (described in Chapter 9, "Signals") allows you to use signals in an IDL-compatible way, the resulting constraints may be difficult to cope with.

IDL Timer API

IDL's use of the process timer requires you to use the IDL timer API instead of the standard system routines. This restriction may be unacceptable to some programs. Under Unix, the timer module can interrupt system calls. Timers are discussed in Chapter 10, "Timers".

GUI Considerations

Most applications will call IDL and display IDL graphics in an IDL window. However, programmers may want to write applications in which they create the graphical user interface (GUI) and then have IDL draw graphics into windows that IDL did not create. It is not always possible for IDL to draw into windows that it did not create for the reasons described below:

X Windows

The IDL X Windows graphics driver can draw in windows it did not create as long as the window is compatible with the IDL display connection (see Chapter 3, "IDL Graphics Devices" of the IDL Reference Guide for details). However, the design of IDL's X Windows driver requires that it open its own display connection and run its own event loop. If your program cannot support a separate display connection, or if dividing time between two event loops is not acceptable, consider the following options:

•	Run IDL in a separate process and use interprocess communication (possibly Remote Procedure Calls, described in Chapter 24 of the IDL User's Guide) to control it.

•	If you choose to use Callable IDL, use the IDL Widget stub interface, described in Chapter 17, "Adding External Widgets to IDL", to obtain the IDL display connection, and create your GUI using that connection rather than creating your own. The IDL event loop will dispatch your events along with IDL's, creating a well-integrated system.

Microsoft Windows

At this time, the IDL for Windows graphics driver does not have the ability to draw into windows that were not created by IDL.

Program Size Considerations

On systems that support preemptive multitasking, a single huge program is a poor use of system capabilities. Such programs inevitably end up implementing primitive task-scheduling mechanisms better left to the operating system.

Troubleshooting

Troubleshooting and debugging applications that call IDL can be very difficult. With standard IDL, malfunctions in the program are clearly the fault of Research Systems, and given a reproducible bug report, we attempt to fix them promptly. A program that combines IDL with other code makes it difficult to unambiguously determine where the problem lies. The level of support Research Systems can provide in such troubleshooting is minimal. The programmer is responsible for locating the source of the difficulty. If the problem is in IDL, a simple program demonstrating the problem must be provided before we can address the issue.

Threading

IDL was not designed be used in a threaded program, nor is it threaded itself. Attempting to integrate IDL in a threaded application may cause unpredictable results.

Inter-language Calling Conventions

IDL is written in standard ANSI C. Calling it from other languages is possible, but it is the programmer's responsibility to understand the inter-language calling conventions of the target machine and compiler.

Appropriate Applications of Callable IDL

Callable IDL is most appropriate in the following situations:

•	Callable IDL is clearly the correct choice when the resulting program is to be a front-end that creates a different interface for IDL. For example, you might wish to turn IDL into an RPC server that uses an RPC protocol not directly supported by IDL, or use IDL as a module in a distributed system.

•	Callable IDL is appropriate if either the calling program or IDL handles all graphics, including the Graphical User Interface, without the involvement of the other. Intermediate situations are possible, but more difficult. In particular, beware of attempts to have two event/message loops.

•	Callable IDL is appropriate when the calling program makes little or no use of signals, timers, or exception handling, or is able to operate within the constraints imposed by IDL.

Alternatives to Callable IDL

Alternatives to Callable IDL depend on the operating system you are using and the design of the application. It is often possible to use IDL as part of a solution without using Callable IDL. If Callable IDL is not suitable for your application, one of the options below may be able to solve your problem:

•	Use CALL_EXTERNAL or LINKIMAGE instead of Callable IDL.

•	On Unix and VMS, IDL could be run in a separate process using local Interprocess Communication (IPC) mechanisms to control it.

•	Use shared memory to move data to and from IDL running in a separate process.

•	On Unix, IDL can be used as an RPC server. IDL's RPC support is documented in the Chapter 24 of the IDL User's Guide.


Licensing Issues and Callable IDL

If you intend to distribute an application that calls IDL, note that each copy of your application must have access to a properly licensed copy of the IDL library. For availability of a runtime version of IDL, contact Research Systems or your IDL distributor.


Using Callable IDL

The process of using Callable IDL has three stages: initialization, IDL use, and cleanup. Between the initialization and the cleanup, your program contains a complete active IDL session, just as if a user were typing commands at an IDL> prompt. In addition to the usual IDL abilities, you can import data from your program and cause IDL to see it as an IDL variable. IDL can use such data in computations as if it had created the variable itself. In addition, you can obtain pointers to data currently held by IDL variables and access the results of IDL computations from your program.

Initialization

Before calling IDL to execute instructions, you must initialize it using IDL_Init(). This is a one-time operation, and must occur before calling any other IDL function. (Unix and VMS users see "Initialization" on page 14-1. Windows users see "Initialization" on page 15-1.)

Call IDL

Once Callable IDL is initialized, you can perform two types of operations:

1.	Send IDL commands to IDL for execution. Commands are sent as strings, using the same syntax as interactive IDL. Note that there is not a separate function for every IDL command—any valid IDL command can be executed as IDL statements. This approach allows us to keep the callable IDL API small and simple while allowing full access to IDL's abilities. (Unix and VMS users see "Executing IDL Statements" on page 14-3. Windows users see "Executing IDL Statements" on page 15-3.)

2.	Call any of the several routines that interact with IDL through other means to perform operations such as:

•	Importing data into IDL. (See "Creating an Array from Existing Data" on page 3-9.)

•	Accessing data within IDL. (See "Looking Up Variables in Current Scope by Name" on page 3-14.)

•	Changing items in the process, such as signal handling or timers. (See Chapter 9, "Signals", or Chapter 10, "Timers".)

•	Redirecting IDL output to your own function for processing. (Unix and VMS users see "Diverting IDL Output" on page 14-2. Windows users see "Diverting IDL Output" on page 15-2.)

The above list is not complete, but is representative of the possibilities afforded by Callable IDL.

Cleanup

After all IDL use is complete, but before the program exits, you must call IDL_Cleanup() to allow IDL to shutdown gracefully and clean up after itself. Once this has been done, you are not allowed to call IDL again from this process. (Unix and VMS users see "Cleanup" on page 14-4, Windows users see "Cleanup" on page 15-4.)


Platform-specific Documentation for Callable IDL

The following chapters discuss the specifics of using Callable IDL under Unix and VMS (Chapter 14, "Using Callable IDL under Unix and VMS") and under Microsoft Windows (Chapter 15, "Using Callable IDL under Windows"). Chapter 16, "AppleScript Support", discusses "calling" IDL for Macintosh via AppleScript.


Using Callable IDL under Unix and VMS
14	Using Callable IDL under Unix and VMS

Chapter Overview

This chapter discusses the procedures used when calling IDL's sharable libraries under Unix and VMS. If you have not yet read Chapter 13, "Introduction to Callable IDL", please do so before continuing.

Procedures used when calling the IDL DLL under Microsoft Windows are covered in Chapter 15, "Using Callable IDL under Windows". Procedures used when "calling" IDL for Macintosh using AppleScript are covered in Chapter 16, "AppleScript Support".

Note: The functions documented in this chapter should only be used when calling IDL from other programs—their use in code called by IDL via CALL_EXTERNAL or LINKIMAGE is not supported and is certain to corrupt and/or crash the IDL process.


Topics in Using Callable IDL under Unix and VMS Chapter
Using Callable IDL under Unix and VMS

This chapter describes calling IDL from Unix and VMS programs.

Topics Covered:

Initialization

Diverting IDL Output

Cleanup

Executing IDL Statements

Interactive IDL

Compiling and Linking Programs that Call IDL

Example—Using Callable IDL from C

Example—Calling a Simple Mathematical Function

Example—Using Callable IDL from Fortran

Compilation and Linking Statements

Jump forward to Using Callable IDL under Windows

Jump backward to Introduction to Callable IDL


Initialization

The IDL_Init() function prepares Callable IDL for use. This must be the first IDL routine called.

int IDL_Init(int options, int *argc, char *argv[])

where:

options

A bitmask used to specify initialization options. The allowed bit values are:

IDL_INIT_GUI

Under Unix, setting this bit causes IDL to use the IDLtool GUI rather than using the standard tty based interface. This option is ignored under Windows and VMS.

IDL_INIT_GUI_AUTO

Under Unix, setting this bit causes IDL to try to use the IDLtool GUI. If that fails, IDL uses the standard tty interface. This option is ignored under Windows and VMS.

IDL_INIT_NOLICALIAS

Under Unix and VMS, our FLEXlm floating licence policy is to alias all IDL sessions that share the same user/system/display to the same license. If IDL_INIT_NOLICALIAS is set, this IDL session will force a unique license to be checked out. In this case, we allow the user to change the DISPLAY environment variable. This is useful for RPC servers that don't know where their output will need to go before invocation.

argc

As passed by the operating system to main().

argv

As passed by the operating system to main().

IDL_Init() returns TRUE if the initialization is successful, and FALSE for failure. Arguments not directly intended for IDL are removed from argv and argc is decremented to match.


Diverting IDL Output

When using a tty-based interface (Unix or VMS), IDL sends its output to the screen for the user to see. When using a GUI based interface (Windows, Macintosh, or Unix), the output goes to the log window. The default output function is automatically installed by IDL at startup. To divert IDL output to a function of your own design, use IDL_ToutPush() and IDL_ToutPop() to change the output function called by IDL.

Internally, IDL maintains a stack of output functions, and provides two functions (IDL_ToutPush() and IDL_ToutPop()) to manage them. The most recently pushed output function is called to output each line of text. Output functions of your own design should have the following type definition:

typedef void (* IDL_TOUT_OUTF)(int flags, char *buf, int n);

The arguments to an output function are:

flags

A bitmask of flag values that specify how the text should be output. The allowed bit values are:

IDL_TOUT_F_STDERR

Send the text to stderr rather than stdout, if that distinction means anything to your output device.

IDL_TOUT_F_NLPOST

After outputting the text, start a new output line. On a tty, this is equivalent to sending a newline ('\n') character.

buf

The text to be output. There may or may not be a NULL termination, so the character count provided by n must be used to move only the specified number of characters.

n

The number of characters in buf to be output.

IDL_ToutPush()

Use IDL_ToutPush() to push a new output function onto the stack. The most recently pushed function is the one used by IDL for output.

void IDL_ToutPush(IDL_TOUT_OUTF outf)

IDL_ToutPop()

IDL_ToutPop() removes the most recently pushed output function. The removed function pointer is returned. Caution: Do not pop an output function you did not push. It is an error to attempt to remove the last remaining function.

IDL_TOUT_OUTF IDL_ToutPop(void)


Executing IDL Statements

There are two functions that allow you to execute IDL statements. IDL_ExecuteStr() executes a single command, while IDL_Execute() takes an array of commands and executes them in order. In both cases, the commands are null terminated strings—just as they would be typed by an IDL user at the IDL> prompt. It is important to realize that the full abilities of IDL are available at this point. Typically, the commands you issue will run IDL programs of varying complexity, including support routines written in IDL from the IDL Library (found via the IDL !PATH system variable). This ability to "download" complicated programs into IDL and then run them via a simple command can be very powerful.

IDL_Execute()

IDL_Execute() executes the command strings in the order given. It returns the value of !ERROR after the final command has executed. If the value of !ERROR is needed for an intermediate command, you should use IDL_ExecuteStr() instead of IDL_Execute().

int IDL_Execute(int argc, char *argv[])

argc

The number of commands contained in argv.

argv

An array of pointers to NULL-terminated strings containing IDL statements to execute.

IDL_ExecuteStr()

IDL_ExecuteStr() returns the value of the !ERROR system variable after the command has executed.

int IDL_ExecuteStr(char *cmd)

cmd

A NULL-terminated string containing an IDL statement to execute.


Cleanup

After your program is finished using IDL (typically just before it exits) it should call IDL_Cleanup() to allow IDL to shut down gracefully. IDL_Cleanup() returns a status value that can be passed to Exit().

int IDL_Cleanup(int just_cleanup)

where:

just_cleanup

If TRUE, IDL_Cleanup() does all the process shutdown tasks, but doesn't actually exit the process. If FALSE (the usual), the process exits.


Interactive IDL

IDL_Main() implements IDL as seen by the interactive user. In the interactive version of IDL as shipped by Research Systems, the actual main() function simply decodes its arguments to determine which options to specify and then calls IDL_Main() to do the rest. IDL_Main() calls exit() and does not return to its caller.

int IDL_Main(int init_options, int argc, char *argv[]);

where:

init_options

The options argument to be passed to IDL_Init().

argc, argv

From main(). Arguments that correspond to options specified via the init_options argument should be removed and converted to init_options flags prior to calling this routine.


Compiling and Linking Programs that Call IDL

A complete discussion of the issues that arise when compiling and linking C programs is beyond the scope of this manual. The following is a brief list of basic concepts to consider when building programs that call IDL.

•	Compilers for some languages add underscores at the beginning or end of user defined names. To check the naming convention employed by your compiler, use the Unix nm(1) command to list the symbols exported from an object file.

If you use only one language, naming details are handled transparently by the compiler, linker, and debugger. If you use more than one language, problems arise if the different compilers use different naming conventions. For example, the SunOS Fortran compiler adds an underscore to the end of each name, while the C compiler does not. To call a Fortran routine from C, you must include this underscore in your code (to call the function "my_code", you would refer to it as "my_code_"). Note that you may also need to set a compiler flag to make case significant.

To determine whether your compilers use compatible naming conventions, consult your compiler documentation or experiment with small test programs using the compilers and the nm command.

•	Every program starts execution at a known routine. In the C language, this routine is explicitly named main(). In Fortran, execution begins with the implicit main program. If you are using Callable IDL, you must provide a main() function for your program.

•	When linking a C program, use the cc command instead of the ld command. cc calls ld to perform the link operation, and when necessary adds a directive to ld that causes the C runtime library to be used.

If you don't use cc to link your program (if you are using ld directly or are using a Fortran compiler, for example) and you get "unsatisfied symbol" errors for symbols that are in the standard C library, try including the runtime library explicitly in your link command. Usually, adding the string "-lc" to the end of the command is all that is necessary.

Note that under Hewlett-Packard's HP-UX operating system, if you use ld directly you may also need to include the PA1.1 math library in order to locate mathematics routines at runtime. Add the flag -L/lib/pa1.1 prior to -lm on the link line to link with the PA1.1 math libraries.

•	Motif libraries are not shipped with SunOS version 4.1.3 and Solaris version 2.3 from Sun Microsystems. Therefore, IDL for Sun platforms is built using IXI Premier Motif, as purchased from Sun Express. When building programs that call IDL version 4.0 on the Sun platforms, you must obtain the IXI package, install it on your system, and link with these libraries.

Note that Solaris versions 2.4 and later do include the necessary Motif libraries, and future versions of Solaris IDL will be built using them. The IXI Motif will always be required for the older SunOS 4.1 system, however, since Sun is no longer enhancing that older operating system.

See "Compilation and Linking Statements" on page 14-16 for examples showing how to compile and link programs with the IDL libraries under various operating systems.


Example - Using Callable IDL from C
Example—Using Callable IDL from C

The following program (calltest.c, found in the callable subdirectory of the examples subdirectory of the external subdirectory of the IDL distribution) demonstrates how to import data from a C program into IDL, execute IDL statements, and obtain data from IDL variables. It performs the following actions:

1.	Create an array of 10 floating point values with each element set to the value of its index. This is equivalent to the IDL command FINDGEN(10).

2.	Initialize Callable IDL.

3.	Import the floating point array into IDL as a variable named TMP.

4.	Have IDL print the value of TMP.

5.	Execute a short sequence of IDL statements from a string array:

tmp2 = total(tmp)
print,'IDL total is ',tmp2
plot, tmp

6.	Set TMP to zero, causing IDL to release the pointer to the floating point array.

7.	Obtain a pointer to the data contained in TMP2. From examining the IDL statements executed to this point, we know that TMP2 is a scalar floating point value.

8.	From our C program, print the value of the IDL TMP2 variable.

9.	Execute a small widget program. Pressing the button allows the program to end:

a = widget_base()
b = widget_button(a, value='Press When Done',xsize=300,
                   ysize=200)
widget_control, /realize, a
dummy = widget_event(a)
widget_control, /destroy, a

See "Using a C Compiler" on page 14-16 for details on compiling and linking this program.

Each line is numbered to make discussion easier. The line numbers are not part of the actual program.

1  #include <stdio.h>
2  #include "export.h"
3
4
5
6
7  static void free_callback(UCHAR *addr)
8  {
9     printf("IDL released(%u)\r\n", addr);
10 }
11
12
13
14 int main(int argc, char **argv)
15 {
16   float f[10];
17   int i;
18   IDL_VPTR v;
19   IDL_LONG dim[IDL_MAX_ARRAY_DIM];
20   static char *cmds[] = { "tmp2 = total(tmp)",
21     "print,'IDL total is ',tmp2", "plot,tmp" };
22   static char *cmds2[] = { "a = widget_base()",
23     "b = widget_button(a, value='Press When Done',
24     xsize=300, ysize=200)", "widget_control,/realize, a",
25     "dummy = widget_event(a)",
26     "widget_control,/destroy, a" };
27
28
29   for (i=0; i < 10; i++) f[i] = (float) i;
30   if (IDL_Init(0, &argc, argv)) {
31     dim[0] = 10;
32     printf("ARRAY ADDRESS(%u)\r\n", f);
33     if (v=IDL_ImportNamedArray("TMP", 1, dim, IDL_TYP_FLOAT,
34                  (UCHAR *) f, free_callback, (void *) 0)) {
35     (void) IDL_ExecuteStr("print, tmp");
36     (void) IDL_Execute(sizeof(cmds)/sizeof(char *), cmds);
37     (void) IDL_ExecuteStr("print, 'Free the user memory'");
38     (void) IDL_ExecuteStr("tmp = 0");
39     if (v = IDL_FindNamedVariable("tmp2", FALSE))
40       printf("Program total is %f\n\r", v->value.f);
41       (void) IDL_Execute(sizeof(cmds2)/sizeof(char *), cmds2);
42       IDL_Cleanup(FALSE);   /* Don't return */
43     }
44   }
45
46  return 1;
47 }

Following is commentary on the above program, by line number:

29

C equivalent to IDL command "F = FINDGEN(10)"

30

Initialize IDL

31­34

Import C array F into IDL as a FLTARR vector named TMP with 10 elements. Note use of the callback argument free_callback. This function will be called when IDL is finished with the array  F, giving us a chance to properly clean up at that time.

35

Have IDL print the value of TMP.

36

Execute the commands contained in the C string array cmds defined on lines 20­21. These commands create a new IDL variable named TMP2 containing the sum of the elements of TMP, print its value, and plot the vector.

37­38

Set TMP to a new value. This will cause IDL to release the user supplied memory from lines 31­34 and call free_callback.

39­40

From C, get a reference to the IDL variable TMP2 and print its value. This should agree with the value printed by IDL on line 36. It is important to realize that the pointer to the variable or anything it points at can only be used until the next call to execute an IDL statement. After that, the pointer and the contents of the referenced IDL_VARIABLE may become invalid as a result of IDL's execution.

41

Run the simple IDL widget program contained in the array C string array cmds2 defined on lines 22­26.

42

Shut down IDL. The FALSE argument instructs IDL_Cleanup() to exit the process, so this call should not return.

46

This line should never be reached. If it is, return the Unix failing status.


Example - Calling a Simple Mathematical Function
Example—Calling a Simple Mathematical Function

This example demonstrates how to write a simple C wrapper function that allows calling IDL commands simply from another language. In this example, we implement a function named call_idl_fft() that calls the IDL FFT function operating on data imported from our C program. It returns TRUE on success, FALSE for failure:

int call_idl_fft(IDL_COMPLEX *data, int n, int direction)

data

A pointer to a linear array of complex data to be processed.

n

The number of data points contained in the array data.

dir

The direction of the FFT transform to take. Specify -1 for a forward transform, 1 for the reverse

Each line is numbered to make discussion easier. These numbers are not part of the actual program.

1  #include <stdio.h>
2  #include "export.h"
3
4
5  int call_idl_fft(IDL_COMPLEX *data, int n, int dir)
6  {
7    int r;
8    IDL_LONG dim[IDL_MAX_ARRAY_DIM];
9    char buf[64];
10
11   dim[0] = n;
12   if (IDL_ImportNamedArray("TMP_FFT_DATA", 1, dim,
13       IDL_TYP_COMPLEX, (UCHAR *) data, 0, 0)) {
14     (void) IDL_ExecuteStr("!ERROR=0");
15     sprintf(buf,"TMP_FFT_DATA=FFT(TMP_FFT_DATA,/OVERWRITE)"16            ,dir);
17     r = !IDL_ExecuteStr(buf);
18     (void) IDL_ExecuteStr("TMP_FFT_DATA=0");
19   } else {
20     r = FALSE;
21   }
22
23   return r;
24 }
25
26 main(int argc, char **argv)
27 {
28 #define NUM_PNTS 10
29   IDL_COMPLEX data[NUM_PNTS];
20   int i;
31
32   for (i = 0; i < NUM_PNTS; i++) data[i].r = data[i].i = i;
33   if (IDL_Init(0, &argc, argv)) {
34     call_idl_fft(data, NUM_PNTS, -1);
35     call_idl_fft(data, NUM_PNTS, 1);
36     for (i = 0; i < NUM_PNTS; i++)
37       printf("(%f, %f)\n", data[i].r, data[i].i);
38     IDL_Cleanup(FALSE);
39   }
40
41  return 1;
42 }

Following is commentary on the above program, by line number:

7

The variable r holds the result from the function.

8

dim is used to import the data into IDL as an array.

9

A temporary buffer to format the IDL FFT command.

11­13

Import data into IDL as the variable TMP_FFT_DATA. We don't set up a free_callback because we will explicitly force IDL to release the pointer after the call to FFT.

14

Set !ERROR to zero so previous errors don't confuse our results.

15­16

Format an FFT command to IDL into buf. Note the use of the OVERWRITE keyword. This tells the IDL FFT function to place the results into the input variable rather than creating a separate output variable. Hence, the results end up in our data array without the need to obtain a pointer to the results and copy them out.

17

Have IDL execute the FFT statement. IDL_ExecuteStr() returns the value of !ERROR, which should be zero for success and non-zero in case of error. Hence, negating the result of IDL_ExecuteStr() yields the status value we require for the result of this function.

18

Set TMP_FFT_DATA to 0 within IDL. This causes IDL to release the data pointer imported previously.

20

If the call to IDL_ImportNamedArray() fails, we must report failure.

26

In order to test the call_idl_fft() function, this main program calls it twice. Taking numerical error into account the end result should be equal to the original data.

32

Set the real and imaginary part of each element to the index value.

33

Initialize Callable IDL.

34

Call call_idl_fft() to perform a forward transform.

35

Call call_idl_fft() to perform a reverse transform.

36­37

Print the results.

38

Shut down IDL and exit the process.

41

This line should never be reached. If it is, return the Unix failing status.


Example - Using Callable IDL from Fortran
Example—Using Callable IDL from Fortran

The following program (CALLTEST, found in the callable subdirectory of the examples subdirectory of the external subdirectory of the IDL distribution) demonstrates how to import data from a Fortran program into IDL, execute IDL statements, and obtain data from IDL variables. See "Using a Fortran Compiler" on page 14-18 for details on compiling and linking this program. The source code for this file can be found in the file calltest.f, located in the callable subdirectory of the examples subdirectory of the external subdirectory of the IDL distribution.

Each line is numbered to make discussion easier. The line numbers are not part of the actual program.

1	C-----------------------------------------------------------------

2	C    Routine to print a floating point value from an IDL variable.

3

4	 SUBROUTINE PRINT_FLOAT(VPTR)

5

6	C    Declare a Fortran Record type that has a compatible form with

7	C    the IDL C struct IDL_VARIABLE for a floating point value.

8	C    Note this structure contains a union which is the size of

9	C    the largest data type. This structure has been padded to

10	C    support the union. Fortran records are not part of

11	C    F77, but most compilers have this option.

12

13	 STRUCTURE /IDL_VARIABLE/

14	           CHARACTER*1 TYPE

15	           CHARACTER*1 FLAGS

16	           INTEGER*4 PAD       !Pad for largest data type

17	           REAL*4 VALUE_F

18	 END STRUCTURE

19

20	 RECORD /IDL_VARIABLE/ VPTR

21

22	 WRITE(*, 10) VPTR.VALUE_F

23	  10 FORMAT('Program total is: ', F6.2)

24

25	 RETURN

26

27	 END

28

29	C-----------------------------------------------------------------

30	C   This function will be called when IDL is finished with the

31	C   array F.

32

33	        SUBROUTINE FREE_CALLBACK(ADDR)

34

35	           INTEGER*4 ADDR

36

37	           WRITE(*,20) LOC(ADDR)

38	  20    FORMAT ('IDL Released:', I12)

39

40	           RETURN

41

42	        END

43

44	C-----------------------------------------------------------------

45	C  This program demonstrates how to import data from a Fortran

46	C  program into IDL, execute IDL statements and obtain data

47	C  from IDL variables.

48

49	     PROGRAM CALLTEST

50

51	C  Some Fortran compilers require external defs. for IDL routines:

52	        EXTERNAL IDL_Init !$pragma C(IDL_Init)

53	        EXTERNAL IDL_Cleanup !$pragma C(IDL_Cleanup)

54	        EXTERNAL IDL_Execute !$pragma C(IDL_Execute)

55	        EXTERNAL IDL_ExecuteStr !$pragma C(IDL_ExecuteStr)

56	        EXTERNAL IDL_ImportNamedArray !$pragma C(IDL_ImportNamedArray)

57	        EXTERNAL IDL_FindNamedVariable !$pragma C(IDL_FindNamedVariable)

58

59	C  Define arguments for IDL_Init routine

60	        INTEGER*4 ARGC

61	        INTEGER*4 ARGV(1)

62	        DATA ARGC, ARGV(1) /2 * 0/

63

64	C  Define IDL Definitions  for IDL_ImportNamedArray

65

66	        PARAMETER (IDL_MAX_ARRAY_DIM = 8)

67	        PARAMETER (IDL_TYP_FLOAT = 4)

68

69	        REAL*4 F(10)

70	        INTEGER*4 DIM(IDL_MAX_ARRAY_DIM)

71	        DATA DIM /10, 7*0/

72	        INTEGER*4 FUNC_PTR     !Address of function

73	        INTEGER*4 VAR_PTR      !Address of IDL variable

74	        EXTERNAL FREE_CALLBACK !Declare ext routine for use as arg

75

76	        PARAMETER (MAXLEN=80)

77	        PARAMETER (N=10)

78

79	C  Define commands to be executed by IDL

80

81	        CHARACTER*(MAXLEN) CMDS(3)

82	        DATA CMDS /"tmp2 = total(tmp)",

83	     &            "print, 'IDL total is ', tmp2",

84	     &            "plot, tmp"/

85	        INTEGER*4 CMD_ARGV(10)

86

87	C  Define widget commands to be executed by IDL

88

89	        CHARACTER*(MAXLEN) WIDGET_CMDS(5)

90	        DATA  WIDGET_CMDS /"a = widget_base()",

91	   &    "b = widget_button(a,val='Press When Done',xs=300,ys=200)",

92	   &    "widget_control, /realize, a",

93	   &    "dummy = widget_event(a)",

94	   &    "widget_control, /destroy, a"/

95

96	        INTEGER*4 ISTAT

97

98	C  Null Terminate command strings and store the address

99	C  for each command string in CMD_ARGV

100

101	        DO I = 1, 3

102	           CMDS(I)(MAXLEN:MAXLEN) = CHAR(0)

103	           CMD_ARGV(I) = LOC(CMDS(I))

104	        ENDDO

105

106	C  Initialize floating point array, equivalent to IDL FINDGEN(10)

107

108	        DO I = 1, N

109	           F(I) = FLOAT(I-1)

110	        ENDDO

111

112	C  Print address of F

113

114	 WRITE(*,30) LOC(F)

115	   30 FORMAT('ARRAY ADDRESS:', I12)

116

117	C  Initialize Callable IDL

118

119	        ISTAT = IDL_Init(%VAL(0), ARGC, ARGV(1))

120

121	        IF (ISTAT .EQ. 1) THEN

122

123	C  Obtain address of callback routine

124

125	        FUNC_PTR = LOC(FREE_CALLBACK)

126

127	C  Import the floating point array into IDL as a variable named TMP

128

129	        CALL IDL_ImportNamedArray("TMP", %VAL(1), DIM,

130	    &       %VAL(IDL_TYP_FLOAT), F, %VAL(FUNC_PTR), %VAL(0))

131

132	C  Have IDL print the value of tmp

133

134	        CALL IDL_ExecuteStr("print, tmp")

135

136	C  Execute a short sequence of IDL statements from a string array

137

138	        CALL IDL_Execute(%VAL(3), CMD_ARGV)

139

140	C  Set tmp to zero, causing IDL to release the pointer to the

141	C  floating point array.

142

143	        CALL IDL_ExecuteStr("tmp = 0")

144

145	C  Obtain the address of the IDL variable containing the

146	C  the floating point data

147

148	        VAR_PTR = IDL_FindNamedVariable("tmp2", %VAL(0))

149

150	C  Call a Fortran routine to print the value of the IDL tmp2 variable

151	        CALL PRINT_FLOAT(%VAL(VAR_PTR))

152

153

154	C  Null Terminate command strings and store the address

155	C  for each command string in CMD_ARGV

156

157	        DO I = 1, 5

158	           WIDGET_CMDS(I)(MAXLEN:MAXLEN) = CHAR(0)

159	           CMD_ARGV(I) = LOC(WIDGET_CMDS(I))

160	        ENDDO

161

162	C  Execute a small widget program. Pressing the button allows

163	C  the program to end

164

165	        CALL IDL_Execute(%VAL(5), CMD_ARGV)

166

167	C  Shut down IDL

168	        CALL IDL_Cleanup(%VAL(0))

169

170	      ENDIF

171

172	   END

Following is a commentary on the program shown above, by line number:

1-27

In order to print variables returned from IDL, we must define a Fortran structure type for IDL_VARIABLEs. This subroutine creates the IDL_VARIABLE structure and defines a way to print the floating-point value returned in the an IDL variable.

14-17

Define a Fortran structure equivalent to the floating-point portion of the C IDL_VARIABLE structure. Since we know our value is a floating-point number, only the floating-point portion of the structure is implemented. The structure is padded for the largest data type contained in the union. With some Fortran compilers, the combination of UNION and MAP can be used to implement the ALLTYPES union portion of the IDL_VARIABLE structure.

29-42

This subroutine is called when IDL releases the user-supplied memory.

44-164

This is the main Fortran program.

51-57

External definitions for IDL internal routines. These definitions may not be necessary with some Fortran compilers.

59-62

Define the argc and argv arguments required by IDL_Init().

66-67

Define constants equivalent to C IDL constants for the maximum array dimensions and type float.

69-77

Define parameters necessary for IDL_ImportNamedArray().

79-85

Define an array of IDL commands to be executed.

87-96

Define an array of IDL widget commands to be executed.

98-104

Null-terminate each of the command strings and store the address of each command to pass to IDL.

106-110

Initialize the floating-point array. This is the Fortran equivalent to the IDL command F=FINDGEN(10).

117-121

Initialize IDL.

125

Obtain the address of the callback function FREE_CALLBACK().

129-130

Import the Fortran array F in the IDL as a 10-element FLTARR vector named TMP. Note the use of the callback argument FREE_CALLBACK(), which will be called when IDL is finished with the array F, giving us a chance to clean up at that time.

138

Execute the commands contained in the character array CMDS defined on lines 71-77. The address for each command is stored in the corresponding array element of CMD_ARGV.

143

Set the TMP variable to a new value. This causes IDL to release the user-supplied memory and call FREE_CALLBACK().

148

Get a reference to the IDL variable TMP2.

151

Call the routine PRINT_FLOAT to print the value of TMP2. This should agree with the value printed by line 130. Note that the address of the IDL variable TMP2, and its contents, can only be used until the next call to execute an IDL statement, since IDL may change the value of the referenced IDL_VARIABLE.

154-165

Execute the commands contained in the character array WIDGET_CMDS defined on lines 79-88.

167-172

Shut down IDL. The 0 argument instructs IDL_CLEANUP() to exit the process, so this call should not return.


Compilation and Linking Statements

This section describes compilation and linking procedures used when calling IDL on a Unix system. Note that different Unix systems have different compilation and link statements. Note also that the name of the entry point in the object may be different than that shown here, because compilers may add leading or trailing underscores to the name of the source routine.

The Makefile in the architecture-specific subdirectory of the bin subdirectory of the IDL distribution contains a make rule for building calltest. The text of this section is derived from those files.

Using a C Compiler

The following commands demonstrate how the calltest program (described in "Example—Using Callable IDL from C" on page 14-6) is built for OSF/1, HP-UX, AIX, IRIX, SunOS, and Solaris 2. In all cases, it is assumed that the environment variable IDL_DIR is defined, and points at the root of the IDL installation (usually /usr/local/rsi/idl_4).

DEC Alpha/OSF1 Version 2.0:

Use the following command to compile the C program calltest.

cc -I$IDL_DIR/external -Wl,-rpath,.:$IDL_DIR/bin/bin.alpha  \

    -o calltest calltest.c -L$IDL_DIR/bin/bin.alpha \

    -lidl -lXm -lXt -lX11 -ltermcap -lm

Hewlett-Packard HP-UX Version 9.0:

Use the following command to compile the C program calltest. Note that Research Systems uses HP's cc compiler, but that compiler is not usually bundled with HP-UX.

cc -I$IDL_DIR/external +z -Aa -D_HPUX_SOURCE -Wl,+b,$IDL_DIR/bin/bin.hp \

    -Wl,+s -o calltest calltest.c -L$IDL_DIR/bin/bin.hp \

    -lidl -lidl_cdf -lidl_netcdf -lidl_hdf -L/usr/lib/X11R5 \

    -L/usr/lib/Motif1.2 -lXm -lXt -lX11 -lcurses -lm -lcl -ldld

IBM AIX Version 3.2:

Use the following command to compile the C program calltest.

cc -I$IDL_DIR/external -qro -qroconst -o calltest calltest.c \

    -x -L$IDL_DIR/bin/bin.ibm -lidl -lm

Silicon Graphics IRIX Version 5.2:

Use the following command to compile the C program calltest.

cc -I$IDL_DIR/external -o calltest calltest.c \

    -L$IDL_DIR/bin/bin.sgi -lidl -lXm -lXt \

    /usr/lib/fixade.o -lmalloc -lX11 -ltermcap -lm -lfpe

SunOS Version 4.1.3:

Use the following command to compile the C program calltest. Note that Research Systems uses Sun's acc compiler, but that compiler is not usually bundled with SunOS. Also, as mentioned in "Compiling and Linking Programs that Call IDL" on page 14-5, IDL for SunOS 4.1.3 is built using IXI Premier Motif as purchased from Sun Express and installed in /opt/IXImd12x.

acc -I$IDL_DIR/external -o calltest calltest.c \

    $IDL_DIR/bin/bin.sunos.4.1/idl.o -L$IDL_DIR/bin/bin.sunos.4.1 -lidl \

    -L/opt/IXImd12x/lib -Bstatic -lXm -lXt -lX11 -Bdynamic -ltermcap -lm

Sun Solaris Version 2.3:

Use the following command to compile the C program calltest. As mentioned in "Compiling and Linking Programs that Call IDL" on page 14-5, IDL version 4.0 for Solaris 2.3 is built using IXI Premier Motif as purchased from Sun Express and installed in /opt/IXImd12s. In the future, IDL for Solaris will be built using the Motif libraries bundled by Sun into Solaris version 2.4 and later.

cc -I$IDL_DIR/external -o calltest calltest.c -z defs -i \

    -R .:$IDL_DIR/bin/bin.solaris2 -L$IDL_DIR/bin/bin.solaris2 \

    -L/opt/IXImd12s/lib -lidl -Bstatic -lXm -lXt -lX11 -Bdynamic \

    -lgen -lsocket -lgen -lnsl -ldl -ltermcap -lw -lintl -lm -lsunmath

Using a Fortran Compiler

The C compilation and link statements shown above can be used as guidelines when compiling Fortran programs. The following makefile demonstrates how the Fortran program CALLTEST (described in "Example—Using Callable IDL from Fortran" on page 14-11) is built for HP-UX.

The Makefiles for SunOS and HP-UX contain a rule for building the Fortran version of CALLTEST.

Hewlett-Packard HP-UX Version 9.0:

Use the following makefile to compile the Fortran program CALLTEST:

# HP makefile for Fortran calltest program

IDL_DIR=/usr/local/rsi/idl_4

LD_FLAGS =  -L/usr/lib/X11R5 -L/usr/lib/Motif1.2 \

    -lXm -lXt -lX11 -lcurses -L/lib/pa1.1 -lm -lcl -lc -ldld

# /lib/crt0.o is a startup file. It contains the program's entry point.

calltest: calltest.o

    ld /lib/crt0.o -o calltest calltest.o \

      -L$(IDL_DIR)/bin/bin.hp -lidl $(LD_FLAGS)

# The +U option is necessary for case-sensitivity.

.f.o :

    f77  +U -c $*.f


Using Callable IDL under Windows
15	Using Callable IDL under Windows

Chapter Overview

This chapter discusses calling the IDL Win32 DLL under Microsoft Windows. If you have not yet read Chapter 13, "Introduction to Callable IDL", please do so before continuing. The first sections of this chapter describe calling IDL from a 32-bit Windows application. Note that IDL does not support calls from 16-bit Windows applications.

Procedures used when calling the IDL for Unix and IDL for VMS sharable objects are covered in Chapter 14, "Using Callable IDL under Unix and VMS". Procedures used when "calling" IDL for Macintosh using AppleScript are covered in Chapter 16, "AppleScript Support".

Note: The functions documented in this chapter should only be used when calling IDL from other programs—their use in code called by IDL via CALL_EXTERNAL or LINKIMAGE is not supported and is certain to corrupt and/or crash the IDL process.


Topics in Using Callable IDL under Windows Chapter
Using Callable IDL under Windows

This chapter describes calling IDL from Microsoft Windows programs.

Topics Covered:

Initialization

Diverting IDL Output

Executing IDL Statements

Cleanup

Building a 32-bit Application that Calls IDL

Example—A Simple Windows Application

Jump forward to AppleScript Support

Jump backward to Using Callable IDL under Unix and VMS


Initialization

The IDL_Win32Init() function prepares the IDL DLL for use. IDL_Win32Init() must be called before any other function except IDL_ToutPush().

int IDL_Win32Init(int iOpts, void *hinstExe, void *hwndExe,
                 void *hAccel)

where:

iOpts

Reserved. This argument should always be 0 (zero).

hinstExe

HINSTANCE from the application that will be calling IDL.

hwndExe

HWND for the application's main window.

hAccel

Reserved. This argument should always be NULL.

IDL_Win32Init() returns TRUE if the initialization is successful, and FALSE for failure.


Diverting IDL Output

When using a tty-based interface (Unix or VMS), IDL sends its output to the screen for the user to see. When using a GUI based interface (Windows, Macintosh, or Unix), the output goes to the log window. The default output function is automatically installed by IDL at startup. To divert IDL output to a function of your own design, use IDL_ToutPush() and IDL_ToutPop() to change the output function called by IDL.

Internally, IDL maintains a stack of output functions, and provides two functions (IDL_ToutPush() and IDL_ToutPop()) to manage them. The most recently pushed output function is called to output each line of text. Output functions of your own design should have the following type definition:

typedef void (* IDL_TOUT_OUTF)(int flags, char *buf, int n);

The arguments to an output function are:

flags

A bitmask of flag values that specify how the text should be output. The allowed bit values are:

IDL_TOUT_F_STDERR

Send the text to stderr rather than stdout, if that distinction means anything to your output device.

IDL_TOUT_F_NLPOST

After outputting the text, start a new output line. On a tty, this is equivalent to sending a newline ('\n') character.

buf

The text to be output. There may or may not be a NULL termination, so the character count provided by n must be used to move only the specified number of characters.

n

The number of characters in buf to be output.

IDL_ToutPush()

Use IDL_ToutPush() to push a new output function onto the stack. The most recently pushed function is the one used by IDL for output.

void IDL_ToutPush(IDL_TOUT_OUTF outf)

IDL_ToutPop()

IDL_ToutPop() removes the most recently pushed output function. The removed function pointer is returned. Caution: Do not pop an output function you did not push. It is an error to attempt to remove the last remaining function.

IDL_TOUT_OUTF IDL_ToutPop(void)


Executing IDL Statements

There are two functions that allow you to execute IDL statements. IDL_ExecuteStr() executes a single command, while IDL_Execute() takes an array of commands and executes them in order. In both cases, the commands are null terminated strings—just as they would be typed by an IDL user at the IDL> prompt. It is important to realize that the full abilities of IDL are available at this point. Typically, the commands you issue will run IDL programs of varying complexity, including support routines written in IDL from the IDL Library (found via the IDL !PATH system variable). This ability to "download" complicated programs into IDL and then run them via a simple command can be very powerful.

IDL_Execute()

IDL_Execute() executes the command strings in the order given. It returns the value of !ERROR after the final command has executed. If the value of !ERROR is needed for an intermediate command, you should use IDL_ExecuteStr() instead of IDL_Execute().

int IDL_Execute(int argc, char *argv[])

argc

The number of commands contained in argv.

argv

An array of pointers to NULL-terminated strings containing IDL statements to execute.

IDL_ExecuteStr()

IDL_ExecuteStr() returns the value of the !ERROR system variable after the command has executed.

int IDL_ExecuteStr(char *cmd)

cmd

A NULL-terminated string containing an IDL statement to execute.


Cleanup

After your program is finished using IDL (typically just before it exits) it should call IDL_Cleanup() to allow IDL to shut down gracefully. IDL_Cleanup() returns a status value that can be passed to Exit().

int IDL_Cleanup(int just_cleanup)

where:

just_cleanup

If TRUE, IDL_Cleanup() does all the process shutdown tasks, but doesn't actually exit the process. If FALSE (the usual), the process exits.

This call should be placed in your Main WndProc and be called as a result of the WM_CLOSE message.

switch(msg){
           ...
      case WM_CLOSE:
           IDL_Cleanup(TRUE);
           any additional processing
           ...


Building a 32-bit Application that Calls IDL

To build your 32-bit application that calls IDL, you must take the following steps:

1.	Include exports.h, found in the external subdirectory of the IDL distribution, in your source code.

2.	Compile your application.

3.	Link your application with IDL32.LIB.

4.	Place IDL32.DLL in a directory with your application.


Example - A Simple Windows Application
Example—A Simple Windows Application

The following program demonstrates how to display message text sent from IDL, execute IDL statements entered by a user, and how to obtain data from IDL variables. It performs the following actions:

1.	Creates a Main window with four client controls; a scrolling edit control to display text messages from IDL, a single line edit control to allow a user to enter an IDL command, a Send button to send the user command to IDL, and a Quit button to exit the application.

2.	Registers a callback function to handle text messages sent by IDL to the application.

3.	Initializes Callable IDL.

4.	Call IDL_Cleanup() when we receive the WM_CLOSE message.

Each line is numbered to make discussion easier. These numbers are not part of the actual program. The source code for this program can be found in the file simple.c, located in the callable subdirectory of the examples subdirectory of the external subdirectory of the IDL distribution. See the source code for details of the program not printed here.

1  /*-------------------------------------------------------------------

2  * simple.c Source code for sample IDL callable application

3  *

4  * Copyright (c) 1992-1995, Research Systems Inc.  All rights reserved.

9  *-------------------------------------------------------------------*/

10 #include <windows.h>

11 #include <windowsx.h>

12 #include <ctl3d.h>

13 #include <string.h>

14 #include <stdio.h>

15 #include "simple.h"

16 #include "export.h"

17

18 /*-------------------------------------------------------------------

19  * WinMain

20  *

21  * This is the required entry point for all windows applications.

22  *

23  * RETURNS:      TRUE if successful

24  *------------------------------------------------------------------*/

25 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hInstancePrev,

26     LPSTR lpszCmndline, int nCmdShow)

27 {

28     HWND              hwnd;

29     MSG               msg;

30

31     // Register the main window class.

32     if (!RegisterWinClass(hInstance)) {

33         return(0);

34     }

35

36         ...

37

38     // Create and display the main window.

39     if ((hwnd = InitMainWindow(hInstance)) == NULL) {

40         return(0);

41     }

42     MainhWnd = hwnd;

43

44     // Register our output function with IDL.

45     IDL_ToutPush(OutFunc);

46

47     // Initialize IDL

48     if (!IDL_Win32Init(hInstance, hwnd, NULL, 0))

49         return(FALSE);

50

51     // Main message loop.

52     while (GetMessage(&msg, NULL, 0, 0)) {

53       TranslateMessage(&msg);

54       DispatchMessage(&msg);

55     }

56

57     return(msg.wParam);

58 }

59

60 /*--------------------------------------------------------------------

61  * RegisterWinClass

62  *

63  * To create a Main window (TLB in IDL speak). You must first register

64  * the class for that window

65  *

66  * RETURNS:      TRUE if successful

67  *------------------------------------------------------------------*/

68 BOOL RegisterWinClass(HINSTANCE hInst)

69 {

70     WNDCLASS            wc;

71

72     wc.style            = CS_HREDRAW | CS_VREDRAW;

73     wc.lpfnWndProc      = MainWndProc;

74     wc.cbClsExtra       = 0;

75     wc.cbWndExtra       = 0;

76     wc.hInstance        = hInst;

77     wc.hIcon            = NULL;

78     wc.hCursor          = LoadCursor(NULL, IDC_ARROW);

79     wc.hbrBackground    = (HBRUSH)(COLOR_BTNFACE + 1);

80     wc.lpszMenuName     = NULL;

81     wc.lpszClassName    = "Simple";

82

83     if (!RegisterClass(&wc)) {

84         return(FALSE);

85     }

86

87     return(TRUE);

88 }

89

90 /*------------------------------------------------------------------

91  * InitMainWindow

92  *

93  * This is where our Main window is created and displayed

94  *

95  * RETURNS:      Handle to window

96  *------------------------------------------------------------------*/

97 HWND InitMainWindow(HINSTANCE hInst)

98 {

99     HWND                hwnd;

100    CREATESTRUCT        cs;

101

102

103    hwnd = CreateWindow("Simple",

104               "Callable IDL Sample Application",

105               WS_DLGFRAME | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE,

106               CW_USEDEFAULT,

107               0,

108               600,

109               480,

110               NULL,

111               NULL,

112               hInst,

113               &cs);

114

115    if (hwnd) {

116       ShowWindow(hwnd, SW_SHOWNORMAL);

117       UpdateWindow(hwnd);

118    }

119

120    return(hwnd);

121 }

122

123 /*------------------------------------------------------------------

124  * MainWndProc

125  *

126  * This is the window procedure (event handler) for our main window.

127  * All messages (events) sent to our app are routed through here

128  *

129  * RETURNS:          Depends of message.

130  *-----------------------------------------------------------------*/

131 LRESULT WINAPI MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

132 {

133    static int               nDisplayable = 0;

134

135

136    switch (uMsg) {

137       //When our app is first created, we are sent this message.

138       //We take this opportunity to create our child controls and

139       //place them in their desired locations on the window.

140       case WM_CREATE:

141        if (!CreateControls(((LPCREATESTRUCT)lParam)->hInstance, hwnd)) {

142            return(0);

143        }

144        if (!LayoutControls(hwnd)) {

145            return(0);

146        }

147        nDisplayable = GetCharacterHeight(GetDlgItem(hwnd, IDE_COMMANDLOG));

148        break;

149

150        ...

151

152       case WM_DESTROY:

153           PostQuitMessage(1);

154           break;

155

156       //Each time a button or menu item is selected, we get this message

157       case WM_COMMAND:

158           OnCommand(hwnd, LOWORD(wParam), wParam, lParam);

159           return(FALSE);

160

161       //This is a message we send ourselves to indicate the need to

162       //display a text message in our log window.

163       case IDL_OUTPUT:

164           OutputMessage(wParam, lParam, nDisplayable);

165           return(FALSE);

166

167       case WM_CLOSE:

168           IDL_Cleanup(TRUE);

169           return(FALSE);

170

171       default:

172           break;

173    }

174

175    return(DefWindowProc(hwnd, uMsg, wParam, lParam));

176 }

177

178 /*------------------------------------------------------------------

179  * OnCommand

180  *

181  * This is the message handle for our WM_COMMAND messages

182  *

183  * RETURNS:          FALSE

184  *-----------------------------------------------------------------*/

185 BOOL OnCommand(HWND hWnd, UINT uId, WPARAM wParam, LPARAM lParam)

186 {

187

188    switch(uId){

189       case IDB_SENDCOMMAND:{

190           LPSTR     lpCommand;

191           LPSTR     lpOut;

192

193           lpCommand = GlobalAllocPtr(GHND, 256);

194           lpOut = GlobalAllocPtr(GHND, 256);

195           if(!lpCommand)

196              return(FALSE);

197

198           /* First we get the string that is in the input window */

199           GetDlgItemText(hWnd, IDE_COMMANDLINE, lpCommand, 255);

200

201           /* and then clear the window */

202           SetDlgItemText(hWnd, IDE_COMMANDLINE, "");

203

204           lstrcpy(lpOut, "\r\nSent to IDL: ");

205           lstrcat(lpOut, lpCommand);

206

207           /* Send the string to our "log" window */

20           OutFunc(IDL_TOUT_F_NLPOST, lpOut, strlen(lpOut));

209

210           /* then send the string to IDL */

211           IDL_ExecuteStr(lpCommand);

212

213           /* Now clean up */

214           GlobalFreePtr(lpCommand);

215           GlobalFreePtr(lpOut);

216     }

217           break;

218  }

219  return(FALSE);

220 }

221

222 /*----------------------------------------------------------------

223  * OutFunc

224  *

225  * This is the output function that receives messages from IDL and

226  * displays them for the user

227  *

228  * RETURNS:          NONE

229  *-----------------------------------------------------------------*/

230 void OutFunc(long flags, char *buf, long n)

231 {

232    static    fShowMain = FALSE;

233

234    /* If there is a message, post it to our MAIN window */

235    if (n){

236        SendMessage (MainhWnd, IDL_OUTPUT, 0, (LPARAM)buf);

237    }

238

239    /* If we need to post a new line message... */

240    if (flags & IDL_TOUT_F_NLPOST){

241        SendMessage (MainhWnd, IDL_OUTPUT, 0, (LPARAM)(LPSTR)"\r\n\0");

242    }

243

244    /* This message gets sent to the log window to have it scroll

245       and display the last message at the bottom of the window.

246       With this, the user will always see the last screen full of

247       messages send

248    */

249    SendMessage (MainhWnd, IDL_OUTPUT, (WPARAM)TRUE,

250                 (LPARAM)(LPSTR)"\0");

251

252    return;

253 }

254

255 /*-------------------------------------------------------------------

256  * OutputMessage

257  *

258  * Here we do the actual display of the text to our log window

259  *

260  * RETURNS:          nothing

261  *

262  *-----------------------------------------------------------------*/

263 void OutputMessage(WPARAM wParam, LPARAM lParam, int nDisplayable)

264 {

265    LRESULT   lRet;

266    LONG      lBufflen, lNumLines, lFirstView;

267

268    /* Turn off the READONLY bit and postpone redraw */

269    lRet = SendMessage(hwndLog, EM_SETREADONLY, FALSE, 0L);

270    lRet = SendMessage(hwndLog, WM_SETREDRAW, FALSE, 0L);

271

272    /* Get the length of the text in the log window*/

273    lBufflen = SendMessage (hwndLog, WM_GETTEXTLENGTH, 0, 0L);

274    lNumLines = SendMessage (hwndLog, EM_GETLINECOUNT, 0, 0L);

275    lFirstView = SendMessage (hwndLog, EM_GETFIRSTVISIBLELINE, 0, 0L);

276    lRet = SendMessage (hwndLog, EM_SETSEL, lBufflen, lBufflen);

277

278    /* If we are adding text, wParam will be 0 */

279    if(!wParam)

280       lRet = SendMessage (hwndLog, EM_REPLACESEL, 0, lParam);

281    else{

282       if (lNumLines > (lFirstView + nDisplayable)){

283            int        iLineLen = 0;

284            int        iChar;

285            int        iLines = 0;

286            lNumLines--;

287            while(!iLineLen){

288                iChar = SendMessage(hwndLog, EM_LINEINDEX,

289                        (WPARAM)lNumLines, 0L);

290                iLineLen = SendMessage(hwndLog, EM_LINELENGTH,

291                        iChar, 0L);

292                if(!iLineLen)

293                    lNumLines--;

294            }

295            iLines = lNumLines-(lFirstView + (nDisplayable - 1));

296            iLines = iLines >= 0 ? iLines : 0;

297            SendMessage (hwndLog, EM_LINESCROLL, 0, (LPARAM)iLines);

298       }

299    }

300

301    /* Set the window to redraw and reset the READONLY bit */

302    lRet = SendMessage(hwndLog, WM_SETREDRAW, TRUE, 0L);

303    lRet = SendMessage(hwndLog, EM_SETREADONLY, TRUE, 0L);

304

305    return;

306 }

The following is a commentary on the program, by line number:

16

export.h contains the IDL_ function prototypes, IDL specific structures, and IDL constants.

45

Call IDL_ToutPush() with the address of the output function (OutFunc) as it's only argument. This will register OutFunc as a callback for IDL. IDL will call OutFunc when it needs to display text.

48

Initialize IDL with the handle to the main window and the HINSTANCE of the application.

52

Start the windows message loop.

131-176

This is the Main window procedure. It will handle any messages that are sent to the main window. This includes WM_COMMAND messages that occur as a result of user interaction with the client controls. In addition, it handles a user defined message called IDL_OUTPUT (the name doesn't matter but this is a clue as to its purpose).

158

When the user presses either the "Send" or "Quit" buttons, route the message to the OnCommand function.

164

When we receive an IDL_OUTPUT message, call the function that displays text in the scrolling window (OutputMessage. See line 263).

168

When we receive the WM_CLOSE message, call IDL_Cleanup() to unlink IDL from our application.

185-220

OnCommand handles the WM_COMMAND messages generated when the user clicks on the application's buttons.

199

Get the IDL command that the user has entered in the single line edit control and store it in a buffer.

202

Clear the text in the edit control.

208

Call the IDL_TOUT_ function to display the command sent to IDL in the output window.

211

Call IDL_ExecuteStr() with the IDL command retrieved in line 199.

230-253

OutFunc is the callback registered with IDL to handle text messages IDL sends to our application. In addition it will handle text from IDL routines that display information, such as PRINT.

263-306

OutputMessage handles displaying the text to the output window. Since this window is a multi-line edit control, we have created it as a read-only window. See the source code for additional information on handling this situation.

280

OutputMessage appends new messages to the existing text in the control.

281-299

When the text has been displayed, OutputMessage scrolls the window to display the last line of text in the bottom of the window.


AppleScript Support
16	AppleScript Support

Chapter Overview

IDL for Macintosh provides support for AppleScript, allowing IDL to control, and be controlled by, other applications running on the Macintosh. See Apple's AppleScript documentation for information on the basics of using AppleScript

Procedures used when calling the IDL for Unix and IDL for VMS sharable objects are covered in Chapter 14, "Using Callable IDL under Unix and VMS". Procedures used when calling the IDL DLL under Microsoft Windows are covered in Chapter 15, "Using Callable IDL under Windows".


Topics in AppleScript Support Chapter
AppleScript Support

This chapter describes controlling IDL for Macintosh via AppleScript commands.

Topics Covered:

Basic AppleScript Support

Executing IDL Commands from AppleScript

Moving Data to and from IDL

Controlling Other Macintosh Applications

IDL Apple Events

References

Jump forward to Adding External Widgets to IDL

Jump backward to Using Callable IDL under Windows


Basic AppleScript Support

IDL for Macintosh supports the four basic AppleScript commands that all applications are required to support. These allow you to launch and quit IDL, and to open and print documents.

Launching IDL

To launch IDL from AppleScript, use the AppleScript command:

tell application "IDL" to activate

Quitting IDL

To cause IDL to quit, use the AppleScript command:

tell application "IDL" to quit

Opening Documents

To have IDL open the text document test.pro on the disk Macintosh HD, use the AppleScript command:

tell application "IDL" to open file "Macintosh HD:test.pro"

Printing Documents

To have IDL print the text document test.pro on the disk Macintosh HD use the AppleScript command:

tell application "IDL" to print file "Macintosh HD:test.pro"


Executing IDL Commands from AppleScript

IDL for Macintosh supports the AppleScript do script command. IDL responds to arbitrary text passed to it via AppleScript as if the text were entered at the IDL command prompt. Note that IDL must be waiting for input from the command line to accept the do script command from AppleScript.

For example, to instruct IDL to create an array of floating-point integers with the value of each element equal to its index and plot the array, use the following AppleScript command:

tell application "IDL" to do script "plot, findgen(20)"

Note

IDL executes commands it receives from AppleScript in the context of the currently active routine. Normally, this is the MAIN routine when IDL is waiting for input from the command line. If, however, IDL has stopped inside another routine (if a routine is active in the debugger, for example), the do script command will be executed in that routine's context.


Moving Data to and from IDL

IDL supports the AppleScript get, set, and copy commands to move data to and from IDL variables. To retrieve the value of a variable with the get or copy commands, the variable must already exist in the currently active routine. When setting the value of a variable with the set or copy commands, the variable will be created automatically in the currently active routine if it does not already exist.

For example, the following AppleScript command retrieves the value of the IDL variable "v" and save it in the AppleScript variable v:

tell application "IDL" to get variable "v"

The following AppleScript command sets the value of the IDL variable "v" to the array [1, 2, 3]. If the IDL variable "v" does not exist, it is created.

tell application "IDL" to set variable "v" to {1, 2, 3}

The following AppleScript command copies the array [1, 2, 3] into the IDL variable "v". If the IDL variable "v" does not exist, it is created.

tell application "IDL" to copy { 1, 2, 3 } to variable "v"

Notes

•	IDL cannot get data from or move data to single- or double-precision complex variables or structure variables.

•	In IDL, arrays are indexed in row-major format, meaning that the linear order of the references to data elements proceeds from the first element of the first row through the last element of the first row before beginning on the second row, and so on. AppleScript indexes arrays in column-major format, which means that the linear order of the references to data elements proceeds from the first element in the first column through the last element in the first column before beginning on the second column, and so on. Use the IDL routine TRANSPOSE to reverse the indexing order before passing the data using AppleScript.

•	Passing large arrays using AppleScript is very inefficient, and is not recommended. In most cases it is more efficient to pass data in a file.

•	Since it is not possible to determine what routine in IDL is executing from outside IDL, we recommend that you use the get, set and copy commands by executing an AppleScript script from within IDL, using the DO_APPLE_SCRIPT procedure. See "Controlling Other Macintosh Applications" on page 16-3 for further information.


Controlling Other Macintosh Applications

IDL can execute arbitrary AppleScript scripts using the DO_APPLE_SCRIPT routine.

Importing Data into IDL

The following example shows how to use IDL to get data from a Microsoft Excel spreadsheet and plot it using IDL's surface command.

script = [ 'tell application "Microsoft Excel"', $

           'get value of range "R1C1:R5C5" of worksheet 1', $

           'end tell' ]

DO_APPLE_SCRIPT, script, RESULT = a

SURFACE, a

Exporting Data from IDL

The next example shows how to copy data from an IDL variable to Microsoft Excel. Once again, we work within IDL (using the DO_APPLE_SCRIPT procedure) so there is no confusion as to what routine context the variable comes from.

a = [ 1, 2, 3, 4, 5 ]

script = [ 'tell application "IDL" to copy variable "a" into t', $

          'tell application "Excel"', $

          'copy t to value of range "R1C1:R5C1" of worksheet 1', $

          'end tell' ]

DO_APPLE_SCRIPT, script

Controlling Other Applications

This example is shows how to control the Metrowerks CodeWarrior application from within IDL. Note that a file name can be built in IDL and passed to another application using AppleScript.

First, use IDL to build a path name:

example = $

    FILEPATH('Call_Demo_PPC.proj', subdir = [ 'External', $

    'Examples', 'ShareLib' ])

Build an AppleScript script to run Metrowerks CodeWarrior:

script = [ $

  'with timeout of 600 seconds', $

  '  tell application "MW C/C++ PPC 1.2.1"', $

  '    activate', $

  '    open file "' + example + '"', $

  '    make project', $

  '    close project', $

  '    quit', $

  '  end tell', $

  'end timeout' ]

Execute the script:

DO_APPLE_SCRIPT, script


IDL Apple Events

Application programs can communicate directly with IDL using Apple events. It is beyond the scope of this document to discuss how to write programs that use Apple events. For a discussion of this topic refer to Inside Macintosh: Interapplication Communication and Apple Event Registry: Standard Suites. This section discusses the three Apple events that IDL accepts.

Note: IDL understands the Apple events that support the get, set, copy, and do script AppleScript statements. In most cases, it is easier to use AppleScript as described above than to use the underlying Apple events. Note also that the copy AppleScript statement is implemented (by AppleScript) using the Get Data and Set Data Apple events. There is no Copy Data Apple event.

Do Script

The Do Script Apple event is used to ask IDL to perform actions specified in a script. IDL executes the specified text as if it were typed at the command line.

Event Class	kAEMiscStandards
Event ID	kAEDoScript
Parameters
keyDirectObject
	Description:	The script to execute
	Descriptor type:	typeChar
	Required or Optional:	Required
Reply Parameters
keyErrorNumber
	Description:	The error code
	Descriptor Type:	typeLongInteger
	Required or Optional:	Optional (Only returned if an error occurred)
keyErrorString
	Description:	A character string describing the error
	Descriptor Type:	typeChar
	Required or Optional:	Optional (Only returned if an error occurred)
Notes	IDL only accepts the text version of this Apple event. It does not accept an alias record for specifying a file to execute.
Result Codes
	1	IDL Interpreter busy
	2	IDL statement too long
Get Data

The Get Data Apple event retrieves data from a specified IDL variable. Note that the specified variable must exist in the currently executing IDL routine, or in the MAIN context if no routine is executing.

Event Class	kAECoreSuite
Event ID	kAEGetData
Parameters
keyDirectObject
	Description:	The name of the variable
	Descriptor Type:	typeObjectSpecifier
	Required or Optional:	Required
keyAEKeyForm
	Description:	The type of object specifier (must be of type formName)
	Descriptor Type:	typeEnumerated
	Required or Optional:	Required
keyAEKeyData
	Description:	The actual variable name
	Descriptor Type:	typeChar
	Required or Optional:	Required
Reply Parameters
keyAEResult
	Description:	The variable's data
	Descriptor Type:	Either a single descriptor type or typeAEList
	Required or Optional:	Required
keyErrorNumber
	Description:	The error code
	Descriptor Type:	typeLongInteger
	Required or Optional:	Optional (Only returned if an error occurred)
keyErrorString
	Description:	A character string describing the error
	Descriptor Type:	typeChar
	Required or Optional:	Optional (Only returned if an error occurred)
Notes	IDL only accepts an object specifier record that specifies the variable by name.
	IDL cannot return data from single- or double-precision complex variables or structure variables.
	IDL indexes data in row-major format. See note under "Do Script" on page 16-5 for details.
	Passing large arrays using AppleScript is very inefficient, and is not recommended. In most cases it is more efficient to pass data in a file.
	Since it is not possible to determine what routine in IDL is executing from outside IDL, we recommend that you use the get, set and copy commands by executing an AppleScript script from within IDL, using the DO_APPLE_SCRIPT procedure. See "Controlling Other Macintosh Applications" on page 16-3 for further information.
	IDL does not support the optional parameter keyAERequestedType (`rtyp'). The returned data type is always the closest match to the IDL variable's data type.
Result Codes
	errAECoercionFail (-1700)	Couldn't convert variable to AppleScript type
	memFullErr (-108)	Not enough memory to get variable
	-2	Variable not specified by name
	-1	Invalid variable name
	3	Variable undefined
Set Data

The Set Data Apple event copies data to a specified IDL variable in the currently executing routine, or in the MAIN context if no routine is executing.

Event Class	kAECoreSuite
Event ID	kAEGetData
Parameters
keyDirectObject
	Description:	The name of the variable
	Descriptor Type:	typeObjectSpecifier
	Required or Optional:	Required
keyAEKeyForm
	Description:	The type of object specifier (must be of type formName)
	Descriptor Type:	typeEnumerated
	Required or Optional:	Required
keyAEKeyData
	Description:	The actual variable name.
	Descriptor Type:	typeChar
	Required or Optional:	Required
keyAEData
	Description:	The data to be copied into the variable
	Descriptor Type:	Either a single descriptor type or typeAEList
	Required or Optional:	Required
Reply Parameters
keyErrorNumber
	Description:	The error code
	Descriptor Type:	typeLongInteger
	Required or Optional:	Optional (Only returned if an error occurred)
keyErrorString
	Description:	A character string describing the error
	Descriptor Type:	typeChar
	Required or Optional:	Optional (Only returned if an error occurred)
Notes	IDL only accepts an object specifier record that specifies the variable by name.
	IDL cannot return data from single- or double-precision complex variables or structure variables.
	IDL indexes data in row-major format. See note under "Do Script" on page 16-5 for details.
	Passing large arrays using AppleScript is very inefficient, and is not recommended. In most cases it is more efficient to pass data in a file.
	Since it is not possible to determine what routine in IDL is executing from outside IDL, we recommend that you use the get, set and copy commands by executing an AppleScript script from within IDL, using the DO_APPLE_SCRIPT procedure. See "Controlling Other Macintosh Applications" on page 16-3 for further information.
Result Codes
	errAECoercionFail (-1700)	Couldn't convert variable to AppleScript type
	memFullErr (-108)	Not enough memory to get variable
	-2	Variable not specified by name
	-1	Invalid variable name
	3	Couldn't create variable (variable undefined)


References

Schneider, Derrick. The Tao of AppleScript. Carmel, IN: Hayden Books, 1993. ISBN 1-56830-075-1

The following books, available from Apple Computer, may also be of interest:

Inside Macintosh: Interapplication Communication

Apple Event Registry: Standard Suites


Adding External Widgets to IDL
17	Adding External Widgets to IDL

Chapter Overview

This chapter describes an IDL widget type not documented in the IDL Reference Guide, called the stub widget. It also describes a small set of internal functions to manipulate stub widgets. Stub widgets allow CALL_EXTERNAL, LINKIMAGE, and Callable IDL users to add their own widgets to IDL widget hierarchies.

This feature does not always work with versions of IDL that statically link against the window system libraries, particularly those for Sun workstations. When Solaris 2.x ships from Sun with sharable Motif libraries, this limitation will disappear.

The next two sections describe IDL's WIDGET_STUB function and changes to WIDGET_CONTROL when used with WIDGET_STUB. "Internal Functions for Use with Stub Widgets" on page 17-5 describes support functions that can be called from your external code to manipulate stub widgets. "Internal Callback Functions" on page 17-6 describes how to make stub widgets generate IDL widget events. Finally, "VMS Example Using WIDGET_STUB" on page 17-7 illustrates the use of stub widgets with an external program.


Topics in Adding External Widgets to IDL Chapter
Adding External Widgets to IDL

This chapter describes stub widgets and internal functions that manage them.

Topics Covered:

WIDGET_STUB

WIDGET_CONTROL Used with WIDGET_STUB

Internal Functions for Use with Stub Widgets

Internal Callback Functions

VMS Example Using WIDGET_STUB

Jump forward to Obsolete Internal Interfaces

Jump backward to AppleScript Support


WIDGET_STUB

The WIDGET_STUB function creates a widget record that contains no actual underlying widgets. Stub widgets are place holders for integrating external widget types into IDL. Events from those widgets can then be processed in a manner consistent with the rest of the IDL widget system.

First, the programmer calls WIDGET_STUB to create the widget, and then uses CALL_EXTERNAL to call additional custom code to handle the rest. A number of internal functions are provided to manipulate widgets from this custom code. See "Internal Functions for Use with Stub Widgets" on page 17-5.

The returned value of this function is the widget ID of the newly-created stub widget.

Calling Sequence

Result = WIDGET_STUB(Parent)

Arguments

Parent

The widget ID of the parent widget. Stub widgets can only have bases or other stub widgets as their parents.

Keywords

The following keywords are accepted by WIDGET_STUB and work the same as for other widget creation functions:

EVENT_FUNC
EVENT_PRO
FUNC_GET_VALUE
GROUP_LEADER
KILL_NOTIFY
NO_COPY
PRO_SET_VALUE
SCR_XSIZE
SCR_YSIZE
UVALUE
XOFFSET
XSIZE
YOFFSET
YSIZE


WIDGET_CONTROL Used with WIDGET_STUB

The WIDGET_CONTROL procedure has some differences and limitations when used with WIDGET_STUB that are not documented in the IDL Reference Guide. These differences are described below.

Keywords

Only the most general keywords are allowed with WIDGET_CONTROL when used with stub widgets. All other keywords are ignored. Here is a list of those keywords that behave identically with all widgets including stub widgets:

BAD_ID
CLEAR_EVENTS
EVENT_FUNC
EVENT_PRO
FUNC_GET_VALUE
GET_UVALUE
GROUP_LEADER
HOURGLASS
ICONIFY
KILL_NOTIFY
MANAGED
NO_COPY
PRO_SET_VALUE
RESET
SET_UVALUE
SHOW
TIMER
TLB_GET_OFFSET
TLB_GET_SIZE
TLB_SET_TITLE
TLB_SET_XOFFSET
TLB_SET_YOFFSET
XOFFSET
YOFFSET

The following keywords also work with stub widgets, but require additional commentary:

DESTROY

When a widget hierarchy containing stub widgets is destroyed, the following steps are taken:

•	The lower-level code that deals with the system toolkit destroys any real widgets currently used by the stub widgets.

•	All IDL widget records are added to the free list for re-use.

•	Any requested KILL_NOTIFY callbacks are called.

You should register KILL_NOTIFY callbacks on the topmost stub widget in each widget subtree. Remember that the actual widgets are gone before the callbacks are issued, so don't attempt to access them. However, the callback provides an opportunity to clean up any related resources used by the widget.

MAP, REALIZE, and SENSITIVE

These keywords cause the toolkit-specific, lower layer of the IDL widgets implementation to be called. In the process of satisfying the specified request, any real widgets used by the stub widgets will be processed, along with the ones created by the non-stub widgets, in the usual way. Any additional processing must be provided via CALL_EXTERNAL.

XSIZE, SCR_XSIZE, YSIZE, and SCR_YSIZE

These keywords inform IDL how large the stub widget is expected to be. This information is necessary for IDL to calculate sizes and offsets of the surrounding widgets.

IDL tries to do something reasonable with these requests but, without knowledge of the actual widget being manipulated, it is possible that the results will not be satisfactory. In such cases, the IDL_WidgetStubSetSizeFunc() function can be used to specify a routine that IDL can call to perform the necessary sizing for your stub widget.


Internal Functions for Use with Stub Widgets

The following functions present a highly simplified interface to the stub widget class that gives the user enough access to IDL widget internals to make the stub widget work but hides the bulk of the actual internals. These functions are exported by the IDL program for use by CALL_EXTERNAL code, but are not advertised in export.h.

void IDL_WidgetStubLock(int set)

IDL event processing occurs asynchronously, so any code that manipulates widgets must execute in a protected region. This function is used to create such a region. Any code that manipulates widgets must be surrounded by two calls to IDL_WidgetStubLock() as follows:

IDL_WidgetStubLock(TRUE);

   /* Do your widget stuff */

IDL_WidgetStubLock(FALSE);

char *IDL_WidgetStubLookup(IDL_ULONG id)

When IDL creates a widget, it returns an integer value to the caller of the widget creation function. Internally, however, IDL widgets are represented by a pointer to memory. The IDL_WidgetStubLookup() function is used to translate the user-level integer value to this memory pointer. All the other internal routines use the memory pointer to reference the widget.

Id is the integer returned at the user level. Your call to CALL_EXTERNAL should pass this integer to your C-level code for use with IDL_WidgetStubLookup() which translates the integer to the pointer.

If the specified id does not represent a valid IDL widget, this function returns NULL. This situation can occur if a widget was killed but its integer handle is still lingering somewhere.

void IDL_WidgetIssueStubEvent(char *rec, LONG value)

Given a handle to the IDL widget, obtained via IDL_WidgetStubLookup(), this function queues an IDL WIDGET_STUB_EVENT. Such an event is a structure that contains the three standard fields (ID, TOP, and HANDLER) as well as an additional field named VALUE that contains the specified value.

VALUE can provide a way to access additional information about the widget, possibly by providing a memory address to the information.

void IDL_WidgetSetStubIds(char *rec, unsigned long t_id, unsigned long b_id)

IDL widgets are built out of one or more actual widgets. Every IDL widget carries two pointers that are used to locate the top and bottom real widget for a given IDL widget. This function allows you to set these top and bottom pointers in the stub widget for later use.

Since the actual pointer type differs from toolkit to toolkit, this function declares t_id (the top real widget) and b_id (the bottom real widget) as unsigned long, an integer data type large enough to safely contain any pointer. Use a C cast operator to handle the difference.

After calling WIDGET_STUB to create an IDL stub widget, you will need to use CALL_EXTERNAL to call additional code that creates the real widgets that represent the stub. Having done that, use IDL_WidgetSetStubIds() to save the top and bottom widget pointers.

void IDL_WidgetGetStubIds(char *rec, unsigned long *t_id, unsigned long *b_id)

This function returns the top (t_id) and bottom (b_id) real widget pointers for any specified widget (not just stub widgets). When using these values for non-stub widgets, it is the caller's responsibility to avoid damaging the IDL-created widgets in any way.

void IDL_WidgetStubSetSizeFunc(char *rec, IDL_WIDGET_STUB_SET_SIZE_FUNC func)
 typedef void (* IDL_WIDGET_STUB_SET_SIZE_FUNC) (IDL_ULONG id, int width, int height);

When IDL needs to set the size of a stub widget, it attempts to set the size of the bottom real widget to the necessary dimensions. Often, this is the desired behavior, but cases can arise where it would be better to handle sizing differently. In such cases, use IDL_WidgetStubSetSizeFunc() to register a function that IDL will call to do the actual sizing.


Internal Callback Functions

Real widget toolkits (upon which IDL widgets are built) are event driven. C language programs register interest in specific events by providing callback functions that are called when that event occurs. All but the most basic of widgets are capable of generating events.

In order for IDL stub widgets to generate IDL events, you must use CALL_EXTERNAL to invoke code that sets up real widget event callbacks for the events you are interested in. This setup can be done as part of creating the real widgets after the initial call to WIDGET_STUB. These callbacks then call IDL_WidgetIssueStubEvent() to issue the IDL event.

Your C-language widget toolkit callback functions should be patterned after the following template. Note that the arguments and return type will depend on the widget toolkit used, and so cannot be shown here:

stub_widget_call()
{
  char *idl_widget;

  IDL_WidgetStubLock(TRUE);

    /* Get the IDL user-level identifier for this widget */
    if (idl_widget = IDL_WidgetStubLookup(id)) {

       /* Do whatever work is required */
        ...

       /* Optionally, issue an IDL event */ 	        IDL_WidgetIssueStubEvent(idl_widget, value)
    }

  IDL_WidgetStubLock(FALSE);
}

Commentary on the Example Shown Above

Note that IDL_WidgetStubLock() is used to protect the critical section where widgets are being manipulated.

Somehow, the callback must be able to find the user-level integer returned by WIDGET_STUB when the stub widget was created in IDL. Usually, this is done in one of two ways:

•	When registering the callback, it is sometimes possible to specify a value that will be passed to the callback without interpretation. For example, the X windows XtAddAppCallback() function takes an argument named client_data. This value is passed to the callback and can be used to supply the user-level identifier.

•	Some widget toolkits have a set of attributes that they carry along with each widget. Under the X windows Xt toolkit, these attributes are called resources. Xt widgets usually have a resource capable of holding a single integer or memory address. This resource can be used to supply the user level identifier.

IDL_WidgetStubLookup() is used to translate the user level widget identifier into a memory pointer. If this function returns NULL, no further event processing is done since it would be a fatal error to issue an IDL event for a non-existent widget.

The event is issued via IDL_WidgetIssueStubEvent(). This step is not required. Many of the IDL widget types process real widget events via callbacks that do not always result in an IDL widget event being sent.


VMS Example Using WIDGET_STUB

The following example adds the Motif ArrowButton widget to the VMS version of IDL in the form of an IDL program named widget_arrowb.pro. It would be straightforward to do the same with any version of IDL supporting CALL_EXTERNAL.

The WIDGET_ARROWB widget implemented below acts like a normal pushbutton. Events are sent when the button is pressed (VALUE=1) and released (VALUE=0). If the USE_OWN_SIZE keyword is set to zero, IDL performs its default sizing on the stub widget. A non-zero value causes a special routine provided by the WIDGET_ARROWB implementation to be registered to handle such sizing.

The IDL Program for WIDGET_ARROWB

The following text is the IDL program for WIDGET_ARROWB. It should be saved in a file named WIDGET_ARROWB.PRO:

FUNCTION widget_arrowb, parent, use_own_size, $
                        UVALUE=uvalue, _EXTRA=extra

parent = LONG(parent)
result = WIDGET_STUB(parent,_extra=extra)

if (N_ELEMENTS(uvalue) NE 0) THEN $
   WIDGET_CONTROL, result, SET_UVALUE=uvalue

JUNK = CALL_EXTERNAL('widget_arrowb','widget_arrowb', $
       def='diska:[idl.ali.arrowb].exe', parent, result, $
       use_own_size, value=[1, 1, 1])

RETURN, result
END

The C Program for widget_arrowb.c

The code invoked by the call to CALL_EXTERNAL is contained in a file named widget_arrowb.c (this file can be found in the widstub subdirectory of the examples subdirectory of the external subdirectory of the IDL distribution). The contents of this file are shown below:

/*
*
*
* arrowb.c - This file contains C code to be called from VMS IDL
* via CALL_EXTERNAL. It uses the IDL stub widget to add a
* Motif ArrowButton to an IDL-created widget hierarchy. The
* button issues a WIDGET_STUB_EVENT every time the button is
* released.
*
*/

#include <stdio.h>
#include <X11:keysym.h>   /* Keysyms for text widget events */
#include <X11:Intrinsic.h>
#include <X11:StringDefs.h>
#include <X11:Shell.h>
#include <Xm:ArrowB.h>
#include "idl_dir:[external]export.h"

/*ARGSUSED*/
static void arrowb_CB(Widget w, caddr_t client_data, caddr_t call_data)
{
  char *rec;
  XmArrowButtonCallbackStruct *abcs;

  IDL_WidgetStubLock(TRUE);

  if (rec = IDL_WidgetStubLookup((unsigned long) client_data)) {
    abcs = (XmArrowButtonCallbackStruct *) call_data;
    IDL_WidgetIssueStubEvent(rec, abcs->reason == XmCR_ARM);
  }

  IDL_WidgetStubLock(FALSE);
}

static void arrowb_size_func(int stub, int width, int height)
{
  char *stub_rec;
  unsigned long t_id, b_id;

  IDL_WidgetStubLock(TRUE);

  if (stub_rec = IDL_WidgetStubLookup(stub)) {
    IDL_WidgetGetStubIds(stub_rec, &t_id, &b_id);
    printf("Setting WIDGET %d to width %d and height %d\n",
           stub, width,height);
    XtVaSetValues((Widget) b_id, XmNwidth, width, XmNheight,
                  height, NULL);
  }

  IDL_WidgetStubLock(FALSE);
}

int widget_arrowb(IDL_LONG parent, IDL_LONG stub,
                  int use_own_size_func)
{
  Widget parent_w;
  Widget stub_w;
  char *parent_rec;
  char *stub_rec;
  unsigned long t_id, b_id;

  IDL_WidgetStubLock(TRUE);

  if ((parent_rec = IDL_WidgetStubLookup(parent))
      && (stub_rec = IDL_WidgetStubLookup(stub))) {
    /* Bottom widget of parent is parent to arrow button */ 	     IDL_WidgetGetStubIds(parent_rec, &t_id, &b_id);
    parent_w = (Widget) b_id;

    stub_w = XtVaCreateManagedWidget("arrowb",
                                     xmArrowButtonWidgetClass,
                                     parent_w, NULL);

    IDL_WidgetSetStubIds(stub_rec, (unsigned long) stub_w,
                         (unsigned long) stub_w);
    XtAddCallback(stub_w, XmNarmCallback, (XtCallbackProc) arrowb_CB,
                  (XtPointer) stub);
   XtAddCallback(stub_w, XmNdisarmCallback, (XtCallbackProc)arrowb_CB,
                 (XtPointer) stub);

    if (use_own_size_func)
        IDL_WidgetStubSetSizeFunc(stub_rec,arrowb_size_func);
  }

  IDL_WidgetStubLock(FALSE);
  return stub;
}

Compiling and Linking the C File

This C file is compiled and linked into a sharable image usable by CALL_EXTERNAL by a DCL command file named WIDGET_ARROWB.COM:

$ if "''f$search("SYS$SYSTEM:VAXVMSSYS.PAR")'" .eqs. ""
$ then
$ !     ALPHA
$       cc widget_arrowb.c
$       link/share widget_arrowb, sys$input/opt /exe=widget_arrowb.exe
                IDL_DIR:[BIN.BIN_ALPHA]idl/share
                SYS$SHARE:DECW$XMLIBSHR12.EXE/SHARE
                SYS$SHARE:DECW$XTLIBSHRR5.EXE/SHARE
                SYS$SHARE:DECW$XLIBSHR/SHARE
                SYMBOL_VECTOR=(widget_arrowb=PROCEDURE)
$ else
$ !     VAX
$       cc widget_arrowb.c
$       link /share widget_arrowb, sys$input/opt /exe=widget_arrowb.exe
        IDL_DIR:[BIN.BIN_VAX]IDL/SHARE
        SYS$SHARE:DECW$XMLIBSHR12.EXE/SHARE
        SYS$SHARE:DECW$XTLIBSHRR5.EXE/SHARE
        SYS$SHARE:DECW$XLIBSHR/SHARE
        sys$share:vaxcrtl/share
        universal = widget_arrowb
$ endif

Execute the file by entering:

$ @WIDGET_ARROWB

An IDL Program to Test the External Widget

Shown below is an IDL widget program to test the ARROWB widget. This program should be saved in a file called TEST.PRO. Note that one arrow button uses IDL's default sizing, while the other uses the WIDGET_ARROWB version:

PRO test_event, ev
WIDGET_CONTROL, GET_UVALUE=val, ev.id
IF (val EQ 0) THEN BEGIN
    WIDGET_CONTROL, /DESTROY, ev.top
ENDIF ELSE BEGIN
    HELP, /STRUCT, ev
    IF (ev.value EQ 1) THEN BEGIN
        WIDGET_CONTROL, val, SET_VALUE='New label string'
        tmp = WIDGET_INFO(ev.id,/GEOMETRY)
        WIDGET_CONTROL, XSIZE=tmp.xsize+25, YSIZE=tmp.ysize+25, ev.id
    ENDIF
ENDELSE
END

PRO test
a = WIDGET_BASE(/COLUMN)
b = WIDGET_BUTTON(a, VALUE='Done', UVALUE = 0)
label = WIDGET_LABEL(a, VALUE='A label')
arrow_w = WIDGET_ARROWB(a, 0, XSIZE=100, YSIZE=100, UVALUE=label)
arrow_w = WIDGET_ARROWB(a, 1, XSIZE=100, YSIZE=50, UVALUE=label)
WIDGET_CONTROL, /REALIZE, a
XMANAGER, 'TEST', a
END

Start IDL and run the test program by entering:

$ IDL

IDL> TEST


Obsolete Internal Interfaces
18	Obsolete Internal Interfaces

Chapter Overview

IDL for Unix and IDL for VMS provide support for code written against the IDL internal interfaces of previous versions of IDL. IDL for Windows and IDL for Macintosh are new enough that there is no legacy code of this nature in existence, so they do not provide these interfaces.


Topics in Obsolete Internal Interfaces Chapter
Obsolete Internal Interfaces

This chapter describes support for previous internal interfaces to IDL.

Topics Covered:

Compatibility of IDL Version 4 Interface with Versions 2 and 3

IDL for VAX/VMS Version 1 Compatibility

Jump backward to Adding External Widgets to IDL


Compatibility of the IDL Version 4 Interface with IDL Versions 2 and 3
Compatibility of IDL Version 4 Interface with Versions 2 and 3

Support for interface routines used by IDL Version 2 and IDL Version 3 is provided by a set of compatibility wrapper routines that recognize code written for the old interfaces and translates it into a form recognizable by the current interface. To use these compatibility routines, you should include the C header file obsolete.h at the top of your file:

#include <stdio.h>
#include "export.h"
#include "obsolete.h"

The obsolete.h file uses the C preprocessor to convert old constants, data types, and functions with variable numbers of arguments to their new names. It also contains prototypes for the compatibility wrapper functions.

Under Unix, the compatibility wrapper functions are contained in an additional sharable library named libidl_obsolete that must be linked against your code. Under VMS, they are contained in the IDL sharable executable along with the current interface.


IDL for VAX/VMS Version 1 Compatibility

The routines described in this section provide compatibility with the interface routines used by IDL for VMS Version 1. These routines should not be used when writing new code. They are documented here for the sole purpose of making the port of older code to the current version easier.

Data Type Codes

IDL Version 1 used a Fortran include file named TYPEDEF.PAR to define the type codes as Parameter Statements. Here is the contents of a version of that file that can be used to port existing code to the current version of IDL:

c TYPEDEF.PAR (Fortran Include File)
c
c Define type codes for VMS IDL Version 2. Define both the
c old Version 1 names and the current Version 2 names.
c
c New type names:
Parameter TYP_BYTE = 1, TYP_INT = 2, TYP_LONG = 3
Parameter TYP_FLOAT = 4, TYP_DOUBLE = 5, TYP_COMPLEX = 6
Parameter TYP_STRING = 7, TYP_STRUCT = 8
c
c Version 1 type names with Version 2 values:
Parameter TBYTE = 1, TINT = 2, TLONG = 3
Parameter TFLT=4, TDOUBL=5, TCOMPLEX = 6, TASCII = 7

Both the old and the new data type names are defined. The actual values that define the type codes, as well as the variable structure, have been changed. Programs that symbolically refer to the type codes need only be recompiled. Note that the compatibility routines referred to in the previous section require the newer type code values.

Routines

Table 18.1 is a summary of the IDL Version 1 VMS interface routines documented in the file WRITEYOUR.RNO (which was distributed with IDL Version 1) and their implementation in newer versions:

Version 1 Routine	Changes
BYTE	Renamed BYTE_V1
FIX	Renamed FIX_V1
FLOAT_IDL
LONG
DOUBLE
COMPLEX	Renamed COMPLEX_V1
STRING	Renamed STRING_V1
CONVERT_TYPE
FOR_CHKPAR	Routine must be called using CALL_V1
GETLON_SCL
GETFLOAT_SCL
GETDOUBLE_SCL
FOR_GETSTRING
FOR_GETTMP
FOR_GETDIMS
CREARR
TSTDELTMP
FOR_STORE_SCALAR
COPYVAR
PRINTERRMSG
IDLERR
IO_ERROR
FOR_IO_ERROR	Not implemented
BYTE1
INTEGER2
INTEGER4
GET_KBINT
STORE1
STORE2
STORE4
Table 18.1 IDL for VMS Version 1 Routines Mapped to Later Versions of IDL.

Calling Convention

IDL Version 1 modules are defined as follows:

SUBROUTINE XXX(P1, ...., Pn)

IDL Version 2 and later modules have the following Fortran definition:

SUBROUTINE XXX(N N, ARG LIST)
C ARG LIST is an array of pointers to VARIABLE structures:
INTEGER * 4 ARG LIST(*)
NARGS = %LOC(N N)

The routine CALL_V1 is provided in IDL Version 2 and later to convert the new calling sequence to the old. To use CALL_V1, you must write a short wrapper routine similar to the following, which calls the function XXX:

INTEGER*4 FUNCTION XXX WRAPPER( ARGC, ARGV )
EXTERNAL XXX
INTEGER*4 CALL_V1
XXX WRAPPER = CALL_V1(ARGC, ARGV, XXX)
RETURN
END
SUBROUTINE XXX(P1, P2, ..., Pn)
... ... ... Body of original routine XXX

The same method can be used to call procedures, which are defined as Fortran subroutines, rather than as functions.


IDL Advanced Development Guide

Contents

 IDL Online Help Navigator

 Overview

 Topics in Overview Chapter

 Inter-language Communication Techniques Supported by IDL

 When is it Appropriate to Add Code to IDL?

 Skills Required to Add Code to IDL

 Recommended Reading

 IDL Organization

 External Definitions

 Linking Details

 Reading the Remainder of this Book

 Types

 Topics in Types Chapter

 Type Codes

 Mapping of Basic Types

 Variables

 Topics in Variables Chapter

 The IDL_VARIABLE Structure

 Scalar Variables

 Array Variables

 Structure Variables

 Temporary Variables

 Creating an Array from Existing Data

 Getting Dynamic Memory

 Copying Variables

 Storing Scalar Values

 Obtaining the Name of a Variable

 Looking Up Main Program Variables by Name

 Looking Up Variables in Current Scope by Name

 Keyword Processing

 Topics in Keyword Processing Chapter

 Creating Routines that Accept Keyword Arguments

 The IDL_KW_PAR Structure

 The IDL_KW_ARR_DESC Structure

 Keyword Processing Options

 Processing Keywords

 Cleaning Up

 Keyword Examples

 String Processing

 Topics in String Processing Chapter

 Accessing IDL_STRING Values

 Copying Strings

 Deleting Strings

 Setting an IDL_STRING Value

 Obtaining an IDL_STRING of a Given Length

 Error Handling

 Topics in Error Handling Chapter

 Issuing Error Messages

 Specifying errno Explicitly

 Issuing VMS Messages

 Checking Arguments

 Type Conversion

 Topics in Type Conversion Chapter

 Converting Arguments to C Scalars

 General Type Conversion

 Converting Between Specific Types

 Files and Input/Output

 Topics in Files and Input/Output Chapter

 File Information

 File Information, continued

 Opening Files

 Closing Files

 Preventing File Closing

 Checking File Status

 Allocating and Freeing File Units

 Detecting End of File

 Flushing Buffered Data

 Reading a Single Character

 Output of IDL Variables

 Adding to the Journal File

 Signals

 Topics in Signals Chapter

 Signal Handlers

 Establishing a Signal Handler

 Removing a Signal Handler

 Unix Signal Masks

 Timers

 Topics in Timers Chapter

 Making Timer Requests

 Canceling Asynchronous Timer Requests

 Blocking Unix Timers

 Miscellaneous Information

 Topics in Miscellaneous Information Chapter

 Dynamic Memory

 Exit Handlers

 Access to IDL Global Data Under VAX/VMS"

 User Interrupts

 System Variables

 Terminal Information

 Type Information

 User Information

 Constants

 Macros

 Adding System Routines

 Topics in Adding System Routines Chapter

 The System Routine Interface

 A Hello World Example

 A More Complex Example

 Simplified Routine Invocation

 A Complete Example

 Registering Routines using IDL_AddSystemRoutine()

 Introduction to Callable IDL

 Topics in Introduction to Callable IDL Chapter

 How Callable IDL is Implemented on Different Platforms

 When is it Appropriate to use Callable IDL?

 Licensing Issues and Callable IDL

 Using Callable IDL

 Platform-specific Documentation for Callable IDL

 Using Callable IDL under Unix and VMS

 Topics in Using Callable IDL under Unix and VMS Chapter

 Initialization

 Diverting IDL Output

 Executing IDL Statements

 Cleanup

 Interactive IDL

 Compiling and Linking Programs that Call IDL

 Example - Using Callable IDL from C

 Example - Calling a Simple Mathematical Function

 Example - Using Callable IDL from Fortran

 Compilation and Linking Statements

 Using Callable IDL under Windows

 Topics in Using Callable IDL under Windows Chapter

 Initialization

 Diverting IDL Output

 Executing IDL Statements

 Cleanup

 Building a 32-bit Application that Calls IDL

 Example - A Simple Windows Application

 AppleScript Support

 Topics in AppleScript Support Chapter

 Basic AppleScript Support

 Executing IDL Commands from AppleScript

 Moving Data to and from IDL

 Controlling Other Macintosh Applications

 IDL Apple Events

 References

 Adding External Widgets to IDL

 Topics in Adding External Widgets to IDL Chapter

 WIDGET_STUB

 WIDGET_CONTROL Used with WIDGET_STUB

 Internal Functions for Use with Stub Widgets

 Internal Callback Functions

 VMS Example Using WIDGET_STUB

 Obsolete Internal Interfaces

 Topics in Obsolete Internal Interfaces Chapter

 Compatibility of the IDL Version 4 Interface with IDL Versions 2 and 3

 IDL for VAX/VMS Version 1 Compatibility