/********************************************************** 
 * Usage : amda-cdf-to-netcdf-w-false FileName [varToKeep1 varToKeep2 .....]
 * No 'false dims' suppression
 *********************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "netcdf.h"
#include "cdf.h"

#define MAX_FILE_NAME_LEN  120      /* Max. file name length              */
#define MAX_ATTRS          3000     /* Max # of CDF attributes            */
#define UNKNOWN_DT         -99      /* Unsupported CDF datatype indicator */

#define DEFAULT_MAPPING_FILE   "cdf_to_netcdf_mapping.dat"
#define MAX_REC_LENGTH         NC_MAX_NAME+CDF_ATTR_NAME_LEN256+2
                                 /* Max record length of DEFAULT_MAPPING_FILE */

#define TRUE               1
#define FALSE              0

/*************************************
   Global variables and structures
**************************************/
CDFid    id;        /* CDF file ID      */ 
int      ncid;      /* netCDF file ID   */

long nZvars,        /* Number of zVariables in a CDF file                     */
     nAttrs;        /* Number of attributes (global & variable) in a CDF file */

typedef struct ncdim {          /* netCDF dimension structure */
    char name[NC_MAX_NAME+1];           /* Dimension name */
    int  size;                          /* Dimension size */
    int  id;                            /* Dimension id   */
} netcdfDimension;

netcdfDimension ncDims[NC_MAX_DIMS];       /* max dimensions per netCDF file */
int  totNCdims = 0;         /* Total # of netCDF dimensions created          */

typedef struct cdfvar {           /* CDF variable structure */
     char name[CDF_VAR_NAME_LEN256+1];   
     long datatype;                   
     long numElements;            /* string length for CDF_CHAR, 1 otherwise */
     long dimensionality;             /* variable dimensionality             */
     long dimSizes[CDF_MAX_DIMS];     /* variable dimension sizes            */
     long recVariance;                /* record variance                     */
     long numRecs;                    /* # of records this variable contains */
} CDFvar;

/****************************************************************************
 *   Arrays to hold the CDF-to-netCDF mapping information that are loaded
 *   from the default or user-define mapping file.
 ****************************************************************************/
char netcdfAttrNames[MAX_ATTRS][NC_MAX_NAME];
char cdfAttrNames[MAX_ATTRS][CDF_ATTR_NAME_LEN256+1];
char comments[MAX_ATTRS][CDF_ATTR_NAME_LEN256+1];

int  totAttrsInMappingFile;           /* # of attributes in the mapping file */
char mappingFile[CDF_PATHNAME_LEN];  /* Mapping file name                   */

int DEBUG = FALSE;



/**************************
   Function prototypes
**************************/
int endsWith(char *, char *);
char * strlaststr(char *, char *);
void get_cdf_attribute(long, long, long, int);
void get_cdf_attr_data(char *, long, long, long, int);
char * cdf_str_datatype (long);
char * NC_datatype (nc_type);
nc_type get_netcdf_datatype (long);
void usage();
CDFid cdf_open(char *);
void read_cdf_file_info();
void get_zVariables(long *, int);
int create_netcdf_dimensions_attribute (int *);
void create_netcdf_dimensions_variable (CDFvar, long, int *, int *);
int create_netcdf_variable (CDFvar, long, int);
void get_cdf_variable_data (long, CDFvar, long);
void read_cdf_variable_data (long, long, long, long, long [], void *);
void removeFilepath();
void handle_netcdf_error (int);
void memory_error(char *, char *);
void cdf_status_handler (CDFstatus, char *);
void insufficient_memory();    /* used for debug */
char * getNextArgument (int, char *[], int);
void parseCmdLine (int, char *[]);
void errorMsg (char *);
void map_CDF_attr_to_netCDF (char *, char *);
void breakdown_DIMENSIONS_entry (char *, char **);

#define	AMDA
#ifdef	AMDA

#include "tools.h"

char	start_time [DD_TIME_STRING_LEN+1];	
char	stop_time  [DD_TIME_STRING_LEN+1];

#endif

/**************************************************************************** 
   NOTE:

   CDF has the following features that are not supported by netCDF:

   - CDF_EPOCH datatype (8-byte real number) that is used to store time 
     values referenced from a particular epoch that is 
     01-Jan-0000 00:00:00.000.  CDF_EPOCH values are the number of 
     milliseconds since the epoch described above.

   - CDF_EPOCH16 datatype (16-byte real number) that is used to store time 
     values referenced from a particular epoch that is 
     01-Jan-0000 00:00:00.000.000.000.000.  CDF_EPOCH16 values are the 
     number of milliseconds since the epoch described above.

   - CDF global attribute (not variable attribute) can have multiple
     attribute entries and each entry can have a different datatype.
*****************************************************************************/
int main(int argc, char *argv[])
{
    FILE  *inFile;             /* Pointer to the CDF-to-netCDF mapping file */
    char  rec[MAX_REC_LENGTH];     /* CDF-to-netCDF mapping record */

    /************************/
    /*  netCDF declaration  */
    /************************/
    int  status,                  /* netCDF status code */
         dummy, i; 

    char *ptr, fileName[CDF_PATHNAME_LEN];

    /**********************/
    /*  CDF declaration   */
    /**********************/
    CDFstatus   cstatus;           /* CDF status code */
    long        attrId;            /* CDF attribute ID */
    long        varId;
    long        *varsToGet;
    int         nVarsToGet;
    
    if (argc <= 1) 
        usage();                   /* CDF input file name not specified */
    else {
        strcpy(fileName, argv[1]);       /* Get the input file name */
        strcpy(mappingFile, "UNSPECIFIED");
        parseCmdLine(argc, argv);
        if (strcmp(mappingFile, fileName) == 0)
            errorMsg("** Error - either input file or mapping file is missing");
        if (strcmp(mappingFile,"UNSPECIFIED") == 0)  
            strcpy(mappingFile, DEFAULT_MAPPING_FILE);
    }

#ifdef	AMDA


    status = check_version();
#endif
    /***********************************************/
    /*  Load the CDF-to-netCDF mapping information */
    /***********************************************/
    if ((inFile = fopen(mappingFile, "r")) == NULL) {
         printf ("WARNING : Cannot open file: %s  **\n", mappingFile);
	 totAttrsInMappingFile = 0;
    }
    else {
        totAttrsInMappingFile = 0;
        while (fgets(rec, MAX_REC_LENGTH, inFile) != NULL) {
            rec[strlen(rec)-1] = '\0';       /* Remove the newline character */

	    /* Process the mapping record if it's not a comment */
	    if (rec[0] != '#'  &&  rec[0] != ' ' && strlen(rec) > 0) {
	        sscanf(rec, "%s %s",
		       netcdfAttrNames[totAttrsInMappingFile],
		       cdfAttrNames[totAttrsInMappingFile]);
	        ptr = (char *) strstr(rec, "//");
	        if (ptr != NULL) {
		    ptr += 3;
		    strcpy (comments[totAttrsInMappingFile], ptr);
	        }
                if (DEBUG)
		    printf("%s %s %s\n",
		           netcdfAttrNames[totAttrsInMappingFile],
		           cdfAttrNames[totAttrsInMappingFile],
		           comments[totAttrsInMappingFile]);
	        totAttrsInMappingFile++;
	    }
        }
    }
    /***********************************************************
     *  Process the CDF information and create a netCDF file
     ***********************************************************/
/*    printf ("\nInput file name: %s\n", fileName); */
    id = cdf_open (fileName);

    /* Remove the file path if it exists (/home/mydata.cdf => mydata.cdf). */
    removeFilepath(fileName);

    if (endsWith(fileName,".cdf")) {
        /* Strip off the .cdf file extension. */
        ptr = (char *) strlaststr(fileName, ".cdf");
        if (ptr != NULL) *ptr = '\0'; 
    }
    strcat(fileName, ".nc");
    status = nc_create(fileName, NC_CLOBBER, &ncid);
    if (status != NC_NOERR) handle_netcdf_error(status);
    printf ("Output file name: %s\n", fileName);

    /***********************************************************************
     * Get the number of dimensions, number of variables, number of global
     * attributes.
     *
     * Note that the information retrieved from read_cdf_file_info are stored
     * into the global variables defined at the top.
     ***********************************************************************/
    read_cdf_file_info ();
    if (DEBUG) printf ("nAttrs=%ld, nZvars=%ld\n", nAttrs, nZvars);

    /* Get the CDF global attributes and create netCDF global attributes. */
    if (DEBUG) printf ("Global attributes:\n");
    dummy = -1; 
    for (attrId = 0; attrId < nAttrs; attrId++) 
         get_cdf_attribute(attrId, GLOBAL_SCOPE, (long) dummy, dummy);

    /* Write the placeholder attributes. */
    for (i=0; i < totAttrsInMappingFile; i++) {
        if (strcmp(cdfAttrNames[i],"*") == 0) {
           status = nc_put_att_text (ncid, 
                                     NC_GLOBAL,           /* vavriable ID */
                                     netcdfAttrNames[i],  /* Attribute name */
                                     strlen(comments[i]), /* # of attr values */
                                     comments[i]);                            
         }
    }


    /* Process CDF variables and create netCDF variables */
    if (argc > 2) {
        varsToGet = (long *) malloc(sizeof(long)*(argc-2));
        for (i = 2; i < argc; i++ ) {
              status = CDFlib ( GET_, zVAR_NUMBER_, argv[i], &varId);
              varsToGet[i-2] = varId;
        }
        nVarsToGet = argc-2;
    }
    else {
        varsToGet = (long *) malloc(sizeof(long)*nZvars);
        for (i = 0; i <  nZvars; i++) varsToGet[i] = (long)i;
        nVarsToGet = nZvars;
    }
    printf("NVARS %d\n",nVarsToGet);
    if (nZvars > 0) get_zVariables(varsToGet, nVarsToGet);


    /* Close the netCDF and CDF files */
    cstatus = CDFlib (CLOSE_, CDF_, NULL_);
    if (cstatus != CDF_OK) cdf_status_handler (cstatus, "CLOSE_, CDF_");

    status = nc_close(ncid);
    if (status != NC_NOERR) handle_netcdf_error(status);
}


