1 /*********************************************************************
2  *   Copyright 2018, UCAR/Unidata
3  *   See netcdf/COPYRIGHT file for copying and redistribution conditions.
4  *********************************************************************/
5 
6 #include "dapincludes.h"
7 #include "ncd2dispatch.h"
8 #include "ncrc.h"
9 #include "ncoffsets.h"
10 #ifdef DEBUG2
11 #include "dapdump.h"
12 #endif
13 
14 #ifdef _MSC_VER
15 #include <crtdbg.h>
16 #endif
17 
18 #ifdef HAVE_GETRLIMIT
19 #  ifdef HAVE_SYS_RESOURCE_H
20 #    include <sys/time.h>
21 #  endif
22 #  ifdef HAVE_SYS_RESOURCE_H
23 #    include <sys/resource.h>
24 #  endif
25 #endif
26 
27 /* Define the set of protocols known to be constrainable */
28 static const char* constrainableprotocols[] = {"http", "https",NULL};
29 
30 static int ncd2initialized = 0;
31 
32 /* Forward */
33 static NCerror buildncstructures(NCDAPCOMMON*);
34 static NCerror builddims(NCDAPCOMMON*);
35 static char* getdefinename(CDFnode* node);
36 static NCerror buildvars(NCDAPCOMMON*);
37 static NCerror buildglobalattrs(NCDAPCOMMON*, CDFnode* root);
38 static NCerror buildattribute(NCDAPCOMMON*, CDFnode*, NCattribute*);
39 static void computedimindexanon(CDFnode* dim, CDFnode* var);
40 static void replacedims(NClist* dims);
41 static int equivalentdim(CDFnode* basedim, CDFnode* dupdim);
42 static NCerror addstringdims(NCDAPCOMMON*);
43 static NCerror defrecorddim(NCDAPCOMMON*);
44 static NCerror defseqdims(NCDAPCOMMON*);
45 static NCerror showprojection(NCDAPCOMMON*, CDFnode* var);
46 static NCerror getseqdimsize(NCDAPCOMMON*, CDFnode* seq, size_t* sizep);
47 static NCerror makeseqdim(NCDAPCOMMON*, CDFnode* seq, size_t count, CDFnode** sqdimp);
48 static NCerror countsequence(NCDAPCOMMON*, CDFnode* xseq, size_t* sizep);
49 static NCerror freeNCDAPCOMMON(NCDAPCOMMON*);
50 static NCerror fetchpatternmetadata(NCDAPCOMMON*);
51 static size_t fieldindex(CDFnode* parent, CDFnode* child);
52 static NCerror computeseqcountconstraints(NCDAPCOMMON*, CDFnode*, NCbytes*);
53 static void computeseqcountconstraintsr(NCDAPCOMMON*, CDFnode*, CDFnode**);
54 static void estimatevarsizes(NCDAPCOMMON*);
55 static NCerror fetchconstrainedmetadata(NCDAPCOMMON*);
56 static NCerror suppressunusablevars(NCDAPCOMMON*);
57 static NCerror fixzerodims(NCDAPCOMMON*);
58 static void applyclientparamcontrols(NCDAPCOMMON*);
59 static NCerror applyclientparams(NCDAPCOMMON*);
60 
61 /**************************************************/
62 
63 static int
64 NCD2_create(const char *path, int cmode,
65            size_t initialsz, int basepe, size_t *chunksizehintp,
66            void* mpidata, const struct NC_Dispatch*,int);
67 
68 static int NCD2_redef(int ncid);
69 static int NCD2__enddef(int ncid, size_t h_minfree, size_t v_align, size_t v_minfree, size_t r_align);
70 static int NCD2_sync(int ncid);
71 static int NCD2_abort(int ncid);
72 
73 static int NCD2_put_vara(int ncid, int varid,
74 	    const size_t *start, const size_t *edges0,
75             const void *value0,
76 	    nc_type memtype);
77 
78 static int NCD2_get_vara(int ncid, int varid,
79 	    const size_t *start, const size_t *edges,
80             void *value,
81 	    nc_type memtype);
82 
83 static int NCD2_put_vars(int ncid, int varid,
84 	    const size_t *start, const size_t *edges, const ptrdiff_t* stride,
85             const void *value0, nc_type memtype);
86 
87 static int NCD2_get_vars(int ncid, int varid,
88 	    const size_t *start, const size_t *edges, const ptrdiff_t* stride,
89             void *value, nc_type memtype);
90 
91 static const NC_Dispatch NCD2_dispatch_base = {
92 
93 NC_FORMATX_DAP2,
94 NC_DISPATCH_VERSION,
95 
96 NCD2_create,
97 NCD2_open,
98 
99 NCD2_redef,
100 NCD2__enddef,
101 NCD2_sync,
102 NCD2_abort,
103 NCD2_close,
104 NCD2_set_fill,
105 NCD2_inq_format,
106 NCD2_inq_format_extended, /*inq_format_extended*/
107 
108 NCD2_inq,
109 NCD2_inq_type,
110 
111 NCD2_def_dim,
112 NCD2_inq_dimid,
113 NCD2_inq_dim,
114 NCD2_inq_unlimdim,
115 NCD2_rename_dim,
116 
117 NCD2_inq_att,
118 NCD2_inq_attid,
119 NCD2_inq_attname,
120 NCD2_rename_att,
121 NCD2_del_att,
122 NCD2_get_att,
123 NCD2_put_att,
124 
125 NCD2_def_var,
126 NCD2_inq_varid,
127 NCD2_rename_var,
128 NCD2_get_vara,
129 NCD2_put_vara,
130 NCD2_get_vars,
131 NCD2_put_vars,
132 NCDEFAULT_get_varm,
133 NCDEFAULT_put_varm,
134 
135 NCD2_inq_var_all,
136 
137 NCD2_var_par_access,
138 NCD2_def_var_fill,
139 
140 NCD2_show_metadata,
141 NCD2_inq_unlimdims,
142 NCD2_inq_ncid,
143 NCD2_inq_grps,
144 NCD2_inq_grpname,
145 NCD2_inq_grpname_full,
146 NCD2_inq_grp_parent,
147 NCD2_inq_grp_full_ncid,
148 NCD2_inq_varids,
149 NCD2_inq_dimids,
150 NCD2_inq_typeids,
151 NCD2_inq_type_equal,
152 NCD2_def_grp,
153 NCD2_rename_grp,
154 NCD2_inq_user_type,
155 NCD2_inq_typeid,
156 
157 NCD2_def_compound,
158 NCD2_insert_compound,
159 NCD2_insert_array_compound,
160 NCD2_inq_compound_field,
161 NCD2_inq_compound_fieldindex,
162 NCD2_def_vlen,
163 NCD2_put_vlen_element,
164 NCD2_get_vlen_element,
165 NCD2_def_enum,
166 NCD2_insert_enum,
167 NCD2_inq_enum_member,
168 NCD2_inq_enum_ident,
169 NCD2_def_opaque,
170 NCD2_def_var_deflate,
171 NCD2_def_var_fletcher32,
172 NCD2_def_var_chunking,
173 NCD2_def_var_endian,
174 NCD2_def_var_filter,
175 NCD2_set_var_chunk_cache,
176 NCD2_get_var_chunk_cache,
177 
178 NC_NOTNC4_filter_actions,
179 
180 };
181 
182 const NC_Dispatch* NCD2_dispatch_table = NULL; /* moved here from ddispatch.c */
183 
184 int
NCD2_initialize(void)185 NCD2_initialize(void)
186 {
187     NCD2_dispatch_table = &NCD2_dispatch_base;
188     ncd2initialized = 1;
189 #ifdef DEBUG
190     /* force logging to go to stderr */
191     nclogclose();
192     if(nclogopen(NULL))
193         ncsetlogging(1); /* turn it on */
194 #endif
195     return NC_NOERR;
196 }
197 
198 int
NCD2_finalize(void)199 NCD2_finalize(void)
200 {
201     return NC_NOERR;
202 }
203 
204 static int
NCD2_redef(int ncid)205 NCD2_redef(int ncid)
206 {
207     return (NC_EPERM);
208 }
209 
210 static int
NCD2__enddef(int ncid,size_t h_minfree,size_t v_align,size_t v_minfree,size_t r_align)211 NCD2__enddef(int ncid, size_t h_minfree, size_t v_align, size_t v_minfree, size_t r_align)
212 {
213     return (NC_EPERM);
214 }
215 
216 static int
NCD2_sync(int ncid)217 NCD2_sync(int ncid)
218 {
219     return (NC_EINVAL);
220 }
221 
222 static int
NCD2_abort(int ncid)223 NCD2_abort(int ncid)
224 {
225     return NCD2_close(ncid,NULL);
226 }
227 
228 static int
NCD2_create(const char * path,int cmode,size_t initialsz,int basepe,size_t * chunksizehintp,void * mpidata,const NC_Dispatch * dispatch,int ncid)229 NCD2_create(const char *path, int cmode,
230            size_t initialsz, int basepe, size_t *chunksizehintp,
231            void* mpidata, const NC_Dispatch* dispatch, int ncid)
232 {
233    return NC_EPERM;
234 }
235 
236 static int
NCD2_put_vara(int ncid,int varid,const size_t * start,const size_t * edges,const void * value,nc_type memtype)237 NCD2_put_vara(int ncid, int varid,
238 	    const size_t *start, const size_t *edges,
239             const void *value,
240 	    nc_type memtype)
241 {
242     return NC_EPERM;
243 }
244 
245 static int
NCD2_get_vara(int ncid,int varid,const size_t * start,const size_t * edges,void * value,nc_type memtype)246 NCD2_get_vara(int ncid, int varid,
247 	    const size_t *start, const size_t *edges,
248             void *value,
249 	    nc_type memtype)
250 {
251     int stat = nc3d_getvarx(ncid, varid, start, edges, NC_stride_one, value,memtype);
252     return stat;
253 }
254 
255 static int
NCD2_put_vars(int ncid,int varid,const size_t * start,const size_t * edges,const ptrdiff_t * stride,const void * value0,nc_type memtype)256 NCD2_put_vars(int ncid, int varid,
257 	    const size_t *start, const size_t *edges, const ptrdiff_t* stride,
258             const void *value0, nc_type memtype)
259 {
260     return NC_EPERM;
261 }
262 
263 static int
NCD2_get_vars(int ncid,int varid,const size_t * start,const size_t * edges,const ptrdiff_t * stride,void * value,nc_type memtype)264 NCD2_get_vars(int ncid, int varid,
265 	    const size_t *start, const size_t *edges, const ptrdiff_t* stride,
266             void *value, nc_type memtype)
267 {
268     int stat = nc3d_getvarx(ncid, varid, start, edges, stride, value, memtype);
269     return stat;
270 }
271 
272 /* See ncd2dispatch.c for other version */
273 int
NCD2_open(const char * path,int mode,int basepe,size_t * chunksizehintp,void * mpidata,const NC_Dispatch * dispatch,int ncid)274 NCD2_open(const char* path, int mode, int basepe, size_t *chunksizehintp,
275           void* mpidata, const NC_Dispatch* dispatch, int ncid)
276 {
277     NCerror ncstat = NC_NOERR;
278     OCerror ocstat = OC_NOERR;
279     NCDAPCOMMON* dapcomm = NULL;
280     NC* drno;
281     const char* value;
282     int nc3id = -1;
283 
284     /* Find pointer to NC struct for this file. */
285     ncstat = NC_check_id(ncid,&drno);
286     if(ncstat != NC_NOERR) {goto done;}
287 
288     if(path == NULL)
289 	{ncstat = NC_EDAPURL; goto done;}
290     if(dispatch == NULL)
291 	PANIC("NCD3_open: no dispatch table");
292 
293     /* Setup our NC and NCDAPCOMMON state*/
294 
295     dapcomm = (NCDAPCOMMON*)calloc(1,sizeof(NCDAPCOMMON));
296     if(dapcomm == NULL)
297 	{ncstat = NC_ENOMEM; goto done;}
298 
299     NCD2_DATA_SET(drno,dapcomm);
300     drno->int_ncid = nc__pseudofd(); /* create a unique id */
301     dapcomm->controller = (NC*)drno;
302 
303     dapcomm->cdf.separator = ".";
304     dapcomm->cdf.smallsizelimit = DFALTSMALLLIMIT;
305     dapcomm->cdf.cache = createnccache();
306 
307 #ifdef HAVE_GETRLIMIT
308     { struct rlimit rl;
309       if(getrlimit(RLIMIT_NOFILE, &rl) >= 0) {
310 	dapcomm->cdf.cache->cachecount = (size_t)(rl.rlim_cur / 2);
311       }
312     }
313 #endif
314 
315 #ifdef OCCOMPILEBYDEFAULT
316     { int rullen = 0;
317     /* set the compile flag by default */
318     rullen = strlen(path)+strlen("[compile]");;
319     rullen++; /* strlcat nul */
320     dapcomm->oc.rawurltext = (char*)emalloc(rullen+1);
321     strncpy(dapcomm->oc.rawurltext,"[compile]",rullen);
322     strlcat(dapcomm->oc.rawurltext, path, rullen);
323     }
324 #else
325     dapcomm->oc.rawurltext = strdup(path);
326 #endif
327 
328     if(ncuriparse(dapcomm->oc.rawurltext,&dapcomm->oc.url))
329 	{ncstat = NC_EURL; goto done;}
330 
331     if(!constrainable(dapcomm->oc.url))
332 	SETFLAG(dapcomm->controls,NCF_UNCONSTRAINABLE);
333 
334 #ifdef COLUMBIA_HACK
335     {
336 	const char* p;
337 	/* Does this url look like it is from columbia? */
338 	if(dapcomm->oc.url->host != NULL) {
339 	    for(p=dapcomm->oc.url->host;*p;p++) {
340 	        if(strncmp(p,COLUMBIA_HACK,strlen(COLUMBIA_HACK))==0)
341 		    SETFLAG(dapcomm->controls,NCF_COLUMBIA);
342 	    }
343 	}
344     }
345 #endif
346 
347     /* fail if we are unconstrainable but have constraints */
348     if(FLAGSET(dapcomm->controls,NCF_UNCONSTRAINABLE)) {
349 	if(dapcomm->oc.url->query != NULL) {
350 	    nclog(NCLOGWARN,"Attempt to constrain an unconstrainable data source: %s",
351 		   dapcomm->oc.url->query);
352 	    ncstat = THROW(NC_EDAPCONSTRAINT);
353 	    goto done;
354 	}
355     }
356 
357     /* Use libsrc code (netcdf-3) for storing metadata */
358     {
359 	char tmpname[32];
360 
361         /* Create fake file name: exact name must be unique,
362            but is otherwise irrelevant because we are using NC_DISKLESS
363         */
364         snprintf(tmpname,sizeof(tmpname),"tmp_%d",drno->int_ncid);
365 
366         /* Now, use the file to create the hidden, in-memory netcdf file.
367 	   We want this hidden file to always be NC_CLASSIC, so we need to
368            force default format temporarily in case user changed it.
369 	   Since diskless is enabled, create file in-memory.
370 	*/
371 	{
372 	    int new = 0; /* format netcdf-3 */
373 	    int old = 0;
374 	    int ncflags = NC_CLOBBER|NC_CLASSIC_MODEL;
375 	    ncflags |= NC_DISKLESS;
376 	    nc_set_default_format(new,&old); /* save and change */
377             ncstat = nc_create(tmpname,ncflags,&nc3id);
378 	    nc_set_default_format(old,&new); /* restore */
379 	    dapcomm->substrate.realfile = ((ncflags & NC_DISKLESS) != 0);
380 	    dapcomm->substrate.filename = strdup(tmpname);
381 	    if(tmpname == NULL) ncstat = NC_ENOMEM;
382 	    dapcomm->substrate.nc3id = nc3id;
383 	}
384         if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;}
385 	/* Avoid fill */
386 	nc_set_fill(nc3id,NC_NOFILL,NULL);
387 
388     }
389     dapcomm->oc.dapconstraint = (DCEconstraint*)dcecreate(CES_CONSTRAINT);
390     dapcomm->oc.dapconstraint->projections = nclistnew();
391     dapcomm->oc.dapconstraint->selections = nclistnew();
392 
393      /* Parse constraints to make sure they are syntactically correct */
394      ncstat = dapparsedapconstraints(dapcomm,dapcomm->oc.url->query,dapcomm->oc.dapconstraint);
395      if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;}
396 
397     /* Construct a url for oc minus any constraint and params*/
398     dapcomm->oc.urltext = ncuribuild(dapcomm->oc.url,NULL,NULL,NCURIBASE);
399 
400     /* Pass to OC */
401     ocstat = oc_open(dapcomm->oc.urltext,&dapcomm->oc.conn);
402     if(ocstat != OC_NOERR) {THROWCHK(ocstat); goto done;}
403 
404 #ifdef DEBUG1
405     (void)oc_trace_curl(dapcomm->oc.conn);
406 #endif
407 
408     nullfree(dapcomm->oc.urltext); /* clean up */
409     dapcomm->oc.urltext = NULL;
410 
411     /* process control client parameters */
412     applyclientparamcontrols(dapcomm);
413 
414     /* Turn on logging; only do this after oc_open*/
415     if((value = dapparamvalue(dapcomm,"log")) != NULL) {
416 	ncloginit();
417         if(nclogopen(value))
418 	    ncsetlogging(1);
419 	ncloginit();
420         if(nclogopen(value))
421 	    ncsetlogging(1);
422     }
423 
424     /* fetch and build the unconstrained DDS for use as
425        pattern */
426     ncstat = fetchpatternmetadata(dapcomm);
427     if(ncstat != NC_NOERR) goto done;
428 
429     /* Operations on the pattern tree */
430 
431     /* Accumulate useful nodes sets  */
432     ncstat = computecdfnodesets(dapcomm,dapcomm->cdf.fullddsroot->tree);
433     if(ncstat) {THROWCHK(ncstat); goto done;}
434 
435     /* Define the dimsettrans list */
436     ncstat = definedimsettrans(dapcomm,dapcomm->cdf.fullddsroot->tree);
437     if(ncstat) {THROWCHK(ncstat); goto done;}
438 
439     /* Mark the nodes of the pattern that are eligible for prefetch */
440     ncstat = markprefetch(dapcomm);
441 
442     /* fetch and build the constrained DDS */
443     ncstat = fetchconstrainedmetadata(dapcomm);
444     if(ncstat != NC_NOERR) goto done;
445 
446 #ifdef DEBUG2
447 fprintf(stderr,"constrained dds: %s\n",dumptree(dapcomm->cdf.ddsroot));
448 #endif
449 
450     /* Operations on the constrained tree */
451 
452     /* Accumulate useful nodes sets  */
453     ncstat = computecdfnodesets(dapcomm,dapcomm->cdf.ddsroot->tree);
454     if(ncstat) {THROWCHK(ncstat); goto done;}
455 
456     /* Fix grids */
457     ncstat = fixgrids(dapcomm);
458     if(ncstat) {THROWCHK(ncstat); goto done;}
459 
460     /* Locate and mark usable sequences */
461     ncstat = sequencecheck(dapcomm);
462     if(ncstat) {THROWCHK(ncstat); goto done;}
463 
464     /* suppress variables not in usable sequences */
465     ncstat = suppressunusablevars(dapcomm);
466     if(ncstat) {THROWCHK(ncstat); goto done;}
467 
468     /* apply client parameters */
469     ncstat = applyclientparams(dapcomm);
470     if(ncstat) {THROWCHK(ncstat); goto done;}
471 
472     /* Add (as needed) string dimensions*/
473     ncstat = addstringdims(dapcomm);
474     if(ncstat) {THROWCHK(ncstat); goto done;}
475 
476     if(nclistlength(dapcomm->cdf.ddsroot->tree->seqnodes) > 0) {
477 	/* Build the sequence related dimensions */
478         ncstat = defseqdims(dapcomm);
479         if(ncstat) {THROWCHK(ncstat); goto done;}
480     }
481 
482     /* Define the dimsetplus and dimsetall lists */
483     ncstat = definedimsets(dapcomm,dapcomm->cdf.ddsroot->tree);
484     if(ncstat) {THROWCHK(ncstat); goto done;}
485 
486     /* Re-compute the dimension names*/
487     ncstat = computecdfdimnames(dapcomm);
488     if(ncstat) {THROWCHK(ncstat); goto done;}
489 
490     /* Deal with zero size dimensions */
491     ncstat = fixzerodims(dapcomm);
492     if(ncstat) {THROWCHK(ncstat); goto done;}
493 
494     /* Attempt to use the DODS_EXTRA info to turn
495        one of the dimensions into unlimited.
496        Assume computecdfdimnames34 has already been called.
497     */
498     ncstat = defrecorddim(dapcomm);
499     if(ncstat) {THROWCHK(ncstat); goto done;}
500     if(dapcomm->cdf.recorddimname != NULL
501        && nclistlength(dapcomm->cdf.ddsroot->tree->seqnodes) > 0) {
502 	/*nclog(NCLOGWARN,"unlimited dimension specified, but sequences exist in DDS");*/
503 	PANIC("unlimited dimension specified, but sequences exist in DDS");
504     }
505 
506     /* Re-compute the var names*/
507     ncstat = computecdfvarnames(dapcomm,
508 				 dapcomm->cdf.ddsroot,
509 				 dapcomm->cdf.ddsroot->tree->varnodes);
510     if(ncstat) {THROWCHK(ncstat); goto done;}
511 
512     /* Transfer data from the unconstrained DDS data to the unconstrained DDS */
513     ncstat = dimimprint(dapcomm);
514     if(ncstat) goto done;
515 
516     /* Process the constraints to map to the constrained CDF tree */
517     /* (must follow fixgrids3) */
518     ncstat = dapmapconstraints(dapcomm->oc.dapconstraint,dapcomm->cdf.ddsroot);
519     if(ncstat != NC_NOERR) goto done;
520 
521     /* Canonicalize the constraint */
522     ncstat = dapfixprojections(dapcomm->oc.dapconstraint->projections);
523     if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;}
524 
525     /* Fill in segment information */
526     ncstat = dapqualifyconstraints(dapcomm->oc.dapconstraint);
527     if(ncstat != NC_NOERR) goto done;
528 
529     /* Accumulate set of variables in the constraint's projections */
530     ncstat = dapcomputeprojectedvars(dapcomm,dapcomm->oc.dapconstraint);
531     if(ncstat) {THROWCHK(ncstat); goto done;}
532 
533     /* using the modified constraint, rebuild the constraint string */
534     if(FLAGSET(dapcomm->controls,NCF_UNCONSTRAINABLE)) {
535 	/* ignore all constraints */
536 	dapcomm->oc.urltext = ncuribuild(dapcomm->oc.url,NULL,NULL,NCURIBASE);
537     } else {
538 	char* constraintstring = dcebuildconstraintstring(dapcomm->oc.dapconstraint);
539         ncurisetquery(dapcomm->oc.url,constraintstring);
540 	nullfree(constraintstring);
541         dapcomm->oc.urltext = ncuribuild(dapcomm->oc.url,NULL,NULL,NCURISVC);
542     }
543 
544 #ifdef DEBUG
545 fprintf(stderr,"ncdap3: final constraint: %s\n",dapcomm->oc.url->query);
546 #endif
547 
548     /* Estimate the variable sizes */
549     estimatevarsizes(dapcomm);
550 
551     /* Build the meta data */
552     ncstat = buildncstructures(dapcomm);
553     if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;}
554 
555     /* Explicitly do not call enddef because it will complain
556        about variables that are too large.
557     */
558 #if 0
559     ncstat = nc_endef(nc3id,NC_NOFILL,NULL);
560     if(ncstat != NC_NOERR && ncstat != NC_EVARSIZE)
561         {THROWCHK(ncstat); goto done;}
562 #endif
563 
564     { /* (for now) break abstractions*/
565 	    NC* ncsub;
566 	    NC3_INFO* nc3i;
567 	    CDFnode* unlimited = dapcomm->cdf.recorddim;
568             /* get the dispatch data for the substrate */
569             ncstat = NC_check_id(nc3id,&ncsub);
570 	    if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;}
571 	    nc3i = (NC3_INFO*)ncsub->dispatchdata;
572 	    /* This must be checked after all dds and data processing
573                so we can figure out the value of numrecs.
574 	    */
575             if(unlimited != NULL) { /* Set the effective size of UNLIMITED */
576                 NC_set_numrecs(nc3i,unlimited->dim.declsize);
577 	    }
578             /* Pretend the substrate is read-only */
579 	    NC_set_readonly(nc3i);
580     }
581 
582     /* Do any necessary data prefetch */
583     if(FLAGSET(dapcomm->controls,NCF_PREFETCH)
584        && FLAGSET(dapcomm->controls,NCF_PREFETCH_EAGER)) {
585         ncstat = prefetchdata(dapcomm);
586         if(ncstat != NC_NOERR) {
587             del_from_NCList((NC*)drno); /* undefine here */
588 	    {THROWCHK(ncstat); goto done;}
589 	}
590     }
591 
592     return ncstat;
593 
594 done:
595     if(drno != NULL) NCD2_close(drno->ext_ncid,NULL);
596     if(ocstat != OC_NOERR) ncstat = ocerrtoncerr(ocstat);
597     return THROW(ncstat);
598 }
599 
600 int
NCD2_close(int ncid,void * ignore)601 NCD2_close(int ncid, void* ignore)
602 {
603     NC* drno;
604     NCDAPCOMMON* dapcomm;
605     int ncstatus = NC_NOERR;
606 
607     ncstatus = NC_check_id(ncid, (NC**)&drno);
608     if(ncstatus != NC_NOERR) return THROW(ncstatus);
609     dapcomm = (NCDAPCOMMON*)drno->dispatchdata;
610 
611     /* We call abort rather than close to avoid
612        trying to write anything or try to pad file length
613      */
614     ncstatus = nc_abort(getnc3id(drno));
615 
616     /* clean NC* */
617     freeNCDAPCOMMON(dapcomm);
618 
619     return THROW(ncstatus);
620 }
621 
622 /**************************************************/
623 
624 static NCerror
buildncstructures(NCDAPCOMMON * dapcomm)625 buildncstructures(NCDAPCOMMON* dapcomm)
626 {
627     NCerror ncstat = NC_NOERR;
628     CDFnode* dds = dapcomm->cdf.ddsroot;
629 
630     ncstat = buildglobalattrs(dapcomm,dds);
631     if(ncstat != NC_NOERR) goto done;
632 
633     ncstat = builddims(dapcomm);
634     if(ncstat != NC_NOERR) goto done;
635 
636     ncstat = buildvars(dapcomm);
637     if(ncstat != NC_NOERR) goto done;
638 
639 done:
640     return THROW(ncstat);
641 }
642 
643 static NCerror
builddims(NCDAPCOMMON * dapcomm)644 builddims(NCDAPCOMMON* dapcomm)
645 {
646     int i;
647     NCerror ncstat = NC_NOERR;
648     int dimid;
649     NClist* dimset = NULL;
650     NC* ncsub;
651     char* definename;
652 
653     /* collect all dimensions from variables */
654     dimset = dapcomm->cdf.ddsroot->tree->dimnodes;
655 
656     /* Sort by fullname just for the fun of it */
657     for(;;) {
658 	int last = nclistlength(dimset) - 1;
659 	int swap = 0;
660         for(i=0;i<last;i++) {
661 	    CDFnode* dim1 = (CDFnode*)nclistget(dimset,i);
662 	    CDFnode* dim2 = (CDFnode*)nclistget(dimset,i+1);
663    	    if(strcmp(dim1->ncfullname,dim2->ncfullname) > 0) {
664 		nclistset(dimset,i,(void*)dim2);
665 		nclistset(dimset,i+1,(void*)dim1);
666 		swap = 1;
667 		break;
668 	    }
669 	}
670 	if(!swap) break;
671     }
672 
673     /* Define unlimited only if needed */
674     if(dapcomm->cdf.recorddim != NULL) {
675 	CDFnode* unlimited = dapcomm->cdf.recorddim;
676 	definename = getdefinename(unlimited);
677         ncstat = nc_def_dim(dapcomm->substrate.nc3id,
678 			definename,
679 			NC_UNLIMITED,
680 			&unlimited->ncid);
681 	nullfree(definename);
682         if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;}
683 
684         /* get the id for the substrate */
685         ncstat = NC_check_id(dapcomm->substrate.nc3id,&ncsub);
686         if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;}
687 #if 0
688 	nc3sub = (NC3_INFO*)&ncsub->dispatchdata;
689         /* Set the effective size of UNLIMITED;
690            note that this cannot easily be done through the normal API.*/
691         NC_set_numrecs(nc3sub,unlimited->dim.declsize);
692 #endif
693 
694     }
695 
696     for(i=0;i<nclistlength(dimset);i++) {
697 	CDFnode* dim = (CDFnode*)nclistget(dimset,i);
698         if(dim->dim.basedim != NULL) continue; /* handle below */
699 	if(DIMFLAG(dim,CDFDIMRECORD)) continue; /* defined above */
700 #ifdef DEBUG1
701 fprintf(stderr,"define: dim: %s=%ld\n",dim->ncfullname,(long)dim->dim.declsize);
702 #endif
703 	definename = getdefinename(dim);
704         ncstat = nc_def_dim(dapcomm->substrate.nc3id,definename,dim->dim.declsize,&dimid);
705         if(ncstat != NC_NOERR) {
706           THROWCHK(ncstat); nullfree(definename); goto done;
707 	}
708 	nullfree(definename);
709         dim->ncid = dimid;
710     }
711 
712     /* Make all duplicate dims have same dimid as basedim*/
713     /* (see computecdfdimnames)*/
714     for(i=0;i<nclistlength(dimset);i++) {
715 	CDFnode* dim = (CDFnode*)nclistget(dimset,i);
716         if(dim->dim.basedim != NULL) {
717 	    dim->ncid = dim->dim.basedim->ncid;
718 	}
719     }
720 done:
721     nclistfree(dimset);
722     return THROW(ncstat);
723 }
724 
725 /* Simultaneously build any associated attributes*/
726 /* and any necessary pseudo-dimensions for string types*/
727 static NCerror
buildvars(NCDAPCOMMON * dapcomm)728 buildvars(NCDAPCOMMON* dapcomm)
729 {
730     int i,j;
731     NCerror ncstat = NC_NOERR;
732     int varid;
733     NClist* varnodes = dapcomm->cdf.ddsroot->tree->varnodes;
734     char* definename;
735 
736     ASSERT((varnodes != NULL));
737     for(i=0;i<nclistlength(varnodes);i++) {
738 	CDFnode* var = (CDFnode*)nclistget(varnodes,i);
739         int dimids[NC_MAX_VAR_DIMS];
740 	unsigned int ncrank;
741         NClist* vardims = NULL;
742 
743 	if(var->invisible) continue;
744 	if(var->array.basevar != NULL) continue;
745 
746 #ifdef DEBUG1
747 fprintf(stderr,"buildvars.candidate=|%s|\n",var->ncfullname);
748 #endif
749 
750 	vardims = var->array.dimsetall;
751 	ncrank = nclistlength(vardims);
752 	if(ncrank > 0) {
753             for(j=0;j<ncrank;j++) {
754                 CDFnode* dim = (CDFnode*)nclistget(vardims,j);
755                 dimids[j] = dim->ncid;
756  	    }
757         }
758 	definename = getdefinename(var);
759 
760 #ifdef DEBUG1
761 fprintf(stderr,"define: var: %s/%s",
762 		definename,var->ocname);
763 if(ncrank > 0) {
764 int k;
765 for(k=0;k<ncrank;k++) {
766 CDFnode* dim = (CDFnode*)nclistget(vardims,k);
767 fprintf(stderr,"[%ld]",dim->dim.declsize);
768  }
769  }
770 fprintf(stderr,"\n");
771 #endif
772         ncstat = nc_def_var(dapcomm->substrate.nc3id,
773 		        definename,
774                         var->externaltype,
775                         ncrank,
776                         (ncrank==0?NULL:dimids),
777                         &varid);
778 	nullfree(definename);
779         if(ncstat != NC_NOERR) {
780 	    THROWCHK(ncstat);
781 	    goto done;
782 	}
783         var->ncid = varid;
784 	if(var->attributes != NULL) {
785 	    NCattribute* unsignedatt = NULL;
786 	    int unsignedval = 0;
787 	    /* See if variable has _Unsigned attribute */
788 	    for(j=0;j<nclistlength(var->attributes);j++) {
789 		NCattribute* att = (NCattribute*)nclistget(var->attributes,j);
790 		if(strcmp(att->name,"_Unsigned") == 0) {
791 		    char* value = nclistget(att->values,0);
792 		    unsignedatt = att;
793 		    if(value != NULL) {
794 			if(strcasecmp(value,"false")==0
795 			   || strcmp(value,"0")==0)
796 			    unsignedval = 0;
797 			else
798 			    unsignedval = 1;
799 		    }
800 		    break;
801 		}
802 	    }
803 	    for(j=0;j<nclistlength(var->attributes);j++) {
804 		NCattribute* att = (NCattribute*)nclistget(var->attributes,j);
805 		char* val = NULL;
806 		/* Check for _FillValue/Variable mismatch */
807 		if(strcmp(att->name,"_FillValue")==0) {
808 		    /* Special case var is byte, fillvalue is int16 and
809 			unsignedattr == 0;
810 			This exception is needed because DAP2 byte type
811 			is equivalent to netcdf ubyte type. So passing
812                         a signed byte thru DAP2 requires some type and
813                         signedness hacking that we have to undo.
814 		    */
815 		    if(var->etype == NC_UBYTE
816 			&& att->etype == NC_SHORT
817 			&& unsignedatt != NULL && unsignedval == 0) {
818 			/* Forcibly change the attribute type and signedness */
819 			att->etype = NC_BYTE;
820 			val = nclistremove(unsignedatt->values,0);
821 			if(val) free(val);
822 			nclistpush(unsignedatt->values,strdup("false"));
823 		    } else if(att->etype != var->etype) {/* other mismatches */
824 			/* Log a message */
825 	                nclog(NCLOGERR,"_FillValue/Variable type mismatch: variable=%s",var->ncbasename);
826 			/* See if mismatch is allowed */
827 			if(FLAGSET(dapcomm->controls,NCF_FILLMISMATCH)) {
828 			    /* Forcibly change the attribute type to match */
829 			    att->etype = var->etype;
830 			} else {
831 			    ncstat = NC_EBADTYPE; /* fail */
832 			    goto done;
833 			}
834 		    }
835 		}
836 		ncstat = buildattribute(dapcomm,var,att);
837         	if(ncstat != NC_NOERR) goto done;
838 	    }
839 	}
840 	/* Tag the variable with its DAP path */
841 	if(dapparamcheck(dapcomm,"show","projection"))
842 	    showprojection(dapcomm,var);
843     }
844 done:
845     return THROW(ncstat);
846 }
847 
848 static NCerror
buildglobalattrs(NCDAPCOMMON * dapcomm,CDFnode * root)849 buildglobalattrs(NCDAPCOMMON* dapcomm, CDFnode* root)
850 {
851     int i;
852     NCerror ncstat = NC_NOERR;
853     const char* txt;
854     char *nltxt, *p;
855     NCbytes* buf = NULL;
856     NClist* cdfnodes;
857 
858     if(root->attributes != NULL) {
859         for(i=0;i<nclistlength(root->attributes);i++) {
860    	    NCattribute* att = (NCattribute*)nclistget(root->attributes,i);
861 	    ncstat = buildattribute(dapcomm,NULL,att);
862             if(ncstat != NC_NOERR) goto done;
863 	}
864     }
865 
866     /* Add global attribute identifying the sequence dimensions */
867     if(dapparamcheck(dapcomm,"show","seqdims")) {
868         buf = ncbytesnew();
869         cdfnodes = dapcomm->cdf.ddsroot->tree->nodes;
870         for(i=0;i<nclistlength(cdfnodes);i++) {
871 	    CDFnode* dim = (CDFnode*)nclistget(cdfnodes,i);
872 	    if(dim->nctype != NC_Dimension) continue;
873 	    if(DIMFLAG(dim,CDFDIMSEQ)) {
874 	        char* cname = cdflegalname(dim->ocname);
875 	        if(ncbyteslength(buf) > 0) ncbytescat(buf,", ");
876 	        ncbytescat(buf,cname);
877 	        nullfree(cname);
878 	    }
879 	}
880         if(ncbyteslength(buf) > 0) {
881             ncstat = nc_put_att_text(dapcomm->substrate.nc3id,NC_GLOBAL,"_sequence_dimensions",
882 	           ncbyteslength(buf),ncbytescontents(buf));
883 	}
884     }
885 
886     /* Define some additional system global attributes
887        depending on show= clientparams*/
888     /* Ignore failures*/
889 
890     if(dapparamcheck(dapcomm,"show","translate")) {
891         /* Add a global attribute to show the translation */
892         ncstat = nc_put_att_text(dapcomm->substrate.nc3id,NC_GLOBAL,"_translate",
893 	           strlen("netcdf-3"),"netcdf-3");
894     }
895     if(dapparamcheck(dapcomm,"show","url")) {
896 	if(dapcomm->oc.rawurltext != NULL)
897             ncstat = nc_put_att_text(dapcomm->substrate.nc3id,NC_GLOBAL,"_url",
898 				       strlen(dapcomm->oc.rawurltext),dapcomm->oc.rawurltext);
899     }
900     if(dapparamcheck(dapcomm,"show","dds")) {
901 	txt = NULL;
902 	if(dapcomm->cdf.ddsroot != NULL)
903   	    txt = oc_tree_text(dapcomm->oc.conn,dapcomm->cdf.ddsroot->ocnode);
904 	if(txt != NULL) {
905 	    /* replace newlines with spaces*/
906 	    nltxt = nulldup(txt);
907 	    for(p=nltxt;*p;p++) {if(*p == '\n' || *p == '\r' || *p == '\t') {*p = ' ';}};
908             ncstat = nc_put_att_text(dapcomm->substrate.nc3id,NC_GLOBAL,"_dds",strlen(nltxt),nltxt);
909 	    nullfree(nltxt);
910 	}
911     }
912     if(dapparamcheck(dapcomm,"show","das")) {
913 	txt = NULL;
914 	if(dapcomm->oc.ocdasroot != NULL)
915 	    txt = oc_tree_text(dapcomm->oc.conn,dapcomm->oc.ocdasroot);
916 	if(txt != NULL) {
917 	    nltxt = nulldup(txt);
918 	    for(p=nltxt;*p;p++) {if(*p == '\n' || *p == '\r' || *p == '\t') {*p = ' ';}};
919             ncstat = nc_put_att_text(dapcomm->substrate.nc3id,NC_GLOBAL,"_das",strlen(nltxt),nltxt);
920 	    nullfree(nltxt);
921 	}
922     }
923 
924 done:
925     ncbytesfree(buf);
926     return THROW(ncstat);
927 }
928 
929 static NCerror
buildattribute(NCDAPCOMMON * dapcomm,CDFnode * var,NCattribute * att)930 buildattribute(NCDAPCOMMON* dapcomm, CDFnode* var, NCattribute* att)
931 {
932     int i;
933     NCerror ncstat = NC_NOERR;
934     unsigned int nvalues = nclistlength(att->values);
935     int varid = (var == NULL ? NC_GLOBAL : var->ncid);
936     void* mem = NULL;
937 
938     /* If the type of the attribute is string, then we need*/
939     /* to convert to a single character string by concatenation.
940 	modified: 10/23/09 to insert newlines.
941 	modified: 10/28/09 to interpret escapes
942     */
943     if(att->etype == NC_STRING || att->etype == NC_URL) {
944 	char* newstring = NULL;
945 	size_t newlen = 0;
946 	for(i=0;i<nvalues;i++) {
947 	    char* s = (char*)nclistget(att->values,i);
948 	    newlen += (1+strlen(s));
949 	}
950 	newlen++; /* for strlcat nul */
951         newstring = (char*)malloc(newlen+1);
952         MEMCHECK(newstring,NC_ENOMEM);
953 	newstring[0] = '\0';
954 	for(i=0;i<nvalues;i++) {
955 	    char* s = (char*)nclistget(att->values,i);
956 	    if(i > 0) strlcat(newstring,"\n",newlen);
957 	    strlcat(newstring,s,newlen);
958 	}
959         dapexpandescapes(newstring);
960 	if(newstring[0]=='\0')
961 	    ncstat = nc_put_att_text(dapcomm->substrate.nc3id,varid,att->name,1,newstring);
962 	else
963 	    ncstat = nc_put_att_text(dapcomm->substrate.nc3id,varid,att->name,strlen(newstring),newstring);
964 	free(newstring);
965         if(ncstat) goto done;
966     } else {
967 	nc_type atype;
968 	unsigned int typesize;
969 	atype = nctypeconvert(dapcomm,att->etype);
970 	typesize = nctypesizeof(atype);
971 	if (nvalues > 0) {
972 	    mem = malloc(typesize * nvalues);
973 	}
974         ncstat = dapcvtattrval(atype,mem,att->values,att);
975         if(ncstat == NC_ERANGE)
976 	    nclog(NCLOGERR,"Attribute value out of range: %s:%s",
977 		(var==NULL?"":var->ncbasename),att->name);
978         if(ncstat) goto done;
979         ncstat = nc_put_att(dapcomm->substrate.nc3id,varid,att->name,atype,nvalues,mem);
980         if(ncstat) goto done;
981     }
982 done:
983     if(mem) free(mem);
984     return THROW(ncstat);
985 }
986 
987 static char*
getdefinename(CDFnode * node)988 getdefinename(CDFnode* node)
989 {
990     char* spath = NULL;
991     NClist* path = NULL;
992 
993     switch (node->nctype) {
994     case NC_Atomic:
995 	/* The define name is same as the fullname with elided nodes */
996 	path = nclistnew();
997         collectnodepath(node,path,!WITHDATASET);
998         spath = makepathstring(path,".",PATHNC|PATHELIDE);
999         nclistfree(path);
1000 	break;
1001 
1002     case NC_Dimension:
1003 	/* Return just the node's ncname */
1004 	spath = nulldup(node->ncbasename);
1005 	break;
1006 
1007     default:
1008 	PANIC("unexpected nctype");
1009     }
1010     return spath;
1011 }
1012 
1013 int
NCDAP2_ping(const char * url)1014 NCDAP2_ping(const char* url)
1015 {
1016     OCerror ocstat = OC_NOERR;
1017     ocstat = oc_ping(url);
1018     return ocerrtoncerr(ocstat);
1019 }
1020 
1021 int
NCD2_inq_format_extended(int ncid,int * formatp,int * modep)1022 NCD2_inq_format_extended(int ncid, int* formatp, int* modep)
1023 {
1024     NC* nc;
1025     int ncstatus = NC_check_id(ncid, (NC**)&nc);
1026     if(ncstatus != NC_NOERR) return THROW(ncstatus);
1027     if(modep) *modep = nc->mode;
1028     if(formatp) *formatp = NC_FORMATX_DAP2;
1029     return NC_NOERR;
1030 }
1031 
1032 /**************************************************/
1033 /* Support functions */
1034 
1035 /*
1036    Provide short and/or unified names for dimensions.
1037    This must mimic lib-ncdap, which is difficult.
1038 */
1039 NCerror
computecdfdimnames(NCDAPCOMMON * nccomm)1040 computecdfdimnames(NCDAPCOMMON* nccomm)
1041 {
1042     int i,j;
1043     char tmp[NC_MAX_NAME*2];
1044     NClist* conflicts = nclistnew();
1045     NClist* varnodes = nccomm->cdf.ddsroot->tree->varnodes;
1046     NClist* alldims;
1047     NClist* basedims;
1048 
1049     /* Collect all dimension nodes from dimsetall lists */
1050 
1051     alldims = getalldims(nccomm,0);
1052 
1053     /* Assign an index to all anonymous dimensions
1054        vis-a-vis its containing variable
1055     */
1056     for(i=0;i<nclistlength(varnodes);i++) {
1057 	CDFnode* var = (CDFnode*)nclistget(varnodes,i);
1058         for(j=0;j<nclistlength(var->array.dimsetall);j++) {
1059 	    CDFnode* dim = (CDFnode*)nclistget(var->array.dimsetall,j);
1060 	    if(dim->ocname != NULL) continue; /* not anonymous */
1061  	    computedimindexanon(dim,var);
1062 	}
1063     }
1064 
1065     /* Unify dimensions by defining one dimension as the "base"
1066        dimension, and make all "equivalent" dimensions point to the
1067        base dimension.
1068 	1. Equivalent means: same size and both have identical non-null names.
1069 	2. Dims with same name but different sizes will be handled separately
1070     */
1071     for(i=0;i<nclistlength(alldims);i++) {
1072 	CDFnode* dupdim = NULL;
1073 	CDFnode* basedim = (CDFnode*)nclistget(alldims,i);
1074 	if(basedim == NULL) continue;
1075 	if(basedim->dim.basedim != NULL) continue; /* already processed*/
1076 	for(j=i+1;j<nclistlength(alldims);j++) { /* Sigh, n**2 */
1077 	    dupdim = (CDFnode*)nclistget(alldims,j);
1078 	    if(basedim == dupdim) continue;
1079 	    if(dupdim == NULL) continue;
1080 	    if(dupdim->dim.basedim != NULL) continue; /* already processed */
1081 	    if(!equivalentdim(basedim,dupdim))
1082 		continue;
1083             dupdim->dim.basedim = basedim; /* equate */
1084 #ifdef DEBUG1
1085 fprintf(stderr,"assign: %s/%s -> %s/%s\n",
1086 basedim->dim.array->ocname,basedim->ocname,
1087 dupdim->dim.array->ocname,dupdim->ocname
1088 );
1089 #endif
1090 	}
1091     }
1092 
1093     /* Next case: same name and different sizes*/
1094     /* => rename second dim */
1095 
1096     for(i=0;i<nclistlength(alldims);i++) {
1097 	CDFnode* basedim = (CDFnode*)nclistget(alldims,i);
1098 	if(basedim->dim.basedim != NULL) continue;
1099 	/* Collect all conflicting dimensions */
1100 	nclistclear(conflicts);
1101         for(j=i+1;j<nclistlength(alldims);j++) {
1102 	    CDFnode* dim = (CDFnode*)nclistget(alldims,j);
1103 	    if(dim->dim.basedim != NULL) continue;
1104 	    if(dim->ocname == NULL && basedim->ocname == NULL) continue;
1105 	    if(dim->ocname == NULL || basedim->ocname == NULL) continue;
1106 	    if(strcmp(dim->ocname,basedim->ocname)!=0) continue;
1107 	    if(dim->dim.declsize == basedim->dim.declsize) continue;
1108 #ifdef DEBUG2
1109 fprintf(stderr,"conflict: %s[%lu] %s[%lu]\n",
1110 			basedim->ncfullname,(unsigned long)basedim->dim.declsize,
1111 			dim->ncfullname,(unsigned long)dim->dim.declsize);
1112 #endif
1113 	    nclistpush(conflicts,(void*)dim);
1114 	}
1115 	/* Give  all the conflicting dimensions an index */
1116 	for(j=0;j<nclistlength(conflicts);j++) {
1117 	    CDFnode* dim = (CDFnode*)nclistget(conflicts,j);
1118 	    dim->dim.index1 = j+1;
1119 	}
1120     }
1121     nclistfree(conflicts);
1122 
1123     /* Replace all non-base dimensions with their base dimension */
1124     for(i=0;i<nclistlength(varnodes);i++) {
1125 	CDFnode* node = (CDFnode*)nclistget(varnodes,i);
1126 	replacedims(node->array.dimsetall);
1127 	replacedims(node->array.dimsetplus);
1128 	replacedims(node->array.dimset0);
1129     }
1130 
1131     /* Collect list of all basedims */
1132     basedims = nclistnew();
1133     for(i=0;i<nclistlength(alldims);i++) {
1134 	CDFnode* dim = (CDFnode*)nclistget(alldims,i);
1135 	if(dim->dim.basedim == NULL) {
1136 	    if(!nclistcontains(basedims,(void*)dim)) {
1137 		nclistpush(basedims,(void*)dim);
1138 	    }
1139 	}
1140     }
1141 
1142     nccomm->cdf.ddsroot->tree->dimnodes = basedims;
1143 
1144     /* cleanup */
1145     nclistfree(alldims);
1146 
1147     /* Assign ncbasenames and ncfullnames to base dimensions */
1148     for(i=0;i<nclistlength(basedims);i++) {
1149 	CDFnode* dim = (CDFnode*)nclistget(basedims,i);
1150 	CDFnode* var = dim->dim.array;
1151 	if(dim->dim.basedim != NULL) PANIC1("nonbase basedim: %s\n",dim->ocname);
1152 	/* stringdim names are already assigned */
1153 	if(dim->ocname == NULL) { /* anonymous: use the index to compute the name */
1154             snprintf(tmp,sizeof(tmp),"%s_%d",
1155                             var->ncfullname,dim->dim.index1-1);
1156             nullfree(dim->ncbasename);
1157             dim->ncbasename = cdflegalname(tmp);
1158             nullfree(dim->ncfullname);
1159             dim->ncfullname = nulldup(dim->ncbasename);
1160     	} else { /* !anonymous; use index1 if defined */
1161    	    char* legalname = cdflegalname(dim->ocname);
1162 	    nullfree(dim->ncbasename);
1163 	    if(dim->dim.index1 > 0) {/* need to fix conflicting names (see above) */
1164 	        char sindex[64];
1165 		size_t baselen;
1166 		snprintf(sindex,sizeof(sindex),"_%d",dim->dim.index1);
1167 		baselen = strlen(sindex)+strlen(legalname);
1168 		baselen++; /* for strlcat nul */
1169 		dim->ncbasename = (char*)malloc(baselen+1);
1170 		if(dim->ncbasename == NULL) {nullfree(legalname); return NC_ENOMEM;}
1171 		strncpy(dim->ncbasename,legalname,baselen);
1172 		strlcat(dim->ncbasename,sindex,baselen);
1173 		nullfree(legalname);
1174 	    } else {/* standard case */
1175 	        dim->ncbasename = legalname;
1176 	    }
1177     	    nullfree(dim->ncfullname);
1178 	    dim->ncfullname = nulldup(dim->ncbasename);
1179 	}
1180      }
1181 
1182     /* Verify unique and defined names for dimensions*/
1183     for(i=0;i<nclistlength(basedims);i++) {
1184 	CDFnode* dim1 = (CDFnode*)nclistget(basedims,i);
1185         CDFnode* dim2 = NULL;
1186 	if(dim1->dim.basedim != NULL) PANIC1("nonbase basedim: %s\n",dim1->ncbasename);
1187 	if(dim1->ncbasename == NULL || dim1->ncfullname == NULL)
1188 	    PANIC1("missing dim names: %s",dim1->ocname);
1189 	/* search backward so we can delete duplicates */
1190 	for(j=nclistlength(basedims)-1;j>i;j--) {
1191       if(!dim1->ncfullname) continue;
1192       dim2 = (CDFnode*)nclistget(basedims,j);
1193       if(strcmp(dim1->ncfullname,dim2->ncfullname)==0) {
1194 		/* complain and suppress one of them */
1195 		fprintf(stderr,"duplicate dim names: %s[%lu] %s[%lu]\n",
1196                 dim1->ncfullname,(unsigned long)dim1->dim.declsize,
1197                 dim2->ncfullname,(unsigned long)dim2->dim.declsize);
1198 		nclistremove(basedims,j);
1199       }
1200 	}
1201     }
1202 
1203 #ifdef DEBUG
1204 for(i=0;i<nclistlength(basedims);i++) {
1205 CDFnode* dim = (CDFnode*)nclistget(basedims,i);
1206 fprintf(stderr,"basedim: %s=%ld\n",dim->ncfullname,(long)dim->dim.declsize);
1207  }
1208 #endif
1209 
1210     return NC_NOERR;
1211 }
1212 
1213 int
constrainable(NCURI * durl)1214 constrainable(NCURI* durl)
1215 {
1216    const char** protocol = constrainableprotocols;
1217    for(;*protocol;protocol++) {
1218 	if(strcmp(durl->protocol,*protocol)==0)
1219 	    return 1;
1220    }
1221    return 0;
1222 }
1223 
1224 /* Lookup a parameter key; case insensitive */
1225 static const char*
paramlookup(NCDAPCOMMON * state,const char * key)1226 paramlookup(NCDAPCOMMON* state, const char* key)
1227 {
1228     const char* value = NULL;
1229     if(state == NULL || key == NULL || state->oc.url == NULL) return NULL;
1230     value = ncurilookup(state->oc.url,key);
1231     return value;
1232 }
1233 
1234 /* Note: this routine only applies some common
1235    client parameters, other routines may apply
1236    specific ones.
1237 */
1238 
1239 static NCerror
applyclientparams(NCDAPCOMMON * nccomm)1240 applyclientparams(NCDAPCOMMON* nccomm)
1241 {
1242     int i,len;
1243     int dfaltstrlen = DEFAULTSTRINGLENGTH;
1244     int dfaltseqlim = DEFAULTSEQLIMIT;
1245     const char* value;
1246     char tmpname[NC_MAX_NAME+32];
1247     char* pathstr = NULL;
1248     OClink conn = nccomm->oc.conn;
1249     unsigned long limit;
1250 
1251     ASSERT(nccomm->oc.url != NULL);
1252 
1253     nccomm->cdf.cache->cachelimit = DFALTCACHELIMIT;
1254     value = paramlookup(nccomm,"cachelimit");
1255     limit = getlimitnumber(value);
1256     if(limit > 0) nccomm->cdf.cache->cachelimit = limit;
1257 
1258     nccomm->cdf.fetchlimit = DFALTFETCHLIMIT;
1259     value = paramlookup(nccomm,"fetchlimit");
1260     limit = getlimitnumber(value);
1261     if(limit > 0) nccomm->cdf.fetchlimit = limit;
1262 
1263     nccomm->cdf.smallsizelimit = DFALTSMALLLIMIT;
1264     value = paramlookup(nccomm,"smallsizelimit");
1265     limit = getlimitnumber(value);
1266     if(limit > 0) nccomm->cdf.smallsizelimit = limit;
1267 
1268     nccomm->cdf.cache->cachecount = DFALTCACHECOUNT;
1269 #ifdef HAVE_GETRLIMIT
1270     { struct rlimit rl;
1271       if(getrlimit(RLIMIT_NOFILE, &rl) >= 0) {
1272 	nccomm->cdf.cache->cachecount = (size_t)(rl.rlim_cur / 2);
1273       }
1274     }
1275 #endif
1276     value = paramlookup(nccomm,"cachecount");
1277     limit = getlimitnumber(value);
1278     if(limit > 0) nccomm->cdf.cache->cachecount = limit;
1279     /* Ignore limit if not caching */
1280     if(!FLAGSET(nccomm->controls,NCF_CACHE))
1281         nccomm->cdf.cache->cachecount = 0;
1282 
1283     if(paramlookup(nccomm,"nolimit") != NULL)
1284 	dfaltseqlim = 0;
1285     value = paramlookup(nccomm,"limit");
1286     if(value != NULL && strlen(value) != 0) {
1287         if(sscanf(value,"%d",&len) && len > 0) dfaltseqlim = len;
1288     }
1289     nccomm->cdf.defaultsequencelimit = dfaltseqlim;
1290 
1291     /* allow embedded _ */
1292     value = paramlookup(nccomm,"stringlength");
1293     if(value == NULL)
1294         value = paramlookup(nccomm,"maxstrlen");
1295     if(value != NULL && strlen(value) != 0) {
1296         if(sscanf(value,"%d",&len) && len > 0) dfaltstrlen = len;
1297     }
1298     nccomm->cdf.defaultstringlength = dfaltstrlen;
1299 
1300     /* String dimension limits apply to variables */
1301     for(i=0;i<nclistlength(nccomm->cdf.ddsroot->tree->varnodes);i++) {
1302 	CDFnode* var = (CDFnode*)nclistget(nccomm->cdf.ddsroot->tree->varnodes,i);
1303 	/* Define the client param stringlength/maxstrlen for this variable*/
1304 	/* create the variable path name */
1305 	var->maxstringlength = 0; /* => use global dfalt */
1306 	strncpy(tmpname,"stringlength_",sizeof(tmpname));
1307 	pathstr = makeocpathstring(conn,var->ocnode,".");
1308 	strlcat(tmpname,pathstr,sizeof(tmpname));
1309 	value = paramlookup(nccomm,tmpname);
1310 	if(value == NULL) {
1311 	    strcpy(tmpname,"maxstrlen_");
1312 	    strncat(tmpname,pathstr,NC_MAX_NAME);
1313 	    value = paramlookup(nccomm,tmpname);
1314         }
1315 	nullfree(pathstr);
1316         if(value != NULL && strlen(value) != 0) {
1317             if(sscanf(value,"%d",&len) && len > 0) var->maxstringlength = len;
1318 	}
1319     }
1320     /* Sequence limits apply to sequences */
1321     for(i=0;i<nclistlength(nccomm->cdf.ddsroot->tree->nodes);i++) {
1322 	CDFnode* var = (CDFnode*)nclistget(nccomm->cdf.ddsroot->tree->nodes,i);
1323 	if(var->nctype != NC_Sequence) continue;
1324 	var->sequencelimit = dfaltseqlim;
1325 	strncpy(tmpname,"nolimit_",sizeof(tmpname));
1326 	pathstr = makeocpathstring(conn,var->ocnode,".");
1327 	strlcat(tmpname,pathstr,sizeof(tmpname));
1328 	if(paramlookup(nccomm,tmpname) != NULL)
1329 	    var->sequencelimit = 0;
1330 	strncpy(tmpname,"limit_",sizeof(tmpname));
1331 	strlcat(tmpname,pathstr,sizeof(tmpname));
1332 	value = paramlookup(nccomm,tmpname);
1333         if(value != NULL && strlen(value) != 0) {
1334             if(sscanf(value,"%d",&len) && len > 0)
1335 		var->sequencelimit = len;
1336 	}
1337 	nullfree(pathstr);
1338     }
1339 
1340     /* test for the appropriate fetch flags */
1341     value = paramlookup(nccomm,"fetch");
1342     if(value != NULL && strlen(value) > 0) {
1343 	if(value[0] == 'd' || value[0] == 'D') {
1344             SETFLAG(nccomm->controls,NCF_ONDISK);
1345 	}
1346     }
1347 
1348     /* test for the force-whole-var flag */
1349     value = paramlookup(nccomm,"wholevar");
1350     if(value != NULL) {
1351         SETFLAG(nccomm->controls,NCF_WHOLEVAR);
1352     }
1353 
1354     return NC_NOERR;
1355 }
1356 
1357 /**
1358  *  Given an anonymous dimension, compute the
1359  *  effective 0-based index wrt to the specified var.
1360  *  The result should mimic the libnc-dap indices.
1361  */
1362 
1363 static void
computedimindexanon(CDFnode * dim,CDFnode * var)1364 computedimindexanon(CDFnode* dim, CDFnode* var)
1365 {
1366     int i;
1367     NClist* dimset = var->array.dimsetall;
1368     for(i=0;i<nclistlength(dimset);i++) {
1369 	CDFnode* candidate = (CDFnode*)nclistget(dimset,i);
1370         if(dim == candidate) {
1371 	   dim->dim.index1=i+1;
1372 	   return;
1373 	}
1374     }
1375 }
1376 
1377 /* Replace dims in a list with their corresponding basedim */
1378 static void
replacedims(NClist * dims)1379 replacedims(NClist* dims)
1380 {
1381     int i;
1382     for(i=0;i<nclistlength(dims);i++) {
1383         CDFnode* dim = (CDFnode*)nclistget(dims,i);
1384 	CDFnode* basedim = dim->dim.basedim;
1385 	if(basedim == NULL) continue;
1386 	nclistset(dims,i,(void*)basedim);
1387     }
1388 }
1389 
1390 /**
1391  Two dimensions are equivalent if
1392  1. they have the same size
1393  2. neither are anonymous
1394  3. they ave the same names.
1395  */
1396 static int
equivalentdim(CDFnode * basedim,CDFnode * dupdim)1397 equivalentdim(CDFnode* basedim, CDFnode* dupdim)
1398 {
1399     if(dupdim->dim.declsize != basedim->dim.declsize) return 0;
1400     if(basedim->ocname == NULL && dupdim->ocname == NULL) return 0;
1401     if(basedim->ocname == NULL || dupdim->ocname == NULL) return 0;
1402     if(strcmp(dupdim->ocname,basedim->ocname) != 0) return 0;
1403     return 1;
1404 }
1405 
1406 static void
getalldimsa(NClist * dimset,NClist * alldims)1407 getalldimsa(NClist* dimset, NClist* alldims)
1408 {
1409     int i;
1410     for(i=0;i<nclistlength(dimset);i++) {
1411 	CDFnode* dim = (CDFnode*)nclistget(dimset,i);
1412 	if(!nclistcontains(alldims,(void*)dim)) {
1413 #ifdef DEBUG3
1414 fprintf(stderr,"getalldims: %s[%lu]\n",
1415 			dim->ncfullname,(unsigned long)dim->dim.declsize);
1416 #endif
1417 	    nclistpush(alldims,(void*)dim);
1418 	}
1419     }
1420 }
1421 
1422 /* Accumulate a set of all the known dimensions
1423    vis-a-vis defined variables
1424 */
1425 NClist*
getalldims(NCDAPCOMMON * nccomm,int visibleonly)1426 getalldims(NCDAPCOMMON* nccomm, int visibleonly)
1427 {
1428     int i;
1429     NClist* alldims = nclistnew();
1430     NClist* varnodes = nccomm->cdf.ddsroot->tree->varnodes;
1431 
1432     /* get bag of all dimensions */
1433     for(i=0;i<nclistlength(varnodes);i++) {
1434 	CDFnode* node = (CDFnode*)nclistget(varnodes,i);
1435 	if(!visibleonly || !node->invisible) {
1436 	    getalldimsa(node->array.dimsetall,alldims);
1437 	}
1438     }
1439     return alldims;
1440 }
1441 
1442 
1443 static NCerror
addstringdims(NCDAPCOMMON * dapcomm)1444 addstringdims(NCDAPCOMMON* dapcomm)
1445 {
1446     /* for all variables of string type, we will need another dimension
1447        to represent the string; Accumulate the needed sizes and create
1448        the dimensions with a specific name: either as specified
1449        in DODS{...} attribute set or defaulting to the variable name.
1450        All such dimensions are global.
1451     */
1452     int i;
1453     NClist* varnodes = dapcomm->cdf.ddsroot->tree->varnodes;
1454     CDFnode* globalsdim = NULL;
1455     char dimname[4096];
1456     size_t dimsize;
1457 
1458     /* Start by creating the global string dimension */
1459     snprintf(dimname,sizeof(dimname),"maxStrlen%lu",
1460 	    (unsigned long)dapcomm->cdf.defaultstringlength);
1461     globalsdim = makecdfnode(dapcomm, dimname, OC_Dimension, NULL,
1462                                  dapcomm->cdf.ddsroot);
1463     nclistpush(dapcomm->cdf.ddsroot->tree->nodes,(void*)globalsdim);
1464     DIMFLAGSET(globalsdim,CDFDIMSTRING);
1465     globalsdim->dim.declsize = dapcomm->cdf.defaultstringlength;
1466     globalsdim->dim.declsize0 = globalsdim->dim.declsize;
1467     globalsdim->dim.array = dapcomm->cdf.ddsroot;
1468     globalsdim->ncbasename = cdflegalname(dimname);
1469     globalsdim->ncfullname = nulldup(globalsdim->ncbasename);
1470     dapcomm->cdf.globalstringdim = globalsdim;
1471 
1472     for(i=0;i<nclistlength(varnodes);i++) {
1473 	CDFnode* var = (CDFnode*)nclistget(varnodes,i);
1474 	CDFnode* sdim = NULL;
1475 
1476 	/* Does this node need a string dim? */
1477 	if(var->etype != NC_STRING && var->etype != NC_URL) continue;
1478 
1479 	dimsize = 0;
1480 	if(var->dodsspecial.maxstrlen > 0)
1481 	    dimsize = var->dodsspecial.maxstrlen;
1482 	else
1483 	    dimsize = var->maxstringlength;
1484 
1485 	/* check is a variable-specific string length was specified */
1486 	if(dimsize == 0)
1487 	    sdim = dapcomm->cdf.globalstringdim; /* use default */
1488 	else {
1489 	    /* create a pseudo dimension for the charification of the string*/
1490 	    if(var->dodsspecial.dimname != NULL) {
1491 	        strncpy(dimname,var->dodsspecial.dimname,sizeof(dimname));
1492 	        dimname[sizeof(dimname)-1] = '\0';
1493 	    } else
1494 	        snprintf(dimname,sizeof(dimname),"maxStrlen%lu",
1495 			 (unsigned long)dimsize);
1496 	    sdim = makecdfnode(dapcomm, dimname, OC_Dimension, NULL,
1497                                  dapcomm->cdf.ddsroot);
1498 	    if(sdim == NULL) return THROW(NC_ENOMEM);
1499 	    nclistpush(dapcomm->cdf.ddsroot->tree->nodes,(void*)sdim);
1500 	    DIMFLAGSET(sdim,CDFDIMSTRING);
1501 	    sdim->dim.declsize = dimsize;
1502 	    sdim->dim.declsize0 = dimsize;
1503 	    sdim->dim.array = var;
1504 	    sdim->ncbasename = cdflegalname(sdim->ocname);
1505 	    sdim->ncfullname = nulldup(sdim->ncbasename);
1506 	}
1507 	/* tag the variable with its string dimension*/
1508 	var->array.stringdim = sdim;
1509     }
1510     return NC_NOERR;
1511 }
1512 
1513 static NCerror
defrecorddim(NCDAPCOMMON * dapcomm)1514 defrecorddim(NCDAPCOMMON* dapcomm)
1515 {
1516     unsigned int i;
1517     NCerror ncstat = NC_NOERR;
1518     NClist* basedims;
1519 
1520     if(dapcomm->cdf.recorddimname == NULL) return NC_NOERR; /* ignore */
1521     /* Locate the base dimension matching the record dim */
1522     basedims = dapcomm->cdf.ddsroot->tree->dimnodes;
1523     for(i=0;i<nclistlength(basedims);i++) {
1524         CDFnode* dim = (CDFnode*)nclistget(basedims,i);
1525 	if(strcmp(dim->ocname,dapcomm->cdf.recorddimname) != 0) continue;
1526         DIMFLAGSET(dim,CDFDIMRECORD);
1527 	dapcomm->cdf.recorddim = dim;
1528 	break;
1529     }
1530 
1531     return ncstat;
1532 }
1533 
1534 static NCerror
defseqdims(NCDAPCOMMON * dapcomm)1535 defseqdims(NCDAPCOMMON* dapcomm)
1536 {
1537     unsigned int i = 0;
1538     NCerror ncstat = NC_NOERR;
1539     int seqdims = 1; /* default is to compute seq dims counts */
1540 
1541     /* Does the user want to compute actual sequence sizes? */
1542     if(dapparamvalue(dapcomm,"noseqdims")) seqdims = 0;
1543 
1544     /*
1545 	Compute and define pseudo dimensions for sequences
1546 	meeting the following qualifications:
1547 	1. all parents (transitively) of the sequence must
1548            be either a dataset or a scalar structure.
1549 	2. it must be possible to find a usable sequence constraint.
1550 	All other sequences will be ignored.
1551     */
1552 
1553     for(i=0;i<nclistlength(dapcomm->cdf.ddsroot->tree->seqnodes);i++) {
1554         CDFnode* seq = (CDFnode*)nclistget(dapcomm->cdf.ddsroot->tree->seqnodes,i);
1555 	size_t seqsize = 0;
1556 	CDFnode* sqdim = NULL;
1557 	CDFnode* container;
1558 	/* Does this sequence match the requirements for use ? */
1559 	seq->usesequence = 1; /* assume */
1560 	for(container=seq->container;container != NULL;container=container->container) {
1561 	    if(container->nctype == NC_Dataset) break;
1562 	    if(container->nctype != NC_Structure
1563 	       || nclistlength(container->array.dimset0) > 0)
1564 		{seq->usesequence = 0; break;}/* no good */
1565 	}
1566 	/* Does the user want us to compute the actual sequence dim size? */
1567 	if(seq->usesequence && seqdims) {
1568 	    ncstat = getseqdimsize(dapcomm,seq,&seqsize);
1569 	    if(ncstat != NC_NOERR) {
1570                 /* Cannot read sequence; mark as unusable */
1571 	        seq->usesequence = 0;
1572 	    }
1573 	} else { /* !seqdims default to size = 1 */
1574 	    seqsize = 1;
1575 	}
1576 	if(seq->usesequence) {
1577 	    /* Note: we are making the dimension in the dds root tree */
1578             ncstat = makeseqdim(dapcomm,seq,seqsize,&sqdim);
1579             if(ncstat) goto fail;
1580             seq->array.seqdim = sqdim;
1581 	} else
1582             seq->array.seqdim = NULL;
1583     }
1584 
1585 fail:
1586     return ncstat;
1587 }
1588 
1589 static NCerror
showprojection(NCDAPCOMMON * dapcomm,CDFnode * var)1590 showprojection(NCDAPCOMMON* dapcomm, CDFnode* var)
1591 {
1592     int i,rank;
1593     NCerror ncstat = NC_NOERR;
1594     NCbytes* projection = ncbytesnew();
1595     NClist* path = nclistnew();
1596     NC* drno = dapcomm->controller;
1597 
1598     /* Collect the set of DDS node name forming the xpath */
1599     collectnodepath(var,path,WITHOUTDATASET);
1600     for(i=0;i<nclistlength(path);i++) {
1601         CDFnode* node = (CDFnode*)nclistget(path,i);
1602 	if(i > 0) ncbytescat(projection,".");
1603 	ncbytescat(projection,node->ocname);
1604     }
1605     nclistfree(path);
1606     /* Now, add the dimension info */
1607     rank = nclistlength(var->array.dimset0);
1608     for(i=0;i<rank;i++) {
1609 	CDFnode* dim = (CDFnode*)nclistget(var->array.dimset0,i);
1610 	char tmp[32];
1611 	ncbytescat(projection,"[");
1612 	snprintf(tmp,sizeof(tmp),"%lu",(unsigned long)dim->dim.declsize);
1613 	ncbytescat(projection,tmp);
1614 	ncbytescat(projection,"]");
1615     }
1616     /* Define the attribute */
1617     ncstat = nc_put_att_text(getncid(drno),var->ncid,
1618                                "_projection",
1619 		               ncbyteslength(projection),
1620 			       ncbytescontents(projection));
1621     ncbytesfree(projection);
1622     return ncstat;
1623 }
1624 
1625 static NCerror
getseqdimsize(NCDAPCOMMON * dapcomm,CDFnode * seq,size_t * sizep)1626 getseqdimsize(NCDAPCOMMON* dapcomm, CDFnode* seq, size_t* sizep)
1627 {
1628     NCerror ncstat = NC_NOERR;
1629     OCerror ocstat = OC_NOERR;
1630     OClink conn = dapcomm->oc.conn;
1631     OCdatanode rootcontent = NULL;
1632     OCddsnode ocroot;
1633     CDFnode* dxdroot;
1634     CDFnode* xseq;
1635     NCbytes* seqcountconstraints = ncbytesnew();
1636     size_t seqsize = 0;
1637 
1638     /* Read the minimal amount of data in order to get the count */
1639     /* If the url is unconstrainable, then get the whole thing */
1640     computeseqcountconstraints(dapcomm,seq,seqcountconstraints);
1641 #ifdef DEBUG
1642 fprintf(stderr,"seqcountconstraints: %s\n",ncbytescontents(seqcountconstraints));
1643 #endif
1644 
1645     /* Fetch the minimal data */
1646     if(FLAGSET(dapcomm->controls,NCF_UNCONSTRAINABLE))
1647         ncstat = dap_fetch(dapcomm,conn,NULL,OCDATADDS,&ocroot);
1648     else
1649         ncstat = dap_fetch(dapcomm,conn,ncbytescontents(seqcountconstraints),OCDATADDS,&ocroot);
1650     if(ncstat) goto fail;
1651 
1652     ncstat = buildcdftree(dapcomm,ocroot,OCDATA,&dxdroot);
1653     if(ncstat) goto fail;
1654     /* attach DATADDS to DDS */
1655     ncstat = attach(dxdroot,seq);
1656     if(ncstat) goto fail;
1657 
1658     /* WARNING: we are now switching to datadds tree */
1659     xseq = seq->attachment;
1660     ncstat = countsequence(dapcomm,xseq,&seqsize);
1661     if(ncstat != NC_NOERR) goto fail;
1662 
1663 #ifdef DEBUG
1664 fprintf(stderr,"sequencesize: %s = %lu\n",seq->ocname,(unsigned long)seqsize);
1665 #endif
1666 
1667     /* throw away the fetch'd trees */
1668     unattach(dapcomm->cdf.ddsroot);
1669     freecdfroot(dxdroot);
1670 #if 1
1671 /*Note sure what this is doing?*/
1672     if(ncstat != NC_NOERR) {
1673         /* Cannot get DATADDDS*/
1674 	char* code;
1675 	char* msg;
1676 	long httperr;
1677 	oc_svcerrordata(dapcomm->oc.conn,&code,&msg,&httperr);
1678 	if(code != NULL) {
1679 	    nclog(NCLOGERR,"oc_fetch_datadds failed: %s %s %l",
1680 			code,msg,httperr);
1681 	}
1682 	ocstat = OC_NOERR;
1683     }
1684 #endif
1685 
1686     if(sizep) *sizep = seqsize;
1687 
1688 fail:
1689     ncbytesfree(seqcountconstraints);
1690     oc_data_free(conn,rootcontent);
1691     if(ocstat != OC_NOERR) ncstat = ocerrtoncerr(ocstat);
1692     return ncstat;
1693 }
1694 
1695 static NCerror
makeseqdim(NCDAPCOMMON * dapcomm,CDFnode * seq,size_t count,CDFnode ** sqdimp)1696 makeseqdim(NCDAPCOMMON* dapcomm, CDFnode* seq, size_t count, CDFnode** sqdimp)
1697 {
1698     CDFnode* sqdim;
1699     CDFnode* root = seq->root;
1700     CDFtree* tree = root->tree;
1701 
1702     /* build the dimension with given size; keep the dimension anonymous */
1703     sqdim = makecdfnode(dapcomm,seq->ocname,OC_Dimension,NULL,root);
1704     if(sqdim == NULL) return THROW(NC_ENOMEM);
1705     nclistpush(tree->nodes,(void*)sqdim);
1706     /* Assign a name to the sequence node */
1707     sqdim->ncbasename = cdflegalname(seq->ocname);
1708     sqdim->ncfullname = nulldup(sqdim->ncbasename);
1709     DIMFLAGSET(sqdim,CDFDIMSEQ);
1710     sqdim->dim.declsize = count;
1711     sqdim->dim.declsize0 = count;
1712     sqdim->dim.array = seq;
1713     if(sqdimp) *sqdimp = sqdim;
1714     return NC_NOERR;
1715 }
1716 
1717 static NCerror
countsequence(NCDAPCOMMON * dapcomm,CDFnode * xseq,size_t * sizep)1718 countsequence(NCDAPCOMMON* dapcomm, CDFnode* xseq, size_t* sizep)
1719 {
1720     unsigned int i;
1721     NClist* path = nclistnew();
1722     int index;
1723     OCerror ocstat = OC_NOERR;
1724     NCerror ncstat = NC_NOERR;
1725     OClink conn = dapcomm->oc.conn;
1726     size_t recordcount;
1727     CDFnode* xroot;
1728     OCdatanode data = NULL;
1729 
1730     ASSERT((xseq->nctype == NC_Sequence));
1731 
1732     /* collect the path to the sequence node */
1733     collectnodepath(xseq,path,WITHDATASET);
1734 
1735     /* Get tree root */
1736     ASSERT(xseq->root == (CDFnode*)nclistget(path,0));
1737     xroot = xseq->root;
1738     ocstat = oc_data_getroot(conn,xroot->tree->ocroot,&data);
1739     if(ocstat) goto done;
1740 
1741     /* Basically we use the path to walk the data instances to reach
1742        the sequence instance
1743     */
1744     for(i=0;i<nclistlength(path);i++) {
1745         CDFnode* current = (CDFnode*)nclistget(path,i);
1746 	OCdatanode nextdata = NULL;
1747 	CDFnode* next = NULL;
1748 
1749 	/* invariant: current = ith node in path; data = corresponding
1750            datanode
1751         */
1752 
1753 	/* get next node in next and next instance in nextdata */
1754 	if(current->nctype == NC_Structure
1755 	   || current->nctype == NC_Dataset) {
1756 	    if(nclistlength(current->array.dimset0) > 0) {
1757 		/* Cannot handle this case */
1758 		ncstat = THROW(NC_EDDS);
1759 		goto done;
1760 	    }
1761 	    /* get next node in path; structure/dataset => exists */
1762 	    next = (CDFnode*)nclistget(path,i+1);
1763 	    index = fieldindex(current,next);
1764             /* Move to appropriate field */
1765 	    ocstat = oc_data_ithfield(conn,data,index,&nextdata);
1766 	    if(ocstat) goto done;
1767 	    oc_data_free(conn,data);
1768 	    data = nextdata; /* set up for next loop iteration */
1769 	} else if(current->nctype ==  NC_Sequence) {
1770 	    /* Check for nested Sequences */
1771 	    if(current != xseq) {
1772 		/* Cannot handle this case */
1773 		ncstat = THROW(NC_EDDS);
1774 		goto done;
1775 	    }
1776 	    /* Get the record count */
1777 	    ocstat = oc_data_recordcount(conn,data,&recordcount);
1778     	    if(sizep) *sizep = recordcount;
1779 	    oc_data_free(conn,data); /* reclaim */
1780 	    break; /* leave the loop */
1781 	} else {
1782 	    PANIC("unexpected mode");
1783 	    return NC_EINVAL;
1784         }
1785     }
1786 
1787 done:
1788     nclistfree(path);
1789     if(ocstat != OC_NOERR) ncstat = ocerrtoncerr(ocstat);
1790     return THROW(ncstat);
1791 }
1792 
1793 static NCerror
freeNCDAPCOMMON(NCDAPCOMMON * dapcomm)1794 freeNCDAPCOMMON(NCDAPCOMMON* dapcomm)
1795 {
1796     if(dapcomm == NULL) return NC_NOERR;
1797     freenccache(dapcomm,dapcomm->cdf.cache);
1798     nclistfree(dapcomm->cdf.projectedvars);
1799     nullfree(dapcomm->cdf.recorddimname);
1800 
1801     /* free the trees */
1802     freecdfroot(dapcomm->cdf.ddsroot);
1803     dapcomm->cdf.ddsroot = NULL;
1804     freecdfroot(dapcomm->cdf.fullddsroot);
1805     dapcomm->cdf.fullddsroot = NULL;
1806     if(dapcomm->oc.ocdasroot != NULL)
1807         oc_root_free(dapcomm->oc.conn,dapcomm->oc.ocdasroot);
1808     dapcomm->oc.ocdasroot = NULL;
1809     oc_close(dapcomm->oc.conn); /* also reclaims remaining OC trees */
1810     ncurifree(dapcomm->oc.url);
1811     nullfree(dapcomm->oc.urltext);
1812     nullfree(dapcomm->oc.rawurltext);
1813 
1814     dcefree((DCEnode*)dapcomm->oc.dapconstraint);
1815     dapcomm->oc.dapconstraint = NULL;
1816 
1817     /* Note that the ncio layer will figure out that the tmp file needs to be deleted,
1818        so we do not have to do it.
1819     */
1820     nullfree(dapcomm->substrate.filename); /* always reclaim */
1821 
1822     free(dapcomm);
1823 
1824     return NC_NOERR;
1825 }
1826 
1827 static size_t
fieldindex(CDFnode * parent,CDFnode * child)1828 fieldindex(CDFnode* parent, CDFnode* child)
1829 {
1830     unsigned int i;
1831     for(i=0;i<nclistlength(parent->subnodes);i++) {
1832 	CDFnode* node = (CDFnode*)nclistget(parent->subnodes,i);
1833 	if(node == child) return i;
1834     }
1835     return -1;
1836 }
1837 
1838 /*
1839 This is more complex than one might think. We want to find
1840 a path to a variable inside the given node so that we can
1841 ask for a single instance of that variable to minimize the
1842 amount of data we retrieve. However, we want to avoid passing
1843 through any nested sequence. This is possible because of the way
1844 that sequencecheck() works.
1845 TODO: some servers will not accept an unconstrained fetch, so
1846 make sure we always have a constraint.
1847 */
1848 
1849 static NCerror
computeseqcountconstraints(NCDAPCOMMON * dapcomm,CDFnode * seq,NCbytes * seqcountconstraints)1850 computeseqcountconstraints(NCDAPCOMMON* dapcomm, CDFnode* seq, NCbytes* seqcountconstraints)
1851 {
1852     int i,j;
1853     NClist* path = NULL;
1854     CDFnode* var = NULL;
1855 
1856     ASSERT(seq->nctype == NC_Sequence);
1857     computeseqcountconstraintsr(dapcomm,seq,&var);
1858 
1859     ASSERT((var != NULL));
1860 
1861     /* Compute var path */
1862     path = nclistnew();
1863     collectnodepath(var,path,WITHOUTDATASET);
1864 
1865     /* construct the projection path using minimal index values */
1866     for(i=0;i<nclistlength(path);i++) {
1867 	CDFnode* node = (CDFnode*)nclistget(path,i);
1868 	if(i > 0) ncbytescat(seqcountconstraints,".");
1869 	ncbytescat(seqcountconstraints,node->ocname);
1870 	if(node == seq) {
1871 	    /* Use the limit */
1872 	    if(node->sequencelimit > 0) {
1873 		char tmp[64];
1874 		snprintf(tmp,sizeof(tmp),"[0:%lu]",
1875 		         (unsigned long)(node->sequencelimit - 1));
1876 		ncbytescat(seqcountconstraints,tmp);
1877 	    }
1878 	} else if(nclistlength(node->array.dimset0) > 0) {
1879 	    int ndims = nclistlength(node->array.dimset0);
1880 	    for(j=0;j<ndims;j++) {
1881 		CDFnode* dim = (CDFnode*)nclistget(node->array.dimset0,j);
1882 		if(DIMFLAG(dim,CDFDIMSTRING)) {
1883 		    ASSERT((j == (ndims - 1)));
1884 		    break;
1885 		}
1886 		ncbytescat(seqcountconstraints,"[0]");
1887 	    }
1888 	}
1889     }
1890     /* Finally, add in any selection from the original URL */
1891     if(dap_getselection(dapcomm->oc.url) != NULL)
1892         ncbytescat(seqcountconstraints,dap_getselection(dapcomm->oc.url));
1893     nclistfree(path);
1894     return NC_NOERR;
1895 }
1896 
1897 
1898 /* Given an existing candidate, see if we prefer newchoice */
1899 static CDFnode*
prefer(CDFnode * candidate,CDFnode * newchoice)1900 prefer(CDFnode* candidate, CDFnode* newchoice)
1901 {
1902     nc_type newtyp;
1903     nc_type cantyp;
1904     int newisstring;
1905     int canisstring;
1906     int newisscalar;
1907     int canisscalar;
1908 
1909     /* always choose !null over null */
1910     if(newchoice == NULL)
1911 	return candidate;
1912     if(candidate == NULL)
1913 	return newchoice;
1914 
1915     newtyp = newchoice->etype;
1916     cantyp = candidate->etype;
1917     newisstring = (newtyp == NC_STRING || newtyp == NC_URL);
1918     canisstring = (cantyp == NC_STRING || cantyp == NC_URL);
1919     newisscalar = (nclistlength(newchoice->array.dimset0) == 0);
1920     canisscalar = (nclistlength(candidate->array.dimset0) == 0);
1921 
1922     ASSERT(candidate->nctype == NC_Atomic && newchoice->nctype == NC_Atomic);
1923 
1924     /* choose non-string over string */
1925     if(canisstring && !newisstring)
1926 	return newchoice;
1927     if(!canisstring && newisstring)
1928 	return candidate;
1929 
1930     /* choose scalar over array */
1931     if(canisscalar && !newisscalar)
1932 	return candidate;
1933     if(!canisscalar && newisscalar)
1934 	return candidate;
1935 
1936     /* otherwise choose existing candidate */
1937     return candidate;
1938 }
1939 
1940 /* computeseqcountconstraints recursive helper function */
1941 static void
computeseqcountconstraintsr(NCDAPCOMMON * dapcomm,CDFnode * node,CDFnode ** candidatep)1942 computeseqcountconstraintsr(NCDAPCOMMON* dapcomm, CDFnode* node, CDFnode** candidatep)
1943 {
1944     CDFnode* candidate;
1945     CDFnode* compound;
1946     unsigned int i;
1947 
1948     candidate = NULL;
1949     compound = NULL;
1950     if(node == NULL)
1951       return;
1952     for(i=0;i<nclistlength(node->subnodes);i++) {
1953         CDFnode* subnode = (CDFnode*)nclistget(node->subnodes,i);
1954         if(subnode->nctype == NC_Structure || subnode->nctype == NC_Grid)
1955 	    compound = subnode; /* save for later recursion */
1956 	else if(subnode->nctype == NC_Atomic)
1957 	    candidate = prefer(candidate,subnode);
1958     }
1959     if(candidate == NULL && compound == NULL) {
1960 	PANIC("cannot find candidate for seqcountconstraints for a sequence");
1961     } else if(candidate != NULL && candidatep != NULL) {
1962 	*candidatep = candidate;
1963     } else { /* compound != NULL by construction */
1964 	/* recurse on a nested grids or strucures */
1965         computeseqcountconstraintsr(dapcomm,compound,candidatep);
1966     }
1967 }
1968 
1969 
1970 static unsigned long
cdftotalsize(NClist * dimensions)1971 cdftotalsize(NClist* dimensions)
1972 {
1973     unsigned int i;
1974     unsigned long total = 1;
1975     if(dimensions != NULL) {
1976 	for(i=0;i<nclistlength(dimensions);i++) {
1977 	    CDFnode* dim = (CDFnode*)nclistget(dimensions,i);
1978 	    total *= dim->dim.declsize;
1979 	}
1980     }
1981     return total;
1982 }
1983 
1984 /* Estimate variables sizes and then resort the variable list
1985    by that size
1986 */
1987 static void
estimatevarsizes(NCDAPCOMMON * dapcomm)1988 estimatevarsizes(NCDAPCOMMON* dapcomm)
1989 {
1990     int ivar;
1991     unsigned int rank;
1992     size_t totalsize = 0;
1993 
1994     for(ivar=0;ivar<nclistlength(dapcomm->cdf.ddsroot->tree->varnodes);ivar++) {
1995         CDFnode* var = (CDFnode*)nclistget(dapcomm->cdf.ddsroot->tree->varnodes,ivar);
1996 	NClist* ncdims = var->array.dimset0;
1997 	rank = nclistlength(ncdims);
1998 	if(rank == 0) { /* use instance size of the type */
1999 	    var->estimatedsize = nctypesizeof(var->etype);
2000 #ifdef DEBUG1
2001 fprintf(stderr,"scalar %s.estimatedsize = %lu\n",
2002 	makecdfpathstring(var,"."),var->estimatedsize);
2003 #endif
2004 	} else {
2005 	    unsigned long size = cdftotalsize(ncdims);
2006 	    size *= nctypesizeof(var->etype);
2007 #ifdef DEBUG1
2008 fprintf(stderr,"array %s(%u).estimatedsize = %lu\n",
2009 	makecdfpathstring(var,"."),rank,size);
2010 #endif
2011 	    var->estimatedsize = size;
2012 	}
2013 	totalsize += var->estimatedsize;
2014     }
2015 #ifdef DEBUG1
2016 fprintf(stderr,"total estimatedsize = %lu\n",totalsize);
2017 #endif
2018     dapcomm->cdf.totalestimatedsize = totalsize;
2019 }
2020 
2021 static NCerror
fetchpatternmetadata(NCDAPCOMMON * dapcomm)2022 fetchpatternmetadata(NCDAPCOMMON* dapcomm)
2023 {
2024     NCerror ncstat = NC_NOERR;
2025     OCerror ocstat = OC_NOERR;
2026     OCddsnode ocroot = NULL;
2027     CDFnode* ddsroot = NULL;
2028     char* ce = NULL;
2029 
2030     /* Temporary hack: we need to get the selection string
2031        from the url
2032     */
2033     /* Get (almost) unconstrained DDS; In order to handle functions
2034        correctly, those selections must always be included
2035     */
2036     if(FLAGSET(dapcomm->controls,NCF_UNCONSTRAINABLE))
2037 	ce = NULL;
2038     else
2039         ce = nulldup(dap_getselection(dapcomm->oc.url));
2040 
2041     /* Get selection constrained DDS */
2042     ncstat = dap_fetch(dapcomm,dapcomm->oc.conn,ce,OCDDS,&ocroot);
2043     if(ncstat != NC_NOERR) {
2044 	/* Special Hack. If the protocol is file, then see if
2045            we can get the dds from the .dods file
2046         */
2047 	if(strcmp(dapcomm->oc.url->protocol,"file") != 0) {
2048 	    THROWCHK(ocstat); goto done;
2049 	}
2050 	/* Fetch the data dds */
2051         ncstat = dap_fetch(dapcomm,dapcomm->oc.conn,ce,OCDATADDS,&ocroot);
2052         if(ncstat != NC_NOERR) {
2053 	    THROWCHK(ncstat); goto done;
2054 	}
2055 	/* Note what we did */
2056 	nclog(NCLOGWARN,"Cannot locate .dds file, using .dods file");
2057     }
2058 
2059     /* Get selection constrained DAS */
2060     ncstat = dap_fetch(dapcomm,dapcomm->oc.conn,ce,OCDAS,&dapcomm->oc.ocdasroot);
2061     if(ncstat != NC_NOERR) {
2062 	/* Ignore but complain */
2063 	nclog(NCLOGWARN,"Could not read DAS; ignored");
2064         dapcomm->oc.ocdasroot = NULL;
2065 	ncstat = NC_NOERR;
2066     }
2067 
2068     /* Construct the netcdf cdf tree corresponding to the dds tree*/
2069     ncstat = buildcdftree(dapcomm,ocroot,OCDDS,&ddsroot);
2070     if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;}
2071     dapcomm->cdf.fullddsroot = ddsroot;
2072     ddsroot = NULL; /* avoid double reclaim */
2073 
2074     /* Combine DDS and DAS */
2075     if(dapcomm->oc.ocdasroot != NULL) {
2076 	ncstat = dapmerge(dapcomm,dapcomm->cdf.fullddsroot,
2077                            dapcomm->oc.ocdasroot);
2078         if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;}
2079     }
2080 
2081 #ifdef DEBUG2
2082 fprintf(stderr,"full pattern:\n%s",dumptree(dapcomm->cdf.fullddsroot));
2083 #endif
2084 
2085 done:
2086     nullfree(ce);
2087     if(ocstat != OC_NOERR) ncstat = ocerrtoncerr(ocstat);
2088     return ncstat;
2089 }
2090 
2091 static NCerror
fetchconstrainedmetadata(NCDAPCOMMON * dapcomm)2092 fetchconstrainedmetadata(NCDAPCOMMON* dapcomm)
2093 {
2094     NCerror ncstat = NC_NOERR;
2095     OCerror ocstat = OC_NOERR;
2096     OCddsnode ocroot;
2097     CDFnode* ddsroot; /* constrained */
2098     char* ce = NULL;
2099 
2100     if(FLAGSET(dapcomm->controls,NCF_UNCONSTRAINABLE))
2101 	ce = NULL;
2102     else
2103         ce = dcebuildconstraintstring(dapcomm->oc.dapconstraint);
2104     {
2105         ncstat = dap_fetch(dapcomm,dapcomm->oc.conn,ce,OCDDS,&ocroot);
2106         if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;}
2107 
2108         /* Construct our parallel dds tree; including attributes*/
2109         ncstat = buildcdftree(dapcomm,ocroot,OCDDS,&ddsroot);
2110         if(ncstat) goto fail;
2111 	ocroot = NULL; /* avoid duplicate reclaim */
2112 
2113 	dapcomm->cdf.ddsroot = ddsroot;
2114 	ddsroot = NULL; /* to avoid double reclamation */
2115 
2116         if(!FLAGSET(dapcomm->controls,NCF_UNCONSTRAINABLE)) {
2117             /* fix DAP server problem by adding back any inserting needed structure nodes */
2118             ncstat = restruct(dapcomm, dapcomm->cdf.ddsroot,dapcomm->cdf.fullddsroot,dapcomm->oc.dapconstraint->projections);
2119             if(ncstat) goto fail;
2120 	}
2121 
2122 #ifdef DEBUG2
2123 fprintf(stderr,"constrained:\n%s",dumptree(dapcomm->cdf.ddsroot));
2124 #endif
2125 
2126         /* Combine DDS and DAS */
2127 	if(dapcomm->oc.ocdasroot != NULL) {
2128             ncstat = dapmerge(dapcomm,dapcomm->cdf.ddsroot,
2129                                dapcomm->oc.ocdasroot);
2130             if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;}
2131 	}
2132         /* map the constrained DDS to the unconstrained DDS */
2133         ncstat = mapnodes(dapcomm->cdf.ddsroot,dapcomm->cdf.fullddsroot);
2134         if(ncstat) goto fail;
2135 
2136     }
2137 
2138 fail:
2139     nullfree(ce);
2140     if(ocstat != OC_NOERR) ncstat = ocerrtoncerr(ocstat);
2141     return ncstat;
2142 }
2143 
2144 /* Suppress variables not in usable sequences*/
2145 static NCerror
suppressunusablevars(NCDAPCOMMON * dapcomm)2146 suppressunusablevars(NCDAPCOMMON* dapcomm)
2147 {
2148     int i,j;
2149     int found = 1;
2150     NClist* path = nclistnew();
2151 
2152     while(found) {
2153 	found = 0;
2154 	/* Walk backwards to aid removal semantics */
2155 	for(i=nclistlength(dapcomm->cdf.ddsroot->tree->varnodes)-1;i>=0;i--) {
2156 	    CDFnode* var = (CDFnode*)nclistget(dapcomm->cdf.ddsroot->tree->varnodes,i);
2157 	    /* See if this var is under an unusable sequence */
2158 	    nclistclear(path);
2159 	    collectnodepath(var,path,WITHOUTDATASET);
2160 	    for(j=0;j<nclistlength(path);j++) {
2161 		CDFnode* node = (CDFnode*)nclistget(path,j);
2162 		if(node->nctype == NC_Sequence
2163 		   && !node->usesequence) {
2164 #ifdef DEBUG
2165 fprintf(stderr,"suppressing var in unusable sequence: %s.%s\n",node->ncfullname,var->ncbasename);
2166 #endif
2167 		    found = 1;
2168 		    break;
2169 		}
2170 	    }
2171 	    if(found) break;
2172 	}
2173         if(found) nclistremove(dapcomm->cdf.ddsroot->tree->varnodes,i);
2174     }
2175     nclistfree(path);
2176     return NC_NOERR;
2177 }
2178 
2179 
2180 /*
2181 For variables which have a zero size dimension,
2182 make them invisible.
2183 */
2184 static NCerror
fixzerodims(NCDAPCOMMON * dapcomm)2185 fixzerodims(NCDAPCOMMON* dapcomm)
2186 {
2187     int i,j;
2188     for(i=0;i<nclistlength(dapcomm->cdf.ddsroot->tree->varnodes);i++) {
2189 	CDFnode* var = (CDFnode*)nclistget(dapcomm->cdf.ddsroot->tree->varnodes,i);
2190         NClist* ncdims = var->array.dimsetplus;
2191 	if(nclistlength(ncdims) == 0) continue;
2192         for(j=0;j<nclistlength(ncdims);j++) {
2193 	    CDFnode* dim = (CDFnode*)nclistget(ncdims,j);
2194 	    if(dim->dim.declsize == 0) {
2195 	 	/* make node invisible */
2196 		var->invisible = 1;
2197 		var->zerodim = 1;
2198 	    }
2199 	}
2200     }
2201     return NC_NOERR;
2202 }
2203 
2204 static void
applyclientparamcontrols(NCDAPCOMMON * dapcomm)2205 applyclientparamcontrols(NCDAPCOMMON* dapcomm)
2206 {
2207     /* clear the flags */
2208     CLRFLAG(dapcomm->controls,NCF_CACHE);
2209     CLRFLAG(dapcomm->controls,NCF_SHOWFETCH);
2210     CLRFLAG(dapcomm->controls,NCF_NC3);
2211     CLRFLAG(dapcomm->controls,NCF_NCDAP);
2212     CLRFLAG(dapcomm->controls,NCF_PREFETCH);
2213     CLRFLAG(dapcomm->controls,NCF_PREFETCH_EAGER);
2214 
2215     /* Turn on any default on flags */
2216     SETFLAG(dapcomm->controls,DFALT_ON_FLAGS);
2217     SETFLAG(dapcomm->controls,(NCF_NC3|NCF_NCDAP));
2218 
2219     /* enable/disable caching */
2220     if(dapparamcheck(dapcomm,"cache",NULL))
2221 	SETFLAG(dapcomm->controls,NCF_CACHE);
2222     else if(dapparamcheck(dapcomm,"nocache",NULL))
2223 	CLRFLAG(dapcomm->controls,NCF_CACHE);
2224 
2225     /* enable/disable cache prefetch and lazy vs eager*/
2226     if(dapparamcheck(dapcomm,"prefetch","eager")) {
2227         SETFLAG(dapcomm->controls,NCF_PREFETCH);
2228         SETFLAG(dapcomm->controls,NCF_PREFETCH_EAGER);
2229     } else if(dapparamcheck(dapcomm,"prefetch","lazy")
2230               || dapparamcheck(dapcomm,"prefetch",NULL)) {
2231         SETFLAG(dapcomm->controls,NCF_PREFETCH);
2232         CLRFLAG(dapcomm->controls,NCF_PREFETCH_EAGER);
2233     } else if(dapparamcheck(dapcomm,"noprefetch",NULL))
2234         CLRFLAG(dapcomm->controls,NCF_PREFETCH);
2235 
2236     if(FLAGSET(dapcomm->controls,NCF_UNCONSTRAINABLE))
2237 	SETFLAG(dapcomm->controls,NCF_CACHE);
2238 
2239     if(dapparamcheck(dapcomm,"show","fetch"))
2240 	SETFLAG(dapcomm->controls,NCF_SHOWFETCH);
2241 
2242     /* enable/disable _FillValue/Variable Mismatch */
2243     if(dapparamcheck(dapcomm,"fillmismatch",NULL))
2244 	SETFLAG(dapcomm->controls,NCF_FILLMISMATCH);
2245     else if(dapparamcheck(dapcomm,"nofillmismatch",NULL))
2246 	CLRFLAG(dapcomm->controls,NCF_FILLMISMATCH);
2247 
2248     nclog(NCLOGNOTE,"Caching=%d",FLAGSET(dapcomm->controls,NCF_CACHE));
2249 
2250 }
2251 
2252 /*
2253 Force dap2 access to be read-only
2254 */
2255 int
NCD2_set_fill(int ncid,int fillmode,int * old_modep)2256 NCD2_set_fill(int ncid, int fillmode, int* old_modep)
2257 {
2258     return THROW(NC_EPERM);
2259 }
2260 
2261 int
NCD2_def_dim(int ncid,const char * name,size_t len,int * idp)2262 NCD2_def_dim(int ncid, const char* name, size_t len, int* idp)
2263 {
2264     return THROW(NC_EPERM);
2265 }
2266 
2267 int
NCD2_put_att(int ncid,int varid,const char * name,nc_type datatype,size_t len,const void * value,nc_type t)2268 NCD2_put_att(int ncid, int varid, const char* name, nc_type datatype,
2269 	   size_t len, const void* value, nc_type t)
2270 {
2271     return THROW(NC_EPERM);
2272 }
2273 
2274 int
NCD2_def_var(int ncid,const char * name,nc_type xtype,int ndims,const int * dimidsp,int * varidp)2275 NCD2_def_var(int ncid, const char *name,
2276   	     nc_type xtype, int ndims, const int *dimidsp, int *varidp)
2277 {
2278     return THROW(NC_EPERM);
2279 }
2280 
2281 /*
2282 Following functions basically return the netcdf-3 value WRT to the nc3id.
2283 */
2284 
2285 int
NCD2_inq_format(int ncid,int * formatp)2286 NCD2_inq_format(int ncid, int* formatp)
2287 {
2288     NC* drno;
2289     int ret = NC_NOERR;
2290     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2291     ret = nc_inq_format(getnc3id(drno), formatp);
2292     return THROW(ret);
2293 }
2294 
2295 int
NCD2_inq(int ncid,int * ndimsp,int * nvarsp,int * nattsp,int * unlimdimidp)2296 NCD2_inq(int ncid, int* ndimsp, int* nvarsp, int* nattsp, int* unlimdimidp)
2297 {
2298     NC* drno;
2299     int ret;
2300     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2301     ret = nc_inq(getnc3id(drno), ndimsp, nvarsp, nattsp, unlimdimidp);
2302     return THROW(ret);
2303 }
2304 
2305 int
NCD2_inq_type(int ncid,nc_type p2,char * p3,size_t * p4)2306 NCD2_inq_type(int ncid, nc_type p2, char* p3, size_t* p4)
2307 {
2308     NC* drno;
2309     int ret;
2310     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2311     ret = nc_inq_type(getnc3id(drno), p2, p3, p4);
2312     return THROW(ret);
2313 }
2314 
2315 int
NCD2_inq_dimid(int ncid,const char * name,int * idp)2316 NCD2_inq_dimid(int ncid, const char* name, int* idp)
2317 {
2318     NC* drno;
2319     int ret;
2320     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2321     ret = nc_inq_dimid(getnc3id(drno), name, idp);
2322     return THROW(ret);
2323 }
2324 
2325 int
NCD2_inq_dim(int ncid,int dimid,char * name,size_t * lenp)2326 NCD2_inq_dim(int ncid, int dimid, char* name, size_t* lenp)
2327 {
2328     NC* drno;
2329     int ret;
2330     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2331     ret = nc_inq_dim(getnc3id(drno), dimid, name, lenp);
2332     return THROW(ret);
2333 }
2334 
2335 int
NCD2_inq_unlimdim(int ncid,int * unlimdimidp)2336 NCD2_inq_unlimdim(int ncid, int* unlimdimidp)
2337 {
2338     NC* drno;
2339     int ret;
2340     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2341     ret = nc_inq_unlimdim(getnc3id(drno), unlimdimidp);
2342     return THROW(ret);
2343 }
2344 
2345 int
NCD2_rename_dim(int ncid,int dimid,const char * name)2346 NCD2_rename_dim(int ncid, int dimid, const char* name)
2347 {
2348     NC* drno;
2349     int ret;
2350     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2351     ret = nc_rename_dim(getnc3id(drno), dimid, name);
2352     return THROW(ret);
2353 }
2354 
2355 int
NCD2_inq_att(int ncid,int varid,const char * name,nc_type * xtypep,size_t * lenp)2356 NCD2_inq_att(int ncid, int varid, const char* name,
2357 	    nc_type* xtypep, size_t* lenp)
2358 {
2359     NC* drno;
2360     int ret;
2361     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2362     ret = nc_inq_att(getnc3id(drno), varid, name, xtypep, lenp);
2363     return THROW(ret);
2364 }
2365 
2366 int
NCD2_inq_attid(int ncid,int varid,const char * name,int * idp)2367 NCD2_inq_attid(int ncid, int varid, const char *name, int *idp)
2368 {
2369     NC* drno;
2370     int ret;
2371     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2372     ret = nc_inq_attid(getnc3id(drno), varid, name, idp);
2373     return THROW(ret);
2374 }
2375 
2376 int
NCD2_inq_attname(int ncid,int varid,int attnum,char * name)2377 NCD2_inq_attname(int ncid, int varid, int attnum, char* name)
2378 {
2379     NC* drno;
2380     int ret;
2381     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2382     ret = nc_inq_attname(getnc3id(drno), varid, attnum, name);
2383     return THROW(ret);
2384 }
2385 
2386 int
NCD2_rename_att(int ncid,int varid,const char * name,const char * newname)2387 NCD2_rename_att(int ncid, int varid, const char* name, const char* newname)
2388 {
2389     NC* drno;
2390     int ret;
2391     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2392     ret = nc_rename_att(getnc3id(drno), varid, name, newname);
2393     return THROW(ret);
2394 }
2395 
2396 int
NCD2_del_att(int ncid,int varid,const char * p3)2397 NCD2_del_att(int ncid, int varid, const char* p3)
2398 {
2399     NC* drno;
2400     int ret;
2401     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2402     ret = nc_del_att(getnc3id(drno), varid, p3);
2403     return THROW(ret);
2404 }
2405 
2406 int
NCD2_get_att(int ncid,int varid,const char * name,void * value,nc_type t)2407 NCD2_get_att(int ncid, int varid, const char* name, void* value, nc_type t)
2408 {
2409     NC* drno;
2410     int ret;
2411     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2412     ret = NCDISPATCH_get_att(getnc3id(drno), varid, name, value, t);
2413     return THROW(ret);
2414 }
2415 
2416 int
NCD2_inq_var_all(int ncid,int varid,char * name,nc_type * xtypep,int * ndimsp,int * dimidsp,int * nattsp,int * shufflep,int * deflatep,int * deflate_levelp,int * fletcher32p,int * contiguousp,size_t * chunksizesp,int * no_fill,void * fill_valuep,int * endiannessp,unsigned int * idp,size_t * nparamsp,unsigned int * params)2417 NCD2_inq_var_all(int ncid, int varid, char *name, nc_type* xtypep,
2418                int* ndimsp, int* dimidsp, int* nattsp,
2419                int* shufflep, int* deflatep, int* deflate_levelp,
2420                int* fletcher32p, int* contiguousp, size_t* chunksizesp,
2421                int* no_fill, void* fill_valuep, int* endiannessp,
2422 	       unsigned int* idp, size_t* nparamsp, unsigned int* params
2423                )
2424 {
2425     NC* drno;
2426     int ret;
2427     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2428     ret = NCDISPATCH_inq_var_all(getnc3id(drno), varid, name, xtypep,
2429                ndimsp, dimidsp, nattsp,
2430                shufflep, deflatep, deflate_levelp,
2431                fletcher32p, contiguousp, chunksizesp,
2432                no_fill, fill_valuep, endiannessp,
2433 	       idp,nparamsp,params
2434 	       );
2435     return THROW(ret);
2436 }
2437 
2438 int
NCD2_inq_varid(int ncid,const char * name,int * varidp)2439 NCD2_inq_varid(int ncid, const char *name, int *varidp)
2440 {
2441     NC* drno;
2442     int ret;
2443     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2444     ret = nc_inq_varid(getnc3id(drno),name,varidp);
2445     return THROW(ret);
2446 }
2447 
2448 int
NCD2_rename_var(int ncid,int varid,const char * name)2449 NCD2_rename_var(int ncid, int varid, const char* name)
2450 {
2451     NC* drno;
2452     int ret;
2453     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2454     ret = nc_rename_var(getnc3id(drno), varid, name);
2455     return THROW(ret);
2456 }
2457 
2458 int
NCD2_var_par_access(int ncid,int p2,int p3)2459 NCD2_var_par_access(int ncid, int p2, int p3)
2460 {
2461     return THROW(NC_ENOPAR);
2462 }
2463 
2464 int
NCD2_def_var_fill(int ncid,int p2,int p3,const void * p4)2465 NCD2_def_var_fill(int ncid, int p2, int p3, const void* p4)
2466 {
2467     NC* drno;
2468     int ret;
2469     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2470     ret = nc_def_var_fill(getnc3id(drno), p2, p3, p4);
2471     return THROW(ret);
2472 }
2473 
2474 int
NCD2_inq_ncid(int ncid,const char * name,int * grp_ncid)2475 NCD2_inq_ncid(int ncid, const char* name, int* grp_ncid)
2476 {
2477     NC* drno;
2478     int ret;
2479     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2480     ret = nc_inq_ncid(getnc3id(drno), name, grp_ncid);
2481     return THROW(ret);
2482 }
2483 
2484 int
NCD2_show_metadata(int ncid)2485 NCD2_show_metadata(int ncid)
2486 {
2487     NC* drno;
2488     int ret;
2489     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2490     ret = nc_show_metadata(getnc3id(drno));
2491     return THROW(ret);
2492 }
2493 
2494 int
NCD2_inq_grps(int ncid,int * p2,int * p3)2495 NCD2_inq_grps(int ncid, int* p2, int* p3)
2496 {
2497     NC* drno;
2498     int ret;
2499     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2500     ret = nc_inq_grps(getnc3id(drno), p2, p3);
2501     return THROW(ret);
2502 }
2503 
2504 int
NCD2_inq_grpname(int ncid,char * p)2505 NCD2_inq_grpname(int ncid, char* p)
2506 {
2507     NC* drno;
2508     int ret;
2509     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2510     ret = nc_inq_grpname(getnc3id(drno), p);
2511     return THROW(ret);
2512 }
2513 
2514 
2515 int
NCD2_inq_unlimdims(int ncid,int * p2,int * p3)2516 NCD2_inq_unlimdims(int ncid, int* p2, int* p3)
2517 {
2518     NC* drno;
2519     int ret;
2520     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2521     ret = nc_inq_unlimdims(getnc3id(drno), p2, p3);
2522     return THROW(ret);
2523 }
2524 
2525 int
NCD2_inq_grpname_full(int ncid,size_t * p2,char * p3)2526 NCD2_inq_grpname_full(int ncid, size_t* p2, char* p3)
2527 {
2528     NC* drno;
2529     int ret;
2530     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2531     ret = nc_inq_grpname_full(getnc3id(drno), p2, p3);
2532     return THROW(ret);
2533 }
2534 
2535 int
NCD2_inq_grp_parent(int ncid,int * p)2536 NCD2_inq_grp_parent(int ncid, int* p)
2537 {
2538     NC* drno;
2539     int ret;
2540     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2541     ret = nc_inq_grp_parent(getnc3id(drno), p);
2542     return THROW(ret);
2543 }
2544 
2545 int
NCD2_inq_grp_full_ncid(int ncid,const char * p2,int * p3)2546 NCD2_inq_grp_full_ncid(int ncid, const char* p2, int* p3)
2547 {
2548     NC* drno;
2549     int ret;
2550     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2551     ret = nc_inq_grp_full_ncid(getnc3id(drno), p2, p3);
2552     return THROW(ret);
2553 }
2554 
2555 int
NCD2_inq_varids(int ncid,int * nvars,int * p)2556 NCD2_inq_varids(int ncid, int* nvars, int* p)
2557 {
2558     NC* drno;
2559     int ret;
2560     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2561     ret = nc_inq_varids(getnc3id(drno), nvars, p);
2562     return THROW(ret);
2563 }
2564 
2565 int
NCD2_inq_dimids(int ncid,int * ndims,int * p3,int p4)2566 NCD2_inq_dimids(int ncid, int* ndims, int* p3, int p4)
2567 {
2568     NC* drno;
2569     int ret;
2570     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2571     ret = nc_inq_dimids(getnc3id(drno), ndims, p3, p4);
2572     return THROW(ret);
2573 }
2574 
2575 int
NCD2_inq_typeids(int ncid,int * ntypes,int * p)2576 NCD2_inq_typeids(int ncid, int*  ntypes, int* p)
2577 {
2578     NC* drno;
2579     int ret;
2580     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2581     ret = nc_inq_typeids(getnc3id(drno), ntypes, p);
2582     return THROW(ret);
2583 }
2584 
2585 int
NCD2_inq_type_equal(int ncid,nc_type t1,int p3,nc_type t2,int * p5)2586 NCD2_inq_type_equal(int ncid, nc_type t1, int p3, nc_type t2, int* p5)
2587 {
2588     NC* drno;
2589     int ret;
2590     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2591     ret = nc_inq_type_equal(getnc3id(drno), t1, p3, t2, p5);
2592     return THROW(ret);
2593 }
2594 
2595 int
NCD2_inq_user_type(int ncid,nc_type t,char * p3,size_t * p4,nc_type * p5,size_t * p6,int * p7)2596 NCD2_inq_user_type(int ncid, nc_type t, char* p3, size_t* p4, nc_type* p5,
2597                    size_t* p6, int* p7)
2598 {
2599     NC* drno;
2600     int ret;
2601     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2602     ret = nc_inq_user_type(getnc3id(drno), t, p3, p4, p5, p6, p7);
2603     return THROW(ret);
2604 }
2605 
2606 int
NCD2_inq_typeid(int ncid,const char * name,nc_type * t)2607 NCD2_inq_typeid(int ncid, const char* name, nc_type* t)
2608 {
2609     NC* drno;
2610     int ret;
2611     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2612     ret = nc_inq_typeid(getnc3id(drno), name, t);
2613     return THROW(ret);
2614 }
2615 
2616 int
NCD2_def_grp(int ncid,const char * p2,int * p3)2617 NCD2_def_grp(int ncid, const char* p2, int* p3)
2618 {
2619     NC* drno;
2620     int ret;
2621     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2622     ret = nc_def_grp(getnc3id(drno), p2, p3);
2623     return THROW(ret);
2624 }
2625 
2626 int
NCD2_rename_grp(int ncid,const char * p)2627 NCD2_rename_grp(int ncid, const char* p)
2628 {
2629     NC* drno;
2630     int ret;
2631     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2632     ret = nc_rename_grp(getnc3id(drno), p);
2633     return THROW(ret);
2634 }
2635 
2636 int
NCD2_def_compound(int ncid,size_t p2,const char * p3,nc_type * t)2637 NCD2_def_compound(int ncid, size_t p2, const char* p3, nc_type* t)
2638 {
2639     NC* drno;
2640     int ret;
2641     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2642     ret = nc_def_compound(getnc3id(drno), p2, p3, t);
2643     return THROW(ret);
2644 }
2645 
2646 int
NCD2_insert_compound(int ncid,nc_type t1,const char * p3,size_t p4,nc_type t2)2647 NCD2_insert_compound(int ncid, nc_type t1, const char* p3, size_t p4, nc_type t2)
2648 {
2649     NC* drno;
2650     int ret;
2651     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2652     ret = nc_insert_compound(getnc3id(drno), t1, p3, p4, t2);
2653     return THROW(ret);
2654 }
2655 
2656 int
NCD2_insert_array_compound(int ncid,nc_type t1,const char * p3,size_t p4,nc_type t2,int p6,const int * p7)2657 NCD2_insert_array_compound(int ncid, nc_type t1, const char* p3, size_t p4,
2658 			  nc_type t2, int p6, const int* p7)
2659 {
2660     NC* drno;
2661     int ret;
2662     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2663     ret = nc_insert_array_compound(getnc3id(drno), t1, p3, p4,  t2, p6, p7);
2664     return THROW(ret);
2665 }
2666 
2667 int
NCD2_inq_compound_field(int ncid,nc_type xtype,int fieldid,char * name,size_t * offsetp,nc_type * field_typeidp,int * ndimsp,int * dim_sizesp)2668 NCD2_inq_compound_field(int ncid, nc_type xtype, int fieldid, char *name,
2669 		      size_t *offsetp, nc_type* field_typeidp, int *ndimsp,
2670 		      int *dim_sizesp)
2671 {
2672     NC* drno;
2673     int ret;
2674     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2675     ret = nc_inq_compound_field(getnc3id(drno), xtype, fieldid, name, offsetp, field_typeidp, ndimsp, dim_sizesp);
2676     return THROW(ret);
2677 }
2678 
2679 int
NCD2_inq_compound_fieldindex(int ncid,nc_type xtype,const char * name,int * fieldidp)2680 NCD2_inq_compound_fieldindex(int ncid, nc_type xtype, const char *name,
2681 			   int *fieldidp)
2682 {
2683     NC* drno;
2684     int ret;
2685     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2686     ret = nc_inq_compound_fieldindex(getnc3id(drno), xtype, name, fieldidp);
2687     return THROW(ret);
2688 }
2689 
2690 int
NCD2_def_vlen(int ncid,const char * p2,nc_type base_typeid,nc_type * t)2691 NCD2_def_vlen(int ncid, const char* p2, nc_type base_typeid, nc_type* t)
2692 {
2693     NC* drno;
2694     int ret;
2695     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2696     ret = nc_def_vlen(getnc3id(drno), p2, base_typeid, t);
2697     return THROW(ret);
2698 }
2699 
2700 int
NCD2_put_vlen_element(int ncid,int p2,void * p3,size_t p4,const void * p5)2701 NCD2_put_vlen_element(int ncid, int p2, void* p3, size_t p4, const void* p5)
2702 {
2703     NC* drno;
2704     int ret;
2705     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2706     ret = nc_put_vlen_element(getnc3id(drno), p2, p3, p4, p5);
2707     return THROW(ret);
2708 }
2709 
2710 int
NCD2_get_vlen_element(int ncid,int p2,const void * p3,size_t * p4,void * p5)2711 NCD2_get_vlen_element(int ncid, int p2, const void* p3, size_t* p4, void* p5)
2712 {
2713     NC* drno;
2714     int ret;
2715     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2716     ret = nc_get_vlen_element(getnc3id(drno), p2, p3, p4, p5);
2717     return THROW(ret);
2718 }
2719 
2720 int
NCD2_def_enum(int ncid,nc_type t1,const char * p3,nc_type * t)2721 NCD2_def_enum(int ncid, nc_type t1, const char* p3, nc_type* t)
2722 {
2723     NC* drno;
2724     int ret;
2725     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2726     ret = nc_def_enum(getnc3id(drno), t1, p3, t);
2727     return THROW(ret);
2728 }
2729 
2730 int
NCD2_insert_enum(int ncid,nc_type t1,const char * p3,const void * p4)2731 NCD2_insert_enum(int ncid, nc_type t1, const char* p3, const void* p4)
2732 {
2733     NC* drno;
2734     int ret;
2735     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2736     ret = nc_insert_enum(getnc3id(drno), t1, p3, p4);
2737     return THROW(ret);
2738 }
2739 
2740 int
NCD2_inq_enum_member(int ncid,nc_type t1,int p3,char * p4,void * p5)2741 NCD2_inq_enum_member(int ncid, nc_type t1, int p3, char* p4, void* p5)
2742 {
2743     NC* drno;
2744     int ret;
2745     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2746     ret = nc_inq_enum_member(getnc3id(drno), t1, p3, p4, p5);
2747     return THROW(ret);
2748 }
2749 
2750 int
NCD2_inq_enum_ident(int ncid,nc_type t1,long long p3,char * p4)2751 NCD2_inq_enum_ident(int ncid, nc_type t1, long long p3, char* p4)
2752 {
2753     NC* drno;
2754     int ret;
2755     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2756     ret = nc_inq_enum_ident(getnc3id(drno), t1, p3, p4);
2757     return THROW(ret);
2758 }
2759 
2760 int
NCD2_def_opaque(int ncid,size_t p2,const char * p3,nc_type * t)2761 NCD2_def_opaque(int ncid, size_t p2, const char* p3, nc_type* t)
2762 {
2763     NC* drno;
2764     int ret;
2765     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2766     ret = nc_def_opaque(getnc3id(drno), p2, p3, t);
2767     return THROW(ret);
2768 }
2769 
2770 int
NCD2_def_var_deflate(int ncid,int p2,int p3,int p4,int p5)2771 NCD2_def_var_deflate(int ncid, int p2, int p3, int p4, int p5)
2772 {
2773     NC* drno;
2774     int ret;
2775     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2776     ret = nc_def_var_deflate(getnc3id(drno), p2, p3, p4, p5);
2777     return THROW(ret);
2778 }
2779 
2780 int
NCD2_def_var_fletcher32(int ncid,int p2,int p3)2781 NCD2_def_var_fletcher32(int ncid, int p2, int p3)
2782 {
2783     NC* drno;
2784     int ret;
2785     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2786     ret = nc_def_var_fletcher32(getnc3id(drno), p2, p3);
2787     return THROW(ret);
2788 }
2789 
2790 int
NCD2_def_var_chunking(int ncid,int p2,int p3,const size_t * p4)2791 NCD2_def_var_chunking(int ncid, int p2, int p3, const size_t* p4)
2792 {
2793     NC* drno;
2794     int ret;
2795     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2796     ret = nc_def_var_chunking(getnc3id(drno), p2, p3, p4);
2797     return THROW(ret);
2798 }
2799 
2800 int
NCD2_def_var_endian(int ncid,int p2,int p3)2801 NCD2_def_var_endian(int ncid, int p2, int p3)
2802 {
2803     NC* drno;
2804     int ret;
2805     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2806     ret = nc_def_var_endian(getnc3id(drno), p2, p3);
2807     return THROW(ret);
2808 }
2809 
2810 int
NCD2_def_var_filter(int ncid,int varid,unsigned int id,size_t n,const unsigned int * params)2811 NCD2_def_var_filter(int ncid, int varid, unsigned int id, size_t n, const unsigned int* params)
2812 {
2813     NC* drno;
2814     int ret;
2815     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2816     ret = nc_def_var_filter(getnc3id(drno), varid, id, n, params);
2817     return THROW(ret);
2818 }
2819 
2820 int
NCD2_set_var_chunk_cache(int ncid,int p2,size_t p3,size_t p4,float p5)2821 NCD2_set_var_chunk_cache(int ncid, int p2, size_t p3, size_t p4, float p5)
2822 {
2823     NC* drno;
2824     int ret;
2825     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2826     ret = nc_set_var_chunk_cache(getnc3id(drno), p2, p3, p4, p5);
2827     return THROW(ret);
2828 }
2829 
2830 int
NCD2_get_var_chunk_cache(int ncid,int p2,size_t * p3,size_t * p4,float * p5)2831 NCD2_get_var_chunk_cache(int ncid, int p2, size_t* p3, size_t* p4, float* p5)
2832 {
2833     NC* drno;
2834     int ret;
2835     if((ret = NC_check_id(ncid, (NC**)&drno)) != NC_NOERR) return THROW(ret);
2836     ret = nc_get_var_chunk_cache(getnc3id(drno), p2, p3, p4, p5);
2837     return THROW(ret);
2838 }
2839 
2840