#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) { strcat(bstring,"&#"); strncat(bstring,p,2); strcat(bstring,";"); 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*/