/*----------------------------------------------------------------------------
 *  TRUE if s1 ends with s2.  Otherwise, FALSE is returned.
 *---------------------------------------------------------------------------*/
int endsWith (char *s1, char *s2)
{ 
    int  i;
    char *ps1, *ps2;

    if (strlen(s2) > strlen(s1))
        return FALSE;

    ps1 = s1 + strlen(s1) - strlen(s2); 
    ps2 = s2;

    for (i=0; i < strlen(s2); i++) 
        if (*(ps1++) != *(ps2++))
            return FALSE;
     
    return TRUE;
}


/*---------------------------------------------------------------------------------
 *  Find the last occurence of s2 in s1.  If s21 is not found, s1 is returned. 
 *--------------------------------------------------------------------------------*/
char * strlaststr (char *s1, char *s2)
{
    char *sc2, *psc1, *ps1;

    if (*s2 == '\0')
        return((char *)s1);

    ps1 = s1 + strlen(s1);

    while(ps1 != s1) {
        --ps1;
        for (psc1 = ps1, sc2 = s2; ; )
                if (*(psc1++) != *(sc2++))
                        break;
                else if (*sc2 == '\0')
                        return ((char *)ps1);
    }
    return ((char *)NULL);
}


/*--------------------------------------------------------------------------
 *  This routine opens a CDF file
 *-------------------------------------------------------------------------*/
CDFid cdf_open (char *fileName)     
{
   CDFstatus status;
   CDFid     id;
   char      msg[500];

   status = CDFlib (OPEN_, CDF_, fileName,    /* in - file name to be opened */
                                 &id,         /* out - CDF file ID           */
                    NULL_);

   if (status != CDF_OK)  {
       strcpy(msg, "OPEN_, CDF_, ");       
       strcat(msg, fileName);
       cdf_status_handler (status, msg);
   }
   return id;
}


/*---------------------------------------------------------------------------
 *   This routine retrievs the following information:
 *
 *      nAttr - number of attributes (including global and variable)
 *      nZvars - number of zVariables
 *
 *   CDF file can have both rVariables (old style) and zVariables (new style)
 *   simultaneously.  zVariable is a superset of rVariable, and it is a lot
 *   more efficient and offers all the functionality a rVariable offers and 
 *   more.  Treat all CDF variables as zVariables.
 *--------------------------------------------------------------------------*/
void read_cdf_file_info ()
{
    CDFstatus status;

    status = CDFlib (SELECT_, CDF_zMODE_, zMODEon2,
                     GET_, CDF_NUMATTRS_,  &nAttrs, 
                           CDF_NUMzVARS_,  &nZvars, 
                     NULL_);
    if (status != CDF_OK) cdf_status_handler (status, "GET_, CDF_FILEINFO_");
}


/*----------------------------------------------------------------------------
 *  This routine retrieves the CDF attribute (global or variable) name
 *  and its data for the given CDF attribute ID.  
 *---------------------------------------------------------------------------*/
void get_cdf_attribute(long attrNum,        /* in - CDF attribute number/id */
                       long scope,          /* in - CDF attribute scope     */
                       long cdfVarId,       /* in - CDF variable number/id  */
                       int  netcdfVarId)    /* in - netCDF variable ID      */
{
    int           ncstatus, i, len;
    long          status, numEntries, datatype, attrScope, entryNum, 
                  numElements;
    nc_type       ncDatatype;      /* netCDF datatype */
    
    char          *cPtr, attrName[CDF_ATTR_NAME_LEN256+1],
                  mappedAttrName[CDF_ATTR_NAME_LEN256+1];

    status = CDFlib (SELECT_, ATTR_, attrNum,
                     GET_,    ATTR_NAME_, attrName,
                              ATTR_SCOPE_, &attrScope,
                     NULL_);
    if (status != CDF_OK) cdf_status_handler (status, "SELECT_, ATTR_");      

    /****************************************************************/
    /* If the attribute scope is not the requested attribute scope  */
    /* (VARIABLE_SCOPE or GLOBAL_SCOPE), do not process the current */
    /* attribute.                                                   */
    /****************************************************************/
    if (attrScope != scope) return;
    if (strcmp(attrName, "DIMENSIONS_G") == 0 ||
        strcmp(attrName, "DIMENSIONS_V") == 0) return;
    map_CDF_attr_to_netCDF(attrName, mappedAttrName);
    strcpy(attrName, mappedAttrName);

    if (attrScope == GLOBAL_SCOPE) {
        status = CDFlib (GET_,  ATTR_NUMgENTRIES_, &numEntries, NULL_);
        if (status != CDF_OK) 
            cdf_status_handler (status, "GET_, ATTR_NUMgENTRIES_");
        if (DEBUG) printf ("\t%s", attrName);

        /*********************************************************************
         * While the CDF global attribute can have multiple entries of 
         * of different datatypes, the CDF variable attribute can only have 
         * one attribute entry.  netCDF doesn't allow more than 1 attribute
         * entry - handle this case 
         ********************************************************************/
        for (entryNum=0; entryNum < numEntries; entryNum++) {
             if (entryNum > 0)            /* Attribute has more than 1 entry */
                 sprintf (attrName, "%s_%ld", mappedAttrName, entryNum);
             status = CDFlib (SELECT_, gENTRY_, entryNum,
                              GET_,    gENTRY_DATATYPE_, &datatype,
                                       gENTRY_NUMELEMS_, &numElements,
                              NULL_);
             if (status == NO_SUCH_ENTRY) return;
             if (status != CDF_OK) cdf_status_handler(status,"GET_ATTR_INFO_");
             get_cdf_attr_data (attrName, gENTRY_DATA_, datatype, numElements,
                                netcdfVarId);
        }
    }
    else {
        /*********************************************************************
         * IMPORTANT NOTE:
         *     For the variable attribute, entry number is the variable ID.
         *********************************************************************/
        status = CDFlib (SELECT_, zENTRY_, cdfVarId,
                         GET_,    zENTRY_DATATYPE_, &datatype,
                                  zENTRY_NUMELEMS_, &numElements,
                         NULL_);
        /******************************************************************/
        /* If there's no attribute entry for the current attribute number */
        /* selected, process the next attribute.                          */
        /******************************************************************/
        if (status == NO_SUCH_ENTRY) return;    

        if (status != CDF_OK) cdf_status_handler (status,"GET_ATTR_INFO_");
        if (DEBUG) printf ("\t%s", attrName);
        if (attrName[strlen(attrName)-1] == (char) '_') {
           char name2[CDF_ATTR_NAME_LEN256+1];
           strcpy(name2, attrName);
           name2[strlen(name2)-1] = (char) '\0';
           status = CDFlib (GET_, ATTR_NUMBER_, name2, &attrNum,
                            NULL_);
           if (status == CDF_OK) {
             strcpy(attrName, name2);
             map_CDF_attr_to_netCDF(attrName, mappedAttrName);
             strcpy(attrName, mappedAttrName);
           }
        }
        get_cdf_attr_data (attrName, zENTRY_DATA_, datatype, numElements,
                           netcdfVarId);
    }
}


/*--------------------------------------------------------------------------
 *  This routine retrieves the CDF attribute data (a.k.a. attribute entries)
 *  and write its values to the target netCDF file.
 *--------------------------------------------------------------------------*/
