LIBRARY (CCP4: Library)

NAME

library - the CCP4 C subroutine library

DESCRIPTION

library.c is a set of low-level C routines used by ccplib and diskio. There is also a header file library.h which should be included in any C program which uses CCP4 routines, as this contains code for dealing with platform dependencies.

CONTENTS

Notes on the header file library.h

This was a part of library.c but has been split off so it can be used with other c programs in the suite.

Comments on platform dependencies

There are several platform dependencies in the code which we need to get right:

Assumptions

It is assumed that a Fortran INTEGER corresponds to a C int, and that a Fortran REAL corresponds to a C float.

Also, the identity of certain calling conventions is only guaranteed if the routines have only a single Fortran CHARACTER-type argument - since in some cases the length of each such argument is given after it in the parameter list, and in other cases they are all collected at the end of the list.

Apart from the possibility of using the Netlib f2c compiler we currently assume that each system uses the vendor-supplied Fortran compiler.

This is for IBM Unix systems - RS/6000 models, at least. The compiler can append "_" (underscore character) to external names, but we assume the default where this doesn't happen. See configure for the enforcement of this.

Platform Identification

The guarded code in library.h is executed when we've identified the platform, so each type of system we know about should cause KNOWN_MACHINE to be defined; it will also define CALL_LIKE_something, depending on the calling conventions used by that system. For example:

#if defined (sgi)
#  define KNOWN_MACHINE
#  define CALL_LIKE_SUN 1
#endif

Thus if you know system foo has the same Fortran calling convention as that used by the native Sun compiler, then define CALL_LIKE_SUN and you won't need to examine the definitions of the interface functions below. Note that further tests on the system type may be necessary, for example to get the include files right.

The calling conventions for the major platforms supported by CCP4 are given below:

CALL_LIKE_ Systems
HPUX AIX, HPUX
SUN Alliant, Convex, ESV, SGI, Sun, Ultrix, OSF1, Linux, LinuxPPC
STARDENT Ardent/Titan/Stardent
VMS VMS
MVS Microsoft Visual Studio (NT)

Practical implementation for different platforms

Once the KNOWN_MACHINE and CALL_LIKE_something variables have been defined, the correct calling conventions are invoked in library.c by the presence of code of the form

...
#if CALL_LIKE_HPUX
  void copen (int *iunit, char *filename, int *istat, int Lfilename);
#endif
#if CALL_LIKE_STARDENT
  void COPEN (int *iunit, struct Str_Desc *filename, int *istat);
#endif
#if defined (VMS)
  void COPEN (int *iunit, struct dsc$descriptor_s *filename, int *istat);
#endif
#if CALL_LIKE_SUN
  void copen_ (int *iunit, char *filename, int *istat, int Lfilename);
#endif
...

Examine the source code for more exact details of how this works in practice.

File mode definitions

Here are the definitions of the diskio modes, specifying the type of data transfer: bytes, half-words, integers, reals, half(integer)-word complex and complex, respectively:

define BYTE 0
define INT16 1
define INT32 6
define FLOAT32 2
define COMP32 3
define COMP64 4

Converting foreign binary number formats

The library is intended to allow the binary file formats MTZ and map files to be read satisfactorily if they were written on another platform. Such files are always written in the native real or integer number format with a machine stamp in the file to identify the formats involved. Then, if necessary, conversion is done from the foreign format to native when the file is read. There is thus only a significant overhead for files imported from platforms with different number formats; locally-written files are read back optimally and there is no write overhead.

When converting from foreign to native formats we're potentially faced with a combinatorial explosion---currently combinations of ieee little-endian, ieee big-endian, VAX and Convex native formats. (This applies only to real number formats---fortunately everything we're interested in has twos complement integers.) Thus we first make sure that the format is converted to canonical form (which we choose as big-endian ieee) and then, if necessary, to the native format in a separate stage.

The basic idea of this is due to David Wild (EMBL, Hamburg, 1991). His original, partially-functional implementation used code from the HDF 3.1 distribution. This re-write is by Dave Love, very loosely based on HDF3.3, but doing the conversion in-place. It works for the full set of relevant systems and no longer has MTZ- and map-specific code in copen. (HDF stuff can be found on ftp.ncsa.uiuc.edu)

Machine stamps

