
/*---------------------------------------------------------------------------
                 Nexus Dictionary API implementation file.

  For documentation see the nxdict.tex file which comes with this 
  distribution.

  copyleft: Mark Koennecke
            Labor fuer Neutronenstreuung
            Paul Scherrer Institut
            CH-5232 Villigen-PSI
            Switzerland
            Mark.Koennecke@psi.ch

  No warranties of any kind, whether explicit or implied, taken.
  Distributed under the GNU copyleft license as documented elsewhere.

  August, 1997

  Version: 1.0

  Version 1.1

  Updated to use the combined HDF4 HDF5 API. New keyword -chunk which
  defines the chunk buffer size for a SDS.

  Mark Koennecke, August 2001

-----------------------------------------------------------------------------*/


#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#include "lld.h"
#include "napi.h"
#include "stringdict.h"
#include "dynstring.h"
#include "nxdict.h"
/*------------------ The magic number used for pointer checking */
#define NXDMAGIC 260558
/*--------------------------------------------------------------------------
      Things defined in napi.c for error reporting 
---------------------------------------------------------------------------*/
       extern void *NXpData;
       extern void (*NXIReportError)(void *pData, char *pBuffer);  
/*--------------------------------------------------------------------------*/
/* #define DEFDEBUG 1 */
/* define DEFDEBUG when you wish to print your definition strings before
   action. This can help a lot to resolve mysteries when working with
   dictionaries.
*/
/*-------------------------------------------------------------------------*/


    typedef struct __NXdict
                           {
                               int iID;
                               pStringDict pDictionary;
                           } sNXdict;
/*------------------ verbosity level -------------------------------------*/
   static int iVerbosity = 0 ;


/*-------------------------------------------------------------------------*/
  static char *NXDIReadFile(FILE *fd)
  {
     char *pNew = NULL;
     long lLength = 0;
 
     assert(fd); 

     /* determine length of file */
     fseek(fd,0L,SEEK_END);
     lLength = ftell(fd);
     if(lLength <= 0)
     {
        return NULL;
     }
     fseek(fd,0L,SEEK_SET);

     /* allocate buffer */
     lLength += 3;
     pNew = (char *)malloc(lLength*sizeof(char));
     if(!pNew)
     {
       return NULL;
     }
     memset(pNew,0,lLength); /* this ensures a 0 at the end */

     /* read file */
     fread(pNew,sizeof(char),lLength-3,fd);
     
     /* check for existence of the NXDICT string in the file */
     if(strncmp(pNew,"##NXDICT-1.0",12) != 0 )
     {
        NXIReportError(NXpData,"ERROR: This is NO NXdict file");
        free(pNew);
        return NULL;
     }
     return pNew;
  }
/*--------------------------------------------------------------------------*/


#define FWORD 1
#define FHASH 2
#define FEOL  3
#define FEOB  4
#define FEQUAL 5
#define FSLASH 6

   static char *NXDIfNextToken(char *pPtr, char *pToken, int *iToken)
   {
       pToken[0] = '\0';
       /* skip whitespace */
       while( (*pPtr == ' ') || (*pPtr == '\t') )
       {
         pPtr++;
       } 

       /* check for special characters */
       if(*pPtr == '#')
       {
         *iToken = FHASH;
         pToken[0] = *pPtr;
         pPtr++;
         return pPtr;
       }
       else if(*pPtr == '\n')
       {
         *iToken = FEOL;
         pToken[0] = *pPtr;
         pPtr++;
         return pPtr;
       }
       else if(*pPtr == '\0')
       {
         *iToken = FEOB;
         pToken[0] = *pPtr;
         pPtr++;
         return pPtr;
       }
       else if(*pPtr == '=')
       {
         *iToken = FEQUAL;
         pToken[0] = *pPtr;
         pPtr++;
         return pPtr;
       }
       else if(*pPtr == '\\')
       {
         *iToken = FSLASH;
         pToken[0] = *pPtr;
         pPtr++;
         return pPtr;
       }
       else 
       {
         *iToken = FWORD;
         /* copy word to pToken */
         while( (*pPtr != ' ') && (*pPtr != '\t') && 
                (*pPtr != '\n') && (*pPtr != '\0') && (*pPtr != '=') )
         {
           *pToken = *pPtr;
           pPtr++;
           pToken++;
         } 
         *pToken = '\0';
         return pPtr;
       }
      /* not reached */
      return pPtr;
   }
    


/*------------------------------------------------------------------------*/


#define AMODE 0
#define DMODE 1

  static void  NXDIParse(char *pBuffer, pStringDict pDict)
  {
     char *pPtr;
     int iToken;
     int iMode;
     char pAlias[132];
     char pDefinition[1024]; /* this is > 10 lines of definition */
     char pWord[132];

     assert(pBuffer);
     assert(pDict);

     iMode = AMODE;
     pPtr = pBuffer;
     iToken = -1;
     pDefinition[0] = '\0';
     pAlias[0] = '\0';
     pWord[0] = '\0';

     while(iToken != FEOB)
     {
         pPtr = NXDIfNextToken(pPtr,pWord,&iToken);
         switch(iToken)
         {
           case FHASH:
           case FSLASH: /* skip over \n to next non blank */
                       while(*pPtr != '\n')
                       {
                          pPtr++;
                          /* check for end of file */
                          if(*pPtr == '\0')
                          {
                            return;
                          }
                       }
                       pPtr++;
                       break;
           case FEQUAL: /* do a mode change */
                       iMode = DMODE;
                       pDefinition[0] = '\0';
                       break;

           case FWORD:
                       if(iMode == AMODE)
                       {
                         strcpy(pAlias,pWord);
                       }  
                       else
                       {
                         strcat(pDefinition,pWord);
                         strcat(pDefinition," ");
                       }
                       break;
           case FEOL:
                       if(iMode == DMODE)
                       {
                          /* enter in dictionary */
                          StringDictAddPair(pDict,pAlias,pDefinition);
                          iMode = AMODE;
                          pAlias[0] = '\0';
                       }
                       break;
           case FEOB:
                       if(iMode == AMODE) 
                       {
                         /* empty line or a problem */
                       }          
                       else
                       {
                          /* enter in dictionary */
                          StringDictAddPair(pDict,pAlias,pDefinition);
                          iMode = AMODE;
                          pAlias[0] = '\0';
                       }
                       return;
           default:
                   assert(0); /* unrecognized token is a programming
                                 error
                              */
                   break;
         }
     }
  }


