/******************************************************************************
*
*  NSSDC/CDF                   Toolbox of routines for CDF Toolkit (General).
*
*  Version 1.4e, 17-May-96, Hughes STX.
*
*  Modification history:
*
*   V1.0  25-Jan-94, J Love     Original version.
*   V1.0a  6-Feb-94, J Love     DEC Alpha/OpenVMS port.
*   V1.1  10-Mar-94, J Love     Modified `ParseOptionList' to make the
*                               enclosing parentheses optional (on any
*                               operating system).
*   V1.2  15-Dec-94, J Love     CDF V2.5.
*   V1.2a 13-Jan-95, J Love	Allow mixed-case in tokens.  Changed
*				`CDFdirList' to allow all possible extensions
*				on Macintosh machines also.
*   V1.2b  6-Mar-95, J Love	Added macro definition checking to catch
*				bug in directory listing system calls.  Pass
*				`char' as `int'.
*   V1.3  21-Mar-95, J Love	POSIX.
*   V1.3a 18-Apr-95, J Love	More POSIX.
*   V1.3b 24-Apr-95, J Love	Fixed `EncodeString' to not overflow the
*				maximum length if the string won't fit.
*   V1.4  25-May-95, J Love	Added `GetCdataType'.  EPOCH styles.
*				SOLARISbsdDIRUTILSbug.
*   V1.4a 12-Jun-95, J Love	EPOCH custom format.  catchrX.
*   V1.4b  5-Sep-95, J Love	Allow NULL for output fields not needed.
*				NUL-terminate decoded string in `DecodeValues'.
*   V1.4c 19-Sep-95, J Love	Macintosh event handling.
*   V1.4d 27-Sep-95, J Love	Reduced TextEdit text size for Think C.
*   V1.4e 17-May-96, J Love	ConvertDataType: `long' -> `Int32', etc.
*
******************************************************************************/

#define _TOOLBOX1_
#include "cdftools.h"

#if defined(mac)
#include "so.rh"
#endif

/******************************************************************************
* Local macros.
******************************************************************************/

#define MAX_cFORMAT_LEN 10

#if defined(mac)
#define MAX_TE_TEXT_LEN         30000	/* 32767 doesn't seem to work. */
#define MAX_TE_TEXT_LEN_wNL     (MAX_TE_TEXT_LEN - NUMsoCOLS)
#endif

/******************************************************************************
* Local enumerators.
******************************************************************************/

#if defined(unix) || defined(mac) || defined(posixSHELL)
enum typeENUM {TBD, STR, WILD1, WILDn};
#endif

/******************************************************************************
* Local structures.
******************************************************************************/

struct FILEstruct {
  char *name;
  struct FILEstruct *next;
};

/******************************************************************************
* Local function prototypes.
******************************************************************************/

static Logical EqStringsIgLTws PROTOARGs((char *string1, char *string2));
static Logical DecodeInt32 PROTOARGs((char *, Int32 *));
static Logical DecodeInt32u PROTOARGs((char *, uInt32 *));
static char *strnchr PROTOARGs((char *string, int chr, int n));
static Logical FtoCformat PROTOARGs((long, char *, long *, char *));
static int UseCformat PROTOARGs((long, long, char *, void *, char *));
static long GetCdataType PROTOARGs((char *, long *));
static void AddToList PROTOARGs((struct FILEstruct **FILEhead,
				 struct FILEstruct *FILEptr));
#if defined(unix) || defined(mac) || defined(posixSHELL)
static int PatternMatch PROTOARGs((char *pattern, char *name));
static int CheckPattern PROTOARGs((int nTypes, enum typeENUM types[],
				   char *strings[], char *name));
#endif

/******************************************************************************
* FatalError.
******************************************************************************/

void FatalError (message)
char *message;
{
#if defined(mac)
  MacMessageDialog ("FATAL ERROR!", message);
#else
  WriteOut (stdout, "FATAL ERROR!");
  WriteOut (stdout, "  ");
  WriteOut (stdout, message);
  WriteOut (stdout, "\n");
#endif
  ExitBAD;
}

/******************************************************************************
* DisplayError.
******************************************************************************/

void DisplayError (message)
char *message;
{
#if defined(mac)
  MacMessageDialog ("Error!", message);
#else
  WriteOut (stdout, "Error!");
  WriteOut (stdout, "  ");
  WriteOut (stdout, message);
  WriteOut (stdout, "\n");
#endif
  return;
}

/******************************************************************************
* DisplayWarning.
*    It is assumed that the message contains at least one character.
******************************************************************************/

void DisplayWarning (message)
char *message;
{
#if defined(mac)
  MacMessageDialog ("Warning...", message);
#else
  WriteOut (stdout, "Warning...");
  WriteOut (stdout, TempCharStr(ToLowerChr((int)message[0])));
  WriteOut (stdout, &message[1]);
  WriteOut (stdout, "\n");
#endif
  return;
}

/******************************************************************************
* DisplayInfo.
******************************************************************************/

void DisplayInfo (message)
char *message;
{
#if defined(mac)
  MacMessageDialog ("", message);
#else
  WriteOut (stdout, message);
  WriteOut (stdout, "\n");
#endif
  return;
}

/******************************************************************************
* PickDelimiter.
*    Exclamation mark (`!') isn't used because it starts a comment in a
* skeleton table.  Period (`.') isn't used because it ends a list of
* attribute entries (in a skeleton table).  Left bracket (`[') isn't used
* because it starts a set of NRV indices (in a skeleton table).
******************************************************************************/

char PickDelimiter (string, N)
char *string;
size_t N;
{
  static char choices[] = "\"'`^~:-,;|+=@#$%&*()/?<>{}]\\";
  int i;
  for (i = 0; choices[i] != NUL; i++) {
     if (strnchr(string,choices[i],N) == NULL) return choices[i];
  }
  return NUL;   /* Very bad, use character stuffing if this is a problem. */
}

/******************************************************************************
* strnchr.
******************************************************************************/

static char *strnchr (string, chr, n)
char *string;
int chr;
int n;
{
  int i;
  for (i = 0; i < n; i++) if (string[i] == chr) return &string[i];
  return NULL;
}

/******************************************************************************
* sleep.  Only for Microsoft C.
******************************************************************************/

#if defined(MICROSOFTC)
void sleep (seconds)
unsigned int seconds;
{
time_t start_time;
time_t current_time;

time (&start_time);
time (&current_time);

while (current_time - start_time < seconds) time (&current_time);

return;
}
#endif

/******************************************************************************
* strstrIgCase.
******************************************************************************/

char *strstrIgCase (string, substring)
char *string;
char *substring;
{
  int stringL = strlen(string);
  int substringL = strlen(substring);
  int i, j;
  if (stringL == 0 || substringL == 0) return NULL;
  if (stringL < substringL) return NULL;
  for (i = 0; i < stringL; i++) {
     for (j = 0; j < substringL; j++)
	if (ToLowerChr(substring[j]) != ToLowerChr(string[i+j])) break;
     if (j == substringL) return &string[i];
  }
  return NULL;
}

/******************************************************************************
* strcmpIgCase.
******************************************************************************/

int strcmpIgCase (string1, string2)
char *string1;
char *string2;
{
  int string1L = strlen(string1);
  int string2L = strlen(string2);
  int maxL = MAXIMUM(string1L,string2L);
  int i;
  for (i = 0; i < maxL; i++)
     if (ToLowerChr(string1[i]) != ToLowerChr(string2[i]))
       return ((int) (string1[i] - string2[i]));
  return 0;
}

/******************************************************************************
* strncmpIgCase.
******************************************************************************/

int strncmpIgCase (string1, string2, count)
char *string1;
char *string2;
size_t count;
{
  int string1L = strlen(string1);
  int string2L = strlen(string2);
  int maxL, i;
  maxL = MAXIMUM(string1L,string2L);
  maxL = MINIMUM(maxL,count);
  for (i = 0; i < maxL; i++)
     if (ToLowerChr(string1[i]) != ToLowerChr(string2[i]))
       return ((int) (string1[i] - string2[i]));
  return 0;
}

/******************************************************************************
* strchrIgCase.
******************************************************************************/

char *strchrIgCase (str, chr)
char *str;
int chr;
{
  int strL = strlen(str);
  int i;
  if (strL == 0) return NULL;
  for (i = 0; i < strL; i++)
     if (ToLowerChr(chr) == ToLowerChr(str[i])) return &str[i];
  return NULL;
}

/******************************************************************************
* WriteEntryValue.
******************************************************************************/

void WriteEntryValue (fp, dataType, numElements, value, ccc, MaXcc)
FILE *fp;
long dataType;
long numElements;
void *value;
int ccc;                /* Current Cursor Column (at start). */
int MaXcc;              /* Maximum Cursor Column at which to output. */
{
  if (STRINGdataType(dataType))
    WriteStringValue (fp, numElements, value, ccc, MaXcc);
  else {
    int i, elemN, n, cccBase = ccc; 
    char evalue[80+1];
    Logical newLine = TRUE;
    for (elemN = 0; elemN < numElements; elemN++) {
       n = EncodeValue (dataType, (Byte *) value + elemN*CDFelemSize(dataType),
			evalue, EPOCH0_STYLE);
       if (ccc + (newLine ? 0 : 1) + n +
	   (elemN != numElements - 1 ? 1 : 0) > MaXcc) {
	 WriteOut (fp, "\n");
	 for (i = 0; i < cccBase; i++) WriteOut (fp, " ");
	 ccc = cccBase;
	 newLine = TRUE;
       }
       if (!newLine) ccc += WriteOut (fp, " ");
       ccc += WriteOut (fp, evalue);
       if (elemN != numElements - 1) ccc += WriteOut (fp, ",");
       newLine = FALSE;
    }
  }
  return;
}

/******************************************************************************
* WriteStringValue.
*    Note that control characters may exist in the string being written (even
* a NUL character).  Control characters are to be replaced with `.'.  For this
* reason `memmove' is used to move around strings/substrings.
******************************************************************************/

void WriteStringValue (fp, numChars, value, ccc, MaXcc)
FILE *fp;
long numChars;
void *value;
int ccc;                /* Current Cursor Column (at start). */
int MaXcc;              /* Maximum Cursor Column at which to output. */
{
  int cccBase = ccc, i;
  char *string = (char *) value;
  char delim;
  size_t textL = MaXcc + 1;             /* MaXcc + 1. */
  char *text = (char *) AllocateMemory ((size_t) (textL + 1), FatalError);

  if (ccc + 1 + numChars + 1 - 1 <= MaXcc) {
    delim = PickDelimiter (string, (size_t) numChars);
    text[0] = delim;
    memmove (&text[1], string, (size_t) numChars);
    text[(int)numChars+1] = delim;
    text[(int)numChars+2] = NUL;
    for (i = 1; i <= numChars; i++) if (!IsPrintChr(text[i])) text[i] = '.';
    WriteOut (fp, text);
  }
  else {
    int sss = MaXcc - ccc - 4 + 1;      /* 4 for starting `"' and ending `" -'
					   (assuming `"' is the delimeter). */
    char *substring = (char *) AllocateMemory ((size_t) (sss + 1), FatalError);
    int x = 0;
    size_t len;
    while (x + sss < numChars) {
      if (string[x+sss] == ' ') /* First character next line will be a blank.*/
	len = sss;
      else {
	if (string[x+sss-1] == ' ') /* Last character on line is a blank. */
	  len = sss;
	else {                 /* Look for a blank at which to break line. */
	  for (i = x + sss - 2; i > x; i--) if (string[i] == ' ') break;
	  if (i > x)
	    len = i - x + 1;
	  else
	    len = sss;
	}
      }
      memmove (substring, &string[x], len);
      substring[len] = NUL;
      for (i = 0; i < len; i++)
	 if (!IsPrintChr(substring[i])) substring[i] = '.';
      delim = PickDelimiter (substring, strlen(substring));
      sprintf (text, "%c%s%c -", delim, substring, delim);
      WriteOut (fp, text);
      WriteOut (fp, "\n");
      for (i = 0; i < cccBase; i++) WriteOut (fp, " ");
      x += len;
    }
    len = (size_t) (numChars - x);
    memmove (substring, &string[x], len);
    substring[len] = NUL;
    for (i = 0; i < len; i++)
       if (!IsPrintChr(substring[i])) substring[i] = '.';
    delim = PickDelimiter (substring, strlen(substring));
    sprintf (text, "%c%s%c", delim, substring, delim);
    WriteOut (fp, text);
    FreeMemory (substring, FatalError);
  }
  FreeMemory (text, FatalError);
  return;
}

/******************************************************************************
* ConvertDataType.
******************************************************************************/

void ConvertDataType (fromDataType, fromNumElems, fromPtr, toDataType,
		      toNumElems, toPtr)