The machine stamp is a 32-bit quantity containing a set of four `nibbles' (half-bytes)---only half the space is used. Each nibble is a number specifying the representation of (in C terms) double (d) , float (f), int (i) and unsigned char (c) types. Thus each stamp is of the form 0xdfic0000. The values for the floating point nibbles may be taken from the list (following HDF):
1 Big-endian ieee
2 VAX
3 Cray
4 Little-endian ieee
5 Convex native
6 Fijitsu VP

The Cray isn't relevant to us because it's not a 32-bit machine and we don't currently have a use for the Fujitsu one, which isn't implemented here. We ignore the possibility of non-ascii characters which might need converting e.g., from ebcdic, so c is always 1; also f and d are the same (as per Fortran). See the HDF code for character code possibilities.

Here are the tags for different formats as used in the code.
class info codes for int
DFNTI_MBO 1 Motorola byte order 2's compl
DFNTI_IBO 4 Intel byte order 2's compl
class info codes for float
DFNTF_BEIEEE 1 big endian IEEE (canonical)
FNTF_VAX 2 Vax format
DFNTF_CONVEXNATIVE 5 Convex native floats
DFNTF_LEIEEE 4 little-endian IEEE format

Following are definitions. Note that some of the symbols tested here to determine the machine type might need to be qualified in the future where they don't necessarily determine the architecture. Only nativeFT and nativeIT, which determine the native real and integer formats, are set.

#if defined (VAX) || defined (vax) /* gcc seems to use vax */
#  define NATIVEFT DFNTF_VAX
#  define NATIVEIT DFNTI_IBO
#endif

Here are the possibilities for little-endian ieee. (The MIPS compilers define MIPSEL or MIPSEB depending on the mode in which the chip operates.) The architectures covered here include some DECstations, i860 and Intel chips like PCs and Alpha (sometimes!).

#if defined(MIPSEL) || defined(alliant) || defined(i386) || defined(i860)
#  define NATIVEIT DFNTI_IBO
#  define NATIVEFT DFNTF_LEIEEE
#endif

Machines using the powerPC chip. Specifically, this has been tried on PowerMacs running LinuxPPC, which appears to be big-endian. But in principle the powerPC chip can support both big-endian and little-endian OS's under software control. The symbol "powerpc" appears in gcc-2.8.1/config/rs6000/linux.h and appears to distinguish LinuxPPC from other OS's for this chip.

                 
#if defined (powerpc)
#  define NATIVEIT DFNTI_MBO
#  define NATIVEFT DFNTF_BEIEEE
#endif

Alpha VMS is a pain: compiler switches can force VAX or ieee number formats. Thus if we know it's an Alpha, we have to check for VMS and then what sort of VMS numbers. [OSF and OpenVMS define __alpha, OpenVMS, only __ALPHA.

#ifdef __alpha
#  ifdef VMS
#    if __IEEE_FLOAT == 1
#      define NATIVEFT DFNTF_LEIEEE
#    else
#      define NATIVEFT DFNTF_VAX
#    endif
#  else                       /* assume OSF/1 */
#    define NATIVEFT DFNTF_LEIEEE
#  endif
#  define NATIVEIT DFNTI_IBO
#endif

Big-endian ieee includes SGI machines, HP machines (68k-based or RISC), RS/6000 and all Suns except the obsolete i386-based ones. (Apollo}s are also apparently in this category.)

#if defined(MIPSEB) || defined(__hpux) || defined(_AIX) || defined(m68k) || 
  defined(mc68000) || defined(sparc) || defined (__sparc__)
#  define NATIVEIT DFNTI_MBO
#  define NATIVEFT DFNTF_BEIEEE
#endif

Convex}s can operate in either native or ieee mode:

#if defined(__convex__) || defined(__convexc__)
#  define NATIVEIT DFNTI_MBO
#  ifdef _IEEE_FLOAT_
#    define NATIVEFT DFNTF_BEIEEE
#  else
#    ifdef _CONVEX_FLOAT_
#      define NATIVEFT DFNTF_CONVEXNATIVE
#    else
#error "Can't determine Convex floating point type. Use native compiler"
#    endif
#  endif
#endif
#ifndef NATIVEFT
#error "Can't determine machine number format"
#endif

Notes on routines in library.c

This file contains the lowest level routines for the CCP4 Program Suite, mainly for i/o (as required by the diskio routines) and bit-twiddling.

The following routines are defined:

Routine Purpose
Internal routines
flength return length of string less trailing blanks
fatal interface to ccperr
cqprint prints a message to FORTAN i/o
file_fatal reports fatal error
FP conversion routines
vaxF2ieeeF VAX(double) <-> ieee(float)
ieeeF2vaxF ieee(double) <-> VAX(float)
convexF2ieeeF Convex(double) <-> ieee(float)
ieeeF2convexF ieee(double) <-> Convex(float)
Miscellaneous routines
ustenv set an environment variable
cunlink unlinks file from directory
hgetlimits get int and float limits
cmkdir wrap around for mkdir function
cchmod wrap around for chmod function
Dynamic memory allocation
ccpal1 calls routine with array arguments
ccp4malloc wrap around for malloc function
ccp4realloc wrap around for realloc function
ccp4calloc wrap around for calloc
Disk i/o routines
copen open random access file using fopen
qrarch set up diskio number translation
qwarch write `machine stamp' to diskio file
qclose shut random access file using fclose
qmode change size of item in file ops.
qread fread from random access file
qreadc fread byte characters from random access file
qwrite fwrite to random access file
qwritc fwrite byte characters to random access file
qseek fseek within random access file
qback backspace within random access file
qskip skip forward within random access file
cqinq inquire file status on the given stream
qlocate current position within random access file
Magic numbers
qnan sets NaN
cisnan checks for NaN
ccpbml absent data test for mtzlib
ccpwrg updates MTZ column ranges
Missing system support
idate/idate_ AIX, F2C, G77
ierrno/ierrno_ HPUX, AIX, F2C, G77
itime/itime_ HPUX, AIX, F2C, G77
etime/etime_ HPUX, AIX, F2C, G77
exit_ F2C, G77
time_ F2C, G77
getpid_ F2C, G77
isatty/isatty_ HPUX, AIX, F2C, G77
gerror_ F2C, G77
ibset_ F2C, G77
ibclr_ F2C, G77
btest_ F2C, G77