/*--------------------------------------------------------------------------*/
  NXstatus NXDinitfromfile(char *filename, NXdict *pData)
  {
     NXdict pNew = NULL;
     FILE *fd = NULL;
     char *pBuffer = NULL;
     char pError[512];



     /* allocate a new NXdict structure */
     if(iVerbosity == NXalot)
     {
         NXIReportError(NXpData, "Allocating new NXdict structure ");
     }
     pNew = (NXdict)malloc(sizeof(sNXdict));
     if(!pNew)
     {
         NXIReportError(NXpData, "Insufficient memory for creation of NXdict");
         return NX_ERROR;
     }
     
     /* initialise it */
     pNew->iID = NXDMAGIC;
     pNew->pDictionary = CreateStringDict();
     if(!pNew->pDictionary)
     {
         NXIReportError(NXpData, "Insufficient memory for creation of NXdict");
         free(pNew);
         return NX_ERROR;
     }



     /* is there a file name argument */
     if(filename == NULL)
     {
        if(iVerbosity == NXalot)
        {
           NXIReportError(NXpData, "NXDinitfrom file finished without data");
        }
        *pData = pNew;
        return NX_OK;
     }


      fd = fopen(filename,"rb");
      if(!fd)
      {
         sprintf(pError,"ERROR: file %s NOT found ",filename);
         NXIReportError(NXpData, pError);
         NXIReportError(NXpData, "NXDinitfrom file finished without data");
         *pData = pNew;
         return NX_ERROR;
      }


       /* read the file contents */
       if(iVerbosity == NXalot)
       {
          NXIReportError(NXpData, "NXDinitfrom: reading file");
       }
       pBuffer = NXDIReadFile(fd);
       fclose(fd); /* we are done with it then */
       if(!pBuffer)
       {
         sprintf(pError,"ERROR: reading file %s or no memory",filename);
         NXIReportError(NXpData, pError);
         NXIReportError(NXpData, "NXDinitfrom file finished without data");
         *pData = pNew;
         return NX_ERROR;
       }

       /* parse it */
       if(iVerbosity == NXalot)
       {
          NXIReportError(NXpData, "NXDinitfrom: parsing dictionary definitions");
       }
       NXDIParse(pBuffer, pNew->pDictionary);


       if(iVerbosity == NXalot)
       {
          NXIReportError(NXpData, "NXDinitfrom: performed successfully");
       }
       free(pBuffer);
       *pData = pNew;
       return NX_OK;
  }   
/*--------------------------------------------------------------------------*/

   NXdict NXDIAssert(NXdict handle)
   {
      NXdict self = NULL;
      assert(handle);
      self = (NXdict)handle;
      assert(self->iID == NXDMAGIC);
      return self;
   }


/*-------------------------------------------------------------------------*/

   NXstatus NXDclose(NXdict handle, char *filename)
   {
      NXdict self;
      const char *pKey = NULL;
      char pValue[1024];
      FILE *fd = NULL;

      self = NXDIAssert(handle);

      if(filename) /* we must write a file */
      {
        if(iVerbosity == NXalot)
        {
           sprintf(pValue,"Writing file %s",filename);
           NXIReportError(NXpData, pValue);
        }
        fd = fopen(filename,"w");
        if(!fd)
        {
           sprintf(pValue,"ERROR: opening file %s for write",filename);
           NXIReportError(NXpData, pValue);
           return NX_ERROR;
        }   
        
        /* write our magic recognition header */
        fprintf(fd,"##NXDICT-1.0\n");

        /* write all our keys */
        pKey = StringDictGetNext(self->pDictionary, pValue,1023);
        while(pKey != NULL)
        {
          fprintf(fd,"%s = %s\n",pKey,pValue);
          pKey = StringDictGetNext(self->pDictionary,pValue,1023);
        }
        fclose(fd);
        if(iVerbosity == NXalot)
        {
           sprintf(pValue,"File %s written",filename);
           NXIReportError(NXpData, pValue);
        }
      }

      /* now we send the cleaners in */
      DeleteStringDict(self->pDictionary);
      free(self);
      return NX_OK;
   }

/*------------------------------------------------------------------------*/

   NXstatus NXDadd(NXdict handle, char *alias, char *pDef)
   {
     NXdict self;
     int iRet;

     self = NXDIAssert(handle);
     iRet = StringDictAddPair(self->pDictionary,alias,pDef);
     if(!iRet)
     {
       return NX_ERROR;
     }
     return NX_OK;
   }
/*---------------------------------------------------------------------------*/
   NXstatus NXDget(NXdict handle, char *pKey, char *pBuffer, int iBufLen)
   {
     NXdict self;
     int iRet;

     self = NXDIAssert(handle);
     iRet = StringDictGet(self->pDictionary,pKey,pBuffer,iBufLen);
     if(!iRet)
     {
       return NX_ERROR;
     }
#ifdef DEFDEBUG
     printf("Resolved: %s to %s\n",pKey,pBuffer);
#endif
     return NX_OK;
   }
/*-------------------------------------------------------------------------*/
   NXstatus NXDupdate(NXdict handle, char *pKey, char *pNewVal)
   {
     NXdict self;
     int iRet;

     self = NXDIAssert(handle);
     iRet = StringDictUpdate(self->pDictionary,pKey,pNewVal);
     if(!iRet)
     {
       return NX_ERROR;
     }
     return NX_OK;
   }
/*-----------------------------------------------------------------------*/
#define NORMAL 1
#define ALIAS  2
   pDynString NXDItextreplace(NXdict handle, char *pDefString)
   {
     NXdict self;
     int iRet, iPos, i;
     pDynString pReplaced = NULL;
     char pBueffel[1024];
     char pBuffer2[1024];
     char *pPtr;
     int iState;

     self = NXDIAssert(handle);
     
     /* create a dynamic string */
     pReplaced = CreateDynString(strlen(pDefString),512);
     if(!pReplaced)
     {
       NXIReportError(NXpData,"ERROR: out of memory in NXDtextreplace");
       return NULL;
     }

     /* the loop */
     iState = NORMAL;
     for(i = 0, pPtr = pDefString; i < strlen(pDefString); i++,pPtr++)
     {
        if(iState == NORMAL)
        {
           if(*pPtr == '$')
           {
             iState = ALIAS;
             memset(pBueffel,0,1024);
             iPos = 0;
           }
           else
           {
               DynStringConcatChar(pReplaced,*pPtr);
           }
        }
        else if(iState == ALIAS)
        {
           switch(*pPtr)
           {
             case '(': /* ignore */
                       break;
             case ')': 
                       /* do the replacement */
                       memset(pBuffer2,0,1023);
                       iRet = NXDget(handle, pBueffel,pBuffer2,1023);
                       if(iRet != NX_OK)
                       {
                         DeleteDynString(pReplaced);
                         return NULL;
                       }
                       DynStringConcat(pReplaced,pBuffer2);
                       iState = NORMAL;
                       break;
             default:
                       pBueffel[iPos] = *pPtr;
                       iPos++;
                       if(iPos >= 1024)
                       {
                          NXIReportError(NXpData,
                                     "ERROR: buffer overrun in NXDItextreplace");
                          DeleteDynString(pReplaced);
                          return NULL;
                       }
                       break;
           }
        }
     }
#ifdef DEFDEBUG
    printf("Replacement result: %s\n",GetCharArray(pReplaced));
#endif
     return pReplaced;
   }