long fromDataType;
long fromNumElems;              /* Only used if character data type. */
void *fromPtr;
long toDataType;
long toNumElems;                /* Only used if character data type. */
void *toPtr;
{
  switch (fromDataType) {
    /**************************************************************************
    * 1-byte signed integer to...
    **************************************************************************/
    case CDF_BYTE:
    case CDF_INT1:
      switch (toDataType) {
	case CDF_BYTE:
	case CDF_INT1:
	  *((Schar *) toPtr) = (Schar) *((Schar *) fromPtr);
	  break;
	case CDF_UINT1:
	  *((Uchar *) toPtr) = (Uchar) *((Schar *) fromPtr);
	  break;
	case CDF_INT2:
	  *((Int16 *) toPtr) = (Int16) *((Schar *) fromPtr);
	  break;
	case CDF_UINT2:
	  *((uInt16 *) toPtr) = (uInt16) *((Schar *) fromPtr);
	  break;
	case CDF_INT4:
	  *((Int32 *) toPtr) = (Int32) *((Schar *) fromPtr);
	  break;
	case CDF_UINT4:
	  *((uInt32 *) toPtr) = (uInt32) *((Schar *) fromPtr);
	  break;
	case CDF_FLOAT:
	case CDF_REAL4:
	  *((float *) toPtr) = (float) *((Schar *) fromPtr);
	  break;
	case CDF_EPOCH:
	case CDF_DOUBLE:
	case CDF_REAL8:
	  *((double *) toPtr) = (double) *((Schar *) fromPtr);
	  break;
	case CDF_CHAR:
	case CDF_UCHAR: {
	  int i, len;
	  char tmp[MAX_nonSTRING_VALUE_LEN+1];
	  sprintf (tmp, "%d", *((Schar *) fromPtr));
	  for (i = 0, len = strlen(tmp); i < toNumElems; i++) {
	     *((char *) toPtr + i) = (i < len ? tmp[i] : ' ');
	  }
	  break;
	}
      }
      break;
    /**************************************************************************
    * 1-byte unsigned integer to...
    **************************************************************************/
    case CDF_UINT1:
      switch (toDataType) {
	case CDF_BYTE:
	case CDF_INT1:
	  *((Schar *) toPtr) = (Schar) *((Uchar *) fromPtr);
	  break;
	case CDF_UINT1:
	  *((Uchar *) toPtr) = (Uchar) *((Uchar *) fromPtr);
	  break;
	case CDF_INT2:
	  *((Int16 *) toPtr) = (Int16) *((Uchar *) fromPtr);
	  break;
	case CDF_UINT2:
	  *((uInt16 *) toPtr) = (uInt16) *((Uchar *) fromPtr);
	  break;
	case CDF_INT4:
	  *((Int32 *) toPtr) = (Int32) *((Uchar *) fromPtr);
	  break;
	case CDF_UINT4:
	  *((uInt32 *) toPtr) = (uInt32) *((Uchar *) fromPtr);
	  break;
	case CDF_FLOAT:
	case CDF_REAL4:
	  *((float *) toPtr) = (float) *((Uchar *) fromPtr);
	  break;
	case CDF_EPOCH:
	case CDF_DOUBLE:
	case CDF_REAL8:
	  *((double *) toPtr) = (double) *((Uchar *) fromPtr);
	  break;
	case CDF_CHAR:
	case CDF_UCHAR: {
	  int i, len;
	  char tmp[MAX_nonSTRING_VALUE_LEN+1];
	  sprintf (tmp, "%u", *((Uchar *) fromPtr));
	  for (i = 0, len = strlen(tmp); i < toNumElems; i++) {
	     *((char *) toPtr + i) = (i < len ? tmp[i] : ' ');
	  }
	  break;
	}
      }
      break;
    /**************************************************************************
    * 2-byte signed integer to...
    **************************************************************************/
    case CDF_INT2:
      switch (toDataType) {
	case CDF_BYTE:
	case CDF_INT1:
	  *((Schar *) toPtr) = (Schar) *((Int16 *) fromPtr);
	  break;
	case CDF_UINT1:
	  *((Uchar *) toPtr) = (Uchar) *((Int16 *) fromPtr);
	  break;
	case CDF_INT2:
	  *((Int16 *) toPtr) = (Int16) *((Int16 *) fromPtr);
	  break;
	case CDF_UINT2:
	  *((uInt16 *) toPtr) = (uInt16) *((Int16 *) fromPtr);
	  break;
	case CDF_INT4:
	  *((Int32 *) toPtr) = (Int32) *((Int16 *) fromPtr);
	  break;
	case CDF_UINT4:
	  *((uInt32 *) toPtr) = (uInt32) *((Int16 *) fromPtr);
	  break;
	case CDF_FLOAT:
	case CDF_REAL4:
	  *((float *) toPtr) = (float) *((Int16 *) fromPtr);
	  break;
	case CDF_EPOCH:
	case CDF_DOUBLE:
	case CDF_REAL8:
	  *((double *) toPtr) = (double) *((Int16 *) fromPtr);
	  break;
	case CDF_CHAR:
	case CDF_UCHAR: {
	  int i, len;
	  char tmp[MAX_nonSTRING_VALUE_LEN+1];
	  sprintf (tmp, "%d", (int) *((Int16 *) fromPtr));
	  for (i = 0, len = strlen(tmp); i < toNumElems; i++) {
	     *((char *) toPtr + i) = (i < len ? tmp[i] : ' ');
	  }
	  break;
	}
      }
      break;
    /**************************************************************************
    * 2-byte unsigned integer to...
    **************************************************************************/
    case CDF_UINT2:
      switch (toDataType) {
	case CDF_BYTE:
	case CDF_INT1:
	  *((Schar *) toPtr) = (Schar) *((uInt16 *) fromPtr);
	  break;
	case CDF_UINT1:
	  *((Uchar *) toPtr) = (Uchar) *((uInt16 *) fromPtr);
	  break;
	case CDF_INT2:
	  *((Int16 *) toPtr) = (Int16) *((uInt16 *) fromPtr);
	  break;
	case CDF_UINT2:
	  *((uInt16 *) toPtr) = (uInt16) *((uInt16 *) fromPtr);
	  break;
	case CDF_INT4:
	  *((Int32 *) toPtr) = (Int32) *((uInt16 *) fromPtr);
	  break;
	case CDF_UINT4:
	  *((uInt32 *) toPtr) = (uInt32) *((uInt16 *) fromPtr);
	  break;
	case CDF_FLOAT:
	case CDF_REAL4:
	  *((float *) toPtr) = (float) *((uInt16 *) fromPtr);
	  break;
	case CDF_EPOCH:
	case CDF_DOUBLE:
	case CDF_REAL8:
	  *((double *) toPtr) = (double) *((uInt16 *) fromPtr);
	  break;
	case CDF_CHAR:
	case CDF_UCHAR: {
	  int i, len;
	  char tmp[MAX_nonSTRING_VALUE_LEN+1];
	  sprintf (tmp, "%u", (int) *((uInt16 *) fromPtr));
	  for (i = 0, len = strlen(tmp); i < toNumElems; i++) {
	     *((char *) toPtr + i) = (i < len ? tmp[i] : ' ');
	  }
	  break;
	}
      }
      break;
    /**************************************************************************
    * 4-byte signed integer to...
    **************************************************************************/
    case CDF_INT4:
      switch (toDataType) {
	case CDF_BYTE:
	case CDF_INT1:
	  *((Schar *) toPtr) = (Schar) *((Int32 *) fromPtr);
	  break;
	case CDF_UINT1:
	  *((Uchar *) toPtr) = (Uchar) *((Int32 *) fromPtr);
	  break;
	case CDF_INT2:
	  *((Int16 *) toPtr) = (Int16) *((Int32 *) fromPtr);
	  break;
	case CDF_UINT2:
	  *((uInt16 *) toPtr) = (uInt16) *((Int32 *) fromPtr);
	  break;
	case CDF_INT4:
	  *((Int32 *) toPtr) = (Int32) *((Int32 *) fromPtr);
	  break;
	case CDF_UINT4:
	  *((uInt32 *) toPtr) = (uInt32) *((Int32 *) fromPtr);
	  break;
	case CDF_FLOAT:
	case CDF_REAL4:
	  *((float *) toPtr) = (float) *((Int32 *) fromPtr);
	  break;
	case CDF_EPOCH:
	case CDF_DOUBLE:
	case CDF_REAL8:
	  *((double *) toPtr) = (double) *((Int32 *) fromPtr);
	  break;
	case CDF_CHAR:
	case CDF_UCHAR: {
	  int i, len;
	  char tmp[MAX_nonSTRING_VALUE_LEN+1];
	  sprintf (tmp, "%ld", (long) *((Int32 *) fromPtr));
	  for (i = 0, len = strlen(tmp); i < toNumElems; i++) {
	     *((char *) toPtr + i) = (i < len ? tmp[i] : ' ');
	  }
	  break;
	}
      }
      break;
    /**************************************************************************
    * 4-byte unsigned integer to...
    **************************************************************************/
    case CDF_UINT4:
      switch (toDataType) {
	case CDF_BYTE:
	case CDF_INT1:
	  *((Schar *) toPtr) = (Schar) *((uInt32 *) fromPtr);
	  break;
	case CDF_UINT1:
	  *((Uchar *) toPtr) = (Uchar) *((uInt32 *) fromPtr);
	  break;
	case CDF_INT2:
	  *((Int16 *) toPtr) = (Int16) *((uInt32 *) fromPtr);
	  break;
	case CDF_UINT2:
	  *((uInt16 *) toPtr) = (uInt16) *((uInt32 *) fromPtr);
	  break;
	case CDF_INT4:
	  *((Int32 *) toPtr) = (Int32) *((uInt32 *) fromPtr);
	  break;
	case CDF_UINT4:
	  *((uInt32 *) toPtr) = (uInt32) *((uInt32 *) fromPtr);
	  break;
	case CDF_FLOAT:
	case CDF_REAL4:
	  *((float *) toPtr) = (float) *((uInt32 *) fromPtr);
	  break;
	case CDF_EPOCH:
	case CDF_DOUBLE:
	case CDF_REAL8:
	  *((double *) toPtr) = (double) *((uInt32 *) fromPtr);
	  break;
	case CDF_CHAR:
	case CDF_UCHAR: {
	  int i, len;
	  char tmp[MAX_nonSTRING_VALUE_LEN+1];
	  sprintf (tmp, "%lu", (unsigned long) *((uInt32 *) fromPtr));
	  for (i = 0, len = strlen(tmp); i < toNumElems; i++) {
	     *((char *) toPtr + i) = (i < len ? tmp[i] : ' ');
	  }
	  break;
	}
      }
      break;
    /**************************************************************************
    * 4-byte floating-point to...
    **************************************************************************/
    case CDF_FLOAT:
    case CDF_REAL4:
      switch (toDataType) {
	case CDF_BYTE:
	case CDF_INT1:
	  *((Schar *) toPtr) = (Schar) *((float *) fromPtr);
	  break;
	case CDF_UINT1:
	  *((Uchar *) toPtr) = (Uchar) *((float *) fromPtr);
	  break;
	case CDF_INT2:
	  *((Int16 *) toPtr) = (Int16) *((float *) fromPtr);
	  break;
	case CDF_UINT2:
	  *((uInt16 *) toPtr) = (uInt16) *((float *) fromPtr);
	  break;
	case CDF_INT4:
	  *((Int32 *) toPtr) = (Int32) *((float *) fromPtr);
	  break;
	case CDF_UINT4:
	  *((uInt32 *) toPtr) = (uInt32) *((float *) fromPtr);
	  break;
	case CDF_FLOAT:
	case CDF_REAL4:
	  *((float *) toPtr) = (float) *((float *) fromPtr);
	  break;
	case CDF_EPOCH:
	case CDF_DOUBLE:
	case CDF_REAL8:
	  *((double *) toPtr) = (double) *((float *) fromPtr);
	  break;
	case CDF_CHAR:
	case CDF_UCHAR: {
	  int i, len;
	  char tmp[MAX_nonSTRING_VALUE_LEN+1];
	  sprintf (tmp, "%f", *((float *) fromPtr));
	  for (i = 0, len = strlen(tmp); i < toNumElems; i++) {
	     *((char *) toPtr + i) = (i < len ? tmp[i] : ' ');
	  }
	  break;
	}
      }
      break;
    /**************************************************************************
    * 8-byte floating-point to...
    **************************************************************************/
    case CDF_EPOCH:
    case CDF_DOUBLE:
    case CDF_REAL8:
      switch (toDataType) {
	case CDF_BYTE:
	case CDF_INT1:
	  *((Schar *) toPtr) = (Schar) *((double *) fromPtr);
	  break;
	case CDF_UINT1:
	  *((Uchar *) toPtr) = (Uchar) *((double *) fromPtr);
	  break;
	case CDF_INT2:
	  *((Int16 *) toPtr) = (Int16) *((double *) fromPtr);
	  break;
	case CDF_UINT2:
	  *((uInt16 *) toPtr) = (uInt16) *((double *) fromPtr);
	  break;
	case CDF_INT4:
	  *((Int32 *) toPtr) = (Int32) *((double *) fromPtr);
	  break;
	case CDF_UINT4:
	  *((uInt32 *) toPtr) = (uInt32) *((double *) fromPtr);
	  break;
	case CDF_FLOAT:
	case CDF_REAL4:
	  *((float *) toPtr) = (float) *((double *) fromPtr);
	  break;
	case CDF_EPOCH:
	case CDF_DOUBLE:
	case CDF_REAL8:
	  *((double *) toPtr) = (double) *((double *) fromPtr);
	  break;
	case CDF_CHAR:
	case CDF_UCHAR: {
	  int i, len;
	  char tmp[MAX_nonSTRING_VALUE_LEN+1];
	  sprintf (tmp, "%f", *((double *) fromPtr));
	  for (i = 0, len = strlen(tmp); i < toNumElems; i++) {
	     *((char *) toPtr + i) = (i < len ? tmp[i] : ' ');
	  }
	  break;
	}
      }
      break;
    /**************************************************************************
    * character (string) to...
    **************************************************************************/
    case CDF_CHAR:
    case CDF_UCHAR: {
      size_t nBytes = (size_t) (fromNumElems + 1);
      char *str = (char *) AllocateMemory (nBytes, FatalError);
      memmove (str, fromPtr, (size_t) fromNumElems);
      str[(int)fromNumElems] = NUL;
      switch (toDataType) {
	case CDF_BYTE:
	case CDF_INT1: {
	  int tmp;
	  if (sscanf(str,"%d",&tmp) < 1)
	    *((Schar *) toPtr) = 0;
	  else
	    *((Schar *) toPtr) = (Schar) tmp;
	  break;
	}
	case CDF_UINT1: {
	  unsigned int tmp;
	  if (sscanf(str,"%u",&tmp) < 1)
	    *((Uchar *) toPtr) = 0;
	  else
	    *((Uchar *) toPtr) = (Uchar) tmp;
	  break;
	}
	case CDF_INT2: {
	  int tmp;
	  if (sscanf(str,"%d",&tmp) < 1)
	    *((Int16 *) toPtr) = 0;
	  else
	    *((Int16 *) toPtr) = (Int16) tmp;
	  break;
	}
	case CDF_UINT2: {
	  unsigned int tmp;
	  if (sscanf(str,"%u",&tmp) < 1)
	    *((uInt16 *) toPtr) = 0;
	  else
	    *((uInt16 *) toPtr) = (uInt16) tmp;
	  break;
	}
	case CDF_INT4: {
	  long tmp;
	  if (sscanf(str,"%ld",&tmp) < 1)
	    *((Int32 *) toPtr) = 0;
	  else
	    *((Int32 *) toPtr) = (Int32) tmp;
	  break;
	}
	case CDF_UINT4: {
	  unsigned long tmp;
	  if (sscanf(str,"%lu",&tmp) < 1)
	    *((uInt32 *) toPtr) = 0;
	  else
	    *((uInt32 *) toPtr) = (uInt32) tmp;
	  break;
	}
	case CDF_FLOAT:
	case CDF_REAL4: {
	  float tmp;
	  if (sscanf(str,"%g",&tmp) < 1)
	    *((float *) toPtr) = 0.0;
	  else
	    *((float *) toPtr) = tmp;
	  break;
	}
	case CDF_EPOCH:
	case CDF_DOUBLE:
	case CDF_REAL8: {
	  double tmp;
	  if (sscanf(str,"%lg",&tmp) < 1)
	    *((double *) toPtr) = 0.0;
	  else
	    *((double *) toPtr) = tmp;
	  break;
	}
	case CDF_CHAR:
	case CDF_UCHAR: {
	  int i;
	  for (i = 0; i < toNumElems; i++) {
	     *((char *) toPtr + i) = (i < fromNumElems ? str[i] : ' ');
	  }
	  break;
	}
      }
      FreeMemory (str, FatalError);
      break;
    }
  }
  return;
}

/******************************************************************************
* WhichFormat.
******************************************************************************/

long WhichFormat (token)
char *token;
{
  if (!strncmpIgCase(token,"SINGLE",6)) return SINGLE_FILE;
  if (!strncmpIgCase(token,"MULTI",5)) return MULTI_FILE;
  return -1;
}

/******************************************************************************
* WhichMajority.
******************************************************************************/

long WhichMajority (token)
char *token;
{
  if (!strncmpIgCase(token,"ROW",3)) return ROW_MAJOR;
  if (!strncmpIgCase(token,"COL",3) ||
      !strncmp(token,"COLUMN",6)) return COL_MAJOR;
  return -1;
}

/******************************************************************************
* WhichEncoding.  `MIPSEL' (DECstation) and `MIPSEB' (SGi) are valid synonyms
*                 in order to remain compatible with previous releases of CDF.
******************************************************************************/

long WhichEncoding (token)
char *token;
{
  if (!strncmpIgCase(token,"HOST",4)) return HOST_ENCODING;
  if (!strncmpIgCase(token,"NETWORK",6)) return NETWORK_ENCODING;
  if (!strncmpIgCase(token,"VAX",3)) return VAX_ENCODING;
  if (!strncmpIgCase(token,"SUN",3)) return SUN_ENCODING;
  if (!strncmpIgCase(token,"SGi",3) ||
      !strncmpIgCase(token,"MIPSEB",6)) return SGi_ENCODING;
  if (!strncmpIgCase(token,"DECSTATION",10) ||
      !strncmpIgCase(token,"MIPSEL",6)) return DECSTATION_ENCODING;
  if (!strncmpIgCase(token,"IBMRS",5)) return IBMRS_ENCODING;
  if (!strncmpIgCase(token,"IBMPC",5)) return IBMPC_ENCODING;
  if (!strncmpIgCase(token,"HP",2)) return HP_ENCODING;
  if (!strncmpIgCase(token,"NeXT",4)) return NeXT_ENCODING;
  if (!strncmpIgCase(token,"ALPHAOSF1",9)) return ALPHAOSF1_ENCODING;
  if (!strncmpIgCase(token,"ALPHAVMSd",9)) return ALPHAVMSd_ENCODING;
  if (!strncmpIgCase(token,"ALPHAVMSg",9)) return ALPHAVMSg_ENCODING;
  if (!strncmpIgCase(token,"MAC",3)) return MAC_ENCODING;
  return -1;
}

/******************************************************************************
* WhichDataType.
******************************************************************************/

long WhichDataType (token)
char *token;
{
  if (!strncmpIgCase(token,"BYTE",4)) return CDF_BYTE;
  if (!strncmpIgCase(token,"INT1",4)) return CDF_INT1;
  if (!strncmpIgCase(token,"UINT1",5)) return CDF_UINT1;
  if (!strncmpIgCase(token,"INT2",4)) return CDF_INT2;
  if (!strncmpIgCase(token,"UINT2",5)) return CDF_UINT2;
  if (!strncmpIgCase(token,"INT4",4)) return CDF_INT4;
  if (!strncmpIgCase(token,"UINT4",5)) return CDF_UINT4;
  if (!strncmpIgCase(token,"REAL4",5)) return CDF_REAL4;
  if (!strncmpIgCase(token,"FLOAT",5)) return CDF_FLOAT;
  if (!strncmpIgCase(token,"REAL8",5)) return CDF_REAL8;
  if (!strncmpIgCase(token,"DOUBLE",6)) return CDF_DOUBLE;
  if (!strncmpIgCase(token,"CHAR",4)) return CDF_CHAR;
  if (!strncmpIgCase(token,"UCHAR",5)) return CDF_UCHAR;
  if (!strncmpIgCase(token,"EPOCH",5)) return CDF_EPOCH;
  if (!strncmpIgCase(token,"CDF_BYTE",8)) return CDF_BYTE;
  if (!strncmpIgCase(token,"CDF_INT1",8)) return CDF_INT1;
  if (!strncmpIgCase(token,"CDF_UINT1",9)) return CDF_UINT1;
  if (!strncmpIgCase(token,"CDF_INT2",8)) return CDF_INT2;
  if (!strncmpIgCase(token,"CDF_UINT2",9)) return CDF_UINT2;
  if (!strncmpIgCase(token,"CDF_INT4",8)) return CDF_INT4;
  if (!strncmpIgCase(token,"CDF_UINT4",9)) return CDF_UINT4;
  if (!strncmpIgCase(token,"CDF_REAL4",9)) return CDF_REAL4;
  if (!strncmpIgCase(token,"CDF_FLOAT",9)) return CDF_FLOAT;
  if (!strncmpIgCase(token,"CDF_REAL8",9)) return CDF_REAL8;
  if (!strncmpIgCase(token,"CDF_DOUBLE",10)) return CDF_DOUBLE;
  if (!strncmpIgCase(token,"CDF_CHAR",8)) return CDF_CHAR;
  if (!strncmpIgCase(token,"CDF_UCHAR",9)) return CDF_UCHAR;
  if (!strncmpIgCase(token,"CDF_EPOCH",9)) return CDF_EPOCH;
  return -1;
}

/******************************************************************************
* DecodeValues.
*     Memory for the values is allocated here but is NOT freed (unless an
* error occurred.)
******************************************************************************/