Routine Descriptions

Internal Routines.

flength

static size_t flength (char *s, int len)

Returns the length (units size_t) of a character string s[len] with the trailing blanks removed.

fatal

static void fatal (char *message)

Interface to CCPERR which avoids mixing FORTRAN and C code.

cqprint

static void cqprint (char *message)

prints a non-fatal message using the Fortran i/o.

file_fatal

static void file_fatal (char *message, char *file)

reports a fatal error with a given file. Calls fatal.

Floating-point conversion routines.

These conversion routines are based on HDF, but do the conversion in-place. They do the obvious conversion between VAX, IEEE and Convex formats implied by the routine names.

vaxF2ieeeF

static void vaxF2ieeeF(union float_uint_uchar buffer[], int size)

ieeeF2vaxF

static void ieeeF2vaxF(union float_uint_uchar buffer[], int size)

The Convex format is like the VAX with a different byte order. Convex does provide ieee native ieee conversion routines, but we need convexF2ieeeF anyhow.

convexF2ieeeF

static void convexF2ieeeF(union float_uint_uchar buffer[], int size)

ieeeF2convexF

static void ieeeF2convexF(union float_uint_uchar buffer[], int size)

Miscellaneous routines.

ustenv

CALL_LIKE_HPUX: void ustenv (char *string, int *result, int Lstr)
CALL_LIKE_STARDENT: void USTENV (struct Str_Desc *string, int *result)
CALL_LIKE_SUN: void ustenv_ (char *string, int *result, int Lstr)
CALL_LIKE_MVS: void __stdcall USTENV (char *string, int *result, int Lstr)

This sets an environment variable var to val, where the argument string == 'var=val'.
This is for use by the "logical name" mechanism for specifying file connexions. Note that a VMS version is supplied in vms.for and that there is no standard way of setting and environment variable. In a minimal posix system it might be necessary to twiddle the environment strings explicitly.

cunlink

CALL_LIKE_HPUX void cunlink (char *filename, int Lfilename)
CALL_LIKE_STARDENT void CUNLINK (struct Str_Desc *filename)
(VMS) void CUNLINK (struct dsc$descriptor_s *filename)
CALL_LIKE_SUN void cunlink_ (char *filename, int Lfilename)
CALL_LIKE_MVS void __stdcall CUNLINK (char *filename, int Lfilename)