/*--------------------------------------------------------------------------*/
   NXstatus NXDtextreplace(NXdict handle, char *pDefString, 
                           char *pBuffer, int iBufLen)
   {
     pDynString pResult = NULL;
     char *pPtr = NULL;

     pResult = NXDItextreplace(handle,pDefString);
     if(!pResult)
     {
         return NX_ERROR;
     }

     /* copy results home */
     pPtr = GetCharArray(pResult);
     strncpy(pBuffer,pPtr,iBufLen);   
     DeleteDynString(pResult);
     return NX_OK;
   }

/*------------------- The Defintion String Parser -----------------------*/
/*------- Data structures */
  typedef struct {
                  char pText[20];
                  int iCode;
                 }  TokDat;
#define TERMSDS 100
#define TERMVG  200
#define TERMLINK 300

   typedef struct {
                    char *pPtr;
                    char pToken[256];
                    int iToken;
                    int iDepth;
                    int iMayCreate;
                    int iTerminal;
                  } ParDat;

   static void DummyError(void *pData, char *pError)
   {
     return;
   }
  typedef struct {
                   char name[256];
                   char value[256];
                  }AttItem;

/*---------------- Token name defines ---------------------------*/
#define DSLASH 0
#define DKOMMA 1
#define DSDS   2
#define DLINK  3
#define DGROUP 4
#define DRANK  5
#define DDIM   6
#define DTYPE  7
#define DWORD  9
#define DOPEN  10
#define DCLOSE 11
#define DATTR  12
#define DEND   13
#define DLZW   14
#define DHUF   15
#define DRLE   16
#define CHUNK  17

/*----------------- Keywords ----------------------------------------*/

  static TokDat TokenList[12] = { 
                                {"SDS",DSDS},
                                {"NXLINK",DLINK},
                                {"NXVGROUP",DGROUP},
                                {"-dim",DDIM},
                                {"-type",DTYPE},
                                {"-rank",DRANK},
                                {"-attr",DATTR},
                                {"-chunk",CHUNK},
                                {"-LZW",DLZW},
                                {"-HUF",DHUF},
                                {"-RLE",DRLE},
                                {NULL,0} };

/*-----------------------------------------------------------------------*/
   static void NXDIDefToken(ParDat *sStat)
   {
       int i;
       

       sStat->pToken[0] = '\0';

       /* skip whitespace */
       while( (*(sStat->pPtr) == ' ') || (*(sStat->pPtr) == '\t') )
       {
         sStat->pPtr++;
       } 

       /* check for special characters */
       if(*(sStat->pPtr) == '/')
       {
         sStat->iToken = DSLASH;
         sStat->pToken[0] = *(sStat->pPtr);
         sStat->pPtr++;
         return;
       }
       else if(*(sStat->pPtr) == ',')
       {
         sStat->iToken = DKOMMA;
         sStat->pToken[0] = *(sStat->pPtr);
         sStat->pPtr++;
         return;
       }
       else if(*(sStat->pPtr) == '\0')
       {
         sStat->iToken = DEND;
         sStat->pToken[0] = *(sStat->pPtr);
         sStat->pPtr++;
         return;
       }
       else if(*(sStat->pPtr) == '{')
       {
         sStat->iToken = DOPEN;
         sStat->pToken[0] = *(sStat->pPtr);
         sStat->pPtr++;
         return;
       }
       else if(*(sStat->pPtr) == '}')
       {
         sStat->iToken = DCLOSE;
         sStat->pToken[0] = *(sStat->pPtr);
         sStat->pPtr++;
         return;
       }
       else 
       {
         sStat->iToken = DWORD;
         /* copy word to pToken */
         i = 0;
         while( (*(sStat->pPtr) != ' ') && (*(sStat->pPtr) != '\t') && 
                (*(sStat->pPtr) != '/') && (*(sStat->pPtr) != '\0') &&
                (*(sStat->pPtr) != ',') && (*(sStat->pPtr) != '}') )
         {
           sStat->pToken[i] = *(sStat->pPtr);
           sStat->pPtr++;
           i++;
         } 
         sStat->pToken[i] = '\0';

         /*--------- try to find word in Tokenlist */
         for(i = 0; i < 10; i++)
         {
           if(strcmp(sStat->pToken,TokenList[i].pText) == 0)
           {
             sStat->iToken = TokenList[i].iCode;
             break;
           }
         }
         return;
       }
      /* not reached */
      return;
   }


/*------------------------------------------------------------------------*/

   int NXDIParsePath(NXhandle hfil, ParDat *pParse)
   {
       int iRet, iToken;
       void (*ErrFunc)(void *pData, char *pErr); 
       char pName[132], pClass[132];
       char pError[256];

       /* get the name */
       NXDIDefToken(pParse); /* next token */
       if( (pParse->iToken == DSDS) || (pParse->iToken == DGROUP)
           || (pParse->iToken == DLINK) )
       {
         /* put back & OK */
         pParse->pPtr -= strlen(pParse->pToken);
         return NX_OK;
       }
       if(pParse->iToken != DWORD)
       {
          sprintf(pError,"ERROR: parse error at %s, expected vGroup name",
                  pParse->pToken);
          NXIReportError(NXpData, pError);
          return NX_ERROR;
       }       
       strcpy(pName,pParse->pToken);
     
       /* now we expect a komma */
       NXDIDefToken(pParse); /* next token */
       if(pParse->iToken != DKOMMA)
       {
          sprintf(pError,"ERROR: parse error at %s, expected komma",
                  pParse->pToken);
          NXIReportError(NXpData, pError);
          return NX_ERROR;
       }       

       /* next must be the class */
       NXDIDefToken(pParse); /* next token */
       if(pParse->iToken != DWORD)
       {
          sprintf(pError,"ERROR: parse error at %s, expected vGroup class",
                  pParse->pToken);
          NXIReportError(NXpData, pError);
          return NX_ERROR;
       }       
       strcpy(pClass,pParse->pToken);

       /* done reading, ACTION, first install dummy error handler */
       ErrFunc = NXIReportError;
       NXMSetError(NXpData, DummyError);
 
       /* try opening vGroup */
       iRet = NXopengroup(hfil, pName, pClass);
       NXMSetError(NXpData,ErrFunc);
       if(iRet == NX_OK)
       {
         pParse->iDepth++;
         return NX_OK;
       } 
       else
       {
          /* we need to create it, if we may */
          if(pParse->iMayCreate)
          {
            iRet = NXmakegroup(hfil,pName,pClass);
            if(iRet != NX_OK)
            { 
                 /* a comment on this one has already been written! */
                 return iRet;
            }
            iRet = NXopengroup(hfil,pName,pClass);
            if(iRet != NX_OK)
            { 
                 /* a comment on this one has already been written! */
                 return iRet;
            }
            pParse->iDepth++;
            return NX_OK; 
          }
          else
          {
             /* this is an error */
             sprintf(pError,"ERROR: vGroup %s, %s NOT found",pName, pClass);
             NXIReportError(NXpData,pError);
             return NX_ERROR;
          }
       }
       /* not reached */
       return NX_ERROR;
   }

