1 /***********************************************************************/
2 /* Open Visualization Data Explorer                                    */
3 /* (C) Copyright IBM Corp. 1989,1999                                   */
4 /* ALL RIGHTS RESERVED                                                 */
5 /* This code licensed under the                                        */
6 /*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
7 /***********************************************************************/
8 #include <dxconfig.h>
9 
10 
11 
12 #include <stdio.h>
13 #include <dx/dx.h>
14 #include "import.h"
15 
16 #if defined(HAVE_LIBNETCDF)
17 
18 #undef int8
19 #undef uint8
20 #undef int16
21 #undef uint16
22 #undef int32
23 #undef uint32
24 #undef float64
25 
26 #if (defined(WIN32) && defined(NDEBUG)) || defined(_MT)
27 #define _HDFDLL_
28 #elif defined(_DEBUG)
29 #undef _DEBUG
30 #endif
31 
32 #include <netcdf.h>
33 #include <ctype.h>
34 #include <string.h>
35 #include <math.h>
36 
37 
38 #define DEBUG 0
39 #define REALCODE  0  /* if 0, sends fake data type info for QueryInfo */
40 #define TESTQUERY 0  /* if 1, send QueryInfo object back for Import */
41 
42 #define VOID    char   /* prefer void, but char if your compiler chokes */
43 #define STR(x)  (Object)DXNewString(x)
44 
45 #define MAX_FILENAME_LEN  MAX_NC_NAME
46 #define MAX_VARNAME_LEN   MAX_NC_NAME
47 #define MAXNAME           MAX_NC_NAME
48 #define MAXHAND           8
49 #define MAXATTRSTR        16
50 
51 /* attribute names in netCDF file */
52 #define SERIESATTRIB      "series"
53 #define SERIESPOSATTRIB   "seriespositions"
54 #define FIELDATTRIB       "field"
55 #define GROUPATTRIB       "group"
56 #define TOPOATTRIB        "connections"
57 #define GEOMATTRIB        "positions"
58 #define OTOPOATTRIB       "topology"     /*  "connections" */
59 #define OGEOMATTRIB       "geometry"     /*  "positions" */
60 #define COMPATTRIB        "component"    /* general purpose component */
61 #define ATTRATTRIB        "attribute"    /* general purpose attribute */
62 #define OATTRATTRIB       "attributes"   /* alternate spelling */
63 #define MAX_DXATTRIBUTES  9		 /* number of variable dx attributes */
64 
65 /* component names in Group/Field */
66 #define TOPCOMPNAME       "connections"
67 #define GEOCOMPNAME       "positions"
68 #define DATCOMPNAME       "data"
69 
70 
71 #define MAXCOMPNAME       128
72 #define MAXRANK           16     /* number of dimensions in array */
73 #define MAXSHAPE          128    /* max dim of individual data item */
74 #define INVALID_VARID     -2
75 #define INVALID_DIMID	  -2
76 
77 /* flags for build_poscon */
78 #define I_NONE            0
79 #define I_COMPACT_P       1
80 #define I_PRODUCT_P       2
81 #define I_IRREGULAR_P     3
82 #define I_REGULAR_P       4
83 #define I_COMPACT_C       5
84 #define I_PRODUCT_C       6
85 #define I_IRREGULAR_C     7
86 #define I_REGULAR_C       8
87 
88 /* flags for Object_Import */
89 #define IMPORT_UIQUERY    0
90 #define IMPORT_DATA       1
91 #define IMPORT_VARINFO    2
92 
93 #ifndef DXD_NON_UNIX_ENV_SEPARATOR
94 #define PATH_SEP_CHAR ':'
95 #else
96 #define PATH_SEP_CHAR ';'
97 #endif
98 
99 /* for collecting series groups, general groups and composite fields.
100  */
101 struct varinfo {
102     Class class;
103     char name[MAXCOMPNAME];
104     int cdfid;
105     char cdfname[MAX_FILENAME_LEN];
106     int varid;
107     double position;
108     int *startframe;
109     int *endframe;
110     int *deltaframe;
111     int recdim;
112     struct varinfo *child;
113     struct varinfo *next;
114 };
115 typedef struct varinfo *Varinfo;
116 
117 
118 /* for assembling arrays correctly.
119  */
120 struct arrayinfo {
121     int cdfhandle;              /* for netCDF variable currently being read */
122     int varid;
123     int recdim;                 /* dim ID for unlimited (record) dimension */
124     int data_isrec;             /* whether data uses record dimension */
125     char stringattr[MAXNAME];
126     int data_ndims;             /* data array shape */
127     int datacounts[MAXRANK];
128     int array_ndims;            /* current array shape */
129     int arraycounts[MAXRANK];
130     int arrayrank;              /* current array individual items */
131     int arrayshape[MAXSHAPE];
132     int arraytype;
133     Varinfo vp;                 /* for getting start/end/delta/current info */
134 };
135 typedef struct arrayinfo *Arrayinfo;
136 
137 static char *dxattributes[] = {
138        "field",
139        "group",
140        "connections",
141        "positions",
142        "topology",
143        "geometry",
144        "component",
145        "attribute",
146        "attributes"
147 };
148 
149 static char *regularname[] = {
150     "point",        /* rank 0 */
151     "lines",        /* rank 1 */
152     "quads",        /* rank 2 */
153     "cubes",        /* rank 3 */
154     "cubes4D",      /* rank 4 */
155     "cubes5D",      /* rank 5 */
156     "cubes6D",      /* rank 6 */
157     "cubes7D",      /* rank 7 */
158     "cubes8D",      /* rank 8 */
159     "cubes9D",      /* rank 9 */
160 };
161 
162 /* these prototypes should be moved to import.h - they are publics */
163 Object _dxfQueryImportInfo(char *filename);
164 static Object EndImport(Object o);
165 
166 /* private functions */
167 static Object QueryNetCDFInfo(char *filename);
168 static Object Object_Import(char *filename, char **varlist, int *starttime,
169 			    int *endtime, int *deltatime, int importflag);
170 
171 static Object import_field(int cdfhandle, Varinfo vp);
172 static Object query_field(int cdfhandle, Varinfo vp);
173 static Varinfo query_var(int cdfhandle, char **varlist, int *starttime,
174 			 int *endtime, int *deltatime);
175 
176 static int open_netcdf_file(char *filename);
177 static void close_netcdf_file(int cdfhandle);
178 static Varinfo match_vp(Varinfo vp, int varid, char **s, char *stringattr);
179 static void vp_delete(Varinfo vp);
180 
181 static Object build_field(int, int);
182 static Object build_series(int, Varinfo vp);
183 static Object build_poscon(Arrayinfo ap, int flag, int dim);
184 static Object build_data(Arrayinfo, int);
185 static Object build_array(Arrayinfo);
186 static Object build_regpos(Arrayinfo, int);
187 static Object build_regpos1D(Arrayinfo, int, int);
188 static Object build_regcon(Arrayinfo);
189 static Object build_regcon1D(Arrayinfo, int);
190 
191 static char *getattr(int hand, int varid, char *attrname,
192                     char *stringattr,int maxlen);
193 static char *getNattr(int hand, int varid, int i, char *attrname, int n,
194                      char *stringattr);
195 static Error setattr(Arrayinfo ap, Field f, char *compname);
196 static char *isattr(int hand, int varid, char *attrname);
197 static Error setuserattr(Arrayinfo ap, Field f, char *compname);
198 static Error getglobalattr(Object o,Varinfo vp);
199 
200 static Error check_serieslength(Arrayinfo ap);
201 static int get_serieslength(Arrayinfo);
202 static int is_seriesvariable(int hand, int varid, int recdim);
203 static Error get_seriesvalue(Arrayinfo, float **);
204 
205 static Error variablename(int hand, int varid, char *cbuf);
206 static int setrank(char *);
207 static char *parseit(char *in, int *n, char *out[]);
208 
209 /* globals defined in xdr_float.c.  xdr_float() increments these counts
210  *  if IEEE defined but not-supported-on-the-i860-without-a-trap numbers
211  *  are encountered.  they are all set to zero.
212  */
213 #if !defined(DXD_STANDARD_IEEE)
214 extern int dx_denorm_fp_count;
215 extern int dx_bad_fp_count;
216 #endif
217 
218 /*
219  * public entry points:
220  */
221 
222 /*
223  * inquire about the fields in a file
224  *
225  */
_dxfQueryImportInfo(char * filename)226 Object _dxfQueryImportInfo(char *filename)
227 {
228     /* only handle netCDF for now */
229     return QueryNetCDFInfo(filename);
230 }
231 
232 
233 /*
234  * create a field or group object.  new interface.
235  *
236  * input: netCDF dataset name, dependant variable name list, int *starttime,
237  *        int *endtime, int *deltatime
238  * output: group or field
239  *
240  */
DXImportNetCDF(char * filename,char ** variable,int * starttime,int * endtime,int * deltatime)241 Object DXImportNetCDF(char *filename, char **variable, int *starttime,
242 		    int *endtime, int *deltatime)
243 {
244     return Object_Import(filename, variable, starttime, endtime,
245 			 deltatime, IMPORT_DATA);
246 }
247 
248 
249 /*
250  * do any processing at end of import, e.g. partitioning or statistics
251  */
EndImport(Object o)252 static Object EndImport(Object o)
253 {
254     return o;
255 }
256 
257 
258 static Object
QueryNetCDFInfo(char * filename)259 QueryNetCDFInfo(char *filename)
260 {
261     return Object_Import(filename, NULL, NULL, NULL, NULL, IMPORT_UIQUERY);
262 }
263 
264 
265 static Object
Object_Import(char * filename,char ** varlist,int * starttime,int * endtime,int * deltatime,int importflag)266 Object_Import(char *filename, char **varlist, int *starttime, int *endtime,
267 	      int *deltatime, int importflag)
268 {
269     int cdfhandle;               /* file descriptor */
270     Varinfo vp = NULL;           /* number and types of fields to import */
271     Object o = NULL;             /* output objects */
272 
273 
274     /* open the file, handle default dirs, socket connections, etc.
275      *  if return < 0, error code has already been set.
276      */
277     if((cdfhandle = open_netcdf_file(filename)) < 0)
278 	return NULL;
279 
280 #if !defined(DXD_STANDARD_IEEE)
281 
282     /* zero out the counts, and look at the end to see if we encountered
283      *  any bad floating point values.
284      */
285     dx_denorm_fp_count = 0;
286     dx_bad_fp_count = 0;
287 
288 #endif
289 
290     /* fill in the varinfo struct, and return NULL if no
291      *  valid fields are found.  sets error code before return.
292      */
293     if(!(vp = query_var(cdfhandle, varlist, starttime, endtime, deltatime)))
294 	return NULL;
295 
296 
297     /* if query, put information about the field into a string object.
298      * if import, import the data and construct the object.
299      */
300     switch(importflag) {
301       case IMPORT_UIQUERY:
302 	o = query_field(cdfhandle, vp);
303 	break;
304 
305       case IMPORT_DATA:
306 	o = import_field(cdfhandle, vp);
307  	if( !o )
308  	  DXWarning("Nothing has been imported. Did you use a OpenDX-compliant netCDF file convention?");
309 	else
310         getglobalattr(o,vp);
311 	break;
312 
313       case IMPORT_VARINFO:
314 	close_netcdf_file(cdfhandle);
315 	return (Object)vp;
316 
317       default:
318 	DXSetError(ERROR_INTERNAL, "bad flag");
319     }
320 
321 
322     /* close the file, sever socket connections, etc.
323      */
324     close_netcdf_file(cdfhandle);
325 
326     vp_delete(vp);
327 
328 #if !defined(DXD_STANDARD_IEEE)
329 
330     if(dx_denorm_fp_count > 0)
331 	DXWarning("%d denormalized floating point values rounded to zero",
332 		dx_denorm_fp_count);
333     if(dx_bad_fp_count > 0)
334 	DXWarning("%d NaN or Infinity floating point values changed to zero",
335 		dx_bad_fp_count);
336 #endif
337 
338     /* hook for pre-processing the imported data, if needed */
339     o = EndImport(o);
340     return o;
341 
342 }
343 
344 /* open the file.  when we start doing distributed things, here is
345  *  where the socket connections should be added.
346  * look for name.nc instead of name.cdf name not found lumba 3Apr96
347  */
348 static int
open_netcdf_file(char * filename)349 open_netcdf_file(char *filename)
350 {
351     int cdfhandle;
352     int foundfile = 0;
353     char *fname = NULL, *foundname = NULL, *cp;
354     char *datadir = NULL;
355     int ll;
356 
357     /* netCDF library options: on error, don't abort and don't print */
358     ncopts = 0;
359 
360 #define XTRA 8  /* space for trailing /, .nc, trailing 0 and some slop */
361 
362     datadir = (char *)getenv("DXDATA");
363     ll = strlen(filename) + XTRA;
364     if (datadir != NULL)
365     	ll += strlen(datadir);
366     fname = (char *)DXAllocateLocalZero(ll);
367     if (!fname)
368 	goto error;
369 
370     /* try name, name.nc, dir/name and dir/name.nc */
371     if((cdfhandle = ncopen(filename, NC_NOWRITE)) < 0) {
372 	if (ncerr == 0 && !foundfile) {
373 	    foundfile++;
374 	    foundname = (char *)DXAllocateLocalZero(strlen(filename)+1);
375 	    if (!foundname)
376 		goto error;
377 	    strcpy(foundname, filename);
378 	}
379 
380 
381 	strcpy(fname, filename);
382 	strcat(fname, ".nc");
383 	if((cdfhandle = ncopen(fname, NC_NOWRITE)) >= 0) {
384 	    DXMessage("opening file '%s'", fname);
385 	    goto file_ok;
386 	}
387 	if (ncerr == 0 && !foundfile) {
388 	    foundfile++;
389 	    foundname = (char *)DXAllocateLocalZero(strlen(fname)+1);
390 	    if (!foundname)
391 		goto error;
392 	    strcpy(foundname, fname);
393 	}
394 
395 	if (!datadir)
396 	    goto e1;
397 
398 	while (datadir) {
399 
400 	    strcpy(fname, datadir);
401 	    if((cp = strchr(fname, PATH_SEP_CHAR)) != NULL)
402 	       *cp = '\0';
403 	    strcat(fname, "/");
404 	    strcat(fname, filename);
405 	    if((cdfhandle = ncopen(fname, NC_NOWRITE)) >= 0) {
406 		DXMessage("opening file '%s'", fname);
407 		goto file_ok;
408 	    }
409 	    if (ncerr == 0 && !foundfile) {
410 		foundfile++;
411 		foundname = (char *)DXAllocateLocalZero(strlen(fname)+1);
412 		if (!foundname)
413 		    goto error;
414 		strcpy(foundname, fname);
415 	    }
416 
417 	    strcat(fname, ".nc");
418 	    if((cdfhandle = ncopen(fname, NC_NOWRITE)) >= 0) {
419 		DXMessage("opening file '%s'", fname);
420 		goto file_ok;
421 	    }
422 	    if (ncerr == 0 && !foundfile) {
423 		foundfile++;
424 		foundname = (char *)DXAllocateLocalZero(strlen(fname)+1);
425 		if (!foundname)
426 		    goto error;
427 		strcpy(foundname, fname);
428 	    }
429 
430 	    datadir = strchr(datadir, PATH_SEP_CHAR);
431 	    if (datadir)
432 		datadir++;
433 	}
434 
435       e1:
436 	if (!foundfile)
437 	    DXSetError(ERROR_BAD_PARAMETER, "can't open file '%s'", filename);
438 	else
439 	    DXSetError(ERROR_BAD_PARAMETER, "'%s' not a netCDF file", foundname);
440 
441       error:
442 	DXFree((Pointer)fname);
443 	DXFree((Pointer)foundname);
444 	return -1;   /* bad filehandle */
445 
446     }
447 
448 file_ok:
449     DXFree((Pointer)fname);
450     DXFree((Pointer)foundname);
451     return cdfhandle;
452 }
453 
454 
455 /* close the file.  when we start doing distributed things, here is
456  *  where the socket connections should be closed cleanly.
457  */
458 static void
close_netcdf_file(int cdfhandle)459 close_netcdf_file(int cdfhandle)
460 {
461     ncclose(cdfhandle);
462 }
463 
464 
465 /* see if this netcdf file can be opened.  don't set error, but return
466  *  OK or ERROR.
467  */
468 ImportStatReturn
_dxfstat_netcdf_file(char * filename)469 _dxfstat_netcdf_file(char *filename)
470 {
471     int cdfhandle;
472     char *fname = NULL, *cp;
473     char *datadir = NULL;
474 
475     /* netCDF library options: on error, don't abort and don't print */
476     ncopts = 0;
477 
478     /* try name, name.cdf, dir/name and dir/name.cdf */
479     if((cdfhandle = ncopen(filename, NC_NOWRITE)) >= 0)
480 	goto file_ok;
481 
482     /* XTRA defined above:
483      * space for trailing /, .nc, trailing 0 and some slop
484      */
485 
486     datadir = (char *)getenv("DXDATA");
487     if (!datadir)
488 	goto error;
489 
490     fname = (char *)DXAllocateLocalZero((datadir ? strlen(datadir) : 0) +
491 					strlen(filename) + XTRA);
492     if (!fname)
493 	goto error;
494 
495     strcpy(fname, filename);
496     strcat(fname, ".nc");
497     if((cdfhandle = ncopen(fname, NC_NOWRITE)) >= 0)
498     {
499 	DXFree((Pointer)fname);
500 	return IMPORT_STAT_NOT_FOUND;
501     }
502 
503     while (datadir) {
504 
505 	strcpy(fname, datadir);
506 	if((cp = strchr(fname, PATH_SEP_CHAR)) != NULL)
507 	    *cp = '\0';
508 	strcat(fname, "/");
509 	strcat(fname, filename);
510 	if((cdfhandle = ncopen(fname, NC_NOWRITE)) >= 0)
511 	    goto file_ok;
512 
513 	strcat(fname, ".nc");
514 	if((cdfhandle = ncopen(fname, NC_NOWRITE)) >= 0)
515 	    goto file_ok;
516 
517 	datadir = strchr(datadir, PATH_SEP_CHAR);
518 	if (datadir)
519 	    datadir++;
520     }
521 
522   error:
523     DXFree((Pointer)fname);
524     return IMPORT_STAT_ERROR;
525 
526   file_ok:
527     DXFree((Pointer)fname);
528     ncclose(cdfhandle);
529     return IMPORT_STAT_FOUND;
530 }
531 
532 /* find out how many netcdf variables match the spec, and construct an
533  *  information block for each field.  if the caller has given a specific
534  *  variable or variable list, restrict it to those; if input list is null,
535  *  count all fields and series attrs in the file.
536  * varlist must be null terminated.
537  */
538 static Varinfo
query_var(int cdfhandle,char ** varlist,int * starttime,int * endtime,int * deltatime)539 query_var(int cdfhandle, char **varlist, int *starttime, int *endtime,
540 	  int *deltatime)
541 {
542     char stringattr[MAXNAME], *cp, *s[MAXATTRSTR], **lp, *attrtext=NULL;
543     char *subv[2];
544     int ndims, nvars, ngatts, recdim;
545     int i, j, k, matched;
546     int count = 0;
547     int numspec = 0, *found = NULL;
548     double position;
549     Varinfo vp = NULL;        /* root of tree, to be returned */
550     Varinfo vp1, vp2, vp3;    /* temp node pointers */
551 
552 
553     /* count the number of netCDF variables in the file
554      */
555     if ((ncinquire(cdfhandle, &ndims, &nvars, &ngatts, &recdim) < 0)
556        || nvars <= 0) {
557 	DXSetError(ERROR_MISSING_DATA, "no netCDF variables found in file");
558 	return NULL;
559     }
560 
561 
562     /* count the number of fields specified by the user */
563     if (varlist != NULL && varlist[0] != NULL) {
564 	for(lp = varlist; *lp; lp++)
565 	    numspec++;
566 
567 	if ((found = (int *)DXAllocateLocalZero(sizeof(int) * numspec)) == NULL)
568 	    return NULL;
569     }
570 
571     /* check for any global series attributes.
572      */
573     for (k = 0; k < ngatts; k++) {
574          /* If a continue has been encountered in the previous iteratation */
575          if(attrtext) DXFree((Pointer) attrtext);
576 
577          if(!(attrtext=getNattr(cdfhandle, NC_GLOBAL, k, SERIESATTRIB,
578                      strlen(SERIESATTRIB), stringattr)))
579             continue;
580 
581 	j = MAXATTRSTR;
582 	cp = parseit(attrtext, &j, s);
583 	if(j <= 0) {
584 	    DXSetError(ERROR_DATA_INVALID, "bad attribute for series");
585 	    goto error;
586 	}
587 
588 	matched = 0;
589 	if(varlist == NULL || varlist[0] == NULL)
590 	    matched = 1;
591 
592 	else for(lp = varlist; *lp; lp++) {
593 	    if(strcmp(s[0], *lp))
594 		continue;
595 
596 	    matched = 1;
597 	    found[lp - varlist]++;
598 	    break;
599 	}
600 
601 	if(!matched)
602 	    continue;
603 
604 	/* we are going to process this netCDF variable - add it to the tree
605          *  we are building.  if match_vp() returns non-null, it found a
606 	 *  match, and has already added it as a compositefield member.
607 	 *
608 	 * vp = root of tree
609 	 * vp1 = new series members
610 	 * vp2 = new series parent node
611 	 * vp3 = temp node for finding end of next->next->next chain.
612 	 */
613 	if (match_vp(vp, NC_GLOBAL, s, attrtext) != NULL)
614 	    continue;
615 
616 	/* new series object
617 	 */
618 	vp2 = (Varinfo)DXAllocateLocalZero(sizeof(struct varinfo));
619 	if(!vp2)
620 	    goto error;
621 	vp2->class = CLASS_SERIES;
622 	strcpy(vp2->name, s[0]);
623 	vp2->varid = INVALID_VARID;
624 	vp2->startframe = starttime;
625 	vp2->endframe = endtime;
626 	vp2->deltaframe = deltatime;
627 	vp2->recdim = INVALID_DIMID;
628 	vp2->cdfid = cdfhandle;
629 
630 	/* multiple files */
631 	if(s[1] && !strcmp(s[1], "files")) {
632 	    i = -1;
633 	    while(1) {
634 		i++;
635 		j = MAXATTRSTR;
636 		cp = parseit(cp, &j, s);
637 		if(j <= 0)
638 		    break;
639 
640 		/* if any of start, end, delta or current are set,
641 		 *  restrict the input to only those in the list.
642 		 */
643 		if (starttime && i < *starttime)
644 		    continue;
645 
646 		if (endtime && i > *endtime)
647 		    continue;
648 
649 		if (deltatime) {
650 		    if ((starttime && (i - *starttime) % *deltatime)
651 		     || (!starttime && i % *deltatime))
652 			continue;
653 		}
654 
655 		subv[1] = NULL;
656 		switch(j) {
657 		  case 1:
658 		    /* only filename, no fieldname or series pos */
659 		    subv[0] = vp2->name;
660 		    position = i;
661 		    break;
662 
663 		  case 2:
664 		    /* filename plus either fieldname or position */
665 		    if(isdigit(s[1][0])) {
666 			subv[0] = vp2->name;
667 			position = atof(s[1]);
668 		    } else {
669 			subv[0] = s[1];
670 			position = i;
671 		    }
672 		    break;
673 
674 		  case 3:
675 		    /* filename, fieldname, position */
676 		    subv[0] = s[1];
677 		    position = atof(s[2]);
678 		    break;
679 
680 		  default:
681 		    /* more than 3 parms? */
682 		    DXSetError(ERROR_BAD_PARAMETER,
683 			     "bad attribute for series '%s'",
684 			     vp2->name);
685 		    goto error;
686 		}
687 
688 		/* new series member */
689 		vp1 = (Varinfo)Object_Import(s[0], subv, starttime, endtime,
690 					     deltatime, IMPORT_VARINFO);
691 		if(!vp1)
692 		    goto error;
693 
694 		strcpy(vp1->cdfname, s[0]);
695 		vp1->cdfid = -1;
696 		vp1->position = position;
697 
698 		/* first series member?  make it the first child */
699 		if (vp2->child == NULL)
700 		    vp2->child = vp1;
701 
702 		else {   /* add to end of series existing list */
703 		    vp3 = vp2->child;
704 		    while (vp3->next != NULL)
705 			vp3 = vp3->next;
706 
707 		    vp3->next = vp1;
708 		}
709 		count++;
710 	    }
711 
712 	} else {
713 	    /* series using variables in this file */
714 	    i = -1;
715 	    while(1) {
716 		i++;
717 		j = MAXATTRSTR;
718 		cp = parseit(cp, &j, s);
719 		if(j <= 0)
720 		    break;
721 
722 		/* if any of start, end, delta or current are set,
723 		 *  restrict the input to only those in the list.
724 		 */
725 		if (starttime && i < *starttime)
726 		    continue;
727 
728 		if (endtime && i > *endtime)
729 		    continue;
730 
731 		if (deltatime) {
732 		    if ((starttime && (i - *starttime) % *deltatime)
733 		     || (!starttime && i % *deltatime))
734 			continue;
735 		}
736 
737 		/* new series member */
738 		vp1 = (Varinfo)DXAllocateLocalZero(sizeof(struct varinfo));
739 		if(!vp1)
740 		    goto error;
741 		vp1->class = CLASS_FIELD;
742 		vp1->varid = ncvarid(cdfhandle, s[0]);
743 		if(vp1->varid < 0) {
744 		    DXSetError(ERROR_BAD_PARAMETER,
745 			     "field %s not found in series %s",
746 			     s[0], vp2->name);
747 		    DXFree((Pointer)vp1);
748 		    goto error;
749 		}
750 		vp1->cdfid = cdfhandle;
751 		vp1->recdim = INVALID_DIMID;
752 		strcpy(vp1->name, s[0]);
753 
754 		if(s[1])
755 		    vp1->position = atof(s[1]);
756 		else
757 		    vp1->position = i;
758 
759 		/* first series member?  make it the first child */
760 		if (vp2->child == NULL)
761 		    vp2->child = vp1;
762 
763 		else {   /* add to end of series existing list */
764 		    vp3 = vp2->child;
765 		    while (vp3->next != NULL)
766 			vp3 = vp3->next;
767 
768 		    vp3->next = vp1;
769 		}
770 		count++;
771 	    }
772 	}
773 
774 	/* vp is root of tree.
775 	 * vp1 is new group object if needed.
776 	 * vp2 is series object constructed during this pass
777 	 * vp3 is temp to add object to end of next->next->next list.
778 	 */
779 
780 	/* if first object, make it the root */
781 	if(!vp)
782 	    vp = vp2;
783 
784 	else if(vp->class != CLASS_GROUP) {  /* new group needed */
785 	    vp1 = (Varinfo)DXAllocateLocalZero(sizeof(struct varinfo));
786 	    if(!vp1)
787 		goto error;
788 	    vp1->class = CLASS_GROUP;
789 	    vp1->varid = INVALID_VARID;
790 	    vp1->recdim = INVALID_DIMID;
791 	    vp1->cdfid = cdfhandle;
792 	    vp1->child = vp;
793 	    vp->next = vp2;
794 	    vp = vp1;
795 
796 	} else {   /* group already exists.  add to end of list */
797 	    vp3 = vp->child;
798 	    while (vp3->next != NULL)
799 		vp3 = vp3->next;
800 	    vp3->next = vp2;
801 	}
802 	count++;
803       DXFree((Pointer) attrtext);
804       attrtext=NULL;
805     }
806     if(attrtext) {
807       DXFree((Pointer) attrtext);
808       attrtext=NULL;
809     }
810 
811 
812     /* check each variable looking for 'field' attribute.
813      */
814     for(i=0; i<nvars; i++) {
815 
816 	if(!getattr(cdfhandle, i, FIELDATTRIB, stringattr, MAXNAME))
817 	    continue;
818 
819 	j = MAXATTRSTR;
820 	cp = parseit(stringattr, &j, s);
821 	if(j <= 0)
822 	    continue;
823 
824 	matched = 0;
825 	if(varlist == NULL || varlist[0] == NULL)
826 	    matched = 1;
827 
828 	else for(lp = varlist; *lp; lp++) {
829 	    if(strcmp(s[0], *lp))
830 		continue;
831 
832 	    matched = 1;
833 	    found[lp - varlist]++;
834 	    break;
835 	}
836 
837 	if(!matched)
838 	    continue;
839 
840 	/* if field name matches an existing one, match_vp returns non-null,
841 	 *  and has already added it as a member of a composite field.
842 	 */
843 	if (match_vp(vp, i, s, stringattr))
844 	    continue;
845 
846 	/* new field/implicit series */
847 	vp2 = (Varinfo)DXAllocateLocalZero(sizeof(struct varinfo));
848 	if(!vp2)
849 	    goto error;
850 
851 	/* check here for series keyword */
852 	if(j > 2 && !strcmp(s[2], "series")) {
853 	    vp2->class = CLASS_SERIES;
854 	    strcpy(vp2->name, s[0]);
855 	    vp2->varid = i;
856 	    vp2->cdfid = cdfhandle;
857 	    vp2->startframe = starttime;
858 	    vp2->endframe = endtime;
859 	    vp2->deltaframe = deltatime;
860 	    vp2->recdim = recdim;
861 	} else {
862 	    vp2->class = CLASS_FIELD;
863 	    strcpy(vp2->name, s[0]);
864 	    vp2->varid = i;
865 	    vp2->cdfid = cdfhandle;
866 	    vp2->recdim = INVALID_DIMID;
867 	}
868 
869 	/* vp is root of tree.
870 	 * vp1 is new group node if needed.
871 	 * vp2 is new field/series
872 	 * vp3 is temp to add object to end of next->next->next list.
873 	 */
874 
875 	/* if first object, make it root */
876 	if(!vp)
877 	    vp = vp2;
878 
879 	else if(vp->class != CLASS_GROUP) {  /* create new group */
880 	    vp1 = (Varinfo)DXAllocateLocalZero(sizeof(struct varinfo));
881 	    if(!vp1)
882 		goto error;
883 	    vp1->class = CLASS_GROUP;
884 	    vp1->varid = INVALID_VARID;
885 	    vp1->recdim = INVALID_DIMID;
886 	    vp1->cdfid = cdfhandle;
887 	    vp1->child = vp;
888 	    vp->next = vp2;
889 	    vp = vp1;
890 
891 	} else {  /* group already exists.  add object to end */
892 	    vp3 = vp->child;
893 	    while (vp3->next != NULL)
894 		vp3 = vp3->next;
895 
896 	    vp3->next = vp2;
897 	}
898 	count++;
899     }
900 
901 
902     /* if no field attributes found, import netCDF variables as reg grids */
903     if (count <= 0) {
904 	if (numspec == 0)
905 	    DXWarning("no 'field' attributes; importing all netCDF variables");
906 	else
907 	    DXWarning("no 'field' attributes matched list; trying netCDF variable names");
908 
909 	/* treat all variables as having a 'field' attribute.
910 	 */
911 	for(i=0; i<nvars; i++) {
912 
913 	    if(!variablename(cdfhandle, i, stringattr))
914 		goto error;
915 
916 	    matched = 0;
917 	    if(varlist == NULL || varlist[0] == NULL)
918 		matched = 1;
919 
920 	    else for(lp = varlist; *lp; lp++) {
921 		if(strcmp(stringattr, *lp))
922 		    continue;
923 
924 		matched = 1;
925 		found[lp - varlist]++;
926 		break;
927 	    }
928 
929 	    if (!strcmp(stringattr,""))	/* dimension=0 so skip this variable */
930 	      matched = 0;
931 
932 	    if(!matched)
933 		continue;
934 
935 
936 	    /* this can be simpler than above, since netCDF variable names
937              *  must be unique.  therefore, we can't have identical names
938 	     *  so there can't be composite fields.  also, we don't recognize
939              *  series (although if recdim is used, we could, i suppose).
940 	     */
941 	    vp2 = (Varinfo)DXAllocateLocalZero(sizeof(struct varinfo));
942 	    if(!vp2)
943 		goto error;
944 
945 	    vp2->class = CLASS_FIELD;
946 	    strcpy(vp2->name, stringattr);
947 	    vp2->varid = i;
948 	    vp2->cdfid = cdfhandle;
949 	    vp2->recdim = INVALID_DIMID;
950 
951 	    /* vp is root of tree.
952 	     * vp1 is new group node if needed.
953 	     * vp2 is new node to be added.
954 	     * vp3 is temp to add node at end of next->next->next list
955 	     */
956 
957 	    /* first object */
958 	    if(!vp)
959 		vp = vp2;
960 
961 	    else if(vp->class != CLASS_GROUP) {  /* create a group */
962 		vp1 = (Varinfo)DXAllocateLocalZero(sizeof(struct varinfo));
963 		if(!vp1)
964 		    goto error;
965 		vp1->class = CLASS_GROUP;
966 		vp1->varid = INVALID_VARID;
967 		vp1->recdim = INVALID_DIMID;
968 		vp1->cdfid = cdfhandle;
969 		vp1->child = vp;
970 		vp->next = vp2;
971 		vp = vp1;
972 
973 	    } else {  /* group already exists.  add to end of list */
974 		vp3 = vp->child;
975 		while (vp3->next)
976 		    vp3 = vp3->next;
977 		vp3->next = vp2;
978 	    }
979 	    count++;
980 	}
981 
982     }
983 
984     /* if STILL 0, user specified list and didn't match variables */
985     if (count <= 0) {
986 	DXSetError(ERROR_BAD_PARAMETER, "no matching fields found in file");
987 	goto error;
988     }
989 
990     /* warn about field names which didn't match anything in the file */
991     if (found) {
992 	for (i=0; i<numspec; i++)
993 	    if (found[i] == 0)
994 		DXWarning("field '%s' not found in file", varlist[i]);
995 
996 	DXFree((Pointer)found);
997     }
998 
999 
1000     return vp;
1001 
1002   error:
1003     if(attrtext)
1004       DXFree((Pointer)attrtext);
1005     if (found)
1006 	DXFree((Pointer)found);
1007     if(vp)
1008         vp_delete(vp);
1009     return NULL;
1010 }
1011 
1012 
1013 
1014 /* if this routine finds a match, it returns non-null.  if the field
1015  *  isn't already found, it returns null.
1016  */
1017 static Varinfo
match_vp(Varinfo vp,int varid,char ** s,char * stringattr)1018 match_vp(Varinfo vp, int varid, char **s, char *stringattr)
1019 {
1020     Varinfo vp1, vp2, vp3;
1021 
1022 
1023     /* search down through vp, looking for name matches.  if hit,
1024      *  this becomes a composite field.
1025      *  (if varid already in the file, skip the field because it's there as
1026      *  part of a series?)
1027      */
1028     for(vp1 = vp; vp1; vp1 = vp1->next) {
1029 #if 0
1030         /* already in tree, skip it this time? */
1031 	if(vp1->varid == varid)
1032 	    return vp;
1033 #endif
1034 
1035 	if(!strcmp(vp1->name, s[0])) {
1036 	    /* make a composite field node - this name matches.
1037 	     *  vp1 is original node.  vp2 is created and the vp1 info is
1038              *  copied into it.  then vp1 is changed to a composite field node.
1039 	     */
1040 	    if(!vp1->child) {
1041 		/* test here: class should == FIELD */
1042 		vp2 = (Varinfo)DXAllocateLocalZero(sizeof(struct varinfo));
1043 		if(!vp2)
1044 		    return NULL;
1045 		vp2->class = vp1->class;
1046 		vp2->varid = vp1->varid;
1047 		vp2->cdfid = vp1->cdfid;
1048 		vp2->recdim = vp1->recdim;
1049 		vp1->class = CLASS_COMPOSITEFIELD;
1050 		vp1->varid = INVALID_VARID;
1051 		vp1->recdim = INVALID_DIMID;
1052 		vp1->child = vp2;
1053 	    }
1054 
1055 	    /* new node for this field */
1056 	    vp2 = (Varinfo)DXAllocateLocalZero(sizeof(struct varinfo));
1057 	    if(!vp2)
1058 		return NULL;
1059 	    vp2->class = CLASS_FIELD;
1060 	    vp2->varid = varid;
1061 	    vp2->recdim = INVALID_DIMID;
1062 	    vp2->cdfid = vp1->cdfid;
1063 
1064 	    /* add to end of composite field list */
1065 	    vp3 = vp1->child;
1066 	    while (vp3->next != NULL)
1067 		vp3 = vp3->next;
1068 
1069 	    vp3->next = vp2;
1070 	    return vp;
1071 	}
1072 
1073 	if(vp1->child)
1074 	    return match_vp(vp1->child, varid, s, stringattr);
1075 
1076 
1077     }
1078 
1079     return NULL;
1080 }
1081 
1082 
1083 /* build a string which describes a field, put it into a string object
1084  *  and return it.
1085  */
1086 static Object
query_field(int cdfhandle,Varinfo vp)1087 query_field(int cdfhandle, Varinfo vp)
1088 {
1089     char stringattr[MAXNAME], *cp, *s[MAXATTRSTR];
1090     int i, rank;
1091     Varinfo nvp;
1092 
1093     Object o = NULL;
1094     Object newo = NULL;
1095     char *sp1 = NULL;
1096 
1097 
1098     switch(vp->class) {
1099       case CLASS_GROUP:
1100 	if(!vp->child)
1101 	    DXErrorReturn(ERROR_INTERNAL, "inconsistant data structs");
1102 
1103 	for(nvp = vp->child; nvp; nvp = nvp->next) {
1104 
1105 	    newo = query_field(cdfhandle, nvp);
1106 	    if(!newo) {
1107 		DXDelete(o);
1108 		return NULL;
1109 	    }
1110 	    if(!o) {
1111 		o = STR("Group: ");
1112 		if(!o) {
1113 		    DXDelete(newo);
1114 		    return NULL;
1115 		}
1116 	    }
1117 	    sp1 = DXAllocate(strlen(DXGetString((String)o)) +
1118 			   strlen(DXGetString((String)newo)));
1119 	    strcpy(sp1, DXGetString((String)o));
1120 	    strcat(sp1, DXGetString((String)newo));
1121 	    strcat(sp1, ";");
1122 	    DXDelete(o);
1123 	    DXDelete(newo);
1124 	    o = STR(sp1);
1125 	}
1126 	break;
1127 
1128       case CLASS_SERIES:
1129 	if(!vp->child) {
1130 	    /* series object all in one N+1 D netcdf variable */
1131 	    o = STR("Series: ");
1132 
1133             newo = query_field(cdfhandle, vp);
1134 	    if(!newo) {
1135 		DXDelete(o);
1136 		return NULL;
1137 	    }
1138 
1139 	    sp1 = DXAllocate(strlen(DXGetString((String)o)) +
1140 			   strlen(DXGetString((String)newo)));
1141 	    strcpy(sp1, DXGetString((String)o));
1142 	    strcat(sp1, DXGetString((String)newo));
1143 	    DXDelete(o);
1144 	    DXDelete(newo);
1145 	    o = STR(sp1);
1146 	    break;
1147 	}
1148 
1149 	for(nvp = vp->child; nvp; nvp = nvp->next) {
1150 
1151             newo = query_field(cdfhandle, nvp);
1152 	    if(!newo) {
1153 		DXDelete(o);
1154 		return NULL;
1155 	    }
1156 	    if(!o) {
1157 		o = STR("Series: ");
1158 		if(!o) {
1159 		    DXDelete(newo);
1160 		    return NULL;
1161 		}
1162 	    }
1163 	    sp1 = DXAllocate(strlen(DXGetString((String)o)) +
1164 			   strlen(DXGetString((String)newo)));
1165 	    strcpy(sp1, DXGetString((String)o));
1166 	    strcat(sp1, DXGetString((String)newo));
1167 	    sprintf(sp1+strlen(sp1), ", position %g;", nvp->position);
1168 	    DXDelete(o);
1169 	    DXDelete(newo);
1170 	    o = STR(sp1);
1171 	}
1172 	break;
1173 
1174       case CLASS_COMPOSITEFIELD:
1175 	if(!vp->child)
1176 	    DXErrorReturn(ERROR_INTERNAL, "inconsistant data structs");
1177 
1178 	for(nvp = vp->child; nvp; nvp = nvp->next) {
1179 
1180 	    newo = query_field(cdfhandle, nvp);
1181 	    if(!newo) {
1182 		DXDelete(o);
1183 		return NULL;
1184 	    }
1185 	    if(!o) {
1186 		o = STR("Composite Field: ");
1187 		if(!o) {
1188 		    DXDelete(newo);
1189 		    return NULL;
1190 		}
1191 	    }
1192 	    sp1 = DXAllocate(strlen(DXGetString((String)o)) +
1193 			   strlen(DXGetString((String)newo)));
1194 	    strcpy(sp1, DXGetString((String)o));
1195 	    strcat(sp1, DXGetString((String)newo));
1196 	    strcat(sp1, ";");
1197 	    DXDelete(o);
1198 	    DXDelete(newo);
1199 	    o = STR(sp1);
1200 	}
1201 	break;
1202 
1203       case CLASS_FIELD:
1204 	if(vp->cdfid < 0 || vp->cdfid != cdfhandle) {
1205             if(vp->cdfname[0] == '\0')
1206                 return NULL;
1207             vp->cdfid = open_netcdf_file(vp->cdfname);
1208             if(vp->cdfid < 0)
1209                 return NULL;
1210         }
1211 	getattr(vp->cdfid, vp->varid, FIELDATTRIB, stringattr, MAXNAME);
1212 
1213 	cp = stringattr;
1214 	i = MAXATTRSTR;
1215 	cp = parseit(cp, &i, s);
1216 
1217 	if(i <= 0)
1218 	    return NULL;
1219 
1220 	/* should already be set in varinfo --
1221 	 *  strcpy(varname, s[0]);
1222 	 */
1223 	rank = setrank(s[1]);
1224 	/* etc etc */
1225 
1226 
1227 #if REALCODE
1228 	sp1 = (char *)DXAllocate(strlen(vp->name) + sizeof("name:") + 2);
1229 	strcpy(sp1, "name:");
1230 	strcat(sp1, vp->name);
1231 	strcat(sp1, ";");
1232 #else
1233 	/* misc whitespace added for testing */
1234 	sp1 = (char *)DXAllocate(1024);
1235 	strcpy(sp1, "name:");
1236 	strcat(sp1, vp->name);
1237 	strcat(sp1, ";contype:tetras;");
1238 	strcat(sp1, "datatype: float;\tdatacat:real;");
1239 	strcat(sp1, "datarank:1; datashape:3;");
1240 	strcat(sp1, "  datacount:37000;\n");
1241 	strcat(sp1, "metahistory:original data;");
1242 	strcat(sp1, "metadesc:3-D seismic data from mars;");
1243 #endif   /* !REALCODE */
1244 
1245 	o = STR(sp1);
1246 	DXFree(sp1);
1247         if(cdfhandle != vp->cdfid) {
1248             close_netcdf_file(vp->cdfid);
1249             vp->cdfid = -1;
1250         }
1251 	break;
1252 
1253       default:
1254 	o = NULL;  /* ??? */
1255 	break;
1256     }
1257 
1258     return o;
1259 }
1260 
1261 
1262 
1263 
1264 /* build an object based on the varinfo structures, and return it
1265  */
1266 static Object
import_field(int cdfhandle,Varinfo vp)1267 import_field(int cdfhandle, Varinfo vp)
1268 {
1269     int i = 0;
1270     Varinfo nvp;
1271 
1272     Object o = NULL;
1273     Object newo = NULL;
1274 
1275 
1276     switch(vp->class) {
1277       case CLASS_GROUP:
1278 	if(!vp->child)
1279 	    DXErrorReturn(ERROR_INTERNAL, "inconsistant data structs");
1280 
1281 	for(nvp = vp->child; nvp; nvp = nvp->next) {
1282 
1283 	    newo = import_field(cdfhandle, nvp);
1284 	    if(!newo) {
1285 		DXDelete(o);
1286 		return NULL;
1287 	    }
1288 	    if(!o) {
1289 		o = (Object)DXNewGroup();
1290 		if(!o) {
1291 		    DXDelete(newo);
1292 		    return NULL;
1293 		}
1294 	    }
1295 	    DXSetMember((Group)o, nvp->name, newo);
1296 	}
1297 	break;
1298 
1299       case CLASS_SERIES:
1300 	if(!vp->child) {
1301             /* series bundled as 1 netcdf variable, using unlimited dim */
1302             o = build_series(cdfhandle, vp);
1303             break;
1304 	}
1305 
1306 	for(nvp = vp->child; nvp; nvp = nvp->next) {
1307 
1308             newo = import_field(cdfhandle, nvp);
1309 	    if(!newo) {
1310 		DXDelete(o);
1311 		return NULL;
1312 	    }
1313 	    if(!o) {
1314 		o = (Object)DXNewSeries();
1315 		if(!o) {
1316 		    DXDelete(newo);
1317 		    return NULL;
1318 		}
1319 		i = 0;
1320 	    }
1321 	    DXSetSeriesMember((Series)o, i++, nvp->position, newo);
1322 	}
1323 	break;
1324 
1325       case CLASS_COMPOSITEFIELD:
1326 	if(!vp->child)
1327 	    DXErrorReturn(ERROR_INTERNAL, "inconsistant data structs");
1328 
1329 	for(nvp = vp->child; nvp; nvp = nvp->next) {
1330 
1331 	    newo = import_field(cdfhandle, nvp);
1332 	    if(!newo) {
1333 		DXDelete(o);
1334 		return NULL;
1335 	    }
1336 	    if(!o) {
1337 		o = (Object)DXNewCompositeField();
1338 		if(!o) {
1339 		    DXDelete(newo);
1340 		    return NULL;
1341 		}
1342 	    }
1343 	    DXSetMember((Group)o, NULL, newo);
1344 	}
1345 	break;
1346 
1347       case CLASS_FIELD:
1348 
1349 	if(vp->cdfid < 0 || vp->cdfid != cdfhandle) {
1350             if(vp->cdfname[0] == '\0')
1351                 return NULL;
1352             vp->cdfid = open_netcdf_file(vp->cdfname);
1353             if(vp->cdfid < 0)
1354                 return NULL;
1355         }
1356 
1357 	o = build_field(vp->cdfid, vp->varid);
1358 
1359         if(cdfhandle != vp->cdfid) {
1360             close_netcdf_file(vp->cdfid);
1361             vp->cdfid = -1;
1362         }
1363 
1364 	if (o && vp->name)
1365 	    DXSetAttribute(o, "name", (Object)DXNewString(vp->name));
1366 
1367 	break;
1368 
1369       default:
1370 	DXSetError(ERROR_INTERNAL, "bad class in varinfo struct");
1371 	DXDelete(o);
1372 	return NULL;
1373     }
1374 
1375     return o;
1376 }
1377 
1378 
1379 /* delete a varinfo tree
1380  */
vp_delete(Varinfo vp)1381 static void vp_delete(Varinfo vp)
1382 {
1383     Varinfo vp1, vp2;
1384 
1385     for(vp2 = vp; vp2; ) {
1386 	if(vp2->child)
1387 	    vp_delete(vp2->child);
1388 
1389 	vp1 = vp2;
1390 	vp2 = vp2->next;
1391 	DXFree((Pointer)vp1);
1392 
1393     }
1394 }
1395 
1396 
1397 /* debug - print a varinfo tree
1398  */
vp_print(Varinfo vp)1399 static void vp_print(Varinfo vp)
1400 {
1401     Varinfo vp1, vp2;
1402 
1403     for(vp2 = vp; vp2; ) {
1404 	if(vp2->child)
1405 	    vp_print(vp2->child);
1406 
1407 	vp1 = vp2;
1408 	vp2 = vp2->next;
1409 	printf("%08x: class = %d, varid = %d, name = %s\n",
1410 	       (unsigned int) vp1, vp1->class, vp1->varid, vp1->name);
1411 	printf("          next = %08x, child = %08x\n", (unsigned int) vp1->next, (unsigned int) vp1->child);
1412 
1413     }
1414 }
1415 
1416 /*
1417  * use netCDF routines to retrieve the named attribute, verify it is a
1418  *  string (char), and null terminate it.
1419  */
1420 static char *
getattr(int hand,int varid,char * attrname,char * stringattr,int maxlen)1421 getattr(int hand, int varid, char *attrname, char *stringattr,int maxlen)
1422 {
1423     nc_type datatype;
1424     int len;
1425     char *tmpattr;
1426 
1427     if(ncattinq(hand, varid, attrname, &datatype, &len) < 0)
1428 	return NULL;
1429 
1430     if(datatype != NC_CHAR || len <= 0)
1431 	return NULL;
1432 
1433     tmpattr=(char *)DXAllocate(len+1);
1434     ncattget(hand, varid, attrname, tmpattr);
1435     tmpattr[len] = '\0';
1436 
1437     strncpy(stringattr,tmpattr,maxlen);
1438     stringattr[maxlen-1]='\0';
1439 
1440     if(strncmp(stringattr,tmpattr,maxlen))
1441       DXWarning("String attribute truncated: `%s' -> `%s'",
1442               tmpattr,stringattr);
1443 
1444     DXFree((Pointer) tmpattr);
1445     return stringattr;
1446 }
1447 /*
1448  * use netCDF routines to retrieve the Nth attribute of a variable and
1449  *  compare the first len characters for a match.  if matched, get the
1450  *  complete value of the attribute, verify it is a string (char), and
1451  *  null terminate it.
1452  *  The attribute is returned as a newly allocated string. It is the
1453  *  caller's responsibility the deallocate it when it is no longer
1454  *  needed.
1455  */
1456 static char *
getNattr(int hand,int varid,int n,char * attrname,int len,char * stringattr)1457 getNattr(int hand, int varid, int n, char *attrname, int len, char *stringattr)
1458 {
1459     nc_type datatype;
1460     int i, alen;
1461     char *attrtext=NULL;
1462 
1463     for(i = n; ; i++) {
1464         if(ncattname(hand, varid, i, stringattr) < 0)
1465             return NULL;
1466 
1467         if(strncmp(stringattr, attrname, len))
1468             continue;
1469 
1470         /* reset attribute name to the one retrieved from netcdf */
1471         attrname = stringattr;
1472         break;
1473     }
1474 
1475     if(ncattinq(hand, varid, attrname, &datatype, &alen) < 0)
1476 	return NULL;
1477 
1478     if(datatype != NC_CHAR || alen <= 0)
1479 	return NULL;
1480 
1481     /* does this work?  we are actually using the same buffer for the
1482      *  input attribute name and for the returned attribute value.
1483      ncattget(hand, varid, attrname, stringattr);
1484      */
1485     /* No it does not. Make sure the target string has sufficient length */
1486     attrtext=DXAllocate(alen+1);
1487     ncattget(hand, varid, attrname, attrtext);
1488     attrtext[alen] = '\0';
1489 
1490     return attrtext;
1491 }
1492 
1493 /*
1494  * test just to see if there is such a named attribute for this variable.
1495  */
1496 static char *
isattr(int hand,int varid,char * attrname)1497 isattr(int hand, int varid, char *attrname)
1498 {
1499     nc_type datatype;
1500     int len;
1501 
1502     if(ncattinq(hand, varid, attrname, &datatype, &len) < 0)
1503 	return NULL;
1504 
1505     return attrname;
1506 
1507 }
1508 
1509 /*
1510  * see if this variable has any ADX attributes, and use them to set
1511  *  the component attributes.
1512  */
setattr(Arrayinfo ap,Field f,char * compname)1513 static Error setattr(Arrayinfo ap, Field f, char *compname)
1514 {
1515     char *cp, *s[MAXATTRSTR];
1516     char savename[MAXCOMPNAME];
1517     int j;
1518 
1519     /* save the component name now, because it may get overwritten
1520      *  further down
1521      */
1522     strcpy(savename, compname);
1523 
1524     /* see if there is an 'attribute' attribute on this variable
1525      */
1526     if(!getattr(ap->cdfhandle, ap->varid, ATTRATTRIB, ap->stringattr,MAXNAME)
1527      && !getattr(ap->cdfhandle, ap->varid, OATTRATTRIB, ap->stringattr,MAXNAME))
1528         goto done;
1529 
1530 
1531     cp = ap->stringattr;
1532     while(1) {
1533         j = MAXATTRSTR;
1534         cp = parseit(cp, &j, s);
1535 
1536         /* field:attribute = "a, b";  attribute required.  exactly two
1537          *  parameters must be present.
1538          *   a = attribute to set.
1539          *   b = string to set it to.
1540          */
1541         if(j <= 0)
1542             break;
1543         if(j != 2) {
1544             DXSetError(ERROR_BAD_PARAMETER,
1545                      "bad attribute value on component '%s'", savename);
1546             return ERROR;
1547         }
1548 
1549 
1550         /* and add it to the field with the given component name */
1551         if(!DXSetComponentAttribute(f, savename, s[0], STR(s[1]))) {
1552             DXSetError(ERROR_BAD_PARAMETER,
1553                      "bad attribute value: '%s' on component '%s'",
1554                      s[0], savename);
1555             return ERROR;
1556 
1557 	}
1558     }
1559 
1560 done:
1561     /* look for non DX specific attributes and make them component
1562      * attributes
1563      */
1564     setuserattr(ap,f,savename);
1565 
1566     return OK;
1567 }
1568 
1569 /* go through each attribute and for each one that is not a
1570  * DX netcdf attribute, create a DX object and make it a
1571  * component attribute
1572  */
setuserattr(Arrayinfo ap,Field f,char * compname)1573 static Error setuserattr(Arrayinfo ap, Field f, char *compname)
1574 {
1575   int i, attr_count=0;
1576   int alen;
1577   nc_type datatype;
1578   Type type;
1579   Array a;
1580   char *attr_string;
1581   void *attr_value;
1582 
1583   while(ncattname(ap->cdfhandle,ap->varid,attr_count,ap->stringattr) !=-1) {
1584     attr_count++;
1585     /* is this a DX specific attribute, if yes ignore */
1586     for (i=0; i<MAX_DXATTRIBUTES; i++){
1587       if (!strcmp(dxattributes[i],ap->stringattr))
1588 	break;
1589     }
1590     if (i < MAX_DXATTRIBUTES) 	/* skip the dx attributes */
1591       continue;
1592 
1593     if(ncattinq(ap->cdfhandle, ap->varid, ap->stringattr, &datatype, &alen) < 0)
1594 	return ERROR;
1595 
1596     /* convert from netCDF data flags to SVS data flags */
1597     switch(datatype) {
1598       case NC_CHAR:   type = TYPE_STRING; break;
1599       case NC_SHORT:  type = TYPE_SHORT;  break;
1600       case NC_LONG:   type = TYPE_INT;    break;
1601       case NC_FLOAT:  type = TYPE_FLOAT;  break;
1602       case NC_DOUBLE: type = TYPE_DOUBLE; break;
1603       default:        type = TYPE_UBYTE;  break;
1604 	/* we need a TYPE_BYTE */
1605     }
1606 
1607     if (type == TYPE_STRING){
1608       attr_string = (char *)DXAllocate(alen+1);
1609       if (ncattget(ap->cdfhandle, ap->varid,ap->stringattr,attr_string) < 0)
1610         return ERROR;
1611       attr_string[alen] = '\0';
1612       DXSetComponentAttribute(f, compname, ap->stringattr, STR(attr_string));
1613       DXFree((Pointer)attr_string);
1614     }
1615     else{
1616       a = DXNewArray(type,CATEGORY_REAL,0);
1617       a = DXAddArrayData(a,0,alen,NULL);
1618       attr_value = DXGetArrayData(a);
1619       if (ncattget(ap->cdfhandle, ap->varid,ap->stringattr,attr_value) < 0)
1620         return ERROR;
1621       DXSetComponentAttribute(f,compname, ap->stringattr, (Object)a);
1622     }
1623   }
1624   return OK;
1625 }
1626 
1627 /* go through each attribute and for each one that is not a
1628  * DX netcdf attribute, create a DX object and make it a
1629  * component attribute
1630  */
getglobalattr(Object o,Varinfo vp)1631 static Error getglobalattr(Object o, Varinfo vp)
1632 {
1633   int attr_count=0;
1634   int alen;
1635   nc_type datatype;
1636   Type type;
1637   Array a;
1638   char stringattr[MAXNAME];
1639   char *attr_string;
1640   void *attr_value;
1641 
1642 	if( !o ) {
1643 		if (DXGetError() == ERROR_NONE)
1644 		DXErrorReturn(ERROR_INTERNAL,"getglobalattr called without a proper object");
1645 	}
1646 
1647   while(ncattname(vp->cdfid,NC_GLOBAL,attr_count,stringattr) !=-1) {
1648     attr_count++;
1649     /* is this a DX specific attribute, if yes ignore */
1650     if (!strcmp(SERIESATTRIB,stringattr))
1651 	continue;
1652 
1653     if(ncattinq(vp->cdfid, NC_GLOBAL, stringattr, &datatype, &alen) < 0)
1654 	return ERROR;
1655 
1656     /* convert from netCDF data flags to SVS data flags */
1657     switch(datatype) {
1658       case NC_CHAR:   type = TYPE_STRING; break;
1659       case NC_SHORT:  type = TYPE_SHORT;  break;
1660       case NC_LONG:   type = TYPE_INT;    break;
1661       case NC_FLOAT:  type = TYPE_FLOAT;  break;
1662       case NC_DOUBLE: type = TYPE_DOUBLE; break;
1663       default:        type = TYPE_UBYTE;  break;
1664 	/* we need a TYPE_BYTE */
1665     }
1666 
1667     if (type == TYPE_STRING){
1668       attr_string = (char *)DXAllocate(alen+1);
1669       if (ncattget(vp->cdfid, NC_GLOBAL,stringattr,attr_string) < 0)
1670         return ERROR;
1671       attr_string[alen] = '\0';
1672       DXSetAttribute(o, stringattr, STR(attr_string));
1673       DXFree((Pointer)attr_string);
1674     }
1675     else{
1676       a = DXNewArray(type,CATEGORY_REAL,0);
1677       a = DXAddArrayData(a,0,alen,NULL);
1678       attr_value = DXGetArrayData(a);
1679       if (ncattget(vp->cdfid, NC_GLOBAL,stringattr,attr_value) < 0)
1680         return ERROR;
1681       DXSetAttribute(o, stringattr, (Object)a);
1682     }
1683   }
1684   return OK;
1685 }
1686 
1687 /* this is where a single netcdf variable is an entire series, with the
1688  *  unlimited record dimension interpreted as the series value, and the
1689  *  data as numdims-1.
1690  */
build_series(int hand,Varinfo vp)1691 static Object build_series(int hand, Varinfo vp)
1692 {
1693     int arraytype = I_NONE;
1694     int series = 0, varseries;
1695     int nopos = 0, nocon = 0;
1696     struct arrayinfo a, *ap = &a;
1697     char *cp, *s[MAXATTRSTR];
1698     int i, j, k;
1699     int varid = vp->varid;
1700     int recdim = vp->recdim;
1701     int nterms;
1702     float value, *valuelist = NULL;
1703     Field f = NULL;
1704     Object indata, arr, terms[MAXRANK], *t = NULL;
1705     Object eltype = NULL;
1706     Object o = NULL;
1707 
1708 
1709     /* set up the Arrayinfo struct
1710      */
1711     ap->cdfhandle = hand;
1712     ap->varid = varid;
1713     ap->recdim = recdim;
1714     ap->vp = vp;
1715 
1716 
1717     /* get the attribute value.
1718      */
1719     if(!getattr(hand, varid, FIELDATTRIB, ap->stringattr,MAXNAME))
1720 	return NULL;
1721 
1722     cp = ap->stringattr;
1723     j = MAXATTRSTR;
1724     cp = parseit(cp, &j, s);
1725     if(j <= 0)
1726 	return NULL;
1727 
1728 
1729     if (!check_serieslength(ap))
1730 	return NULL;
1731 
1732     series = get_serieslength(ap);
1733     if(series <= 0)
1734 	DXErrorReturn(ERROR_DATA_INVALID, "no members in series dimension");
1735 
1736 
1737     o = (Object)DXNewSeries();
1738 
1739 
1740     /*
1741      * data component
1742      */
1743 
1744     /* decide if it's scalar, vector or matrix data */
1745     ap->arrayrank = setrank(s[1]);
1746 
1747     varseries = is_seriesvariable(ap->cdfhandle, ap->varid, ap->recdim);
1748     ap->data_isrec = varseries;
1749 
1750     /* this routine reads a netCDF variable into an Array.
1751      *  in this case, it's reading in the data component.
1752      */
1753     if(varseries) {
1754 	indata = build_data(ap, recdim);
1755 	if(!get_seriesvalue(ap, &valuelist))
1756 	    goto error;
1757     } else
1758 	indata = build_data(ap, INVALID_DIMID);
1759 
1760     if(!indata)
1761 	goto error;
1762 
1763     for(i=0; i<series; i++) {
1764 	f = DXNewField();
1765 	if(!f)
1766 	    goto error;
1767 
1768 	arr = varseries ? DXGetSeriesMember((Series)indata, i, &value)
1769 	                : indata;
1770 
1771 	if(!DXSetComponentValue(f, DATCOMPNAME, arr))
1772 	    goto error;
1773 
1774 	/* look here for more attributes on the data component - to set
1775 	 *  data component attributes.
1776 	 */
1777 	if(!setattr(ap, f, DATCOMPNAME))
1778 	    goto error;
1779 
1780 
1781 	if(varseries && valuelist)
1782 	    value = valuelist[i];
1783 #if 0
1784 	else
1785 	    value = i;
1786 #endif
1787 
1788 	o = (Object)DXSetSeriesMember((Series)o, i, value, (Object)f);
1789 
1790     }
1791 
1792     /* if the data was a series-in-a-variable, delete the temporary group
1793      *  holding the data arrays.
1794      */
1795     if(varseries) {
1796 	DXDelete(indata);
1797 	DXFree((Pointer)valuelist);
1798     }
1799 
1800     /*
1801      * positions component
1802      */
1803 
1804     ap->arrayrank = 1;   /* unless they explicitly say 'scalar' */
1805     varseries = 0;
1806 
1807     /* look for a positions (old points) component.  if the data variable
1808      *  doesn't have a 'positions' attribute, assume a regularly spaced grid.
1809      */
1810     if(!getattr(hand, varid, GEOMATTRIB, ap->stringattr,MAXNAME)
1811     && !getattr(hand, varid, OGEOMATTRIB, ap->stringattr,MAXNAME)) {
1812         nterms = 1;
1813 	indata = build_poscon(ap, I_REGULAR_P, 0);
1814 	terms[0] = indata;
1815 
1816     } else {
1817 	/* else they gave us a geometry.  parse it.
1818 	 */
1819 
1820 	if(isattr(hand, varid, OGEOMATTRIB))
1821 	    DXWarning("'geometry' attribute obsolete, use 'positions' instead");
1822 
1823 	cp = ap->stringattr;
1824         nterms = 0;
1825 	while(1) {
1826 	    j = MAXATTRSTR;
1827 	    cp = parseit(cp, &j, s);
1828 	    if(j <= 0)
1829 		break;
1830 
1831 	    /* positions are vectors unless specifically told scalar */
1832 	    if((j > 1 && !strcmp("scalar", s[1]))
1833 	     ||(j > 2 && !strcmp("scalar", s[2])))
1834 		ap->arrayrank = 0;
1835 
1836 	    if(!strcmp("none", s[0])) {
1837 
1838 		/* no positions - should this be ok? */
1839 		nopos++;
1840 
1841 	    } else if(!strcmp("regular", s[0])) {
1842 
1843 		/* 'regular' is same as default */
1844 		arraytype = I_REGULAR_P;
1845 
1846 	    } else if((j > 1 && !strcmp("product", s[1]))
1847                     ||(j > 2 && !strcmp("product", s[2]))) {
1848 
1849 
1850 		if((k = ncvarid(hand, s[0])) < 0) {
1851 		    DXSetError(ERROR_BAD_PARAMETER,"positions variable missing");
1852 		    goto error;
1853 		}
1854                 ap->varid = k;
1855 		varseries = is_seriesvariable(ap->cdfhandle, ap->varid,
1856 					      ap->recdim);
1857 
1858 		/* regular grid, non-zero origin or non-unit spacing */
1859 		if((j > 1 &&
1860                    (!strcmp("regular", s[1]) || !strcmp("compact", s[1])))
1861                 || (j > 2 &&
1862                    (!strcmp("regular", s[2]) || !strcmp("compact", s[2])))) {
1863 
1864 		    arraytype = I_PRODUCT_P;
1865 
1866 		} else {
1867 
1868                     arraytype = I_IRREGULAR_P;
1869                 }
1870 
1871 
1872 	    } else {
1873 		/* else positions isn't regular, or is grid
1874 		 *  but not 0.0 origin or 1.0 deltas.
1875 		 */
1876 		if((k = ncvarid(hand, s[0])) < 0) {
1877 		    DXSetError(ERROR_BAD_PARAMETER,"positions variable missing");
1878 		    goto error;
1879 		}
1880                 ap->varid = k;
1881 		varseries = is_seriesvariable(ap->cdfhandle, ap->varid,
1882 					      ap->recdim);
1883 
1884 		/* regular grid, non-zero origin or non-unit spacing */
1885 		if((j > 1 &&
1886                    (!strcmp("regular", s[1]) || !strcmp("compact", s[1])))
1887                 || (j > 2 &&
1888                    (!strcmp("regular", s[2]) || !strcmp("compact", s[2])))) {
1889 
1890 		    if(j > 2 && !strcmp("product", s[2]))
1891                         arraytype = I_PRODUCT_P;
1892                     else
1893                         arraytype = I_COMPACT_P;
1894 
1895 		} else {
1896 
1897                     arraytype = I_IRREGULAR_P;
1898                 }
1899 
1900 	    }
1901 
1902             if(nopos)
1903                 break;
1904 
1905 	    indata = build_poscon(ap, arraytype, nterms);
1906 	    terms[nterms] = indata;
1907             nterms++;
1908         }
1909     }
1910 
1911     /* if there is a positions component */
1912     if(!nopos) {
1913 	if(!varseries) {
1914 	    if(nterms > 1) {
1915 		indata = (Object)DXNewProductArrayV(nterms, (Array *)terms);
1916 		if(!indata) {
1917 		    DXSetError(ERROR_BAD_PARAMETER, "bad positions attribute");
1918 		    goto error;
1919 		}
1920 	    }
1921 
1922 	    for(i=0; i < series; i++) {
1923 
1924 		f = (Field)DXGetEnumeratedMember((Group)o, i, NULL);
1925 
1926 		/* and add it to field */
1927 		f = DXSetComponentValue(f, GEOCOMPNAME, indata);
1928 		f = DXSetComponentAttribute(f, DATCOMPNAME,
1929 					  "dep", STR(GEOCOMPNAME));
1930 		if(!f)
1931 		    goto error;
1932 
1933 		/* and check for attributes if another variable was used */
1934 		if(ap->varid != varid && !setattr(ap, f, GEOCOMPNAME))
1935 		    goto error;
1936 
1937 	    }
1938 
1939 	} else {
1940 
1941 	    if(nterms > 1) {
1942 		t = (Object *)DXAllocateLocalZero(series * nterms * sizeof(Array));
1943 		if(!t)
1944 		    goto error;
1945 
1946 		for(k=0; k < nterms; k++)
1947 		    for(j=0; j < series; j++)
1948 			t[j * nterms + k] =
1949 			  DXGetEnumeratedMember((Group)terms[k], j, NULL);
1950 
1951 	    }
1952 
1953 	    for(j=0; j < series; j++) {
1954 
1955 		f = (Field)DXGetEnumeratedMember((Group)o, j, NULL);
1956 
1957 		if(nterms > 1) {
1958 		    arr = (Object)DXNewProductArrayV(nterms,
1959 						   (Array *)&t[j * nterms]);
1960 		    if(!arr) {
1961 			DXSetError(ERROR_BAD_PARAMETER,
1962 				 "bad positions attribute");
1963 			goto error;
1964 		    }
1965 		} else
1966 		    arr = DXGetEnumeratedMember((Group)indata, j, NULL);
1967 
1968 		/* and add it to field */
1969 		f = DXSetComponentValue(f, GEOCOMPNAME, arr);
1970 		f = DXSetComponentAttribute(f, DATCOMPNAME,
1971 					  "dep", STR(GEOCOMPNAME));
1972 		if(!f)
1973 		    goto error;
1974 
1975 		/* and check for attributes if another variable was used */
1976 		if(ap->varid != varid && !setattr(ap, f, GEOCOMPNAME))
1977 		    goto error;
1978 	    }
1979 
1980 	    if(t)
1981 		DXFree((Pointer)t);
1982 	}
1983 
1984 	/* delete anything which is a series-in-a-single-netcdf-variable,
1985          *  because a temporary Group object was constructed to hold it.
1986          */
1987 	for(j=0; j<nterms; j++)
1988 	    if(DXGetObjectClass(terms[j]) == CLASS_GROUP)
1989 		DXDelete(terms[j]);
1990     }
1991 
1992 
1993     /*
1994      * connections component
1995      */
1996     varseries = 0;
1997 
1998     /* now look for the 'connections' information.  if there isn't
1999      *  anything, assume it's a regular grid.  if there is an attribute
2000      *  for 'connections' (currently 'topology' also still accepted in the
2001      *  netCDF file), parse it.  if it's 'none', there isn't a topology
2002      *  component.  if it's 'regular', set it to a regular grid.
2003      *  otherwise, it should be the name of another netCDF variable
2004      *  which contains the connections information.
2005      */
2006     if(!getattr(hand, varid, TOPOATTRIB, ap->stringattr,MAXNAME)
2007     && !getattr(hand, varid, OTOPOATTRIB, ap->stringattr,MAXNAME)) {
2008         nterms = 1;
2009 	indata = build_poscon(ap, I_REGULAR_C, 0);
2010         eltype = STR(regularname[ap->data_ndims]);
2011 	terms[0] = indata;
2012 
2013     } else {
2014 
2015 	/* a connections attribute.  parse it */
2016 	if(isattr(hand, varid, OTOPOATTRIB))
2017            DXWarning("'topology' attribute obsolete, use 'connections' instead");
2018 
2019 	cp = ap->stringattr;
2020         nterms = 0;
2021 	while(1) {
2022 	    j = MAXATTRSTR;
2023 	    cp = parseit(cp, &j, s);
2024 	    if(j <= 0)
2025 		break;
2026 
2027 	    /* no topology */
2028 	    if(!strcmp("none", s[0])) {
2029                 nocon++;
2030 
2031 	    } else if(!strcmp("regular", s[0])) {
2032 
2033 		/* also accept this as regular grid */
2034                 arraytype = I_REGULAR_C;
2035                 eltype = STR(regularname[ap->data_ndims]);
2036 
2037 	    } else if(j > 1 && !strcmp("product", s[1])) {
2038 
2039 		if((k = ncvarid(hand, s[0])) < 0) {
2040 		    DXSetError(ERROR_BAD_PARAMETER,
2041                              "connections variable missing");
2042 		    goto error;
2043 		}
2044                 ap->varid = k;
2045 		varseries = is_seriesvariable(ap->cdfhandle, ap->varid,
2046 					      ap->recdim);
2047 
2048                 arraytype = I_PRODUCT_C;
2049 
2050 		/* if irregular, they must specify an element type */
2051 		if(j < 3 || !s[2] || !s[2][0]) {
2052 		    DXSetError(ERROR_BAD_PARAMETER,
2053 			     "'element type' must be specified");
2054 		    goto error;
2055                 } else
2056                     eltype = STR(s[2]);
2057 	    } else {
2058 
2059 		/* irregular - look for the other netCDF variable */
2060 		if((k = ncvarid(hand, s[0])) < 0) {
2061 		    DXSetError(ERROR_BAD_PARAMETER,
2062                              "connections variable missing");
2063 		    goto error;
2064 		}
2065                 ap->varid = k;
2066 		varseries = is_seriesvariable(ap->cdfhandle, ap->varid,
2067 					      ap->recdim);
2068 
2069                 arraytype = I_IRREGULAR_C;
2070 
2071 		/* if irregular, they must specify an element type */
2072 		if(j < 2 || !s[1] || !s[1][0]) {
2073 		    DXSetError(ERROR_BAD_PARAMETER,
2074 			     "'element type' must be specified");
2075 		    goto error;
2076                 } else
2077                     eltype = STR(s[1]);
2078 	    }
2079 
2080             if(nocon)
2081                 break;
2082 
2083 	    indata = build_poscon(ap, arraytype, nterms);
2084             terms[nterms] = indata;
2085             nterms++;
2086 	}
2087 
2088     }
2089 
2090 
2091     /* if there is a connections component */
2092     if(!nocon) {
2093 	if(!varseries) {
2094 	    if(nterms > 1) {
2095 		indata = (Object)DXNewMeshArrayV(nterms, (Array *)terms);
2096 		if(!indata) {
2097 		    DXSetError(ERROR_BAD_PARAMETER, "bad connections attribute");
2098 		    goto error;
2099 		}
2100 	    }
2101 
2102 	    for(j=0; j < series; j++) {
2103 
2104 		f = (Field)DXGetEnumeratedMember((Group)o, j, NULL);
2105 
2106 		/* and add it to field */
2107 		f = DXSetComponentValue(f, TOPCOMPNAME, indata);
2108 		f = DXSetComponentAttribute(f, TOPCOMPNAME,
2109 					  "element type", eltype);
2110 		f = DXSetComponentAttribute(f, TOPCOMPNAME,
2111 					  "ref", STR(GEOCOMPNAME));
2112 		if(!f)
2113 		    goto error;
2114 
2115 
2116 		/* and check for attributes if another variable was used */
2117 		if(ap->varid != varid && !setattr(ap, f, TOPCOMPNAME))
2118 		    goto error;
2119 
2120 	    }
2121 
2122 	} else {
2123 
2124 	    if(nterms > 1) {
2125 		t = (Object *)DXAllocateLocalZero(series * nterms * sizeof(Array));
2126 		if(!t)
2127 		    goto error;
2128 
2129 		for(k=0; k < nterms; k++)
2130 		    for(j=0; j < series; j++)
2131 			t[j * nterms + k] =
2132 			  DXGetEnumeratedMember((Group)terms[k], j, NULL);
2133 
2134 	    }
2135 
2136 	    for(j=0; j < series; j++) {
2137 
2138 		f = (Field)DXGetEnumeratedMember((Group)o, j, NULL);
2139 
2140 		if(nterms > 1) {
2141 		    arr = (Object)DXNewMeshArrayV(nterms,
2142 						(Array *)&t[j * nterms]);
2143 		    if(!arr) {
2144 			DXSetError(ERROR_BAD_PARAMETER,
2145 				 "bad connections attribute");
2146 			goto error;
2147 		    }
2148 		} else
2149 		    arr = DXGetEnumeratedMember((Group)indata, j, NULL);
2150 
2151 		/* and add it to field */
2152 		f = DXSetComponentValue(f, TOPCOMPNAME, arr);
2153 		f = DXSetComponentAttribute(f, TOPCOMPNAME,
2154 					  "element type", eltype);
2155 		f = DXSetComponentAttribute(f, TOPCOMPNAME,
2156 					  "ref", STR(GEOCOMPNAME));
2157 		if(!f)
2158 		    goto error;
2159 
2160 
2161 		/* and check for attributes if another variable was used */
2162 		if(ap->varid != varid && !setattr(ap, f, TOPCOMPNAME))
2163 		    goto error;
2164 	    }
2165 
2166 	    if(t)
2167 		DXFree((Pointer)t);
2168 	}
2169 
2170 	/* delete anything which is a series-in-a-single-netcdf-variable,
2171          *  because a temporary Group object was constructed to hold it.
2172          */
2173 	for(j=0; j<nterms; j++)
2174 	    if(DXGetObjectClass(terms[j]) == CLASS_GROUP)
2175 		DXDelete(terms[j]);
2176 
2177     }
2178 
2179 
2180 
2181     /*
2182      * additional components
2183      */
2184 
2185     /* are there additional components that should be added?
2186      */
2187     if(getattr(hand, varid, COMPATTRIB, ap->stringattr,MAXNAME)) {
2188 
2189 	cp = ap->stringattr;
2190 	while(1) {
2191 	    j = MAXATTRSTR;
2192 	    cp = parseit(cp, &j, s);
2193 
2194 	    /* field:component = "a, b, c";  attribute required.  all three
2195 	     *  parameters must be present for additional components.
2196 	     *   a = name of variable.
2197 	     *   b = component name
2198 	     *   c = rank (scalar, vector, matrix, tensor)
2199 	     */
2200 	    if(j <= 0)
2201                 break;
2202 
2203             if(j < 3) {
2204                 DXSetError(ERROR_BAD_PARAMETER,
2205                          "varname, compname, shape required");
2206 		goto error;
2207             }
2208 
2209 	    if((k = ncvarid(hand, s[0])) < 0) {
2210 		DXSetError(ERROR_BAD_PARAMETER,
2211 			 "component variable '%s' not found", s[0]);
2212 		goto error;
2213 	    }
2214 
2215             ap->varid = k;
2216 	    ap->arrayrank = setrank(s[2]);
2217 	    varseries = is_seriesvariable(ap->cdfhandle, ap->varid, ap->recdim);
2218 
2219 	    /* actually read the data into an Array */
2220 	    if(!(indata = build_array(ap)))
2221 		goto error;
2222 
2223             for(i=0; i<series; i++) {
2224 
2225 		f = (Field)DXGetEnumeratedMember((Group)o, i, NULL);
2226 
2227 		/* and add it to the field with the given component name */
2228 		arr = varseries ? DXGetEnumeratedMember((Group)indata, i, NULL)
2229 		                : indata;
2230 
2231 		if(!DXSetComponentValue(f, s[1], arr))
2232 		    goto error;
2233 
2234 		/* and check for attributes */
2235 		if(!setattr(ap, f, s[1]))
2236 		    goto error;
2237 
2238 	    }
2239 
2240 	    if(varseries)
2241 		DXDelete(indata);
2242 	}
2243 
2244     }
2245 
2246 #ifdef DO_ENDFIELD
2247     for(i=0; i<series; i++)
2248 	DXEndField((Field)DXGetSeriesMember((Series)o, i, &value));
2249 #endif
2250 
2251     return o;
2252 
2253   error:
2254     DXDelete((Object)f);
2255     DXDelete(o);
2256     DXFree((Pointer)t);
2257     return NULL;
2258 
2259 }
2260 
2261 
2262 /* verify that start and end are not out of range.
2263  */
check_serieslength(Arrayinfo ap)2264 static Error check_serieslength(Arrayinfo ap)
2265 {
2266     long size;
2267 
2268     if(ncdiminq(ap->cdfhandle,		/* file descriptor */
2269 		ap->recdim,		/* record dimension id */
2270 		(char *)0,		/* dimension name */
2271 		&size) < 0)		/* record size */
2272 	DXErrorReturn(ERROR_INTERNAL, "netCDF library error");
2273 
2274     if (ap->vp->startframe && *ap->vp->startframe < 0) {
2275 	DXSetError(ERROR_BAD_PARAMETER, "start must be a non-negative integer");
2276 	return ERROR;
2277     }
2278 
2279     if (ap->vp->endframe && *ap->vp->endframe > size) {
2280 	DXSetError(ERROR_BAD_PARAMETER, "end is larger than series members");
2281 	return ERROR;
2282     }
2283 
2284     return OK;
2285 }
2286 
2287 /* given the dimension number of the unlimited dimension, find out how
2288  *  long it is.
2289  */
get_serieslength(Arrayinfo ap)2290 static int get_serieslength(Arrayinfo ap)
2291 {
2292     int cnt;
2293     long size;
2294     int start, end, delta;
2295 
2296     if(ncdiminq(ap->cdfhandle,		/* file descriptor */
2297 		ap->recdim,		/* record dimension id */
2298 		(char *)0,		/* dimension name */
2299 		&size) < 0)		/* record size */
2300 	DXErrorReturn(ERROR_INTERNAL, "netCDF library error");
2301 
2302     start = ap->vp->startframe ? *ap->vp->startframe : 0;
2303     end = ap->vp->endframe ? *ap->vp->endframe : size-1;
2304     delta = ap->vp->deltaframe ? *ap->vp->deltaframe : 1;
2305 
2306     if(delta == 0)
2307 	return 0;
2308 
2309     cnt = 0;
2310     for (size=start; size<=end; size+=delta)
2311 	cnt++;
2312 
2313     return (cnt);
2314 }
2315 
2316 /* given a variable and the record dimension id, return whether that variable
2317  *  uses that id.
2318  */
is_seriesvariable(int hand,int varid,int recdim)2319 static int is_seriesvariable(int hand, int varid, int recdim)
2320 {
2321     nc_type datatype;
2322     int natts, ndims;
2323     int dim[MAX_VAR_DIMS];
2324 
2325     /* inquire about the variable - it's size, shape and type */
2326     if(ncvarinq(hand,                      /* file descriptor */
2327 		varid,                     /* variable ID */
2328 		(char *)0,                 /* variable name */
2329 		&datatype,                 /* data item type */
2330 		&ndims,                    /* number of dimensions */
2331 		dim,                       /* array of dimension ID's */
2332 		&natts) < 0)               /* number of attributes */
2333 	DXErrorReturn(ERROR_INTERNAL, "netCDF library error");
2334 
2335 
2336     /* if the first variable is the unlimited dimension, this is a series.
2337      */
2338     return (dim[0] == recdim);
2339 }
2340 
2341 
2342 /* given a variable which uses the record dimension, and an index,
2343  *  find out what number is associated with that record number.
2344  */
get_seriesvalue(Arrayinfo ap,float ** valuelist)2345 static Error get_seriesvalue(Arrayinfo ap, float **valuelist)
2346 {
2347 #if 0
2348     return (float)index;
2349 #else
2350     /* bother.  what makes sense for this is: to have a separate netCDF
2351      *  variable, a 1D array using the unlimited dimension, which contains
2352      *  the series value for each dimension.  but how to you specify it?
2353      *  as a 'var1:series = seriesvar' attribute on the data field?
2354      */
2355     nc_type datatype;
2356     int varid;
2357     int i, j, natts, ndims;
2358     int dim[MAX_VAR_DIMS];
2359     long size, start;
2360     int delta;
2361     char *s[MAXATTRSTR];
2362 
2363 
2364     /* value list comes in as NULL.  if there is no series positions
2365      *  arrray, just return.  else try to read in the variable, allocate
2366      *  space for the array, and return with valuelist pointing to it.
2367      */
2368     if(!getattr(ap->cdfhandle, ap->varid, SERIESPOSATTRIB, ap->stringattr,MAXNAME))
2369 	return OK;
2370 
2371     j = MAXATTRSTR;
2372     parseit(ap->stringattr, &j, s);
2373 
2374     if(j != 1)
2375 	DXErrorReturn(ERROR_DATA_INVALID, "bad seriesposition attribute");
2376 
2377     if((varid = ncvarid(ap->cdfhandle, s[0])) < 0) {
2378 	DXSetError(ERROR_BAD_PARAMETER,
2379 		    "seriesposition variable '%s' not found", s[0]);
2380 	return ERROR;
2381     }
2382 
2383 
2384     /* inquire about the variable - it's size, shape and type */
2385     if(ncvarinq(ap->cdfhandle,          /* file descriptor */
2386 		varid,                  /* variable ID */
2387 		(char *)0,              /* variable name */
2388 		&datatype,              /* data item type */
2389 		&ndims,                 /* number of dimensions */
2390 		dim,                    /* array of dimension ID's */
2391 		&natts) < 0)            /* number of attributes */
2392 	DXErrorReturn(ERROR_INTERNAL, "netCDF library error");
2393 
2394     if(dim[0] != ap->recdim)
2395 	DXErrorReturn(ERROR_DATA_INVALID,
2396 		  "seriespositions array must use unlimited record dimension");
2397 
2398     if(datatype != NC_FLOAT)
2399 	DXErrorReturn(ERROR_DATA_INVALID,
2400 		    "seriespositions array must be type float");
2401 
2402     if(ndims != 1)
2403 	DXErrorReturn(ERROR_DATA_INVALID, "seriespositions array must be 1D");
2404 
2405     size = get_serieslength(ap);
2406     start = ap->vp->startframe ? *ap->vp->startframe : 0;
2407     delta = ap->vp->deltaframe ? *ap->vp->deltaframe : 1;
2408 
2409     if(!(*valuelist = (float *)DXAllocateLocal(sizeof(float) * size)))
2410        return ERROR;
2411 
2412     if(delta == 1) {
2413 	if(ncvarget(ap->cdfhandle,	/* file descriptor */
2414 		    varid,		/* variable ID */
2415 		    &start,		/* array-origin for each dim */
2416 		    &size,              /* array-counts along each dim */
2417 		    (void *)*valuelist) < 0) /* where values are returned */
2418 	    goto error;
2419     } else {
2420 	for(i=0; i<size; i++) {
2421 	    if(ncvarget1(ap->cdfhandle, varid, &start,
2422 			                (void *)&valuelist[i]) < 0)
2423 		goto error;
2424 
2425 	    start += delta;
2426 	}
2427     }
2428     return OK;
2429 
2430   error:
2431     DXFree((Pointer) *valuelist);
2432     DXErrorReturn(ERROR_INTERNAL, "netCDF library error");
2433 
2434 #endif
2435 }
2436 
2437 /*
2438  * read a variable into global memory, and build up a field.
2439  */
build_field(int hand,int varid)2440 static Object build_field(int hand, int varid)
2441 {
2442     Array indata, terms[MAXRANK];
2443     int nopos = 0, nocon = 0;
2444     struct arrayinfo a, *ap = &a;
2445     int arraytype = I_NONE;
2446     char *cp, *s[MAXATTRSTR];
2447     int i, j, k;
2448     Field f = NULL;
2449     Object eltype = NULL;
2450 
2451 
2452     /* set up the Arrayinfo struct
2453      */
2454     ap->cdfhandle = hand;
2455     ap->varid = varid;
2456     ap->recdim = INVALID_DIMID;
2457 
2458 
2459     /* get the attribute value.
2460      */
2461     if(getattr(hand, varid, FIELDATTRIB, ap->stringattr,MAXNAME)) {
2462 
2463 	cp = ap->stringattr;
2464 	j = MAXATTRSTR;
2465 	cp = parseit(cp, &j, s);
2466 	if(j <= 0)
2467 	    return NULL;
2468     } else {
2469 	if (!variablename(hand, varid, ap->stringattr))
2470 	    return NULL;
2471 
2472 	s[0] = ap->stringattr;
2473 	s[1] = NULL;
2474 	s[2] = NULL;
2475 	j = 1;
2476     }
2477 
2478 
2479 
2480     /*
2481      * data component
2482      */
2483 
2484     /* decide if it's scalar, vector or matrix data */
2485     ap->arrayrank = setrank(s[1]);
2486 
2487     /* this routine reads a netCDF variable into an Array.
2488      *  in this case, it's reading in the data component.
2489      */
2490     if(!(indata = (Array)build_data(ap, INVALID_DIMID)))
2491 	    goto error;
2492 
2493     f = DXNewField();
2494     if(!f)
2495 	goto error;
2496 
2497     if(!DXSetComponentValue(f, DATCOMPNAME, (Object)indata))
2498 	goto error;
2499 
2500     /* look here for more attributes on the data component - to set
2501      *  data component attributes.
2502      */
2503     if(!setattr(ap, f, DATCOMPNAME))
2504 	goto error;
2505 
2506     /*
2507      * positions component
2508      */
2509 
2510     ap->arrayrank = 1;   /* unless they explicitly say 'scalar' */
2511 
2512     /* look for a positions (old points) component.  if the data variable
2513      *  doesn't have a 'positions' attribute, assume a regularly spaced grid.
2514      */
2515     if(!getattr(hand, varid, GEOMATTRIB, ap->stringattr,MAXNAME)
2516     && !getattr(hand, varid, OGEOMATTRIB, ap->stringattr,MAXNAME)) {
2517         i = 1;
2518         indata = (Array)build_poscon(ap, I_REGULAR_P, 0);
2519 
2520     } else {
2521 	/* else they gave us a geometry.  parse it.
2522 	 */
2523 
2524 	if(isattr(hand, varid, OGEOMATTRIB))
2525 	    DXWarning("'geometry' attribute obsolete, use 'positions' instead");
2526 
2527 	cp = ap->stringattr;
2528         i = 0;
2529 	while(1) {
2530 	    j = MAXATTRSTR;
2531 	    cp = parseit(cp, &j, s);
2532 	    if(j <= 0)
2533 		break;
2534 
2535 	    /* positions are vectors unless specifically told scalar */
2536 	    if((j > 1 && !strcmp("scalar", s[1]))
2537 	     ||(j > 2 && !strcmp("scalar", s[2])))
2538 		ap->arrayrank = 0;
2539 
2540 	    if(!strcmp("none", s[0])) {
2541 
2542 		/* no positions - should this be ok? */
2543 		nopos++;
2544 
2545 	    } else if(!strcmp("regular", s[0])) {
2546 
2547 		/* 'regular' is same as default */
2548 		arraytype = I_REGULAR_P;
2549 
2550 	    } else if((j > 1 && !strcmp("product", s[1]))
2551                     ||(j > 2 && !strcmp("product", s[2]))) {
2552 
2553 
2554 		if((k = ncvarid(hand, s[0])) < 0) {
2555 		    DXSetError(ERROR_BAD_PARAMETER,"positions variable missing");
2556 		    goto error;
2557 		}
2558                 ap->varid = k;
2559 
2560 		/* regular grid, non-zero origin or non-unit spacing */
2561 		if((j > 1 &&
2562                    (!strcmp("regular", s[1]) || !strcmp("compact", s[1])))
2563                 || (j > 2 &&
2564                    (!strcmp("regular", s[2]) || !strcmp("compact", s[2])))) {
2565 
2566 		    arraytype = I_PRODUCT_P;
2567 
2568 		} else {
2569 
2570                     arraytype = I_IRREGULAR_P;
2571                 }
2572 
2573 
2574 	    } else {
2575 		/* else positions isn't regular, or is grid
2576 		 *  but not 0.0 origin or 1.0 deltas.
2577 		 */
2578 		if((k = ncvarid(hand, s[0])) < 0) {
2579 		    DXSetError(ERROR_BAD_PARAMETER,"positions variable missing");
2580 		    goto error;
2581 		}
2582                 ap->varid = k;
2583 
2584 		/* regular grid, non-zero origin or non-unit spacing */
2585 		if((j > 1 &&
2586                    (!strcmp("regular", s[1]) || !strcmp("compact", s[1])))
2587                 || (j > 2 &&
2588                    (!strcmp("regular", s[2]) || !strcmp("compact", s[2])))) {
2589 
2590 		    if(j > 2 && !strcmp("product", s[2]))
2591                         arraytype = I_PRODUCT_P;
2592                     else
2593                         arraytype = I_COMPACT_P;
2594 
2595 		} else {
2596 
2597                     arraytype = I_IRREGULAR_P;
2598                 }
2599 
2600 	    }
2601 
2602             if(nopos)
2603                 break;
2604 
2605             indata = (Array)build_poscon(ap, arraytype, i);
2606             terms[i] = indata;
2607             i++;
2608         }
2609     }
2610 
2611     /* if there is a positions component */
2612     if(!nopos) {
2613         if(i > 1) {
2614             indata = (Array)DXNewProductArrayV(i, terms);
2615             if(!indata) {
2616                 DXSetError(ERROR_BAD_PARAMETER, "bad positions attribute");
2617                 goto error;
2618             }
2619         }
2620 
2621         /* and add it to field */
2622         f = DXSetComponentValue(f, GEOCOMPNAME, (Object)indata);
2623         f = DXSetComponentAttribute(f, DATCOMPNAME, "dep", STR(GEOCOMPNAME));
2624         if(!f)
2625             goto error;
2626 
2627         /* and check for attributes if another variable was used */
2628         if(ap->varid != varid && !setattr(ap, f, GEOCOMPNAME))
2629             goto error;
2630 
2631     }
2632 
2633 
2634     /*
2635      * connections component
2636      */
2637 
2638     /* now look for the 'connections' information.  if there isn't
2639      *  anything, assume it's a regular grid.  if there is an attribute
2640      *  for 'connections' (currently 'topology' also still accepted in the
2641      *  netCDF file), parse it.  if it's 'none', there isn't a topology
2642      *  component.  if it's 'regular', set it to a regular grid.
2643      *  otherwise, it should be the name of another netCDF variable
2644      *  which contains the connections information.
2645      */
2646     if(!getattr(hand, varid, TOPOATTRIB, ap->stringattr,MAXNAME)
2647     && !getattr(hand, varid, OTOPOATTRIB, ap->stringattr,MAXNAME)) {
2648         i = 1;
2649         indata = (Array)build_poscon(ap, I_REGULAR_C, 0);
2650         eltype = STR(regularname[ap->data_ndims]);
2651 
2652     } else {
2653 
2654 	/* a connections attribute.  parse it */
2655 	if(isattr(hand, varid, OTOPOATTRIB))
2656            DXWarning("'topology' attribute obsolete, use 'connections' instead");
2657 
2658 	cp = ap->stringattr;
2659         i = 0;
2660 	while(1) {
2661 	    j = MAXATTRSTR;
2662 	    cp = parseit(cp, &j, s);
2663 	    if(j <= 0)
2664 		break;
2665 
2666 	    /* no topology */
2667 	    if(!strcmp("none", s[0])) {
2668                 nocon++;
2669 
2670 	    } else if(!strcmp("regular", s[0])) {
2671 
2672 		/* also accept this as regular grid */
2673                 arraytype = I_REGULAR_C;
2674                 eltype = STR(regularname[ap->data_ndims]);
2675 
2676 	    } else if(j > 1 && !strcmp("product", s[1])) {
2677 
2678 		if((k = ncvarid(hand, s[0])) < 0) {
2679 		    DXSetError(ERROR_BAD_PARAMETER,
2680                              "connections variable missing");
2681 		    goto error;
2682 		}
2683                 ap->varid = k;
2684 
2685                 arraytype = I_PRODUCT_C;
2686 
2687 		/* if irregular, they must specify an element type */
2688 		if(j < 3 || !s[2] || !s[2][0]) {
2689 		    DXSetError(ERROR_BAD_PARAMETER,
2690 			     "'element type' must be specified");
2691 		    goto error;
2692                 } else
2693                     eltype = STR(s[2]);
2694 	    } else {
2695 
2696 		/* irregular - look for the other netCDF variable */
2697 		if((k = ncvarid(hand, s[0])) < 0) {
2698 		    DXSetError(ERROR_BAD_PARAMETER,
2699                              "connections variable missing");
2700 		    goto error;
2701 		}
2702                 ap->varid = k;
2703 
2704                 arraytype = I_IRREGULAR_C;
2705 
2706 		/* if irregular, they must specify an element type */
2707 		if(j < 2 || !s[1] || !s[1][0]) {
2708 		    DXSetError(ERROR_BAD_PARAMETER,
2709 			     "'element type' must be specified");
2710 		    goto error;
2711                 } else
2712                     eltype = STR(s[1]);
2713 	    }
2714 
2715             if(nocon)
2716                 break;
2717 
2718             indata = (Array)build_poscon(ap, arraytype, i);
2719             terms[i] = indata;
2720             i++;
2721 	}
2722 
2723     }
2724 
2725     /* if there is a connections component */
2726     if(!nocon) {
2727         if(i > 1) {
2728             indata = (Array)DXNewMeshArrayV(i, terms);
2729             if(!indata) {
2730                 DXSetError(ERROR_BAD_PARAMETER, "bad connections attribute");
2731                 goto error;
2732             }
2733         }
2734 
2735         /* and add it to field */
2736         f = DXSetComponentValue(f, TOPCOMPNAME, (Object)indata);
2737         f = DXSetComponentAttribute(f, TOPCOMPNAME, "element type", eltype);
2738         f = DXSetComponentAttribute(f, TOPCOMPNAME, "ref", STR(GEOCOMPNAME));
2739         if(!f)
2740             goto error;
2741 
2742         /* and check for attributes if another variable was used */
2743         if(ap->varid != varid && !setattr(ap, f, TOPCOMPNAME))
2744             goto error;
2745 
2746     }
2747 
2748 
2749     /*
2750      * additional components
2751      */
2752 
2753     /* are there additional components that should be added?
2754      */
2755     if(getattr(hand, varid, COMPATTRIB, ap->stringattr,MAXNAME)) {
2756 
2757 	cp = ap->stringattr;
2758 	while(1) {
2759 	    j = MAXATTRSTR;
2760 	    cp = parseit(cp, &j, s);
2761 
2762 	    /* field:component = "a, b, c";  attribute required.  all three
2763 	     *  parameters must be present for additional components.
2764 	     *   a = name of variable.
2765 	     *   b = component name
2766 	     *   c = rank (scalar, vector, matrix, tensor)
2767 	     */
2768 	    if(j <= 0)
2769                 break;
2770 
2771             if(j < 3) {
2772                 DXSetError(ERROR_BAD_PARAMETER,
2773                          "varname, compname, shape required");
2774 		goto error;
2775             }
2776 
2777 	    if((k = ncvarid(hand, s[0])) < 0) {
2778 		DXSetError(ERROR_BAD_PARAMETER,
2779 			 "component variable '%s' not found", s[0]);
2780 		goto error;
2781 	    }
2782 
2783             ap->varid = k;
2784 	    ap->arrayrank = setrank(s[2]);
2785 
2786 	    /* actually read the data into an Array */
2787 	    if(!(indata = (Array)build_array(ap)))
2788 		goto error;
2789 
2790 	    /* and add it to the field with the given component name */
2791 	    if(!DXSetComponentValue(f, s[1], (Object)indata))
2792 		goto error;
2793 
2794             /* and check for attributes */
2795             if(!setattr(ap, f, s[1]))
2796                 goto error;
2797 	}
2798 
2799     }
2800 
2801 #ifdef DO_ENDFIELD
2802     f = DXEndField(f);
2803 #endif
2804     return (Object)f;
2805 
2806   error:
2807     DXDelete((Object)f);
2808     return NULL;
2809 }
2810 
2811 
2812 /* single routine for deciding what routine to call to actually construct
2813  *  either a positions or connections array.  (it's called from more than
2814  *  one place, so it's here to keep the calls to the routines from being
2815  *  scattered thoughout the code)
2816  */
2817 static Object
build_poscon(Arrayinfo ap,int flag,int dim)2818 build_poscon(Arrayinfo ap, int flag, int dim)
2819 {
2820     Object indata = NULL;
2821 
2822     switch(flag) {
2823 
2824       case I_NONE:
2825         break;
2826 
2827       case I_COMPACT_P:
2828         /* regular grid, non-zero origin or non-unit spacing */
2829         indata = build_regpos(ap, 0);
2830         break;
2831 
2832       case I_REGULAR_P:
2833         /* origin at 0.0, deltas 1.0, regular grid */
2834         indata = build_regpos(ap, 1);
2835         break;
2836 
2837       case I_REGULAR_C:
2838         /* N-dim connected grid */
2839         indata = build_regcon(ap);
2840         break;
2841 
2842       case I_IRREGULAR_P:
2843       case I_IRREGULAR_C:
2844         /* explicit positions or connections list */
2845         indata = build_array(ap);
2846         break;
2847 
2848       case I_PRODUCT_P:
2849         /* indata = build_regpos(ap, 0); */
2850         indata = build_regpos1D(ap, 0, dim);
2851         break;
2852 
2853       case I_PRODUCT_C:
2854         indata = build_regcon1D(ap, dim);
2855         break;
2856 
2857       default:
2858         DXSetError(ERROR_INTERNAL, "bad case in build_pos switch statement");
2859         break;
2860     }
2861 
2862     return indata;
2863 }
2864 
2865 
2866 
2867 
2868 /* this code reads in a single variable into an array and also sets
2869  *  up the AI struct for later.  if recdim is valid, it is the id of
2870  *  the variable used for the series dimension.  it needs to be the
2871  *  first variable if it is used.
2872  */
2873 static Object
build_data(Arrayinfo ap,int recdim)2874 build_data(Arrayinfo ap, int recdim)
2875 {
2876     nc_type datatype;
2877     int i, j, size, natts, series;
2878     int start, end, delta;
2879     int dim[MAX_VAR_DIMS];
2880     long dstart[MAX_VAR_DIMS];
2881     long long_datacounts[MAXRANK];
2882     VOID *dataval;
2883     Object o = NULL;
2884     Array adata = NULL;
2885     long *tempbuf;
2886     int k,n,ndims=1;
2887     int *data_int;
2888 
2889     /* inquire about the variable - it's size, shape and type */
2890     if(ncvarinq(ap->cdfhandle,             /* file descriptor */
2891 		ap->varid,                 /* variable ID */
2892 		(char *)0,                 /* variable name */
2893 		&datatype,                 /* data item type */
2894 		&ap->data_ndims,           /* number of dimensions */
2895 		dim,                       /* array of dimension ID's */
2896 		&natts) < 0)               /* number of attributes */
2897 	DXErrorReturn(ERROR_INTERNAL, "netCDF library error");
2898 
2899 
2900     if(ap->data_ndims <= 0){
2901 	DXSetError(ERROR_INTERNAL, "%s variable with no dimensions",
2902 	ap->stringattr);
2903 	return ERROR;
2904     }
2905 
2906     /* figure out how big it is - total number of items, taking into
2907      *  account that if it is an array of vectors or matricies, the
2908      *  quickest varying dimension isn't part of the total shape.
2909      */
2910     size = 1;
2911     for (i=0; i<ap->data_ndims; i++) {
2912 	ncdiminq (ap->cdfhandle, dim[i], (char *)0, &long_datacounts[i]);
2913 	ap->datacounts[i] = long_datacounts[i];
2914 	dstart[i] = 0;
2915 	if(i < ap->data_ndims - ap->arrayrank)
2916 	    size *= ap->datacounts[i];
2917     }
2918 
2919     /* convert from netCDF data flags to SVS data flags */
2920     switch(datatype) {
2921       case NC_CHAR:   ap->arraytype = TYPE_UBYTE;  break;
2922       case NC_SHORT:  ap->arraytype = TYPE_SHORT;  break;
2923       case NC_LONG:   ap->arraytype = TYPE_INT;    break;
2924       case NC_FLOAT:  ap->arraytype = TYPE_FLOAT;  break;
2925       case NC_DOUBLE: ap->arraytype = TYPE_DOUBLE; break;
2926       default:        ap->arraytype = TYPE_UBYTE;  break;
2927 	/* we need a TYPE_BYTE */
2928     }
2929 
2930 
2931     /* if the first variable is the unlimited dimension, this is a series.
2932      */
2933     series = (dim[0] == recdim);
2934 
2935     /* create a group to hold the output, and don't count the size of
2936      *  the first dimension in the data.
2937      */
2938     if(series) {
2939 	o = (Object)DXNewSeries();
2940 	size /= ap->datacounts[0];
2941 
2942 	start = ap->vp->startframe ? *ap->vp->startframe : 0;
2943 	end = ap->vp->endframe ? *ap->vp->endframe : ap->datacounts[0]-1;
2944 	delta = ap->vp->deltaframe ? *ap->vp->deltaframe : 1;
2945 
2946 	if(delta == 0) {
2947 	    DXSetError(ERROR_DATA_INVALID, "delta is zero");
2948 	    goto error;
2949 	}
2950 
2951 	ap->datacounts[0] = 1;
2952 	long_datacounts[0] = 1;
2953     } else {
2954 	start = 0;
2955 	end = 0;
2956 	delta = 1;
2957     }
2958 
2959     for(i=start, j=0; i<=end; i+=delta, j++) {
2960 
2961 	/* variables are labeled as a vector/matrix/tensor.  the outermost
2962 	 *  (last) variable must vary fastest.  if not scalar, pass the shape
2963 	 *  of the vector or matrix into DXNewArray as shape.
2964 	 */
2965 	adata = DXNewArrayV (ap->arraytype, CATEGORY_REAL, ap->arrayrank,
2966 			    &ap->datacounts[ap->data_ndims - ap->arrayrank]);
2967 
2968 	adata = DXAddArrayData (adata, 0, size, NULL);
2969 
2970 	if(series)
2971 	    dstart[0] = i;
2972 
2973 	/* if data is long, create a temp long buffer to copy
2974 	   netCDF data to then move to the SVS array buffer */
2975 	if (ap->arraytype == TYPE_INT) {
2976 	   data_int = (int *)DXGetArrayData (adata);
2977 	   if(!data_int)
2978 	       goto error;
2979 	   for (k=0,n=ap->data_ndims - ap->arrayrank; k<ap->arrayrank; k++,n++)
2980 	      ndims *= ap->datacounts[n];
2981 	   tempbuf = (long *)DXAllocate(ndims * size * sizeof(long));
2982 	   if(ncvarget(ap->cdfhandle,          /* netcdf file handle */
2983 		    ap->varid,              /* variable id */
2984 		    dstart,                 /* array-origin for each dim */
2985 		    long_datacounts,         /* array-counts along each dim */
2986 		    tempbuf) < 0) {         /* memory pointer */
2987 	       DXSetError(ERROR_INTERNAL, "netCDF library error");
2988 	       goto error;
2989 	   }
2990 	   for (k=0; k<size*ndims; k++)
2991 	      data_int[k] = (int)tempbuf[k];
2992  	   DXFree((Pointer)tempbuf);
2993 	}
2994 	else {
2995 
2996 	dataval = DXGetArrayData (adata);
2997 	if(!dataval)
2998 	    goto error;
2999 
3000 	/* do the memory copy from netCDF buffer to SVS array buffer */
3001 	if(ncvarget(ap->cdfhandle,          /* netcdf file handle */
3002 		    ap->varid,              /* variable id */
3003 		    dstart,                 /* array-origin for each dim */
3004 		    long_datacounts,         /* array-counts along each dim */
3005 		    dataval) < 0) {         /* memory pointer */
3006 	    DXSetError(ERROR_INTERNAL, "netCDF library error");
3007 	    goto error;
3008 	}
3009   	}
3010 
3011 	if(series) {
3012 	    if (!DXSetSeriesMember((Series)o, j, (double)i, (Object)adata))
3013 		goto error;
3014 
3015 	    adata = NULL;
3016 	}
3017     }
3018 
3019     /* account for a vector or matrix at each location, and also series.
3020      */
3021     ap->data_ndims -= ap->arrayrank;
3022     if(series) {
3023 	ap->data_ndims--;
3024 	for(i=0; i < ap->data_ndims; i++){
3025 	    ap->datacounts[i] = ap->datacounts[i+1];
3026 	    long_datacounts[i] = long_datacounts[i+1];
3027 	}
3028     }
3029 
3030     /* if single array instead of group */
3031     if(!o)
3032 	o = (Object)adata;
3033 
3034     return(o);
3035 
3036   error:
3037     DXDelete(o);
3038     DXDelete((Object)adata);
3039     return NULL;
3040 }
3041 
3042 
3043 /* this code reads in a single variable into an array, using an AI struct
3044  *  in which the shape of the data has already been initialized.
3045  */
3046 static Object
build_array(Arrayinfo ap)3047 build_array(Arrayinfo ap)
3048 {
3049     nc_type datatype;
3050     int i, size, natts, series;
3051     int start, end, delta;
3052     int dim[MAX_VAR_DIMS];
3053     long dstart[MAX_VAR_DIMS];
3054     long long_arraycounts[MAXRANK];
3055     VOID *dataval;
3056     Object o = NULL;
3057     Array adata = NULL;
3058     long *tempbuf;
3059     int k,n,ndims=1;
3060     int *data_int;
3061 
3062 
3063     /* inquire about the variable - it's size, shape and type */
3064     if(ncvarinq(ap->cdfhandle,             /* file descriptor */
3065 		ap->varid,                 /* variable ID */
3066 		(char *)0,                 /* variable name */
3067 		&datatype,                 /* data item type */
3068 		&ap->array_ndims,          /* number of dimensions */
3069 		dim,                       /* array of dimension ID's */
3070 		&natts) < 0)               /* number of attributes */
3071 	DXErrorReturn(ERROR_INTERNAL, "netCDF library error");
3072 
3073 
3074     if(ap->array_ndims < 0)
3075 	DXErrorReturn(ERROR_INTERNAL, "netCDF variable with no dimensions");
3076 
3077     /* figure out how big it is - total number of items, taking into
3078      *  account that if it is an array of vectors or matricies, the
3079      *  quickest varying dimension isn't part of the total shape.
3080      */
3081     size = 1;
3082     for (i=0; i<ap->array_ndims; i++) {
3083 	ncdiminq (ap->cdfhandle, dim[i], (char *)0, &long_arraycounts[i]);
3084 	ap->arraycounts[i] = long_arraycounts[i];
3085 	dstart[i] = 0;
3086 	if(i < ap->array_ndims - ap->arrayrank)
3087 	    size *= ap->arraycounts[i];
3088     }
3089 
3090     /* convert from netCDF data flags to SVS data flags */
3091     switch(datatype) {
3092       case NC_CHAR:   ap->arraytype = TYPE_UBYTE;  break;
3093       case NC_SHORT:  ap->arraytype = TYPE_SHORT;  break;
3094       case NC_LONG:   ap->arraytype = TYPE_INT;    break;
3095       case NC_FLOAT:  ap->arraytype = TYPE_FLOAT;  break;
3096       case NC_DOUBLE: ap->arraytype = TYPE_DOUBLE; break;
3097       default:        ap->arraytype = TYPE_UBYTE;  break;
3098 	/* we need a TYPE_BYTE */
3099     }
3100 
3101 
3102     /* if the first variable is the unlimited dimension, this is a series.
3103      */
3104     series = (dim[0] == ap->recdim);
3105 
3106     /* create a group to hold the output, and don't count the size of
3107      *  the first dimension in the data.
3108      */
3109     if(series) {
3110 	o = (Object)DXNewGroup();
3111 	size /= ap->arraycounts[0];
3112 
3113 	start = ap->vp->startframe ? *ap->vp->startframe : 0;
3114 	end = ap->vp->endframe ? *ap->vp->endframe : ap->arraycounts[0]-1;
3115 	delta = ap->vp->deltaframe ? *ap->vp->deltaframe : 1;
3116 
3117 	if(delta == 0) {
3118 	    DXSetError(ERROR_DATA_INVALID, "delta is zero");
3119 	    goto error;
3120 	}
3121 
3122 	ap->arraycounts[0] = 1;
3123 	long_arraycounts[0] =1;
3124     } else {
3125 	start = 0;
3126 	end = 0;
3127 	delta = 1;
3128     }
3129 
3130     for(i=start; i<=end; i+=delta) {
3131 
3132 	/* variables are labeled as a vector/matrix/tensor.  the outermost
3133 	 *  (last) variable must vary fastest.  if not scalar, pass the shape
3134 	 *  of the vector or matrix into DXNewArray as shape.
3135 	 */
3136 	adata = DXNewArrayV (ap->arraytype, CATEGORY_REAL, ap->arrayrank,
3137 			    &ap->arraycounts[ap->array_ndims - ap->arrayrank]);
3138 
3139 
3140 
3141 	adata = DXAddArrayData (adata, 0, size, NULL);
3142 
3143 	if(series)
3144 	    dstart[0] = i;
3145 
3146 	/* if data is long, create a temp long buffer to copy
3147 	   netCDF data to then move to the SVS array buffer */
3148 	if (ap->arraytype == TYPE_INT) {
3149 	   data_int = DXGetArrayData (adata);
3150 	   if(!data_int)
3151 	       goto error;
3152 	   for (k=0, n=ap->array_ndims - ap->arrayrank; k<ap->arrayrank;k++,n++)
3153 	      ndims *=ap->arraycounts[n];
3154 	   tempbuf = (long *)DXAllocate(size * ndims * sizeof(long));
3155 	   if(ncvarget(ap->cdfhandle,          /* netcdf file handle */
3156 		    ap->varid,              /* variable id */
3157 		    dstart,                 /* array-origin for each dim */
3158 		    long_arraycounts,         /* array-counts along each dim */
3159 		    tempbuf) < 0) {         /* memory pointer */
3160 	       DXSetError(ERROR_INTERNAL, "netCDF library error");
3161 	       goto error;
3162 	   }
3163 	   for (k=0; k<size*ndims; k++)
3164 	      data_int[k] = (int)tempbuf[k];
3165  	   DXFree((Pointer)tempbuf);
3166 	}
3167 	else {
3168 	dataval = DXGetArrayData (adata);
3169 	if(!dataval)
3170 	    goto error;
3171 	/* do the memory copy from netCDF buffer to SVS array buffer */
3172 	if(ncvarget(ap->cdfhandle,          /* netcdf file handle */
3173 		    ap->varid,              /* variable id */
3174 		    dstart,                 /* array-origin for each dim */
3175 		    long_arraycounts,        /* array-counts along each dim */
3176 		    dataval) < 0) {         /* memory pointer */
3177 	    DXSetError(ERROR_INTERNAL, "netCDF library error");
3178 	    goto error;
3179 	}
3180  	}
3181 
3182 	if(series) {
3183 	    if (!DXSetMember((Group)o, NULL, (Object)adata))
3184 		goto error;
3185 
3186 	    adata = NULL;
3187 	}
3188 
3189     }
3190 
3191 
3192     /* if single array instead of group */
3193     if(!o)
3194 	o = (Object)adata;
3195 
3196     return(o);
3197 
3198   error:
3199     DXDelete(o);
3200     DXDelete((Object)adata);
3201     return NULL;
3202 }
3203 
3204 
3205 /*
3206  * construct a regular Ndim positions component -
3207  *   either origins 0.0, deltas 1.0, or user specified origins & deltas
3208  */
build_regpos(Arrayinfo ap,int regular)3209 static Object build_regpos(Arrayinfo ap, int regular)
3210 {
3211     int i, j, k, loops;
3212     long index[3];
3213     float origins[MAX_VAR_DIMS], deltas[MAXRANK*MAXRANK];
3214     int start, delta;
3215     Array indata;
3216     Object o = NULL;
3217 
3218     memset((char *)deltas, '\0', sizeof(deltas));
3219 
3220     /* setup arrays for each dimension */
3221     if(regular) {
3222 
3223 	for (i=0; i<ap->data_ndims; i++) {
3224 	    origins[i] = 0.0;
3225 	    j = ap->data_ndims * i + i;
3226 	    deltas[j] = 1.0;
3227 	}
3228 
3229 	/* actually make the positions Array */
3230 	indata = DXMakeGridPositionsV(ap->data_ndims, ap->datacounts,
3231 				    origins, deltas);
3232 
3233 	return((Object)indata);
3234     }
3235 
3236     /* not completely regular */
3237 
3238     /* is single positions array shared, or is it a series? */
3239     if(!is_seriesvariable(ap->cdfhandle, ap->varid, ap->recdim)) {
3240 
3241 	for (i=0; i<ap->data_ndims; i++) {
3242 
3243 	    /* origins */
3244 	    index[0] = i;
3245 	    index[1] = 0;
3246 	    if(ncvarget1(ap->cdfhandle, ap->varid, index, &origins[i]) < 0)
3247 		DXErrorReturn(ERROR_INTERNAL,
3248 			    "netCDF library error getting origins");
3249 
3250 	    /* deltas */
3251 	    index[1] = 1;
3252 	    j = ap->data_ndims * i + i;
3253 	    if(ncvarget1(ap->cdfhandle, ap->varid, index, &deltas[j]) < 0)
3254 		DXErrorReturn(ERROR_INTERNAL,
3255 			    "netCDF library error getting deltas");
3256 
3257 	}
3258 
3259 	/* actually make the positions Array */
3260 	indata = DXMakeGridPositionsV(ap->data_ndims, ap->datacounts,
3261 				    origins, deltas);
3262 
3263 	return((Object)indata);
3264     }
3265 
3266     /* positions are also series */
3267     loops = get_serieslength(ap);
3268     start = ap->vp->startframe ? *ap->vp->startframe : 0;
3269     delta = ap->vp->deltaframe ? *ap->vp->deltaframe : 1;
3270 
3271     for (j=0; j<loops; j++) {
3272 
3273 	memset((char *)deltas, '\0', sizeof(deltas));
3274 
3275 	for (i=0; i<ap->data_ndims; i++) {
3276 
3277 	    /* origins */
3278 	    index[0] = start;
3279 	    index[1] = i;
3280 	    index[2] = 0;
3281 	    if(ncvarget1(ap->cdfhandle, ap->varid, index, &origins[i]) < 0)
3282 		DXErrorReturn(ERROR_INTERNAL,
3283 			    "netCDF library error getting origins");
3284 
3285 	    /* deltas */
3286 	    index[2] = 1;
3287 	    k = ap->data_ndims * i + i;
3288 	    if(ncvarget1(ap->cdfhandle, ap->varid, index, &deltas[k]) < 0)
3289 		DXErrorReturn(ERROR_INTERNAL,
3290 			    "netCDF library error getting deltas");
3291 
3292 	}
3293 
3294 	/* make the positions Array for one timestep */
3295 	indata = DXMakeGridPositionsV(ap->data_ndims, ap->datacounts,
3296 				  origins, deltas);
3297 
3298 	if(!o)
3299 	    o = (Object)DXNewGroup();
3300 	o = (Object)DXSetMember((Group)o, NULL, (Object)indata);
3301 	if(!o)
3302 	    return NULL;
3303 
3304 	start += delta;
3305     }
3306 
3307     return o;
3308 }
3309 
3310 
3311 
3312 /*
3313  * construct a single regular array -
3314  *   either origin 0.0, delta 1.0, or user specified origin & deltas
3315  */
build_regpos1D(Arrayinfo ap,int regular,int dim)3316 static Object build_regpos1D(Arrayinfo ap, int regular, int dim)
3317 {
3318     int i, j, loops;
3319     long index[3];
3320     float origins[MAX_VAR_DIMS], deltas[MAXRANK*MAXRANK];
3321     int start, delta;
3322     Array indata;
3323     Object o = NULL;
3324 
3325     memset((char *)deltas, '\0', sizeof(deltas));
3326 
3327     /* setup an array for one dimension */
3328     if(regular) {
3329 
3330 	for (i=0; i<ap->data_ndims; i++)
3331 	    origins[0] = 0.0;
3332 
3333 	deltas[dim] = 1.0;
3334 
3335 	indata = (Array)DXNewRegularArray(TYPE_FLOAT, ap->data_ndims,
3336 					ap->datacounts[dim],
3337 					(Pointer)origins, (Pointer)deltas);
3338 
3339 	return((Object)indata);
3340     }
3341 
3342     /* is the positions variable also a series, or constant? */
3343     if(!is_seriesvariable(ap->cdfhandle, ap->varid, ap->recdim)) {
3344 
3345 	/* origins */
3346 	index[0] = 0;
3347 	for (i=0; i<ap->data_ndims; i++) {
3348 	    index[1] = i;
3349 	    if(ncvarget1(ap->cdfhandle, ap->varid, index, &origins[i]) < 0)
3350 		DXErrorReturn(ERROR_INTERNAL,
3351 				"netCDF library error getting origins");
3352 	}
3353 
3354 	/* deltas */
3355 	index[0] = 1;
3356 	for (i=0; i<ap->data_ndims; i++) {
3357 	    index[1] = i;
3358 	    if(ncvarget1(ap->cdfhandle, ap->varid, index, &deltas[i]) < 0)
3359 		DXErrorReturn(ERROR_INTERNAL,
3360 			    "netCDF library error getting deltas");
3361 	}
3362 
3363 
3364 	indata = (Array)DXNewRegularArray(TYPE_FLOAT, ap->data_ndims,
3365 					ap->datacounts[dim],
3366 					(Pointer)origins, (Pointer)deltas);
3367 
3368 	return((Object)indata);
3369     }
3370 
3371     /* positions are also series */
3372     loops = get_serieslength(ap);
3373     start = ap->vp->startframe ? *ap->vp->startframe : 0;
3374     delta = ap->vp->deltaframe ? *ap->vp->deltaframe : 1;
3375 
3376     for (j=0; j<loops; j++) {
3377 	for (i=0; i<ap->data_ndims; i++) {
3378 
3379 	    /* origins */
3380 	    index[0] = start;
3381 	    index[1] = 0;
3382 	    for (i=0; i<ap->data_ndims; i++) {
3383 		index[2] = i;
3384 		if(ncvarget1(ap->cdfhandle, ap->varid, index, &origins[i]) < 0)
3385 		    DXErrorReturn(ERROR_INTERNAL,
3386 				"netCDF library error getting origins");
3387 	    }
3388 
3389 	    /* deltas */
3390 	    index[1] = 1;
3391 	    for (i=0; i<ap->data_ndims; i++) {
3392 		index[2] = i;
3393 		if(ncvarget1(ap->cdfhandle, ap->varid, index, &deltas[i]) < 0)
3394 		    DXErrorReturn(ERROR_INTERNAL,
3395 				"netCDF library error getting deltas");
3396 	    }
3397 
3398 
3399 	    indata = (Array)DXNewRegularArray(TYPE_FLOAT, ap->data_ndims,
3400 					    ap->datacounts[dim],
3401 					    (Pointer)origins, (Pointer)deltas);
3402 
3403 	    if(!o)
3404 		o = (Object)DXNewGroup();
3405 	    o = (Object)DXSetMember((Group)o, NULL, (Object)indata);
3406 	    if(!o)
3407 		return NULL;
3408 
3409 	    start += delta;
3410 	}
3411 
3412     }
3413     return o;
3414 }
3415 
3416 
3417 /*
3418  * completely regular Ndim connections component
3419  */
build_regcon(Arrayinfo ap)3420 static Object build_regcon(Arrayinfo ap)
3421 {
3422     return (Object)DXMakeGridConnectionsV(ap->data_ndims, ap->datacounts);
3423 }
3424 
3425 
3426 /*
3427  * completely regular 1D connections array
3428  */
build_regcon1D(Arrayinfo ap,int dim)3429 static Object build_regcon1D(Arrayinfo ap, int dim)
3430 {
3431     return (Object)DXNewPathArray(ap->datacounts[dim]);
3432 }
3433 
3434 /* given a variable, return it's name
3435  */
variablename(int hand,int varid,char * cbuf)3436 static Error variablename(int hand, int varid, char *cbuf)
3437 {
3438     nc_type datatype;
3439     int natts, ndims;
3440     int dim[MAX_VAR_DIMS];
3441 
3442     /* inquire about the variable - it's size, shape and type */
3443     if(ncvarinq(hand,                      /* file descriptor */
3444 		varid,                     /* variable ID */
3445 		cbuf,                      /* variable name */
3446 		&datatype,                 /* data item type */
3447 		&ndims,                    /* number of dimensions */
3448 		dim,                       /* array of dimension ID's */
3449 		&natts) < 0)               /* number of attributes */
3450 	DXErrorReturn(ERROR_INTERNAL, "netCDF library error");
3451 
3452     if (ndims <= 0)	/* no dimensions for this variable */
3453       *cbuf='\0';
3454 
3455     return OK;
3456 }
3457 
3458 
3459 
3460 /* return whether a char string matches: scalar, vector, matrix or tensor
3461  */
setrank(char * s)3462 static int setrank(char *s)
3463 {
3464     char *cp;
3465 
3466     if(!s || !s[0])
3467 	return 0;
3468 
3469     for(cp = s; *cp; cp++)
3470 	if(isupper(*cp))
3471 	   *cp = tolower(*cp);
3472 
3473     if(!strcmp("scalar", s))
3474 	return 0;
3475 
3476     if(!strcmp("vector", s))
3477 	return 1;
3478 
3479     if(!strcmp("matrix", s))
3480 	return 2;
3481 
3482     if(!strcmp("tensor", s))
3483 	return 3;
3484 
3485     return 0;
3486 }
3487 
3488 
3489 /* give it a string in the form: parm, parm, parm; parm, parm, parm; ...
3490  *  it will parse up to N parms or ;, whichever occurs first.  it sets
3491  *  the num found, pointers to each, and returns a pointer to the next set.
3492  *  THIS IS DESTRUCTIVE TO THE ORIGINAL STRING (it replaces ,'s with NULLS)
3493  */
parseit(char * in,int * n,char * out[])3494 static char * parseit(char *in, int *n, char *out[])
3495 {
3496     int instring = 0;
3497     int found = 0;
3498     int i;
3499 
3500 
3501     while(*in != '\0') {
3502 
3503 	if(isspace(*in)) {
3504 	    in++;
3505 	    continue;
3506 	}
3507 
3508 	if(*in == ';') {
3509 	    *in = '\0';
3510 	    in++;
3511 	    break;
3512 	}
3513 
3514 	if(*in == ',') {
3515 	    *in = '\0';
3516 
3517 	    if(!instring) {
3518 		if(++found > *n) {
3519 		    while(*in && *in != ';')
3520 			in++;
3521 		    if(*in)
3522 			in++;
3523 		    found--;
3524 		    break;
3525 		}
3526 
3527 		*out = in;
3528 		out++;
3529 	    }
3530 
3531 	    instring = 0;
3532 	    in++;
3533 	    continue;
3534 	}
3535 
3536 	if(!instring) {
3537 	    if(++found > *n) {
3538 		while(*in && *in != ';')
3539 		    in++;
3540 		if(*in)
3541 		    in++;
3542 		found--;
3543 		break;
3544 	    }
3545 
3546 	    *out = in;
3547 	    out++;
3548 	    instring++;
3549 	}
3550 
3551 	in++;
3552     }
3553 
3554     for(i = found; i < *n; i++)
3555 	*out++ = NULL;
3556 
3557     *n = found;
3558 
3559     return in;
3560 }
3561 
3562 #else
3563 
3564 ImportStatReturn
_dxfstat_netcdf_file(char * filename)3565 _dxfstat_netcdf_file(char *filename)
3566 {
3567     return IMPORT_STAT_NOT_FOUND;
3568 }
3569 
DXImportNetCDF(char * filename,char ** variable,int * starttime,int * endtime,int * deltatime)3570 Object DXImportNetCDF(char *filename, char **variable, int *starttime,
3571                     int *endtime, int *deltatime)
3572 { DXSetError(ERROR_NOT_IMPLEMENTED, "netCDF libs not included"); return ERROR; }
_dxfQueryImportInfo(char * filename)3573 Object _dxfQueryImportInfo(char *filename)
3574 { DXSetError(ERROR_NOT_IMPLEMENTED, "netCDF libs not included"); return ERROR; }
3575 
3576 #endif /* DXD_LACKS_NCDF_FORMAT */
3577