void get_cdf_attr_data (char *attrName,    /* in - attribute name          */
                        long entryData,    /* in - type of attr entry data */
                        long datatype,     /* in - attribute datatype      */ 
                        long numElements,  /* in - # of attribute values   */
                        int netcdfVarId)   /* in - netCDF variable ID      */
{
    char     entryDataType[20], msg[100],
             epString[EPOCH4_STRING_LEN+1],
             ep16String[EPOCH16_4_STRING_LEN+1],
             mappedAttrName[CDF_ATTR_NAME_LEN256+1],
             tt2000String[TT2000_3_STRING_LEN+1];
    double   epoch16[2];

    nc_type  ncDatatype;                  /* netCDF datatype    */
    int      nc_status,                   /* netCDF status code */
             varId;                       /* netCDF variable ID */
    long     status;                      /* CDF status code    */

    char          *cPtr;
    signed char   *scPtr;
    unsigned char *uscPtr;
    short         *sPtr;
    unsigned short *usPtr;
    int           *iPtr, i;
    unsigned int  *uiPtr;
    long long     *jPtr, aLonglong;
    float         *fPtr;
    double        *dPtr;

    /*************************************************
     * entryData has one of following values: 
     * 
     *    gENTRY_DATA_ - global attribute entry
     *    rENTRY_DATA_ - rVariable attribute entry
     *    zENTRY_DATA_ - zVariable attribute entry
     *************************************************/
    if (entryData == gENTRY_DATA_) {
        varId = NC_GLOBAL;
        strcpy(entryDataType, "gENTRY_DATA_");
    }
    else {
        varId = netcdfVarId;
        strcpy(entryDataType, "zENTRY_DATA_");
    }
    strcpy (msg, "get_cdf_attr_data, GET_, ");
    strcat (msg, entryDataType);
    strcat (msg, cdf_str_datatype(datatype));

    /*  Map the CDF datatype to an appropriate netCDF datatype. */
    ncDatatype = get_netcdf_datatype (datatype);

    /* Remove trailing blank spaces if there are any */
    for (i=strlen(attrName)-1; i >= 0; i--) {
         if (attrName[i] != ' ') {
             attrName[i+1] = '\0';
             break;
         }
    }
    /* Replace blanks space(s) with underscore(s). */
    for (i=0; i < strlen(attrName); i++) {
         if (attrName[i] == ' ') attrName[i] = '_';
    }

    switch (datatype) {
    case CDF_CHAR:
    case CDF_UCHAR:
        cPtr = (char *) malloc(numElements * sizeof(char) + 1);
        if (cPtr == NULL) memory_error("get_cdf_attr_data", "NC_CHAR");
        status = CDFlib (GET_, entryData, cPtr, NULL_);
        if (status != CDF_OK) cdf_status_handler (status, msg);
        *(cPtr+numElements) = '\0';               /* End of string mark */
        if (DEBUG) printf (" = \"%s\"", cPtr);
        nc_status = nc_put_att_text(ncid, varId, attrName, numElements, cPtr);
        if (nc_status != NC_NOERR) handle_netcdf_error(nc_status);
        free(cPtr);
        break;

    case CDF_BYTE:
    case CDF_INT1:
        scPtr = (signed char *) malloc (sizeof(signed char) * numElements);
        if (scPtr == NULL) memory_error("get_cdf_attr_data", "NC_BYTE");
        status = CDFlib (GET_, entryData, scPtr, NULL_);
        if (status != CDF_OK) cdf_status_handler (status, msg);
        if (DEBUG) {
            printf (" = ");
            for (i=0; i < numElements; i++) {
                 if (i > 0) printf (", ");
                 printf ("%d", *(scPtr+i));
            }
        }
        nc_status = nc_put_att_schar(ncid, varId, attrName, ncDatatype, 
                                     numElements, scPtr);
        if (nc_status != NC_NOERR) handle_netcdf_error(nc_status);
        free (scPtr);
        break;

    case CDF_UINT1:
        uscPtr = (unsigned char *) malloc (sizeof(unsigned char) * numElements);
        if (uscPtr == NULL) memory_error("get_cdf_attr_data", "NC_BYTE");
        sPtr = (short *) malloc (sizeof(short) * numElements);
        if (sPtr == NULL) memory_error("get_cdf_attr_data", "NC_SHORT");
        status = CDFlib (GET_, entryData, uscPtr, NULL_);
        if (status != CDF_OK) cdf_status_handler (status, msg);
        if (DEBUG) {
            printf (" = ");
            for (i=0; i < numElements; i++) {
                 if (i > 0) printf (", ");
                 printf ("%d", *(uscPtr+i));
            }
        }
        for (i = 0; i < numElements; ++i) {
          *(sPtr+i) = (short) *(uscPtr+i);
        }
        nc_status = nc_put_att_short(ncid, varId, attrName, ncDatatype, 
                                     numElements, sPtr);
        if (nc_status != NC_NOERR) handle_netcdf_error(nc_status);
        free (uscPtr);
        free (sPtr);
        break;

    case CDF_INT2:
        sPtr = (short *) malloc (sizeof(short) * numElements);
        if (sPtr == NULL) memory_error("get_cdf_attr_data", "NC_SHORT");
        status = CDFlib (GET_, entryData, sPtr, NULL_);
        if (status != CDF_OK) cdf_status_handler (status, msg);
        if (DEBUG) {    
            printf (" = ");    
            for (i=0; i < numElements; i++) {    
                 if (i > 0) printf (", ");    
                 printf ("%hd", *(sPtr+i));   
            }    
        }    
        nc_status = nc_put_att_short(ncid, varId, attrName, ncDatatype, 
                                     numElements, sPtr);
        if (nc_status != NC_NOERR) handle_netcdf_error(nc_status);
        free (sPtr);
        break;

    case CDF_UINT2:
        usPtr = (unsigned short *) malloc (sizeof(unsigned short) * numElements);
        if (usPtr == NULL) memory_error("get_cdf_attr_data", "NC_SHORT");
        iPtr = (int *) malloc (sizeof(int) * numElements);
        if (iPtr == NULL) memory_error("get_cdf_attr_data", "NC_INT");
        status = CDFlib (GET_, entryData, usPtr, NULL_);
        if (status != CDF_OK) cdf_status_handler (status, msg);
        if (DEBUG) {    
            printf (" = ");    
            for (i=0; i < numElements; i++) {    
                 if (i > 0) printf (", ");    
                 printf ("%hu", *(usPtr+i));   
            }    
        }    
        for (i = 0; i < numElements; ++i) {
          *(iPtr+i) = (int) *(usPtr+i);
        }   
        nc_status = nc_put_att_int(ncid, varId, attrName, ncDatatype, 
                                   numElements, iPtr);
        if (nc_status != NC_NOERR) handle_netcdf_error(nc_status);
        free (usPtr);
        free (iPtr);
        break;

    case CDF_INT4:
        iPtr = (int *) malloc (sizeof(int) * numElements);
        if (iPtr == NULL) memory_error("get_cdf_attr_data", "NC_INT");
        status = CDFlib (GET_, entryData, iPtr, NULL_);
        if (status != CDF_OK) cdf_status_handler (status, msg);
        if (DEBUG) {
            printf (" = ");
            for (i=0; i < numElements; i++) {
                 if (i > 0) printf (", ");
                 printf ("%d", *(iPtr+i));
            }
        }
        nc_status = nc_put_att_int(ncid, varId, attrName, ncDatatype, 
                                   numElements, iPtr);
        if (nc_status != NC_NOERR) handle_netcdf_error(nc_status);
        free (iPtr);
        break;

    case CDF_UINT4:
        uiPtr = (unsigned int *) malloc (sizeof(unsigned int) * numElements);
        if (uiPtr == NULL) memory_error("get_cdf_attr_data", "NC_INT");
        dPtr = (double *) malloc (sizeof(double) * numElements);
        if (dPtr == NULL) memory_error("get_cdf_attr_data", "NC_DOUBLE");
        status = CDFlib (GET_, entryData, uiPtr, NULL_);
        if (status != CDF_OK) cdf_status_handler (status, msg);
        if (DEBUG) {
            printf (" = ");
            for (i=0; i < numElements; i++) {
                 if (i > 0) printf (", ");
                 printf ("%ud", *(uiPtr+i));
            }
        }
        for (i = 0; i < numElements; ++i) {
          *(dPtr+i) = (double) *(uiPtr+i);
        }   
        nc_status = nc_put_att_double(ncid, varId, attrName, ncDatatype, 
                                      numElements, dPtr);
        if (nc_status != NC_NOERR) handle_netcdf_error(nc_status);
        free (uiPtr);
        free (dPtr);
        break;

    case CDF_INT8:
        jPtr = (long long *) malloc (sizeof(long long) * numElements);
        if (jPtr == NULL) memory_error("get_cdf_attr_data", "NC_INT64");
        dPtr = (double *) malloc (sizeof(double) * numElements);
        if (dPtr == NULL) memory_error("get_cdf_attr_data", "NC_DOUBLE");
        status = CDFlib (GET_, entryData, jPtr, NULL_);
        if (status != CDF_OK) cdf_status_handler (status, msg);
        if (DEBUG) {
            printf (" = ");
            for (i=0; i < numElements; i++) {
                 if (i > 0) printf (", ");
                 printf ("%lld", *(jPtr+i));
            }
        }
        for (i = 0; i < numElements; ++i) {
          *(dPtr+i) = (double) *(jPtr+i);
        } 
        nc_status = nc_put_att_double(ncid, varId, attrName, ncDatatype, 
                                      numElements, dPtr);
        if (nc_status != NC_NOERR) handle_netcdf_error(nc_status);
        free (jPtr);
        free (dPtr);
        break;

    case CDF_FLOAT:
    case CDF_REAL4:
        fPtr = (float *) malloc (sizeof(float) * numElements);
        if (fPtr == NULL) memory_error("get_cdf_attr_data", "NC_FLOAT");
        status = CDFlib (GET_, entryData, fPtr, NULL_);
        if (status != CDF_OK) cdf_status_handler (status, msg);
        if (DEBUG) {
            printf (" = ");
            for (i=0; i < numElements; i++) {
                 if (i > 0) printf (", ");
                 printf ("%g", *(fPtr+i));
            }
        }
        nc_status = nc_put_att_float(ncid, varId, attrName, ncDatatype, 
                                     numElements, fPtr);
        if (nc_status != NC_NOERR) handle_netcdf_error(nc_status);
        free (fPtr);
        break;

    case CDF_DOUBLE:
    case CDF_REAL8:
        dPtr = (double *) malloc (sizeof(double) * numElements);
        if (dPtr == NULL) memory_error("get_cdf_attr_data", "NC_DOUBLE");
        status = CDFlib (GET_, entryData, dPtr, NULL_);
        if (status != CDF_OK) cdf_status_handler (status, msg);
        if (DEBUG) {
            printf (" = ");
            for (i=0; i < numElements; i++) {
                 if (i > 0) printf (", ");
                 printf ("%g", *(dPtr+i));
            }
        }
        nc_status = nc_put_att_double(ncid, varId, attrName,
                                      ncDatatype, numElements, dPtr);
        if (nc_status != NC_NOERR) handle_netcdf_error(nc_status);
        free (dPtr);
        break;

    case CDF_EPOCH:       /* 8-byte real number */ 
        dPtr = (double *) malloc (sizeof(double) * numElements);
        if (dPtr == NULL) memory_error("get_cdf_attr_data", "CDF_EPOCH");
        status = CDFlib (GET_, entryData, dPtr, NULL_);
        if (status != CDF_OK) cdf_status_handler (status, msg);
        if (DEBUG) printf (" = ");
        for (i=0; i < numElements; i++) {
             encodeEPOCH4 (* (dPtr+i), epString);
             nc_status = nc_put_att_text(ncid, varId, attrName, EPOCH4_STRING_LEN, epString);
             if (nc_status != NC_NOERR) handle_netcdf_error(nc_status);
             if (DEBUG)  {
                 if (i > 0) printf (", ");
                 printf ("%s", epString);
                 if (i == (numElements -1)) printf ("\n");
             }
        }
        free (dPtr);
        break;

    case CDF_EPOCH16:       /* 16-byte real number */
        dPtr = (double *) malloc (sizeof(double) * numElements * 2);
        if (dPtr == NULL) memory_error("get_cdf_attr_data", "CDF_EPOCH16");
        status = CDFlib (GET_, entryData, dPtr, NULL_);
        if (status != CDF_OK) cdf_status_handler (status, msg);
        if (DEBUG) printf (" = "); 
        for (i=0; i < numElements; i++) {
             epoch16[0] = *(dPtr+i*2);
             epoch16[1] = *(dPtr+i*2+1);
             encodeEPOCH16_4 (epoch16, ep16String);
             nc_status = nc_put_att_text(ncid, varId, attrName,
                                         strlen(ep16String), ep16String);
             if (nc_status != NC_NOERR) handle_netcdf_error(nc_status);
             if (DEBUG)  {
                 if (i > 0) printf (", "); 
                 printf ("%s", ep16String);
                 if (i == (numElements -1)) printf ("\n");
             }   
        }
        free (dPtr);
        break;

    case CDF_TIME_TT2000:       /* 8-byte integer number */ 
        jPtr = (long long *) malloc (sizeof(long long) * numElements);
        if (jPtr == NULL) memory_error("get_cdf_attr_data", "CDF_TIME_TT2000");
        status = CDFlib (GET_, entryData, jPtr, NULL_);
        if (status != CDF_OK) cdf_status_handler (status, msg);
        if (DEBUG) printf (" = ");
        for (i=0; i < numElements; i++) {
             encodeTT2000 (*(jPtr+i), tt2000String);
             nc_status = nc_put_att_text(ncid, varId, attrName,
                                         TT2000_3_STRING_LEN, tt2000String);
             if (nc_status != NC_NOERR) handle_netcdf_error(nc_status);
             if (DEBUG)  {
                 if (i > 0) printf (", ");
                 printf ("%s", tt2000String);
                 if (i == (numElements -1)) printf ("\n");
             }
        }
        free (jPtr);
        break;

    default:
            printf ("** Error in get_cdf_attribute: bad data type");
    }
    if (DEBUG) printf (" ;\n");
}