/*------------------------------------------------------------------------*/

   static int NXDIParseAttr(ParDat *pParse, int iList)
   {
     char pError[256];
     int iRet;
     AttItem sAtt;

     /* a { is expected */
     NXDIDefToken(pParse);
     if(pParse->iToken != DOPEN)
     {
        sprintf(pError,"ERROR: expected {, got %s",pParse->pToken);
        NXIReportError(NXpData,pError);
        return NX_ERROR;
     }

     /* a word is expected */
     NXDIDefToken(pParse);
     if(pParse->iToken != DWORD)
     {
        sprintf(pError,"ERROR: expected attribute name, got %s",pParse->pToken);
        NXIReportError(NXpData,pError);
        return NX_ERROR;
     }
     strcpy(sAtt.name,pParse->pToken);

     /* a , is expected */
     NXDIDefToken(pParse);
     if(pParse->iToken != DKOMMA)
     {
        sprintf(pError,"ERROR: expected , , got %s",pParse->pToken);
        NXIReportError(NXpData,pError);
        return NX_ERROR;
     }

     /* a word is expected */
     NXDIDefToken(pParse);
     if(pParse->iToken != DWORD)
     {
        sprintf(pError,"ERROR: expected attribute value, got %s",pParse->pToken);
        NXIReportError(NXpData,pError);
        return NX_ERROR;
     }
     strcpy(sAtt.value,pParse->pToken);

     /* a } is expected */
     NXDIDefToken(pParse);
     if(pParse->iToken != DCLOSE)
     {
        sprintf(pError,"ERROR: expected }, got %s",pParse->pToken);
        NXIReportError(NXpData,pError);
        return NX_ERROR;
     }

     /* enter into list */
     LLDnodeAppendFrom(iList,&sAtt);
     return NX_OK;
   }

/*------------------------------------------------------------------------*/
   static int NXDIParseDim(ParDat *pParse, int *iDim)
   {
       char pError[256];
       int iRet, i;

        /* initialise dimensions to 0 */
        for(i = 0; i < MAX_VAR_DIMS; i++)
        {
          iDim[i] = 0;
        }

        NXDIDefToken(pParse);
        if(pParse->iToken != DOPEN)
        {
          sprintf(pError,"ERROR: expected {, got %s",pParse->pToken);
          NXIReportError(NXpData,pError);
          return NX_ERROR;
        }

        i = 0;      
        while(pParse->iToken != DCLOSE)
        {
           /* get a number */
           NXDIDefToken(pParse);
           if(pParse->iToken != DWORD)
           {
              sprintf(pError,"ERROR: expected number, got %s",pParse->pToken);
              NXIReportError(NXpData,pError);
              return NX_ERROR;
           }
           iDim[i] = atoi(pParse->pToken);
           i++;
           /* next must be close of komma */
           NXDIDefToken(pParse);
           if( (pParse->iToken != DKOMMA) && (pParse->iToken != DCLOSE) )
           {
              sprintf(pError,"ERROR: expected , or }, got %s",pParse->pToken);
              NXIReportError(NXpData,pError);
              return NX_ERROR;
           }
           if(pParse->iToken == DCLOSE)
           {
              break;
           }
        }
        return NX_OK;
   }
/*------------------------------------------------------------------------*/
   static TokDat tDatType[] = {
                      {"DFNT_FLOAT32",DFNT_FLOAT32},   
                      {"DFNT_FLOAT64",DFNT_FLOAT64},    
                      {"DFNT_INT8",DFNT_INT8},  
                      {"DFNT_UINT8",DFNT_UINT8},
                      {"DFNT_INT16",DFNT_INT16},      
                      {"DFNT_UINT16",DFNT_UINT16},
                      {"DFNT_INT32",DFNT_INT32},
                      {"DFNT_UINT32",DFNT_UINT32},
                      {"DFNT_CHAR",DFNT_CHAR},
                      {NULL,-122} };



   static int NXDIParseType(ParDat *pParse, int *iType)
   {
      char pError[256];
      int i = 0;

      NXDIDefToken(pParse);
      if(pParse->iToken != DWORD)
      { 
        sprintf(pError,"ERROR: expected data type, got %s", pParse->pToken);
        NXIReportError(NXpData,pError);
        return NX_ERROR;
      }
       
      /* try to interpret data type */
      while(tDatType[i].iCode > 0) {
        if(strcmp(tDatType[i].pText,pParse->pToken) == 0)
        {
           *iType = tDatType[i].iCode;
            return NX_OK;
        }
        i++;
      }
      /* if we are here, the data type has not been recognized. Reason for
         some boring error reporting code
      */
      sprintf(pError,"ERROR: %s not recognized as valid data type",
                      pParse->pToken);
      NXIReportError(NXpData,pError);
      return NX_ERROR;      
   }