Logical DecodeValues (string, dataType, numElems, values, style)
char *string;
long dataType;
long *numElems;
void **values;
int style;		/* EPOCH style. */
{
if (STRINGdataType(dataType)) {
  /****************************************************************************
  * Delimited character string.
  ****************************************************************************/
  char delim;
  char *firstDelim, *secondDelim;
  char *ptr = string;
  while (*ptr != NUL && IsSpaceChr(*ptr)) ptr++;    /* Skip to delimiter. */
  if (*ptr == NUL) return FALSE;		    /* Null-string. */
  delim = *ptr;
  firstDelim = ptr;
  secondDelim = strchr (ptr + 1, delim);
  if (secondDelim == NULL) return FALSE;
  *numElems = secondDelim - firstDelim - 1;
  *values = AllocateMemory ((size_t) (*numElems + 1), FatalError);
  memmove (*values, firstDelim + 1, (size_t) *numElems);
  ((char *) *values)[(int)(*numElems)] = NUL;
}
else {
  /****************************************************************************
  * Comma separated values.
  ****************************************************************************/
  int i, Ncommas; char *ptr;
  for (Ncommas = 0, i = 0; string[i] != NUL; i++)       /* Count commas. */
     if (string[i] == ',') Ncommas++;
  *numElems = Ncommas + 1;
  *values = AllocateMemory ((size_t) (*numElems * CDFelemSize(dataType)),
			    FatalError);
  for (ptr = string, i = 0; i < *numElems; i++) {
     switch (dataType) {
       case CDF_BYTE:
       case CDF_INT1: {
	 int temp;
	 if (sscanf(ptr,"%d",&temp) != 1) {
	   FreeMemory (*values, FatalError);
	   return FALSE;
	 }
	 if (temp < -128 || temp > 127) {
	   FreeMemory (*values, FatalError);
	   return FALSE;
	 }
	 *((Schar *) *values + i) = (Schar) temp;
	 break;
       }
       case CDF_UINT1: {
	 int temp;
	 if (sscanf(ptr,"%d",&temp) != 1) {
	   FreeMemory (*values, FatalError);
	   return FALSE;
	 }
	 if (temp < 0 || temp > 255) {
	   FreeMemory (*values, FatalError);
	   return FALSE;
	 }
	 *((Uchar *) *values + i) = (Uchar) temp;
	 break;
       }
       case CDF_INT2: {
	 if (sscanf(ptr,"%hd",(Int16 *) *values + i) != 1) {
	   FreeMemory (*values, FatalError);
	   return FALSE;
	 }
	 break;
       }
       case CDF_UINT2: {
	 long temp;
	 if (sscanf(ptr,"%ld",&temp) != 1) {
	   FreeMemory (*values, FatalError);
	   return FALSE;
	 }
	 if (temp < 0 || temp > 65535L) {
	   FreeMemory (*values, FatalError);
	   return FALSE;
	 }
	 *((uInt16 *) *values + i) = (uInt16) temp;
	 break;
       }
       case CDF_INT4:
	 if (!DecodeInt32(ptr,(Int32 *) *values + i)) {
	   FreeMemory (*values, FatalError);
	   return FALSE;
	 }
	 break;
       case CDF_UINT4:
	 if (!DecodeInt32u(ptr,(uInt32 *) *values + i)) {
	   FreeMemory (*values, FatalError);
	   return FALSE;
	 }
	 break;
       case CDF_REAL4:
       case CDF_FLOAT:
	 if (sscanf(ptr,"%f",(float *) *values + i) != 1) {
	   FreeMemory (*values, FatalError);
	   return FALSE;
	 }
	 break;
       case CDF_REAL8:
       case CDF_DOUBLE:
	 if (sscanf(ptr,"%lf",(double *) *values + i) != 1) {
	   FreeMemory (*values, FatalError);
	   return FALSE;
	 }
	 break;
       case CDF_EPOCH: {
	 double value;
	 switch (style) {
	   case EPOCH0_STYLE:
	     value = parseEPOCH (ptr);
	     break;
	   case EPOCH1_STYLE:
	     value = parseEPOCH1 (ptr);
	     break;
	   case EPOCH2_STYLE:
	     value = parseEPOCH2 (ptr);
	     break;
	   case EPOCH3_STYLE:
	     value = parseEPOCH3 (ptr);
	     break;
	   case EPOCHf_STYLE:
	     FreeMemory (*values, FatalError);
	     return DecodeValues(string,CDF_DOUBLE,numElems,values,style);
	   case EPOCHx_STYLE:
	     value = parseEPOCH (ptr);	/* Assume EPOCH0_STYLE instead. */
	     break;
	 }
	 if (value == ILLEGAL_EPOCH_VALUE) {
	   FreeMemory (*values, FatalError);
	   return FALSE;
	 }
	 else
	   *((double *) *values + i) = value;
	 break;
       }
     }
     ptr = strchr (ptr, ',') + 1;      /* Garbage last time, doesn't matter. */
  }
}
return TRUE;
}

/******************************************************************************
* DecodeInt32.
*    This routine is necessary because DEC Alphas running OpenVMS do not
* decode "-2147483648" properly.
******************************************************************************/

static Logical DecodeInt32 (text, binary)
char *text;
Int32 *binary;
{
  if (EqStringsIgLTws(text,"-2147483648"))
#if defined(dos)
    *binary = (Int32) -2147483648L;
#else
    *binary = (Int32) 0x80000000;
#endif
  else
    if (sscanf(text,Int32FORMAT,binary) != 1) return FALSE;
  return TRUE;
}

/******************************************************************************
* DecodeInt32u.
*    This routine is necessary because VAX/VMS systems do not properly decode
* unsigned values.
******************************************************************************/

static Logical DecodeInt32u (text, binary)
char *text;
uInt32 *binary;
{
   char *start = text;
   char *end, temp[MAX_ENCODED_INT4_LEN+1];
   int len; unsigned long tempB;
   while (*start != NUL && IsSpaceChr(*start)) start++;
   if (*start == NUL) return FALSE;
   end = start;
   while (*end != NUL && IsDigitChr(*end)) end++;
   len = (int) (end - start);
   if (len > MAX_ENCODED_INT4_LEN) return FALSE;
   strcpyX (temp, start, MINIMUM(len,MAX_ENCODED_INT4_LEN));
   if (len < MAX_ENCODED_INT4_LEN) {
     if (sscanf(temp,"%ld",&tempB) != 1) return FALSE;
   }
   else {
     if (strcmp(temp,"4294967295") > 0) return FALSE;
     if (sscanf(&temp[1],"%ld",&tempB) != 1) return FALSE;
#if defined(dos)
     tempB += (1000000000UL * (temp[0] - '0'));
#else
     tempB += ((unsigned long) 1000000000 * (temp[0] - '0'));
#endif
   }
   *binary = (uInt32) tempB;
   return TRUE;
}

/******************************************************************************
* DecodeVariances.
******************************************************************************/

Logical DecodeVariances (mask, numDims, recVary, dimVarys)
char *mask;
long numDims;
long *recVary;
long dimVarys[];
{
  int dimN, charN;

  switch (mask[0]) {
    case 'T':
    case 't':
      *recVary = VARY;
      break;
    case 'F':
    case 'f':
      *recVary = NOVARY;
      break;
    default:
      return FALSE;
  }

  for (dimN = 0, charN = 2; dimN < numDims; dimN++, charN++) {
     switch (mask[charN]) {
       case 'T':
       case 't':
	 dimVarys[dimN] = VARY;
	 break;
       case 'F':
       case 'f':
	 dimVarys[dimN] = NOVARY;
	 break;
       default:
	 return FALSE;
   }
  }

  return TRUE;
}

/******************************************************************************
* DecodeDelimitedString.
******************************************************************************/

Logical DecodeDelimitedString (dString, pString)
char *dString;          /* Delimited string. */
char *pString;          /* Parsed (decoded) string. */
{
  char *d1 = dString;           /* First delimiter. */
  char *d2;                     /* Second delimiter. */
  size_t len;                   /* Size of decoded string. */

  while (*d1 != NUL && IsSpaceChr(*d1)) d1++;
  if (*d1 == NUL) return FALSE;

  d2 = d1 + 1;
  while (*d2 != NUL && *d2 != *d1) d2++;
  if (*d2 == NUL) return FALSE;

  len = (size_t) (d2 - d1 - 1);
  memmove (pString, d1 + 1, len);
  pString[len] = NUL;

  return TRUE;
}

/******************************************************************************
* DecodeDimensionality.
******************************************************************************/

Logical DecodeDimensionality (string, numDims, dimSizes)
char *string;
long *numDims;
long *dimSizes;
{
  char *cp;
  /****************************************************************************
  * The number of dimensions should be first.
  ****************************************************************************/
  cp = NextNonSpace (string);
  if (sscanf(cp,"%ld",numDims) != 1) return FALSE;
  if (*numDims < 0) return FALSE;
  cp = NextNonDigit (cp);
  /****************************************************************************
  * Skip past the `:' and `['.
  ****************************************************************************/
  cp = NextNonSpace (cp);
  if (*cp != ':') return FALSE;
  cp = NextNonSpace (cp+1);
  if (*cp != '[') return FALSE;
  cp = NextNonSpace (cp+1);
  /****************************************************************************
  * Decode each dimension size.
  ****************************************************************************/
  if (*numDims > 0) {
    int dimN, lastDim = (int) (*numDims - 1);
    for (dimN = 0; dimN <= lastDim; dimN++) {
       if (sscanf(cp,"%ld",&dimSizes[dimN]) != 1) return FALSE;
       cp = NextNonDigit (cp);
       cp = NextNonSpace (cp);
       if (dimN != lastDim) {
	 if (*cp != ',') return FALSE;
	 cp = NextNonSpace (cp+1);
       }
    }
  }
  /****************************************************************************
  * Check for a trailing `]'.
  ****************************************************************************/
  if (*cp != ']') return FALSE;
  /****************************************************************************
  * Everything went Ok, return TRUE;
  ****************************************************************************/
  return TRUE;
}

/******************************************************************************
* DecodeRecordAndIndices.
******************************************************************************/

Logical DecodeRecordAndIndices (string, recN, nIndices, indices)
char *string;
long *recN;			/* Can be NULL if not needed. */
long *nIndices;
long indices[CDF_MAX_DIMS];
{
  char *cp; long recNt;
  /****************************************************************************
  * The record number will be first if it exists;
  ****************************************************************************/
  cp = NextNonSpace (string);
  if (*cp == '[') {
    recNt = 0;
  }
  else {
    if (sscanf(cp,"%ld",&recNt) != 1) return FALSE;
    recNt -= 1;
    if (recNt < 0) return FALSE;
    cp = NextNonDigit (cp);
    cp = NextNonSpace (cp);
    if (*cp != ':') return FALSE;
    cp = NextNonSpace (cp+1);
    if (*cp != '[') return FALSE;
  }
  ASSIGNnotNULL (recN, recNt)
  cp = NextNonSpace (cp+1);
  /****************************************************************************
  * Decode each index while counting how many there are.
  ****************************************************************************/
  *nIndices = 0;
  if (*cp == ']') return TRUE;
  for (;;) {
     if (*nIndices == CDF_MAX_DIMS) return FALSE;       /* Too many indices. */
     if (sscanf(cp,"%ld",&indices[(int)(*nIndices)]) != 1) return FALSE;
     indices[(int)(*nIndices)] -= 1;
     if (indices[(int)(*nIndices)] < 0) return FALSE;
     *nIndices += 1;
     cp = NextNonDigit (cp);    /* Skip to space, comma, or `]'. */
     cp = NextNonSpace (cp);    /* Skip past spaces. */
     switch (*cp) {
       case ']':
	 return TRUE;
       case ',':
	 cp = NextNonSpace (cp + 1);
	 break;
       default:
	 return FALSE;
     }
  }
}

/******************************************************************************
* NextNonSpace.
******************************************************************************/

char *NextNonSpace (cp)
char *cp;
{
  while (IsSpaceChr(*cp)) cp++;
  return cp;
}

/******************************************************************************
* NextNonDigit.
******************************************************************************/

char *NextNonDigit (cp)
char *cp;
{
  while (IsDigitChr(*cp)) cp++;
  return cp;
}

/******************************************************************************
* FormatWidth.
*    Returns width of a format specifier [or zero if the width is unknown or
* illegal].
******************************************************************************/

int FormatWidth (format)
char *format;
{
  int width;
  char *ptr = format;
  /****************************************************************************
  * Skip past Fortran repeat count (eg. the `20' in `20I8').  Note that this
  * won't skip past a C `%'.
  ****************************************************************************/
  while (*ptr != NUL && (IsSpaceChr(*ptr) || IsDigitChr(*ptr))) ptr++;
  if (*ptr == NUL) return 0;
  /****************************************************************************
  * Skip past Fortran format type (eg. the `F' in `F4.1') or C `%' and/or
  * flags (one of which might be a `0').
  ****************************************************************************/
  while (*ptr != NUL && (!IsDigitChr(*ptr) || *ptr == '0')) ptr++;
  if (*ptr == NUL) return 0;
  /****************************************************************************
  * Decode format width.
  ****************************************************************************/
  if (sscanf(ptr,"%d",&width) != 1) width = 0;
  return width;
}

/******************************************************************************
* FormatPrecision.
*   Returns precision of a format specifier [or zero if the precision doesn't
* exist [eg. integer format]).
******************************************************************************/

int FormatPrecision (format)
char *format;
{
  int precision;
  char *ptr = strchr (format, '.');
  if (ptr == NULL)
    precision = 0;
  else
    if (sscanf(ptr+1,"%d",&precision) != 1) precision = 0;
  return precision;
}

/******************************************************************************
* EncodeValuesFormat.  Returns width of encoded values.
******************************************************************************/

int EncodeValuesFormat (dataType, numElems, binary, text, format, minWidth,
			maxWidth, style)
long dataType;          /* Data type of values. */
long numElems;          /* Number of elements (values). */
void *binary;           /* In: Values in binary. */
char *text;             /* Out: Encoded values. */
char *format;           /* If NULL, use default formats.  Not applicable if
			   a string data type. */
int minWidth;           /* Minimum width (including delimeters if a string
			   data type.  If zero, no minimum width (no blank
			   padding).  If positive, right justified (padding
			   on left side).  If negative, left justified
			   (padding on right side). */
int maxWidth;           /* Maximum width (including delimeters if a string
			   data type.  If zero, no maximum width (no
			   truncating). */
int style;		/* EPOCH style. */
{

  /****************************************************************************
  * Encode value(s).
  ****************************************************************************/

  if (STRINGdataType(dataType))
    EncodeString (numElems, binary, text, minWidth, maxWidth);
  else {
    long elemN;
    char tText[MAX_nonSTRING_VALUE_LEN+1];
    strcpyX (text, "", maxWidth);
    for (elemN = 0; elemN < numElems; elemN++) {
       /***********************************************************************
       * Encode each value (if it fits).
       ***********************************************************************/
       EncodeValueFormat (dataType, (Byte *) binary +
				    (size_t) (elemN*CDFelemSize(dataType)),
			  tText, format, 0, 0, style);
       if (maxWidth > 0) {
	 int n = strlen(tText);
	 if (strlen(text) + (elemN > 0 ? 2 : 0) + n > (size_t) maxWidth) {
	   /*******************************************************************
	   * This value won't fit.  Find a comma after which to put the
	   * truncation symbol (`...').  `i' starts at the NUL just in case
	   * this is the first value (`text' is a null-string).
	   *******************************************************************/
	   int i;
	   for (i = strlen(text); i != 0; i--)
	      if (text[i] == ',' &&
		  (i + strlen(" ...") < (size_t) maxWidth)) break;
	   if (i != 0)
	     strcpyX (&text[i+1], " ...", maxWidth);
	   else
	     switch (elemN) {
	       case 0:
		 CpyNcharacters (text, maxWidth, (int) '*');
		 break;
	       case 1:
		 if (strlen(text) + strlen(", ...") <= (size_t) maxWidth)
		   strcatX (text, ", ...", maxWidth);
		 else {
		   CpyNcharacters (text, maxWidth, (int) '*');
		 }
		 break;
	       default:
		 CpyNcharacters (text, maxWidth, (int) '*');
		 break;
	     }
	   break;
	 }
	 else
	   sprintf (&text[strlen(text)], "%s%s", (elemN > 0 ? ", " : ""),
		    tText);
       }
       else
	 sprintf (&text[strlen(text)], "%s%s", (elemN > 0 ? ", " : ""), tText);
    }

    /**************************************************************************
    * Blank pad to `minWidth' if necessary.
    **************************************************************************/

    if (minWidth != 0) {
      int n = strlen(text);
      if (minWidth > 0) {               /* Right justify. */
	if (n < minWidth) {
	  int i, pad = minWidth - n;
	  memmove (&text[pad], text, n + 1);
	  for (i = 0; i < pad; i++) text[i] = ' ';
	}
      }
      else {                    /* Left justify. */
	int min = -minWidth;
	if (n < min) {
	  int pad = min - n;
	  CatNcharacters (text, pad, (int) ' ');
	}
      }
    }
  }

  /****************************************************************************
  * Return length of encoded value(s).
  ****************************************************************************/

  return strlen(text);
}

/******************************************************************************
* EncodeValueFormat.
*    Returns width of encoded value.  Not for use with character string data
* types (CDF_CHAR or CDF_UCHAR).
******************************************************************************/

int EncodeValueFormat (dataType, binary, text, format, minWidth, maxWidth,
		       style)
