1 /*********************************************************************
2  *   Copyright 2016, UCAR/Unidata
3  *   See netcdf/COPYRIGHT file for copying and redistribution conditions.
4  *********************************************************************/
5 
6 /**
7 This provides a simple netcdf-4 metadata -> xml printer.
8 Primarily for use in debugging, but could be adapted to
9 create other tools.
10 */
11 
12 #include "config.h"
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include "xxxnetcdf.h"
17 #include "ncbytes.h"
18 #include "nclist.h"
19 
20 #undef DEBUG
21 
22 #define BUFSIZE 4096
23 
24 #define NC_MAX_IDS 8192
25 
26 typedef enum NCSORT {
27 GROUP,
28 VAR,
29 FIELD,
30 DIM,
31 ATTR,
32 ATOMTYPE,
33 USERTYPE,
34 } NCSORT;
35 
36 typedef struct NCID NCID;
37 
38 typedef struct NC4printer {
39     NCbytes* out;
40     NClist* types;
41     NClist* dims;
42     NClist* allnodes;
43     NCbytes* tmp1;
44     NCbytes* tmp2;
45 } NC4printer;
46 
47 struct NCID {
48     NCSORT sort;
49     struct NCID* parent;
50     int id;
51     char name[NC_MAX_NAME+1];
52     NCID* base;
53     size_t size;
54     struct {nc_type kind;} usertype; /*sort == USERTYPE*/
55     struct {int rank;} var; /*sort == VAR*/
56     struct {int fid;} field; /*sort == FIELD*/
57     struct {int isroot;} group; /*sort == GROUP*/
58 };
59 
60 #define MAKEID(Node,Sort,Parent,Id) \
61 NCID* Node = (NCID*)calloc(1,sizeof(NCID)); \
62 Node->sort = Sort; Node->parent = Parent; Node->id = Id; \
63 track(out,Node);
64 
65 union NUMVALUE {
66     unsigned char i8[8];
67     unsigned short i16[4];
68     unsigned short i32[2];
69     unsigned long long i64[1];
70 };
71 
72 #define SETNAME(x,y) strncpy((x)->name,(y),NC_MAX_NAME+1);
73 #define GRPIDFOR(gid) ((gid) & 0xFFFF)
74 #define GROUPOF(x) ((x)->parent->id)
75 
76 #define FAIL {return ret;}
77 
78 #define PRINTF(fmt,...) snprintf(out->buf,BUFSIZE,fmt,__VAR_ARGS__)
79 #define CAT(x) ncbytescat(out->out,x)
80 #define INDENT(x) indent(out,x)
81 
82 #define hasMetadata(node) (nclistlength(node->attributes) > 0)
83 #define hasMaps(var) (nclistlength(node->maps) > 0)
84 #define hasDimensions(var) (nclistlength(node->dimrefs) > 0)
85 
86 static void track(NC4printer* out, NCID* node);
87 
88 /*Forward*/
89 static int buildAtomicTypes(NC4printer* out, NCID* root);
90 static void* computeOffset(NCID* base, void* values, size_t index);
91 static void entityEscape(NCbytes* buf, const char* s);
92 static NCID* findDim(NC4printer* out, int dimid);
93 static NCID* findType(NC4printer* out, nc_type t);
94 static void fqnWalk(NCID* grp, NCbytes* path);
95 static void freeNC4Printer(NC4printer* out);
96 static void getAtomicTypeName(nc_type base, char* name);
97 static int getPrintValue(NCbytes* out, NCID* basetype, void* value);
98 static void indent(NC4printer* out, int depth);
99 static unsigned long long getNumericValue(union NUMVALUE numvalue, nc_type base);
100 static void makeFQN(NCID* id, NCbytes* path);
101 static int printAttribute(NC4printer* out, NCID* attr, int depth);
102 static int printDimref(NC4printer* out, NCID* dim, int depth);;
103 static int printNode(NC4printer* out, NCID* node, int depth);
104 static void printOpaque(NCbytes* out, const unsigned char* s, size_t len, int leadx);
105 static void printString(NCbytes* out, const char* s, int quotes);
106 static int printValue(NC4printer* out, NCID* basetype, void* value, int depth);
107 static int printXMLAttributeInt(NC4printer* out, char* name, long long value);
108 static int printXMLAttributeName(NC4printer* out, char* name, char* value);
109 static int printXMLAttributeSize(NC4printer* out, char* name, size_t value);
110 static int printXMLAttributeString(NC4printer* out, char* name, char* s);
111 static int readAttributeValues(NCID* attr,void**);
112 static void record(NC4printer* out, NCID* node);
113 
114 /**************************************************/
115 
116 int
NC4print(NCbytes * buf,int ncid)117 NC4print(NCbytes* buf, int ncid)
118 {
119     int ret = NC_NOERR;
120     NC4printer* out;
121 
122     if(buf == NULL) return NC_EINVAL;
123     out = (NC4printer*)calloc(1,sizeof(NC4printer));
124     if(out == NULL) return NC_ENOMEM;
125     out->out = buf;
126     out->tmp1 = ncbytesnew();
127     out->tmp2 = ncbytesnew();
128     out->allnodes = nclistnew();
129     out->types = nclistnew();
130     out->dims = nclistnew();
131 
132     MAKEID(root,GROUP,NULL,ncid);
133 	root->group.isroot = 1;
134 
135     buildAtomicTypes(out,root);
136 
137     ret = printNode(out,root,0);
138 
139     freeNC4Printer(out);
140 
141     return ret;
142 }
143 
144 /*************************************************/
145 
146 static void
freeNC4Printer(NC4printer * out)147 freeNC4Printer(NC4printer* out)
148 {
149     int i;
150 
151     if(out == NULL) return;
152 
153 #ifdef DEBUG
154 fprintf(stderr,"free: |allnodes=%ld\n",nclistlength(out->allnodes));
155 fflush(stderr);
156 #endif
157 
158     for(i=0;i<nclistlength(out->allnodes);i++) {
159 	NCID* node = (NCID*)nclistget(out->allnodes,i);
160 #ifdef DEBUG
161 fprintf(stderr,"free: node=%lx\n",(unsigned long)node);
162 fflush(stderr);
163 #endif
164 	if(node != NULL) free(node);
165     }
166 
167     ncbytesfree(out->tmp1);
168     ncbytesfree(out->tmp2);
169     nclistfree(out->types);
170     nclistfree(out->dims);
171     nclistfree(out->allnodes);
172 
173     free(out);
174 }
175 
176 
177 /*************************************************/
178 
179 /**
180  * Print an arbitrary file and its subnodes in xml
181  * Handling newlines is a bit tricky because they may be
182  * embedded for e.g. groups, enums,
183  * etc.  So the rule is that the
184  * last newline is elided and left
185  * for the caller to print.
186  * Exceptions: printMetadata
187  * printDimrefs.
188  *
189  * @param out - the output buffer
190  * @param ncid - the open'd file to print
191  * @param depth - the depth of our code
192  */
193 
194 static int
printNode(NC4printer * out,NCID * node,int depth)195 printNode(NC4printer* out, NCID* node, int depth)
196 {
197     int ret = NC_NOERR;
198     int i = 0;
199     char name[NC_MAX_NAME+1];
200     int ndims, nvars, natts, nunlim, ntypes, ngroups;
201     int n;
202     int ids[NC_MAX_IDS];
203     nc_type base;
204     size_t len, count, size;
205     union NUMVALUE numvalue;
206 
207     switch (node->sort) {
208     case GROUP:
209 	/* Get group name */
210 	if((ret=nc_inq_grpname(node->id,name))) FAIL;
211 	SETNAME(node,name);
212 	/* get group counts */
213 	if((ret=nc_inq(node->id,&ndims,&nvars,&natts,&nunlim))) FAIL;
214         if((ret=nc_inq_typeids(node->id,&ntypes,NULL))) FAIL;
215         if((ret=nc_inq_grps(node->id,&ngroups,NULL))) FAIL;
216 	if(ndims >= NC_MAX_IDS) FAIL;
217 	if(nvars >= NC_MAX_IDS) FAIL;
218 	if(nunlim >= NC_MAX_IDS) FAIL;
219 	if(ntypes >= NC_MAX_IDS) FAIL;
220 	if(ngroups >= NC_MAX_IDS) FAIL;
221 
222         INDENT(depth);
223         CAT("<Group");
224 	printXMLAttributeName(out,"name",name);
225         CAT(">\n");
226         depth++;
227 
228 	{
229 	   /* Print: dims, types, vars(+attr), group-attr, subgroups */
230            if((ret=nc_inq_dimids(node->id,&n,ids, 0))) FAIL;
231             for(i=0;i<ndims;i++) {
232 	        MAKEID(eid,DIM,node,ids[i]);
233                 printNode(out,eid,depth);
234                 CAT("\n");
235 		record(out,eid);
236 	    }
237         }
238 	{
239 	    if((ret=nc_inq_typeids(node->id,&n,ids))) FAIL;
240             for(i=0;i<ntypes;i++) {
241 		nc_type kind;
242 		if((ret=nc_inq_user_type(node->id, ids[i], name, &size, &base, NULL, &kind))) FAIL;
243 		MAKEID(eid,USERTYPE,node,ids[i]);
244 			SETNAME(eid,name);
245 			eid->size = size;
246 			eid->usertype.kind = kind;
247 			if(base > 0) eid->base = findType(out,base);
248 		record(out,eid);
249 		printNode(out,eid,depth);
250 	        CAT("\n");
251 	    }
252         }
253 	{
254 	    if((ret=nc_inq_varids(node->id,&n,ids))) FAIL;
255             for(i=0;i<nvars;i++) {
256 		nc_type base;
257 		if((ret=nc_inq_var(node->id, ids[i], name, &base, &ndims, NULL, NULL))) FAIL;
258 		MAKEID(vid,VAR,node,ids[i]);
259 			SETNAME(vid,name);
260 			vid->base = findType(out,base);
261 			vid->var.rank = ndims;
262 		printNode(out,vid,depth);
263 	        CAT("\n");
264 	    }
265         }
266 	{
267             for(i=0;i<natts;i++) {
268 		if((ret=nc_inq_attname(node->id,NC_GLOBAL,i,name))) FAIL;
269 		MAKEID(id,ATTR,node,NC_GLOBAL);
270 			SETNAME(id,name);
271 		printAttribute(out,id,depth);
272 	        CAT("\n");
273 	    }
274         }
275 
276 	{
277 	    if((ret=nc_inq_grps(node->id,&n,ids))) FAIL;
278             for(i=0;i<ngroups;i++) {
279 		MAKEID(id,GROUP,node,ids[i]);
280 		printNode(out,id,depth);
281 	        CAT("\n");
282 		record(out,id);
283 	    }
284         }
285         depth--;
286         INDENT(depth); CAT("</Group>");
287         break;
288 
289     case DIM:
290         if((ret=nc_inq_dim(GROUPOF(node),node->id,name,&len))) FAIL;
291 		SETNAME(node,name);
292 		node->size = len;
293         INDENT(depth);
294         CAT("<Dimension");
295         printXMLAttributeName(out, "name", name);
296         printXMLAttributeSize(out, "size", len);
297         CAT("/>");
298         break;
299 
300     case USERTYPE:
301 	switch (node->usertype.kind) {
302 	case NC_OPAQUE:
303 	    INDENT(depth); CAT("<Opaque");
304             printXMLAttributeName(out, "name", node->name);
305             printXMLAttributeSize(out, "size", node->size);
306             CAT("/>");
307 	    break;
308 	case NC_ENUM:
309             if((ret=nc_inq_enum(GROUPOF(node),node->id,NULL,NULL,NULL,&count))) FAIL;
310 	    INDENT(depth); CAT("<Enumeration");
311             printXMLAttributeName(out, "name", node->name);
312             CAT(">\n");
313             depth++;
314             for(i=0;i<count;i++) {
315 	        long long value;
316 	        if((ret=nc_inq_enum_member(GROUPOF(node),node->id,i,name,&numvalue))) FAIL;
317 	        value = getNumericValue(numvalue,node->base->id);
318                 INDENT(depth);
319                 CAT("<EnumConst");
320                 printXMLAttributeName(out, "name", name);
321                 printXMLAttributeInt(out, "value", value);
322 	        CAT("/>\n");
323             }
324             depth--;
325             INDENT(depth); CAT("</Enumeration>");
326             break;
327 
328         case NC_COMPOUND:
329             if((ret=nc_inq_compound(GROUPOF(node),node->id,NULL,NULL,&count))) FAIL;
330             INDENT(depth); CAT("<Compound");
331             printXMLAttributeName(out, "name", node->name);
332             CAT(">\n");
333             depth++;
334             for(i=0;i<count;i++) {
335                 if((ret=nc_inq_compound_field(GROUPOF(node),node->id,i,name,NULL,&base,NULL,NULL))) FAIL;
336                 MAKEID(id,FIELD,node->parent,node->id);
337                     SETNAME(id,name);
338                     id->base = findType(out,base);
339                     id->field.fid = i;
340                 printNode(out,id,depth);
341                 CAT("\n");
342             }
343             depth--;
344             INDENT(depth); CAT("</Compound>");
345             break;
346 	case NC_VLEN:
347 	    abort();
348 	    break;
349 	default:
350 	    abort();
351 	    break;
352 	}
353 	break;
354 
355     case VAR:
356         if((ret=nc_inq_var(GROUPOF(node), node->id, name, &base, &ndims, ids, &natts))) FAIL;
357 		node->base = findType(out,base);
358 		SETNAME(node,name);
359 		node->var.rank = ndims;
360         INDENT(depth); CAT("<Var");
361         printXMLAttributeName(out, "name", node->name);
362         makeFQN(node->base,out->tmp2);
363         printXMLAttributeName(out, "type", ncbytescontents(out->tmp2));
364 	if(node->var.rank > 0)
365             printXMLAttributeInt(out, "rank", node->var.rank);
366         if(ndims > 0 || natts > 0) {
367             CAT(">\n");
368             depth++;
369             for(i=0;i<ndims;i++) {
370 		NCID* dim = findDim(out,ids[i]);
371                 printDimref(out,dim,depth);
372 		CAT("\n");
373 	    }
374             for(i=0;i<natts;i++) {
375                 if((ret=nc_inq_attname(GROUPOF(node),node->id,i,name))) FAIL;
376 		if((ret=nc_inq_att(GROUPOF(node),node->id,name,&base,&count))) FAIL;
377                 MAKEID(id,ATTR,node,node->id);
378                         SETNAME(id,name);
379 			id->base = findType(out,base);
380 			id->size = count;
381                 printAttribute(out,id,depth);
382 		CAT("\n");
383 	    }
384             depth--;
385             INDENT(depth); CAT("</Var>");
386         } else
387             CAT("/>");
388         break;
389 
390     case ATOMTYPE:
391     default:
392 	abort();
393         ret = NC_EINVAL;
394         break;
395     }
396     return ret;
397 }
398 
399 static int
printXMLAttributeName(NC4printer * out,char * name,char * value)400 printXMLAttributeName(NC4printer* out, char* name, char* value)
401 {
402     int ret = NC_NOERR;
403     if(name == NULL) return ret;
404     CAT(" "); CAT(name); CAT("=\"");
405     if(value == NULL) value = "";
406     /* add xml entity escaping */
407     entityEscape(out->tmp1,value);
408     CAT(ncbytescontents(out->tmp1));
409     CAT("\"");
410     return ret;
411 }
412 
413 static int
printXMLAttributeSize(NC4printer * out,char * name,size_t value)414 printXMLAttributeSize(NC4printer* out, char* name, size_t value)
415 {
416     return printXMLAttributeInt(out,name,(long long)value);
417 }
418 
419 static int
printXMLAttributeInt(NC4printer * out,char * name,long long value)420 printXMLAttributeInt(NC4printer* out, char* name, long long value)
421 {
422     int ret = NC_NOERR;
423     char svalue[128+1];
424 
425     CAT(" "); CAT(name); CAT("=\"");
426     snprintf(svalue,sizeof(svalue),"%lld",value);
427     CAT(svalue);
428     CAT("\"");
429     return ret;
430 }
431 
432 static int
printXMLAttributeString(NC4printer * out,char * name,char * s)433 printXMLAttributeString(NC4printer* out, char* name, char* s)
434 {
435     int ret = NC_NOERR;
436     CAT(" "); CAT(name); CAT("=");
437     printString(out->out,s,1);
438     return ret;
439 }
440 
441 static int
printAttribute(NC4printer * out,NCID * attr,int depth)442 printAttribute(NC4printer* out, NCID* attr, int depth)
443 {
444     int ret = NC_NOERR;
445     int i = 0;
446     void* values;
447 
448     INDENT(depth); CAT("<Attribute");
449     printXMLAttributeName(out,"name",attr->name);
450     CAT(">\n");
451     if((ret=readAttributeValues(attr,&values))) FAIL;
452     depth++;
453     for(i=0;i<attr->size;i++) {
454 	void* value = computeOffset(attr->base,values,i);
455 	if((ret=printValue(out,attr->base,value,depth))) FAIL;
456     }
457     depth--;
458     INDENT(depth);
459     CAT("</Attribute>");
460     return ret;
461 }
462 
463 /**
464  * Print the dimrefs for a variable's dimensions.
465  * If the variable has a non-whole projection, then use size
466  * else use the dimension name.
467  *
468  * @param var whole dimensions are to be printed
469  * @throws DapException
470  */
471 static int
printDimref(NC4printer * out,NCID * d,int depth)472 printDimref(NC4printer* out, NCID* d, int depth)
473 {
474     INDENT(depth);
475     CAT("<Dim");
476     makeFQN(d,out->tmp2);
477     printXMLAttributeName(out, "name", ncbytescontents(out->tmp2));
478     CAT("/>");
479     return NC_NOERR;
480 }
481 
482 static int
printValue(NC4printer * out,NCID * basetype,void * value,int depth)483 printValue(NC4printer* out, NCID* basetype, void* value, int depth)
484 {
485     int ret;
486     if(basetype->id > NC_MAX_ATOMIC_TYPE && basetype->usertype.kind == NC_ENUM) {
487 	basetype = basetype->base;
488     }
489     if((ret=getPrintValue(out->tmp2,basetype,value))) FAIL;
490     INDENT(depth);
491     CAT("<Value");
492     printXMLAttributeString(out,"value",ncbytescontents(out->tmp2));
493     CAT("/>\n");
494     return ret;
495 }
496 
497 /*************************************************/
498 /* Misc. Static Utilities */
499 
500 /* Make public to allow use elsewhere */
501 
502 static char hexchars[16] = "0123456789abcdef";
503 
504 static int
getPrintValue(NCbytes * out,NCID * basetype,void * value)505 getPrintValue(NCbytes* out, NCID* basetype, void* value)
506 {
507     int ret = NC_NOERR;
508     char buf[256];
509     ncbytesclear(out);
510     switch (basetype->id) {
511     case NC_CHAR:
512         snprintf(buf,sizeof(buf),"'%c'",*(char*)value);
513         ncbytescat(out,buf);
514         break;
515     case NC_BYTE:
516         snprintf(buf,sizeof(buf),"%d",*(char*)value);
517         ncbytescat(out,buf);
518         break;
519     case NC_UBYTE:
520         snprintf(buf,sizeof(buf),"%u",*(unsigned char*)value);
521         ncbytescat(out,buf);
522         break;
523     case NC_SHORT:
524         snprintf(buf,sizeof(buf),"%d",*(short*)value);
525         ncbytescat(out,buf);
526         break;
527     case NC_USHORT:
528         snprintf(buf,sizeof(buf),"%u",*(unsigned short*)value);
529         ncbytescat(out,buf);
530         break;
531     case NC_INT:
532         snprintf(buf,sizeof(buf),"%d",*(int*)value);
533         ncbytescat(out,buf);
534         break;
535     case NC_UINT:
536         snprintf(buf,sizeof(buf),"%u",*(unsigned int*)value);
537         ncbytescat(out,buf);
538         break;
539     case NC_INT64:
540         snprintf(buf,sizeof(buf),"%lld",*(long long*)value);
541         ncbytescat(out,buf);
542         break;
543     case NC_UINT64:
544         snprintf(buf,sizeof(buf),"%llu",*(unsigned long long*)value);
545         ncbytescat(out,buf);
546         break;
547     case NC_FLOAT:
548         snprintf(buf,sizeof(buf),"%g",*(float*)value);
549         ncbytescat(out,buf);
550         break;
551     case NC_DOUBLE:
552         snprintf(buf,sizeof(buf),"%g",*(double*)value);
553         ncbytescat(out,buf);
554         break;
555     case NC_STRING: {
556         char* s = *(char**)value;
557 	printString(out,s,0);
558         } break;
559     case NC_OPAQUE: {
560         unsigned char* s = *(unsigned char**)value;
561 	printOpaque(out,s,basetype->size,1);
562         } break;
563     case NC_ENUM:
564         /* use true basetype */
565         ret = getPrintValue(out,basetype->base,value);
566         break;
567     default:
568         break;
569     }
570     return ret;
571 }
572 
573 static void
getAtomicTypeName(nc_type base,char * name)574 getAtomicTypeName(nc_type base, char* name)
575 {
576     const char* tname = NULL;
577     switch (base) {
578     case NC_BYTE: tname = "Byte"; break;
579     case NC_UBYTE: tname = "UByte"; break;
580     case NC_SHORT: tname = "Short"; break;
581     case NC_USHORT: tname = "UShort"; break;
582     case NC_INT: tname = "Int"; break;
583     case NC_UINT: tname = "UInt"; break;
584     case NC_FLOAT: tname = "Float"; break;
585     case NC_DOUBLE: tname = "Double"; break;
586     case NC_INT64: tname = "Int64"; break;
587     case NC_UINT64: tname = "UInt64"; break;
588     case NC_STRING: tname = "String"; break;
589     default: tname = ""; break;
590     }
591     strncpy(name,tname,strlen(tname)+1);
592 }
593 
594 static void
indent(NC4printer * out,int depth)595 indent(NC4printer* out, int depth)
596 {
597     while(depth-- >= 0) ncbytescat(out->out,"  ");
598 }
599 
600 static unsigned long long
getNumericValue(union NUMVALUE numvalue,nc_type base)601 getNumericValue(union NUMVALUE numvalue, nc_type base)
602 {
603     switch (base) {
604     case NC_CHAR: case NC_BYTE: return numvalue.i8[0];
605     case NC_SHORT: case NC_USHORT: return numvalue.i16[0];
606     case NC_INT: case NC_UINT: return numvalue.i32[0];
607     case NC_INT64: case NC_UINT64: return numvalue.i64[0];
608     }
609     return NC_MAX_UINT64;
610 }
611 
612 static NCID*
findType(NC4printer * out,nc_type t)613 findType(NC4printer* out, nc_type t)
614 {
615     int len = nclistlength(out->types);
616     if(t >= len)
617 	abort();
618     return (NCID*)nclistget(out->types,t);
619 }
620 
621 static NCID*
findDim(NC4printer * out,int dimid)622 findDim(NC4printer* out, int dimid)
623 {
624     if(nclistlength(out->dims) <= dimid) abort();
625     return (NCID*)nclistget(out->dims,dimid);
626 }
627 
628 static void
makeFQN(NCID * id,NCbytes * path)629 makeFQN(NCID* id, NCbytes* path)
630 {
631     NCID* g = id;
632     ncbytesclear(path);
633     if(id->sort != GROUP)
634         g = id->parent;
635     if(!g->group.isroot)
636         fqnWalk(g,path);
637     ncbytesappend(path,'/');
638     if(id->sort != GROUP)
639         ncbytescat(path,id->name);
640     ncbytesnull(path);
641 }
642 
643 static void
fqnWalk(NCID * grp,NCbytes * path)644 fqnWalk(NCID* grp, NCbytes* path)
645 {
646     if(grp->id != 0) {
647         NCID* parent = grp->parent;
648         fqnWalk(parent,path);
649         ncbytesappend(path,'/');
650         ncbytescat(path,parent->name);
651     }
652 }
653 
654 static void
record(NC4printer * out,NCID * node)655 record(NC4printer* out, NCID* node)
656 {
657     switch (node->sort) {
658     case DIM:
659 	if(nclistlength(out->dims) <= node->id) {
660             nclistsetalloc(out->dims,node->id+1);
661 	    nclistsetlength(out->dims,node->id+1);
662 	}
663         nclistset(out->dims,node->id,node);
664         break;
665     case ATOMTYPE:
666     case USERTYPE:
667 	if(nclistlength(out->types) <= node->id) {
668             nclistsetalloc(out->types,node->id+1);
669 	    nclistsetlength(out->types,node->id+1);
670 	}
671         nclistset(out->types,node->id,node);
672         break;
673     default: break;
674     }
675 }
676 
677 static void
track(NC4printer * out,NCID * node)678 track(NC4printer* out, NCID* node)
679 {
680     if(out == NULL || node == NULL || out->allnodes == NULL)
681 	abort();
682 #ifdef DEBUG
683     fprintf(stderr,"track: node=%lx\n",(unsigned long)node);
684 #endif
685     nclistpush(out->allnodes,node);
686 #ifdef DEBUG
687     fprintf(stderr,"track: |allnodes|=%ld\n",nclistlength(out->allnodes));
688     fflush(stderr);
689 #endif
690 }
691 
692 static void
entityEscape(NCbytes * escaped,const char * s)693 entityEscape(NCbytes* escaped, const char* s)
694 {
695     const char* p;
696     ncbytesclear(escaped);
697     for(p=s;*p;p++) {
698 	int c = *p;
699 	switch (c) {
700 	case '&':  ncbytescat(escaped,"&amp;"); break;
701 	case '<':  ncbytescat(escaped,"&lt;"); break;
702 	case '>':  ncbytescat(escaped,"&gt;"); break;
703 	case '"':  ncbytescat(escaped,"&quot;"); break;
704 	case '\'': ncbytescat(escaped,"&apos;"); break;
705         default:   ncbytesappend(escaped,(c)); break;
706         }
707 	ncbytesnull(escaped);
708     }
709 }
710 
711 static int
buildAtomicTypes(NC4printer * out,NCID * root)712 buildAtomicTypes(NC4printer* out, NCID* root)
713 {
714     int ret = NC_NOERR;
715     nc_type tid;
716     char name[NC_MAX_NAME+1];
717     size_t size;
718     for(tid=NC_NAT+1;tid<=NC_MAX_ATOMIC_TYPE;tid++) {
719 	if((ret=nc_inq_type(root->id,tid,NULL,&size))) FAIL;
720         getAtomicTypeName(tid,name);
721         MAKEID(type,ATOMTYPE,root,tid);
722 	    SETNAME(type,name);
723 	    type->size = size;
724 	    type->usertype.kind = tid;
725         record(out, type);
726     }
727     return ret;
728 }
729 
730 static void
printString(NCbytes * out,const char * s,int quotes)731 printString(NCbytes* out, const char* s, int quotes)
732 {
733     const char* p;
734     if(quotes) ncbytesappend(out,'"');
735     if(s == NULL) s = "";
736     for(p=s;*p;p++) {
737 	int c = *p;
738 	if(c == '\\') ncbytescat(out,"\\\\");
739         else if(c == '"') ncbytescat(out,"\\\"");
740         else ncbytesappend(out,c);
741     }
742     if(quotes) ncbytesappend(out,'"');
743     ncbytesnull(out);
744 }
745 
746 static void
printOpaque(NCbytes * out,const unsigned char * s,size_t len,int leadx)747 printOpaque(NCbytes* out, const unsigned char* s, size_t len, int leadx)
748 {
749     int i;
750     char digit;
751     if(s == NULL) {s = (unsigned char*)""; len = 1;}
752     if(leadx) ncbytescat(out,"0x");
753     for(i=0;i<len;i++) {
754         unsigned int c = s[i];
755         digit = hexchars[(c>>4) & 0xF];
756         ncbytesappend(out,digit);
757         digit = hexchars[c & 0xF];
758         ncbytesappend(out,digit);
759     }
760     ncbytesnull(out);
761 }
762 
763 static void*
computeOffset(NCID * base,void * values,size_t index)764 computeOffset(NCID* base, void* values, size_t index)
765 {
766     unsigned char* p = (unsigned char*)values; /*so we can do arithmetic */
767     return (void*)(p + ((base->size)*index));
768 }
769 
770 static int
readAttributeValues(NCID * attr,void ** valuesp)771 readAttributeValues(NCID* attr, void** valuesp)
772 {
773     int ret;
774     void* values = NULL;
775     NCID* var = attr->parent;
776     NCID* base = attr->base;
777     size_t len;
778 
779     len = base->size * attr->size;
780     values = malloc(len);
781     if(values == NULL) return NC_ENOMEM;
782     if((ret=nc_get_att(GROUPOF(var),var->id,attr->name,values))) FAIL;
783     if(valuesp) *valuesp = values;
784     return ret;
785 }
786 
787