/*-------------------------------------------------------------------------*/
   static int NXDIParseSDS(NXhandle hfil, ParDat *pParse)
   {
      int iType = DFNT_FLOAT32;
      int iRank = 1;
      int iCompress = NX_COMP_NONE;
      int32 iDim[MAX_VAR_DIMS], iChunk[MAX_VAR_DIMS];
      int iList, iChunkDefined = 0 ;
      int iRet, iStat, i;
      char pError[256];
      char pName[MAX_NC_NAME];
      void (*ErrFunc)(void *pData, char *pErr);
      AttItem sAtt; 


       iDim[0] = 1;
       /* first find the name */
       NXDIDefToken(pParse);
       if(pParse->iToken != DWORD)
       {
          sprintf(pError,"ERROR: parsing, expected name, got %s",
                  pParse->pToken);
          NXIReportError(NXpData,pError);
          return NX_ERROR;
       }
        strcpy(pName,pParse->pToken);

       /* create the attribute list */
       iList = LLDcreate(sizeof(AttItem));
       if(iList < 0)
       {
          NXIReportError(NXpData, "ERROR: cannot create list in NXDIParseSDS");
          return NX_ERROR;
       }

       NXDIDefToken(pParse);
       while(pParse->iToken != DEND)
       {
          switch(pParse->iToken)
          {
            case DRANK: /* rank */
                       NXDIDefToken(pParse); /* advance */
                       if(pParse->iToken != DWORD)
                       {
                         sprintf(pError,
                         "ERROR: expected int, got %s", pParse->pToken);
                          NXIReportError(NXpData,pError);
                          LLDdelete(iList);
                          return NX_ERROR; 
                       }
                       iRank = atoi(pParse->pToken);
                       break;
            case CHUNK: /* chunk size for compression  */
                     iRet = NXDIParseDim(pParse, iChunk);
                     if(iRet == NX_ERROR)
                     {
                        LLDdelete(iList);
                        return iRet;
                     } 
                     iChunkDefined = 1;
                     break;
            case DDIM:
                     iRet = NXDIParseDim(pParse, iDim);
                     if(iRet == NX_ERROR)
                     {
                        LLDdelete(iList);
                        return iRet;
                     } 
                     break;
            case DTYPE:
                     iRet = NXDIParseType(pParse, &iType);
                     if(iRet == NX_ERROR)
                     {
                        LLDdelete(iList);
                        return iRet;
                     } 
                     break;
            case DATTR:
                     iRet = NXDIParseAttr(pParse, iList);
                     if(iRet == NX_ERROR)
                     {
                        LLDdelete(iList);
                        return iRet;
                     } 
                     break;
	    case DLZW:
                     iCompress = NX_COMP_LZW;
                     break;
	    case DRLE:
                     iCompress = NX_COMP_RLE;
                     break;
	    case DHUF:
                     iCompress = NX_COMP_HUF;
                     break;
            case DEND:
                     break;
            default:
                     sprintf(pError,"ERROR: cannot identify token %s",
                                     pParse->pToken);
                     NXIReportError(NXpData, pError);
                     LLDdelete(iList);
                     return NX_ERROR;
                    
          }
          NXDIDefToken(pParse);
       }

       /* whew! got all information for doing the SDS 
          However, if the chunk sizes for compression have not 
          been set, default them to the dimensions of the data set
       */
       if(iChunkDefined == 0)
       {
	 for(i = 0; i < iRank; i++)
	 {
	   iChunk[i] = iDim[i];
         }
       }
       
       /* first install dummy error handler, try open it, then
          deinstall again and create if allowed 
       */
       ErrFunc = NXIReportError;
       NXMSetError(NXpData, DummyError);
 
       /* try opening SDS */
       iRet = NXopendata(hfil, pName);
       NXMSetError(NXpData,ErrFunc);
       if(iRet == NX_OK)
       {
         LLDdelete(iList);
         return NX_OK;
       } 
       else
       {
          /* we need to create it, if we may */
          if(pParse->iMayCreate)
          {
            iRet = NXcompmakedata(hfil,pName,iType, iRank,iDim,
                                  iCompress,iChunk);
            if(iRet != NX_OK)
            { 
                 /* a comment on this one has already been written! */
                 LLDdelete(iList);
                 return iRet;
            }
            iRet = NXopendata(hfil,pName);
            if(iRet != NX_OK)
            { 
                 /* a comment on this one has already been written! */
                 LLDdelete(iList);
                 return iRet;
            }

            /* put attributes in */
            iRet = LLDnodePtr2First(iList);
            while(iRet != 0)
            {
              LLDnodeDataTo(iList,&sAtt);
              iStat = NXputattr(hfil,sAtt.name,
                                sAtt.value,strlen(sAtt.value),NX_CHAR);
              if(iStat != NX_OK)
              {
                 /* NeXus already complained bitterly */
                 LLDdelete(iList);
                 return iStat;
              }
              iRet = LLDnodePtr2Next(iList);
            }
            LLDdelete(iList);
            return NX_OK; 
          }
          else
          {
             /* this is an error */
             sprintf(pError,"ERROR: SDS %s NOT found",pName);
             NXIReportError(NXpData,pError);
             LLDdelete(iList);
             return NX_ERROR;
          }
       }
       return NX_OK;
   }
/*------------------------------------------------------------------------*/
   static int NXDIParseLink(NXhandle hfil, NXdict pDict,ParDat *pParse)
   {
     char pError[256];
     int i, iRet;

     /* need one word of alias */ 
     NXDIDefToken(pParse);
     if(pParse->iToken != DCLOSE)
     {
        sprintf(pError,"ERROR: expected alias , got %s",pParse->pToken);
        NXIReportError(NXpData,pError);
        return NX_ERROR;
     }
    
     /* move back in hierarchy */
     for(i = 0; i < pParse->iDepth; i++)
     {
       iRet = NXclosegroup(hfil);
       if(iRet == NX_ERROR)
       {
         return NX_ERROR;
       }
     }
     
     /* open the link instead */ 
     return NXDopenalias(hfil, pDict, pParse->pToken);

   }
/*------------------------------------------------------------------------*/
    int NXDIDefParse(NXhandle hFil, NXdict pDict, ParDat *pParse)
    {
       int iRet;
       char pError[256];
 
       pParse->iToken = -1;
       while(pParse->iToken != DEND)
       {
         NXDIDefToken(pParse); /* next token */
         switch(pParse->iToken)
         {
            case DEND:
                      break;
            case DSLASH:
                      iRet = NXDIParsePath(hFil, pParse);
                      if(iRet == NX_ERROR)
                      {
                         return NX_ERROR;
                      }
                      break;
            case DSDS:
                      iRet = NXDIParseSDS(hFil, pParse);
                      if(iRet == NX_ERROR)
                      {
                         return NX_ERROR;
                      }
                      pParse->iTerminal = TERMSDS;
                      break;
            case DLINK:
                      iRet = NXDIParseLink(hFil,pDict, pParse);                      
                      if(iRet == NX_ERROR)
                      {
                         return NX_ERROR;
                      }
                      pParse->iTerminal = TERMLINK;
                      break;
            case DGROUP:
                      pParse->iTerminal = TERMVG;
                      return NX_OK;
            default:
                     sprintf(pError,
            "ERROR: Definition String parse error: %s not permitted here",
             pParse->pToken);
                     NXIReportError(NXpData,pError);
                     return NX_ERROR;
                     break;
         }
       }
       return NX_OK;
    }
/*----------------------------------------------------------------------*/
  NXstatus NXDIUnwind(NXhandle hFil, int iDepth)
  {
     int i, iRet;
      
     for(i = 0; i < iDepth; i++)
     {
        iRet = NXclosegroup(hFil);
        if(iRet != NX_OK)
        {
          return NX_ERROR;
        }
     }
     return NX_OK;
  }
