1 /* Copyright 2018, UCAR/Unidata and OPeNDAP, Inc.
2    See the COPYRIGHT file for more information. */
3 
4 #include "config.h"
5 #include "ocinternal.h"
6 #include "occompile.h"
7 #include "ocdebug.h"
8 
9 static OCerror mergedas1(OCnode* dds, OCnode* das);
10 static OCerror mergedods1(OCnode* dds, OCnode* das);
11 static OCerror mergeother1(OCnode* root, OCnode* das);
12 static char* pathtostring(NClist* path, char* separator);
13 static void computefullname(OCnode* node);
14 static OCerror mergeother1(OCnode* root, OCnode* das);
15 static OCerror mergeother(OCnode* ddsroot, NClist* dasnodes);
16 
17 /* Process ocnodes to fix various semantic issues*/
18 void
occomputesemantics(NClist * ocnodes)19 occomputesemantics(NClist* ocnodes)
20 {
21     unsigned int i,j;
22     OCASSERT((ocnodes != NULL));
23     for(i=0;i<nclistlength(ocnodes);i++) {
24 	OCnode* node = (OCnode*)nclistget(ocnodes,i);
25 	/* set the container for dims*/
26 	if(node->octype == OC_Dimension && node->dim.array != NULL) {
27 	    node->container = node->dim.array->container;
28 	}
29     }
30     /* Fill in array.sizes */
31     for(i=0;i<nclistlength(ocnodes);i++) {
32 	OCnode* node = (OCnode*)nclistget(ocnodes,i);
33 	if(node->array.rank > 0) {
34 	    node->array.sizes = (size_t*)malloc(node->array.rank*sizeof(size_t));
35 	    for(j=0;j<node->array.rank;j++) {
36 		OCnode* dim = (OCnode*)nclistget(node->array.dimensions,j);
37 		node->array.sizes[j] = dim->dim.declsize;
38 	    }
39 	}
40     }
41 }
42 
43 void
occomputefullnames(OCnode * root)44 occomputefullnames(OCnode* root)
45 {
46     unsigned int i;
47     if(root->name != NULL) computefullname(root);
48     if(root->subnodes != NULL) { /* recurse*/
49         for(i=0;i<nclistlength(root->subnodes);i++) {
50 	    OCnode* node = (OCnode*)nclistget(root->subnodes,i);
51 	    occomputefullnames(node);
52 	}
53     }
54 }
55 
56 static void
computefullname(OCnode * node)57 computefullname(OCnode* node)
58 {
59     char* tmp;
60     char* fullname;
61     NClist* path;
62 
63     OCASSERT((node->name != NULL));
64     if(node->fullname != NULL)
65 	return;
66     path = nclistnew();
67     occollectpathtonode(node,path);
68     tmp = pathtostring(path,PATHSEPARATOR);
69     if(tmp == NULL) {
70         fullname = nulldup(node->name);
71     } else {
72         fullname = tmp;
73     }
74     node->fullname = fullname;
75     nclistfree(path);
76 }
77 
78 /* Convert path to a string */
79 static char*
pathtostring(NClist * path,char * separator)80 pathtostring(NClist* path, char* separator)
81 {
82     int slen,i,len;
83     char* pathname;
84     if(path == NULL) return NULL;
85     len = nclistlength(path);
86     if(len == 0) return NULL;
87     for(i=0,slen=0;i<len;i++) {
88 	OCnode* node = (OCnode*)nclistget(path,(size_t)i);
89 	if(node->container == NULL || node->name == NULL) continue;
90 	slen += strlen(node->name);
91     }
92     slen += ((len-1)*strlen(separator));
93     slen += 1;   /* for null terminator*/
94     pathname = (char*)ocmalloc((size_t)slen);
95     MEMCHECK(pathname,NULL);
96     pathname[0] = '\0';
97     for(i=0;i<len;i++) {
98 	OCnode* node = (OCnode*)nclistget(path,(size_t)i);
99 	if(node->container == NULL || node->name == NULL) continue;
100 	if(strlen(pathname) > 0) strcat(pathname,separator);
101         strcat(pathname,node->name);
102     }
103     return pathname;
104 }
105 
106 /* Collect the set of nodes ending in "node"*/
107 void
occollectpathtonode(OCnode * node,NClist * path)108 occollectpathtonode(OCnode* node, NClist* path)
109 {
110     if(node == NULL) return;
111     occollectpathtonode(node->container,path);
112     nclistpush(path,(void*)node);
113 }
114 
115 OCnode*
ocnode_new(char * name,OCtype ptype,OCnode * root)116 ocnode_new(char* name, OCtype ptype, OCnode* root)
117 {
118     OCnode* cdf = (OCnode*)ocmalloc(sizeof(OCnode));
119     MEMCHECK(cdf,(OCnode*)NULL);
120     memset((void*)cdf,0,sizeof(OCnode));
121     cdf->header.magic = OCMAGIC;
122     cdf->header.occlass = OC_Node;
123     cdf->name = (name?nulldup(name):NULL);
124     cdf->octype = ptype;
125     cdf->array.dimensions = NULL;
126     cdf->root = root;
127     return cdf;
128 }
129 
130 static OCattribute*
makeattribute(char * name,OCtype ptype,NClist * values)131 makeattribute(char* name, OCtype ptype, NClist* values)
132 {
133     OCattribute* att = (OCattribute*)ocmalloc(sizeof(OCattribute)); /* ocmalloc zeros*/
134     MEMCHECK(att,(OCattribute*)NULL);
135     att->name = nulldup(name);
136     att->etype = ptype;
137     att->nvalues = nclistlength(values);
138     att->values = NULL;
139     if(att->nvalues > 0) {
140 	int i;
141         att->values = (char**)ocmalloc(sizeof(char*)*att->nvalues);
142         for(i=0;i<att->nvalues;i++)
143 	    att->values[i] = nulldup((char*)nclistget(values,(size_t)i));
144     }
145     return att;
146 }
147 
148 void
ocroot_free(OCnode * root)149 ocroot_free(OCnode* root)
150 {
151     OCtree* tree;
152     OCstate* state;
153     int i;
154 
155     if(root == NULL || root->tree == NULL) return;
156 
157     tree = root->tree;
158     state = tree->state;
159 
160     /* Free up the OCDATA instance, if any */
161     if(tree->data.data != NULL)
162 	ocdata_free(state,tree->data.data);
163 
164     for(i=0;i<nclistlength(state->trees);i++) {
165 	OCnode* node = (OCnode*)nclistget(state->trees,(size_t)i);
166 	if(root == node)
167 	    nclistremove(state->trees,(size_t)i);
168     }
169     /* Note: it is ok if state->trees does not contain this root */
170     octree_free(tree);
171 }
172 
173 void
octree_free(OCtree * tree)174 octree_free(OCtree* tree)
175 {
176     if(tree == NULL) return;
177     ocnodes_free(tree->nodes);
178     ocfree(tree->constraint);
179     ocfree(tree->text);
180     if(tree->data.xdrs != NULL) {
181         xxdr_free(tree->data.xdrs);
182     }
183     ocfree(tree->data.filename); /* may be null */
184     if(tree->data.file != NULL) fclose(tree->data.file);
185     ocfree(tree->data.memory);
186     ocfree(tree);
187 }
188 
189 void
ocnodes_free(NClist * nodes)190 ocnodes_free(NClist* nodes)
191 {
192     unsigned int i,j;
193     for(i=0;i<nclistlength(nodes);i++) {
194 	OCnode* node = (OCnode*)nclistget(nodes,i);
195         ocfree(node->name);
196         ocfree(node->fullname);
197         while(nclistlength(node->att.values) > 0) {
198 	    char* value = (char*)nclistpop(node->att.values);
199 	    ocfree(value);
200         }
201         while(nclistlength(node->attributes) > 0) {
202             OCattribute* attr = (OCattribute*)nclistpop(node->attributes);
203 	    ocfree(attr->name);
204 #if 0
205 	    /* If the attribute type is string, then we need to free them*/
206 all values are strings now
207 	    if(attr->etype == OC_String || attr->etype == OC_URL)
208 #endif
209 	    {
210 		char** strings = (char**)attr->values;
211 		for(j=0;j<attr->nvalues;j++) {ocfree(*strings); strings++;}
212 	    }
213 	    ocfree(attr->values);
214 	    ocfree(attr);
215         }
216         if(node->array.dimensions != NULL) nclistfree(node->array.dimensions);
217         if(node->subnodes != NULL) nclistfree(node->subnodes);
218         if(node->att.values != NULL) nclistfree(node->att.values);
219         if(node->attributes != NULL) nclistfree(node->attributes);
220 	if(node->array.sizes != NULL) free(node->array.sizes);
221         ocfree(node);
222     }
223     nclistfree(nodes);
224 }
225 
226 /*
227 In order to be as compatible as possible with libdap,
228 we try to use the same algorithm for DAS->DDS matching.
229 As described there, the algorithm is as follows.
230     If the [attribute] name contains one or
231     more field separators then look for a [DDS]variable whose
232     name matches exactly. If the name contains no field separators then
233     the look first in the top level [of the DDS] and then in all subsequent
234     levels and return the first occurrence found. In general, this
235     searches constructor types in the order in which they appear
236     in the DDS, but there is no requirement that it do so.
237 
238     Note: If a dataset contains two constructor types which have field names
239     that are the same (say point.x and pair.x) one should use fully qualified
240     names to get each of those variables.
241 
242 */
243 
244 OCerror
ocddsdasmerge(OCstate * state,OCnode * dasroot,OCnode * ddsroot)245 ocddsdasmerge(OCstate* state, OCnode* dasroot, OCnode* ddsroot)
246 {
247     OCerror stat = OC_NOERR;
248     NClist* dasglobals = nclistnew();
249     NClist* dodsglobals = nclistnew(); /* top-level DODS_XXX {...} */
250     NClist* dasnodes = nclistnew();
251     NClist* varnodes = nclistnew();
252     NClist* ddsnodes;
253     unsigned int i,j;
254 
255     if(dasroot->tree == NULL || dasroot->tree->dxdclass != OCDAS)
256 	{stat = OCTHROW(OC_EINVAL); goto done;}
257     if(ddsroot->tree == NULL || (ddsroot->tree->dxdclass != OCDDS
258         && ddsroot->tree->dxdclass != OCDATADDS))
259 	{stat = OCTHROW(OC_EINVAL); goto done;}
260 
261     ddsnodes = ddsroot->tree->nodes;
262 
263     /* 1. collect all the relevant DAS nodes;
264           namely those that contain at least one
265           attribute value, not including the leaf attributes.
266           Simultaneously look for potential ambiguities
267           if found; complain but continue: result are indeterminate.
268           also collect globals separately*/
269     for(i=0;i<nclistlength(dasroot->tree->nodes);i++) {
270 	OCnode* das = (OCnode*)nclistget(dasroot->tree->nodes,i);
271 	int hasattributes = 0;
272 	if(das->octype == OC_Attribute) continue; /* ignore these for now*/
273 	if(das->name == NULL || das->att.isglobal) {
274 	    nclistpush(dasglobals,(void*)das);
275 	    continue;
276 	}
277 	if(das->att.isdods) {
278 	    nclistpush(dodsglobals,(void*)das);
279 	    continue;
280 	}
281 	for(j=0;j<nclistlength(das->subnodes);j++) {
282 	    OCnode* subnode = (OCnode*)nclistget(das->subnodes,j);
283 	    if(subnode->octype == OC_Attribute) {hasattributes = 1; break;}
284 	}
285 	if(hasattributes) {
286 	    /* Look for previously collected nodes with same name*/
287             for(j=0;j<nclistlength(dasnodes);j++) {
288 	        OCnode* das2 = (OCnode*)nclistget(dasnodes,j);
289 		if(das->name == NULL || das2->name == NULL) continue;
290 		if(strcmp(das->name,das2->name)==0) {
291 		    nclog(NCLOGWARN,"oc_mergedas: potentially ambiguous DAS name: %s",das->name);
292 		}
293 	    }
294 	    nclistpush(dasnodes,(void*)das);
295 	}
296     }
297 
298     /* 2. collect all the leaf DDS nodes (of type OC_Atomic)*/
299     for(i=0;i<nclistlength(ddsnodes);i++) {
300 	OCnode* dds = (OCnode*)nclistget(ddsnodes,i);
301 	if(dds->octype == OC_Atomic) nclistpush(varnodes,(void*)dds);
302     }
303 
304     /* 3. For each das node, locate matching DDS node(s) and attach
305           attributes to the DDS node(s).
306           Match means:
307           1. DAS->fullname :: DDS->fullname
308           2. DAS->name :: DDS->fullname (support DAS names with embedded '.')
309           3. DAS->name :: DDS->name
310     */
311     for(i=0;i<nclistlength(dasnodes);i++) {
312 	OCnode* das = (OCnode*)nclistget(dasnodes,i);
313         for(j=0;j<nclistlength(varnodes);j++) {
314 	    OCnode* dds = (OCnode*)nclistget(varnodes,j);
315 	    if(strcmp(das->fullname,dds->fullname)==0
316 	       || strcmp(das->name,dds->fullname)==0
317 	       || strcmp(das->name,dds->name)==0) {
318 		mergedas1(dds,das);
319 		/* remove from dasnodes list*/
320 		nclistset(dasnodes,i,(void*)NULL);
321 	    }
322 	}
323     }
324 
325     /* 4. Assign globals*/
326     for(i=0;i<nclistlength(dasglobals);i++) {
327 	OCnode* das = (OCnode*)nclistget(dasglobals,i);
328 	if(das == NULL) continue;
329 	mergedas1(ddsroot,das);
330     }
331     /* 5. Assign DODS_*/
332     for(i=0;i<nclistlength(dodsglobals);i++) {
333 	OCnode* das = (OCnode*)nclistget(dodsglobals,i);
334 	if(das == NULL) continue;
335 	mergedods1(ddsroot,das);
336     }
337 
338     /* 6. Assign other orphan attributes, which means
339 	  construct their full name and assign as a global attribute.
340 	  This is complicated because some servers (e.g. thredds) returns
341           attributes for variables that were not referenced in the DDS.
342           These we continue to suppress.
343      */
344     mergeother(ddsroot,dasnodes);
345 
346 done:
347     /* cleanup*/
348     nclistfree(dasglobals);
349     nclistfree(dodsglobals);
350     nclistfree(dasnodes);
351     nclistfree(varnodes);
352     return OCTHROW(stat);
353 }
354 
355 static OCerror
mergedas1(OCnode * dds,OCnode * das)356 mergedas1(OCnode* dds, OCnode* das)
357 {
358     unsigned int i;
359     OCerror stat = OC_NOERR;
360     if(das == NULL) return OC_NOERR; /* nothing to do */
361     if(dds->attributes == NULL) dds->attributes = nclistnew();
362     /* assign the simple attributes in the das set to this dds node*/
363     for(i=0;i<nclistlength(das->subnodes);i++) {
364 	OCnode* attnode = (OCnode*)nclistget(das->subnodes,i);
365 	if(attnode->octype == OC_Attribute) {
366             OCattribute* att;
367 	    if(dds->octype == OC_Atomic
368 		|| dds->octype == OC_Sequence
369 		|| dds->octype == OC_Structure
370 		|| dds->octype == OC_Grid)
371 	        attnode->att.var = dds;
372             att = makeattribute(attnode->name,
373                                 attnode->etype,
374                                 attnode->att.values);
375             nclistpush(dds->attributes,(void*)att);
376 	}
377     }
378     return OCTHROW(stat);
379 }
380 
381 static OCerror
mergedods1(OCnode * dds,OCnode * dods)382 mergedods1(OCnode* dds, OCnode* dods)
383 {
384     unsigned int i;
385     OCerror stat = OC_NOERR;
386     if(dods == NULL) return OC_NOERR; /* nothing to do */
387     OCASSERT(dods->octype == OC_Attributeset);
388     if(dds->attributes == NULL) dds->attributes = nclistnew();
389     /* assign the simple attributes in the das set to this dds node
390        with renaming to tag as DODS_
391     */
392     for(i=0;i<nclistlength(dods->subnodes);i++) {
393 	OCnode* attnode = (OCnode*)nclistget(dods->subnodes,i);
394 	if(attnode->octype == OC_Attribute) {
395 	    OCattribute* att;
396 	    /* prefix the attribute name with the name of the attribute
397                set plus "."
398             */
399 	    size_t len =   strlen(attnode->name)
400                          + strlen(dods->name)
401 			 + strlen(".");
402 	    len++; /*strlcat nul*/
403 	    char* newname = (char*)malloc(len+1);
404 	    if(newname == NULL) return OC_ENOMEM;
405 	    strncpy(newname,dods->name,len);
406 	    strlcat(newname,".",len);
407 	    strlcat(newname,attnode->name,len);
408 	    att = makeattribute(newname,attnode->etype,attnode->att.values);
409 	    free(newname);
410             nclistpush(dds->attributes,(void*)att);
411 	}
412     }
413     return OCTHROW(stat);
414 }
415 
416 static OCerror
mergeother(OCnode * ddsroot,NClist * dasnodes)417 mergeother(OCnode* ddsroot, NClist* dasnodes)
418 {
419     OCerror stat = OC_NOERR;
420     int i;
421     for(i=0;i<nclistlength(dasnodes);i++) {
422 	OCnode* das = (OCnode*)nclistget(dasnodes,i);
423 	if(das == NULL) continue;
424 	if((stat = mergeother1(ddsroot, das))) break;
425     }
426     return stat;
427 }
428 
429 static OCerror
mergeother1(OCnode * root,OCnode * das)430 mergeother1(OCnode* root, OCnode* das)
431 {
432     OCerror stat = OC_NOERR;
433     OCattribute* att = NULL;
434 
435     OCASSERT(root != NULL);
436     if(root->attributes == NULL) root->attributes = nclistnew();
437 
438     /* Only include if this is not connected to a variable */
439     if(das->att.var != NULL) goto done;
440 
441     if(das->octype == OC_Attribute) {
442         /* compute the full name of this attribute */
443         computefullname(das);
444         /* create attribute */
445         att = makeattribute(das->fullname,das->etype,das->att.values);
446         nclistpush(root->attributes,(void*)att);
447     } else if(das->octype == OC_Attributeset) {
448 	int i;
449 	/* Recurse */
450         for(i=0;i<nclistlength(das->subnodes);i++) {
451 	    OCnode* sub = (OCnode*)nclistget(das->subnodes,i);
452 	    if(sub == NULL) continue;
453 	    mergeother1(root,sub);
454 	}
455     } else
456 	stat = OC_EDAS;
457 done:
458     return OCTHROW(stat);
459 }
460 
461 static void
ocuncorrelate(OCnode * root)462 ocuncorrelate(OCnode* root)
463 {
464     OCtree* tree = root->tree;
465     unsigned int i;
466     if(tree == NULL) return;
467     for(i=0;i<nclistlength(tree->nodes);i++) {
468 	OCnode* node = (OCnode*)nclistget(tree->nodes,i);
469 	node->datadds = NULL;
470     }
471 }
472 
473 static OCerror
occorrelater(OCnode * dds,OCnode * dxd)474 occorrelater(OCnode* dds, OCnode* dxd)
475 {
476     int i,j;
477     OCerror ocstat = OC_NOERR;
478 
479     if(dds->octype != dxd->octype) {
480 	OCTHROWCHK((ocstat = OC_EINVAL)); goto fail;
481     }
482     if(dxd->name != NULL && dxd->name != NULL
483        && strcmp(dxd->name,dds->name) != 0) {
484 	OCTHROWCHK((ocstat = OC_EINVAL)); goto fail;
485     } else if(dxd->name != dds->name) { /* test NULL==NULL */
486 	OCTHROWCHK((ocstat = OC_EINVAL)); goto fail;
487     }
488 
489     if(dxd->array.rank != dds->array.rank) {
490 	OCTHROWCHK((ocstat = OC_EINVAL)); goto fail;
491     }
492 
493     dds->datadds = dxd;
494 
495     switch (dds->octype) {
496     case OC_Dataset:
497     case OC_Structure:
498     case OC_Grid:
499     case OC_Sequence:
500 	/* Remember: there may be fewer datadds fields than dds fields */
501 	for(i=0;i<nclistlength(dxd->subnodes);i++) {
502 	    OCnode* dxd1 = (OCnode*)nclistget(dxd->subnodes,(size_t)i);
503 	    for(j=0;j<nclistlength(dds->subnodes);j++) {
504 		OCnode* dds1 = (OCnode*)nclistget(dds->subnodes,(size_t)j);
505 		if(strcmp(dxd1->name,dds1->name) == 0) {
506 		    ocstat = occorrelater(dds1,dxd1);
507 		    if(ocstat != OC_NOERR) {OCTHROWCHK(ocstat); goto fail;}
508 		    break;
509 		}
510 	    }
511 	}
512 	break;
513     case OC_Dimension:
514     case OC_Atomic:
515 	break;
516     default: OCPANIC1("unexpected node type: %d",dds->octype);
517     }
518     /* Correlate the dimensions */
519     if(dds->array.rank > 0) {
520 	for(i=0;i<nclistlength(dxd->subnodes);i++) {
521 	    OCnode* ddsdim = (OCnode*)nclistget(dds->array.dimensions,(size_t)i);
522 	    OCnode* dxddim = (OCnode*)nclistget(dxd->array.dimensions,(size_t)i);
523 	    ocstat = occorrelater(ddsdim,dxddim);
524 	    if(!ocstat) goto fail;
525 	}
526     }
527 
528 fail:
529     return OCTHROW(ocstat);
530 
531 }
532 
533 OCerror
occorrelate(OCnode * dds,OCnode * dxd)534 occorrelate(OCnode* dds, OCnode* dxd)
535 {
536     if(dds == NULL || dxd == NULL) return OCTHROW(OC_EINVAL);
537     ocuncorrelate(dds);
538     return occorrelater(dds,dxd);
539 }
540 
541 /*
542 Mark cacheable those atomic String/URL typed nodes
543 that are contained only in structures with rank > 0.
544 */
545 void
ocmarkcacheable(OCstate * state,OCnode * ddsroot)546 ocmarkcacheable(OCstate* state, OCnode* ddsroot)
547 {
548     int i,j;
549 #if 0
550     int ok;
551 #endif
552     NClist* treenodes = ddsroot->tree->nodes;
553     NClist* path = nclistnew();
554     for(i=0;i<nclistlength(treenodes);i++) {
555         OCnode* node = (OCnode*)nclistget(treenodes,(size_t)i);
556 	if(node->octype != OC_Atomic) continue;
557 	if(node->etype != OC_String && node->etype != OC_URL) continue;
558 	/* collect node path */
559         nclistclear(path);
560         occollectpathtonode(node,path);
561 #if 0
562         ok = 1;
563 #endif
564 	for(j=1;j<nclistlength(path)-1;j++) {/* skip top level dataset and node itself*/
565             OCnode* pathnode = (OCnode*)nclistget(path,(size_t)j);
566 	    if(pathnode->octype != OC_Structure
567 		|| pathnode->array.rank > 0) {
568 #if 0
569 	    ok=0;
570 #endif
571 	    break;
572 	    }
573 	}
574 #if 0
575 	if(ok) {
576    	    node->cache.cacheable = 1;
577 	    node->cache.valid = 0;
578 	}
579 #endif
580     }
581     nclistfree(path);
582 }
583 
584 #if 0
585 
586 OCerror
587 ocddsdasmerge(OCstate* state, OCnode* ddsroot, OCnode* dasroot)
588 {
589     int i,j;
590     OCerror stat = OC_NOERR;
591     NClist* globals = nclistnew();
592     if(dasroot == NULL) return OCTHROW(stat);
593     /* Start by looking for global attributes*/
594     for(i=0;i<nclistlength(dasroot->subnodes);i++) {
595 	OCnode* node = (OCnode*)nclistget(dasroot->subnodes,i);
596 	if(node->att.isglobal) {
597 	    for(j=0;j<nclistlength(node->subnodes);j++) {
598 		OCnode* attnode = (OCnode*)nclistget(node->subnodes,j);
599 		Attribute* att = makeattribute(attnode->name,
600 						attnode->etype,
601 						attnode->att.values);
602 		nclistpush(globals,(void*)att);
603 	    }
604 	}
605     }
606     ddsroot->attributes = globals;
607     /* Now try to match subnode names with attribute set names*/
608     for(i=0;i<nclistlength(dasroot->subnodes);i++) {
609 	OCnode* das = (OCnode*)nclistget(dasroot->subnodes,i);
610 	int match = 0;
611         if(das->att.isglobal) continue;
612         if(das->octype == OC_Attributeset) {
613             for(j=0;j<nclistlength(ddsroot->subnodes) && !match;j++) {
614 	        OCnode* dds = (OCnode*)nclistget(ddsroot->subnodes,j);
615 	        if(strcmp(das->name,dds->name) == 0) {
616 		    match = 1;
617 	            stat = mergedas1(dds,das);
618 	            if(stat != OC_NOERR) break;
619 		}
620 	    }
621 	}
622         if(!match) {marklostattribute(das);}
623     }
624     if(stat == OC_NOERR) ddsroot->attributed = 1;
625     return OCTHROW(stat);
626 }
627 
628 /* Merge das attributes into the dds node*/
629 
630 static int
631 mergedas1(OCnode* dds, OCnode* das)
632 {
633     int i,j;
634     int stat = OC_NOERR;
635     if(dds->attributes == NULL) dds->attributes = nclistnew();
636     /* assign the simple attributes in the das set to this dds node*/
637     for(i=0;i<nclistlength(das->subnodes);i++) {
638 	OCnode* attnode = (OCnode*)nclistget(das->subnodes,i);
639 	if(attnode->octype == OC_Attribute) {
640 	    Attribute* att = makeattribute(attnode->name,
641 						attnode->etype,
642 						attnode->att.values);
643             nclistpush(dds->attributes,(void*)att);
644 	}
645     }
646     /* Try to merge any enclosed sets with subnodes of dds*/
647     for(i=0;i<nclistlength(das->subnodes);i++) {
648 	OCnode* dasnode = (OCnode*)nclistget(das->subnodes,i);
649 	int match = 0;
650         if(dasnode->octype == OC_Attribute) continue; /* already dealt with above*/
651         for(j=0;j<nclistlength(dds->subnodes) && !match;j++) {
652 	    OCnode* ddsnode = (OCnode*)nclistget(dds->subnodes,j);
653 	    if(strcmp(dasnode->name,ddsnode->name) == 0) {
654 	        match = 1;
655 	        stat = mergedas1(ddsnode,dasnode);
656 	        if(stat != OC_NOERR) break;
657 	    }
658 	}
659         if(!match) {marklostattribute(dasnode);}
660     }
661     return OCTHROW(stat);
662 }
663 
664 void*
665 oclinearize(OCtype etype, unsigned int nstrings, char** strings)
666 {
667     int i;
668     size_t typesize;
669     char* memp;
670     char* memory;
671 
672     if(nstrings == 0) return NULL;
673     typesize = octypesize(etype);
674     memory = (char*)ocmalloc(nstrings*typesize);
675     MEMCHECK(memory,NULL);
676     memp = memory;
677     for(i=0;i<nstrings;i++) {
678 	char* value = strings[i];
679         converttype(etype,value,memp);
680 	memp += typesize;
681     }
682     return memory;
683 }
684 
685 static int
686 converttype(OCtype etype, char* value, char* memory)
687 {
688     long iv;
689     unsigned long uiv;
690     double dv;
691     char c[1];
692     int outofrange = 0;
693 #ifdef HAVE_LONG_LONG_INT
694     long long llv;
695     unsigned long long ullv;
696 #endif
697 
698     switch (etype) {
699     case OC_Char:
700 	if(sscanf(value,"%c",c) != 1) goto fail;
701 	*((char*)memory) = c[0];
702 	break;
703     case OC_Byte:
704 	if(sscanf(value,"%ld",&iv) != 1) goto fail;
705         else if(iv > OC_BYTE_MAX || iv < OC_BYTE_MIN) {iv = OC_BYTE_MAX; outofrange = 1;}
706 	*((signed char*)memory) = (signed char)iv;
707 	break;
708     case OC_UByte:
709 	if(sscanf(value,"%lu",&uiv) != 1) goto fail;
710         else if(uiv > OC_UBYTE_MAX) {uiv = OC_UBYTE_MAX; outofrange = 1;}
711 	*((unsigned char*)memory) = (unsigned char)uiv;
712 	break;
713     case OC_Int16:
714 	if(sscanf(value,"%ld",&iv) != 1) goto fail;
715         else if(iv > OC_INT16_MAX || iv < OC_INT16_MIN) {iv = OC_INT16_MAX; outofrange = 1;}
716 	*((signed short*)memory) = (signed short)iv;
717 	break;
718     case OC_UInt16:
719 	if(sscanf(value,"%lu",&uiv) != 1) goto fail;
720         else if(uiv > OC_UINT16_MAX) {uiv = OC_UINT16_MAX; outofrange = 1;}
721 	*((unsigned short*)memory) = (unsigned short)uiv;
722 	break;
723     case OC_Int32:
724 	if(sscanf(value,"%ld",&iv) != 1) goto fail;
725         else if(iv > OC_INT32_MAX || iv < OC_INT32_MIN) {iv = OC_INT32_MAX; outofrange = 1;}
726 	*((signed int*)memory) = (signed int)iv;
727 	break;
728     case OC_UInt32:
729 	if(sscanf(value,"%lu",&uiv) != 1) goto fail;
730         else if(uiv > OC_UINT32_MAX) {uiv = OC_UINT32_MAX; outofrange = 1;}
731 	*((unsigned char*)memory) = (unsigned int)uiv;
732 	break;
733 #ifdef HAVE_LONG_LONG_INT
734     case OC_Int64:
735 	if(sscanf(value,"%lld",&llv) != 1) goto fail;
736         /*else if(iv > OC_INT64_MAX || iv < OC_INT64_MIN) goto fail;*/
737 	*((signed long long*)memory) = (signed long long)llv;
738 	break;
739     case OC_UInt64:
740 	if(sscanf(value,"%llu",&ullv) != 1) goto fail;
741 	*((unsigned long long*)memory) = (unsigned long long)ullv;
742 	break;
743 #endif
744     case OC_Float32:
745 	if(sscanf(value,"%lf",&dv) != 1) goto fail;
746 	*((float*)memory) = (float)dv;
747 	break;
748     case OC_Float64:
749 	if(sscanf(value,"%lf",&dv) != 1) goto fail;
750 	*((double*)memory) = (double)dv;
751 	break;
752     case OC_String: case OC_URL:
753 	*((char**)memory) = nulldup(value);
754 	break;
755     default:
756 	goto fail;
757     }
758     if(outofrange)
759         oc_log(LOGWARN,"converttype range failure: %d: %s",etype,value);
760     return 1;
761 fail:
762     oc_log(LOGERR,"converttype bad value: %d: %s",etype,value);
763     return 0;
764 }
765 #endif /*0*/
766