netcdf-c/ncgen/cmldata.c
Dennis Heimbigner 6901206927 Regularize the semantics of mkstemp.
re: https://github.com/Unidata/netcdf-c/issues/1827

The issue is partly resolved by this PR. The proximate problem appears to be that the semantics of mkstemp in **nix is different than the semantics of _mktemp_s in Windows. I had thought they were the same but that is incorrect. The _mktemp_s function will only produce 26 different files and so the netcdf temp file code will fail after about that many iterations.

So, to solve this, I created my own version of mkstemp for windows that uses a random number generator. This appears to solve the reported issue.  I also added the testcase ncdap_test/test_manyurls but made it conditional on --enable-dap-long-tests because it is very slow.

I did note that the provided test program now fails after some 800 iterations with a libcurl error claiming it cannot resolve the host name. My belief is that the library is just running out of resources at this point: too many open curl handles or some such. I doubt if this failure is fixable.

So bottom line is that it is really important to do nc_close when you are finished with a file.

Misc. Other Changes:

1. I took the opportunity to clean up some bad string hacks in the code. Specifically
    * change all uses of strncat to strlcat
    * remove old string hacks: occoncat and occopycat
2. Add heck to see if test.opendap.org is running and if not, then skip test
3. Make CYGWIN use TEMP environment variable
2021-05-14 11:33:03 -06:00

496 lines
13 KiB
C

/*! \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,"<compound type=\"");
bbCat(databuf,xtypename(tsym));
bbCat(databuf,"\">\n");
for(i=0;i<listlength(tsym->subnodes);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,"</compound>\n");
srcpop(datasrc);
} break;
case NC_VLEN:
if(!issublist(datasrc)) {
semerror(srcline(datasrc),"Vlen data must be enclosed in {..}");
}
srcpush(datasrc);
bbCat(databuf,"<vlen type=\"");
bbCat(databuf,xtypename(tsym));
bbCat(databuf,"\">\n");
gencml_data(tsym->typ.basetype,datasrc,NULL,databuf);
bbCat(databuf,"\n</vlen>\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,"<field name=");
bbCat(databuf,xname(tsym));
bbCat(databuf," type=\"");
bbCat(databuf,xtypename(tsym->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,"</field>\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;i<size;i++) {
if(lastdim) {
if(!isprimplus(typecode) && *commap==SECOND)
bbCat(databuf,"\n");
gencml_data(basetype,src,NULL,databuf);
setcomma(*commap);
} else { /* !lastdim*/
gencml_fieldarray(basetype,src,dimset,index+1,databuf,commap);
}
}
}
}
static void
gencml_primdata(Symbol* tsym, Datasrc* src, Datalist* fillsrc,
Bytebuffer* databuf)
{
Constant target, *prim;
prim = srcnext(src);
if(prim == NULL || prim->nctype == 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,"<unlimited>\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,"</unlimited>");
if(!firstdim) srcpop(src);
*commap = savecomma;
} else { /* !isunlimited*/
for(i=0;i<declsize;i++) {
con = srcpeek(src);
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 { /* ! lastdim*/
(void)gencml_arraydatar(vsym,src,odom,index+1,commap,databuf);
}
odom->dims[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 &#00; 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,"&#00;");
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*/