long dataType;          /* Data type of value being encoded. */
void *binary;           /* In: Value in binary. */
char *text;             /* Out: Encoded value. */
char *format;           /* Format string to be used. */
int minWidth;           /* Minimum width (including delimeters if a string
			   data type.  If zero, no minimum width (no blank
			   padding).  If positive, right justified (padding
			   on left side).  If negative, left justified
			   (padding on right side). */
int maxWidth;           /* Maximum width.  If zero, no maximum width (no
			   asteriks if too big). */
int style;		/* EPOCH style. */
{
  long CdataType;
  char Cformat[MAX_cFORMAT_LEN+1];
  char tText[MAX_nonSTRING_VALUE_LEN+1];
  /****************************************************************************
  * Encode value.
  ****************************************************************************/
  if (EPOCHdataType(dataType)) {
    switch (style) {
      case EPOCHf_STYLE:
	return EncodeValueFormat(CDF_DOUBLE,binary,text,
			         format,minWidth,maxWidth,0);
      case EPOCHx_STYLE:
	encodeEPOCHx (*((double *) binary), format, tText);
	break;
      default:
	EncodeValue (CDF_EPOCH, binary, tText, style);
	break;
    }
  }
  else
    if (format == NULL)
      EncodeValue (dataType, binary, tText, 0);
    else
      if (strchr(format,'%') != NULL)
        if (GetCdataType(format,&CdataType))
	  UseCformat (dataType, CdataType, format, binary, tText);
        else
	  EncodeValue (dataType, binary, tText, 0);
      else
        if (FtoCformat(dataType,format,&CdataType,Cformat))
	  UseCformat (dataType, CdataType, Cformat, binary, tText);
        else
	  EncodeValue (dataType, binary, tText, 0);
  /****************************************************************************
  * Fill in asteriks if encoded value is too big.  Otherwise, copy encoded
  * value.
  ****************************************************************************/
  if (maxWidth > 0) {
    int n = strlen(tText);
    if (n > maxWidth)
      CpyNcharacters (text, maxWidth, (int) '*');
    else
      strcpyX (text, tText, maxWidth);
  }
  else
    strcpyX (text, tText, maxWidth);
  /****************************************************************************
  * Blank pad to `minWidth' if necessary.
  ****************************************************************************/
  if (minWidth != 0) {
    int n = strlen(text);
    if (minWidth > 0) {         /* Right justify. */
      if (n < minWidth) {
	int i, pad = minWidth - n;
	memmove (&text[pad], text, n + 1);
	for (i = 0; i < pad; i++) text[i] = ' ';
      }
    }
    else {                      /* Left justify. */
      int min = -minWidth;
      if (n < min) {
	int pad = min - n;
	CatNcharacters (text, pad, (int) ' ');
      }
    }
  }
  /****************************************************************************
  * Return length of encoded value.
  ****************************************************************************/
  return strlen(text);
}

/******************************************************************************
* EncodeValue.  Returns width of encoded value.
******************************************************************************/

int EncodeValue (dataType, binary, text, style)
long dataType;          /* CDF data type. */
void *binary;           /* In: Value to encode. */
char *text;             /* Out: Encoded value. */
int style;		/* In: EPOCH style. */
{
  MakeNUL (text);
  switch (dataType) {
    case CDF_BYTE:
    case CDF_INT1:
      sprintf (text, "%d", *((Schar *) binary));
      break;
    case CDF_INT2:
      sprintf (text, "%d", *((Int16 *) binary));
      break;
    case CDF_INT4:
      sprintf (text, Int32FORMAT, *((Int32 *) binary));
      break;
    case CDF_UINT1:
      sprintf (text, "%u", *((Uchar *) binary));
      break;
    case CDF_UINT2:
      sprintf (text, "%u", *((uInt16 *) binary));
      break;
    case CDF_UINT4:
      sprintf (text, Int32uFORMAT, *((uInt32 *) binary));
      break;
    case CDF_REAL4:
    case CDF_FLOAT: {
      if (NegativeZeroReal4((float *) binary))
	strcpyX (text, NEGATIVEzeroSYMBOL, 0);
      else {
	size_t len;
	char *ePtr, tText[80+1];
	sprintf (text, "%g", *((float *) binary));
	if ((ePtr = strchr(text,'e')) == NULL) {  /* eg., 0 */
	  if (strchr(text,'.') == NULL) strcatX (text,".0", 0);
	}
	else {
	  if (strchr(text,'.') == NULL) {  /* eg., 1e+07 */
	    len = (size_t) (ePtr - text);
	    strcpyX (tText, text, MINIMUM(len,80));
	    strcatX (tText, ".0", 0);
	    strcatX (tText, ePtr, 0);
	    strcpyX (text, tText, 0);
	  }
	}
      }
      break;
    }
    case CDF_REAL8:
    case CDF_DOUBLE: {
      if (NegativeZeroReal8((double *) binary))
	strcpyX (text, NEGATIVEzeroSYMBOL, 0);
      else {
	size_t len;
	char *ePtr, tText[80+1];
	sprintf (text, "%g", *((double *) binary));
	if ((ePtr = strchr(text,'e')) == NULL) {  /* eg., 0 */
	  if (strchr(text,'.') == NULL) strcatX (text,".0", 0);
	}
	else {
	  if (strchr(text,'.') == NULL) {  /* eg., 1e+07 */
	    len = (size_t) (ePtr - text);
	    strcpyX (tText, text, MINIMUM(len,80));
	    strcatX (tText, ".0", 0);
	    strcatX (tText, ePtr, 0);
	    strcpyX (text, tText, 0);
	  }
	}
      }
      break;
    }
    case CDF_CHAR:
      sprintf (text, "%c", *((Schar *) binary));
      break;
    case CDF_UCHAR:
      sprintf (text, "%c", *((Uchar *) binary));
      break;
    case CDF_EPOCH:
      switch (style) {
	case EPOCH0_STYLE:
	  encodeEPOCH (*((double *) binary), text);
	  break;
	case EPOCH1_STYLE:
	  encodeEPOCH1 (*((double *) binary), text);
	  break;
	case EPOCH2_STYLE:
	  encodeEPOCH2 (*((double *) binary), text);
	  break;
	case EPOCH3_STYLE:
	  encodeEPOCH3 (*((double *) binary), text);
	  break;
	case EPOCHf_STYLE:
	  return EncodeValue(CDF_DOUBLE,binary,text,0);
	case EPOCHx_STYLE:
	  /* Unsupported (a format specification is needed). */
	  break;
      }
      break;
  }
  return strlen(text);
}

/******************************************************************************
* EncodeString.  Returns width of encoded string (including delimeters).
******************************************************************************/

int EncodeString (nChars, iString, oString, minWidth, maxWidth)
long nChars;            /* Number of characters (elements). */
char *iString;          /* String to encode (input). */
char *oString;          /* Encoded string (output). */
int minWidth;           /* Minimum width (including delimeters if a string
			   data type.  If zero, no minimum width (no blank
			   padding).  If positive, right justified (padding
			   on left side).  If negative, left justified
			   (padding on right side). */
int maxWidth;           /* Maximum width (including delimeters).  If zero,
			   no maximum width (no truncating). */
{
  char delim = PickDelimiter (iString, (size_t) nChars); int i;
  /****************************************************************************
  * Encode string.
  ****************************************************************************/
  if (nChars + 2 > BOO(maxWidth == 0,(int)(nChars + 2),maxWidth)) {
    /**************************************************************************
    * Won't fit.  Should at least encode `"a..."'.  If not enough room, fill
    * in with astericks.
    **************************************************************************/
    if (maxWidth < 6) {
      CpyNcharacters (oString, maxWidth, (int) '*');
    }
    else {
      oString[0] = delim;
      for (i = 0; i < maxWidth - 5; i++) {
	 oString[i+1] = (IsPrintChr(iString[i]) ? iString[i] : '.');
      }
      oString[i+1] = '.';
      oString[i+2] = '.';
      oString[i+3] = '.';
      oString[i+4] = delim;
      oString[i+5] = NUL;
    }
  }
  else {
    /**************************************************************************
    * Fits.
    **************************************************************************/
    oString[0] = delim;
    for (i = 0; i < nChars; i++) {
       oString[i+1] = (IsPrintChr(iString[i]) ? iString[i] : '.');
    }
    oString[i+1] = delim;
    oString[i+2] = NUL;
  }
  /****************************************************************************
  * Blank pad to `minWidth' if necessary.
  ****************************************************************************/
  if (minWidth != 0) {
    int n = strlen(oString);
    if (minWidth > 0) {         /* Right justify. */
      if (n < minWidth) {
	int i, pad = minWidth - n;
	memmove (&oString[pad], oString, n + 1);
	for (i = 0; i < pad; i++) oString[i] = ' ';
      }
    }
    else {                      /* Left justify. */
      int min = -minWidth;
      if (n < min) {
	int pad = min - n;
	CatNcharacters (oString, pad, (int) ' ');
      }
    }
  }
  /****************************************************************************
  * Return length of encoded string.
  ****************************************************************************/
  return strlen(oString);
}

/******************************************************************************
* EncodeNegativeZero.
******************************************************************************/

void EncodeNegativeZero (string, format)
char *string;
char *format;
{
  char *ptr;
  /****************************************************************************
  * Encode 0.0 using format then check if the value was successfully encoded.
  ****************************************************************************/
  string[0] = NUL;
  sprintf(string, format, 0.0);
  if (NULstring(string)) {
    strcpyX (string, "-0.0(bad FORMAT)", 0);
    return;
  }
  /****************************************************************************
  * 0.0 was successfully encoded - put a negative sign at the beginning
  * (increasing the length of the string if necessary).
  ****************************************************************************/
  ptr = string;
  while (*ptr == ' ') ptr++;
  if (ptr == string) {
    memmove (&string[1], string, strlen(string) + 1);
    string[0] = '-';
  }
  else
    *(ptr - 1) = '-';
  return;
}

/******************************************************************************
* UseCformat.
*   Returns width of encoded value.  Not for use with CDF_EPOCH or CDF_(U)CHAR
* values.
******************************************************************************/

static int UseCformat (dataType, CdataType, Cformat, binary, text)
long dataType;          /* Value's actual data type.  This should never be
			   CDF_EPOCH. */
long CdataType;         /* Data type corresponding to C format.  This should
			   never be CDF_EPOCH. */
char *Cformat;          /* C format specification. */
void *binary;		/* Value to be encoded. */
char *text;		/* String into which to encode the value. */
{
  switch (dataType) {
    case CDF_BYTE:
    case CDF_INT1:
      switch (CdataType) {
        case CDF_BYTE:
        case CDF_INT1:
	  sprintf (text, Cformat, (Schar) *((Schar *) binary));
	  break;
        case CDF_UINT1:
	  sprintf (text, Cformat, (Uchar) *((Schar *) binary));
	  break;
        case CDF_INT2:
	  sprintf (text, Cformat, (Int16) *((Schar *) binary));
	  break;
        case CDF_UINT2:
	  sprintf (text, Cformat, (uInt16) *((Schar *) binary));
	  break;
        case CDF_INT4:
	  sprintf (text, Cformat, (Int32) *((Schar *) binary));
	  break;
        case CDF_UINT4:
	  sprintf (text, Cformat, (uInt32) *((Schar *) binary));
	  break;
        case CDF_REAL4:
        case CDF_FLOAT:
	  sprintf (text, Cformat, (float) *((Schar *) binary));
	  break;
        case CDF_REAL8:
        case CDF_DOUBLE:
	  sprintf (text, Cformat, (double) *((Schar *) binary));
	  break;
      }
      break;
    case CDF_UINT1:
      switch (CdataType) {
        case CDF_BYTE:
        case CDF_INT1:
	  sprintf (text, Cformat, (Schar) *((Uchar *) binary));
	  break;
        case CDF_UINT1:
	  sprintf (text, Cformat, (Uchar) *((Uchar *) binary));
	  break;
        case CDF_INT2:
	  sprintf (text, Cformat, (Int16) *((Uchar *) binary));
	  break;
        case CDF_UINT2:
	  sprintf (text, Cformat, (uInt16) *((Uchar *) binary));
	  break;
        case CDF_INT4:
	  sprintf (text, Cformat, (Int32) *((Uchar *) binary));
	  break;
        case CDF_UINT4:
	  sprintf (text, Cformat, (uInt32) *((Uchar *) binary));
	  break;
        case CDF_REAL4:
        case CDF_FLOAT:
	  sprintf (text, Cformat, (float) *((Uchar *) binary));
	  break;
        case CDF_REAL8:
        case CDF_DOUBLE:
	  sprintf (text, Cformat, (double) *((Uchar *) binary));
	  break;
      }
      break;
    case CDF_INT2:
      switch (CdataType) {
        case CDF_BYTE:
        case CDF_INT1:
	  sprintf (text, Cformat, (Schar) *((Int16 *) binary));
	  break;
        case CDF_UINT1:
	  sprintf (text, Cformat, (Uchar) *((Int16 *) binary));
	  break;
        case CDF_INT2:
	  sprintf (text, Cformat, (Int16) *((Int16 *) binary));
	  break;
        case CDF_UINT2:
	  sprintf (text, Cformat, (uInt16) *((Int16 *) binary));
	  break;
        case CDF_INT4:
	  sprintf (text, Cformat, (Int32) *((Int16 *) binary));
	  break;
        case CDF_UINT4:
	  sprintf (text, Cformat, (uInt32) *((Int16 *) binary));
	  break;
        case CDF_REAL4:
        case CDF_FLOAT:
	  sprintf (text, Cformat, (float) *((Int16 *) binary));
	  break;
        case CDF_REAL8:
        case CDF_DOUBLE:
	  sprintf (text, Cformat, (double) *((Int16 *) binary));
	  break;
      }
      break;
    case CDF_UINT2:
      switch (CdataType) {
        case CDF_BYTE:
        case CDF_INT1:
	  sprintf (text, Cformat, (Schar) *((uInt16 *) binary));
	  break;
        case CDF_UINT1:
	  sprintf (text, Cformat, (Uchar) *((uInt16 *) binary));
	  break;
        case CDF_INT2:
	  sprintf (text, Cformat, (Int16) *((uInt16 *) binary));
	  break;
        case CDF_UINT2:
	  sprintf (text, Cformat, (uInt16) *((uInt16 *) binary));
	  break;
        case CDF_INT4:
	  sprintf (text, Cformat, (Int32) *((uInt16 *) binary));
	  break;
        case CDF_UINT4:
	  sprintf (text, Cformat, (uInt32) *((uInt16 *) binary));
	  break;
        case CDF_REAL4:
        case CDF_FLOAT:
	  sprintf (text, Cformat, (float) *((uInt16 *) binary));
	  break;
        case CDF_REAL8:
        case CDF_DOUBLE:
	  sprintf (text, Cformat, (double) *((uInt16 *) binary));
	  break;
      }
      break;
    case CDF_INT4:
      switch (CdataType) {
        case CDF_BYTE:
        case CDF_INT1:
	  sprintf (text, Cformat, (Schar) *((Int32 *) binary));
	  break;
        case CDF_UINT1:
	  sprintf (text, Cformat, (Uchar) *((Int32 *) binary));
	  break;
        case CDF_INT2:
	  sprintf (text, Cformat, (Int16) *((Int32 *) binary));
	  break;
        case CDF_UINT2:
	  sprintf (text, Cformat, (uInt16) *((Int32 *) binary));
	  break;
        case CDF_INT4:
	  sprintf (text, Cformat, (Int32) *((Int32 *) binary));
	  break;
        case CDF_UINT4:
	  sprintf (text, Cformat, (uInt32) *((Int32 *) binary));
	  break;
        case CDF_REAL4:
        case CDF_FLOAT:
	  sprintf (text, Cformat, (float) *((Int32 *) binary));
	  break;
        case CDF_REAL8:
        case CDF_DOUBLE:
	  sprintf (text, Cformat, (double) *((Int32 *) binary));
	  break;
      }
      break;
    case CDF_UINT4:
      switch (CdataType) {
        case CDF_BYTE:
        case CDF_INT1:
	  sprintf (text, Cformat, (Schar) *((uInt32 *) binary));
	  break;
        case CDF_UINT1:
	  sprintf (text, Cformat, (Uchar) *((uInt32 *) binary));
	  break;
        case CDF_INT2:
	  sprintf (text, Cformat, (Int16) *((uInt32 *) binary));
	  break;
        case CDF_UINT2:
	  sprintf (text, Cformat, (uInt16) *((uInt32 *) binary));
	  break;
        case CDF_INT4:
	  sprintf (text, Cformat, (Int32) *((uInt32 *) binary));
	  break;
        case CDF_UINT4:
	  sprintf (text, Cformat, (uInt32) *((uInt32 *) binary));
	  break;
        case CDF_REAL4:
        case CDF_FLOAT:
	  sprintf (text, Cformat, (float) *((uInt32 *) binary));
	  break;
        case CDF_REAL8:
        case CDF_DOUBLE:
	  sprintf (text, Cformat, (double) *((uInt32 *) binary));
	  break;
      }
      break;
    case CDF_REAL4:
    case CDF_FLOAT:
      switch (CdataType) {
        case CDF_BYTE:
        case CDF_INT1:
	  sprintf (text, Cformat, (Schar) *((float *) binary));
	  break;
        case CDF_UINT1:
	  sprintf (text, Cformat, (Uchar) *((float *) binary));
	  break;
        case CDF_INT2:
	  sprintf (text, Cformat, (Int16) *((float *) binary));
	  break;
        case CDF_UINT2:
	  sprintf (text, Cformat, (uInt16) *((float *) binary));
	  break;
        case CDF_INT4:
	  sprintf (text, Cformat, (Int32) *((float *) binary));
	  break;
        case CDF_UINT4:
	  sprintf (text, Cformat, (uInt32) *((float *) binary));
	  break;
        case CDF_REAL4:
        case CDF_FLOAT:
	  if (NegativeZeroReal4((float *) binary))
	    EncodeNegativeZero (text, Cformat);
	  else
	    sprintf (text, Cformat, (float) *((float *) binary));
	  break;
        case CDF_REAL8:
        case CDF_DOUBLE:
	  if (NegativeZeroReal8((double *) binary))
	    EncodeNegativeZero (text, Cformat);
	  else
	    sprintf (text, Cformat, (double) *((float *) binary));
	  break;
      }
      break;
    case CDF_REAL8:
    case CDF_DOUBLE:
      switch (CdataType) {
        case CDF_BYTE:
        case CDF_INT1:
	  sprintf (text, Cformat, (Schar) *((double *) binary));
	  break;
        case CDF_UINT1:
	  sprintf (text, Cformat, (Uchar) *((double *) binary));
	  break;
        case CDF_INT2:
	  sprintf (text, Cformat, (Int16) *((double *) binary));
	  break;
        case CDF_UINT2:
	  sprintf (text, Cformat, (uInt16) *((double *) binary));
	  break;
        case CDF_INT4:
	  sprintf (text, Cformat, (Int32) *((double *) binary));
	  break;
        case CDF_UINT4:
	  sprintf (text, Cformat, (uInt32) *((double *) binary));
	  break;
        case CDF_REAL4:
        case CDF_FLOAT:
	  if (NegativeZeroReal4((float *) binary))
	    EncodeNegativeZero (text, Cformat);
	  else
	    sprintf (text, Cformat, (float) *((double *) binary));
	  break;
        case CDF_REAL8:
        case CDF_DOUBLE:
	  if (NegativeZeroReal8((double *) binary))
	    EncodeNegativeZero (text, Cformat);
	  else
	    sprintf (text, Cformat, (double) *((double *) binary));
	  break;
      }
      break;
  }
  return strlen(text);
}