/*--------------------------------------------------------------------------
 *  Get the zVariables in the CDF file.
 *--------------------------------------------------------------------------*/
void get_zVariables(long *vars, int nvars) 
{
   CDFstatus status;
   int       i, ncstatus, createUnlimitedDim, unlimitedDimId, 
             attrId, netcdfVarId, bypass; 
   char      netcdfDimName[NC_MAX_NAME+1];       /* netCDF dimension name */
   long      holdDim, holdDim0;
   long      varId, ncVarId;
   CDFvar    var;

   if (DEBUG) printf ("\n");

   createUnlimitedDim = TRUE;
   bypass = FALSE;
   
   for (i = 0; i < nvars; i++) {
       
        varId = vars[i];
        ncVarId = (long)i;
        status = CDFlib (SELECT_,zVAR_, varId,
                         GET_, zVAR_NAME_, var.name,
                               zVAR_DATATYPE_, &var.datatype,
                               zVAR_NUMELEMS_, &var.numElements,
                               zVAR_NUMDIMS_, &var.dimensionality,
                               zVAR_DIMSIZES_, var.dimSizes,
                               zVAR_NUMRECS_, &var.numRecs,
                               zVAR_RECVARY_, &var.recVariance,
                         NULL_);   
        if (status != CDF_OK) cdf_status_handler (status, "GET_, zVARS_");

        if (DEBUG) {
            printf ("var name = %s, %s/%ld, %ld:[", 
                    var.name, cdf_str_datatype(var.datatype), 
                    var.numElements, var.dimensionality); 
            for (i=0; i < var.dimensionality; i++) {
                 if (i > 0) printf (",");
                 printf ("%ld", var.dimSizes[i]);
            }
            printf("], numRecs = %ld\n", var.numRecs);
        }

        if (varId == 0)
          bypass = create_netcdf_dimensions_attribute (&unlimitedDimId);
        if (!bypass)
          create_netcdf_dimensions_variable (var, ncVarId, &createUnlimitedDim,
                                             &unlimitedDimId);
        netcdfVarId = create_netcdf_variable (var, ncVarId, unlimitedDimId);

        /* Leave the define mode before adding data */
        ncstatus = nc_enddef(ncid);
        if (ncstatus != NC_NOERR) handle_netcdf_error(ncstatus);

        get_cdf_variable_data (varId, var, ncVarId);

        /* Enter the define mode before writing netCDF variable attributes */
        /* and creating a variable.                                        */
        ncstatus = nc_redef(ncid);
        if (ncstatus != NC_NOERR) handle_netcdf_error(ncstatus);

        /* Get the variable attributes */
        for (attrId = 0; attrId < nAttrs; attrId++)
             get_cdf_attribute(attrId, VARIABLE_SCOPE, varId, netcdfVarId);
   }
#ifdef	AMDA

    if (set_file_time_coverage() == -1) {

	printf ("ERROR : unable to compute file time coverage\n");
    }

    if (rename_time_variable () == -1) {

        printf ("ERROR : uname to rename time variable\n");
    }


#endif
}


/*--------------------------------------------------------------------------
 *  This routine creates all netCDF dimensions from a global attribute if
 *  such attribute exists (likely the CDF is created by netcdf-to-cdf
 *  converter). It returns TRUE if dimensions are creaated, FLASE otherwise.
 *--------------------------------------------------------------------------*/
int create_netcdf_dimensions_attribute (int *unlimitedDimId)
{
   int     status,                             /* netCDF status code */
           i, id, value, len;                  
   long    numElems, numEntries;
   char    netcdfDimName[NC_MAX_NAME+1];       /* netCDF dimension name */
   char    valueStr[20+1];
   char    *ptr, entry[NC_MAX_NAME + 20 + 1];
   CDFstatus statusC;
   
   statusC = CDFlib (SELECT_, ATTR_NAME_, "DIMENSIONS_G",
                     GET_, ATTR_NUMgENTRIES_, &numEntries,
                     NULL_);
   if (statusC != CDF_OK) return FALSE;
   /* A CDF file that was created by netCDF-to-cdf */
   for (i = 0; i < (int) numEntries; ++i) {
       statusC = CDFlib (SELECT_, ATTR_NAME_, "DIMENSIONS_G",
                                  gENTRY_, (long) i,
                         GET_, gENTRY_DATA_, entry,
                               gENTRY_NUMELEMS_, &numElems,
                         NULL_);
       if (statusC != CDF_OK) continue;
       entry[numElems] = (char) '\0';
       ptr = strstr(entry, "=");
       if (ptr == NULL) continue;
       len = (int) (ptr - entry);
       strncpy(netcdfDimName, entry, len);
       strcpy(valueStr, ptr+1);
       netcdfDimName[len] = (char) '\0';
       if (strcmp(valueStr, "UNLIMITED") == 0) {
           status = nc_def_dim(ncid,               /* in  */
                               netcdfDimName,      /* in  */
                               NC_UNLIMITED,       /* in  */
                               unlimitedDimId);    /* out */
       } else {
           if (sscanf(valueStr, "%d", &value) != 1) continue;
           status = nc_def_dim(ncid,               /* in  */
                               netcdfDimName,      /* in  */
                               value,              /* in  */
                               &id);               /* out */
       }
       if (status != NC_NOERR) handle_netcdf_error(status);
   }
   return TRUE;
}