This unlinks filename from the directory. It's intended for use with scratch files, so that they can be hidden when opened but still be available as long as they remain connected (see CCPOPN). This functionality doesn't seem to exist in VMS. Failure to unlink isn't fatal (it's been observed, apparently spuriously).

hgetlimits

CALL_LIKE_HPUX void hgetlimits (int *IValueNotDet, float *ValueNotDet)
(VMS) void HGETLIMITS (int *IValueNotDet, float *ValueNotDet)
CALL_LIKE_STARDENT void HGETLIMITS (int *IValueNotDet, float *ValueNotDet)
CALL_LIKE_SUN void hgetlimits_ (int *IValueNotDet, float *ValueNotDet)
CALL_LIKE_MVS void __stdcall HGETLIMITS (int *IValueNotDet, float *ValueNotDet)

Returns largest int and largest float as defined in limits.h and float.h

cmkdir

CALL_LIKE_HPUX void cmkdir (const char *path, const char *cmode, int *result, int Lpath, int Lmode)
(VMS) void CMKDIR (struct dsc$descriptor_s *path, struct dsc$descriptor_s *cmode, int *result)
CALL_LIKE_STARDENT void CMKDIR (struct Str_Desc *path, struct Str_Desc *cmode, int *result)
CALL_LIKE_SUN void cmkdir_ (const char *path, const char *cmode, int *result, int Lpath, int Lmode)
CALL_LIKE_MVS void __stdcall CMKDIR (const char *path, int Lpath, const char *cmode, int Lmode, int *result)

Wrap-around for mkdir function. Returns 0 if successful, 1 if directory already exists, and -1 if other error

cchmod

CALL_LIKE_HPUX void cchmod (const char *path, const char *cmode, int *result, int Lpath, int Lmode)
(VMS) void CCHMOD (struct dsc$descriptor_s *path, struct dsc$descriptor_s *cmode, int *result)
CALL_LIKE_STARDENT void CCHMOD (struct Str_Desc *path, struct Str_Desc *cmode, int *result)
CALL_LIKE_SUN void cchmod_ (const char *path, const char *cmode, int *result, int Lpath, int Lmode)
CALL_LIKE_MVS void __stdcall CCHMOD (const char *path, int Lpath, const char *cmode, int Lmode, int *result)

Wrap around for chmod function.

Dynamic memory allocation

It's nice to be able to determine array sizes at run time to avoid messy recompilation. The only way effectively to get dynamic allocation in Fortran77 reasonably portably is to do the allocation, e.g.\ in C, and invoke the Fortran routine passed as a parameter with pointers to the allocated memory which it will treat as arrays. If we want to allow more than one array, it's more tricky.

ccpal1

CALL_LIKE_HPUX void ccpal1 (void (* routne), int *n, int type[], int length[])
(VMS) || CALL_LIKE_STARDENT void CCPAL1 (void (* routne) (), int *n, int type[], int length[])
CALL_LIKE_SUN void ccpal1_ (void (* routne) (), int *n, int type[], int length[])
CALL_LIKE_MVS void __stdcall CCPAL1 (void (* routne) (), int *n, int type[], int length[])

Arranges to call subroutine routne with n array arguments. Each has a type indicated by type(i) and a length given by length(i). type is an integer array with values 1, 2, 3, 4 indicating INTEGER, REAL, DOUBLE PRECISION and COMPLEX respectively. It's not immediately clear what all the Fortran/C conventions are for passing CHARACTER arrays, so we'll arrange a higher-level interface and have types here just numeric. The Fortran (CCPALC) will also do argument validation. Also the rules for passing external routines as arguments aren't clear--assume the obvious way.

There's a VMS Fortran version of this, although the code here does work fine in VMS\@.

NB: there's a possibility of a hook here to use memory-mapped files on systems with the capability and insufficient VM.

Under protest, this now allocates zeroed storage for where programs make bad assumptions.

ccp4malloc

void *ccp4malloc(size_t size)

This is a wrapper for the malloc function, which adds some error trapping

ccp4realloc

void *ccp4realloc(void *ptr, size_t size)

This is a wrapper for the realloc function, which adds some error trapping

ccp4calloc

void *ccp4calloc(size_t nelem , size_t elsize)

This is a wrapper for the calloc function, which adds some error trapping

Diskio Routines.

copen