/******************************************************************************
* FtoCformat.  Returns TRUE if valid FORTRAN format specification.
******************************************************************************/

static Logical FtoCformat (dataType, Fformat, CdataType, Cformat)
long dataType;          /* Value's actual data type.  This should never be
			   CDF_EPOCH. */
char *Fformat;          /* FORTRAN format specification. */
long *CdataType;        /* Data type implied by C format specification. */
char *Cformat;          /* C format specification. */
{
  int nFound;
  int w, m, d;
  /****************************************************************************
  * Change `Fformat' to point to first non-blank, non-digit character.  This
  * will skip over a Fortran repeat count (eg. the `20' in `20F8.4').
  ****************************************************************************/
  while (*Fformat != NUL &&
         (IsSpaceChr(*Fformat) || IsDigitChr(*Fformat))) Fformat++;
  if (*Fformat == NUL) return FALSE;
  /****************************************************************************
  * Encode C format specification.
  ****************************************************************************/
  switch (Fformat[0]) {
    /**************************************************************************
    * Integer/decimal.
    **************************************************************************/
    case 'I':
    case 'i':
      nFound = sscanf (&Fformat[1], "%d.%d", &w, &m);
      switch (nFound) {
        case 0:
	  return FALSE;
        case 1:
	  switch (dataType) {
	    case CDF_BYTE:
	    case CDF_INT1:
	      sprintf (Cformat, "%%%dd", w);
	      *CdataType = CDF_INT1;
	      return TRUE;
	    case CDF_UINT1:
	      sprintf (Cformat, "%%%du", w);
	      *CdataType = CDF_UINT1;
	      return TRUE;
	    case CDF_INT2:
	      sprintf (Cformat, "%%%dd", w);
	      *CdataType = CDF_INT2;
	      return TRUE;
	    case CDF_UINT2:
	      sprintf (Cformat, "%%%du", w);
	      *CdataType = CDF_UINT2;
	      return TRUE;
	    case CDF_INT4:
	      sprintf (Cformat, "%%%d%sd", w, Int32FORMATmod);
	      *CdataType = CDF_INT4;
	      return TRUE;
	    case CDF_UINT4:
	      sprintf (Cformat, "%%%d%su", w, Int32FORMATmod);
	      *CdataType = CDF_UINT4;
	      return TRUE;
	    case CDF_REAL4:
	    case CDF_FLOAT:
	    case CDF_REAL8:
	    case CDF_DOUBLE:
	      sprintf (Cformat, "%%%d%sd", w, Int32FORMATmod);
	      *CdataType = CDF_INT4;
	      return TRUE;
	  }
	  return FALSE;
        case 2:
	  switch (dataType) {
	    case CDF_BYTE:
	    case CDF_INT1:
	      sprintf (Cformat, "%%%d.%dd", w, m);
	      *CdataType = CDF_INT1;
	      return TRUE;
	    case CDF_UINT1:
	      sprintf (Cformat, "%%%d.%du", w, m);
	      *CdataType = CDF_UINT1;
	      return TRUE;
	    case CDF_INT2:
	      sprintf (Cformat, "%%%d.%dd", w, m);
	      *CdataType = CDF_INT2;
	      return TRUE;
	    case CDF_UINT2:
	      sprintf (Cformat, "%%%d.%du", w, m);
	      *CdataType = CDF_UINT2;
	      return TRUE;
	    case CDF_INT4:
	      sprintf (Cformat, "%%%d.%d%sd", w, m, Int32FORMATmod);
	      *CdataType = CDF_INT4;
	      return TRUE;
	    case CDF_UINT4:
	      sprintf (Cformat, "%%%d.%d%su", w, m, Int32FORMATmod);
	      *CdataType = CDF_UINT4;
	      return TRUE;
	    case CDF_REAL4:
	    case CDF_FLOAT:
	    case CDF_REAL8:
	    case CDF_DOUBLE:
	      sprintf (Cformat, "%%%d.%d%sd", w, m, Int32FORMATmod);
	      *CdataType = CDF_INT4;
	      return TRUE;
	  }
	  return FALSE;
      }
      return FALSE;
    /**************************************************************************
    * Integer/octal.
    **************************************************************************/
    case 'O':
    case 'o':
      nFound = sscanf (&Fformat[1], "%d.%d", &w, &m);
      switch (nFound) {
        case 0:
	  return FALSE;
        case 1:
	  switch (dataType) {
	    case CDF_BYTE:
	    case CDF_INT1:
	      sprintf (Cformat, "%%%do", w);
	      *CdataType = CDF_UINT1;
	      return TRUE;
	    case CDF_UINT1:
	      sprintf (Cformat, "%%%do", w);
	      *CdataType = CDF_UINT1;
	      return TRUE;
	    case CDF_INT2:
	      sprintf (Cformat, "%%%do", w);
	      *CdataType = CDF_UINT2;
	      return TRUE;
	    case CDF_UINT2:
	      sprintf (Cformat, "%%%do", w);
	      *CdataType = CDF_UINT2;
	      return TRUE;
	    case CDF_INT4:
	      sprintf (Cformat, "%%%d%so", w, Int32FORMATmod);
	      *CdataType = CDF_UINT4;
	      return TRUE;
	    case CDF_UINT4:
	      sprintf (Cformat, "%%%d%so", w, Int32FORMATmod);
	      *CdataType = CDF_UINT4;
	      return TRUE;
	    case CDF_REAL4:
	    case CDF_FLOAT:
	    case CDF_REAL8:
	    case CDF_DOUBLE:
	      sprintf (Cformat, "%%%d%so", w, Int32FORMATmod);
	      *CdataType = CDF_UINT4;
	      return TRUE;
	  }
	  return FALSE;
        case 2:
	  switch (dataType) {
	    case CDF_BYTE:
	    case CDF_INT1:
	      sprintf (Cformat, "%%%d.%do", w, m);
	      *CdataType = CDF_UINT1;
	      return TRUE;
	    case CDF_UINT1:
	      sprintf (Cformat, "%%%d.%do", w, m);
	      *CdataType = CDF_UINT1;
	      return TRUE;
	    case CDF_INT2:
	      sprintf (Cformat, "%%%d.%do", w, m);
	      *CdataType = CDF_UINT2;
	      return TRUE;
	    case CDF_UINT2:
	      sprintf (Cformat, "%%%d.%do", w, m);
	      *CdataType = CDF_UINT2;
	      return TRUE;
	    case CDF_INT4:
	      sprintf (Cformat, "%%%d.%d%so", w, m, Int32FORMATmod);
	      *CdataType = CDF_UINT4;
	      return TRUE;
	    case CDF_UINT4:
	      sprintf (Cformat, "%%%d.%d%so", w, m, Int32FORMATmod);
	      *CdataType = CDF_UINT4;
	      return TRUE;
	    case CDF_REAL4:
	    case CDF_FLOAT:
	    case CDF_REAL8:
	    case CDF_DOUBLE:
	      sprintf (Cformat, "%%%d.%d%so", w, m, Int32FORMATmod);
	      *CdataType = CDF_UINT4;
	      return TRUE;
	  }
	  return FALSE;
      }
      return FALSE;
    /**************************************************************************
    * Integer/hexadecimal.
    **************************************************************************/
    case 'Z':
    case 'z':
      nFound = sscanf (&Fformat[1], "%d.%d", &w, &m);
      switch (nFound) {
        case 0:
	  return FALSE;
        case 1:
	  switch (dataType) {
	    case CDF_BYTE:
	    case CDF_INT1:
	      sprintf (Cformat, "%%%dX", w);
	      *CdataType = CDF_UINT1;
	      return TRUE;
	    case CDF_UINT1:
	      sprintf (Cformat, "%%%dX", w);
	      *CdataType = CDF_UINT1;
	      return TRUE;
	    case CDF_INT2:
	      sprintf (Cformat, "%%%dX", w);
	      *CdataType = CDF_UINT2;
	      return TRUE;
	    case CDF_UINT2:
	      sprintf (Cformat, "%%%dX", w);
	      *CdataType = CDF_UINT2;
	      return TRUE;
	    case CDF_INT4:
	      sprintf (Cformat, "%%%d%sX", w, Int32FORMATmod);
	      *CdataType = CDF_UINT4;
	      return TRUE;
	    case CDF_UINT4:
	      sprintf (Cformat, "%%%d%sX", w, Int32FORMATmod);
	      *CdataType = CDF_UINT4;
	      return TRUE;
	    case CDF_REAL4:
	    case CDF_FLOAT:
	    case CDF_REAL8:
	    case CDF_DOUBLE:
	      sprintf (Cformat, "%%%d%sX", w, Int32FORMATmod);
	      *CdataType = CDF_UINT4;
	      return TRUE;
	  }
	  return FALSE;
        case 2:
	  switch (dataType) {
	    case CDF_BYTE:
	    case CDF_INT1:
	      sprintf (Cformat, "%%%d.%dX", w, m);
	      *CdataType = CDF_UINT1;
	      return TRUE;
	    case CDF_UINT1:
	      sprintf (Cformat, "%%%d.%dX", w, m);
	      *CdataType = CDF_UINT1;
	      return TRUE;
	    case CDF_INT2:
	      sprintf (Cformat, "%%%d.%dX", w, m);
	      *CdataType = CDF_UINT2;
	      return TRUE;
	    case CDF_UINT2:
	      sprintf (Cformat, "%%%d.%dX", w, m);
	      *CdataType = CDF_UINT2;
	      return TRUE;
	    case CDF_INT4:
	      sprintf (Cformat, "%%%d.%d%sX", w, m, Int32FORMATmod);
	      *CdataType = CDF_UINT4;
	      return TRUE;
	    case CDF_UINT4:
	      sprintf (Cformat, "%%%d.%d%sX", w, m, Int32FORMATmod);
	      *CdataType = CDF_UINT4;
	      return TRUE;
	    case CDF_REAL4:
	    case CDF_FLOAT:
	    case CDF_REAL8:
	    case CDF_DOUBLE:
	      sprintf (Cformat, "%%%d.%d%sX", w, m, Int32FORMATmod);
	      *CdataType = CDF_UINT4;
	      return TRUE;
	  }
	  return FALSE;
      }
      return FALSE;
    /**************************************************************************
    * Floating-point/non-scientific notation (which is called...
    **************************************************************************/
    case 'F':
    case 'f':
      nFound = sscanf (&Fformat[1], "%d.%d", &w, &d);
      switch (nFound) {
        case 2:
	  sprintf (Cformat, "%%%d.%df", w, d);
	  *CdataType = CDF_DOUBLE;
	  return TRUE;
      }
      return FALSE;
    /**************************************************************************
    * Floating-point/scientific notation.
    **************************************************************************/
    case 'E':
    case 'e':
    case 'D':
    case 'd':
      nFound = sscanf (&Fformat[1], "%d.%d", &w, &d);
      switch (nFound) {
        case 2:
	  sprintf (Cformat, "%%%d.%de", w, d);
	  *CdataType = CDF_DOUBLE;
	  return TRUE;
      }
      return FALSE;
    /**************************************************************************
    * Floating-point/notation depends on value.
    **************************************************************************/
    case 'G':
    case 'g':
      nFound = sscanf (&Fformat[1], "%d.%d", &w, &d);
      switch (nFound) {
        case 2:
	  sprintf (Cformat, "%%%d.%dg", w, d);
	  *CdataType = CDF_DOUBLE;
	  return TRUE;
      }
      return FALSE;
    /**************************************************************************
    * Character.
    **************************************************************************/
    case 'A':
    case 'a':
      nFound = sscanf (&Fformat[1], "%d", &w);
      switch (nFound) {
        case 1:
	  sprintf (Cformat, "%%%d.%ds", w, w);
	  *CdataType = CDF_CHAR;
	  return TRUE;
      }
      return FALSE;
  }
  return FALSE;                         /* Unknown FORTRAN format character. */
}

/******************************************************************************
* GetCdataType.
*   Given a C format specification, returns the corresponding C data type.
******************************************************************************/

static long GetCdataType (Cformat, CdataType)
char *Cformat;          /* C format specification. */
long *CdataType;	/* Corresponding C data type. */
{
  char *ptrB;		/* Beginning of specification. */
  char *ptrE;		/* End of specification. */
  /****************************************************************************
  * Find beginning of specification.
  ****************************************************************************/
  ptrB = Cformat;
  while (*ptrB != NUL && IsSpaceChr(*ptrB)) ptrB++;
  if (*ptrB != '%') return FALSE;
  /****************************************************************************
  * Find end of specification.
  ****************************************************************************/
  ptrE = Cformat + strlen(Cformat) - 1;
  while (ptrE != ptrB && IsSpaceChr(*ptrE)) ptrE--;
  if (ptrE == ptrB) return FALSE;
  /****************************************************************************
  * Choose a data type based on the conversion character and possible modifier.
  ****************************************************************************/
  switch (*ptrE) {
    case 'd':
    case 'i':
      switch (*(ptrE-1)) {
	case 'l':
	  *CdataType = CDF_INT4;
	  break;
	case 'h':
	  *CdataType = CDF_INT2;
	  break;
	default:
#if defined(dos)
	  *CdataType = CDF_INT2;
#else
	  *CdataType = CDF_INT4;
#endif
	  break;
      }
      break;
    case 'x':
    case 'X':
    case 'o':
    case 'u':
      switch (*(ptrE-1)) {
	case 'l':
	  *CdataType = CDF_UINT4;
	  break;
	case 'h':
	  *CdataType = CDF_UINT2;
	  break;
	default:
#if defined(dos)
	  *CdataType = CDF_UINT2;
#else
	  *CdataType = CDF_UINT4;
#endif
	  break;
      }
      break;
    case 'c':
    case 's':
      *CdataType = CDF_CHAR;
      break;
    case 'f':
    case 'e':
    case 'E':
    case 'g':
    case 'G':
      *CdataType = CDF_DOUBLE;
      break;
    default:
      return FALSE;	/* Unknown C format description. */
  }
  return TRUE;
}

/******************************************************************************
* EncodeDimensionality.  Returns width of encoded dimensionality
******************************************************************************/

int EncodeDimensionality (string, numDims, dimSizes)
char *string;
long numDims;
long *dimSizes;
{
  int dimN;
  sprintf (string, "%ld:[", numDims);
  for (dimN = 0; dimN < numDims; dimN++)
     sprintf (&(string[strlen(string)]), "%s%ld", (dimN > 0 ? "," : ""),
	      dimSizes[dimN]);
  strcatX (string, "]", 0);
  return strlen(string);
}

/******************************************************************************
* EncodeVariances.  Returns width of encoded variances.
******************************************************************************/

int EncodeVariances (string, recVary, numDims, dimVarys)
char *string;
long recVary;
long numDims;
long *dimVarys;
{
  int dimN;
  strcpyX (string, TFvarianceToken(recVary), 0);
  strcatX (string, "/", 0);
  for (dimN = 0; dimN < numDims; dimN++)
     strcatX (string, TFvarianceToken(dimVarys[dimN]), 0);
  return strlen(string);
}

/******************************************************************************
* EncodeRecordIndices.
*    Uses `*' for record/dimension variances of NOVARY.  Returns width of
* record/indices.
******************************************************************************/