/*-------------------- The Data Transfer Functions ----------------------*/
   NXstatus NXDopendef(NXhandle hfil, NXdict dict, char *pDef)
   {
     NXdict pDict;
     ParDat pParse;
     int iRet, i, iStat;

     pDict = NXDIAssert(dict);
     
     /* parse and act on definition string */
     pParse.iMayCreate = 1;
     pParse.pPtr = pDef;
     pParse.iDepth = 0;
     iRet = NXDIDefParse(hfil,pDict,&pParse);
     if(iRet == NX_ERROR)
     {
         /* unwind and throw up */
         iRet = NXDIUnwind(hfil,pParse.iDepth);
         return NX_ERROR;
     }

     
     /* try rewinding the hierarchy */
     if(pParse.iTerminal == TERMSDS)
     {
         iStat = NXDIUnwind(hfil,pParse.iDepth);
         if(iStat != NX_OK)
         {
            return NX_ERROR;
         }
     }
     /* do not rewind on links */
     return iRet;
   }
/*------------------------------------------------------------------------*/
   NXstatus NXDopenalias(NXhandle hfil, NXdict dict, char *pAlias)
   {
     NXdict pDict;
     int iRet;
     char pDefinition[2048];
     pDynString pReplaced = NULL;

     pDict = NXDIAssert(dict);
     
     /* get Definition String  */
     iRet = NXDget(pDict,pAlias,pDefinition,2047);
     if(iRet != NX_OK)
     {
        sprintf(pDefinition,"ERROR: alias %s not recognized",pAlias);
        NXIReportError(NXpData,pDefinition);
        return NX_ERROR;
     }
     
     /* do the text replacement */
     pReplaced = NXDItextreplace(dict,pDefinition);
     if(!pReplaced)
     {
         return NX_ERROR;
     }

     /* call NXDopendef */
     iRet = NXDopendef(hfil,dict,GetCharArray(pReplaced));
     DeleteDynString(pReplaced);
     return iRet;
   }
/*------------------------------------------------------------------------*/
   NXstatus NXDputdef(NXhandle hFil, NXdict dict, char *pDef, void *pData)
   {
     NXdict pDict;
     ParDat pParse;
     int iRet, i, iStat;

     pDict = NXDIAssert(dict);
     
     /* parse and act on definition string */
     pParse.iMayCreate = 1;
     pParse.pPtr = pDef;
     pParse.iDepth = 0;
#ifdef DEFDEBUG
     printf("Putting: %s\n",pDef);
#endif
     iRet = NXDIDefParse(hFil,pDict,&pParse);
     if(iRet == NX_ERROR)
     {
          NXDIUnwind(hFil,pParse.iDepth);
          return NX_ERROR;
     }

     
     /* only SDS can be written */
     if(pParse.iTerminal != TERMSDS)
     {
        NXIReportError(NXpData,
                     "ERROR: can only write to an SDS!");
        iStat = NX_ERROR;
     } 
     else 
     {
       /* the SDS should be open by now, write it */
       iStat = NXputdata(hFil, pData);
       iRet = NXclosedata(hFil);
     }   


     /* rewind the hierarchy */
     iRet = NXDIUnwind(hFil,pParse.iDepth);
     if(iRet != NX_OK)
     {
       return NX_ERROR;
     }
     return iStat;
   }
/*------------------------------------------------------------------------*/
  NXstatus NXDputalias(NXhandle hFil, NXdict dict, char *pAlias, void *pData)
  {
     NXdict pDict;
     int iRet;
     char pDefinition[2048];
     pDynString pReplaced = NULL;

     pDict = NXDIAssert(dict);
     
     /* get Definition String  */
     iRet = NXDget(pDict,pAlias,pDefinition,2047);
     if(iRet != NX_OK)
     {
        sprintf(pDefinition,"ERROR: alias %s not recognized",pAlias);
        NXIReportError(NXpData,pDefinition);
        return NX_ERROR;
     }
     
     /* do text replacement */
     pReplaced = NXDItextreplace(dict,pDefinition);
     if(!pReplaced)
     {
       return NX_ERROR;
     }

     /* call NXDputdef */
     iRet = NXDputdef(hFil,dict,GetCharArray(pReplaced),pData);
     DeleteDynString(pReplaced);
     return iRet;
  }
/*------------------------------------------------------------------------*/
   NXstatus NXDgetdef(NXhandle hFil, NXdict dict, char *pDef, void *pData)
   {
     NXdict pDict;
     ParDat pParse;
     int iRet, i, iStat;

     pDict = NXDIAssert(dict);
     
     /* parse and act on definition string */
     pParse.iMayCreate = 0;
     pParse.pPtr = pDef;
     pParse.iDepth = 0;
#ifdef DEFDEBUG
     printf("Getting: %s\n",pDef);
#endif
     iRet = NXDIDefParse(hFil,pDict,&pParse);
     if(iRet == NX_ERROR)
     {
         /* unwind and throw up */
          NXDIUnwind(hFil,pParse.iDepth);
          return NX_ERROR;
     }


     /* only SDS can be written */
     if(pParse.iTerminal != TERMSDS)
     {
        NXIReportError(NXpData,
                     "ERROR: can only write to an SDS!");
        iStat = NX_ERROR;
     } 
     else 
     {
       /* the SDS should be open by now, read it */
       iStat = NXgetdata(hFil, pData);
       iRet = NXclosedata(hFil);
     }   


     /* rewind the hierarchy */
     iRet = NXDIUnwind(hFil,pParse.iDepth);
     if(iRet != NX_OK)
     {
        return NX_ERROR;
     }
     return iStat;
   }

/*------------------------------------------------------------------------*/
  NXstatus NXDgetalias(NXhandle hFil, NXdict dict, char *pAlias, void *pData)
  {
     NXdict pDict;
     int iRet;
     char pDefinition[2048];
     pDynString pReplaced = NULL;

     pDict = NXDIAssert(dict);
     
     /* get Definition String  */
     iRet = NXDget(pDict,pAlias,pDefinition,2047);
     if(iRet != NX_OK)
     {
        sprintf(pDefinition,"ERROR: alias %s not recognized",pAlias);
        NXIReportError(NXpData,pDefinition);
        return NX_ERROR;
     }
 
     /* do text replacement */
     pReplaced = NXDItextreplace(dict,pDefinition);
     if(!pReplaced)
     {
       return NX_ERROR;
     }
     
     /* call NXDgetdef */
     iRet = NXDgetdef(hFil,dict,GetCharArray(pReplaced),pData);
     DeleteDynString(pReplaced);
     return iRet;
  }
