1 /*********************************************************************
2  *   Copyright 2018, UCAR/Unidata
3  *   See netcdf/COPYRIGHT file for copying and redistribution conditions.
4  *********************************************************************/
5 
6 #include "dapincludes.h"
7 #include "daputil.h"
8 #include "dapdump.h"
9 
10 #ifdef DAPDEBUG
11 extern char* ocfqn(OCddsnode);
12 #endif
13 
14 /* Forward*/
15 static NCerror sequencecheckr(CDFnode* node, NClist* vars, CDFnode* topseq);
16 static NCerror restructr(NCDAPCOMMON*, CDFnode*, CDFnode*, NClist*);
17 static NCerror repairgrids(NCDAPCOMMON*, NClist*);
18 static NCerror structwrap(NCDAPCOMMON*, CDFnode*, CDFnode*, int, CDFnode*, int);
19 static int findin(CDFnode* parent, CDFnode* child);
20 static CDFnode* makenewstruct(NCDAPCOMMON*, CDFnode*, CDFnode*);
21 static NCerror mapnodesr(CDFnode*, CDFnode*, int depth);
22 static NCerror mapfcn(CDFnode* dstnode, CDFnode* srcnode);
23 static NCerror definedimsetplus(NCDAPCOMMON* nccomm, CDFnode* node);
24 static NCerror definedimsetall(NCDAPCOMMON* nccomm, CDFnode* node);
25 static NCerror definetransdimset(NCDAPCOMMON* nccomm, CDFnode* node);
26 static NCerror definedimsettransR(NCDAPCOMMON* nccomm, CDFnode* node);
27 static NCerror definedimsetsR(NCDAPCOMMON* nccomm, CDFnode* node);
28 static NCerror buildcdftreer(NCDAPCOMMON*, OCddsnode, CDFnode*, CDFtree*, CDFnode**);
29 static void free1cdfnode(CDFnode* node);
30 static NCerror fixnodes(NCDAPCOMMON*, NClist* cdfnodes);
31 static void defdimensions(OCddsnode ocnode, CDFnode* cdfnode, NCDAPCOMMON* nccomm, CDFtree* tree);
32 
33 /* Accumulate useful node sets  */
34 NCerror
computecdfnodesets(NCDAPCOMMON * nccomm,CDFtree * tree)35 computecdfnodesets(NCDAPCOMMON* nccomm, CDFtree* tree)
36 {
37     unsigned int i;
38     NClist* varnodes;
39     NClist* allnodes;
40 
41     allnodes = tree->nodes;
42     varnodes = nclistnew();
43 
44     if(tree->seqnodes == NULL) tree->seqnodes = nclistnew();
45     if(tree->gridnodes == NULL) tree->gridnodes = nclistnew();
46     nclistclear(tree->seqnodes);
47     nclistclear(tree->gridnodes);
48 
49     computevarnodes(nccomm,allnodes,varnodes);
50     nclistfree(tree->varnodes);
51     tree->varnodes = varnodes;
52     varnodes = NULL;
53 
54     /* Now compute other sets of interest */
55     for(i=0;i<nclistlength(allnodes);i++) {
56 	CDFnode* node = (CDFnode*)nclistget(allnodes,i);
57 	switch (node->nctype) {
58 	case NC_Sequence:
59 	    nclistpush(tree->seqnodes,(void*)node);
60 	    break;
61 	case NC_Grid:
62 	    nclistpush(tree->gridnodes,(void*)node);
63 	    break;
64 	default: break;
65 	}
66     }
67     return NC_NOERR;
68 }
69 
70 NCerror
computevarnodes(NCDAPCOMMON * nccomm,NClist * allnodes,NClist * varnodes)71 computevarnodes(NCDAPCOMMON* nccomm, NClist* allnodes, NClist* varnodes)
72 {
73     unsigned int i,len;
74     NClist* allvarnodes = nclistnew();
75     for(i=0;i<nclistlength(allnodes);i++) {
76 	CDFnode* node = (CDFnode*)nclistget(allnodes,i);
77 #if 0
78 	/* If this node has a bad name, repair it */
79 	if(dap_badname(node->ocname)) {
80 	    char* newname = dap_repairname(node->ocname);
81 	    nullfree(node->ocname);
82 	    node->ocname = newname;
83 	}
84 #endif
85 	if(node->nctype == NC_Atomic)
86 	    nclistpush(allvarnodes,(void*)node);
87     }
88     /* Further process the variable nodes to get the final set */
89     /* Use toplevel vars first */
90     len = nclistlength(allvarnodes);
91     for(i=0;i<len;i++) {
92 	CDFnode* node = (CDFnode*)nclistget(allvarnodes,i);
93 	if(node == NULL) continue;
94         if(daptoplevel(node)) {
95 	    nclistpush(varnodes,(void*)node);
96 	    nclistset(allvarnodes,i,(void*)NULL);
97 	}
98     }
99     /*... then grid arrays and maps.
100       but exclude the coordinate variables if we are trying to
101       exactly mimic nc-dap
102     */
103     for(i=0;i<len;i++) {
104 	CDFnode* node = (CDFnode*)nclistget(allvarnodes,i);
105 	if(node == NULL) continue;
106 	if(dapgridarray(node)) {
107 	    nclistpush(varnodes,(void*)node);
108 	    nclistset(allvarnodes,i,(void*)NULL);
109         } else if(dapgridmap(node)) {
110 	    if(!FLAGSET(nccomm->controls,NCF_NCDAP))
111 		nclistpush(varnodes,(void*)node);
112 	    nclistset(allvarnodes,i,(void*)NULL);
113 	}
114     }
115     /*... then all others */
116     for(i=0;i<len;i++) {
117 	CDFnode* node = (CDFnode*)nclistget(allvarnodes,i);
118 	if(node == NULL) continue;
119         nclistpush(varnodes,(void*)node);
120     }
121     nclistfree(allvarnodes);
122 #ifdef DEBUG2
123 for(i=0;i<nclistlength(varnodes);i++) {
124 CDFnode* node = (CDFnode*)nclistget(varnodes,i);
125 if(node == NULL) continue;
126 fprintf(stderr,"computevarnodes: var: %s\n",makecdfpathstring(node,"."));
127 }
128 #endif
129     return NC_NOERR;
130 }
131 
132 
133 
134 NCerror
fixgrid(NCDAPCOMMON * nccomm,CDFnode * grid)135 fixgrid(NCDAPCOMMON* nccomm, CDFnode* grid)
136 {
137     unsigned int i,glen;
138     CDFnode* array;
139 
140     glen = nclistlength(grid->subnodes);
141     array = (CDFnode*)nclistget(grid->subnodes,0);
142     if(nccomm->controls.flags & (NCF_NC3)) {
143         /* Rename grid Array: variable, but leave its oc base name alone */
144         nullfree(array->ncbasename);
145         array->ncbasename = nulldup(grid->ncbasename);
146         if(!array->ncbasename) return NC_ENOMEM;
147     }
148     /* validate and modify the grid structure */
149     if((glen-1) != nclistlength(array->array.dimset0)) goto invalid;
150     for(i=1;i<glen;i++) {
151 	CDFnode* arraydim = (CDFnode*)nclistget(array->array.dimset0,i-1);
152 	CDFnode* map = (CDFnode*)nclistget(grid->subnodes,i);
153 	CDFnode* mapdim;
154 	/* map must have 1 dimension */
155 	if(nclistlength(map->array.dimset0) != 1) goto invalid;
156 	/* and the map name must match the ith array dimension */
157 	if(arraydim->ocname != NULL && map->ocname != NULL
158 	   && strcmp(arraydim->ocname,map->ocname) != 0)
159 	    goto invalid;
160 	/* and the map name must match its dim name (if any) */
161 	mapdim = (CDFnode*)nclistget(map->array.dimset0,0);
162 	if(mapdim->ocname != NULL && map->ocname != NULL
163 	   && strcmp(mapdim->ocname,map->ocname) != 0)
164 	    goto invalid;
165 	/* Add appropriate names for the anonymous dimensions */
166 	/* Do the map name first, so the array dim may inherit */
167 	if(mapdim->ocname == NULL) {
168 	    nullfree(mapdim->ncbasename);
169 	    mapdim->ocname = nulldup(map->ocname);
170 	    if(!mapdim->ocname) return NC_ENOMEM;
171 	    mapdim->ncbasename = cdflegalname(mapdim->ocname);
172 	    if(!mapdim->ncbasename) return NC_ENOMEM;
173 	}
174 	if(arraydim->ocname == NULL) {
175 	    nullfree(arraydim->ncbasename);
176 	    arraydim->ocname = nulldup(map->ocname);
177 	    if(!arraydim->ocname) return NC_ENOMEM;
178 	    arraydim->ncbasename = cdflegalname(arraydim->ocname);
179 	    if(!arraydim->ncbasename) return NC_ENOMEM;
180 	}
181         if(FLAGSET(nccomm->controls,(NCF_NCDAP|NCF_NC3))) {
182 	    char tmp[3*NC_MAX_NAME];
183             /* Add the grid name to the basename of the map */
184 	    snprintf(tmp,sizeof(tmp),"%s%s%s",map->container->ncbasename,
185 					  nccomm->cdf.separator,
186 					  map->ncbasename);
187 	    nullfree(map->ncbasename);
188             map->ncbasename = nulldup(tmp);
189 	    if(!map->ncbasename) return NC_ENOMEM;
190 	}
191     }
192     return NC_NOERR;
193 invalid:
194     return NC_EINVAL; /* mal-formed grid */
195 }
196 
197 NCerror
fixgrids(NCDAPCOMMON * nccomm)198 fixgrids(NCDAPCOMMON* nccomm)
199 {
200     unsigned int i;
201     NClist* gridnodes = nccomm->cdf.ddsroot->tree->gridnodes;
202 
203     for(i=0;i<nclistlength(gridnodes);i++) {
204         CDFnode* grid = (CDFnode*)nclistget(gridnodes,i);
205         (void)fixgrid(nccomm,grid);
206 	/* Ignore mal-formed grids */
207     }
208     return NC_NOERR;
209 }
210 
211 /*
212 Figure out the names for variables.
213 */
214 NCerror
computecdfvarnames(NCDAPCOMMON * nccomm,CDFnode * root,NClist * varnodes)215 computecdfvarnames(NCDAPCOMMON* nccomm, CDFnode* root, NClist* varnodes)
216 {
217     unsigned int i,j,d;
218 
219     /* clear all elided marks; except for dataset and grids */
220     for(i=0;i<nclistlength(root->tree->nodes);i++) {
221 	CDFnode* node = (CDFnode*)nclistget(root->tree->nodes,i);
222 	node->elided = 0;
223 	if(node->nctype == NC_Grid || node->nctype == NC_Dataset)
224 	    node->elided = 1;
225     }
226 
227     /* ensure all variables have an initial full name defined */
228     for(i=0;i<nclistlength(varnodes);i++) {
229 	CDFnode* var = (CDFnode*)nclistget(varnodes,i);
230 	nullfree(var->ncfullname);
231 	var->ncfullname = makecdfpathstring(var,nccomm->cdf.separator);
232 #ifdef DEBUG2
233 fprintf(stderr,"var names: %s %s %s\n",
234 	var->ocname,var->ncbasename,var->ncfullname);
235 #endif
236     }
237 
238     /*  unify all variables with same fullname and dimensions
239 	basevar fields says: "for duplicate grid variables";
240         when does this happen?
241     */
242     if(FLAGSET(nccomm->controls,NCF_NC3)) {
243         for(i=0;i<nclistlength(varnodes);i++) {
244 	    int match;
245 	    CDFnode* var = (CDFnode*)nclistget(varnodes,i);
246 	    for(j=0;j<i;j++) {
247 	        CDFnode* testnode = (CDFnode*)nclistget(varnodes,j);
248 		match = 1;
249 	        if(testnode->array.basevar != NULL)
250 		    continue; /* already processed */
251 	        if(strcmp(var->ncfullname,testnode->ncfullname) != 0)
252 		    match = 0;
253 		else if(nclistlength(testnode->array.dimsetall)
254 			!= nclistlength(var->array.dimsetall))
255 		    match = 0;
256 	        else for(d=0;d<nclistlength(testnode->array.dimsetall);d++) {
257 		    CDFnode* vdim = (CDFnode*)nclistget(var->array.dimsetall,d);
258 		    CDFnode* tdim = (CDFnode*)nclistget(testnode->array.dimsetall,d);
259 	            if(vdim->dim.declsize != tdim->dim.declsize) {
260 		        match = 0;
261 			break;
262 		    }
263 		}
264 		if(match) {
265 		    testnode->array.basevar = var;
266 fprintf(stderr,"basevar invoked: %s\n",var->ncfullname);
267 		}
268 	    }
269 	}
270     }
271 
272     /* Finally, verify unique names */
273     for(i=0;i<nclistlength(varnodes);i++) {
274 	CDFnode* var1 = (CDFnode*)nclistget(varnodes,i);
275 	if(var1->array.basevar != NULL) continue;
276 	for(j=0;j<i;j++) {
277 	    CDFnode* var2 = (CDFnode*)nclistget(varnodes,j);
278 	    if(var2->array.basevar != NULL) continue;
279 	    if(strcmp(var1->ncfullname,var2->ncfullname)==0) {
280 		PANIC1("duplicate var names: %s",var1->ncfullname);
281 	    }
282 	}
283     }
284     return NC_NOERR;
285 }
286 
287 
288 /* locate and connect usable sequences and vars.
289 A sequence is usable iff:
290 1. it has a path from one of its subnodes to a leaf and that
291    path does not contain a sequence.
292 2. No parent container has dimensions.
293 */
294 
295 NCerror
sequencecheck(NCDAPCOMMON * nccomm)296 sequencecheck(NCDAPCOMMON* nccomm)
297 {
298     (void)sequencecheckr(nccomm->cdf.ddsroot,
299                           nccomm->cdf.ddsroot->tree->varnodes,NULL);
300     return NC_NOERR;
301 }
302 
303 
304 static NCerror
sequencecheckr(CDFnode * node,NClist * vars,CDFnode * topseq)305 sequencecheckr(CDFnode* node, NClist* vars, CDFnode* topseq)
306 {
307     unsigned int i;
308     NCerror err = NC_NOERR;
309     int ok = 0;
310     if(topseq == NULL && nclistlength(node->array.dimset0) > 0) {
311 	err = NC_EINVAL; /* This container has dimensions, so no sequence within it
312                             can be usable */
313     } else if(node->nctype == NC_Sequence) {
314 	/* Recursively walk the path for each subnode of this sequence node
315            looking for a path without any sequence */
316 	for(i=0;i<nclistlength(node->subnodes);i++) {
317 	    CDFnode* sub = (CDFnode*)nclistget(node->subnodes,i);
318 	    err = sequencecheckr(sub,vars,node);
319 	    if(err == NC_NOERR) ok = 1; /* there is at least 1 usable var below */
320 	}
321 	if(topseq == NULL && ok == 1) {
322 	    /* this sequence is usable because it has scalar container
323                (by construction) and has a path to a leaf without an intermediate
324                sequence. */
325 	    err = NC_NOERR;
326 	    node->usesequence = 1;
327 	} else {
328 	    /* this sequence is unusable because it has no path
329                to a leaf without an intermediate sequence. */
330 	    node->usesequence = 0;
331 	    err = NC_EINVAL;
332 	}
333     } else if(nclistcontains(vars,(void*)node)) {
334 	/* If we reach a leaf, then topseq is usable, so save it */
335 	node->array.sequence = topseq;
336     } else { /* Some kind of non-sequence container node with no dimensions */
337 	/* recursively compute usability */
338 	for(i=0;i<nclistlength(node->subnodes);i++) {
339 	    CDFnode* sub = (CDFnode*)nclistget(node->subnodes,i);
340 	    err = sequencecheckr(sub,vars,topseq);
341 	    if(err == NC_NOERR) ok = 1;
342 	}
343 	err = (ok?NC_NOERR:NC_EINVAL);
344     }
345     return err;
346 }
347 
348 /*
349 Originally, if one did a constraint on a Grid such that only
350 one array or map in the grid was returned, that element was
351 returned as a top level variable.  This is incorrect because
352 it loses the Grid scope information.
353 
354 Eventually, this behavior was changed so that such partial
355 grids are converted to structures where the structure name
356 is the grid name. This preserves the proper scoping.
357 However, it is still the case that some servers do the old
358 behavior.
359 
360 The rules that most old-style servers appear to adhere to are these.
361 1. Asking for just a grid array or a single grid map
362    returns just the array not wrapped in a structure.
363 2. Asking for a subset of the fields (array plus map) of a grid
364    returns those fields wrapped in a structure.
365 3. However, there is an odd situation: asking for a grid array
366    plus any subset of maps that includes the last map in the grid
367    returns a malformed grid. This is clearly a bug.
368 
369 For case 1, we insert a structure node so that case 1 is consistent
370 with case 2. Case 3 should cause an error with a malformed grid.
371 
372 [Note: for some reason, this code has been difficult to get right;
373 I have rewritten 6 times and it probably is still not right.]
374 [2/25/2013 Sigh! Previous fixes have introducted another bug,
375 so now we fix the fix.]
376 
377 Input is
378 (1) the root of the dds that needs to be re-gridded
379 (2) the full datadds tree that defines where the grids are.
380 (3) the projections that were used to produce (1) from (2).
381 
382 */
383 
384 NCerror
restruct(NCDAPCOMMON * ncc,CDFnode * ddsroot,CDFnode * patternroot,NClist * projections)385 restruct(NCDAPCOMMON* ncc, CDFnode* ddsroot, CDFnode* patternroot, NClist* projections)
386 {
387     NCerror ncstat = NC_NOERR;
388     NClist* repairs = nclistnew();
389 
390     /* The current restruct assumes that the ddsroot tree
391        has missing grids compared to the pattern.
392        It is also assumed that order of the nodes
393        in the ddsroot is the same as in the pattern.
394     */
395     if(ddsroot->tree->restructed) {
396       nclistfree(repairs);
397       return NC_NOERR;
398     }
399 
400 #ifdef DEBUG
401 fprintf(stderr,"restruct: ddsroot=%s\n",dumptree(ddsroot));
402 fprintf(stderr,"restruct: patternroot=%s\n",dumptree(patternroot));
403 #endif
404 
405     /* Match roots */
406     if(!simplenodematch(ddsroot,patternroot))
407 	ncstat = NC_EDATADDS;
408     else if(!restructr(ncc,ddsroot,patternroot,repairs))
409 	ncstat = NC_EDATADDS;
410     else if(nclistlength(repairs) > 0) {
411 	/* Do the repairs */
412 	ncstat = repairgrids(ncc, repairs);
413     }
414 
415     if(repairs)
416       nclistfree(repairs);
417 
418     return THROW(ncstat);
419 }
420 
421 /*
422 Locate nodes in the tree rooted at node
423 that correspond to a single grid field in the pattern
424 when the pattern is a grid.
425 Wrap that grid field in a synthesized structure.
426 
427 The key thing to look for is the case where
428 we have an atomic variable that appear where
429 we expected a grid.
430 
431 */
432 
433 static int
restructr(NCDAPCOMMON * ncc,CDFnode * dxdparent,CDFnode * patternparent,NClist * repairlist)434 restructr(NCDAPCOMMON* ncc, CDFnode* dxdparent, CDFnode* patternparent, NClist* repairlist)
435 {
436     int index, i, j, match;
437 
438 #ifdef DEBUG
439 fprintf(stderr,"restruct: matched: %s -> %s\n",
440 ocfqn(dxdparent->ocnode),ocfqn(patternparent->ocnode));
441 #endif
442 
443     /* walk each node child and locate its match
444        in the pattern's children; recurse on matches,
445        non-matches may be nodes needing wrapping.
446     */
447 
448     for(index=0;index<nclistlength(dxdparent->subnodes);index++) {
449         CDFnode* dxdsubnode = (CDFnode*)nclistget(dxdparent->subnodes,index);
450 	CDFnode* matchnode = NULL;
451 
452 	/* Look for a matching pattern node with same ocname */
453         for(i=0;i<nclistlength(patternparent->subnodes);i++) {
454             CDFnode* patternsubnode = (CDFnode*)nclistget(patternparent->subnodes,i);
455 	    if(strcmp(dxdsubnode->ocname,patternsubnode->ocname) == 0) {
456 		matchnode = patternsubnode;
457 		break;
458 	    }
459 	}
460 #ifdef DEBUG
461 fprintf(stderr,"restruct: candidate: %s -> %s\n",
462 ocfqn(dxdsubnode->ocnode),ocfqn(matchnode->ocnode));
463 #endif
464 	if(simplenodematch(dxdsubnode,matchnode)) {
465 	    /* this subnode of the node matches the corresponding
466                node of the pattern, so it is ok =>
467                recurse looking for nested mis-matches
468             */
469 	    if(!restructr(ncc,dxdsubnode,matchnode,repairlist))
470 		return 0;
471 	} else {
472             /* If we do not have a direct match, then we need to look
473                at all the grids to see if this node matches a field
474                in one of the grids
475             */
476             for(match=0,i=0;!match && i<nclistlength(patternparent->subnodes);i++) {
477                 CDFnode* subtemp = (CDFnode*)nclistget(patternparent->subnodes,i);
478                 if(subtemp->nctype == NC_Grid) {
479 		    /* look inside */
480                     for(j=0;j<nclistlength(patternparent->subnodes);j++) {
481                         CDFnode* gridfield = (CDFnode*)nclistget(subtemp->subnodes,j);
482                         if(simplenodematch(dxdsubnode,gridfield)) {
483                             /* We need to do this repair */
484                             nclistpush(repairlist,(void*)dxdsubnode);
485                             nclistpush(repairlist,(void*)gridfield);
486                             match = 1;
487                             break;
488                         }
489                     }
490                 }
491             }
492 	    if(!match) return 0; /* we failed */
493 	}
494     }
495     return 1; /* we matched everything at this level */
496 }
497 
498 /* Wrap the node wrt the pattern grid or pattern struct */
499 
500 static NCerror
repairgrids(NCDAPCOMMON * ncc,NClist * repairlist)501 repairgrids(NCDAPCOMMON* ncc, NClist* repairlist)
502 {
503     NCerror ncstat = NC_NOERR;
504     int i;
505     assert(nclistlength(repairlist) % 2 == 0);
506     for(i=0;i<nclistlength(repairlist);i+=2) {
507 	CDFnode* node = (CDFnode*)nclistget(repairlist,i);
508 	CDFnode* pattern = (CDFnode*)nclistget(repairlist,i+1);
509 	int index = findin(node->container,node);
510 	int tindex = findin(pattern->container,pattern);
511 	ncstat = structwrap(ncc, node,node->container,index,
512                              pattern->container,tindex);
513 #ifdef DEBUG
514 fprintf(stderr,"repairgrids: %s -> %s\n",
515 ocfqn(node->ocnode),ocfqn(pattern->ocnode));
516 #endif
517 
518     }
519     return ncstat;
520 }
521 
522 static NCerror
structwrap(NCDAPCOMMON * ncc,CDFnode * node,CDFnode * parent,int parentindex,CDFnode * patterngrid,int gridindex)523 structwrap(NCDAPCOMMON* ncc, CDFnode* node, CDFnode* parent, int parentindex,
524                            CDFnode* patterngrid, int gridindex)
525 {
526     CDFnode* newstruct;
527 
528     ASSERT((patterngrid->nctype == NC_Grid));
529     newstruct = makenewstruct(ncc, node,patterngrid);
530     if(newstruct == NULL) {return THROW(NC_ENOMEM);}
531 
532     /* replace the node with the new structure
533        in the parent's list of children*/
534     nclistset(parent->subnodes,parentindex,(void*)newstruct);
535 
536     /* Update the list of all nodes in the tree */
537     nclistpush(node->root->tree->nodes,(void*)newstruct);
538     return NC_NOERR;
539 }
540 
541 static int
findin(CDFnode * parent,CDFnode * child)542 findin(CDFnode* parent, CDFnode* child)
543 {
544     int i;
545     NClist* subnodes = parent->subnodes;
546     for(i=0;i<nclistlength(subnodes);i++) {
547 	if(nclistget(subnodes,i) == child)
548 	    return i;
549     }
550     return -1;
551 }
552 
553 /* Create a structure to surround projected grid array or map;
554    this occurs because some servers (that means you ferret and you thredds!)
555    do not adhere to the DAP2 protocol spec.
556 */
557 
558 static CDFnode*
makenewstruct(NCDAPCOMMON * ncc,CDFnode * node,CDFnode * patternnode)559 makenewstruct(NCDAPCOMMON* ncc, CDFnode* node, CDFnode* patternnode)
560 {
561     CDFnode* newstruct = makecdfnode(ncc,patternnode->ocname,OC_Structure,
562                                       patternnode->ocnode, node->container);
563     if(newstruct == NULL) return NULL;
564     newstruct->nc_virtual = 1;
565     newstruct->ncbasename = nulldup(patternnode->ncbasename);
566     newstruct->subnodes = nclistnew();
567     newstruct->pattern = patternnode;
568     node->container = newstruct;
569     nclistpush(newstruct->subnodes,(void*)node);
570     return newstruct;
571 }
572 
573 /**
574 Make the constrained dds nodes (root)
575 point to the corresponding unconstrained
576 dds nodes (fullroot).
577  */
578 
579 NCerror
mapnodes(CDFnode * root,CDFnode * fullroot)580 mapnodes(CDFnode* root, CDFnode* fullroot)
581 {
582     NCerror ncstat = NC_NOERR;
583     ASSERT(root != NULL && fullroot != NULL);
584     if(!simplenodematch(root,fullroot))
585 	{THROWCHK(ncstat=NC_EINVAL); goto done;}
586     /* clear out old associations*/
587     unmap(root);
588     ncstat = mapnodesr(root,fullroot,0);
589 done:
590     return ncstat;
591 }
592 
593 static NCerror
mapnodesr(CDFnode * connode,CDFnode * fullnode,int depth)594 mapnodesr(CDFnode* connode, CDFnode* fullnode, int depth)
595 {
596     unsigned int i,j;
597     NCerror ncstat = NC_NOERR;
598 
599     ASSERT((simplenodematch(connode,fullnode)));
600 
601 #ifdef DEBUG
602   {
603 char* path1 = makecdfpathstring(fullnode,".");
604 char* path2 = makecdfpathstring(connode,".");
605 fprintf(stderr,"mapnode: %s->%s\n",path1,path2);
606 nullfree(path1); nullfree(path2);
607   }
608 #endif
609 
610     /* Map node */
611     mapfcn(connode,fullnode);
612 
613 #if 0
614   {
615     int i;
616     for(i=0;i<nclistlength(fullnode->subnodes);i++) {
617 	CDFnode* n = (CDFnode*)nclistget(fullnode->subnodes,i);
618 	fprintf(stderr,"fullnode.subnode[%d]: (%d) %s\n",i,n->nctype,n->ocname);
619     }
620     for(i=0;i<nclistlength(connode->subnodes);i++) {
621 	CDFnode* n = (CDFnode*)nclistget(connode->subnodes,i);
622 	fprintf(stderr,"connode.subnode[%d]: (%d) %s\n",i,n->nctype,n->ocname);
623     }
624   }
625 #endif
626 
627     /* Try to match connode subnodes against fullnode subnodes */
628     ASSERT(nclistlength(connode->subnodes) <= nclistlength(fullnode->subnodes));
629 
630     for(i=0;i<nclistlength(connode->subnodes);i++) {
631         CDFnode* consubnode = (CDFnode*)nclistget(connode->subnodes,i);
632 	/* Search full subnodes for a matching subnode from con */
633         for(j=0;j<nclistlength(fullnode->subnodes);j++) {
634             CDFnode* fullsubnode = (CDFnode*)nclistget(fullnode->subnodes,j);
635             if(simplenodematch(fullsubnode,consubnode)) {
636                 ncstat = mapnodesr(consubnode,fullsubnode,depth+1);
637    	        if(ncstat) goto done;
638 	    }
639 	}
640     }
641 done:
642     return THROW(ncstat);
643 }
644 
645 
646 /* The specific actions of a map are defined
647    by this function.
648 */
649 static NCerror
mapfcn(CDFnode * dstnode,CDFnode * srcnode)650 mapfcn(CDFnode* dstnode, CDFnode* srcnode)
651 {
652     /* Mark node as having been mapped */
653     dstnode->basenode = srcnode;
654     return NC_NOERR;
655 }
656 
657 void
unmap(CDFnode * root)658 unmap(CDFnode* root)
659 {
660     unsigned int i;
661     CDFtree* tree = root->tree;
662     for(i=0;i<nclistlength(tree->nodes);i++) {
663 	CDFnode* node = (CDFnode*)nclistget(tree->nodes,i);
664 	node->basenode = NULL;
665     }
666 }
667 
668 /*
669 Move dimension data from basenodes to nodes
670 */
671 
672 NCerror
dimimprint(NCDAPCOMMON * nccomm)673 dimimprint(NCDAPCOMMON* nccomm)
674 {
675     NCerror ncstat = NC_NOERR;
676     NClist* allnodes;
677     int i,j;
678     CDFnode* basenode;
679 
680     allnodes = nccomm->cdf.ddsroot->tree->nodes;
681     for(i=0;i<nclistlength(allnodes);i++) {
682 	CDFnode* node = (CDFnode*)nclistget(allnodes,i);
683 	int noderank, baserank;
684         /* Do dimension imprinting */
685 	basenode = node->basenode;
686 	if(basenode == NULL) continue;
687 	noderank = nclistlength(node->array.dimset0);
688 	baserank = nclistlength(basenode->array.dimset0);
689 	if(noderank == 0) continue;
690         ASSERT(noderank == baserank);
691 #ifdef DEBUG
692 fprintf(stderr,"dimimprint %s/%d -> %s/%d\n",
693 	makecdfpathstring(basenode,"."),
694 	noderank,
695 	makecdfpathstring(node,"."),
696 	baserank);
697 #endif
698         for(j=0;j<noderank;j++) {
699 	    CDFnode* dim = (CDFnode*)nclistget(node->array.dimset0,j);
700 	    CDFnode* basedim = (CDFnode*)nclistget(basenode->array.dimset0,j);
701 	    dim->dim.declsize0 = basedim->dim.declsize;
702 #ifdef DEBUG
703 fprintf(stderr,"dimimprint: %d: %lu -> %lu\n",i,basedim->dim.declsize,dim->dim.declsize0);
704 #endif
705         }
706     }
707     return ncstat;
708 }
709 
710 static CDFnode*
clonedim(NCDAPCOMMON * nccomm,CDFnode * dim,CDFnode * var)711 clonedim(NCDAPCOMMON* nccomm, CDFnode* dim, CDFnode* var)
712 {
713     CDFnode* clone;
714     clone = makecdfnode(nccomm,dim->ocname,OC_Dimension,
715 			  NULL,dim->container);
716     /* Record its existence */
717     nclistpush(dim->container->root->tree->nodes,(void*)clone);
718     clone->dim = dim->dim; /* copy most everything */
719     clone->dim.dimflags |= CDFDIMCLONE;
720     clone->dim.array = var;
721     return clone;
722 }
723 
724 static NClist*
clonedimset(NCDAPCOMMON * nccomm,NClist * dimset,CDFnode * var)725 clonedimset(NCDAPCOMMON* nccomm, NClist* dimset, CDFnode* var)
726 {
727     NClist* result = NULL;
728     int i;
729 
730     for(i=0;i<nclistlength(dimset);i++) {
731         CDFnode *dim = NULL;
732         if(result == NULL)
733            result = nclistnew();
734 
735         dim = (CDFnode*)nclistget(dimset,i);
736         nclistpush(result,(void*)clonedim(nccomm,dim,var));
737     }
738     return result;
739 }
740 
741 /* Define the dimsetplus list for a node = dimset0+pseudo dims */
742 static NCerror
definedimsetplus(NCDAPCOMMON * nccomm,CDFnode * node)743 definedimsetplus(NCDAPCOMMON* nccomm/*notused*/, CDFnode* node)
744 {
745     int ncstat = NC_NOERR;
746     NClist* dimset = NULL;
747     CDFnode* clone = NULL;
748 
749     if(node->array.dimset0 != NULL)
750         /* copy the dimset0 into dimset */
751         dimset = nclistclone(node->array.dimset0);
752     /* Insert the sequence or string dims */
753     if(node->array.stringdim != NULL) {
754         if(dimset == NULL) dimset = nclistnew();
755 	clone = node->array.stringdim;
756         nclistpush(dimset,(void*)clone);
757     }
758     if(node->array.seqdim != NULL) {
759         if(dimset == NULL) dimset = nclistnew();
760 	clone = node->array.seqdim;
761         nclistpush(dimset,(void*)clone);
762     }
763     node->array.dimsetplus = dimset;
764     return ncstat;
765 }
766 
767 /* Define the dimsetall list for a node =  */
768 static NCerror
definedimsetall(NCDAPCOMMON * nccomm,CDFnode * node)769 definedimsetall(NCDAPCOMMON* nccomm/*notused*/, CDFnode* node)
770 {
771     int i;
772     int ncstat = NC_NOERR;
773     NClist* dimsetall = NULL;
774 
775     if(node->container != NULL) {
776 	/* We need to clone the parent dimensions because we will be assigning
777            indices vis-a-vis this variable */
778         dimsetall = clonedimset(nccomm,node->container->array.dimsetall,node);
779     }
780     /* append dimsetplus; */
781     for(i=0;i<nclistlength(node->array.dimsetplus);i++) {
782 		CDFnode* clone = NULL;
783         if(dimsetall == NULL) dimsetall = nclistnew();
784 		clone = (CDFnode*)nclistget(node->array.dimsetplus,i);
785 		nclistpush(dimsetall,(void*)clone);
786     }
787     node->array.dimsetall = dimsetall;
788 #ifdef DEBUG1
789 fprintf(stderr,"dimsetall: |%s|=%d\n",node->ocname,(int)nclistlength(dimsetall));
790 #endif
791     return ncstat;
792 }
793 
794 /* Define the dimsettrans list for a single node */
795 static NCerror
definetransdimset(NCDAPCOMMON * nccomm,CDFnode * node)796 definetransdimset(NCDAPCOMMON* nccomm/*notused*/, CDFnode* node)
797 {
798     int i;
799     int ncstat = NC_NOERR;
800     NClist* dimsettrans = NULL;
801 
802 #ifdef DEBUG1
803 fprintf(stderr,"dimsettrans3: node=%s/%d\n",node->ocname,nclistlength(node->array.dimset0));
804 #endif
805     if(node->container != NULL) {
806 	/* We need to clone the parent dimensions because we will be assigning
807            indices vis-a-vis this variable */
808         dimsettrans = clonedimset(nccomm,node->container->array.dimsettrans,node);
809     }
810     /* concat parent dimset0 and dimset;*/
811     if(dimsettrans == NULL)
812 	dimsettrans = nclistnew();
813     for(i=0;i<nclistlength(node->array.dimset0);i++) {
814 	CDFnode* clone = NULL;
815 	clone = (CDFnode*)nclistget(node->array.dimset0,i);
816 	nclistpush(dimsettrans,(void*)clone);
817     }
818     node->array.dimsettrans = dimsettrans;
819     dimsettrans = NULL;
820 #ifdef DEBUG1
821 fprintf(stderr,"dimsettrans: |%s|=%d\n",node->ocname,(int)nclistlength(dimsettrans));
822 #endif
823     return ncstat;
824 }
825 
826 /*
827 Recursively define the transitive closure of dimensions
828 (dimsettrans) based on the original dimension set (dimset0):
829 */
830 
831 NCerror
definedimsettrans(NCDAPCOMMON * nccomm,CDFtree * tree)832 definedimsettrans(NCDAPCOMMON* nccomm, CDFtree* tree)
833 {
834     /* recursively walk the tree */
835     definedimsettransR(nccomm, tree->root);
836     return NC_NOERR;
837 }
838 
839 /*
840 Recursive helper for definedimsettrans3
841 */
842 static NCerror
definedimsettransR(NCDAPCOMMON * nccomm,CDFnode * node)843 definedimsettransR(NCDAPCOMMON* nccomm, CDFnode* node)
844 {
845     int i;
846     int ncstat = NC_NOERR;
847 
848     definetransdimset(nccomm,node);
849     /* recurse */
850     for(i=0;i<nclistlength(node->subnodes);i++) {
851 	CDFnode* subnode = (CDFnode*)nclistget(node->subnodes,i);
852 	if(subnode->nctype == NC_Dimension) continue; /*ignore*/
853 	ASSERT((subnode->array.dimsettrans == NULL));
854 	ASSERT((subnode->array.dimsetplus == NULL));
855 	ASSERT((subnode->array.dimsetall == NULL));
856 	ncstat = definedimsettransR(nccomm,subnode);
857 	if(ncstat != NC_NOERR)
858 	    break;
859     }
860     return ncstat;
861 }
862 
863 
864 /*
865 Recursively define two dimension sets for each structural node
866 based on the original dimension set (dimset0):
867 1. dimsetplus = dimset0+pseudo-dimensions (string,sequence).
868 2. dimsetall = parent-dimsetall + dimsetplus
869 */
870 
871 NCerror
definedimsets(NCDAPCOMMON * nccomm,CDFtree * tree)872 definedimsets(NCDAPCOMMON* nccomm, CDFtree* tree)
873 {
874     /* recursively walk the tree */
875     definedimsetsR(nccomm, tree->root);
876     return NC_NOERR;
877 }
878 
879 /*
880 Recursive helper
881 */
882 static NCerror
definedimsetsR(NCDAPCOMMON * nccomm,CDFnode * node)883 definedimsetsR(NCDAPCOMMON* nccomm, CDFnode* node)
884 {
885     int i;
886     int ncstat = NC_NOERR;
887 
888     definedimsetplus(nccomm,node);
889     definedimsetall(nccomm,node);
890     /* recurse */
891     for(i=0;i<nclistlength(node->subnodes);i++) {
892 	CDFnode* subnode = (CDFnode*)nclistget(node->subnodes,i);
893 	if(subnode->nctype == NC_Dimension) continue; /*ignore*/
894 	ASSERT((subnode->array.dimsettrans == NULL));
895 	ASSERT((subnode->array.dimsetplus == NULL));
896 	ASSERT((subnode->array.dimsetall == NULL));
897 	ncstat = definedimsetsR(nccomm,subnode);
898 	if(ncstat != NC_NOERR)
899 	    break;
900     }
901     return ncstat;
902 }
903 
904 CDFnode*
makecdfnode(NCDAPCOMMON * nccomm,char * ocname,OCtype octype,OCddsnode ocnode,CDFnode * container)905 makecdfnode(NCDAPCOMMON* nccomm, char* ocname, OCtype octype,
906              /*optional*/ OCddsnode ocnode, CDFnode* container)
907 {
908     CDFnode* node;
909     assert(nccomm != NULL);
910     node = (CDFnode*)calloc(1,sizeof(CDFnode));
911     if(node == NULL) return (CDFnode*)NULL;
912 
913     node->ocname = NULL;
914     if(ocname) {
915         size_t len = strlen(ocname);
916         if(len >= NC_MAX_NAME) len = NC_MAX_NAME-1;
917         node->ocname = (char*)malloc(len+1);
918 	if(node->ocname == NULL) { nullfree(node); return NULL;}
919 	memcpy(node->ocname,ocname,len);
920 	node->ocname[len] = '\0';
921     }
922     node->nctype = octypetonc(octype);
923     node->ocnode = ocnode;
924     node->subnodes = nclistnew();
925     node->container = container;
926     if(ocnode != NULL) {
927 	oc_dds_atomictype(nccomm->oc.conn,ocnode,&octype);
928         node->etype = octypetonc(octype);
929     }
930     if(container != NULL)
931 	node->root = container->root;
932     else if(node->nctype == NC_Dataset)
933 	node->root = node;
934     return node;
935 }
936 
937 /* Given an OCnode tree, mimic it as a CDFnode tree;
938    Add DAS attributes if DAS is available. Accumulate set
939    of all nodes in preorder.
940 */
941 NCerror
buildcdftree(NCDAPCOMMON * nccomm,OCddsnode ocroot,OCdxd occlass,CDFnode ** cdfrootp)942 buildcdftree(NCDAPCOMMON* nccomm, OCddsnode ocroot, OCdxd occlass, CDFnode** cdfrootp)
943 {
944     CDFnode* root = NULL;
945     CDFtree* tree = (CDFtree*)calloc(1,sizeof(CDFtree));
946     NCerror err = NC_NOERR;
947     if(!tree)
948       return OC_ENOMEM;
949 
950     tree->ocroot = ocroot;
951     tree->nodes = nclistnew();
952     tree->occlass = occlass;
953     tree->owner = nccomm;
954 
955     err = buildcdftreer(nccomm,ocroot,NULL,tree,&root);
956     if(!err) {
957 	if(occlass != OCDAS)
958 	    fixnodes(nccomm,tree->nodes);
959 	if(cdfrootp) *cdfrootp = root;
960     }
961     return err;
962 }
963 
964 static NCerror
buildcdftreer(NCDAPCOMMON * nccomm,OCddsnode ocnode,CDFnode * container,CDFtree * tree,CDFnode ** cdfnodep)965 buildcdftreer(NCDAPCOMMON* nccomm, OCddsnode ocnode, CDFnode* container,
966                 CDFtree* tree, CDFnode** cdfnodep)
967 {
968     size_t i,ocrank,ocnsubnodes;
969     OCtype octype;
970     OCtype ocatomtype;
971     char* ocname = NULL;
972     NCerror ncerr = NC_NOERR;
973     CDFnode* cdfnode = NULL;
974 
975     oc_dds_class(nccomm->oc.conn,ocnode,&octype);
976     if(octype == OC_Atomic)
977 	oc_dds_atomictype(nccomm->oc.conn,ocnode,&ocatomtype);
978     else
979 	ocatomtype = OC_NAT;
980     oc_dds_name(nccomm->oc.conn,ocnode,&ocname);
981     oc_dds_rank(nccomm->oc.conn,ocnode,&ocrank);
982     oc_dds_nsubnodes(nccomm->oc.conn,ocnode,&ocnsubnodes);
983 
984 #ifdef DEBUG1
985     if(ocatomtype == OC_NAT)
986 	fprintf(stderr,"buildcdftree: connect: %s %s\n",oc_typetostring(octype),ocname);
987     else
988 	fprintf(stderr,"buildcdftree: connect: %s %s\n",oc_typetostring(ocatomtype),ocname);
989 #endif
990 
991     switch (octype) {
992     case OC_Dataset:
993 	cdfnode = makecdfnode(nccomm,ocname,octype,ocnode,container);
994 	nclistpush(tree->nodes,(void*)cdfnode);
995 	tree->root = cdfnode;
996 	cdfnode->tree = tree;
997 	break;
998 
999     case OC_Grid:
1000     case OC_Structure:
1001     case OC_Sequence:
1002 	cdfnode = makecdfnode(nccomm,ocname,octype,ocnode,container);
1003 	nclistpush(tree->nodes,(void*)cdfnode);
1004 #if 0
1005 	if(tree->root == NULL) {
1006 	    tree->root = cdfnode;
1007 	    cdfnode->tree = tree;
1008 	}
1009 #endif
1010 	break;
1011 
1012     case OC_Atomic:
1013 	cdfnode = makecdfnode(nccomm,ocname,octype,ocnode,container);
1014 	nclistpush(tree->nodes,(void*)cdfnode);
1015 #if 0
1016 	if(tree->root == NULL) {
1017 	    tree->root = cdfnode;
1018 	    cdfnode->tree = tree;
1019 	}
1020 #endif
1021 	break;
1022 
1023     case OC_Dimension:
1024     default: PANIC1("buildcdftree: unexpected OC node type: %d",(int)octype);
1025 
1026     }
1027     /* Avoid a rare but perhaps possible null-dereference
1028        of cdfnode. Not sure what error to throw, so using
1029        NC_EDAP: generic DAP error. */
1030     if(!cdfnode) {
1031       return NC_EDAP;
1032     }
1033 
1034 #if 0
1035     /* cross link */
1036     assert(tree->root != NULL);
1037     cdfnode->root = tree->root;
1038 #endif
1039 
1040     if(ocrank > 0) defdimensions(ocnode,cdfnode,nccomm,tree);
1041     for(i=0;i<ocnsubnodes;i++) {
1042 	OCddsnode ocsubnode;
1043 	CDFnode* subnode;
1044 	oc_dds_ithfield(nccomm->oc.conn,ocnode,i,&ocsubnode);
1045 	ncerr = buildcdftreer(nccomm,ocsubnode,cdfnode,tree,&subnode);
1046 	if(ncerr) {
1047 	  if(ocname) free(ocname);
1048 	  return ncerr;
1049 	}
1050 	nclistpush(cdfnode->subnodes,(void*)subnode);
1051     }
1052     nullfree(ocname);
1053     if(cdfnodep) *cdfnodep = cdfnode;
1054     return ncerr;
1055 }
1056 
1057 void
freecdfroot(CDFnode * root)1058 freecdfroot(CDFnode* root)
1059 {
1060     int i;
1061     CDFtree* tree;
1062     NCDAPCOMMON* nccomm;
1063     if(root == NULL) return;
1064     tree = root->tree;
1065     ASSERT((tree != NULL));
1066     /* Explicitly FREE the ocroot */
1067     nccomm = tree->owner;
1068     oc_root_free(nccomm->oc.conn,tree->ocroot);
1069     tree->ocroot = NULL;
1070     for(i=0;i<nclistlength(tree->nodes);i++) {
1071 	CDFnode* node = (CDFnode*)nclistget(tree->nodes,i);
1072 	free1cdfnode(node);
1073     }
1074     nclistfree(tree->nodes);
1075     nclistfree(tree->varnodes);
1076     nclistfree(tree->seqnodes);
1077     nclistfree(tree->gridnodes);
1078     nullfree(tree);
1079 }
1080 
1081 /* Free up a single node, but not any
1082    nodes it points to.
1083 */
1084 static void
free1cdfnode(CDFnode * node)1085 free1cdfnode(CDFnode* node)
1086 {
1087     unsigned int j,k;
1088     if(node == NULL) return;
1089     nullfree(node->ocname);
1090     nullfree(node->ncbasename);
1091     nullfree(node->ncfullname);
1092     if(node->attributes != NULL) {
1093 	for(j=0;j<nclistlength(node->attributes);j++) {
1094 	    NCattribute* att = (NCattribute*)nclistget(node->attributes,j);
1095 	    nullfree(att->name);
1096 	    for(k=0;k<nclistlength(att->values);k++)
1097 		nullfree((char*)nclistget(att->values,k));
1098 	    nclistfree(att->values);
1099 	    nullfree(att);
1100 	}
1101     }
1102     nullfree(node->dodsspecial.dimname);
1103     nclistfree(node->subnodes);
1104     nclistfree(node->attributes);
1105     nclistfree(node->array.dimsetplus);
1106     nclistfree(node->array.dimsetall);
1107     nclistfree(node->array.dimset0);
1108     nclistfree(node->array.dimsettrans);
1109 
1110     /* Clean up the ncdap4 fields also */
1111     nullfree(node->typename);
1112     nullfree(node->vlenname);
1113     nullfree(node);
1114 }
1115 
1116 
1117 
1118 /* Return true if node and node1 appear to refer to the same thing;
1119    takes grid->structure changes into account.
1120 */
1121 int
nodematch(CDFnode * node1,CDFnode * node2)1122 nodematch(CDFnode* node1, CDFnode* node2)
1123 {
1124     return simplenodematch(node1,node2);
1125 }
1126 
1127 /*
1128 Try to figure out if two nodes
1129 are the "related" =>
1130     same name && same nc_type and same arity
1131 but: Allow Grid == Structure
1132 */
1133 
1134 int
simplenodematch(CDFnode * node1,CDFnode * node2)1135 simplenodematch(CDFnode* node1, CDFnode* node2)
1136 {
1137     /* Test all the obvious stuff */
1138     if(node1 == NULL || node2 == NULL)
1139 	return 0;
1140 
1141     /* Add hack to address the screwed up Columbia server
1142        which returns different Dataset {...} names
1143        depending on the constraint.
1144     */
1145 
1146     if(FLAGSET(node1->root->tree->owner->controls,NCF_COLUMBIA)
1147        && node1->nctype == NC_Dataset) return 1;
1148 
1149     if(strcmp(node1->ocname,node2->ocname)!=0) /* same names */
1150 	return 0;
1151     if(nclistlength(node1->array.dimset0)
1152 	!= nclistlength(node2->array.dimset0)) /* same arity */
1153 	return 0;
1154 
1155     if(node1->nctype != node2->nctype) {
1156 	/* test for struct-grid match */
1157 	int structgrid = ((node1->nctype == NC_Grid && node2->nctype == NC_Structure)
1158                           || (node1->nctype == NC_Structure && node2->nctype == NC_Grid) ? 1 : 0);
1159 	if(!structgrid)
1160 	    return 0;
1161     }
1162 
1163     if(node1->nctype == NC_Atomic && node1->etype != node2->etype)
1164 	return 0;
1165 
1166     return 1;
1167 }
1168 
1169 /* Ensure every node has an initial base name defined and fullname */
1170 /* Exceptions: anonymous dimensions. */
1171 static NCerror
fix1node(NCDAPCOMMON * nccomm,CDFnode * node)1172 fix1node(NCDAPCOMMON* nccomm, CDFnode* node)
1173 {
1174     if(node->nctype == NC_Dimension && node->ocname == NULL) return NC_NOERR;
1175     ASSERT((node->ocname != NULL));
1176     nullfree(node->ncbasename);
1177     node->ncbasename = cdflegalname(node->ocname);
1178     if(node->ncbasename == NULL) return NC_ENOMEM;
1179     nullfree(node->ncfullname);
1180     node->ncfullname = makecdfpathstring(node,nccomm->cdf.separator);
1181     if(node->ncfullname == NULL) return NC_ENOMEM;
1182     if(node->nctype == NC_Atomic)
1183         node->externaltype = nctypeconvert(nccomm,node->etype);
1184     return NC_NOERR;
1185 }
1186 
1187 static NCerror
fixnodes(NCDAPCOMMON * nccomm,NClist * cdfnodes)1188 fixnodes(NCDAPCOMMON* nccomm, NClist* cdfnodes)
1189 {
1190     int i;
1191     for(i=0;i<nclistlength(cdfnodes);i++) {
1192 	CDFnode* node = (CDFnode*)nclistget(cdfnodes,i);
1193 	NCerror err = fix1node(nccomm,node);
1194 	if(err) return err;
1195     }
1196     return NC_NOERR;
1197 }
1198 
1199 static void
defdimensions(OCddsnode ocnode,CDFnode * cdfnode,NCDAPCOMMON * nccomm,CDFtree * tree)1200 defdimensions(OCddsnode ocnode, CDFnode* cdfnode, NCDAPCOMMON* nccomm, CDFtree* tree)
1201 {
1202     size_t i,ocrank;
1203 
1204     oc_dds_rank(nccomm->oc.conn,ocnode,&ocrank);
1205     assert(ocrank > 0);
1206     for(i=0;i<ocrank;i++) {
1207 	CDFnode* cdfdim;
1208 	OCddsnode ocdim;
1209 	char* ocname;
1210 	size_t declsize;
1211 
1212 	oc_dds_ithdimension(nccomm->oc.conn,ocnode,i,&ocdim);
1213 	oc_dimension_properties(nccomm->oc.conn,ocdim,&declsize,&ocname);
1214 
1215 	cdfdim = makecdfnode(nccomm,ocname,OC_Dimension,
1216                               ocdim,cdfnode->container);
1217 	nullfree(ocname);
1218 	nclistpush(tree->nodes,(void*)cdfdim);
1219 	/* Initially, constrained and unconstrained are same */
1220 	cdfdim->dim.declsize = declsize;
1221 	cdfdim->dim.array = cdfnode;
1222 	if(cdfnode->array.dimset0 == NULL)
1223 	    cdfnode->array.dimset0 = nclistnew();
1224 	nclistpush(cdfnode->array.dimset0,(void*)cdfdim);
1225     }
1226 }
1227