/*--------------------------------------------------------------------------
 *  This routine creates a netCDF dimension(s) from a variable.
 *--------------------------------------------------------------------------*/
void create_netcdf_dimensions_variable (CDFvar var, long varNum,
                                        int *createUnlimitedDim,
                                        int *unlimitedDimId)
{
   int     status,                             /* netCDF status code */
           i, j, dimensionCreated, id;
   long    dimensionality;
   char    netcdfDimName[NC_MAX_NAME+1];       /* netCDF dimension name */
   CDFstatus statusC;

   dimensionality = var.dimensionality;

   if (var.datatype == CDF_CHAR) {
       var.dimSizes[dimensionality] = var.numElements;
       dimensionality++;
   }
   else if (var.datatype == CDF_EPOCH) {
       var.dimSizes[dimensionality] = DD_TIME_STRING_LEN;
       dimensionality++;
   }
   else if (var.datatype == CDF_EPOCH16) {
       var.dimSizes[dimensionality] = DD_TIME_STRING_LEN;
       dimensionality++;
   }
   else if (var.datatype == CDF_TIME_TT2000) {
       var.dimSizes[dimensionality] = DD_TIME_STRING_LEN;
       dimensionality++;
   }

   if (*createUnlimitedDim) {
       if (var.recVariance == VARY) {
           status = nc_def_dim(ncid, "Time", NC_UNLIMITED,unlimitedDimId);
           if (status != NC_NOERR) handle_netcdf_error(status);
           *createUnlimitedDim = FALSE;
       }
   }
   for (i=0; i < dimensionality; i++) {
        dimensionCreated = FALSE;
        for (j=0; j < totNCdims; j++) {
             if (ncDims[j].size == var.dimSizes[i]) {
                 dimensionCreated = TRUE;
                 break;
             }
        }
        if (!dimensionCreated && var.dimSizes[i] > 1) {
            ncDims[totNCdims].size = var.dimSizes[i];
            sprintf (netcdfDimName, "dim%d", totNCdims);
            strcpy(ncDims[totNCdims].name, netcdfDimName);
            status = nc_def_dim(ncid,                      /* in  */
                                ncDims[totNCdims].name,    /* in  */
                                ncDims[totNCdims].size,    /* in  */
                                &ncDims[totNCdims].id);    /* out */
            if (status != NC_NOERR) handle_netcdf_error(status);
            totNCdims++;
        }
   }
}


/*--------------------------------------------------------------------------
 *  This routine creates a netCDF variable.
 *--------------------------------------------------------------------------*/
int create_netcdf_variable (CDFvar var,          /* in - CDF variable     */
                            long varNum,         /* in - CDF variable id  */
                            int unlimitedDimId)  /* in - unlimited dim ID */
{
   int     status,                       /* netCDF status code */
           i, j, k, id,
           varId,                        /* netCDF variable ID */
           nc_dimensionality,
           dimensionality,               /* netCDF dimensionality */
           dims[NC_MAX_DIMS];            /* netCDF dimensions */
   nc_type datatype;
   char *entry;
   char **dimStrings;
   int count = 0;
   long numElems;
   CDFstatus statusC;

   dimensionality = var.dimensionality;
   datatype = get_netcdf_datatype (var.datatype);

   /*********************************************************************
    * While the string value is treated as 0-d in CDF, it is treated as 
    * 1-D in netCDF.                                                   
    *********************************************************************/
   if (var.datatype == CDF_CHAR) {
       var.dimSizes[dimensionality] = var.numElements;
       dimensionality++;
   }

   /*********************************************************************
    * In CDF, CDF_EPOCH, CDF_EPOCH16 and CDF_TIME_TT2000 are used to
    * store a date and time value into a 8-byte real, 16-byte real or
    * 8-byte integer value, respectively.  Since netCDF 
    * doesn't support EPOCH, the CDF EPOCH variable needs to be translated 
    * as a string value. 
    *********************************************************************/
   else if (var.datatype == CDF_EPOCH) {
       var.dimSizes[dimensionality] = DD_TIME_STRING_LEN;
       dimensionality++;
   }
   else if (var.datatype == CDF_EPOCH16) {
       var.dimSizes[dimensionality] = DD_TIME_STRING_LEN;
       dimensionality++;
   }
   else if (var.datatype == CDF_TIME_TT2000) {
       var.dimSizes[dimensionality] = DD_TIME_STRING_LEN;
       dimensionality++;
   }

   /************************************************************************
    *  If CDF's record variance is true, then the netCDF's first dimension 
    *  reflects the record number.                               
    ************************************************************************/
   k = 0;
   if (var.recVariance == VARY) { 
       dims[k++] = unlimitedDimId;
       dimensionality++;
   }

   entry = (char *) malloc ((size_t) (dimensionality+1) * NC_MAX_NAME +
                            dimensionality + 1 );
   statusC = CDFlib (SELECT_, ATTR_NAME_, "DIMENSIONS_V",
                              zENTRY_, varNum,
                     GET_, zENTRY_DATA_, entry,
                           zENTRY_NUMELEMS_, &numElems,
                     NULL_);
   if (statusC == CDF_OK) { /* A CDF converted from netCDF-to-cdf. */
      entry[numElems] = (char) '\0';
      for (i = 0; i < strlen(entry); ++i) if (entry[i] == (char) ',') ++ count;
      ++count;
      dimStrings = (char **) malloc (sizeof(char *) * count);
      for (i = 0; i < count; ++i) {
        dimStrings[i] = (char *) malloc(NC_MAX_NAME + 1);
      }
      breakdown_DIMENSIONS_entry(entry, dimStrings);
      k = 0;
      for (j = 0; j < count; ++j) {
        status = nc_inq_dimid(ncid, dimStrings[j], &id);
        if (status != NC_NOERR) handle_netcdf_error(status);
        dims[k++] = id;
      }
      for (i = 0; i < count; ++i) free (dimStrings[i]);
      free (dimStrings);
   } else {
      for (i=0; i < dimensionality; i++) {
        for (j=0; j < totNCdims; j++) {
             if (ncDims[j].size == var.dimSizes[i]) {
                 dims[k++] = ncDims[j].id;
                 break;
             }
        }
      }
   }
   free (entry);
   /* Remove trailing blank spaces if there are any */
   for (i=strlen(var.name)-1; i >= 0; i--) {
        if (var.name[i] != ' ') {
            var.name[i+1] = '\0'; 
            break;
        }
   }

   /* Replace blank space(s) with underscore(s). */
   for (i=0; i < strlen(var.name); i++) {
        if (var.name[i] == ' ') var.name[i] = '_'; 
   }

/*  Exclude "false" dimension  */
   nc_dimensionality = dimensionality;
//    for (i=0; i < dimensionality; i++) {
//     if (  var.dimSizes[i] == 1 ) { 
//              nc_dimensionality--;
//              j = i;
//              break;
//         }
//   }
   
   /* Create a netCDF variable. */
   status = nc_def_var(ncid, var.name, datatype, nc_dimensionality, dims, &varId); 
   if (status != NC_NOERR) handle_netcdf_error(status);
   return varId;
}


/*--------------------------------------------------------------------------
 *  Get the CDF variable data and write the data just read into the 
 *  netCDF variable created in create_netcdf_variable.
 *--------------------------------------------------------------------------*/