int EncodeRecordIndices (string, recNum, recVary, numDims, indices, dimVarys)
char *string;
long recNum;
long recVary;
long numDims;
long indices[];
long dimVarys[];
{
  int dimN;
  if (recVary)
    sprintf (string, "%ld", recNum + 1);
  else
    strcpyX (string, "*", 0);
  strcatX (string, ":[", 0);
  for (dimN = 0; dimN < numDims; dimN++) {
     if (dimN != 0) strcatX (string, ",", 0);
     if (dimVarys[dimN])
       sprintf (&string[strlen(string)], "%ld", indices[dimN] + 1);
     else
       strcatX (string, "*", 0);
  }
  strcatX (string, "]", 0);
  return strlen(string);
}

/******************************************************************************
* EncodeRecordIndicesVARY.  Force record/dimension variances to VARY.
******************************************************************************/

int EncodeRecordIndicesVARY (string, recNum, numDims, indices)
char *string;
long recNum;
long numDims;
long indices[];
{
  static long recVary = VARY;
  static long dimVarys[CDF_MAX_DIMS] = { VARY, VARY, VARY, VARY, VARY,
					 VARY, VARY, VARY, VARY, VARY };
  EncodeRecordIndices (string, recNum, recVary, numDims, indices, dimVarys);
  return strlen(string);
}

/******************************************************************************
* EncodingToken.  Return address of character string for encoding.
******************************************************************************/

char *EncodingToken(encoding)
long encoding;
{
switch (encoding) {
  case NETWORK_ENCODING: return "NETWORK";
  case VAX_ENCODING: return "VAX";
  case SUN_ENCODING: return "SUN";
  case DECSTATION_ENCODING: return "DECSTATION";
  case SGi_ENCODING: return "SGi";
  case IBMRS_ENCODING: return "IBMRS";
  case IBMPC_ENCODING: return "IBMPC";
  case HP_ENCODING: return "HP";
  case NeXT_ENCODING: return "NeXT";
  case ALPHAOSF1_ENCODING: return "ALPHAOSF1";
  case ALPHAVMSd_ENCODING: return "ALPHAVMSd";
  case ALPHAVMSg_ENCODING: return "ALPHAVMSg";
  case MAC_ENCODING: return "MAC";
}
return "?";
}

/******************************************************************************
* MajorityToken.  Return address of character string for majority.
******************************************************************************/

char *MajorityToken(majority)
long majority;
{
switch (majority) {
  case ROW_MAJOR: return "ROW";
  case COL_MAJOR: return "COLUMN";
}
return "?";
}

/******************************************************************************
* FormatToken.  Return address of character string for format.
******************************************************************************/

char *FormatToken(format)
long format;
{
switch (format) {
  case SINGLE_FILE: return "SINGLE";
  case MULTI_FILE: return "MULTI";
}
return "?";
}

/******************************************************************************
* ScopeToken.  Return address of character string for scope.
******************************************************************************/

char *ScopeToken(scope)
long scope;
{
switch (scope) {
  case GLOBAL_SCOPE: return "GLOBAL";
  case VARIABLE_SCOPE: return "VARIABLE";
}
return "?";
}

/******************************************************************************
* VarianceToken.  Return address of character string for variance.
******************************************************************************/

char *VarianceToken(variance)
long variance;
{
switch (variance) {
  case VARY: return "VARY";
  case NOVARY: return "NOVARY";
}
return "?";
}

/******************************************************************************
* TFvarianceToken.  Return address of character string for variance (T/F).
******************************************************************************/

char *TFvarianceToken(variance)
long variance;
{
switch (variance) {
  case VARY: return "T";
  case NOVARY: return "F";
}
return "?";
}

/******************************************************************************
* DataTypeToken.  Return address of character string for data type.
******************************************************************************/

char *DataTypeToken(dataType)
long dataType;
{
switch (dataType) {
  case CDF_BYTE: return "BYTE";
  case CDF_INT1: return "INT1";
  case CDF_INT2: return "INT2";
  case CDF_INT4: return "INT4";
  case CDF_UINT1: return "UINT1";
  case CDF_UINT2: return "UINT2";
  case CDF_UINT4: return "UINT4";
  case CDF_REAL4: return "REAL4";
  case CDF_REAL8: return "REAL8";
  case CDF_FLOAT: return "FLOAT";
  case CDF_DOUBLE: return "DOUBLE";
  case CDF_EPOCH: return "EPOCH";
  case CDF_CHAR: return "CHAR";
  case CDF_UCHAR: return "UCHAR";
}
return "?";
}

/*****************************************************************************
* OnlineHelpFP.
*
*   UNIX - The full pathname of the executable is not passed in via
*          argv[0].  The command which was entered on the command line
*          is passed in (aliases are, however, evaluated).
*
******************************************************************************/

FILE *OnlineHelpFP (filename, execpath)
char *filename;                         /* Name of help file. */
char *execpath;                         /* Pathname of executable (argv[0]). */
{
  FILE *fp;
  char pathname[DU_MAX_PATH_LEN+1];
#if !defined(mac)
  char execname[DU_MAX_NAME_LEN+1];
  char *ptr;
#endif

#if defined(mac)
  execpath;                     /* Quites the MPW C compiler. */
#endif

  /****************************************************************************
  * Try to use logical name (VMS) or environment variable (UNIX/POSIXshell and
  * DOS).
  ****************************************************************************/

#if defined(vms)
  strcpyX (pathname, "CDF$HELP:", DU_MAX_PATH_LEN);
  AppendToDir (pathname, filename);
  fp = fopen (pathname, "r");
  if (fp != NULL) return fp;
#endif

#if defined(unix) || defined(dos) || defined(posixSHELL)
  ptr = getenv ("CDF_HELP");
  if (ptr != NULL) {
    strcpyX (pathname, ptr, DU_MAX_PATH_LEN);
    AppendToDir (pathname, filename);
    fp = fopen (pathname, "r");
    if (fp != NULL) return fp;
  }
#endif

  /***************************************************************************
  * If not on a Macintosh...
  * Try relative paths from location of executable in the following order...
  *  1. From standard executable directory.
  *  2. From source directory.
  *  3. Same directory as that containing the executable.
  ***************************************************************************/

#if !defined(mac)
  if (execpath != NULL) {
    if (execpath[0] != NUL) {
      ParsePath (execpath, pathname, execname);
#if defined(vms)
      pathname[strlen(pathname)-1] = NUL;   /* Wipes out trailing `]'. */
      strcatX (pathname, "-.HELP]", DU_MAX_PATH_LEN);
#endif
#if defined(unix) || defined(posixSHELL)
      AppendToDir (pathname, "../help");
#endif
#if defined(dos)
      AppendToDir (pathname, "..\\help");
#endif
      AppendToDir (pathname, filename);
      fp = fopen (pathname, "r");
      if (fp != NULL) return fp;

      ParsePath (execpath, pathname, execname);
#if defined(vms)
      pathname[strlen(pathname)-1] = NUL;   /* Wipes out trailing `]'. */
      strcatX (pathname, "-.-.HELP]", DU_MAX_PATH_LEN);
#endif
#if defined(unix) || defined(posixSHELL)
      AppendToDir (pathname, "../../help");
#endif
#if defined(dos)
      AppendToDir (pathname, "..\\..\\help");
#endif
      AppendToDir (pathname, filename);
      fp = fopen (pathname, "r");  if (fp != NULL) return fp;

      ParsePath (execpath, pathname, execname);
      AppendToDir (pathname, filename);
      fp = fopen (pathname, "r");
      if (fp != NULL) return fp;
    }
  }
#endif

  /***************************************************************************
  * If on a Macintosh...try relative (partial) paths to the `help' file.
  ***************************************************************************/

#if defined(mac)
  strcpyX (pathname, "::help", DU_MAX_PATH_LEN);
  AppendToDir (pathname, filename);
  fp = fopen (pathname, "r");
  if (fp != NULL) return fp;
  
  strcpyX (pathname, ":::help", DU_MAX_PATH_LEN);
  AppendToDir (pathname, filename);
  fp = fopen (pathname, "r");
  if (fp != NULL) return fp;
#endif

  /***************************************************************************
  * Try current directory.
  ***************************************************************************/

  fp = fopen (filename, "r");
  if (fp != NULL) return fp;

  /***************************************************************************
  * Give up.
  ***************************************************************************/

  return NULL;
}

/*****************************************************************************
* PageOLH.
*****************************************************************************/

void PageOLH (olhFile, argv0)
char *olhFile;
char *argv0;
{
  FILE *fp;
  char line[MAX_SCREENLINE_LEN+1+1];
  int nestLevel = 0;                    /* Depth into `#ifos's. */
  int osMask = 0;                       /* When 0, display line.  Note that bit
					   0 is not used (eg. nesting depth of
					   1 uses bit 1, etc.). */
#if defined(vms)
  static char thisOS[] = "vms";
#endif
#if defined(unix) || defined(posixSHELL)
  static char thisOS[] = "unix";
#endif
#if defined(dos)
  static char thisOS[] = "dos";
#endif
#if defined(mac)
  static char thisOS[] = "mac";
#endif
  static char *readErrorMsg = {
    "Error reading online help file - contact CDFsupport.\n"
  };
  static char *syntaxErrorMsg = {
    "Syntax error in online help file - contact CDFsupport.\n"
  };

  /****************************************************************************
  * Open online help file.
  ****************************************************************************/

  fp = OnlineHelpFP (olhFile, argv0);
  if (fp == NULL) {
    WriteOut (stdout, readErrorMsg);
    return;
  }

  /****************************************************************************
  * Read online help file - passing appropriate lines through for display.
  ****************************************************************************/

  while (fgets(line,MAX_SCREENLINE_LEN+1+1,fp) != NULL) {
    /**************************************************************************
    * Check if an operating system directive.  If not, display the line if
    * all of the bits in the operating system mask are clear.
    **************************************************************************/
    if (line[0] == '#') {
      line[strlen(line)-1] = NUL;
      /************************************************************************
      * Check for a `#ifos' directive.  If this operating system is not
      * specified, then set the bit in the operating system mask for this
      * nesting level.
      ************************************************************************/
      if (!strncmp(line,"#ifos",5)) {
	nestLevel++;
	if (strstr(line,thisOS) == NULL) SETBIT (osMask, nestLevel);
	continue;
      }
      /************************************************************************
      * Check for a `#else' directive.  Simply flip the bit in the operating
      * system mask for this nesting level.
      ************************************************************************/
      if (!strcmp(line,"#else")) {
	if (nestLevel < 1) {
	  WriteOut (stdout, syntaxErrorMsg);
	  break;
	}
	FLPBIT (osMask, nestLevel);
	continue;
      }
      /************************************************************************
      * Check for a `#endos' directive.  Clear the bit in the operating system
      * mask for this nesting level and decrement the nesting level.
      ************************************************************************/
      if (!strcmp(line,"#endos")) {
	if (nestLevel < 1) {
	  WriteOut (stdout, syntaxErrorMsg);
	  break;
	}
	CLRBIT (osMask, nestLevel);
	nestLevel--;
	continue;
      }
      /************************************************************************
      * An unknown directive has been encountered.
      ************************************************************************/
      WriteOut (stdout, syntaxErrorMsg);
      break;
    }
    else {
      if (osMask == 0) WriteOut (stdout, line);
    }
  }
  if (nestLevel != 0) WriteOut (stdout, syntaxErrorMsg);

  fclose (fp);
  return;
}

/******************************************************************************
* OutputWithMargin.
*   Outputs a long text string to multiple lines of a file (or stdout)
* breaking the lines at blanks if possible.
******************************************************************************/

void OutputWithMargin (fp, text, maxLen, marginLen)
FILE *fp;
char *text;
int maxLen;     /* Maximum length of line (including margin). */
int marginLen;  /* Length of margin preceeding text on each line. */
{
  char *ptr = text, *ptrT;
  int maxLenT = maxLen - marginLen;     /* Maximum number of characters per
					   line (excluding the margin). */
  for (;;) {
     /*************************************************************************
     * If the remaining characters will fit on a line, print them and return.
     * This also covers the case where the entire text will fit on one line.
     *************************************************************************/
     if (strlen(ptr) <= (size_t) maxLenT) {
       if (marginLen > 0) Ncharacters (fp, marginLen, (int) ' ');
       WriteOut (fp, ptr);
       WriteOut (fp, "\n");
       return;
     }
     /*************************************************************************
     * Otherwise, find a blank character at which to break the characters.  The
     * blank is not printed on either line.
     *************************************************************************/
     for (ptrT = ptr + maxLenT; ptrT != ptr; ptrT--) {
	if (*ptrT == ' ') {
	  char *string = NULedString (ptr, (int) (ptrT - ptr));
	  if (marginLen > 0) Ncharacters (fp, marginLen, (int) ' ');
	  WriteOut (fp, string);
	  WriteOut (fp, "\n");
	  ptr = ptrT + 1;
	  FreeMemory (string, FatalError);
	  break;
	}
     }
     /*************************************************************************
     * If a blank could not be found, break the characters at the end of the
     * maximum number per line.
     *************************************************************************/
     if (ptrT == ptr) {
       char *string = NULedString (ptr, maxLenT);
       if (marginLen > 0) Ncharacters (fp, marginLen, (int) ' ');
       WriteOut (fp, string);
       WriteOut (fp, "\n");
       ptr += maxLenT;
       FreeMemory (string, FatalError);
     }
  }
}

/******************************************************************************
* InitPctLog.
******************************************************************************/

void InitPctLog (lastPct, pctOn)
int *lastPct;
Logical *pctOn;
{
  WriteOut (stdout, "..0%");
  *lastPct = 0;
  *pctOn = TRUE;
  return;
}

/******************************************************************************
* UpdatePctLog.
******************************************************************************/

void UpdatePctLog (pct, lastPct)
int pct;
int *lastPct;
{
  if (pct != *lastPct) {
    char string[10+1];
    sprintf (string, "\b\b\b\b%s%d%%",
	     (pct < 10 ? ".." : (pct < 100 ? "." : "")), pct);
    WriteOut (stdout, string);
    *lastPct = pct;
  }
  return;
}

/******************************************************************************
* RefreshPctLog.
******************************************************************************/

void RefreshPctLog (lastPct)
int lastPct;
{
  char string[6+1];
  sprintf (string, "%s%d%%",
	   (lastPct < 10 ? ".." : (lastPct < 100 ? "." : "")), lastPct);
  WriteOut (stdout, string);
  return;
}

/******************************************************************************
* CleanupPctLog.
*   100% is output in case there were no values (in which case 0% would be
* currently displayed).
******************************************************************************/

void CleanupPctLog (pctOn)
Logical *pctOn;
{
  WriteOut (stdout, "\b\b\b\b100%\n");
  *pctOn = FALSE;
  return;
}

/******************************************************************************
* Ncharacters.
*   Outputs a specified number of a selected character to a file pointer.
******************************************************************************/

void Ncharacters (fp, nChars, chr)
FILE *fp;
int nChars;
int chr;
{
  int i;
  for (i = 0; i < nChars; i++) WriteOut (fp, TempCharStr(chr));
  return;
}

/******************************************************************************
* CatNcharacters.
*   Concatenates some number of a specified character to a string.
******************************************************************************/

void CatNcharacters (string, nChars, chr)
char *string;
int nChars;
int chr;
{
  int i;
  for (i = 0; i < nChars; i++) catchrX (string, chr, 0);
  return;
}

/******************************************************************************
* CpyNcharacters.
*   Copies some number of a specified character to a string.
******************************************************************************/

void CpyNcharacters (string, nChars, chr)
char *string;
int nChars;
int chr;
{
  int i;
  for (i = 0; i < nChars; i++) string[i] = (char) chr;
  string[nChars] = NUL;
  return;
}

/******************************************************************************
* TFqualifier.
******************************************************************************/

Logical TFqualifier (qop, variable, Tx, Fx, defaultTF, conflictText)
QOP *qop;
Logical *variable;
int Tx;
int Fx;
Logical defaultTF;
char *conflictText;
{
  int count = 0;
  if (qop->qualEntered[Tx]) count++;
  if (qop->qualEntered[Fx]) count++;
  switch (count) {
    case 0:
      *variable = defaultTF;
      return TRUE;
    case 1:
      *variable = (qop->qualEntered[Tx] ? TRUE : FALSE);
      return TRUE;
    default: {
      char tempS[MAX_MESSAGE_TEXT_LEN+1];
      sprintf (tempS, "Conflicting qualifiers (%s & no%s).",
	       conflictText, conflictText);
      DisplayError (tempS);
      return FALSE;
    }
  }
}

/******************************************************************************
* S2qualifierLong.
******************************************************************************/

Logical S2qualifierLong (qop, variable, S1x, S1, S2x, S2, defaultS,
			 conflictText)
QOP *qop;
long *variable;
int S1x;
long S1;
int S2x;
long S2;
long defaultS;
char *conflictText;
{
  int count = 0;
  if (qop->qualEntered[S1x]) count++;
  if (qop->qualEntered[S2x]) count++;
  switch (count) {
    case 0:
      *variable = defaultS;
      return TRUE;
    case 1:
      *variable = (qop->qualEntered[S1x] ? S1 : S2);
      return TRUE;
    default: {
      char tempS[MAX_MESSAGE_TEXT_LEN+1];
      sprintf (tempS, "Conflicting qualifiers (%s).", conflictText);
      return FALSE;
    }
  }
}

/******************************************************************************
* BlankPadRight.
*   Concatenate blanks to the right end of a string.
******************************************************************************/

void BlankPadRight (string, count)
char *string;
int count;
{
  int i;
  for (i = 0; i < count; i++) catchrX (string, (int) ' ', 0);
  return;
}

