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,"&"); break;
701 case '<': ncbytescat(escaped,"<"); break;
702 case '>': ncbytescat(escaped,">"); break;
703 case '"': ncbytescat(escaped,"""); break;
704 case '\'': ncbytescat(escaped,"'"); 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