CALL_LIKE_HPUX void copen (int *iunit, char *filename, int *istat, int Lfilename)
CALL_LIKE_STARDENT void COPEN (int *iunit, struct Str_Desc *filename, int *istat)
(VMS) void COPEN (int *iunit, struct dsc$descriptor_s *filename, int *istat)
CALL_LIKE_SUN void copen_ (int *iunit, char *filename, int *istat, int Lfilename)
CALL_LIKE_MVS void __stdcall COPEN (int *iunit, char *filename, int Lfilename, int *istat)

Opens filename on diskio stream iunit. istat corresponds to the open mode given to qopen, from which copen is always called--see diskio documentation.

qrarch

CALL_LIKE_HPUX void qrarch (int *iunit, int *ipos, int *ireslt)
CALL_LIKE_STARDENT || (VMS) void QRARCH (int *iunit, int *ipos, int *ireslt)
CALL_LIKE_SUN void qrarch_ (int *iunit, int *ipos, int *ireslt)
CALL_LIKE_MVS void __stdcall QRARCH (int *iunit, int *ipos, int *ireslt)

For binary files with a well-determined structure in terms of float's and int's we may want to set up the connected stream to do transparent reading of files written on a machine with a different architecture. This is currently the case for map files and MTZ files and this routine is called from mtzlib and maplib.

qrarch reads the "machine stamp" at word ipos for the diskio file on stream iunit and sets up the appropriate bit-twiddling for subsequent qreadi's on that stream. The information read from the file is returned in ireslt in the form fileFT+16*fileIT. If the stamp is zero (as it would be for files written with a previous version of the library) we assume the file is in native format and needs no conversion in qread; in this case ireslt will be zero and the caller can issue a warning. Iconvert and Fconvert are used by qread to determine the type of conversion (if any) to be applied to integers and reals.

Extra feature: logical/environment variable CONVERT_FROM may be set to one of BEIEEE, LEIEEE, VAX or CONVEXNATIVE to avoid reading the machine stamp and assume the file is from the stipulated architecture for all input MTZ and map files for which qrarch is called.

N.B.: leaves the stream positioned just after the machine stamp.

qwarch

CALL_LIKE_HPUX void qwarch (int *iunit, int *ipos)
CALL_LIKE_STARDENT || (VMS) void QWARCH (int *iunit, int *ipos)
CALL_LIKE_SUN void qwarch_ (int *iunit, int *ipos)
CALL_LIKE_MVS void __stdcall QWARCH (int *iunit, int *ipos)

This is the complement of qrarch, writing the native machine architecture information ("machine stamp") to diskio stream iunit at word ipos. Currently called from mtzlib and maplib.

The machine stamp in mtstring is four nibbles in order, indicating complex and real format (must both be the same), integer format and character format (currently irrelevant). The last two bytes of mtstring are currently unused and always zero.

N.B.: leaves the stream positioned just after the machine stamp.

qclose

CALL_LIKE_HPUX void qclose (int *iunit)
(VMS) || CALL_LIKE_STARDENT void QCLOSE (int *iunit)
CALL_LIKE_SUN void qclose_ (int *iunit)
CALL_LIKE_MVS void __stdcall QCLOSE (int *iunit)

Closes the file open on diskio stream iunit.

qmode

CALL_LIKE_HPUX void qmode (int *iunit, int *mode, int *size)
(VMS) || CALL_LIKE_STARDENT void QMODE (int *iunit, int *mode, int *size)
CALL_LIKE_SUN void qmode_ (int *iunit, int *mode, int *size)
CALL_LIKE_MVS void __stdcall QMODE (int *iunit, int *mode, int *size)

Changes the diskio "access mode" for stream iunit to mode. The resulting size in bytes of items for transfer is returned as size.

qread

CALL_LIKE_HPUX void qread (int *iunit, uint8 * buffer, int *nitems, int *result)
(VMS) || (stardent) void QREAD (int *iunit, uint8 * buffer, int *nitems, int *result)
CALL_LIKE_SUN void qread_ (int *iunit, uint8 * buffer, int *nitems, int *result)
CALL_LIKE_MVS void __stdcall QREAD (int *iunit, uint8 * buffer, int *nitems, int *result)

Reads nitems in the current mode (set by qmode ) from diskio stream iunit previously opened by qqopen (copen) and returns result which is 0 on success, or -1 at EOF.