/******************************************************************************
* GetFormatEntry.
*    Returns the corresponding entry of the FORMAT attribute for a specified
* variable.  A character string is allocated (and must be freed by the caller).
* It is assumed that the current CDF has been selected.
******************************************************************************/

char *GetFormatEntry (Z, varNum)
Logical Z;
long varNum;
{
  CDFstatus status;
  long dataType, numElems;
  char *format;
  status = CDFlib (SELECT_, ATTR_NAME_, "FORMAT",
			    Zq(Z,zENTRY_,rENTRY_), varNum,
		   GET_, Zq(Z,zENTRY_DATATYPE_,rENTRY_DATATYPE_), &dataType,
			 Zq(Z,zENTRY_NUMELEMS_,rENTRY_NUMELEMS_), &numElems,
		   NULL_);
  if (!StatusOK(status)) return NULL;
  if (!STRINGdataType(dataType)) return NULL;
  format = (char *) AllocateMemory ((size_t) (numElems + 1), FatalError);
  status = CDFlib (GET_, Zq(Z,zENTRY_DATA_,rENTRY_DATA_), format,
		   NULL_);
  if (StatusBAD(status)) {
    FreeMemory (format, FatalError);
    return NULL;
  }
  format[(int)numElems] = NUL;
  return format;
}

/******************************************************************************
* ParseOptionList.
******************************************************************************/

Logical ParseOptionList (nTokens, tokens, list, present)
int nTokens;
char *tokens[];
char *list;
Logical present[];
{
  int tokenN, matchN;
  char token[MAX_TOKEN_LEN+1], *ptr1, *ptr2, terminator;
  /****************************************************************************
  * Assume initially that none of the tokens were found.
  ****************************************************************************/
  for (tokenN = 0; tokenN < nTokens; tokenN++) present[tokenN] = FALSE;
  /****************************************************************************
  * Check that the entered list of tokens is not a NUL string, that there are
  * some possible tokens to match, etc.
  ****************************************************************************/
  if (NULstring(list)) return TRUE;     /* Obviously, no tokens were found. */
  if (nTokens < 1) return FALSE;        /* Probably a logic error. */
  /****************************************************************************
  * Scan the entered list of tokens searching for matches with the list of
  * possible tokens.  First determine the starting character position and the
  * ending delimiter.
  ****************************************************************************/
  switch (list[0]) {
    case '(':           /* VMS-style. */
      terminator = ')';
      ptr1 = list + 1;
      break;
    case '"':		/* UNIX-style on a Mac (double quotes not stripped). */
      terminator = '"';
      ptr1 = list + 1;
      break;
    default:            /* UNIX-style on a UNIX machine or IBM PC. */
      terminator = NUL;
      ptr1 = list;
      break;
  }
  for (;;) {
     /*************************************************************************
     * Find beginning of the next token in list (skipping past any leading
     * blanks).  If the end of the list is reached instead, then there are
     * no more tokens.
     *************************************************************************/
     while (*ptr1 == ' ') ptr1++;
     if (*ptr1 == terminator) return TRUE;
     /*************************************************************************
     * Find the end of the token and copy.  The token is ended by either a
     * blank, comma, or the terminator.
     *************************************************************************/
     ptr2 = ptr1 + 1;
     while (*ptr2 != ' ' && *ptr2 != ',' && *ptr2 != terminator) ptr2++;
     strcpyX (token, ptr1, MINIMUM((int)(ptr2-ptr1),MAX_TOKEN_LEN));
     /*************************************************************************
     * Search possible tokens for a match.  If more than one match is found,
     * return an error.
     *************************************************************************/
     for (matchN = -1, tokenN = 0; tokenN < nTokens; tokenN++)
	if (strncmpIgCase(token,tokens[tokenN],strlen(token)) == 0)
	  if (matchN == -1)
	    matchN = tokenN;
	  else
	    return FALSE;
     if (matchN == -1)
       return FALSE;
     else
       present[matchN] = TRUE;
     /*************************************************************************
     * Setup to search for next token in list.
     *************************************************************************/
     switch (*ptr2) {
       case ' ':
	 /*********************************************************************
	 * The last token was ended by a blank.  Check to see if a comma is
	 * the next non-blank character.  If so, the next token begins one
	 * character beyond the comma (leading blanks will be skipped when
	 * the beginning of that token is located).  Otherwise, the next token
	 * begins at the non-blank character.
	 *********************************************************************/
	 ptr1 = ptr2 + 1;
	 while (*ptr1 == ' ') ptr1++;
	 if (*ptr1 == ',') ptr1++;
	 break;
       case ',':
	 /*********************************************************************
	 * The last token was ended by a comma.  The next token begins one
	 * character beyond the comma.
	 *********************************************************************/
	 ptr1 = ptr2 + 1;
	 break;
       default:
	 /*********************************************************************
	 * The terminator must have ended the last token.
	 *********************************************************************/
	 return TRUE;
     }
  }
}

/******************************************************************************
* NULedString.
*    Returns a pointer to a NUL-terminated string that was allocated here but
* not freed.  The caller must free the string when no longer needed.  Also,
* `len'+1 characters will be allocated for the new string (and copied to from
* the existing string) regardless of whether or not a NUL appears in the first
* `len' characters.
******************************************************************************/

char *NULedString (ptr, len)
char *ptr;                      /* Pointer to the string from which to extract
				   the first `len' characters. */
size_t len;                     /* Number of characters to extract.  This does
				   not include the terminating NUL that will
				   be appended to the string being returned. */
{
  char *string;
  string = (char *) AllocateMemory (len + 1, FatalError);
  memmove (string, ptr, len);
  string[len] = NUL;
  return string;
}

/******************************************************************************
* WriteOutSO.
******************************************************************************/

#if defined(STDARG)
int WriteOutSO (char *format, ...)
#else
int WriteOutSO (va_alist)
va_dcl
#endif
{
#if !defined(STDARG)
  char *format;
#endif
  int n;
  char text[MAX_oTEXT_LEN];
  va_list ap;
#if defined(STDARG)
  va_start (ap, format);
#else
  VA_START (ap);
  format = va_arg (ap, char *);
#endif
  vsprintf (text, format, ap);
  n = WriteOut (stdout, text);
  va_end (ap);
  return n;
}

/******************************************************************************
* WriteOutFP.
******************************************************************************/

#if defined(STDARG)
int WriteOutFP (FILE *fp, char *format, ...)
#else
int WriteOutFP (va_alist)
va_dcl
#endif
{
#if !defined(STDARG)
  FILE *fp;
  char *format;
#endif
  int n;
  char text[MAX_oTEXT_LEN];
  va_list ap;
#if defined(STDARG)
  va_start (ap, format);
#else
  VA_START (ap);
  fp = va_arg (ap, FILE *);
  format = va_arg (ap, char *);
#endif
  vsprintf (text, format, ap);
  n = WriteOut (fp, text);
  va_end (ap);
  return n;
}

/******************************************************************************
* WriteOut.
******************************************************************************/

int WriteOut (fp, text)
FILE *fp;
char *text;
{
  /****************************************************************************
  * If the output is not going to `stdout', then assume it is going to a file
  * (and ignore paging if requested).
  ****************************************************************************/
  if (fp != stdout)
    fprintf (fp, "%s", text);
  else {
    /**************************************************************************
    * The output is going to `stdout'.  Assume that `stdout' has not been
    * redirected away from the user's terminal (to a file).  If `stdout' has
    * been redirected to a file, then hopefully the user had enough sense not
    * to request paging.
    **************************************************************************/
    char *ptr, *ptrNL;
    size_t len;
    /**************************************************************************
    * If paging is on, first prompt for `more...' if the end of the standard
    * output screen has been reached.
    **************************************************************************/
    for (ptr = text;;) {
       /***********************************************************************
       * If paging on, check if the maximum number of lines have been written.
       ***********************************************************************/
       if (pagingOn) {
	 if (pagingCount == MAX_LINES_WHEN_PAGING) {
	   char key = NUL;
#if defined(mac)
	   DrawStatusLine ("Enter RETURN for more...");
	   MacReadKeySO (&key);
	   if (key != '\r' && key != '\n') pagingOn = FALSE;
	   ResetStdOut (FALSE);
	   DrawStatusLine ("Executing...");
#else
	   static char prompt[] = "\nEnter RETURN for more...";
	   WriteStringStdOut (prompt, strlen(prompt));
	   ReadCharStdIn (&key);
	   if (key != '\n') pagingOn = FALSE;
	   WriteStringStdOut ("\n\n", 2);
#endif
	   pagingCount = 0;
	 }
       }
       /***********************************************************************
       * Write the string up to the next newline character.  If there aren't
       * any(more) newline characters, write the rest of the string.
       ***********************************************************************/
       ptrNL = strchr (ptr, Nl);
       if (ptrNL == NULL) {
	 len = strlen (ptr);
#if defined(mac)
	 if (len + (*soTextH)->teLength > MAX_TE_TEXT_LEN) MacSOoverFlow ();
#endif
	 WriteStringStdOut (ptr, len);
	 break;
       }
       else {
	 len = (int) (ptrNL - ptr + 1);
#if defined(mac)
	 if (len + (*soTextH)->teLength > MAX_TE_TEXT_LEN) MacSOoverFlow ();
#endif
	 WriteStringStdOut (ptr, len);
#if defined(mac)
	 if ((*soTextH)->teLength > MAX_TE_TEXT_LEN_wNL)
	   MacSOoverFlow ();
	 else {
	   if (pagingOn) pagingCount++;
	 }
#else
	 if (pagingOn) pagingCount++;
#endif
	 ptr = ptrNL + 1;
	 if (*ptr == NUL) break;
       }
    }
#if !defined(mac)
    fflush (stdout);
#endif
  }
  return strlen(text);           /* Return number of characters output. */
}

/******************************************************************************
* DirList.
*   If an error occurs, zero (0) is returned.
******************************************************************************/

int DirList (dir, patternC, patternS, dirS, nameS)
char *dir;              /* In: Directory to search. */
int patternC;           /* In: Pattern count. */
char **patternS;        /* In: Patterns to search for. */
char ***dirS;           /* Out: Directories. */
char ***nameS;          /* Out: File names. */
{
int i;
int nFiles = 0;
int tNameChars = 0;
struct FILEstruct *FILEhead = NULL;
struct FILEstruct *FILEptr;
struct FILEstruct *FILEptrT;
char *dirP, *nameP;
size_t nNameChars;

#if defined(BORLANDC)
char pathX[DU_MAX_PATH_LEN + 1];
struct ffblk ffblk;
int done;
#endif

#if defined(MICROSOFTC)
char pathX[DU_MAX_PATH_LEN + 1];
struct find_t fileinfo;
int done;
#endif

#if defined(unix) || defined(posixSHELL)
DIR *dirp;
char dirX[DU_MAX_PATH_LEN + 1];
char pattern[DU_MAX_NAME_LEN+1];
#if defined(Mach)
struct direct *dp;
#else
struct dirent *dp;
#endif
#endif

#if defined(vms)
char pathX[DU_MAX_PATH_LEN + 1];
char pathFound[DU_MAX_PATH_LEN + 1];
struct dsc$descriptor_s pathDESCR;
struct dsc$descriptor_s pathFoundDESCR;
unsigned long context = 0;
char name[DU_MAX_NAME_LEN + 1];
unsigned long status;
#endif

#if defined(mac)
short vRefNum;
long dirID;
OSErr rCode;
CInfoPBRec cParms;
Str255 tempS;
char *name;
char pattern[DU_MAX_NAME_LEN+1];
#endif

#if defined(dos)
  /****************************************************************************
  * Find files on MS-DOS machine.  Note that only one directory is possible
  * per path on MS-DOS systems.
  ****************************************************************************/
  for (i = 0; i < patternC; i++) {
     ExpandPath (dir, pathX);
     AppendToDir (pathX, patternS[i]);
#if defined(BORLANDC)
     done = findfirst (pathX, &ffblk, 0);
#endif
#if defined(MICROSOFTC)
     done = _dos_findfirst (pathX, 0, &fileinfo);
#endif
     while (!done) {
       /***********************************************************************
       * Allocate/setup file structure.
       ***********************************************************************/
       FILEptr = (struct FILEstruct *)
		 AllocateMemory (sizeof(struct FILEstruct), FatalError);
#if defined(BORLANDC)
       nNameChars = strlen (ffblk.ff_name);
       FILEptr->name = (char *) AllocateMemory (nNameChars + 1, FatalError);
       strcpyX (FILEptr->name, ffblk.ff_name, nNameChars);
#endif
#if defined(MICROSOFTC)
       nNameChars = strlen (fileinfo.name);
       FILEptr->name = (char *) AllocateMemory (nNameChars + 1, FatalError);
       strcpyX (FILEptr->name, fileinfo.name, nNameChars);
#endif
       tNameChars += nNameChars + 1;
       nFiles++;
       FILEptr->next = NULL;
       AddToList (&FILEhead, FILEptr);
       /***********************************************************************
       * Get next matching file.
       ***********************************************************************/
#if defined(BORLANDC)
       done = findnext (&ffblk);
#endif
#if defined(MICROSOFTC)
       done = _dos_findnext (&fileinfo);
#endif
     }
   }
#endif

#if defined(unix) || defined(posixSHELL)
  /****************************************************************************
  * Find files on UNIX machine.  Note that only one directory is possible per
  * path on UNIX systems.
  ****************************************************************************/
  ExpandPath (dir, dirX);
  if (NULstring(dirX)) strcpyX (dirX, ".", DU_MAX_PATH_LEN);
						  /* Default to current dir. */
  dirp = opendir (dirX);
  if (dirp == NULL) return 0;
  dp = readdir (dirp);
  while (dp != NULL) {
    char *name = dp->d_name;
#if SOLARISbsdDIRUTILSbug
    name -= 2;
#endif
    for (i = 0; i < patternC; i++) {
       strcpyX (pattern, patternS[i], DU_MAX_NAME_LEN);
       if (NULstring(pattern)) strcpyX (pattern, "*", DU_MAX_NAME_LEN);
						  /* Default to all files. */
       if (PatternMatch (pattern, name)) {
	 /*********************************************************************
	 * Allocate/setup file structure.
	 *********************************************************************/
	 FILEptr = (struct FILEstruct *)
		   AllocateMemory (sizeof(struct FILEstruct), FatalError);
	 nNameChars = strlen (name);
	 FILEptr->name = (char *) AllocateMemory (nNameChars + 1, FatalError);
	 strcpyX (FILEptr->name, name, nNameChars);
	 tNameChars += nNameChars + 1;
	 nFiles++;
	 FILEptr->next = NULL;
	 AddToList (&FILEhead, FILEptr);
	 break;
       }
    }
    /**************************************************************************
    * Get next file.
    **************************************************************************/
    dp = readdir (dirp);
  }
  closedir (dirp);
#endif

#if defined(vms)
  /****************************************************************************
  * Find files on VMS machine.  Note that a logical name defined to be a set of
  * two or more directories may result in files from more than one directory
  * being found.  In that case, the logical name would be returned as the name
  * of the directory (the VMS file system handles this when opening a file).
  * The actual directories are ignored.
  ****************************************************************************/
  pathFoundDESCR.dsc$w_length = sizeof(pathFound) - 1; /* Pass full string. */
  pathFoundDESCR.dsc$b_dtype = DSC$K_DTYPE_T;
  pathFoundDESCR.dsc$b_class = DSC$K_CLASS_S;
  pathFoundDESCR.dsc$a_pointer = pathFound;
  for (i = 0; i < patternC; i++) {
     ExpandPath (dir, pathX);
     AppendToDir (pathX, patternS[i]);
     pathDESCR.dsc$w_length = strlen(pathX);            /* Pass part used. */
     pathDESCR.dsc$b_dtype = DSC$K_DTYPE_T;
     pathDESCR.dsc$b_class = DSC$K_CLASS_S;
     pathDESCR.dsc$a_pointer = pathX;
     while ((status = lib$find_file (&pathDESCR, &pathFoundDESCR, &context,
				     NULL, NULL, NULL, NULL)) == RMS$_NORMAL) {
       /***********************************************************************
       * A file was found, NUL terminate and remove trailing blanks (version
       * number is left on).
       ***********************************************************************/
       pathFound[pathFoundDESCR.dsc$w_length] = NUL;    /* NUL-terminate. */
       for (i = strlen(pathFound) - 1; i >= 0 && pathFound[i] == ' '; i--) {
	  pathFound[i] = NUL;
       }
       ParsePath (pathFound, NULL, name);
       /***********************************************************************
       * Allocate/setup file structure.
       ***********************************************************************/
       FILEptr = (struct FILEstruct *)
		 AllocateMemory (sizeof(struct FILEstruct), FatalError);
       nNameChars = strlen (name);
       FILEptr->name = (char *) AllocateMemory (nNameChars + 1, FatalError);
       strcpyX (FILEptr->name, name, nNameChars);
       tNameChars += nNameChars + 1;
       nFiles++;
       FILEptr->next = NULL;
       AddToList (&FILEhead, FILEptr);
     }
     status = lib$find_file_end (&context);
  }
#endif

#if defined(mac)
  /****************************************************************************
  * Find files on a Macintosh.  Note that only one directory is possible per
  * path on Macintosh systems.
  ****************************************************************************/
  if (!MacDirSpecified(dir,&vRefNum,&dirID)) {
    *nameS = NULL;
    *dirS = NULL;
    return 0;
  }
  cParms.hFileInfo.ioNamePtr = tempS;
  cParms.hFileInfo.ioVRefNum = vRefNum;
  cParms.hFileInfo.ioFDirIndex = 1;
  cParms.hFileInfo.ioDirID = dirID;
  rCode = PBGetCatInfo (&cParms, FALSE);
  while (rCode == noErr) {
    /**************************************************************************
    * Check if a file (rather than a directory).
    **************************************************************************/
    if (BITCLR(cParms.hFileInfo.ioFlAttrib,4)) {
      name = PtoCstr (cParms.hFileInfo.ioNamePtr);
      for (i = 0; i < patternC; i++) {
	 strcpyX (pattern, patternS[i], DU_MAX_NAME_LEN);
	 if (NULstring(pattern)) strcpyX (pattern, "*", DU_MAX_NAME_LEN);
	 if (PatternMatch(pattern,name)) {
	   /*******************************************************************
	   * Allocate/setup file structure.
	   *******************************************************************/
	   FILEptr = (struct FILEstruct *)
		     AllocateMemory (sizeof(struct FILEstruct), FatalError);
	   nNameChars = strlen (name);
	   FILEptr->name = (char *) AllocateMemory (nNameChars+1, FatalError);
	   strcpyX (FILEptr->name, name, nNameChars);
	   tNameChars += nNameChars + 1;
	   nFiles++;
	   FILEptr->next = NULL;
	   AddToList (&FILEhead, FILEptr);
	   break;
	 }
      }
    }
    /**************************************************************************
    * Get next file.
    **************************************************************************/
    cParms.hFileInfo.ioDirID = dirID;    /* This must be reset each time. */
    cParms.hFileInfo.ioFDirIndex++;         /* Increment to next file. */
    rCode = PBGetCatInfo (&cParms, FALSE);
  }
#endif

  /****************************************************************************
  * Build directory/file arrays.
  ****************************************************************************/
  if (nFiles > 0) {
    /**************************************************************************
    * Setup to traverse linked list.
    **************************************************************************/
    *dirS = (char **) AllocateMemory ((nFiles*sizeof(char *))+strlen(dir)+1,
				      FatalError);
    *nameS = (char **) AllocateMemory ((nFiles*sizeof(char *)) + tNameChars,
				       FatalError);
    dirP = (char *) (*dirS) + nFiles*sizeof(char *);
    nameP = (char *) (*nameS) + nFiles*sizeof(char *);
    /**************************************************************************
    * Traverse linked list of file structures.  Note that the second through
    * last directory strings actually point to the first directory string.
    **************************************************************************/
    for (FILEptr=FILEhead, i=0; FILEptr != NULL; FILEptr=FILEptr->next, i++) {
       if (i == 0) {
	 strcpyX (dirP, dir, 0);
	 (*dirS)[i] = dirP;
       }
       else
	 (*dirS)[i] = (*dirS)[i-1];
       strcpyX (nameP, FILEptr->name, 0);
       (*nameS)[i] = nameP;
       nameP += strlen(FILEptr->name) + 1;
    }
    /**************************************************************************
    * Free linked list of file structures.
    **************************************************************************/
    FILEptr = FILEhead;
    while (FILEptr != NULL) {
      FILEptrT = FILEptr;
      FILEptr = FILEptr->next;
      FreeMemory (FILEptrT->name, FatalError);
      FreeMemory (FILEptrT, FatalError);
    }
  }
  else {
    /**************************************************************************
    * No files found.
    **************************************************************************/
    *nameS = NULL;
    *dirS = NULL;
  }
  return nFiles;
}

