/*! \file Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 University Corporation for Atmospheric Research/Unidata. See \ref copyright file for more info. */ #include "includes.h" #ifdef ENABLE_CML /**************************************************/ /* Code for generating CML data lists*/ /**************************************************/ typedef enum Comma {FIRST, SECOND, SUPPRESS } Comma; #define setcomma(comma) (comma)=((comma)!=SUPPRESS?SECOND:SUPPRESS) #define clrcomma(comma) (comma)=((comma)!=SUPPRESS?LEAD:SUPPRESS) /* Forward*/ static void gencml_data(Symbol* sym, Datasrc*, Datalist*, Bytebuffer*); static void gencml_primdata(Symbol*, Datasrc*, Datalist*, Bytebuffer*); static void gencml_fieldarray(Symbol*, Datasrc*, Dimset*, int, Bytebuffer*, Comma*); static void gencml_arraydatar(Symbol* basetype, Datasrc* src, Odometer* odom, int index, Comma*, Bytebuffer* code); static void gencml_stringarray(Symbol*, Datasrc*, Bytebuffer*); /* Datalist rules: see the rules on the man page */ /* Specialty wrappers for gencml_data */ void gencml_attrdata(Symbol* asym, Bytebuffer* databuf) { int first = 1; Datasrc* src; nc_type typecode = asym->typ.basetype->typ.typecode; if(asym->data == NULL) return; if(typecode == NC_CHAR) {gen_charattr(asym,databuf); return;} src = datalist2src(asym->data); if(typecode == NC_STRING) {gencml_stringarray(asym,src,databuf); return;} while(srcmore(src)) { if(!first) bbCat(databuf," "); else first = 0; gencml_data(asym->typ.basetype,src,NULL,databuf); } } void gencml_scalardata(Symbol* vsym, Bytebuffer* databuf) { Datasrc* src; if(vsym->data == NULL) return; src = datalist2src(vsym->data); gencml_data(vsym->typ.basetype,src, vsym->var.special._Fillvalue,databuf); if(srcmore(src)) { semerror(srcline(src),"Extra data at end of datalist"); } } static void gencml_data(Symbol* tsym, Datasrc* datasrc, Datalist* fillsrc, Bytebuffer* databuf) { int usecmpd; Constant* con = srcpeek(datasrc); if(con == NULL || con->nctype == NC_FILLVALUE) { srcnext(datasrc); gencml_fillvalue(tsym,fillsrc,datasrc,databuf); return; } switch (tsym->subclass) { case NC_ENUM: case NC_OPAQUE: case NC_PRIM: if(issublist(datasrc)) { semerror(srcline(datasrc),"Expected primitive found {..}"); } gencml_primdata(tsym,datasrc,fillsrc,databuf); break; case NC_COMPOUND: { int i; if(!issublist(datasrc)) { semerror(srcline(datasrc),"Compound data must be enclosed in {..}"); } srcpush(datasrc); bbCat(databuf,"\n"); for(i=0;isubnodes);i++) { Symbol* field = (Symbol*)listget(tsym->subnodes,i); if(!srcmore(datasrc)) { /* generate a fill value*/ Datalist* fillsrc = getfiller(tsym); gencml_data(field,datasrc,fillsrc,databuf); } else gencml_data(field,datasrc,NULL,databuf); } bbCat(databuf,"\n"); srcpop(datasrc); } break; case NC_VLEN: if(!issublist(datasrc)) { semerror(srcline(datasrc),"Vlen data must be enclosed in {..}"); } srcpush(datasrc); bbCat(databuf,"\n"); gencml_data(tsym->typ.basetype,datasrc,NULL,databuf); bbCat(databuf,"\n\n"); srcpop(datasrc); break; case NC_FIELD: /* enclose in braces if and only if field is an array */ usecmpd = (issublist(datasrc) && tsym->typ.dimset.ndims > 0); if(usecmpd) srcpush(datasrc); bbCat(databuf,"typ.basetype)); bbCat(databuf,"\""); if(isprimplus(tsym->typ.basetype->typ.typecode)) { bbCat(databuf," values=\""); } else { bbCat(databuf," >\n"); } if(tsym->typ.dimset.ndims > 0) { Comma comma = (isprimplus(tsym->typ.basetype->typ.typecode)?SUPPRESS:SECOND); gencml_fieldarray(tsym->typ.basetype,datasrc,&tsym->typ.dimset,0,databuf,&comma); } else { gencml_data(tsym->typ.basetype,datasrc,NULL,databuf); } if(isprimplus(tsym->typ.basetype->typ.typecode)) { bbCat(databuf,"\" >\n"); } else { bbCat(databuf,"\n"); } if(usecmpd) srcpop(datasrc); break; default: PANIC1("gencml_data: unexpected subclass %d",tsym->subclass); } } /* Used only for structure field arrays*/ static void gencml_fieldarray(Symbol* basetype, Datasrc* src, Dimset* dimset, int index, Bytebuffer* databuf, Comma* commap) { int i; Symbol* dim = dimset->dimsyms[index]; unsigned int size = dim->dim.size; int lastdim = (index == (dimset->ndims - 1)); /* last dimension*/ nc_type typecode = basetype->typ.typecode; if(typecode == NC_CHAR) { /* Collect the char field in a separate buffer */ Bytebuffer* fieldbuf = bbNew(); gen_charfield(src,dimset,index,fieldbuf); /* Add to the existing data buf as a single constant */ xquotestring(fieldbuf); switch (*commap) { case FIRST: break; case SUPPRESS: bbCat(databuf," "); break; case SECOND: bbCat(databuf,"\n"); break; } bbAppendn(databuf,bbContents(fieldbuf),bbLength(fieldbuf)); setcomma(*commap); bbFree(fieldbuf); } else if(typecode == NC_STRING) { Bytebuffer* fieldbuf = bbNew(); gencml_stringarray(basetype,src,fieldbuf); switch (*commap) { case FIRST: break; case SUPPRESS: bbCat(databuf," "); break; case SECOND: bbCat(databuf,"\n"); break; } bbAppendn(databuf,bbContents(fieldbuf),bbLength(fieldbuf)); setcomma(*commap); bbFree(fieldbuf); } else { ASSERT(size != 0); for(i=0;inctype == NC_FILLVALUE) { gencml_fillvalue(tsym,fillsrc,src,databuf); return; } target.nctype = tsym->typ.typecode; if(prim == NULL) { #ifdef GENFILL /* generate a fill value*/ nc_getfill(&target); /* fall thru*/ #else return; #endif } ASSERT(prim->nctype != NC_COMPOUND); switch (target.nctype) { case NC_ECONST: if(tsym->subclass != NC_ENUM) { semerror(prim->lineno,"Conversion to enum not supported (yet)"); nc_getfill(&target); } else { target.nctype = NC_ECONST; convert1(prim,&target); } break; case NC_OPAQUE: convert1(prim,&target); setprimlength(&target,tsym->typ.size*2); break; default: convert1(prim,&target); break; } bbCat(databuf,xconst(&target)); } void gencml_fillvalue(Symbol* tsym, Datalist* fillsrc, Datasrc* src, Bytebuffer* databuf) { Datalist* list = NULL; ASSERT(tsym->objectclass == NC_TYPE); list = fillsrc; if(list == NULL) list = getfiller(tsym); srcpushlist(src,list); gencml_data(tsym,src,NULL,databuf); srcpop(src); } /* Result is a pool string or a constant => do not free*/ char* xconst(Constant* ci) { char tmp[64]; tmp[0] = '\0'; switch (ci->nctype) { case NC_CHAR: { char* escaped; char* result; tmp[0] = ci->value.charv; tmp[1] = '\0'; escaped = xescapify(tmp,'\0',1); result = poolalloc(1+2+strlen(escaped)); strcat(result,escaped); return result; } break; case NC_BYTE: sprintf(tmp,"%hhd",ci->value.int8v); break; case NC_SHORT: sprintf(tmp,"%hd",ci->value.int16v); break; case NC_INT: sprintf(tmp,"%d",ci->value.int32v); break; case NC_FLOAT: sprintf(tmp,"%.8g",ci->value.floatv); break; case NC_DOUBLE: sprintf(tmp,"%.16g",ci->value.doublev); break; case NC_UBYTE: sprintf(tmp,"%hhu",ci->value.uint8v); break; case NC_USHORT: sprintf(tmp,"%hu",ci->value.uint16v); break; case NC_UINT: sprintf(tmp,"%uU",ci->value.uint32v); break; case NC_INT64: sprintf(tmp,"%lldLL",ci->value.int64v); break; case NC_UINT64: sprintf(tmp,"%lluLLU",ci->value.uint64v); break; case NC_ECONST: sprintf(tmp,"%s",cname(ci->value.enumv)); break; case NC_STRING: { char* escaped = xescapify(ci->value.stringv.stringv, '\0',ci->value.stringv.len); char* result = poolalloc(1+2+strlen(escaped)); strcat(result,escaped); return result; } break; case NC_OPAQUE: { char* bstring; char* p; int bslen; bslen=(4*ci->value.opaquev.len); bstring = poolalloc(bslen+2+1); p = ci->value.opaquev.stringv; while(*p) { strlcat(bstring,"&#",bslen+3); strlcat(bstring,p,bslen+3); strlcat(bstring,";",bslen+3); p += 2; } return bstring; } break; default: PANIC1("ncstype: bad type code: %d",ci->nctype); } return pooldup(tmp); /*except for NC_STRING and NC_OPAQUE*/ } void gencml_arraydata(Symbol* vsym, Bytebuffer* databuf) { Datasrc* src; Datalist* list; nc_type typecode = vsym->typ.basetype->typ.typecode; Comma comma = (isprimplus(typecode)?SUPPRESS:FIRST); if(vsym->data == NULL) return; if(typecode == NC_CHAR) { gen_chararray(vsym,NULL,databuf); return; } list = vsym->data; ASSERT(list->dimdata != NULL); src = datalist2src(list); if(typecode == NC_STRING) { gencml_stringarray(vsym,src,databuf); return; } gencml_arraydatar(vsym,src,list->dimdata,0,&comma,databuf); } static void gencml_arraydatar(Symbol* vsym, Datasrc* src, Odometer* odom, int index, Comma* commap, Bytebuffer* databuf) { int i; int rank = odom->rank; int lastdim = (index == (rank - 1)); /* last dimension*/ int firstdim = (index == 0); int declsize = odom->dims[index].declsize; int isunlimited = (declsize == 0); Symbol* basetype = vsym->typ.basetype; nc_type typecode = basetype->typ.typecode; Datalist* fillsrc = vsym->var.special._Fillvalue; Constant* con; ASSERT(index >= 0 && index < rank); odom->dims[index].index = 0; /* reset*/ if(isunlimited) { Constant* con; Comma savecomma = *commap; *commap = (isprimplus(typecode)?SUPPRESS:FIRST); if(!firstdim) { if(!issublist(src)) { semerror(srcline(src),"Unlimited data must be enclosed in {..}"); return; } srcpush(src); /* enter the unlimited data */ } bbCat(databuf,"\n"); while((con=srcpeek(src))!=NULL) { if(lastdim) { switch (*commap) { case FIRST: break; case SECOND: bbCat(databuf,"\n"); break; case SUPPRESS: bbCat(databuf," "); break; } gencml_data(basetype,src,fillsrc,databuf); setcomma(*commap); } else { gencml_arraydatar(vsym,src,odom,index+1,commap,databuf); } odom->dims[index].index++; } odom->dims[index].datasize = odom->dims[index].index; bbCat(databuf,""); if(!firstdim) srcpop(src); *commap = savecomma; } else { /* !isunlimited*/ for(i=0;idims[index].index++; } } } void xquotestring(Bytebuffer* databuf) { char* escaped = xescapify(bbContents(databuf),'"',bbLength(databuf)); bbClear(databuf); bbCat(databuf,escaped); } /* NcML wants sequences of strings to be encoded by using some separator character. In this experimental prototype, we use � as the separator. This code is a modified version of gen_charattri code in genchar.c. */ static void gencml_stringarray(Symbol* sym, Datasrc* src, Bytebuffer* databuf) { Constant* con; char* xescaped; while((con=srcnext(src))) { bbCat(databuf,"�"); switch (con->nctype) { case NC_STRING: xescaped = xescapify(con->value.stringv.stringv,'\0', con->value.stringv.len); bbCat(databuf,xescaped); break; case NC_FILLVALUE: { Datalist* fill = getfiller(sym); Datasrc* fillsrc = datalist2src(fill); gencml_stringarray(sym,fillsrc,databuf); } break; default: semerror(srcline(src), "Encountered non-string constant in attribute: %s", sym->name); return; } } } #endif /*ENABLE_CML*/