void get_cdf_variable_data (long varId,      /* in - variable ID/number     */
                            CDFvar var,     /* in - CDF variable structure */
                            long ncVarId)  /* in - netCDF variable ID/number  */
{
   CDFstatus status;                         /* CDF status code    */
   int       ncstatus;                       /* netCDF status code */
   char      epString[EPOCH4_STRING_LEN+1];
   char      ep16String[EPOCH16_4_STRING_LEN+1];
   char      tt2000String[TT2000_3_STRING_LEN+1];
   double    epoch16[2];

    size_t start[NC_MAX_DIMS],              /* index for where to read first */ 
           count[NC_MAX_DIMS];              /* # of values to read           */

    int     i, j, k, divide, 
                  dimensionality,
                  numValues;                /* # of values on each record */
char	* ptr;
    char          *cPtr;
    signed char   *scPtr;
    unsigned char *ucPtr;
    short         *sPtr;
    unsigned short *usPtr;
    int           *iPtr;
    unsigned int  *uiPtr;
    long long     *jPtr, aLonglong;
    float         *fPtr;
    double        *dPtr;
    long          recNo, 
                  totRecs,              /* total # of records to read */
                  numRecsLeftToRead,
                  numRecs;       /* # of records to read and write at a time */ 

    j = 0;
    start[0]  = 0;
    numValues = 1;                      /* # of values on each CDF record */

    if (var.recVariance == VARY) {   /* Treat it as a netCDF record variable */
        start[j] = 0;                         /* start record #        */
        count[j++] = var.numRecs;             /* # of records to write */
    }

   dimensionality = var.dimensionality;

   if (var.datatype == CDF_CHAR) {
       var.dimSizes[dimensionality] = var.numElements;
       dimensionality++;
   }

   /*********************************************************************
    * In CDF, CDF_EPOCH and CDF_EPOCH16 are used to store a date and time
    * value into a 8-byte and 16-byte real value, respectively.  Since netCDF
    * doesn't support EPOCH, the CDF EPOCH variable needs to be translated
    * as a string value. Added newer 8-byte integer CDF_TIME_TT2000 data.
    *********************************************************************/
   else if (var.datatype == CDF_EPOCH) { 
       var.dimSizes[dimensionality] = DD_TIME_STRING_LEN;
       dimensionality++;
   }
   else if (var.datatype == CDF_EPOCH16) { 
       var.dimSizes[dimensionality] = DD_TIME_STRING_LEN;
       dimensionality++;
   }
   else if (var.datatype == CDF_TIME_TT2000) { 
       var.dimSizes[dimensionality] = DD_TIME_STRING_LEN;
       dimensionality++;
   }

    for (i=0; i < dimensionality; i++) {
         start[j] = 0;
         if (var.dimSizes[i] > 1) count[j++] = var.dimSizes[i];
         numValues *= var.dimSizes[i];
    }

    if (var.datatype == CDF_EPOCH)
        numValues /= DD_TIME_STRING_LEN;
    else if (var.datatype == CDF_EPOCH16)
        numValues /= DD_TIME_STRING_LEN;
    else if (var.datatype == CDF_TIME_TT2000)
        numValues /= DD_TIME_STRING_LEN;

    totRecs           = var.numRecs;      /* total # of records for this var */
    numRecsLeftToRead = var.numRecs;

    while (numRecsLeftToRead > 0) {
        recNo   = start[0];               /* record # to start reading */
        divide  = 1;
        numRecs = numRecsLeftToRead;      /* # of records to read at a time */

        switch (var.datatype) {
        case CDF_CHAR:
        case CDF_UCHAR:
            cPtr = NULL;
            while (cPtr == NULL) {
               cPtr = (char *) malloc (sizeof(char) * numValues * numRecs);
               if (cPtr == NULL) {
                   if (DEBUG) insufficient_memory();
                   divide *= 2;
                   numRecs = totRecs / divide;
               }
            }
            read_cdf_variable_data (varId,
                                    recNo,          /* starting record # */
                                    numRecs,        /* # of records to read */
                                    var.dimensionality,
                                    var.dimSizes,   /* dimesion sizes */
                                    cPtr);          /* data pointer   */
            if (DEBUG) {
                printf ("   retrieved CDF data = \n");
                for (k=0; k < numRecs; k++) {
                  printf("\"");
                  for (i=0; i < numValues; i++) 
                    printf ("%c", *(cPtr+(k*numValues+i)));
                  printf("\"\n");
                }
            }

            start[0] = 0;
            start[1] = 0;
            if (var.recVariance == VARY) {
                count[0] = numRecs;
            }

            if (DEBUG) {
                for (i=0; i < dimensionality; i++)
                     printf ("   start[%d] = %ld, count[%d] = %ld\n",
                             i, start[i], i, count[i]);
            }
            ncstatus = nc_put_vara_text(ncid, ncVarId, start, count, cPtr);
            if (ncstatus != NC_NOERR) handle_netcdf_error(ncstatus);
            if (DEBUG && ncstatus != NC_NOERR) {
                if (ncstatus != NC_NOERR) 
                    printf ("   Error putting data...\n");
                else
                    printf ("   Done putting data...\n");
            }
            free (cPtr);
            break;

        case CDF_BYTE:
        case CDF_INT1:
            scPtr = NULL;
            while (scPtr == NULL) {
               scPtr = 
                 (signed char *) malloc (sizeof(signed char)*numValues*numRecs);
               if (scPtr == NULL) {
                   if (DEBUG) insufficient_memory();
                   divide *= 2;
                   numRecs = totRecs / divide;
               }
            }
            read_cdf_variable_data (varId,
                                    recNo,           /* starting record # */
                                    numRecs,         /* # of records to read */
                                    var.dimensionality,
                                    var.dimSizes,    /* dimesion sizes */
                                    scPtr);          /* data pointer   */
            if (var.recVariance == VARY)
                count[0] = numRecs;
            ncstatus = nc_put_vara_schar(ncid, ncVarId, start, count, scPtr);
            if (ncstatus != NC_NOERR) handle_netcdf_error(ncstatus);
            free (scPtr);
            break;

        case CDF_UINT1:
            ucPtr = NULL;
            sPtr = NULL;
            while (ucPtr == NULL || sPtr == NULL) {
               ucPtr = 
                 (unsigned char *) malloc (sizeof(unsigned char)*numValues*numRecs);
               sPtr = 
                 (short *) malloc (sizeof(short)*numValues*numRecs);
               if (ucPtr == NULL || sPtr == NULL) {
                   if (DEBUG) insufficient_memory();
                   divide *= 2;
                   numRecs = totRecs / divide;
               }
            }
            read_cdf_variable_data (varId,
                                    recNo,           /* starting record # */
                                    numRecs,         /* # of records to read */
                                    var.dimensionality,
                                    var.dimSizes,    /* dimesion sizes */
                                    ucPtr);          /* data pointer   */
            if (var.recVariance == VARY)
                count[0] = numRecs;
            for (j = 0; j < numValues*numRecs; ++j) {
              *(sPtr+j) = (short) *(ucPtr+j);
            }
            ncstatus = nc_put_vara_short(ncid, ncVarId, start, count, sPtr);
            if (ncstatus != NC_NOERR) handle_netcdf_error(ncstatus);
            free (ucPtr);
            free (sPtr);
            break;

        case CDF_INT2:
            sPtr = NULL;
            while (sPtr == NULL) {
               sPtr = (short *) malloc (sizeof(short) * numValues * numRecs);
               if (sPtr == NULL) {
                   if (DEBUG) insufficient_memory();
                   divide *= 2;
                   numRecs = totRecs / divide;
               }
            }
            read_cdf_variable_data (varId,
                                    recNo,          /* starting record #    */
                                    numRecs,        /* # of records to read */
                                    var.dimensionality,
                                    var.dimSizes,   /* dimesion sizes */
                                    sPtr);          /* data pointer   */
            if (var.recVariance == VARY)
                count[0] = numRecs;
            ncstatus = nc_put_vara_short(ncid, ncVarId, start, count, sPtr);
            if (ncstatus != NC_NOERR) handle_netcdf_error(ncstatus);
            free (sPtr);
            break;

        case CDF_UINT2:
            usPtr = NULL;
            iPtr = NULL;
            while (usPtr == NULL || iPtr == NULL) {
               usPtr = (unsigned short *) malloc (sizeof(unsigned short) * numValues * numRecs);
               iPtr = (int *) malloc (sizeof(int) * numValues * numRecs);
               if (usPtr == NULL || iPtr == NULL) {
                   if (DEBUG) insufficient_memory();
                   divide *= 2;
                   numRecs = totRecs / divide;
               }
            }
            read_cdf_variable_data (varId,
                                    recNo,          /* starting record #    */
                                    numRecs,        /* # of records to read */
                                    var.dimensionality,
                                    var.dimSizes,   /* dimesion sizes */
                                    usPtr);         /* data pointer   */
            if (var.recVariance == VARY)
                count[0] = numRecs;
            for (j = 0; j < numValues*numRecs; ++j) {
              *(iPtr+j) = (int) *(usPtr+j);
            }
            ncstatus = nc_put_vara_int(ncid, ncVarId, start, count, iPtr);
            if (ncstatus != NC_NOERR) handle_netcdf_error(ncstatus);
            free (usPtr);
            free (iPtr);
            break;

        case CDF_INT4:
            iPtr = NULL;
            while (iPtr == NULL) {
               iPtr = (int *) malloc (sizeof(int) * numValues * numRecs);
               if (iPtr == NULL) {
                   if (DEBUG) insufficient_memory();
                   divide *= 2;
                   numRecs = totRecs / divide;
               }
            }
            read_cdf_variable_data (varId,
                                    recNo,          /* starting record #    */
                                    numRecs,        /* # of records to read */
                                    var.dimensionality,
                                    var.dimSizes,   /* dimesion sizes */
                                    iPtr);          /* data pointer   */
            if (var.recVariance == VARY)
                count[0] = numRecs;
            ncstatus = nc_put_vara_int(ncid, ncVarId, start, count, iPtr);
            if (ncstatus != NC_NOERR) handle_netcdf_error(ncstatus);
            free (iPtr);
            break;

        case CDF_UINT4:
            uiPtr = NULL;
            dPtr = NULL;
            while (uiPtr == NULL || dPtr == NULL) {
               uiPtr = (unsigned int *) malloc (sizeof(unsigned int) * numValues * numRecs);
               dPtr = (double *) malloc (sizeof(double) * numValues * numRecs);
               if (uiPtr == NULL) {
                   if (DEBUG) insufficient_memory();
                   divide *= 2;
                   numRecs = totRecs / divide;
               }
            }
            read_cdf_variable_data (varId,
                                    recNo,          /* starting record #    */
                                    numRecs,        /* # of records to read */
                                    var.dimensionality,
                                    var.dimSizes,   /* dimesion sizes */
                                    uiPtr);          /* data pointer   */
            if (var.recVariance == VARY)
                count[0] = numRecs;
            for (j = 0; j < numValues*numRecs; ++j) {
              *(dPtr+j) = (double) *(uiPtr+j);
            }
            ncstatus = nc_put_vara_double(ncid, ncVarId, start, count, dPtr);
            if (ncstatus != NC_NOERR) handle_netcdf_error(ncstatus);
            free (uiPtr);
            free (dPtr);
            break;

        case CDF_INT8:
            jPtr = NULL;
            dPtr = NULL;
            while (jPtr == NULL || dPtr == NULL) {
               jPtr = (long long *) malloc (sizeof(long long) * numValues * numRecs);
               dPtr = (double *) malloc (sizeof(double) * numValues * numRecs);
               if (jPtr == NULL || dPtr == NULL) {
                   if (DEBUG) insufficient_memory();
                   divide *= 2;
                   numRecs = totRecs / divide;
               }
            }
            read_cdf_variable_data (varId,
                                    recNo,          /* starting record #    */
                                    numRecs,        /* # of records to read */
                                    var.dimensionality,
                                    var.dimSizes,   /* dimesion sizes */
                                    jPtr);          /* data pointer   */
            if (var.recVariance == VARY)
                count[0] = numRecs;
            for (j = 0; j < numValues*numRecs; ++j) {
              *(dPtr+j) = (double) *(jPtr+j);
            }
            ncstatus = nc_put_vara_double(ncid, ncVarId, start, count, dPtr);
            if (ncstatus != NC_NOERR) handle_netcdf_error(ncstatus);
            free (jPtr);
            free (dPtr);
            break;

        case CDF_FLOAT:
        case CDF_REAL4:
            fPtr = NULL;
            while (fPtr == NULL) {
               fPtr = (float *) malloc (sizeof(float) * numValues * numRecs);
               if (fPtr == NULL) {
                   if (DEBUG) insufficient_memory();
                   divide *= 2;
                   numRecs = totRecs / divide;
               }
            }
            read_cdf_variable_data (varId,
                                    recNo,          /* starting record #    */
                                    numRecs,        /* # of records to read */
                                    var.dimensionality,
                                    var.dimSizes,   /* dimesion sizes */
                                    fPtr);          /* data pointer   */
            if (var.recVariance == VARY)
                count[0] = numRecs;
            ncstatus = nc_put_vara_float(ncid, ncVarId, start, count, fPtr);
            if (ncstatus != NC_NOERR) handle_netcdf_error(ncstatus);
            free (fPtr);
            break;

        case CDF_DOUBLE:
        case CDF_REAL8:
            dPtr = NULL;
            while (dPtr == NULL) {
               dPtr = (double *) malloc (sizeof(double) * numValues * numRecs);
               if (dPtr == NULL) {
                   if (DEBUG) insufficient_memory();
                   divide *= 2;
                   numRecs = totRecs / divide;
               }
            }
            read_cdf_variable_data (varId,
                                    recNo,           /* starting record # */
                                    numRecs,         /* # of records to read */
                                    var.dimensionality,
                                    var.dimSizes,    /* dimesion sizes */
                                    dPtr);           /* data pointer   */
            if (var.recVariance == VARY)
                count[0] = numRecs;

            ncstatus = nc_put_vara_double(ncid, ncVarId, start, count, dPtr);
            if (ncstatus != NC_NOERR) handle_netcdf_error(ncstatus);
            free (dPtr);
            break;

        case CDF_EPOCH:
            dPtr = NULL;
            while (dPtr == NULL) {
               dPtr = (double *) malloc (sizeof(double)*numValues*numRecs);
               if (dPtr == NULL) {
                   if (DEBUG) insufficient_memory();
                   divide *= 2;
                   numRecs = totRecs / divide;
               }
            }
            read_cdf_variable_data (varId,
                                    recNo,           /* starting record # */
                                    numRecs,         /* # of records to read */
                                    var.dimensionality,
                                    var.dimSizes,    /* dimesion sizes */
                                    dPtr);           /* data pointer   */
            if (var.recVariance == VARY)
                count[0] = numRecs;

            cPtr = (char *) malloc (sizeof(char) * numValues * numRecs *
                                    DD_TIME_STRING_LEN + 1);
            if (cPtr == NULL)
                memory_error("get_cdf_variable_data", "CDF_EPOCH");

            ptr = cPtr;

            /* Get all the EPOCH values into a single string. */
            for (i=0; i < numRecs * numValues; i++) {
                 encodeEPOCH4 (*(dPtr+i), epString); 
                 strcpy (ptr, isotime_to_dd_time (epString));
                 ptr += DD_TIME_STRING_LEN;
            }
            ncstatus = nc_put_vara_text(ncid, ncVarId, start, count, cPtr);
            if (ncstatus != NC_NOERR) handle_netcdf_error(ncstatus);
            free (cPtr);
            free (dPtr);
            break;

        case CDF_EPOCH16:
            dPtr = NULL;
            while (dPtr == NULL) {
               dPtr = (double *) malloc (sizeof(double) * numValues*numRecs*2);
               if (dPtr == NULL) {
                   if (DEBUG) insufficient_memory();
                   divide *= 2;
                   numRecs = totRecs / divide;
               }
            }
            read_cdf_variable_data (varId,
                                    recNo,           /* starting record # */
                                    numRecs,         /* # of records to read */
                                    var.dimensionality,
                                    var.dimSizes,    /* dimesion sizes */
                                    dPtr);           /* data pointer   */
            if (var.recVariance == VARY)
                count[0] = numRecs;

            cPtr = (char *) malloc (sizeof(char) * numValues * numRecs *
                                    DD_TIME_STRING_LEN + 1);
            if (cPtr == NULL)
                memory_error("get_cdf_variable_data", "CDF_EPOCH16");

            ptr = cPtr;

            /* Get all the EPOCH16 values into a single string. */
            for (i=0; i < numRecs * numValues; i++) {
                 epoch16[0] = *(dPtr+i*2);
                 epoch16[1] = *(dPtr+i*2+1);
                 encodeEPOCH16_4 (epoch16, ep16String);
                 strcpy (ptr, isotime_to_dd_time (ep16String));
                 ptr += DD_TIME_STRING_LEN;
            }
            printf(" %d %d\n", count[0], count[1]);
            ncstatus = nc_put_vara_text(ncid, ncVarId, start, count, cPtr);
            if (ncstatus != NC_NOERR) handle_netcdf_error(ncstatus);
            free (cPtr);
            free (dPtr);
            break;

        case CDF_TIME_TT2000:
            jPtr = NULL;
            while (jPtr == NULL) {
               jPtr = (long long *) malloc (sizeof(long long)*numValues*numRecs);
               if (jPtr == NULL) {
                   if (DEBUG) insufficient_memory();
                   divide *= 2;
                   numRecs = totRecs / divide;
               }
            }
            read_cdf_variable_data (varId,
                                    recNo,           /* starting record # */
                                    numRecs,         /* # of records to read */
                                    var.dimensionality,
                                    var.dimSizes,    /* dimesion sizes */
                                    jPtr);           /* data pointer   */
            if (var.recVariance == VARY)
                count[0] = numRecs;

            cPtr = (char *) malloc (sizeof(char) * numValues * numRecs *
                                    DD_TIME_STRING_LEN + 1);
            if (cPtr == NULL)
                memory_error("get_cdf_variable_data", "CDF_EPOCH");

            ptr = cPtr;

            /* Get all the TT2000 values into a single string. */
            for (i=0; i < numRecs * numValues; i++) {
              //   encodeTT2000 (*(jPtr+i), tt2000String);
                 CDF_TT2000_to_UTC_string(*(jPtr+i), tt2000String, 3);
                 strcpy (ptr, isotime_to_dd_time (tt2000String));
                 ptr += DD_TIME_STRING_LEN;
            }
            ncstatus = nc_put_vara_text(ncid, ncVarId, start, count, cPtr);
            if (ncstatus != NC_NOERR) handle_netcdf_error(ncstatus);
            free (cPtr);
            free (jPtr);
            break;

        default:
            printf ("** Error in getVarData: bad type");
        }    

        numRecsLeftToRead = totRecs - numRecs;
        if (numRecsLeftToRead > 0) {
            totRecs  = numRecsLeftToRead;
            start[0] = numRecs;
        }
    }       /* end of the 'while' loop */

}


