netcdf-c/ncgen/gencml.c
Sean McBride dfc2ac7296 Replaced trivial uses of sprintf with snprintf
In all these cases the size of the buffer can be computed with sizeof.
2023-12-08 13:30:38 -05:00

545 lines
14 KiB
C

/*********************************************************************
* Copyright 2018, UCAR/Unidata
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
* $Header: /upc/share/CVS/netcdf-3/ncgen/gencml.c,v 1.5 2010/04/04 19:39:46 dmh Exp $
*********************************************************************/
#include "includes.h"
#include <ctype.h> /* for isprint() */
#ifdef ENABLE_CML
#undef TRACE
extern List* vlenconstants; /* List<Constant*>;*/
/* Forward */
/*
static const char* ncstype(nc_type);
static const char* nctype(nc_type);
static const char* ncctype(nc_type);
static const char* groupncid(Symbol*);
static const char* typencid(Symbol*);
static const char* varncid(Symbol*);
static const char* dimncid(Symbol*);
*/
static void gencml_group(Symbol* group);
static char* ncxtype(nc_type type);
#ifdef USE_NETCDF4
static void definextype(Symbol*);
static char* xprefixed(List* prefix, char* suffix, char* separator);
static void definespecialattributes(Symbol* vsym);
#endif
static void defineattribute(Symbol* asym);
static void definevardata(Symbol*);
#ifdef USE_NETCDF4
static void xprint(Bytebuffer* buf);
#endif
static void xpartial(char* line);
static void xline(char* line);
static void xflush(void);
/*
Global Bytebuffer into which to store the C code output;
periodically dumped to file by xflush().
*/
Bytebuffer* xcode;
/*
* Generate C code for creating netCDF from in-memory structure.
*/
void
gencml_netcdf(void)
{
xcode = bbNew();
bbSetalloc(xcode,C_MAX_STMT);
const char *filename = rootgroup->file.filename;
/* Dump XML header */
xline("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
xline("<netcdf xmlns=\"https://www.unidata.ucar.edu/namespaces/netcdf/ncml-2.2\">");
xline("");
xline("<explicit/>");
xline("");
xflush();
/* Recursively dump all groups via the rootgroup */
gencml_group(rootgroup);
}
void
gencml_close(void)
{
xline("</netcdf>");
xflush();
}
static void
gencml_group(Symbol* group)
{
int idim, ivar, iatt;
int ndims, nvars, natts, ngatts, ngrps, ntyps;
#ifdef USE_NETCDF4
int igrp,ityp;
#endif
ndims = listlength(dimdefs);
nvars = listlength(vardefs);
natts = listlength(attdefs);
ngatts = listlength(gattdefs);
ngrps = listlength(grpdefs);
ntyps = listlength(typdefs);
#ifdef USE_NETCDF4
if(group != rootgroup) {
xline("");
xpartial("<group name=");
xpartial("\"");
xpartial(xname(group));
xline("\" >");
}
#endif
#ifdef USE_NETCDF4
/* Construct code to define types*/
if(ntyps > 0) {
xline("<!-- define types -->");
xline("<types>");
for(ityp = 0; ityp < ntyps; ityp++) {
Symbol* tsym = (Symbol*)listget(typdefs,ityp);
xline("");
if(tsym->subclass == NC_PRIM
|| tsym->subclass == NC_ARRAY) continue;/*no need to do these*/
definextype(tsym);
}
xline("");
xline("</types>");
}
xflush();
#endif
/* define dimensions from info in dims array */
if (ndims > 0) {
xline("");
xline("<!-- define dimensions -->");
for(idim = 0; idim < ndims; idim++) {
Symbol* dsym = (Symbol*)listget(dimdefs,idim);
unsigned long declsize = dsym->dim.size;
char* utag = "";
if(declsize == 0)
utag = " isUnlimited=\"true\"";
nprintf(stmt,sizeof(stmt),
"<dimension name=\"%s\" length=\"%lu\"%s />",
xname(dsym),declsize,utag);
xline(stmt);
}
}
xflush();
/* Define the global attributes*/
if(ngatts > 0) {
xline("");
xline("<!-- Define global attributes -->");
for(iatt = 0; iatt < ngatts; iatt++) {
Symbol* gasym = (Symbol*)listget(gattdefs,iatt);
if(gasym->att.var == NULL) /* => global */
defineattribute(gasym);
}
xline("");
}
xflush();
/* define variables from info in vars array */
if (nvars > 0) {
xline("");
xline("<!-- define variables -->");
for(ivar = 0; ivar < nvars; ivar++) {
Symbol* vsym = (Symbol*)listget(vardefs,ivar);
Dimset* dimset = &vsym->typ.dimset;
if(ivar > 0) xline("");
nprintf(stmt,sizeof(stmt),"<variable name=\"%s\"",xname(vsym));
xpartial(stmt);
if(dimset->ndims > 0) {
xpartial(" shape=\"");
for(idim = 0; idim < dimset->ndims; idim++) {
Symbol* dsym = dimset->dimsyms[idim];
if(idim > 0) xpartial(" ");
xpartial(xname(dsym));
}
xpartial("\"");
}
xpartial(" type=\"");
xpartial(xname(vsym->typ.basetype));
xline(" >");
#ifdef USE_NETCDF4
definespecialattributes(vsym);
#endif /*USE_NETCDF4*/
if(natts > 0) {
for(iatt = 0; iatt < natts; iatt++) {
Symbol* asym = (Symbol*)listget(attdefs,iatt);
if(asym->att.var == vsym) defineattribute(asym);
}
}
if(vsym->data != NULL) definevardata(vsym);
xline("</variable>");
}
}
xflush();
#ifdef USE_NETCDF4
/* Recursively define subgroups */
for(igrp=0;igrp<ngrps;igrp++) {
Symbol* gsym = (Symbol*)listget(grpdefs,igrp);
if(gsym->container != group) continue;
gencml_group(gsym);
}
#endif
#ifdef USE_NETCDF4
if(group != rootgroup) {
xline("</group>");
}
#endif
}
#ifdef USE_NETCDF4
static void
definespecialattributes(Symbol* vsym)
{
Specialdata* special = &vsym->var.special;
if(usingclassic) return;
if(special->flags & _STORAGE_FLAG) {
int storage = special->_Storage;
size_t* chunks = special->_ChunkSizes;
nprintf(stmt,sizeof(stmt),"<attribute name=\"_Chunksizes\" storage=\"%s\"",
(storage == NC_CONTIGUOUS?"contiguous":"chunked"));
xpartial(stmt);
if(special->nchunks != 0 && chunks != NULL) {
int i;
xpartial(" value=\"");
for(i=0;i<special->nchunks;i++) {
if(i > 0) xpartial(" ");
nprintf(stmt,sizeof(stmt),"%ld",special->_ChunkSizes[i]);
xpartial(stmt);
}
xpartial("\"");
}
xline(" />");
}
if(special->flags & _FLETCHER32_FLAG) {
nprintf(stmt,sizeof(stmt),
"<attribute name=\"_Fletcher32\" value=\"%s\" />",
(special->_Fletcher32?"true":"false"));
xline(stmt);
}
if(special->flags & (_DEFLATE_FLAG | _SHUFFLE_FLAG)) {
nprintf(stmt,sizeof(stmt),
"<attribute name=\"_DeflateLevel\" shuffle=\"%s\" deflate=\"%s\" value=\"%d\" />",
(special->_Shuffle==1?"shuffle":"noshuffle"),
(special->_DeflateLevel >= 0?"true":"false"),
(special->_DeflateLevel >= 0?special->_DeflateLevel:0));
xline(stmt);
}
if(special->flags & _ENDIAN_FLAG) {
nprintf(stmt,sizeof(stmt),
"<attribute name=\"_Endianness\" value=\"%s\" />",
(special->_Endianness == NC_ENDIAN_LITTLE?"little":"big"));
xline(stmt);
}
if(special->flags & _NOFILL_FLAG) {
nprintf(stmt,sizeof(stmt),
"<attribute name=\"_Fill\" value=\"%s\" />",
(special->_Fill?"fill":"nofill"));
xline(stmt);
}
}
static void
definextype(Symbol* tsym)
{
int i;
ASSERT(tsym->objectclass == NC_TYPE);
switch (tsym->subclass) {
case NC_PRIM: break; /* these are already taken care of*/
case NC_OPAQUE:
nprintf(stmt,sizeof(stmt),"<opaque name=\"%s\" size=\"%lu\" />",
xname(tsym),(unsigned long)tsym->typ.size);
xline(stmt);
break;
case NC_ENUM:
nprintf(stmt,sizeof(stmt),"<enum name=\"%s\" type=\"%s\" >",
xname(tsym),xtypename(tsym->typ.basetype));
xline(stmt);
stmt[0] = 0;
for(i=0;i<listlength(tsym->subnodes);i++) {
Symbol* econst = (Symbol*)listget(tsym->subnodes,i);
ASSERT(econst->subclass == NC_ECONST);
nprintf(stmt,sizeof(stmt),"<enumconst name=\"%s\" value=\"%s\" />",
xname(econst),
xconst(econst->typ.econst));
xline(stmt);
}
xline("</enum>");
break;
case NC_VLEN:
nprintf(stmt,sizeof(stmt),"<vlen name=\"%s\" type=\"%s\" />",
xname(tsym),xtypename(tsym));
xline(stmt);
break;
case NC_COMPOUND:
nprintf(stmt,sizeof(stmt),"<compound name=\"%s\" >",
xname(tsym));
xline(stmt);
for(i=0;i<listlength(tsym->subnodes);i++) {
Symbol* efield = (Symbol*)listget(tsym->subnodes,i);
ASSERT(efield->subclass == NC_FIELD);
nprintf(stmt,sizeof(stmt),"<field name=\"%s\"",xname(efield));
xpartial(stmt);
/* compute any dimension specification*/
if(efield->typ.dimset.ndims > 0) {
Bytebuffer* dimbuf = bbNew();
int j;
bbCat(dimbuf," shape=\"");
for(j=0;j<efield->typ.dimset.ndims;j++) {
char tmp[32];
Symbol* dim;
if(j > 0) bbCat(dimbuf," ");
dim = efield->typ.dimset.dimsyms[j];
ASSERT(dim->dim.isconstant);
snprintf(tmp,sizeof(tmp),"%lu",(unsigned long)dim->dim.size);
bbCat(dimbuf,tmp);
}
bbCat(dimbuf,"\"");
xprint(dimbuf);
bbFree(dimbuf);
}
nprintf(stmt,sizeof(stmt)," type=\"%s\" />",
xtypename(efield->typ.basetype));
xline(stmt);
}
xline("</compound>");
xflush();
break;
case NC_ARRAY:
/* ignore: this will be handled by def_var*/
break;
default: panic("definextype: unexpected type subclass: %d",tsym->subclass);
}
}
#endif /*USE_NETCDF4*/
static void
defineattribute(Symbol* asym)
{
unsigned long len;
Datalist* list;
Symbol* basetype = asym->typ.basetype;
Bytebuffer* code = NULL; /* capture other decls*/
list = asym->data;
if(list == NULL) PANIC("empty attribute list");
len = asym->att.count;
if(len == 0) PANIC("empty attribute list");
code = bbNew();
gencml_attrdata(asym,code);
/* Handle NC_CHAR specially */
if(basetype->typ.typecode == NC_CHAR) {
/* revise the length count */
len = bbLength(code);
if(len == 0) {bbAppend(code,'\0'); len++;}
xquotestring(code);
}
if(isprimplus(basetype->typ.typecode)) {
nprintf(stmt,sizeof(stmt),"<attribute name=\"%s\" type=\"%s\" value=\"%s\" />",
xname(asym),xtypename(asym->typ.basetype),
bbContents(code));
xline(stmt);
} else {
nprintf(stmt,sizeof(stmt),"<attribute name=\"%s\" type=\"%s\" >",
xname(asym),xtypename(asym->typ.basetype));
xline(stmt);
xpartial("<values>");
xpartial(bbContents(code));
xline("</values>");
xline("</attribute>");
}
xflush();
}
/* Note: no closure is used for CML */
static void
definevardata(Symbol* vsym)
{
Dimset* dimset = &vsym->typ.dimset;
int isscalar = (dimset->ndims == 0);
Bytebuffer* code;
nc_type typecode = vsym->typ.basetype->typ.typecode;
if(vsym->data == NULL) return;
code = bbNew();
/* Handle special cases first*/
if(isscalar) {
gencml_scalardata(vsym,code);
} else { /* Non-scalar*/
gencml_arraydata(vsym,code);
}
if(typecode == NC_STRING) {
xpartial("<values>");
xpartial(bbContents(code));
xline("</values>");
} else {
xline("<values>");
xpartial(bbContents(code));
if(isprimplus(vsym->typ.basetype->typ.typecode)) xline("");
xline("</values>");
}
bbFree(code);
}
/* return CML name for netCDF type, given type code */
static char *
ncxtype(nc_type type) /* netCDF type code */
{
switch (type) {
case NC_CHAR: return "char";
case NC_BYTE: return "byte";
case NC_SHORT: return "short";
case NC_INT: return "int";
case NC_FLOAT: return "float";
case NC_DOUBLE: return "double";
case NC_UBYTE: return "ubyte";
case NC_USHORT: return "ushort";
case NC_UINT: return "uint";
case NC_INT64: return "int64";
case NC_UINT64: return "uint64";
case NC_STRING: return "string";
default: PANIC("nctype: bad type code");
}
return NULL;
}
/* Compute the CML name for a given type*/
char*
xtypename(Symbol* tsym)
{
char* name;
ASSERT(tsym->objectclass == NC_TYPE);
if(tsym->subclass == NC_PRIM)
name = ncxtype(tsym->typ.typecode);
else
name = xname(tsym);
return name;
}
/* Compute the CML name for a given symbol*/
/* Cache in symbol->lname*/
char*
xname(Symbol* sym)
{
if(sym->lname == NULL) {
char* name = pooldup(sym->name);
#ifdef USE_NETCDF4
if(sym->subclass == NC_FIELD || sym->subclass == NC_ECONST) {
sym->lname = nulldup(codify(name));
} else
sym->lname = nulldup(codify(xprefixed(sym->prefix,name,"/")));
#else
sym->lname = nulldup(codify(name)); /* convert to usable form*/
#endif
}
return sym->lname;
}
#ifdef USE_NETCDF4
/* Result is pool alloc'd*/
static char*
xprefixed(List* prefix, char* suffix, char* separator)
{
int slen;
int plen;
int i;
char* result;
ASSERT(suffix != NULL);
plen = prefixlen(prefix);
if(prefix == NULL || plen == 0) return codify(suffix);
/* plen > 0*/
slen = 0;
for(i=0;i<plen;i++) {
Symbol* sym = (Symbol*)listget(prefix,i);
slen += (strlen(sym->name)+strlen(separator));
}
slen += strlen(suffix);
slen++; /* for null terminator*/
result = poolalloc(slen);
result[0] = '\0';
/* Leave off the root*/
i = (rootgroup == (Symbol*)listget(prefix,0))?1:0;
for(;i<plen;i++) {
Symbol* sym = (Symbol*)listget(prefix,i);
strcat(result,sym->name); /* append "<prefix[i]/>"*/
strcat(result,separator);
}
strcat(result,suffix); /* append "<suffix>"*/
return result;
}
#endif
/**************************************************/
/*
* Output a CML statement
*/
#ifdef USE_NETCDF4
static void
xprint(Bytebuffer* buf)
{
bbAppendn(xcode,bbContents(buf),bbLength(buf));
}
#endif
static void
xpartial(char* line)
{
bbCat(xcode,line);
}
static void
xline(char* line)
{
xpartial(line);
bbCat(xcode,"\n");
}
static void
xflush(void)
{
if(bbLength(xcode) > 0) {
bbAppend(xcode,'\0');
fputs(bbContents(xcode),stdout);
fflush(stdout);
bbClear(xcode);
}
}
#endif /*ENABLE_CML*/