It aborts on an i/o error.

Numbers written in a foreign format will be translated if necessary if the stream is connected to an MTZ or map file.

qreadc

CALL_LIKE_HPUX void qreadc (int *iunit, char * buffer, int *result, int Lbuffer)
VMS void QREADC (int *iunit, struct dsc$descriptor_s *buffer, int *result)
CALL_LIKE_STARDENT void QREADC (int *iunit, struct Str_Desc *buffer, int *result)
CALL_LIKE_SUN void qreadc_ (int *iunit, char * buffer, int *result, int Lbuffer)
CALL_LIKE_MVS void __stdcall QREADC (int *iunit, char * buffer, int *result, int Lbuffer)

Fills CHARACTER buffer in byte mode from diskio stream iunit previously opened by qqopen (copen) and returns result which is the number of items read or 0 on failure.

Call it with a character substring if necessary to control the number of bytes read.

qwrite

CALL_LIKE_HP void qwrite (int *iunit, uint8 * buffer, int *nitems)
(VMS) || CALL_LIKE_STARDENT void QWRITE (int *iunit, uint8 * buffer, int *nitems)
CALL_LIKE_SUN void qwrite_ (int *iunit, uint8 * buffer, int *nitems)
CALL_LIKE_MVS void __stdcall QWRITE (int *iunit, uint8 * buffer, int *nitems)

This writes nitems items from buffer to opened stream iunit using the current mode.

qwritc

CALL_LIKE_HPUX void qwritc (int *iunit, char * buffer, int Lbuffer)
(VMS) void QWRITC (int *iunit, struct dsc$descriptor_s *buffer)
CALL_LIKE_STARDENT void QWRITC (int *iunit, struct Str_Desc *buffer)
CALL_LIKE_SUN void qwritc_ (int *iunit, char * buffer, int Lbuffer)
CALL_LIKE_MVS void __stdcall QWRITC (int *iunit, char * buffer, int Lbuffer)

Writes CHARACTER*(*) buffer to opened stream iunit in byte mode.

qseek

CALL_LIKE_HPUX void qseek (int *iunit, int *irec, int *iel, int *lrecl)
(VMS) || CALL_LIKE_STARDENT void QSEEK (int *iunit, int *irec, int *iel, int *lrecl)
CALL_LIKE_SUN void qseek_ (int *iunit, int *irec, int *iel, int *lrecl)
CALL_LIKE_MVS void __stdcall QSEEK (int *iunit, int *irec, int *iel, int *lrecl)

Seeks to element iel in record irec in diskio stream iunit whose record length is lrecl.

qback

CALL_LIKE_HPUX void qback (int *iunit, int *lrecl)
(VMS) || CALL_LIKE_STARDENT void QBACK (int *iunit, int *lrecl)
CALL_LIKE_SUN void qback_ (int *iunit, int *lrecl)
CALL_LIKE_MVS void __stdcall QBACK (int *iunit, int *lrecl)

Backspaces one record, of length lrecl on diskio stream iunit.

cqinq

CALL_LIKE_HPUX void cqinq (int *istrm, char *filnam, int *length, int len_filnam)
CALL_LIKE_STARDENT void CQINQ (int *istrm, struct Str_Desc *filnam, int *length)
(VMS) void CQINQ (int *istrm, struct dsc$descriptor_s *filnam, int *length)
CALL_LIKE_SUN void cqinq_ (int *istrm, char *filnam, int *length, int len_filnam)
CALL_LIKE_MVS void __stdcall CQINQ (int *istrm, char *filnam, int len_filnam, int *length)

Returns the name filnam and length of the file (if any) open on diskio stream istrm.

qlocate

CALL_LIKE_HPUX void qlocate (int *iunit, int *locate)
(VMS) || CALL_LIKE_STARDENT void QLOCATE (int *iunit, int *locate)
CALL_LIKE_SUN void qlocate_ (int *iunit, int *locate)
CALL_LIKE_MVS void __stdcall QLOCATE (int *iunit, int *locate)

Returns the current position locate in the diskio stream iunit.