/*------------------------------------------------------------------------*/

   NXstatus NXDinfodef(NXhandle hFil, NXdict dict, char *pDef, int *rank,
   			int dimension[], int *iType)
   {
     NXdict pDict;
     ParDat pParse;
     int iRet, i, iStat;

     pDict = NXDIAssert(dict);
     
     /* parse and act on definition string */
     pParse.iMayCreate = 0;
     pParse.pPtr = pDef;
     pParse.iDepth = 0;
#ifdef DEFDEBUG
     printf("Getting: %s\n",pDef);
#endif
     iRet = NXDIDefParse(hFil,pDict,&pParse);
     if(iRet == NX_ERROR)
     {
         /* unwind and throw up */
          NXDIUnwind(hFil,pParse.iDepth);
          return NX_ERROR;
     }


     /* only SDS can be written */
     if(pParse.iTerminal != TERMSDS)
     {
        NXIReportError(NXpData,
                     "ERROR: can only write to an SDS!");
        iStat = NX_ERROR;
     } 
     else 
     {
       /* the SDS should be open by now, read it */
       iStat = NXgetinfo(hFil, rank,dimension, iType);
       iRet = NXclosedata(hFil);
     }   


     /* rewind the hierarchy */
     iRet = NXDIUnwind(hFil,pParse.iDepth);
     if(iRet != NX_OK)
     {
        return NX_ERROR;
     }
     return iStat;
   }

/*------------------------------------------------------------------------*/

  NXstatus NXDinfoalias(NXhandle hFil, NXdict dict, char *pAlias, int *rank,
   			int dimension[], int *iType)
  {
     NXdict pDict;
     int iRet;
     char pDefinition[2048];
     pDynString pReplaced = NULL;

     pDict = NXDIAssert(dict);
     
     /* get Definition String  */
     iRet = NXDget(pDict,pAlias,pDefinition,2047);
     if(iRet != NX_OK)
     {
        sprintf(pDefinition,"ERROR: alias %s not recognized",pAlias);
        NXIReportError(NXpData,pDefinition);
        return NX_ERROR;
     }
 
     /* do text replacement */
     pReplaced = NXDItextreplace(dict,pDefinition);
     if(!pReplaced)
     {
       return NX_ERROR;
     }
     
     /* call NXDgetdef */
     iRet = NXDinfodef(hFil,dict,GetCharArray(pReplaced),rank,dimension,iType);
     DeleteDynString(pReplaced);
     return iRet;
  }

/*------------------------------------------------------------------------*/
  NXstatus NXDdeflink(NXhandle hFil, NXdict dict, 
                      char *pTarget, char *pVictim)
  {
     NXdict pDict;
     ParDat pParseT, pParseV;
     int iRet, i, iStat;
     NXlink sLink;

     pDict = NXDIAssert(dict);


#ifdef DEFDEBUG
     printf("Linking: %s\n",pVictim);
     printf("To: %s\n", pTarget);
#endif
     

     /* parse Victim  */
     pParseV.iMayCreate = 0;
     pParseV.pPtr = pVictim;
     pParseV.iDepth = 0;
     iRet = NXDIDefParse(hFil,pDict,&pParseV);
     if(iRet == NX_ERROR)
     {
         /* unwind and throw up */
          NXDIUnwind(hFil,pParseV.iDepth);
          return NX_ERROR;
     }
     /* get link data */
     if(pParseV.iTerminal == TERMSDS)
     {
       NXgetdataID(hFil,&sLink);
       iRet =  NXclosedata(hFil);
       if(iRet != NX_OK)
       {
         /* unwind and throw up */
          NXDIUnwind(hFil,pParseV.iDepth);
          return NX_ERROR;
       }
     }
     else if(pParseV.iTerminal == TERMVG)
     {
       NXgetgroupID(hFil,&sLink);
     }
     else
     {
        assert(0); /* serious programming error */
     }
     /* Unwind */
     iRet = NXDIUnwind(hFil,pParseV.iDepth);
     if(iRet != NX_OK)
     {
       return NX_ERROR;
     }

     /* parse Target */
     pParseT.iMayCreate = 1;
     pParseT.pPtr = pTarget;
     pParseT.iDepth = 0;
     iRet = NXDIDefParse(hFil,pDict,&pParseT);
     if(iRet == NX_ERROR)
     {
         /* unwind and throw up */
          NXDIUnwind(hFil,pParseT.iDepth);
          return NX_ERROR;
     }
     /* check it being a vGroup! */
     if(pParseT.iTerminal != TERMVG)
     {
       NXIReportError(NXpData,"ERROR: can link only into a vGroup");
       NXDIUnwind(hFil,pParseT.iDepth);
       return NX_ERROR;
     }
    
     /* link, finally */
     iRet = NXmakelink(hFil,&sLink);
     /* Unwind anyway */
     iStat = NXDIUnwind(hFil,pParseT.iDepth);
     if(iStat != NX_OK)
     {
       return NX_ERROR;
     }
     return iStat;
  }
/*--------------------------------------------------------------------------*/
  NXstatus NXDaliaslink(NXhandle hFil, NXdict dict, 
                      char *pTarget, char *pVictim)
  {
    char pTargetDef[2048], pVictimDef[2048];
    int iRet;
    NXdict pDict;
    pDynString pRep1 = NULL, pRep2 = NULL;

     pDict = NXDIAssert(dict);
     
     /* get Target Definition String  */
     iRet = NXDget(pDict,pTarget,pTargetDef,2047);
     if(iRet != NX_OK)
     {
        sprintf(pTargetDef,"ERROR: alias %s not recognized",pTarget);
        NXIReportError(NXpData,pTargetDef);
        return NX_ERROR;
     }       

     /* get Victim definition string */
     iRet = NXDget(pDict,pVictim,pVictimDef,2047);
     if(iRet != NX_OK)
     {
        sprintf(pTargetDef,"ERROR: alias %s not recognized",pTarget);
        NXIReportError(NXpData,pTargetDef);
        return NX_ERROR;
     }

     /* do replacements */
     pRep1 = NXDItextreplace(dict,pTargetDef);
     pRep2 = NXDItextreplace(dict,pVictimDef);
     if( (!pRep1) || (!pRep2) )
     {
        if(pRep1)
           DeleteDynString(pRep1);
        if(pRep2)
           DeleteDynString(pRep2);
        return NX_ERROR;
     }

     /* call NXdeflin */
     iRet = NXDdeflink(hFil,pDict,GetCharArray(pRep1),GetCharArray(pRep2));
     DeleteDynString(pRep1);
     DeleteDynString(pRep2);
     return iRet;
  }
