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