/******************************************************************************
* AddToList.
******************************************************************************/

static void AddToList (FILEhead, FILEptr)
struct FILEstruct **FILEhead;
struct FILEstruct *FILEptr;
{
  if (*FILEhead == NULL)
    *FILEhead = FILEptr;
  else
    if (strcmp(FILEptr->name,(*FILEhead)->name) < 0) {
      FILEptr->next = *FILEhead;
      *FILEhead = FILEptr;
    }
    else {
      struct FILEstruct *FILEptrT = *FILEhead;
      for (;;) {
	 if (FILEptrT->next == NULL) {
	   FILEptrT->next = FILEptr;
	   break;
	 }
	 else
	   if (strcmp(FILEptr->name,FILEptrT->next->name) < 0) {
	     FILEptr->next = FILEptrT->next;
	     FILEptrT->next = FILEptr;
	     break;
	   }
	 FILEptrT = FILEptrT->next;
      }
    }
  return;
}

/******************************************************************************
* PatternMatch. (Only used on UNIX/POSIXshell and Macintosh systems).
******************************************************************************/

#if defined(unix) || defined(mac) || defined(posixSHELL)
static int PatternMatch (pattern, name)
char *pattern;
char *name;
{
enum typeENUM types[DU_MAX_PATTERNS];
char *strings[DU_MAX_PATTERNS];
int nTypes = 0;
int len, i, result;

for (i = 0; i < DU_MAX_PATTERNS; i++) types[i] = TBD;

for (i = 0; pattern[i] != NUL; i++)
   switch (pattern[i]) {
     case '?':
       types[nTypes++] = WILD1;
       break;
     case '*':
       if (nTypes == 0 || types[nTypes] != WILDn) types[nTypes++] = WILDn;
       break;
     default:
       if (types[nTypes] == TBD) {
	 types[nTypes] = STR;
	 strings[nTypes] = (char *) AllocateMemory (DU_MAX_NAME_LEN + 1,
						    FatalError);
	 strings[nTypes][0] = pattern[i];
	 strings[nTypes][1] = NUL;
	 nTypes++;
       }
       else {
	 len = strlen (strings[nTypes-1]);
	 strings[nTypes-1][len] = pattern[i];
	 strings[nTypes-1][len+1] = NUL;
       }
   }

result = CheckPattern (nTypes, types, strings, name);

for (i = 0; i < DU_MAX_PATTERNS; i++) {
   if (types[i] == STR) FreeMemory (strings[i], FatalError);
}

return result;
}
#endif

/******************************************************************************
* CheckPattern. (Only used on UNIX/POSIXshell and Macintosh systems).
******************************************************************************/

#if defined(unix) || defined(mac) || defined(posixSHELL)
static int CheckPattern (nTypes, types, strings, name)
int nTypes;
enum typeENUM types[];
char *strings[];
char *name;
{
int sp = 0;
int i, j;

for (i = 0; i < nTypes; i++)
   switch (types[i]) {
     case TBD:
       /* Do nothing? */
       break;
     case STR:
       if (strncmp(&name[sp],strings[i],strlen(strings[i])) != 0)
	 return FALSE;
       else
	 sp += strlen(strings[i]);
       break;
     case WILD1:
       if (name[sp] == NUL)
	 return FALSE;
       else
	 sp++;
       break;
     case WILDn:
       if (i == nTypes-1)       /* At last type to check? */
	 return TRUE;
       else {
	 for (j = i; name[j] != NUL; j++)
	    if (CheckPattern (nTypes-i-1, &types[i+1],
			      &strings[i+1], &name[j])) return TRUE;
	 return FALSE;
       }
   }

if (name[sp] == NUL)
  return TRUE;
else
  return FALSE;
}
#endif

/******************************************************************************
* CDFdirList.
*    Does a `DirList' using the possible CDF extensions (`.cdf' on all
* platforms and also `.CDF', `.cdf;1', and `.CDF;1' on UNIX/POSIXshell and
* Macintosh platforms because of CD-ROM inconsistencies
******************************************************************************/

int CDFdirList (path, dirS, nameS)
char *path;
char ***dirS;
char ***nameS;
{
#if defined(unix) || defined(mac) || defined(posixSHELL)
  /****************************************************************************
  * UNIX/POSIXshell and Macintosh.
  ****************************************************************************/
  char dir[DU_MAX_DIR_LEN+1];
  char pattern[DU_MAX_NAME_LEN+1];
  static char pattern1[DU_MAX_NAME_LEN+1];
  static char pattern2[DU_MAX_NAME_LEN+1];
  static char pattern3[DU_MAX_NAME_LEN+1];
  static char pattern4[DU_MAX_NAME_LEN+1];
  static char *patternS[4] = { pattern1, pattern2, pattern3, pattern4 };
  int nCDFs;

  ParsePath (path, dir, pattern);

  strcpyX (pattern1, pattern, DU_MAX_NAME_LEN);
  if (NULstring(pattern1)) strcatX (pattern1, "*", DU_MAX_NAME_LEN);
  strcatX (pattern1, ".cdf", DU_MAX_NAME_LEN);

  strcpyX (pattern2, pattern, DU_MAX_NAME_LEN);
  if (NULstring(pattern2)) strcatX (pattern2, "*", DU_MAX_NAME_LEN);
  strcatX (pattern2, ".CDF", DU_MAX_NAME_LEN);

  strcpyX (pattern3, pattern, DU_MAX_NAME_LEN);
  if (NULstring(pattern3)) strcatX (pattern3, "*", DU_MAX_NAME_LEN);
  strcatX (pattern3, ".cdf;1", DU_MAX_NAME_LEN);

  strcpyX (pattern4, pattern, DU_MAX_NAME_LEN);
  if (NULstring(pattern4)) strcatX (pattern4, "*", DU_MAX_NAME_LEN);
  strcatX (pattern4, ".CDF;1", DU_MAX_NAME_LEN);

  nCDFs = DirList (dir, 4, patternS, dirS, nameS);
  RemoveExtensions (nCDFs, *nameS);

  return nCDFs;
#endif
#if defined(vms) || defined(dos)
  /****************************************************************************
  * VMS and MS-DOS.
  ****************************************************************************/
  char dir[DU_MAX_DIR_LEN+1];
  static char pattern[DU_MAX_NAME_LEN+1];
  static char *patternS[1] = { pattern };
  int nCDFs;

  ParsePath (path, dir, pattern);

  if (NULstring(pattern)) strcatX (pattern, "*", DU_MAX_NAME_LEN);
  strcatX (pattern, ".cdf", DU_MAX_NAME_LEN);

  nCDFs = DirList (dir, 1, patternS, dirS, nameS);
  RemoveExtensions (nCDFs, *nameS);

  return nCDFs;
#endif
}

/******************************************************************************
* AppendToDir.
*     If necessary, appends a '/' (UNIX), a '\' (MS-DOS), or a `:' (Mac) to
* make a directory specification to which the file name can then be appended.
******************************************************************************/

void AppendToDir (dir, name)
char *dir;
char *name;
{
#if defined(vms)
  strcatX (dir, name, 0);
#endif
#if defined(unix) || defined(posixSHELL)
  if (!NULstring(dir)) {
    int lastChar = strlen(dir) - 1;
    if (dir[lastChar] != '/') strcatX (dir, "/", 0);
  }
  strcatX (dir, name, 0);
#endif
#if defined(dos)
  if (!NULstring(dir)) {
    int lastChar = strlen(dir) - 1;
    if (dir[lastChar] != '\\' && dir[lastChar] != ':') strcatX (dir, "\\", 0);
  }
  strcatX (dir, name, 0);
#endif
#if defined(mac)
  if (!NULstring(dir)) {
    int lastChar = strlen(dir) - 1;
    if (dir[lastChar] != ':') strcatX (dir, ":", 0);
  }
  strcatX (dir, name, 0);
#endif
  return;
}

/******************************************************************************
* RemoveExtensions.  Locates the last '.' in a file name and replaces it with
*                    a NUL.  This effectively removes the file extension.  If
*                    the file name does not have an extension and has a '.' in
*                    its name, this could have undesired effects.
******************************************************************************/

void RemoveExtensions (nFiles, files)
int nFiles;
char *files[];
{
  int i;
  char *ptr;
  for (i = 0; i < nFiles; i++)
     if ((ptr = strrchr(files[i],'.')) != NULL) *ptr = NUL;
  return;
}

/******************************************************************************
* WriteStringStdOut.
******************************************************************************/

void WriteStringStdOut (string, length)
char *string;
size_t length;
{
#if defined(mac)
  WriteStringStdOutMac (string, length);
#else
  fprintf (stdout, "%*.*s", (int) length, (int) length, string);
#endif
  return;
}

/******************************************************************************
* ReadCharStdIn.
******************************************************************************/

#if !defined(mac)
void ReadCharStdIn (key)
char *key;
{
  *key = (char) fgetc (stdin);
  return;
}
#endif

/******************************************************************************
* TempCharStr.
*    Returns a pointer to a NUL-terminated character string containing only
* the specified character.  Care must be taken when using this routine.  For
* instance, don't put two or more embedded calls to this routine in a function
* call.  The static character string would be overwritten for one of the two.
******************************************************************************/

char *TempCharStr (chr)
int chr;
{
  static char tempS[1+1] = " ";
  tempS[0] = (char) chr;
  return tempS;
}

/******************************************************************************
* DisplayStatistics.
******************************************************************************/

void DisplayStatistics (label, vStats)
char *label;
vSTATS *vStats;
{
  char temp1[MAX_SCREENLINE_LEN+1],
       temp2[MAX_SCREENLINE_LEN+1],
       temp3[MAX_SCREENLINE_LEN+1];
  BuildStatistics (label, vStats, temp1, temp2, temp3);
  WriteOut (stdout, temp1);
  WriteOut (stdout, "\n");
  WriteOut (stdout, temp2);
  WriteOut (stdout, "\n");
  WriteOut (stdout, temp3);
  WriteOut (stdout, "\n");
  return;
}

/******************************************************************************
* BuildStatistics.
******************************************************************************/

void BuildStatistics (label, vStats, temp1, temp2, temp3)
char *label;
vSTATS *vStats;
char temp1[MAX_SCREENLINE_LEN+1];
char temp2[MAX_SCREENLINE_LEN+1];
char temp3[MAX_SCREENLINE_LEN+1];
{
  strcpyX (temp1, "  Statistics", MAX_SCREENLINE_LEN);
  if (label != NULL) {
    strcatX (temp1, " for ", MAX_SCREENLINE_LEN);
    strcatX (temp1, label, MAX_SCREENLINE_LEN);
  }
  strcatX (temp1, ":", MAX_SCREENLINE_LEN);
  sprintf (temp2, "    %d buffer%s maximum (%d actually used), ",
	   vStats->maxBuffers, BOO(vStats->maxBuffers==1,"","s"),
	   vStats->nBuffers);
  sprintf (EofS(temp2), "[%ldr/%ldw]", vStats->nV_reads, vStats->nV_writes);
  sprintf (temp3, "    %ld page in%s, %ld page out%s, ",
	   vStats->nPageIns, BOO(vStats->nPageIns==1,"","s"),
	   vStats->nPageOuts, BOO(vStats->nPageOuts==1,"","s"));
  sprintf (EofS(temp3), "%ld block read%s, %ld block write%s",
	   vStats->nBlockReads, BOO(vStats->nBlockReads==1,"","s"),
	   vStats->nBlockWrites, BOO(vStats->nBlockWrites==1,"","s"));
  return;
}

/******************************************************************************
* EqStringsIgLTws.
*     Compares two strings ignoring leading and trailing white space.
******************************************************************************/

static Logical EqStringsIgLTws (string1, string2)
char *string1;
char *string2;
{
  char *tString1, *tString2, *ptr;
  size_t nChars;
  Logical returnValue;
  /****************************************************************************
  * Strip leading and trailing white space from 1st string.
  ****************************************************************************/
  ptr = string1;
  while (*ptr != NUL && IsSpaceChr(*ptr)) ptr++;
  nChars = strlen (ptr);
  tString1 = (char *) AllocateMemory (nChars + 1, FatalError);
  strcpyX (tString1, ptr, 0);
  ptr = tString1 + nChars;
  while (ptr != tString1 && IsSpaceChr(*(ptr-1))) ptr--;
  *ptr = NUL;
  /****************************************************************************
  * Strip leading and trailing white space from 2nd string.
  ****************************************************************************/
  ptr = string2;
  while (*ptr != NUL && IsSpaceChr(*ptr)) ptr++;
  nChars = strlen (ptr);
  tString2 = (char *) AllocateMemory (nChars + 1, FatalError);
  strcpyX (tString2, ptr, 0);
  ptr = tString2 + nChars;
  while (ptr != tString2 && IsSpaceChr(*(ptr-1))) ptr--;
  *ptr = NUL;
  /****************************************************************************
  * Free temporary strings and return result of comparison.
  ****************************************************************************/
  returnValue = (!strcmp(tString1,tString2) ? TRUE : FALSE);
  FreeMemory (tString1, FatalError);
  FreeMemory (tString2, FatalError);
  return returnValue;
}

/******************************************************************************
* StatusCodeReportOptions.
******************************************************************************/

char *StatusCodeReportOptions (e,w,i)
Logical e;
Logical w;
Logical i;
{
  static char mask[MAX_REPORTMASK_LEN+1];
#if defined(vms)
  strcpyX (mask, "(", MAX_REPORTMASK_LEN);
#else
  strcpyX (mask, "\"", MAX_REPORTMASK_LEN);
#endif
  strcatX (mask, (e ? "errors" : ""), MAX_REPORTMASK_LEN);
  strcatX (mask, (w ? (e ? ",warnings" : "warnings") : ""),
	   MAX_REPORTMASK_LEN);
  strcatX (mask, (i ? (e || w ? ",informationals" : "informationals") : ""),
	   MAX_REPORTMASK_LEN);
#if defined(vms)
  strcatX (mask, ")", MAX_REPORTMASK_LEN);
#else
  strcatX (mask, "\"", MAX_REPORTMASK_LEN);
#endif
#if defined(vms)
  Uppercase (mask);
#endif
  return mask;
}