/*-------------------------------------------------------------------------*/
  static void SNXFormatTime(char *pBuffer, int iBufLen)
  {
     time_t iDate;
     struct tm *psTime;

     /* make time string */
     iDate = time(NULL);
     psTime = localtime(&iDate);
     memset(pBuffer,0,iBufLen);         
     strftime(pBuffer,iBufLen,"%Y-%m-%d %H:%M:%S",psTime);
  }  
/*--------------------------------------------------------------------------*/
   NXstatus NXUwriteglobals(NXhandle pFile, 
                            char *filename,
                            char *owner,
                            char *adress,
                            char *phone,
                            char *email,
                            char *fax,
                            char *instrument)
   {
     char pBueffel[512];
     int iStat;
     
     /* store global attributes, now done by NXopen
     iStat = NXputattr(pFile,"file_name",filename, 
                       strlen(filename)+1,NX_CHAR);
     if(iStat == NX_ERROR)
     {
       return NX_ERROR;
     }
     */
     
     /* write creation time, now done by NXopen
     SNXFormatTime(pBueffel,512);
     iStat = NXputattr(pFile,"file_time",pBueffel,
                       strlen(pBueffel)+1,NX_CHAR);
     if(iStat == NX_ERROR)
     {
       return NX_ERROR;
     }
     */

     /* instrument name */
     iStat = NXputattr(pFile,"instrument",instrument,
                       strlen(instrument)+1,NX_CHAR);
     if(iStat == NX_ERROR)
     {
       return iStat;
     }

     /* owner */
     iStat = NXputattr(pFile,"owner",owner,
                       strlen(owner)+1,NX_CHAR);
     if(iStat == NX_ERROR)
     {
       return iStat;
     }

     /* Adress */
     iStat = NXputattr(pFile,"owner_adress",adress,
                       strlen(adress)+1,NX_CHAR);
     if(iStat == NX_ERROR)
     {
        return iStat;
     }

     /* phone */
     iStat = NXputattr(pFile,"owner_telephone_number",phone,
                       strlen(phone)+1,NX_CHAR);
     if(iStat == NX_ERROR)
     {
        return iStat;
     }

     /* fax */
     iStat = NXputattr(pFile,"owner_fax_number",fax,
                       strlen(fax)+1,NX_CHAR);
     if(iStat == NX_ERROR)
     {
       return iStat;
     }

     /* email */
     iStat = NXputattr(pFile,"owner_email",email,
                       strlen(email)+1,NX_CHAR);
     if(iStat == NX_ERROR)
     {
       return iStat;
     }
     return NX_OK;
   }
/*-----------------------------------------------------------------------*/
   NXstatus NXUentergroup(NXhandle hFil, char *name, char *class)
   {
       void (*ErrFunc)(void *pData, char *pErr); 
       int iRet;

       /* ACTION, first install dummy error handler */
       ErrFunc = NXIReportError;
       NXMSetError(NXpData, DummyError);
 
       /* try opening vGroup */
       iRet = NXopengroup(hFil, name, class);
       NXMSetError(NXpData,ErrFunc);
       if(iRet == NX_OK)
       {
         return NX_OK;
       } 
       else
       {
          /* we need to create it */
            iRet = NXmakegroup(hFil,name,class);
            if(iRet != NX_OK)
            { 
                 /* a comment on this one has already been written! */
                 return iRet;
            }
            iRet = NXopengroup(hFil,name,class);
            if(iRet != NX_OK)
            { 
                 /* a comment on this one has already been written! */
                 return iRet;
            }
       }
       return NX_OK;
   }
/*-----------------------------------------------------------------------*/
   NXstatus NXUenterdata(NXhandle hFil, char *label, int datatype,
                         int rank, int dim[], char *pUnits)
   {
       void (*ErrFunc)(void *pData, char *pErr); 
       int iRet;

       /* ACTION, first install dummy error handler */
       ErrFunc = NXIReportError;
       NXMSetError(NXpData, DummyError);
 
       /* try opening SDS */
       iRet = NXopendata(hFil, label);
       NXMSetError(NXpData,ErrFunc);
       if(iRet == NX_OK)
       {
         return NX_OK;
       } 
       else
       {
          /* we need to create it */
            iRet = NXmakedata(hFil,label, datatype, rank,dim);
            if(iRet != NX_OK)
            { 
                 /* a comment on this one has already been written! */
                 return iRet;
            }
            iRet = NXopendata(hFil,label);
            if(iRet != NX_OK)
            { 
                 /* a comment on this one has already been written! */
                 return iRet;
            }
            iRet = NXputattr(hFil, "Units",pUnits,
                             strlen(pUnits) + 1,DFNT_INT8);
            if(iRet != NX_OK)
            { 
                 /* a comment on this one has already been written! */
                 return iRet;
            }
       }
       return NX_OK;
   }
/*-----------------------------------------------------------------------*/
   NXstatus NXUallocSDS(NXhandle hFil, void **pData)
   {
      int iDIM[MAX_VAR_DIMS];
      int iRank,iType;
      int iRet, i;
      long lLength;

      /* get info */
      iRet = NXgetinfo(hFil,&iRank, iDIM, &iType);
      if(iRet != NX_OK)
      {
        return iRet;
      }      

      /* calculate Size */
      lLength = iDIM[0];
      for(i = 1; i < iRank; i++)
      {
        lLength *= iDIM[i];
      }
      switch(iType)
      {
        case DFNT_FLOAT32:
             lLength *= sizeof(float32);
             break;
        case DFNT_FLOAT64:
             lLength *= sizeof(float64);
             break;
        case DFNT_INT8:
        case DFNT_CHAR:
        case DFNT_UCHAR8:
             lLength *= sizeof(int8);
             break;
        case DFNT_UINT8:
             lLength *= sizeof(uint8);
             break;
        case DFNT_INT16:
             lLength *= sizeof(int16);
             break;
        case DFNT_UINT16:
             lLength *= sizeof(uint16);
             break;
        case DFNT_INT32:
             lLength *= sizeof(int32);
             break;
        case DFNT_UINT32:
             lLength *= sizeof(uint32);
             break;
        default:
             NXIReportError(NXpData,"ERROR: Internal: number type not recoginized");
             return NX_ERROR;
      }

      /* time to malloc */
      *pData = NULL;
      *pData = malloc(lLength);
      if(*pData == NULL)
      {
        NXIReportError(NXpData,"ERROR: memory exhausted in NXUallocSDS");
        return NX_ERROR;
      }
      memset(*pData,0,lLength);
      return NX_OK;
   }
/*----------------------------------------------------------------------*/
   NXstatus NXUfreeSDS(void **pData)
   {
      free(*pData);
      *pData = NULL;
      return NX_OK;
   }

