source: trunk/src/napi.c @ 1822

Revision 1797, 56.6 KB checked in by Freddie Akeroyd, 4 months ago (diff)
  • Fix issue of only writing links to output HDF5 files
  • Also speed up conversion to definition by using NXgetslab/NXputslab

Refs #320,#321

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*---------------------------------------------------------------------------
2  NeXus - Neutron & X-ray Common Data Format
3 
4  Application Program Interface Routines
5 
6  Copyright (C) 1997-2006 Mark Koennecke, Przemek Klosowski
7 
8  This library is free software; you can redistribute it and/or
9  modify it under the terms of the GNU Lesser General Public
10  License as published by the Free Software Foundation; either
11  version 2 of the License, or (at your option) any later version.
12 
13  This library is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  Lesser General Public License for more details.
17 
18  You should have received a copy of the GNU Lesser General Public
19  License along with this library; if not, write to the Free Software
20  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 
22  For further information, see <http://www.nexusformat.org>
23
24----------------------------------------------------------------------------*/
25
26static const char* rscid = "$Id$";      /* Revision inserted by CVS */
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <assert.h>
31#include <string.h>
32#include <ctype.h>
33#include <time.h>
34#include <stdarg.h>
35#include <stdint.h>
36
37#include "nxconfig.h"
38#include "napi.h"
39#include "nxstack.h"
40
41/*---------------------------------------------------------------------
42 Recognized and handled napimount URLS
43 -----------------------------------------------------------------------*/
44#define NXBADURL 0
45#define NXFILE 1
46
47/*--------------------------------------------------------------------*/
48static int iFortifyScope;
49/*----------------------------------------------------------------------
50  This is a section with code for searching the NX_LOAD_PATH
51  -----------------------------------------------------------------------*/
52#ifdef _WIN32
53#define LIBSEP ";"
54#define PATHSEP "\\"
55#define THREAD_LOCAL __declspec(thread)
56#else
57#define LIBSEP ":"
58#define PATHSEP "/"
59#define THREAD_LOCAL __thread
60#endif
61
62#ifdef _MSC_VER
63#define snprintf _snprintf
64#endif /* _MSC_VER */
65
66#include "nx_stptok.h"
67
68#if defined(_WIN32)
69/*
70 *  HDF5 on windows does not do locking for multiple threads conveniently so we will implement it ourselves.
71 *  Freddie Akeroyd, 16/06/2011
72 */
73#include <windows.h>
74
75
76static CRITICAL_SECTION nx_critical;
77
78static int nxilock()
79{
80    static int first_call = 1;
81    if (first_call)
82    {
83        first_call = 0;
84        InitializeCriticalSection(&nx_critical);
85    }
86    EnterCriticalSection(&nx_critical);
87    return NX_OK;
88}
89
90static int nxiunlock(int ret)
91{
92    LeaveCriticalSection(&nx_critical);
93    return ret;
94}
95
96#define LOCKED_CALL(__call) \
97    ( nxilock() , nxiunlock(__call) )
98
99#elif HAVE_LIBPTHREAD
100
101#include <pthread.h>
102
103static pthread_mutex_t nx_mutex;
104
105#ifdef PTHREAD_MUTEX_RECURSIVE
106#define RECURSIVE_LOCK PTHREAD_MUTEX_RECURSIVE
107#else
108#define RECURSIVE_LOCK PTHREAD_MUTEX_RECURSIVE_NP
109#endif /* PTHREAD_MUTEX_RECURSIVE */
110
111static void nx_pthread_init()
112{
113    pthread_mutexattr_t attr;
114    pthread_mutexattr_init(&attr);
115    pthread_mutexattr_settype(&attr, RECURSIVE_LOCK);
116    pthread_mutex_init(&nx_mutex, &attr);
117}
118
119static int nxilock()
120{
121    static pthread_once_t once_control = PTHREAD_ONCE_INIT;
122    if (pthread_once(&once_control, nx_pthread_init) != 0)
123    {
124        NXReportError("pthread_once failed");
125        return NX_ERROR;
126    }
127    if (pthread_mutex_lock(&nx_mutex) != 0)
128    {
129        NXReportError("pthread_mutex_lock failed");
130        return NX_ERROR;
131    }
132    return NX_OK;
133}
134
135static int nxiunlock(int ret)
136{
137    if (pthread_mutex_unlock(&nx_mutex) != 0)
138    {
139        NXReportError("pthread_mutex_unlock failed");
140        return NX_ERROR;
141    }
142    return ret;
143}
144
145#define LOCKED_CALL(__call) \
146    ( nxilock() , nxiunlock(__call) )
147
148#else
149
150#define LOCKED_CALL(__call) \
151           __call
152
153#endif /* _WIN32 */
154
155/**
156 * valid NeXus names
157 */
158int validNXName(const char* name, int allow_colon)
159{
160    int i;
161    if (name == NULL)
162    {
163        return 0;
164    }
165    for(i=0; i<strlen(name); ++i)
166    {
167        if ( (name[i] >= 'a' && name[i] <= 'z') ||
168             (name[i] >= 'A' && name[i] <= 'Z') ||
169             (name[i] >= '0' && name[i] <= '9') ||
170             (name[i] == '_') )
171        {
172            ;
173        }
174        else if (allow_colon && name[i] == ':')
175        {
176            ;
177        }
178        else
179        {
180            return 0;
181        }
182    }
183    return 1;
184}
185
186static int64_t* dupDimsArray(int* dims_array, int rank)
187{
188        int i;
189        int64_t* dims64 = (int64_t*)malloc(rank * sizeof(int64_t));
190        if (dims64 != NULL)
191        {
192                for(i=0; i<rank; ++i)
193                {
194                        dims64[i] = dims_array[i];
195                }
196        }
197        return dims64;
198}
199
200/*---------------------------------------------------------------------
201 wrapper for getenv. This is a future proofing thing for porting to OS
202 which have different ways of accessing environment variables
203 --------------------------------------------------------------------*/ 
204static char *nxgetenv(const char *name){
205  return getenv(name);
206}
207/*----------------------------------------------------------------------*/
208static int canOpen(char *filename){
209  FILE *fd = NULL;
210
211  fd = fopen(filename,"r");
212  if(fd != NULL){
213    fclose(fd);
214    return 1;
215  } else {
216    return 0;
217  }
218} 
219/*--------------------------------------------------------------------*/
220static char *locateNexusFileInPath(char *startName){
221  char *loadPath = NULL, *testPath = NULL, *pPtr = NULL;
222  char pathPrefix[256];
223  int length;
224
225  if(canOpen(startName)){
226    return strdup(startName);
227  }
228
229  loadPath = nxgetenv("NX_LOAD_PATH");
230  if(loadPath == NULL){
231    /* file not found will be issued by upper level code */
232    return strdup(startName);
233  }
234
235  pPtr = stptok(loadPath,pathPrefix,255,LIBSEP);
236  while(pPtr != NULL){
237    length = strlen(pathPrefix) + strlen(startName) + strlen(PATHSEP) + 2;
238    testPath = (char*)malloc(length*sizeof(char));
239    if(testPath == NULL){
240      return strdup(startName);
241    }
242    memset(testPath,0,length*sizeof(char));
243    strcpy(testPath, pathPrefix);
244    strcat(testPath,PATHSEP);
245    strcat(testPath,startName);
246    if(canOpen(testPath)){
247      return(testPath);
248    }
249    free(testPath);
250    pPtr = stptok(pPtr,pathPrefix,255,LIBSEP);
251  }
252  return  strdup(startName);
253}
254/*------------------------------------------------------------------------
255  HDF-5 cache size special stuff
256  -------------------------------------------------------------------------*/
257long nx_cacheSize =  1024000; /* 1MB, HDF-5 default */
258
259NXstatus NXsetcache(long newVal)
260{
261  if(newVal > 0)
262  {
263    nx_cacheSize = newVal;
264    return NX_OK;
265  }
266  return NX_ERROR;
267}
268   
269/*-----------------------------------------------------------------------*/
270static NXstatus NXisXML(CONSTCHAR *filename)
271{
272  FILE *fd = NULL;
273  char line[132];
274
275  fd = fopen(filename,"r");
276  if(fd) {
277    int fgets_ok = (fgets(line,131,fd) != NULL);
278    fclose(fd);
279    if (fgets_ok) {
280      if(strstr(line,"?xml") != NULL){
281        return NX_OK;
282      }
283    }
284    else {
285      return NX_ERROR;
286    }
287  }
288  return NX_ERROR;
289}
290
291/*-------------------------------------------------------------------------*/
292  static void NXNXNXReportError(void *pData, char *string)
293  {
294    fprintf(stderr, "%s \n", string);
295  }
296  /*---------------------------------------------------------------------*/
297
298  static void *NXEHpData = NULL;
299  static void (*NXEHIReportError)(void *pData, char *string) = NXNXNXReportError;
300#ifdef HAVE_TLS
301  static THREAD_LOCAL void *NXEHpTData = NULL;
302  static THREAD_LOCAL void (*NXEHIReportTError)(void *pData, char *string) = NULL;
303#endif
304
305  void NXIReportError(void *pData, char *string) {
306        fprintf(stderr, "Your application uses NXIReportError, but its first parameter is ignored now - you should use NXReportError.");
307        NXReportError(string);
308  }
309
310  void NXReportError(char *string) {
311#ifdef HAVE_TLS
312        if (NXEHIReportTError) {
313                (*NXEHIReportTError)(NXEHpTData, string);
314                return;
315        } 
316#endif
317        if (NXEHIReportError) {
318            (*NXEHIReportError)(NXEHpData, string);
319        }
320  }
321
322  /*---------------------------------------------------------------------*/
323  extern void NXMSetError(void *pData, void (*NewError)(void *pD, char *text))
324  {
325    NXEHpData = pData;
326    NXEHIReportError = NewError;
327  }
328/*----------------------------------------------------------------------*/
329  extern void NXMSetTError(void *pData, void (*NewError)(void *pD, char *text))
330  {
331#ifdef HAVE_TLS
332    NXEHpTData = pData;
333    NXEHIReportTError = NewError;
334#else
335    NXMSetError(pData, NewError);
336#endif
337  }
338/*----------------------------------------------------------------------*/
339extern ErrFunc NXMGetError(){
340#ifdef HAVE_TLS
341        if (NXEHIReportTError) {
342                return NXEHIReportTError;
343        }
344#endif
345  return NXEHIReportError;
346}
347
348/*----------------------------------------------------------------------*/
349static void NXNXNoReport(void *pData, char *string){
350  /* do nothing */
351} 
352/*----------------------------------------------------------------------*/
353
354static ErrFunc last_global_errfunc = NXNXNXReportError;
355#ifdef HAVE_TLS
356static THREAD_LOCAL ErrFunc last_thread_errfunc = NULL;
357#endif
358
359extern void NXMDisableErrorReporting()
360{
361#ifdef HAVE_TLS
362        if (NXEHIReportTError) {
363                last_thread_errfunc = NXEHIReportTError;
364                NXEHIReportTError = NXNXNoReport;
365                return;
366        } 
367#endif
368        if (NXEHIReportError) {
369            last_global_errfunc = NXEHIReportError;
370            NXEHIReportError = NXNXNoReport;
371        }
372}
373
374extern void NXMEnableErrorReporting()
375{
376#ifdef HAVE_TLS
377        if (last_thread_errfunc) {
378                NXEHIReportTError = last_thread_errfunc;
379                last_thread_errfunc = NULL;
380                return;
381        } 
382#endif
383        if (last_global_errfunc) {
384            NXEHIReportError = last_global_errfunc;
385            last_global_errfunc = NULL;
386        }
387}
388
389/*----------------------------------------------------------------------*/
390#ifdef HDF5
391#include "napi5.h"
392#endif
393#ifdef HDF4
394#include "napi4.h"
395#endif
396#ifdef NXXML
397#include "nxxml.h"
398#endif 
399  /* ----------------------------------------------------------------------
400 
401                          Definition of NeXus API
402
403   ---------------------------------------------------------------------*/
404static int determineFileTypeImpl(CONSTCHAR *filename)
405{
406  FILE *fd = NULL;
407  int iRet;
408 
409  /*
410    this is for reading, check for existence first
411  */
412  fd = fopen(filename,"r");
413  if(fd == NULL){
414    return -1;
415  }
416  fclose(fd);
417#ifdef HDF5
418  iRet=H5Fis_hdf5((const char*)filename);
419  if( iRet > 0){
420    return 2;
421  }
422#endif 
423#ifdef HDF4
424  iRet=Hishdf((const char*)filename);
425  if( iRet > 0){
426    return 1;
427  }
428#endif
429#ifdef NXXML
430  iRet = NXisXML(filename);
431  if(iRet == NX_OK){
432    return 3;
433  }
434#endif
435  /*
436    file type not recognized
437  */
438  return 0;
439}
440
441static int determineFileType(CONSTCHAR *filename)
442{
443    return LOCKED_CALL(determineFileTypeImpl(filename));
444}
445
446/*---------------------------------------------------------------------*/
447static pNexusFunction handleToNexusFunc(NXhandle fid){
448  pFileStack fileStack = NULL;
449  fileStack = (pFileStack)fid;
450  if(fileStack != NULL){
451    return peekFileOnStack(fileStack);
452  } else {
453    return NULL;
454  }
455}
456/*--------------------------------------------------------------------*/
457static NXstatus   NXinternalopen(CONSTCHAR *userfilename, NXaccess am, 
458           pFileStack fileStack);
459/*----------------------------------------------------------------------*/
460NXstatus   NXopen(CONSTCHAR *userfilename, NXaccess am, NXhandle *gHandle){
461  int status;
462  pFileStack fileStack = NULL;
463
464  *gHandle = NULL;
465  fileStack = makeFileStack();
466  if(fileStack == NULL){
467    NXReportError("ERROR: no memory to create filestack");
468      return NX_ERROR;
469  }
470  status = NXinternalopen(userfilename,am,fileStack);
471  if(status == NX_OK){
472    *gHandle = fileStack;
473  }
474
475  return status;
476}
477/*-----------------------------------------------------------------------*/
478static NXstatus   NXinternalopenImpl(CONSTCHAR *userfilename, NXaccess am, pFileStack fileStack)
479  {
480    int hdf_type=0;
481    int iRet=0;
482    NXhandle hdf5_handle = NULL;
483    NXhandle hdf4_handle = NULL;
484    NXhandle xmlHandle = NULL;
485    pNexusFunction fHandle = NULL;
486    NXstatus retstat = NX_ERROR;
487    char error[1024];
488    char *filename = NULL;
489    int my_am = (am & NXACCMASK_REMOVEFLAGS);
490       
491    /* configure fortify
492    iFortifyScope = Fortify_EnterScope();
493    Fortify_CheckAllMemory();
494    */
495   
496    /*
497      allocate data
498    */
499    fHandle = (pNexusFunction)malloc(sizeof(NexusFunction));
500    if (fHandle == NULL) {
501      NXReportError("ERROR: no memory to create Function structure");
502      return NX_ERROR;
503    }
504    memset(fHandle, 0, sizeof(NexusFunction)); /* so any functions we miss are NULL */
505       
506    /*
507      test the strip flag. Elimnate it for the rest of the tests to work
508    */
509    fHandle->stripFlag = 1;
510    if(am & NXACC_NOSTRIP){
511      fHandle->stripFlag = 0;
512      am = (NXaccess)(am & ~NXACC_NOSTRIP);
513    }
514    fHandle->checkNameSyntax = 0;
515    if (am & NXACC_CHECKNAMESYNTAX) {
516        fHandle->checkNameSyntax = 1;
517        am = (NXaccess)(am & ~NXACC_CHECKNAMESYNTAX);
518    }
519
520
521    if (my_am==NXACC_CREATE) {
522      /* HDF4 will be used ! */
523      hdf_type=1;
524      filename = strdup(userfilename);
525    } else if (my_am==NXACC_CREATE4) {
526      /* HDF4 will be used ! */
527      hdf_type=1;   
528      filename = strdup(userfilename);
529    } else if (my_am==NXACC_CREATE5) {
530      /* HDF5 will be used ! */
531      hdf_type=2;   
532      filename = strdup(userfilename);
533    } else if (my_am==NXACC_CREATEXML) {
534      /* XML will be used ! */
535      hdf_type=3;   
536      filename = strdup(userfilename);
537    } else {
538      filename = locateNexusFileInPath((char *)userfilename);
539      if(filename == NULL){
540        NXReportError("Out of memory in NeXus-API");
541        free(fHandle);
542        return NX_ERROR;
543      }
544      /* check file type hdf4/hdf5/XML for reading */
545      iRet = determineFileType(filename);
546      if(iRet < 0) {
547        snprintf(error,1023,"failed to open %s for reading",
548                 filename);
549        NXReportError(error);
550        free(filename);
551        return NX_ERROR;
552      }
553      if(iRet == 0){
554        snprintf(error,1023,"failed to determine filetype for %s ",
555                 filename);
556        NXReportError(error);
557        free(filename);
558        free(fHandle);
559        return NX_ERROR;
560      }
561      hdf_type = iRet;
562    }
563    if(filename == NULL){
564        NXReportError("Out of memory in NeXus-API");
565        return NX_ERROR;
566    }
567
568    if (hdf_type==1) {
569      /* HDF4 type */
570#ifdef HDF4
571      retstat = NX4open((const char *)filename,am,&hdf4_handle);
572      if(retstat != NX_OK){
573        free(fHandle);
574        free(filename);
575        return retstat;
576      }
577      fHandle->pNexusData=hdf4_handle;
578      NX4assignFunctions(fHandle);
579      pushFileStack(fileStack,fHandle,filename);
580#else
581      NXReportError(
582         "ERROR: Attempt to create HDF4 file when not linked with HDF4");
583      retstat = NX_ERROR;
584#endif /* HDF4 */
585      free(filename);
586      return retstat; 
587    } else if (hdf_type==2) {
588      /* HDF5 type */
589#ifdef HDF5
590      retstat = NX5open(filename,am,&hdf5_handle);
591      if(retstat != NX_OK){
592        free(fHandle);
593        free(filename);
594        return retstat;
595      }
596      fHandle->pNexusData=hdf5_handle;
597      NX5assignFunctions(fHandle);
598      pushFileStack(fileStack,fHandle, filename);
599#else
600      NXReportError(
601         "ERROR: Attempt to create HDF5 file when not linked with HDF5");
602      retstat = NX_ERROR;
603#endif /* HDF5 */
604      free(filename);
605      return retstat;
606    } else if(hdf_type == 3){
607      /*
608        XML type
609      */
610#ifdef NXXML
611      retstat = NXXopen(filename,am,&xmlHandle);
612      if(retstat != NX_OK){
613        free(fHandle);
614        free(filename);
615        return retstat;
616      }
617      fHandle->pNexusData=xmlHandle;
618      NXXassignFunctions(fHandle);
619      pushFileStack(fileStack,fHandle, filename);
620#else
621      NXReportError(
622         "ERROR: Attempt to create XML file when not linked with XML");
623      retstat = NX_ERROR;
624#endif
625    } else {
626      NXReportError(
627          "ERROR: Format not readable by this NeXus library");
628      retstat = NX_ERROR;
629    }
630    if (filename != NULL) {
631        free(filename); 
632    }
633    return retstat;
634  }
635
636static NXstatus   NXinternalopen(CONSTCHAR *userfilename, NXaccess am, pFileStack fileStack)
637{
638    return LOCKED_CALL(NXinternalopenImpl(userfilename, am, fileStack));
639}
640
641
642NXstatus  NXreopen(NXhandle pOrigHandle, NXhandle* pNewHandle)
643{
644      pFileStack newFileStack;
645          pFileStack origFileStack = (pFileStack)pOrigHandle;
646        pNexusFunction fOrigHandle = NULL, fNewHandle = NULL;
647          *pNewHandle = NULL;
648          newFileStack = makeFileStack();
649          if(newFileStack == NULL){
650          NXReportError ("ERROR: no memory to create filestack");
651          return NX_ERROR;
652      }
653          // The code below will only open the last file on a stack
654          // for the moment raise an error, but this behaviour may be OK
655          if (fileStackDepth(origFileStack) > 0)   
656          {
657                NXReportError (
658          "ERROR: handle stack referes to many files - cannot reopen");
659                  return NX_ERROR;
660          }
661          fOrigHandle = peekFileOnStack(origFileStack);
662          if (fOrigHandle->nxreopen == NULL)
663          {
664                NXReportError (
665          "ERROR: NXreopen not implemented for this underlying file format");
666                  return NX_ERROR;
667          }
668          fNewHandle = (NexusFunction*)malloc(sizeof(NexusFunction));
669          memcpy(fNewHandle, fOrigHandle, sizeof(NexusFunction));
670          LOCKED_CALL(fNewHandle->nxreopen( fOrigHandle->pNexusData, &(fNewHandle->pNexusData) ));
671          pushFileStack( newFileStack, fNewHandle, peekFilenameOnStack(origFileStack) );
672          *pNewHandle = newFileStack;
673          return NX_OK;
674}
675
676
677/* ------------------------------------------------------------------------- */
678
679  NXstatus  NXclose (NXhandle *fid)
680  { 
681    NXhandle hfil; 
682    int status;
683    pFileStack fileStack = NULL;
684    pNexusFunction pFunc=NULL;
685    if (*fid == NULL)
686    {
687        return NX_OK;
688    }
689    fileStack = (pFileStack)*fid;
690    pFunc = peekFileOnStack(fileStack);
691    hfil = pFunc->pNexusData;
692    status = LOCKED_CALL(pFunc->nxclose(&hfil));
693    pFunc->pNexusData = hfil;
694    free(pFunc);
695    popFileStack(fileStack);
696    if(fileStackDepth(fileStack) < 0){
697      killFileStack(fileStack);
698      *fid = NULL;
699    }
700    /* we can't set fid to NULL always as the handle points to a stack of files for external file support */
701    /*
702    Fortify_CheckAllMemory();
703    */
704
705    return status;   
706  }
707
708  /*-----------------------------------------------------------------------*/   
709
710  NXstatus  NXmakegroup (NXhandle fid, CONSTCHAR *name, CONSTCHAR *nxclass) 
711  {
712     char buffer[256];
713     pNexusFunction pFunc = handleToNexusFunc(fid);
714     if ( pFunc->checkNameSyntax && (nxclass != NULL) /* && !strncmp("NX", nxclass, 2) */ && !validNXName(name, 0) )
715     {
716        sprintf(buffer, "ERROR: invalid characters in group name \"%s\"", name);
717        NXReportError(buffer);
718        return NX_ERROR;
719     }
720     return LOCKED_CALL(pFunc->nxmakegroup(pFunc->pNexusData, name, nxclass));   
721  }
722  /*------------------------------------------------------------------------*/
723static int analyzeNapimount(char *napiMount, char *extFile, int extFileLen, 
724                            char *extPath, int extPathLen){
725  char *pPtr = NULL, *path = NULL;
726  int length;
727
728  memset(extFile,0,extFileLen);
729  memset(extPath,0,extPathLen);
730  pPtr = strstr(napiMount,"nxfile://");
731  if(pPtr == NULL){
732    return NXBADURL;
733  }
734  path = strrchr(napiMount,'#');
735  if(path == NULL){
736    length = strlen(napiMount) - 9;
737    if(length > extFileLen){
738      NXReportError("ERROR: internal errro with external linking");
739      return NXBADURL;
740    }
741    memcpy(extFile,pPtr+9,length);
742    strcpy(extPath,"/");
743    return NXFILE;
744  } else {
745    pPtr += 9;
746    length = path - pPtr;
747    if(length > extFileLen){
748      NXReportError("ERROR: internal errro with external linking");
749      return NXBADURL;
750    }
751    memcpy(extFile,pPtr,length);
752    length = strlen(path-1);
753    if(length > extPathLen){
754      NXReportError("ERROR: internal error with external linking");
755      return NXBADURL;
756    }
757    strcpy(extPath,path+1);
758    return NXFILE;
759  }
760  return NXBADURL;
761}
762  /*------------------------------------------------------------------------*/
763
764  NXstatus  NXopengroup (NXhandle fid, CONSTCHAR *name, CONSTCHAR *nxclass)
765  {
766    int status, attStatus, type = NX_CHAR, length = 1023;
767    NXaccess access = NXACC_READ;
768    NXlink breakID;
769    pFileStack fileStack;   
770    char nxurl[1024], exfile[512], expath[512];
771    pNexusFunction pFunc = NULL;
772
773    fileStack = (pFileStack)fid;
774    pFunc = handleToNexusFunc(fid);
775
776    status = LOCKED_CALL(pFunc->nxopengroup(pFunc->pNexusData, name, nxclass)); 
777    if(status == NX_OK){
778      pushPath(fileStack,name);
779    }
780    NXMDisableErrorReporting();
781    attStatus = NXgetattr(fid,"napimount",nxurl,&length, &type);
782    NXMEnableErrorReporting();
783    if(attStatus == NX_OK){
784      /*
785        this is an external linking group
786      */
787      status = analyzeNapimount(nxurl,exfile,511,expath,511);
788      if(status == NXBADURL){
789        return NX_ERROR;
790      }
791      status = NXinternalopen(exfile, access, fileStack);
792      if(status == NX_ERROR){
793        return status;
794      }
795      status = NXopenpath(fid, expath);
796      NXgetgroupID(fid,&breakID);
797      setCloseID(fileStack,breakID);
798    }
799   
800    return status;
801  } 
802
803  /* ------------------------------------------------------------------- */
804
805  NXstatus  NXclosegroup (NXhandle fid)
806  {
807    int status;
808    pFileStack fileStack = NULL;
809    NXlink closeID, currentID;
810
811    pNexusFunction pFunc = handleToNexusFunc(fid);
812    fileStack = (pFileStack)fid;
813    if(fileStackDepth(fileStack) == 0){
814      status = LOCKED_CALL(pFunc->nxclosegroup(pFunc->pNexusData)); 
815      if(status == NX_OK){
816        popPath(fileStack);
817      }
818      return status;
819    } else {
820      /* we have to check for leaving an external file */
821      NXgetgroupID(fid,&currentID);
822      peekIDOnStack(fileStack,&closeID);
823      if(NXsameID(fid,&closeID,&currentID) == NX_OK){
824        NXclose(&fid);
825        status = NXclosegroup(fid);
826      } else {
827        status = LOCKED_CALL(pFunc->nxclosegroup(pFunc->pNexusData));
828        if(status == NX_OK){
829          popPath(fileStack);
830        }
831      }
832      return status;
833    }
834  }   
835
836  /* --------------------------------------------------------------------- */
837 
838  NXstatus  NXmakedata (NXhandle fid, CONSTCHAR *name, int datatype, 
839                                  int rank, int dimensions[])
840  {
841          int status;
842          int64_t* dims64 = dupDimsArray(dimensions, rank);
843      status = NXmakedata64(fid, name, datatype, rank, dims64);
844          free(dims64);
845          return status;
846  }
847
848  NXstatus  NXmakedata64 (NXhandle fid, CONSTCHAR *name, int datatype, 
849                                  int rank, int64_t dimensions[])
850  {
851    char buffer[256];
852    pNexusFunction pFunc = handleToNexusFunc(fid);
853    if ( pFunc->checkNameSyntax && !validNXName(name, 0) )
854    {
855        sprintf(buffer, "ERROR: invalid characters in dataset name \"%s\"", name);
856        NXReportError(buffer);
857        return NX_ERROR;
858    }
859    return LOCKED_CALL(pFunc->nxmakedata64(pFunc->pNexusData, name, datatype, rank, dimensions)); 
860  }
861
862 /* --------------------------------------------------------------------- */
863 
864  NXstatus  NXcompmakedata (NXhandle fid, CONSTCHAR *name, int datatype, 
865                           int rank, int dimensions[], int compress_type, int chunk_size[])
866  {
867          int status;
868          int64_t* dims64 = dupDimsArray(dimensions, rank);
869          int64_t* chunk64 = dupDimsArray(chunk_size, rank);
870      status = NXcompmakedata64(fid, name, datatype, rank, dims64, compress_type, chunk64);
871          free(dims64);
872          free(chunk64);
873          return status;
874  } 
875 
876  NXstatus  NXcompmakedata64 (NXhandle fid, CONSTCHAR *name, int datatype, 
877                           int rank, int64_t dimensions[], int compress_type, int64_t chunk_size[])
878  {
879    char buffer[256];
880    pNexusFunction pFunc = handleToNexusFunc(fid); 
881    if ( pFunc->checkNameSyntax && !validNXName(name, 0) )
882    {
883        sprintf(buffer, "ERROR: invalid characters in dataset name \"%s\"", name);
884        NXReportError(buffer);
885        return NX_ERROR;
886    }
887    return LOCKED_CALL(pFunc->nxcompmakedata64 (pFunc->pNexusData, name, datatype, rank, dimensions, compress_type, chunk_size)); 
888  } 
889 
890  /* --------------------------------------------------------------------- */
891
892  NXstatus  NXcompress (NXhandle fid, int compress_type)
893  {
894    pNexusFunction pFunc = handleToNexusFunc(fid); 
895    return LOCKED_CALL(pFunc->nxcompress (pFunc->pNexusData, compress_type)); 
896  }
897 
898 
899  /* --------------------------------------------------------------------- */
900 
901  NXstatus  NXopendata (NXhandle fid, CONSTCHAR *name)
902  {
903    int status, attStatus, type = NX_CHAR, length = 1023;
904    NXaccess access = NXACC_READ;
905    NXlink breakID;
906    pFileStack fileStack;
907    char nxurl[1024], exfile[512], expath[512];
908    pNexusFunction pFunc = NULL;
909
910    fileStack = (pFileStack)fid;
911    pFunc = handleToNexusFunc(fid);
912    status = LOCKED_CALL(pFunc->nxopendata(pFunc->pNexusData, name)); 
913
914    if(status == NX_OK){
915      pushPath(fileStack,name);
916    }
917
918    NXMDisableErrorReporting();
919    attStatus = NXgetattr(fid,"napimount",nxurl,&length, &type);
920    NXMEnableErrorReporting();
921    if(attStatus == NX_OK){
922      /*
923        this is an external linking group
924      */
925      status = analyzeNapimount(nxurl,exfile,511,expath,511);
926      if(status == NXBADURL){
927        return NX_ERROR;
928      }
929      status = NXinternalopen(exfile, access, fileStack);
930      if(status == NX_ERROR){
931        return status;
932      }
933      status = NXopenpath(fid,expath);
934      NXgetdataID(fid,&breakID);
935      setCloseID(fileStack,breakID);
936    }
937
938    return status;
939  } 
940
941  /* ----------------------------------------------------------------- */
942   
943  NXstatus NXclosedata (NXhandle fid)
944  { 
945    int status;
946    pFileStack fileStack = NULL;
947    NXlink closeID, currentID;
948
949    pNexusFunction pFunc = handleToNexusFunc(fid);
950    fileStack = (pFileStack)fid;
951
952    if(fileStackDepth(fileStack) == 0){
953      status = LOCKED_CALL(pFunc->nxclosedata(pFunc->pNexusData));
954      if(status == NX_OK){
955        popPath(fileStack);
956      }
957      return status;
958    } else {
959      /* we have to check for leaving an external file */
960      NXgetdataID(fid,&currentID);
961      peekIDOnStack(fileStack,&closeID);
962      if(NXsameID(fid,&closeID,&currentID) == NX_OK){
963        NXclose(&fid);
964        status = NXclosedata(fid);
965      } else {
966        status = LOCKED_CALL(pFunc->nxclosedata(pFunc->pNexusData));
967        if(status == NX_OK){
968          popPath(fileStack);
969        }
970      }
971      return status;
972    }
973  }
974
975  /* ------------------------------------------------------------------- */
976
977  NXstatus  NXputdata (NXhandle fid, const void *data)
978  {
979    pNexusFunction pFunc = handleToNexusFunc(fid);
980    return LOCKED_CALL(pFunc->nxputdata(pFunc->pNexusData, data));
981  }
982
983  /* ------------------------------------------------------------------- */
984
985  NXstatus  NXputattr (NXhandle fid, CONSTCHAR *name, const void *data, 
986                                  int datalen, int iType)
987  {
988    char buffer[256];
989    pNexusFunction pFunc = handleToNexusFunc(fid);
990    if (datalen > 1 && iType != NX_CHAR)
991    {
992        NXReportError("NXputattr: numeric arrays are not allowed as attributes - only character strings and single numbers");
993        return NX_ERROR;
994    }
995    if ( pFunc->checkNameSyntax && !validNXName(name, 0) )
996    {
997        sprintf(buffer, "ERROR: invalid characters in attribute name \"%s\"", name);
998        NXReportError(buffer);
999        return NX_ERROR;
1000    }
1001    return LOCKED_CALL(pFunc->nxputattr(pFunc->pNexusData, name, data, datalen, iType));
1002  }
1003
1004  /* ------------------------------------------------------------------- */
1005
1006  NXstatus  NXputslab (NXhandle fid, const void *data, const int iStart[], const int iSize[])
1007  {
1008          int i, iType, rank;
1009          int64_t iStart64[NX_MAXRANK], iSize64[NX_MAXRANK];
1010          if (NXgetinfo64(fid, &rank, iStart64, &iType) != NX_OK)
1011          {
1012                  return NX_ERROR;
1013          }
1014          for(i=0; i < rank; ++i)
1015          {
1016                  iStart64[i] = iStart[i];
1017                  iSize64[i] = iSize[i];
1018          }
1019          return NXputslab64(fid, data, iStart64, iSize64);
1020  }
1021
1022  NXstatus  NXputslab64 (NXhandle fid, const void *data, const int64_t iStart[], const int64_t iSize[])
1023  {
1024    pNexusFunction pFunc = handleToNexusFunc(fid);
1025    return LOCKED_CALL(pFunc->nxputslab64(pFunc->pNexusData, data, iStart, iSize));
1026  }
1027
1028  /* ------------------------------------------------------------------- */
1029
1030  NXstatus  NXgetdataID (NXhandle fid, NXlink* sRes)
1031  { 
1032    pNexusFunction pFunc = handleToNexusFunc(fid);
1033    return LOCKED_CALL(pFunc->nxgetdataID(pFunc->pNexusData, sRes));
1034  }
1035
1036
1037  /* ------------------------------------------------------------------- */
1038
1039  NXstatus  NXmakelink (NXhandle fid, NXlink* sLink)
1040  {
1041    pNexusFunction pFunc = handleToNexusFunc(fid);
1042    return LOCKED_CALL(pFunc->nxmakelink(pFunc->pNexusData, sLink));
1043  }
1044  /* ------------------------------------------------------------------- */
1045
1046  NXstatus  NXmakenamedlink (NXhandle fid, CONSTCHAR *newname,  NXlink* sLink)
1047  {
1048    char buffer[256];
1049    pNexusFunction pFunc = handleToNexusFunc(fid);
1050    if ( pFunc->checkNameSyntax && !validNXName(newname, 0) )
1051    {
1052        sprintf(buffer, "ERROR: invalid characters in link name \"%s\"", newname);
1053        NXReportError(buffer);
1054        return NX_ERROR;
1055    }
1056    return LOCKED_CALL(pFunc->nxmakenamedlink(pFunc->pNexusData, newname, sLink));
1057  }
1058  /* --------------------------------------------------------------------*/
1059  NXstatus  NXopensourcegroup(NXhandle fid)
1060  {
1061    char target_path[512];
1062    int status, type = NX_CHAR, length = 511;
1063
1064    status = NXgetattr(fid,"target",target_path,&length,&type);
1065    if(status != NX_OK)
1066    {
1067      NXReportError("ERROR: item not linked");
1068      return NX_ERROR;
1069    }
1070    return NXopengrouppath(fid,target_path);
1071  }
1072  /*----------------------------------------------------------------------*/
1073
1074  NXstatus  NXflush(NXhandle *pHandle)
1075  {
1076    NXhandle hfil; 
1077    pFileStack fileStack = NULL;
1078    int status;
1079   
1080    pNexusFunction pFunc=NULL;
1081    fileStack = (pFileStack)*pHandle;
1082    pFunc = peekFileOnStack(fileStack);
1083    hfil = pFunc->pNexusData;
1084    status =  LOCKED_CALL(pFunc->nxflush(&hfil));
1085    pFunc->pNexusData = hfil;
1086    return status; 
1087  }
1088
1089
1090  /*-------------------------------------------------------------------------*/
1091 
1092 
1093  NXstatus  NXmalloc (void** data, int rank, 
1094                                   const int dimensions[], int datatype)
1095  {
1096          int status;
1097          int64_t* dims64 = dupDimsArray(dimensions, rank);
1098          status = NXmalloc64(data, rank, dims64, datatype);
1099          free(dims64);
1100          return status;
1101  }
1102
1103  NXstatus  NXmalloc64 (void** data, int rank, 
1104                                   const int64_t dimensions[], int datatype)
1105  {
1106    int i;
1107    size_t size = 1;
1108    *data = NULL;
1109    for(i=0; i<rank; i++)
1110        {
1111        size *= dimensions[i];
1112        }
1113    if ((datatype == NX_CHAR) || (datatype == NX_INT8) 
1114            || (datatype == NX_UINT8)) {
1115        /* allow for terminating \0 */
1116      size += 2;
1117      }
1118      else if ((datatype == NX_INT16) || (datatype == NX_UINT16)) {
1119      size *= 2;
1120      }   
1121      else if ((datatype == NX_INT32) || (datatype == NX_UINT32) 
1122               || (datatype == NX_FLOAT32)) {
1123        size *= 4;
1124      }   
1125      else if ((datatype == NX_INT64) || (datatype == NX_UINT64)){
1126        size *= 8;
1127      }   
1128      else if (datatype == NX_FLOAT64) {
1129        size *= 8;
1130      }
1131      else {
1132        NXReportError(
1133                        "ERROR: NXmalloc - unknown data type in array");
1134        return NX_ERROR;
1135    }
1136    *data = (void*)malloc(size);
1137     memset(*data,0,size);
1138    return NX_OK;
1139  }
1140   
1141  /*-------------------------------------------------------------------------*/
1142
1143  NXstatus  NXfree (void** data)
1144  {
1145    if (data == NULL) {
1146       NXReportError( "ERROR: passing NULL to NXfree");
1147       return NX_ERROR;
1148    }
1149    if (*data == NULL) {
1150       NXReportError("ERROR: passing already freed pointer to NXfree");
1151       return NX_ERROR;
1152    }
1153    free(*data);
1154    *data = NULL;
1155    return NX_OK;
1156  }
1157
1158  /* --------------------------------------------------------------------- */
1159           
1160 
1161  NXstatus  NXgetnextentry (NXhandle fid, NXname name, NXname nxclass, int *datatype)
1162  {
1163    pNexusFunction pFunc = handleToNexusFunc(fid);
1164    return LOCKED_CALL(pFunc->nxgetnextentry(pFunc->pNexusData, name, nxclass, datatype)); 
1165  }
1166/*----------------------------------------------------------------------*/
1167/*
1168**  TRIM.C - Remove leading, trailing, & excess embedded spaces
1169**
1170**  public domain by Bob Stout
1171*/
1172#define NUL '\0'
1173
1174char *nxitrim(char *str)
1175{
1176      char *ibuf = str;
1177      int i = 0;
1178
1179      /*
1180      **  Trap NULL
1181      */
1182
1183      if (str)
1184      {
1185            /*
1186            **  Remove leading spaces (from RMLEAD.C)
1187            */
1188
1189            for (ibuf = str; *ibuf && isspace(*ibuf); ++ibuf)
1190                  ;
1191            str = ibuf;
1192
1193            /*
1194            **  Remove trailing spaces (from RMTRAIL.C)
1195            */
1196            i = strlen(str);
1197            while (--i >= 0)
1198            {
1199                  if (!isspace(str[i]))
1200                        break;
1201            }
1202            str[++i] = NUL;
1203      }
1204      return str;
1205}
1206  /*-------------------------------------------------------------------------*/
1207
1208  NXstatus  NXgetdata (NXhandle fid, void *data)
1209  {
1210    int status, type, rank;
1211        int64_t iDim[NX_MAXRANK];
1212    char *pPtr, *pPtr2;
1213
1214    pNexusFunction pFunc = handleToNexusFunc(fid);
1215    status = LOCKED_CALL(pFunc->nxgetinfo64(pFunc->pNexusData, &rank, iDim, &type)); /* unstripped size if string */
1216    /* only strip one dimensional strings */
1217    if ( (type == NX_CHAR) && (pFunc->stripFlag == 1) && (rank == 1) )
1218    {
1219                pPtr = (char*)malloc(iDim[0]+5);
1220        memset(pPtr, 0, iDim[0]+5);
1221        status = LOCKED_CALL(pFunc->nxgetdata(pFunc->pNexusData, pPtr)); 
1222                pPtr2 = nxitrim(pPtr);
1223                strncpy((char*)data, pPtr2, strlen(pPtr2)); /* not NULL terminated by default */
1224                free(pPtr);
1225    }
1226    else
1227    {
1228        status = LOCKED_CALL(pFunc->nxgetdata(pFunc->pNexusData, data)); 
1229    }
1230    return status;
1231  }
1232/*---------------------------------------------------------------------------*/
1233  NXstatus  NXgetrawinfo64 (NXhandle fid, int *rank, 
1234                                    int64_t dimension[], int *iType)
1235  {
1236    pNexusFunction pFunc = handleToNexusFunc(fid);
1237    return LOCKED_CALL(pFunc->nxgetinfo64(pFunc->pNexusData, rank, dimension, iType));
1238  }
1239
1240  NXstatus  NXgetrawinfo (NXhandle fid, int *rank, 
1241                                    int dimension[], int *iType)
1242  {
1243    int i, status;
1244        int64_t dims64[NX_MAXRANK];
1245    pNexusFunction pFunc = handleToNexusFunc(fid);
1246    status = LOCKED_CALL(pFunc->nxgetinfo64(pFunc->pNexusData, rank, dims64, iType));
1247        for(i=0; i < *rank; ++i)
1248        {
1249                dimension[i] = dims64[i];
1250        }
1251    return status;
1252  }
1253  /*-------------------------------------------------------------------------*/
1254 
1255  NXstatus  NXgetinfo (NXhandle fid, int *rank, 
1256                                    int dimension[], int *iType)
1257  {
1258          int i, status;
1259          int64_t dims64[NX_MAXRANK];
1260          status = NXgetinfo64(fid, rank, dims64, iType);
1261          for(i=0; i < *rank; ++i)
1262          {
1263                  dimension[i] = dims64[i];
1264          }
1265          return status;
1266  }
1267
1268  NXstatus  NXgetinfo64 (NXhandle fid, int *rank, 
1269                                    int64_t dimension[], int *iType)
1270  {
1271    int status;
1272    char *pPtr = NULL;
1273    pNexusFunction pFunc = handleToNexusFunc(fid);
1274        *rank = 0;
1275    status = LOCKED_CALL(pFunc->nxgetinfo64(pFunc->pNexusData, rank, dimension, iType));
1276    /*
1277      the length of a string may be trimmed....
1278    */
1279    /* only strip one dimensional strings */
1280    if((*iType == NX_CHAR) && (pFunc->stripFlag == 1) && (*rank == 1)){
1281      pPtr = (char *)malloc((dimension[0]+1)*sizeof(char));
1282      if(pPtr != NULL){
1283        memset(pPtr,0,(dimension[0]+1)*sizeof(char));
1284        LOCKED_CALL(pFunc->nxgetdata(pFunc->pNexusData, pPtr));
1285        dimension[0] = strlen(nxitrim(pPtr));
1286        free(pPtr);
1287      }
1288    } 
1289    return status;
1290  }
1291 
1292  /*-------------------------------------------------------------------------*/
1293
1294  NXstatus  NXgetslab (NXhandle fid, void *data, 
1295                                    const int iStart[], const int iSize[])
1296  {
1297          int i, iType, rank;
1298          int64_t iStart64[NX_MAXRANK], iSize64[NX_MAXRANK];
1299          if (NXgetinfo64(fid, &rank, iStart64, &iType) != NX_OK)
1300          {
1301                  return NX_ERROR;
1302          }
1303          for(i=0; i < rank; ++i)
1304          {
1305                  iStart64[i] = iStart[i];
1306                  iSize64[i] = iSize[i];
1307          }
1308          return NXgetslab64(fid, data, iStart64, iSize64);
1309  }
1310 
1311  NXstatus  NXgetslab64 (NXhandle fid, void *data, 
1312                                    const int64_t iStart[], const int64_t iSize[])
1313  {
1314    pNexusFunction pFunc = handleToNexusFunc(fid);
1315    return LOCKED_CALL(pFunc->nxgetslab64(pFunc->pNexusData, data, iStart, iSize));
1316  }
1317 
1318  /*-------------------------------------------------------------------------*/
1319
1320  NXstatus  NXgetnextattr (NXhandle fileid, NXname pName,
1321                                     int *iLength, int *iType)
1322  {
1323    pNexusFunction pFunc = handleToNexusFunc(fileid);
1324    return LOCKED_CALL(pFunc->nxgetnextattr(pFunc->pNexusData, pName, iLength, iType)); 
1325  }
1326 
1327
1328  /*-------------------------------------------------------------------------*/
1329
1330  NXstatus  NXgetattr (NXhandle fid, char *name, void *data, int* datalen, int* iType)
1331  {
1332    pNexusFunction pFunc = handleToNexusFunc(fid);
1333    return LOCKED_CALL(pFunc->nxgetattr(pFunc->pNexusData, name, data, datalen, iType)); 
1334  }
1335
1336
1337  /*-------------------------------------------------------------------------*/
1338
1339  NXstatus  NXgetattrinfo (NXhandle fid, int *iN)
1340  {
1341    pNexusFunction pFunc = handleToNexusFunc(fid);
1342    return LOCKED_CALL(pFunc->nxgetattrinfo(pFunc->pNexusData, iN)); 
1343  }
1344
1345
1346  /*-------------------------------------------------------------------------*/
1347
1348  NXstatus  NXgetgroupID (NXhandle fileid, NXlink* sRes)
1349  {
1350    pNexusFunction pFunc = handleToNexusFunc(fileid);
1351    return LOCKED_CALL(pFunc->nxgetgroupID(pFunc->pNexusData, sRes)); 
1352  }
1353
1354  /*-------------------------------------------------------------------------*/
1355
1356  NXstatus  NXgetgroupinfo (NXhandle fid, int *iN, NXname pName, NXname pClass)
1357  {
1358    pNexusFunction pFunc = handleToNexusFunc(fid);
1359    return LOCKED_CALL(pFunc->nxgetgroupinfo(pFunc->pNexusData, iN, pName, pClass)); 
1360  }
1361
1362 
1363  /*-------------------------------------------------------------------------*/
1364
1365  NXstatus  NXsameID (NXhandle fileid, NXlink* pFirstID, NXlink* pSecondID)
1366  {
1367    pNexusFunction pFunc = handleToNexusFunc(fileid);
1368    return LOCKED_CALL(pFunc->nxsameID(pFunc->pNexusData, pFirstID, pSecondID)); 
1369  }
1370
1371  /*-------------------------------------------------------------------------*/
1372 
1373  NXstatus  NXinitattrdir (NXhandle fid)
1374  {
1375    pNexusFunction pFunc = handleToNexusFunc(fid);
1376    return LOCKED_CALL(pFunc->nxinitattrdir(pFunc->pNexusData));
1377  }
1378  /*-------------------------------------------------------------------------*/
1379 
1380  NXstatus  NXsetnumberformat (NXhandle fid, 
1381                                            int type, char *format)
1382  {
1383    pNexusFunction pFunc = handleToNexusFunc(fid);
1384    if(pFunc->nxsetnumberformat != NULL)
1385    {
1386      return LOCKED_CALL(pFunc->nxsetnumberformat(pFunc->pNexusData,type,format));
1387    }
1388    else
1389    {
1390      /*
1391        silently ignore this. Most NeXus file formats do not require
1392        this
1393      */
1394      return NX_OK;
1395    }
1396  }
1397 
1398 
1399  /*-------------------------------------------------------------------------*/
1400 
1401  NXstatus  NXinitgroupdir (NXhandle fid)
1402  {
1403    pNexusFunction pFunc = handleToNexusFunc(fid);
1404    return LOCKED_CALL(pFunc->nxinitgroupdir(pFunc->pNexusData));
1405  }
1406/*----------------------------------------------------------------------*/
1407 NXstatus  NXinquirefile(NXhandle handle, char *filename, 
1408                                int filenameBufferLength){
1409  pFileStack fileStack;
1410  char *pPtr = NULL;
1411  int length, status;
1412
1413  pNexusFunction pFunc = handleToNexusFunc(handle);
1414   if (pFunc->nxnativeinquirefile != NULL) {
1415
1416        status = LOCKED_CALL(pFunc->nxnativeinquirefile(pFunc->pNexusData, filename, filenameBufferLength));
1417        if (status < 0) {
1418                return NX_ERROR;
1419        } else {
1420                return NX_OK;
1421        }
1422
1423   }
1424
1425  fileStack = (pFileStack)handle;
1426  pPtr = peekFilenameOnStack(fileStack);
1427  if(pPtr != NULL){
1428    length = strlen(pPtr);
1429    if(length > filenameBufferLength){
1430      length = filenameBufferLength -1;
1431    }
1432    memset(filename,0,filenameBufferLength);
1433    memcpy(filename,pPtr, length);
1434    return NX_OK;
1435  } else {
1436    return NX_ERROR;
1437  }
1438}
1439/*------------------------------------------------------------------------*/
1440NXstatus  NXisexternalgroup(NXhandle fid, CONSTCHAR *name, CONSTCHAR *nxclass, 
1441                            char *url, int urlLen){
1442  int status, attStatus, length = 1023, type = NX_CHAR;
1443  char nxurl[1024];
1444
1445  pNexusFunction pFunc = handleToNexusFunc(fid);
1446
1447   if (pFunc->nxnativeisexternallink != NULL) {
1448        status = LOCKED_CALL(pFunc->nxnativeisexternallink(pFunc->pNexusData, name, url, urlLen));
1449        if (status == NX_OK) {
1450                return NX_OK;
1451        }
1452        // need to continue, could still be old style link
1453   }
1454
1455  status = LOCKED_CALL(pFunc->nxopengroup(pFunc->pNexusData, name, nxclass));
1456  if(status != NX_OK){
1457    return status;
1458  }
1459  NXMDisableErrorReporting();
1460  attStatus = NXgetattr(fid,"napimount",nxurl,&length, &type);
1461  NXMEnableErrorReporting();
1462  LOCKED_CALL(pFunc->nxclosegroup(pFunc->pNexusData));
1463  if(attStatus == NX_OK){
1464    length = strlen(nxurl);
1465    if(length >= urlLen){
1466      length = urlLen - 1;
1467    }
1468    memset(url,0,urlLen);
1469    memcpy(url,nxurl,length);
1470    return attStatus;
1471  } else {
1472    return NX_ERROR;
1473  }
1474}
1475/*------------------------------------------------------------------------*/
1476NXstatus  NXisexternaldataset(NXhandle fid, CONSTCHAR *name,
1477                            char *url, int urlLen){
1478  int status, attStatus, length = 1023, type = NX_CHAR;
1479  char nxurl[1024];
1480
1481  pNexusFunction pFunc = handleToNexusFunc(fid);
1482
1483   if (pFunc->nxnativeisexternallink != NULL) {
1484        status = LOCKED_CALL(pFunc->nxnativeisexternallink(pFunc->pNexusData, name, url, urlLen));
1485        if (status == NX_OK) {
1486                return NX_OK;
1487        }
1488        // need to continue, could still be old style link
1489   }
1490
1491  status = LOCKED_CALL(pFunc->nxopendata(pFunc->pNexusData, name));
1492  if(status != NX_OK){
1493    return status;
1494  }
1495  NXMDisableErrorReporting();
1496  attStatus = NXgetattr(fid,"napimount",nxurl,&length, &type);
1497  NXMEnableErrorReporting();
1498  LOCKED_CALL(pFunc->nxclosedata(pFunc->pNexusData));
1499  if(attStatus == NX_OK){
1500    length = strlen(nxurl);
1501    if(length >= urlLen){
1502      length = urlLen - 1;
1503    }
1504    memset(url,0,urlLen);
1505    memcpy(url,nxurl,length);
1506    return attStatus;
1507  } else {
1508    return NX_ERROR;
1509  }
1510}
1511/*------------------------------------------------------------------------*/
1512NXstatus  NXlinkexternal(NXhandle fid, CONSTCHAR *name, CONSTCHAR *nxclass, CONSTCHAR *url) {
1513  int status, type = NX_CHAR, length=1024, urllen;
1514  char nxurl[1024], exfile[512], expath[512];
1515  pNexusFunction pFunc = handleToNexusFunc(fid);
1516
1517  // in HDF5 we support external linking natively
1518  if (pFunc->nxnativeexternallink != NULL) {
1519        urllen = strlen(url);
1520        memset(nxurl, 0, length);
1521        if(urllen >= length){
1522          urllen = length - 1;
1523        }
1524        memcpy(nxurl, url, urllen);
1525        status = analyzeNapimount(nxurl,exfile,511,expath,511);
1526        if(status != NX_OK){
1527           return status;
1528        }
1529        status = LOCKED_CALL(pFunc->nxnativeexternallink(pFunc->pNexusData, name, exfile, expath));
1530        if(status != NX_OK){
1531           return status;
1532        }
1533        return NX_OK;
1534  }
1535
1536  NXMDisableErrorReporting();
1537  LOCKED_CALL(pFunc->nxmakegroup(pFunc->pNexusData,name,nxclass));
1538  NXMEnableErrorReporting();
1539
1540  status = LOCKED_CALL(pFunc->nxopengroup(pFunc->pNexusData,name,nxclass));
1541  if(status != NX_OK){
1542    return status;
1543  }
1544  length = strlen(url);
1545  status = NXputattr(fid, "napimount",url,length, type);
1546  if(status != NX_OK){
1547    return status;
1548  }
1549  LOCKED_CALL(pFunc->nxclosegroup(pFunc->pNexusData));
1550  return NX_OK;
1551}
1552/*------------------------------------------------------------------------*/
1553NXstatus  NXlinkexternaldataset(NXhandle fid, CONSTCHAR *name, 
1554                         CONSTCHAR *url){
1555  int status, type = NX_CHAR, length=1024, urllen;
1556  char nxurl[1024], exfile[512], expath[512];
1557  pNexusFunction pFunc = handleToNexusFunc(fid);
1558  int rank = 1;
1559  int64_t dims[1] = {1};
1560 
1561  //TODO cut and paste
1562
1563  // in HDF5 we support external linking natively
1564  if (pFunc->nxnativeexternallink != NULL) {
1565        urllen = strlen(url);
1566        memset(nxurl, 0, length);
1567        if(urllen > length){
1568          urllen = length - 1;
1569        }
1570        memcpy(nxurl, url, urllen);
1571        status = analyzeNapimount(nxurl,exfile,511,expath,511);
1572        if(status != NX_OK){
1573           return status;
1574        }
1575        status = LOCKED_CALL(pFunc->nxnativeexternallink(pFunc->pNexusData, name, exfile, expath));
1576        if(status != NX_OK){
1577           return status;
1578        }
1579        return NX_OK;
1580  }
1581
1582
1583  status = LOCKED_CALL(pFunc->nxmakedata64(pFunc->pNexusData, name, NX_CHAR, rank, dims));
1584  if(status != NX_OK){
1585    return status;
1586  }
1587  status = LOCKED_CALL(pFunc->nxopendata(pFunc->pNexusData, name));
1588  if(status != NX_OK){
1589    return status;
1590  }
1591  length = strlen(url);
1592  status = NXputattr(fid, "napimount",url,length, type);
1593  if(status != NX_OK){
1594    return status;
1595  }
1596  LOCKED_CALL(pFunc->nxclosedata(pFunc->pNexusData));
1597  return NX_OK;
1598}
1599/*------------------------------------------------------------------------
1600  Implementation of NXopenpath
1601  --------------------------------------------------------------------------*/
1602static int isDataSetOpen(NXhandle hfil)
1603{
1604  NXlink id;
1605 
1606  /*
1607    This uses the (sensible) feauture that NXgetdataID returns NX_ERROR
1608    when no dataset is open
1609  */
1610  if(NXgetdataID(hfil,&id) == NX_ERROR)
1611  {
1612    return 0;
1613  }
1614  else 
1615  {
1616    return 1;
1617  }
1618}
1619/*----------------------------------------------------------------------*/
1620static int isRoot(NXhandle hfil)
1621{
1622  NXlink id;
1623 
1624  /*
1625    This uses the feauture that NXgetgroupID returns NX_ERROR
1626    when we are at root level
1627  */
1628  if(NXgetgroupID(hfil,&id) == NX_ERROR)
1629  {
1630    return 1;
1631  }
1632  else 
1633  {
1634    return 0;
1635  }
1636}
1637/*--------------------------------------------------------------------
1638  copies the next path element into element.
1639  returns a pointer into path beyond the extracted path
1640  ---------------------------------------------------------------------*/
1641static char *extractNextPath(char *path, NXname element)
1642{
1643  char *pPtr, *pStart;
1644  int length;
1645
1646  pPtr = path;
1647  /*
1648    skip over leading /
1649  */
1650  if(*pPtr == '/')
1651  {
1652    pPtr++;
1653  }
1654  pStart = pPtr;
1655 
1656  /*
1657    find next /
1658  */
1659  pPtr = strchr(pStart,'/');
1660  if(pPtr == NULL)
1661  {
1662    /*
1663      this is the last path element
1664    */
1665    strcpy(element,pStart);
1666    return NULL;
1667  } else {
1668    length = pPtr - pStart;
1669    strncpy(element,pStart,length);
1670    element[length] = '\0';
1671  }
1672  return pPtr + 1;
1673}
1674/*-------------------------------------------------------------------*/
1675static NXstatus gotoRoot(NXhandle hfil)
1676{
1677    int status;
1678
1679    if(isDataSetOpen(hfil))
1680    {
1681      status = NXclosedata(hfil);
1682      if(status == NX_ERROR)
1683      {
1684        return status;
1685      }
1686    }
1687    while(!isRoot(hfil))
1688    {
1689      status = NXclosegroup(hfil);
1690      if(status == NX_ERROR)
1691      {
1692        return status;
1693      }
1694    }
1695    return NX_OK;
1696}
1697/*--------------------------------------------------------------------*/
1698static int isRelative(char *path)
1699{
1700  if(path[0] == '.' && path[1] == '.')
1701    return 1;
1702  else
1703    return 0;
1704}
1705/*------------------------------------------------------------------*/
1706static NXstatus moveOneDown(NXhandle hfil)
1707{
1708  if(isDataSetOpen(hfil))
1709  {
1710    return NXclosedata(hfil);
1711  } 
1712  else
1713  {
1714    return NXclosegroup(hfil);
1715  }
1716}
1717/*-------------------------------------------------------------------
1718  returns a pointer to the remaining path string to move up
1719  --------------------------------------------------------------------*/
1720static char *moveDown(NXhandle hfil, char *path, int *code)
1721{
1722  int status;
1723  char *pPtr;
1724
1725  *code = NX_OK;
1726
1727  if(path[0] == '/')
1728  {
1729    *code = gotoRoot(hfil);
1730    return path;
1731  } 
1732  else 
1733  {
1734    pPtr = path;
1735    while(isRelative(pPtr))
1736    {
1737      status = moveOneDown(hfil);
1738      if(status == NX_ERROR)
1739      {
1740        *code = status;
1741        return pPtr;
1742      }
1743      pPtr += 3;
1744    }
1745    return pPtr;
1746  }
1747} 
1748/*--------------------------------------------------------------------*/
1749static NXstatus stepOneUp(NXhandle hfil, char *name)
1750{
1751  int datatype;
1752  NXname name2, xclass;
1753  char pBueffel[256]; 
1754
1755  /*
1756    catch the case when we are there: i.e. no further stepping
1757    necessary. This can happen with paths like ../
1758  */
1759  if (strlen(name) < 1) {
1760      return NX_OK;
1761  }
1762 
1763  NXinitgroupdir(hfil);
1764
1765  while(NXgetnextentry(hfil, name2, xclass, &datatype) != NX_EOD)
1766  {
1767    if(strcmp(name2,name) == 0)
1768    {
1769      if(strcmp(xclass,"SDS") == 0)
1770      {
1771        return NXopendata(hfil,name);
1772      } else {
1773        return NXopengroup(hfil,name,xclass);
1774      }
1775    }
1776  }
1777  snprintf(pBueffel,255,"ERROR: NXopenpath cannot step into %s",name);
1778  NXReportError( pBueffel);
1779  return NX_ERROR;             
1780}
1781/*--------------------------------------------------------------------*/
1782static NXstatus stepOneGroupUp(NXhandle hfil, char *name)
1783{
1784  int datatype;
1785  NXname name2, xclass;
1786  char pBueffel[256]; 
1787
1788  /*
1789    catch the case when we are there: i.e. no further stepping
1790    necessary. This can happen with paths like ../
1791  */
1792  if(strlen(name) < 1)
1793  {
1794      return NX_OK;
1795  }
1796 
1797  NXinitgroupdir(hfil);
1798  while(NXgetnextentry(hfil,name2,xclass,&datatype) != NX_EOD)
1799  {
1800   
1801    if(strcmp(name2,name) == 0)
1802    {
1803      if(strcmp(xclass,"SDS") == 0) {
1804        return NX_EOD;
1805      } 
1806      else
1807      {
1808        return NXopengroup(hfil,name,xclass);
1809      }
1810    }
1811  }
1812  snprintf(pBueffel,255,"ERROR: NXopenpath cannot step into %s",name);
1813  NXReportError( pBueffel);
1814  return NX_ERROR;             
1815}
1816/*---------------------------------------------------------------------*/
1817NXstatus  NXopenpath(NXhandle hfil, CONSTCHAR *path)
1818{
1819  int status, run = 1;
1820  NXname pathElement;
1821  char *pPtr;
1822
1823  if(hfil == NULL || path == NULL)
1824  {
1825    NXReportError(
1826     "ERROR: NXopendata needs both a file handle and a path string");
1827    return NX_ERROR;
1828  }
1829
1830  pPtr = moveDown(hfil,(char *)path,&status);
1831  if(status != NX_OK)
1832  {
1833    NXReportError( 
1834                    "ERROR: NXopendata failed to move down in hierarchy");
1835    return status;
1836  }
1837
1838  while(run == 1)
1839  {
1840    pPtr = extractNextPath(pPtr, pathElement);
1841    status = stepOneUp(hfil,pathElement);
1842    if(status != NX_OK)
1843    {
1844      return status;
1845    }
1846    if(pPtr == NULL)
1847    {
1848      run = 0;
1849    }
1850  }
1851  return NX_OK;
1852}
1853/*---------------------------------------------------------------------*/
1854NXstatus  NXopengrouppath(NXhandle hfil, CONSTCHAR *path)
1855{
1856  int status, run = 1;
1857  NXname pathElement;
1858  char *pPtr;
1859
1860  if(hfil == NULL || path == NULL)
1861  {
1862    NXReportError(
1863     "ERROR: NXopendata needs both a file handle and a path string");
1864    return NX_ERROR;
1865  }
1866
1867  pPtr = moveDown(hfil,(char *)path,&status);
1868  if(status != NX_OK)
1869  {
1870    NXReportError( 
1871                    "ERROR: NXopendata failed to move down in hierarchy");
1872    return status;
1873  }
1874
1875  while(run == 1)
1876  {
1877    pPtr = extractNextPath(pPtr, pathElement);
1878    status = stepOneGroupUp(hfil,pathElement);
1879    if(status == NX_ERROR)
1880    {
1881      return status;
1882    }
1883    if(pPtr == NULL || status == NX_EOD)
1884    {
1885      run = 0;
1886    }
1887  }
1888  return NX_OK;
1889}
1890/*---------------------------------------------------------------------*/
1891NXstatus NXIprintlink(NXhandle fid, NXlink* link)
1892{
1893     pNexusFunction pFunc = handleToNexusFunc(fid);
1894     return LOCKED_CALL(pFunc->nxprintlink(pFunc->pNexusData, link));   
1895}
1896/*----------------------------------------------------------------------*/
1897NXstatus NXgetpath(NXhandle fid, char *path, int pathlen){
1898  int status;
1899  pFileStack fileStack = NULL;
1900
1901  fileStack = (pFileStack)fid;
1902  status = buildPath(fileStack,path,pathlen);
1903  if(status != 1){
1904    return NX_ERROR;
1905  } 
1906  return NX_OK;
1907}
1908
1909/*--------------------------------------------------------------------
1910  format NeXus time. Code needed in every NeXus file driver
1911  ---------------------------------------------------------------------*/
1912char *NXIformatNeXusTime(){
1913    time_t timer;
1914    char* time_buffer = NULL;
1915    struct tm *time_info;
1916    const char* time_format;
1917    long gmt_offset;
1918#ifdef USE_FTIME
1919    struct timeb timeb_struct;
1920#endif
1921
1922    time_buffer = (char *)malloc(64*sizeof(char));
1923    if(!time_buffer){
1924      NXReportError("Failed to allocate buffer for time data");
1925      return NULL;
1926    }
1927
1928#ifdef NEED_TZSET
1929    tzset();
1930#endif
1931    time(&timer);
1932#ifdef USE_FTIME
1933    ftime(&timeb_struct);
1934    gmt_offset = -timeb_struct.timezone * 60;
1935    if (timeb_struct.dstflag != 0)
1936    {
1937        gmt_offset += 3600;
1938    }
1939#else
1940    time_info = gmtime(&timer);
1941    if (time_info != NULL)
1942    {
1943        gmt_offset = (long)difftime(timer, mktime(time_info));
1944    }
1945    else
1946    {
1947        NXReportError( 
1948        "Your gmtime() function does not work ... timezone information will be incorrect\n");
1949        gmt_offset = 0;
1950    }
1951#endif
1952    time_info = localtime(&timer);
1953    if (time_info != NULL)
1954    {
1955        if (gmt_offset < 0)
1956        {
1957            time_format = "%04d-%02d-%02dT%02d:%02d:%02d-%02d:%02d";
1958        }
1959        else
1960        {
1961            time_format = "%04d-%02d-%02dT%02d:%02d:%02d+%02d:%02d";
1962        }
1963        sprintf(time_buffer, time_format,
1964            1900 + time_info->tm_year,
1965            1 + time_info->tm_mon,
1966            time_info->tm_mday,
1967            time_info->tm_hour,
1968            time_info->tm_min,
1969            time_info->tm_sec,
1970            abs(gmt_offset / 3600),
1971            abs((gmt_offset % 3600) / 60)
1972        );
1973    }
1974    else
1975    {
1976        strcpy(time_buffer, "1970-01-01T00:00:00+00:00");
1977    }
1978    return time_buffer;
1979}
1980/*----------------------------------------------------------------------
1981                 F77 - API - Support - Routines
1982  ----------------------------------------------------------------------*/
1983  /*
1984   * We store the whole of the NeXus file in the array - that way
1985   * we can just pass the array name to C as it will be a valid
1986   * NXhandle. We could store the NXhandle value in the FORTRAN array
1987   * instead, but that would mean writing far more wrappers
1988   */
1989  NXstatus  NXfopen(char * filename, NXaccess* am, 
1990                                 NXhandle pHandle)
1991  {
1992        NXstatus ret;
1993        NXhandle fileid = NULL;
1994        ret = NXopen(filename, *am, &fileid);
1995        if (ret == NX_OK)
1996        {
1997          memcpy(pHandle, fileid, getFileStackSize());
1998        }
1999        else
2000        {
2001          memset(pHandle, 0, getFileStackSize());
2002        }
2003        if (fileid != NULL)
2004        {
2005            free(fileid);
2006        }
2007        return ret;
2008  }
2009/*
2010 * The pHandle from FORTRAN is a pointer to a static FORTRAN
2011 * array holding the NexusFunction structure. We need to malloc()
2012 * a temporary copy as NXclose will try to free() this
2013 */
2014  NXstatus  NXfclose (NXhandle pHandle)
2015  {
2016    NXhandle h;
2017    NXstatus ret;
2018    h = (NXhandle)malloc(getFileStackSize());
2019    memcpy(h, pHandle, getFileStackSize());
2020    ret = NXclose(&h);          /* does free(h) */
2021    memset(pHandle, 0, getFileStackSize());
2022    return ret;
2023  }
2024 
2025/*---------------------------------------------------------------------*/ 
2026  NXstatus  NXfflush(NXhandle pHandle)
2027  {
2028    NXhandle h;
2029    NXstatus ret;
2030    h = (NXhandle)malloc(getFileStackSize());
2031    memcpy(h, pHandle, getFileStackSize());
2032    ret = NXflush(&h);          /* modifies and reallocates h */
2033    memcpy(pHandle, h, getFileStackSize());
2034    return ret;
2035  }
2036/*----------------------------------------------------------------------*/
2037  NXstatus  NXfmakedata(NXhandle fid, char *name, int *pDatatype,
2038                int *pRank, int dimensions[])
2039  {
2040    NXstatus ret;
2041    static char buffer[256];
2042    int i, *reversed_dimensions;
2043    reversed_dimensions = (int*)malloc(*pRank * sizeof(int));
2044    if (reversed_dimensions == NULL)
2045    {
2046        sprintf (buffer, 
2047        "ERROR: Cannot allocate space for array rank of %d in NXfmakedata", 
2048                *pRank);
2049        NXReportError( buffer);
2050        return NX_ERROR;
2051    }
2052/*
2053 * Reverse dimensions array as FORTRAN is column major, C row major
2054 */
2055    for(i=0; i < *pRank; i++)
2056    {
2057        reversed_dimensions[i] = dimensions[*pRank - i - 1];
2058    }
2059    ret = NXmakedata(fid, name, *pDatatype, *pRank, reversed_dimensions);
2060    free(reversed_dimensions);
2061    return ret;
2062  }
2063
2064/*-----------------------------------------------------------------------*/
2065  NXstatus  NXfcompmakedata(NXhandle fid, char *name, 
2066                int *pDatatype,
2067                int *pRank, int dimensions[],
2068                int *compression_type, int chunk[])
2069  {
2070    NXstatus ret;
2071    static char buffer[256];
2072    int i, *reversed_dimensions, *reversed_chunk;
2073    reversed_dimensions = (int*)malloc(*pRank * sizeof(int));
2074    reversed_chunk = (int*)malloc(*pRank * sizeof(int));
2075    if (reversed_dimensions == NULL || reversed_chunk == NULL)
2076    {
2077        sprintf (buffer, 
2078      "ERROR: Cannot allocate space for array rank of %d in NXfcompmakedata", 
2079         *pRank);
2080        NXReportError( buffer);
2081        return NX_ERROR;
2082    }
2083/*
2084 * Reverse dimensions array as FORTRAN is column major, C row major
2085 */
2086    for(i=0; i < *pRank; i++)
2087    {
2088        reversed_dimensions[i] = dimensions[*pRank - i - 1];
2089        reversed_chunk[i] = chunk[*pRank - i - 1];
2090    }
2091    ret = NXcompmakedata(fid, name, *pDatatype, *pRank, 
2092        reversed_dimensions,*compression_type, reversed_chunk);
2093    free(reversed_dimensions);
2094    free(reversed_chunk);
2095    return ret;
2096  }
2097/*-----------------------------------------------------------------------*/
2098  NXstatus  NXfcompress(NXhandle fid, int *compr_type)
2099  { 
2100      return NXcompress(fid,*compr_type);
2101  }
2102/*-----------------------------------------------------------------------*/
2103  NXstatus  NXfputattr(NXhandle fid, const char *name, const void *data, 
2104                                   int *pDatalen, int *pIType)
2105  {
2106    return NXputattr(fid, name, data, *pDatalen, *pIType);
2107  }
2108
2109
2110  /*
2111   * implement snprintf when it is not available
2112   */
2113  int nxisnprintf(char* buffer, int len, const char* format, ... )
2114  {
2115          int ret;
2116          va_list valist;
2117          va_start(valist,format);
2118          ret = vsprintf(buffer, format, valist);
2119          va_end(valist);
2120          return ret;
2121  }
2122
2123/*--------------------------------------------------------------------------*/
2124NXstatus NXfgetpath(NXhandle fid, char *path, int *pathlen)
2125{
2126  return NXgetpath(fid,path,*pathlen);
2127}
2128
2129const char* NXgetversion()
2130{
2131    return NEXUS_VERSION ;
2132}
2133
Note: See TracBrowser for help on using the repository browser.