.help LIBC Sep84 "C Runtime Library" .sp 2 .ce \fBIRAF Runtime Library for the C Language\fR .ce CL Interface to IRAF .sp 3 .nh Introduction The IRAF runtime library for the C language was developed to port the IRAF command language (CL) from UNIX to IRAF. The C runtime library (LIBC) consists of two parts: an emulation of the standard i/o library provided for the C language on an UNIX host, and a library of "system calls", i.e., the C interface to the IRAF virtual operating system. .nh Naming Conventions Providing familiar and predictable procedure names in C is complicated by the possibility of name collisions with external names in the program interface libraries. To solve this problem while maintaining compatibility with UNIX the external names of all UNIX emulation procedures are assigned in C \fIdefine\fR statements. The external name is simply the UNIX name preceded by the prefix "u_", e.g., fopen() compiles as u_fopen() The names of the "system calls" are not compatible with those of the UNIX system calls. Each system call maps directly to an IRAF program interface procedure. The name of the C version is the IRAF name preceded by the prefix "c_", e.g., open() is called in C as c_open() The "c_" names are not redefined except where necessary to produce an external identifier unique in the first seven characters. When an external name is redefined to make it unique in the first seven characters this is done by application of the 4+1 rule, leaving the "c_" prefix as is. The calling sequences of the C system calls have been kept as compatible with the "crib sheet" versions as possible, even when a more convenient syntax could have been used for C. .nh Include Files C style global include files pose a problem in a portable system since machine dependent filenames cannot be used. This problem is sidestepped for LIBC by using the C language preprocessor to indirectly reference the global include files via a single master include file installed in the C system include file directory. The master include file is referenced as . The actual include files reside in the IRAF directory system (as does a copy of ) and hence are automatically ported with the system. The pathname to the LIBC global include files is arbitrary, but currently these files are stored in lib$libc. The technique used to access LIBC global include files in perhaps best explained by use of a simple example from the CL: .ks .nf #define import_spp global includes #define import_libc #define import_stdio #include #include "config.h" local includes #include "operand.h" #include "param.h" #include "task.h" .fi .ke The include file contains preprocessor control lines which load the include files referenced by "#define import_packagename" statements. In addition to being portable, this technique has the benefits of ensuring that the include files are loaded in the correct order and are not loaded more than once. The include file referenced by \fIimport_libc\fR should be included in every C source file which uses LIBC. In addition to loading the referenced include files, also includes definitions for the IRAF root directory IRAFDIR, the default image storage directory IMAGEDIR, and the name of the host operating system (e.g. "UNIX" or "VMS"). .nh UNIX Emulation All procedures in the UNIX standard i/o (stdio) package are emulated in libc. A listing of the calling sequences of all currently implemented procedures is given as an appendix. The syntax and semantics of these procedures have been kept as close to those of the V7 UNIX procedures as possible. .nh IRAF Program Interface Routines All i/o in the CL is implemented ultimately by calls to procedures in the IRAF program interface libraries. The UNIX emulation procedures discussed in the previous sections, for example, are reasonably portable C language packages which call C versions of the IRAF program interface routines. With few exceptions the C version of each procedure maps trivially to the corresponding program interface procedure. The main complication arises from the need to pack and unpack character strings when calling an SPP (Fortran) procedure from C. Read only arguments are passed by value for the convenience of the C language progammer. The full program interface contains on the order of a thousand procedures (including generics) and it would be prohibitively difficult to make them all available in C. We have therefore included only the packages actually used by the CL in the interface, and then only the most commonly used procedures in each package. All files which directly reference program interface procedures should include a reference to the C language include file \fBirafio.h\fR. .nh 2 File I/O (FIO) The fundamental unit of storage in both C and SPP is the \fBchar\fR, but unfortunately a char is not necessarily the same size in both languages. In the C version of FIO data is referenced in units of C chars (bytes), subject to the restriction that only an \fIintegral\fR number of SPP chars can be read and written at a time, and seeks must be aligned on a char boundary. If a nonintegral number of SPP chars are read or written, the interface will silently move the extra bytes necessary to fill out an SPP char, possibly writing beyond the end of the input buffer on a read. These problems are less serious than it might seem, however, since CL level i/o is predominantly text only (binary file i/o is not currently used in the CL). In keeping with the C language tradition, all FIO offsets are \fIzero\fR indexed, and all integer valued procedures return ERR as the function value in the event of an error. Pointer valued functions return NULL in the event of an error. Although only "significant" function values are shown in the calling sequences below, all procedures return a function value. .ks .nf High Level FIO: fd = c_open (vfn, mode, type) c_close (fd) c_flush (fd) c_fseti (fd, param, value) int = c_fstati (fd, param) stat = c_finfo (vfn, &fi) y/n = c_access (vfn, mode, type) c_delete (vfn) c_rename (old_vfn, new_vfn) c_mktemp (root, &fname, maxch) .fi .ke The "low level" FIO procedures perform binary file i/o and fill and flush the internal FIO file buffer. These procedures are called by the STDIO package and are not intended to be called directly by general CL code. Seeking is not implemented in STDIO due to the difficulty of implementing \fBfseek\fR in a portable system, but is not currently required anywhere in the CL. STDIO directly accesses the internal FIO buffer pointers via a data structure defined in \fBirafio.h\fR. .ks .nf Low Level FIO: nchars = c_read (fd, &buf, maxch) c_write (fd, &buf, nchars) c_seek (fd, loffset) loffset = c_note (fd) ch = c_filbuf (fd) ch = c_flsbuf (fd, ch) FILE = c_fioptr (fd) .fi .ke The file access modes and types are specified as in SPP, i.e., via predefined integer constants defined in \fIirafio.h\fR (READ_ONLY, NEW_FILE, etc.). Only a few \fBfset\fR options are implemented; these are likewise defined in \fIirafio.h\fR. The integer constants STDIN, STDOUT, etc. refer to FIO file descriptors, and should not be confused with \fBstdin\fR, \fBstdout\fR, etc., which reference STDIO file pointers. .nh 2 Environment Facilities The environment list is managed entirely by the program interface via the ENV package. The CL calls ENV procedures to create, modify, and access the environment list. The \fBc_propen\fR procedure in the program interface passes the environment list on to a connected child process at process creation time. .ks .nf nchars = c_envgets (name, &value, maxch) redef = c_envputs (name, &value) c_envmark (&sp) nredefs = c_envfree (sp) bool = c_envgetb (name) int = c_envgeti (name) c_envlist (out_fd, prefix, show_redefs) nscan = c_envscan (input_source) .fi .ke The following (non program interface) procedure is defined and used internally by the CL to lookup names in the environment list: strp = envget (name) .nh 3 Implementation Notes The environment list is maintained as a multi-threaded linked list. This provides the searching efficiency of a hash table plus stack like semantics for redefinitions and for freeing blocks of variables. There are two primary data structures internally, an array of pointers to the heads of the threads, and a buffer containing the list elements. These data structures are dynamically allocated and will be automatically reallocated at runtime if overflow occurs. The number of threads determines the hashing efficiency and is a compile time parameter. The \fBenvmark\fR and \fBenvfree\fR procedures mark and free storage on the environment list stack. All environment variables defined or redefined after a call to \fBenvmark\fR will be deleted and storage freed by a call to \fBenvfree\fR. If a redef is freed the next most recent definition becomes current. \fBEnvfree\fR returns as its function value the number of redefined variables uncovered by the free operation. The calling program must mark and free in the correct order or the environment list may be trashed. The \fBenvlist\fR procedure prints the environment list on a file. Redefined values will be printed only if so indicated. The environment list is printed as a list of \fBset\fR statements in most recent first order, i.e., .ks .nf set nameN=valueN set nameM=valueM ... set name1=value1 .fi .ke The \fBenvlist\fR function is used both to inspect the environment list and to pass the list on to a child process. Redefined variables are omitted when passing the list on to a child process, hence the order of definition does not matter. The output format is "prefix name=value", where the prefix string is supplied by the user. The \fBenvscan\fR function parses one or more \fBset\fR statements, calling \fBenvputs\fR to enter the SET declarations into the environment list. The argument is either a \fBset\fR declaration or a string of the form "set @filename", where "filename" is the name of a file containing \fBset\fR declarations. .nh 2 Process Control Separate facilities are provided for \fBconnected\fR and \fBdetached\fR processes. Virtually all process control is concerned with connected subprocesses, i.e., subprocesses running synchronously with the CL and communicating with the CL via bidirectional IPC channels. The only detached process in the system is the CL itself, when spawned as a background job by another (usually interactive) CL process. .nh 3 Connected Subprocesses A connected subprocess is connected with \fBpropen\fR and disconnected with \fBprclose\fR. The \fBpropen\fR procedure spawns the named process, connects the IPC channels to FIO file descriptors, then sends commands to the child process to initialize the environment and current working directory. Once connected the \fIin\fR and \fIout\fR file descriptors may be reopened with \fBfdopen\fR for UNIX style i/o to the subprocess. The \fBprclose\fR procedure sends the "bye" (shutdown) command to the child, waits for the child to terminate, and then returns the process termination status as the function value. Normal exit status is OK, otherwise a positive integer error code is returned. .ks .nf pid = c_propen (process, in, out) stat = c_prclose (pid) c_prsignal (pid, signal) c_prredir (pid, stream, new_fd) c_prupdate (message) .fi .ke To execute a task in a connected child process the CL writes a command to the \fIout\fR channel with a conventional \fBfputs\fR or other STDIO call. After starting the task the CL redirects its command input to the \fIin\fR channel of the task; conventional \fBfgets\fR or \fBgetc\fR calls are made to read commands from the task, until "bye" is received, signaling task termination. New \fBset\fR or \fBchdir\fR statements may be broadcast to all connected subprocesses at any time (except while actually executing a task resident in a connected subprocess) by a call to \fBprupdate\fR. While there is no way the CL can free space on the environment stack in a child process, it is possible to broadcast new redefinitions to all child processes if redefinitions should be uncovered by an \fBenvfree\fR call in the CL. The \fBprsignal\fR procedure is used to raise the interrupt exception X_INT in a connected child process. When the user types interrupt (e.g. ctrl/c) at the CL level, the CL interrupt exception handler signals the child process containing the external task currently in execution (if any), and then resumes processing of commands from the child. If a special exception handler is not posted in the child it will go through error restart, eventually sending the \fBerror\fR statement to the CL indicating abnormal termination of the task. Note that it is permissible for a child process to ignore the interrupt exception, or take special recovery actions if interrupt occurs. .nh 4 I/O Redirection Pseudofile i/o (\fBxmit\fR and \fBxfer\fR directives for the task's STDIN, STDOUT, etc.) is handled by the program interface transparently to the CL. By default the standard i/o streams of the child are connected to the identical streams of the parent (the CL). If redirection of a stream is desired the stream may be redirected in either of two ways: .ls .ls [1] A stdio stream may be redirected directly at the task level in the child process by including redirection information on the command line sent to the child to execute the task. This is the most efficient technique, and it should be used when appropriate, e.g., for pipes or whenever an output stream of an external task is explicitly redirected on the CL command line. The syntax of the task statement recognized by the IRAF Main is documented in the \fISystem Interface Reference Manual\fR. For example, to run a task with the standard output redirected to a pipe file: .ks .nf fprintf (out_fp, "%s %d > %s\n", taskname, STDOUT, pipefilename); .fi .ke Pipe files, by the way, are implemented as binary files for maximum flexibility and efficiency. This is acceptable since they are read and written only by the system. Very high i/o bandwidths are possible using direct i/o to a binary file. .le .ls [2] A stdio stream may be redirected at the CL level to any previously opened FIO file, e.g., to one of the CL's standard streams, to a text or binary file opened explicitly by the CL, or to another child process (e.g. redirection of the standard graphics output of the child to a graphics subprocess). This type of redirection requires the following steps by the CL: .ls .ls o Open local stream to which child's stream is to be redirected. .le .ls o Call \fBprredir\fR to map the child's stream to the local stream. .le .ls o When the task is run, include an argument of the form "N >" on the task command line, indicating that stream N has been redirected by the parent process (the file name is omitted). .le .ls o When the task terminates, or when the next task is run in the same process, restore the original i/o connection with another call to \fBprredir\fR. The default connection is established by the system only at \fBpropen\fR time. .le .le .le .le The I/O redirection mechanism permits messages to be shuffled from a child process deep in the process tree to a device owned by the CL, or from a child process in one branch of the process tree to a process in another branch of the tree. If raw mode is set on the STDIN stream in the child it will automatically be passed on to the process which physically reads the raw mode device. Asynchronous execution is possible so long as messages pass only one way. Synchronization occurs whenever a process waits on a read. The most complex example of the IPC i/o redirection mechanism in the current system occurs when a science or utility task sends graphics commands via the CL to a separate graphics task. Ignoring GKS inquires, this process is fully asynchronous and should be acceptably efficient provided the IPC buffer size is reasonable (1-4 Kb) and large amounts of bulk data do not have to be passed. .nh 3 Detached Processes The CL executes commands in the background, i.e., asynchronously, by dumping the entire run time context of the CL into a binary background file, then spawning a detached CL process to execute the already compiled command in the context of the parent. The run time context consists of the dictionary and stack areas, the environment list, and various other internal state parameters which are copied into the header area of the bkgfile. This is a potential problem area if dynamic memory is used, since it may not be possible to duplicate the virtual addresses of the parent's data area in the child. .ks .nf job = c_propdpr (process, bkgfile) stat = c_prcldpr (job) y/n = c_prdone (job) c_prkill (job) exit = c_onentry (prytpe, bkgfile) c_onexit (epa) .fi .ke The CL process uses the same IRAF Main as a normal IRAF process, except that a special \fBonentry\fR procedure is linked which serves as the CL main. The \fBonentry\fR procedure is called by the IRAF Main during process startup with the arguments shown; the function value returned by \fBonentry\fR determines whether or not the interpreter in the IRAF Main is entered. Since we do not want IRAF Main prompts from the CL process the CL version of \fBonentry\fR always returns CL_EXIT, causing process shutdown following execution of any procedures posted with \fBonexit\fR during execution of \fBonentry\fR. A detached process opened with \fBpropdpr\fR should always be closed with \fBprcldpr\fR if it terminates while the parent is still executing. The \fBprdone\fR procedure may be called to determine if a background job has terminated. A background job may be aborted with \fBprkill\fR. .nh 2 Terminal Control The TTY interface is provided at the CL level to support screen editing. TTY is the IRAF interface to the \fBtermcap\fR terminal capability database, originally developed at Berkeley for UNIX by Bill Joy. .ks .nf tty = c_ttyodes (ttyname) c_ttycdes (tty) c_ttyseti (tty, parameter, value) int = c_ttystati (tty, parameter) bool = c_ttygetb (tty, cap) int = c_ttygeti (tty, cap) float = c_ttygetr (tty, cap) nchars = c_ttygets (tty, cap, &outstr, maxch) c_ttyctrl (fd, tty, cap, afflncnt) c_ttyputs (fd, tty, ctrlstr, afflncnt) c_ttyclear (fd, tty) c_ttyclearln (fd, tty) c_ttygoto (fd, tty, col, line) c_ttyinit (fd, tty) c_ttyputline (fd, tty, text, map_cc) c_ttyso (fd, tty, onflag) .fi .ke Complete descriptions of TTY and \fBtermcap\fR are given elsewhere. Briefly, the device descriptor for a particular terminal is opened with \fBttyodes\fR, which returns a IRAF pointer (C integer) to the binary TTY descriptor. The terminal name may be given as "terminal", in which case \fBttyodes\fR will look up the name of the default terminal in the environment and search the termcap database for the entry for the named device. The \fBttyget\fR functions are used to read the capabilities. Capabilities are specified by two character mnemonics (character strings), shown as the \fIcap\fR arguments in the calling sequences above. Control sequences may be output with \fBttyctrl\fR or with \fBttyputs\fR, depending on whether you are willing to do a binary search for a particular capability at run time. The remaining high level functions make it easy to perform the more common control functions. Raw mode output to a terminal device is provided by the system interface (the newline and tab characters are exceptions). Raw mode input is provided as an \fBfseti\fR option in FIO. To set raw mode on STDIN: c_fseti (STDIN, F_RAW, YES); While raw mode is in effect input characters are read as they are typed, few or no control characters are recognized, and no echoing is performed. .nh 2 Memory Management Both heap and stack storage facilities are available in the program interface, and we have made both types of facilities available in the CL. Note, however, that the CL does not currently use dynamic memory allocation directly due to the difficulties such use would cause when passing the context of the CL to a background CL (used to execute commands in the background). The CL currently makes heavy use of pointers in the dictionary data structures, and since the dictionary is passed to the background CL as a binary array, it must be restored to the same base memory address or the pointers will be meaningless. This led to the implementation of the dictionary and stack areas as fixed size, statically allocated arrays. The use of a fixed size dictionary is restrictive and wasteful of storage; a future implementation based on \fBsalloc\fR (the stack facilities) is desirable provided the context passing problem can be solved. Note that the environment facilities do use dynamic storage and that it is nonetheless possible to pass the environment to a background CL, despite the internal use of pointers in the environment management package. .ks .nf Heap Storage (UNIX compatible): buf = malloc (nchars) u_malloc buf = calloc (nchars) u_calloc mfree (buf) u_mfree Stack Storage: c_smark (&sp) buf = c_salloc (nchars) c_sfree (sp) .fi .ke Note that the C callable versions of \fBmalloc\fR, \fBcalloc\fR, and \fBmfree\fR emulate the comparable UNIX procedures. The storage units are C chars, i.e., bytes. Promotion to an integral number of SPP chars is automatic. The \fIbuf\fR argument is a C pointer. The \fIsp\fR argument, used to mark the position of the stack pointer, is an integer. The stack is implemented in segments allocated on the heap, hence there is no builtin limit on the size of the stack.