/*--------------------------------------------------------------------------
 *  This routine returns the string representation of the given CDF 
 *  datatype.
 *--------------------------------------------------------------------------*/
char *  cdf_str_datatype (long type)
{
    switch (type) {
      case CDF_BYTE:
         return "CDF_BYTE";

      case CDF_INT1:
         return "CDF_INT1";

      case CDF_CHAR:
         return "CDF_CHAR";

      case CDF_INT2:
         return "CDF_INT2";

      case CDF_UCHAR:
         return "CDF_UCHAR";

      case CDF_UINT1:
         return "CDF_UINT1";

      case CDF_INT4:
         return "CDF_INT4";

      case CDF_INT8:
         return "CDF_INT8";

      case CDF_UINT2:
         return "CDF_UINT2";

      case CDF_FLOAT:
         return "CDF_FLOAT";

      case CDF_REAL4:
         return "CDF_REAL4";

      case CDF_DOUBLE:
         return "CDF_DOUBLE";

      case CDF_REAL8:
         return "CDF_REAL8";

      case CDF_UINT4:
         return "CDF_UINT4";

      case CDF_EPOCH:
         return "CDF_EPOCH";

      case CDF_EPOCH16:
         return "CDF_EPOCH16";

      case CDF_TIME_TT2000:
         return "CDF_TIME_TT2000";
      
      default:
         return "BAD_CDF_TYPE";

    }
}