`Magic' numbers

When an erroneous result occurs, for instance a floating point exception, it is useful to give it a special value--a "magic number"--possibly in addition to a special value, like a negative one. Using such a number in a calculation (by mistake, through ignoring the value) should not allow one to get half-sensible results as one might if this number was -9999 or some such.

The obvious tactic with ieee arithmetic is to use a NaN value in such situations. Things may be set up so that we either get an exception on using it in arithmetic or it silently propagates to all values using it and its presence is indicated by a NaN in the output.

We need to provide a means of setting the magic number and checking whether a given value is such. These are architecture-dependent bit-level operations, hence their presence in the C code.

The suite doesn't currently use these routines, but should do soon.

qnan

CALL_LIKE_HPUX void qnan (union float_uint_uchar *realnum)
(VMS) || CALL_LIKE_STARDENT void QNAN (union float_uint_uchar *realnum)
CALL_LIKE_SUN void qnan_ (union float_uint_uchar *realnum)
CALL_LIKE_MVS void __stdcall QNAN (union float_uint_uchar *realnum)

Sets a value to NaN.

We have a choice of NaN values in ieee arithmetic. 0xfffa5a5a is the one used by the MIPS compilers as an undefined value. Note the hex constant is the same for both byte sexes!

cisnan

CALL_LIKE_HPUX int cisnan (union float_uint_uchar *realnum)
(VMS) || CALL_LIKE_STARDENT int CISNAN (union float_uint_uchar *realnum)
CALL_LIKE_SUN int cisnan_ (union float_uint_uchar *realnum)
CALL_LIKE_MVS int __stdcall CISNAN (union float_uint_uchar *realnum)

integer (logical) function cisnan tests whether its argument is a NaN. We have to do this by writing a C int-valued procedure and testing the returned value in the calling function so that we don't have to assume how it represents logical values. The diskio library library provides the trivial interface qisnan.

ccpbml

CALL_LIKE_HPUX void ccpbml (int *ncols, union float_uint_uchar cols[])
(VMS) || CALL_LIKE_STARDENT void CCPBML (int *ncols, union float_uint_uchar cols[])
CALL_LIKE_SUN void ccpbml_ (int *ncols, union float_uint_uchar cols[])
CALL_LIKE_MVS void __stdcall CCPBML (int *ncols, union float_uint_uchar cols[])

Absent data test for mtzlib.
In mtzlib there's a fudge for BIOMOL-convention absence flags, which are re-written to zeroes. To do the real number comparison, though, it's necessary to do a qnan-type test first. We don't want to call qnan (which calls cisnan) on every number in the data file, so the tests are amortised in this routine which deals with a whole array cols of length ncols.

ccpwrg

CALL_LIKE_HPUX void ccpwrg (int *ncols, union float_uint_uchar cols[], float wminmax[])
(VMS) || CALL_LIKE_STARDENT void CCPWRG (int *ncols, union float_uint_uchar cols[], float wminmax[])
CALL_LIKE_SUN void ccpwrg_ (int *ncols, union float_uint_uchar cols[], float wminmax[])
CALL_LIKE_MVS void __stdcall CCPWRG (int *ncols, union float_uint_uchar cols[], float wminmax[])

For updating MTZ column ranges.
This is a similar fudge to ccpbml to avoid QISNAN calls in updating the MTZ column ranges in mtzlib. Note that wminmax actually indexes a 3-D Fortran array with the first dimension range of 2, indicating minimum and maximum values respectively.

Missing system support

Routines often found in libU77.a or somesuch are missing on some systems, eg HPUX, AIX, and F2C and G77.

idate

AIX void idate (int iarray)
F2C || G77 int idate_ (int *iarray)

Returns date in dd/mm/yy format from localtime().

ierrno

HPUX || AIX int ierrno ()
F2C || G77 int ierrno_ ()

itime

HPUX || AIX void itime (int array)
F2C || G77 int itime_ (int *array)

etime

HPUX || AIX float etime (float tarray)
F2C || G77 doublereal etime_ (float *tarray)

exit_

F2C || G77 int exit_ (int *status)

time_

F2C || G77 int time_ ()

getpid_

F2C || G77 int getpid_ ()

isatty

F2C || G77 int isatty_ (int *lunit)
CALL_LIKE_MVS int __stdcall ISATTY (int *lunit)

gerror_

F2C || G77 int gerror_ (char *str, int Lstr)

ibset_

F2C || G77 int ibset_ (int *a, int *b)

ibclr_

F2C || G77 int ibclr_ (int *a, int *b)

btest_

F2C || G77 int btest_ (int *a, int *b)