/*--------------------------------------------------------------------------
 *  This routine returns the string representation of the given netCDF 
 *  datatype.
 *--------------------------------------------------------------------------*/
char *  NC_datatype (nc_type type)
{
    switch (type) {
      case NC_BYTE:
        return "byte";
      case NC_CHAR:
        return "char";
      case NC_SHORT:
        return "short";   
      case NC_INT:
        return "int";
      case NC_FLOAT:
        return "float";
      case NC_DOUBLE:      
        return "double";
      default:
        return "UNKNOWN";
    }
}


/*--------------------------------------------------------------------------
 *  This routine returns the netCDF datatype for the given CDF datatype.
 *  All unsigned types are bumped up, either to double the size, or to
 *  a double. CDF's epoch types are converted to string.
 *--------------------------------------------------------------------------*/
nc_type get_netcdf_datatype (long type)
{
    nc_type  netcdf_type;

    switch (type) {
      case CDF_BYTE:
      case CDF_INT1:
        netcdf_type = NC_BYTE;
        break;

      case CDF_CHAR:
      case CDF_UCHAR:
      case CDF_EPOCH:
      case CDF_EPOCH16:
      case CDF_TIME_TT2000:
        netcdf_type = NC_CHAR;
        break;

      case CDF_UINT1:
        netcdf_type = NC_SHORT;
        break;

      case CDF_INT2:
        netcdf_type = NC_SHORT;
        break;

      case CDF_UINT2:
        netcdf_type = NC_INT;
        break;

      case CDF_INT4:
        netcdf_type = NC_INT;
        break;

      case CDF_UINT4:
        netcdf_type = NC_DOUBLE;
        break;

      case CDF_INT8:
        netcdf_type = NC_DOUBLE;
        break;

      case CDF_FLOAT:
      case CDF_REAL4:
        netcdf_type = NC_FLOAT;
        break;

      case CDF_DOUBLE:
      case CDF_REAL8:
        netcdf_type = NC_DOUBLE;
        break;

      default: { 
        printf ("** ERROR: Unknown CDF datatype\n");
        exit (1);
      }
    }
    return netcdf_type;
}


/*---------------------------------------------------------------------------
 *  Read the CDF variable data for the given CDF variable number.
 *---------------------------------------------------------------------------*/
void read_cdf_variable_data (long varNum,      /* CDF variable number        */
                             long recNo,       /* record # for start reading */
                             long recCount,    /* # of records to read       */
                             long dim,         /* CDF dimensionality         */
                             long dimSizes[],  /* CDF dimesion sizes         */
                             void *data)       /* data pointer               */
{
   CDFstatus status;
   int       i;
   long      recInterval,                /* No. of recors to skip            */
             dimIndices[CDF_MAX_DIMS],   /* Beginning location of the array  */
                                         /* to be dumped                     */
             dimIntervals[CDF_MAX_DIMS]; /* No. of elements to skip          */

   recInterval = 1L;             /* # of records to skip between writes   */

   for (i=0; i < dim; i++) {
        dimIndices[i]   = 0;     /* Dump data from the beginning of array */
        dimIntervals[i] = 1;     /* Dump all the elements in the array    */
   }

   status = CDFlib (SELECT_,zVAR_, varNum,
                            zVAR_RECNUMBER_, recNo,
                            zVAR_RECCOUNT_, recCount,
                            zVAR_RECINTERVAL_, recInterval,
                            zVAR_DIMINDICES_, dimIndices,
                            zVAR_DIMCOUNTS_, dimSizes,
                            zVAR_DIMINTERVALS_, dimIntervals,
                    GET_, zVAR_HYPERDATA_, data,
                    NULL_);
   if (status != CDF_OK) cdf_status_handler (status, "GET_, zVAR_HYPERDATA_");
}



/*--------------------------------------------------------------------------
 *  Remove the filepath from a file name.
 *
 *  Example:
 *      /home/data/mydata.txt => mydata.txt
 *--------------------------------------------------------------------------*/
void removeFilepath (char *fileName) {
    char *ptr;

    ptr = (char *) strrchr(fileName, '/');        /* Unix file path separator */
    if (ptr != NULL) {
        ptr++;
        strcpy(fileName, ptr);
    }
    else {
       ptr = (char *) strrchr(fileName, '\\');     /* Windows file separator */
       if (ptr != NULL) {
           ptr++;
           strcpy(fileName, ptr);
       }
    }
}


/*--------------------------------------------------------------------------
 *  Handles a CDF error.
 *--------------------------------------------------------------------------*/
void cdf_status_handler (CDFstatus status, char *source)
{
   char  message[CDF_STATUSTEXT_LEN+1];

   CDFerror (status, message);              /* Get the appropriate message */

   if (status < CDF_WARN) {
       printf ("An error has occurred, halting...\n"); 
       printf ("%s\n", message);
       printf ("** Error source: %s\n", source);
       exit (status);
   }
   else if (status < CDF_OK) {
       printf ("Warning, function may not have compeleted as expected...\n");
       printf ("%s\n", message);
   }
   else if (status > CDF_OK) {
       printf ("Function compeleted successfully, but be advised that...\n");
       printf ("%s\n", message);
   }
}


/*--------------------------------------------------------------------------
 *  Handles a netCDF error.
 *--------------------------------------------------------------------------*/
void handle_netcdf_error(int status) {
  fprintf(stderr, "%s\n", nc_strerror(status));
}


/*--------------------------------------------------------------------------
 *  Handles a memory allocation error.
 *--------------------------------------------------------------------------*/
void memory_error(char *source, char *datatype) {
   printf ("** Memory allocation error occurred in %s. data type = %s", 
           source, datatype);
   exit (1);
}

void insufficient_memory () {
   printf("Insufficient memory to load all the records at once");
}


/*--------------------------------------------------------------------------*/
void usage()
{
   printf ("\nDescription:\n");
   printf ("    This program converts a CDF file into a netCDF file.");
   printf ("\n");
   printf ("Usage: cdf-to-netCDF -[Options] <CDF file name>\n");
   printf ("\n");
   printf ("Options:\n");
   printf ("    -map <mapfile>    Use the user-supplied mapping file.\n");
   printf ("    -debug            Show debug information.\n");
   printf ("\n");
   printf ("Examples of usage: \n");
   printf ("1) cdf-to-netCDF test.cdf\n");
   printf ("   will convert the test.cdf file into the test.nc file.\n\n");
   printf ("2) cdf-to-netCDF -map mymap.dat test.cdf\n");
   printf ("   will convert the test.cdf file into the test.nc file using\n");
   printf ("   the user-supplied mapping file called 'mymap.dat' instead\n");
   printf ("   of using the default cdf-to-netCDF mapping file.\n");
   printf ("\n");
   exit(1);
}

void parseCmdLine (int argc, char *argv[])
{
   int  i;
   char *nextArg;

   for (i=0; i < argc; i++) {
        if (strcmp(argv[i],"-map") == 0) {
            nextArg = getNextArgument(argc, argv, i);
            if (strcmp(nextArg, "No next argument") == 0 ||
                *(nextArg+0) == '-')
                errorMsg("** Error - mapping file is not defined");
            else
                 strcpy(mappingFile, nextArg);
        }
        else if (strcmp(argv[i],"-debug") == 0)
            DEBUG = TRUE;
   }
}


char * getNextArgument (int argc, char *argv[], int currentIndex)
{
    char *nextArg, msg[30];
    int  nextIndex;

    nextIndex  = currentIndex+1;
    if (nextIndex == argc) {
        strcpy (msg, "No next argument");
        nextArg = msg;
    }
    else
        nextArg = argv[nextIndex];

    return nextArg;
}

void errorMsg (char *msg)
{
   printf ("%s\n", msg);
   exit (1);
}


void map_CDF_attr_to_netCDF (char *searchAttr,     /* in - CDF attr name     */
                             char *mappedAttrName) /* out - netCDF attr name */
{
   int i;

   strcpy(mappedAttrName, searchAttr);

   for (i=0; i < totAttrsInMappingFile; i++) {
        if (strcmp(cdfAttrNames[i], searchAttr) == 0) {
            strcpy(mappedAttrName, netcdfAttrNames[i]);
            break;
        }
   }
}

void breakdown_DIMENSIONS_entry (char *entry,     /* in -  a DIMENSIONS entry */
                                 char **dimStrings) /* out - dimesion names */
{
   char *ptr, *ptr1;
   int i = 0, j;

   ptr = entry;
   ptr1 = strstr(ptr, ",");
   i = 0;
   while (ptr1 != NULL) {
     j = ptr1 - ptr;
     strncpy (dimStrings[i], ptr, (size_t)j);
     dimStrings[i][j] = (char) '\0';
     ptr = ptr1 + 1;
     ptr1 = strstr(ptr, ",");
     ++i;
   }
   if (ptr != NULL) strcpy (dimStrings[i], ptr);
}