1/*********************************************************************
2 *   Copyright 2009, UCAR/Unidata
3 *   See netcdf/COPYRIGHT file for copying and redistribution conditions.
4 *********************************************************************/
5
6#include "includes.h"
7#include "odom.h"
8
9/******************************************************/
10/* Code for generating char variables etc; mostly
11   language independent */
12/******************************************************/
13
14/*Forward*/
15static size_t gen_charconstant(NCConstant*, Bytebuffer*, int fillchar);
16static int getfillchar(Datalistfillsrc);
17static void gen_leafchararray(Dimset*,int,Datalist*,Bytebuffer*, int);
18#if 0
19static void gen_chararrayr(Dimset*,int,int,Bytebuffer*,Datalist*,int,int,int);
20#endif
21static NCConstantmakeconst(int lineno, int len, char* str);
22
23/*
24Matching strings to char variables, attributes, and vlen
25constants is challenging because it is desirable to mimic
26the original ncgen(3). The "algorithms" used there have no
27simple characterization (such as "abc" == {'a','b','c'}).
28So, this rather ugly code is kept in this file
29and a variety of heuristics are used to mimic ncgen3.
30
31The core algorithm is as follows.
321. Assume we have a set of dimensions D1..Dn.
33   Any of the Di may be unlimited,
34   but it is assumed that the sizes of the Di are all known.
352. Given a sequence of string or character constants
36   C1..Cm, our goal is to construct a single string
37   whose length is the cross product of D1 thru Dn.
383. For purposes of this algorithm, character constants
39   are treated as strings of size 1.
404. Construct Dx = cross product of D1 thru D(n-1).
415. For each constant Ci, add fill characters, if necessary,
42   so that its length is a multiple of Dn.
436. Concatenate the modified C1..Cm to produce string S.
447. Add fill characters to S to make its size be a multiple of
45   Dn.
468. If S is longer than the Dx * Dn, then truncate
47   and generate a warning.
48
49Two special cases:
501. character vlen: char(*) vlen_t.
51    For this case, we simply concat all the elements.
522. character attribute.
53    For this case, we simply concat all the elements.
54*/
55
56void
57gen_chararray(Dimsetdimset, int dimindexDatalistdataBytebuffercharbufDatalistfillsrc)
58{
59    int fillchar = getfillchar(fillsrc);
60    int rank = rankfor(dimset);
61    int firstunlim = findunlimited(dimset,0);
62    int nunlim = countunlimited(dimset);
63    int nc3unlim = (nunlim <= 1 && (firstunlim == 0 || firstunlim == rank)); /* netcdf-3 case of at most 1 unlim in 0th dimension */
64
65    /* Case: netcdf3 case */
66    if(nc3unlim) {
67 gen_leafchararray(dimset,0,data,charbuf,fillchar);
68 return;
69    }
70
71    /* else generate should have done all the hard work */
72    gen_leafchararray(dimset,dimindex,data,charbuf,fillchar);
73}
74
75#if 0
76/* Recursive helper */
77static void
78gen_chararrayr(Dimsetdimset, int dimindex,
79               BytebufferdatabufDatalistdata, int fillchar,
80        int unitsize, int expectedsize)
81{
82    int i;
83    size_t dimsize = declsizefor(dimset,dimindex);
84    int rank = dimset->ndims;
85    int firstunlim = findunlimited(dimset,0);
86    int lastunlimited = findlastunlimited(dimset);
87    int nextunlimited = findunlimited(dimset,dimindex+1);
88    int islastgroup = (lastunlimited == rank || dimindex >= lastunlimited || dimindex == rank-1);
89    Odometersubodom = NULL;
90
91    ASSERT(rank > 0);
92    ASSERT((islastgroup));
93
94    /* we should be at a list of simple constants */
95    for(i=0;i<data->length;i++) {
96        NCConstantc = datalistith(data,i);
97        ASSERT(!islistconst(c));
98        if(isstringable(c->nctype)) {
99            int j;
100            size_t constsize;
101            constsize = gen_charconstant(c,databuf,fillchar);
102            if(constsize % unitsize > 0) {
103                size_t padsize = unitsize - (constsize % unitsize);
104                for(j=0;j<padsize;j++) bbAppend(databuf,fillchar);
105            }
106        } else {
107            semwarn(constline(c),
108                   "Encountered non-string and non-char constant in datalist; ignored");
109        }
110    }/* for */
111
112    /* If |databuf| > expectedsize, complain: exception is zero length */
113    if(bbLength(databuf) == 0 && expectedsize == 1) {
114        /* this is okay */
115    } else if(bbLength(databuf) > expectedsize) {
116        semwarn(data->data[0].lineno,"character data list too long; expected %d character constant, found %d: ",expectedsize,bbLength(databuf));
117    } else {
118        size_t bufsize = bbLength(databuf);
119        /* Pad to size dimproduct size */
120        if(bufsize % expectedsize > 0) {
121            size_t padsize = expectedsize - (bufsize % expectedsize);
122            for(i=0;i<padsize;i++) bbAppend(databuf,fillchar);
123        }
124    }
125}
126#endif
127
128void
129gen_charattr(DatalistdataBytebufferdatabuf)
130{
131    gen_charvlen(data,databuf);
132}
133
134void
135gen_charvlen(DatalistdataBytebufferdatabuf)
136{
137    int i;
138    NCConstantc;
139
140    ASSERT(bbLength(databuf) == 0);
141
142    for(i=0;i<data->length;i++) {
143        c = datalistith(data,i);
144        if(isstringable(c->nctype)) {
145            (void)gen_charconstant(c,databuf,NC_FILL_CHAR);
146        } else {
147            semerror(constline(c),
148                     "Encountered non-string and non-char constant in datalist");
149            return;
150        }
151    }
152}
153
154static size_t
155gen_charconstant(NCConstantconBytebufferdatabuf, int fillchar)
156{
157    /* Following cases should be consistent with isstringable */
158    size_t constsize = 1;
159    switch (con->nctype) {
160    case NC_CHAR:
161        bbAppend(databuf,con->value.charv);
162        break;
163    case NC_BYTE:
164        bbAppend(databuf,con->value.int8v);
165        break;
166    case NC_UBYTE:
167        bbAppend(databuf,con->value.uint8v);
168        break;
169    case NC_STRING:
170        constsize = con->value.stringv.len;
171        bbAppendn(databuf,con->value.stringv.stringv,
172                         con->value.stringv.len);
173        bbNull(databuf);
174        break;
175    case NC_FILL:
176        bbAppend(databuf,fillchar);
177        break;
178    default:
179        PANIC("unexpected constant type");
180    }
181    return constsize;
182}
183
184static int
185getfillchar(Datalistfillsrc)
186{
187    /* Determine the fill char */
188    int fillchar = 0;
189    if(fillsrc != NULL && fillsrc->length > 0) {
190        NCConstantccon = fillsrc->data;
191        if(ccon->nctype == NC_CHAR) {
192            fillchar = ccon->value.charv;
193        } else if(ccon->nctype == NC_STRING) {
194            if(ccon->value.stringv.len > 0) {
195                fillchar = ccon->value.stringv.stringv[0];
196            }
197        }
198    }
199    if(fillchar == 0) fillchar = NC_FILL_CHAR; /* default */
200    return fillchar;
201}
202
203/* I think there is a flaw in the ncgen manual
204   about handling something like this:
205    dimensions:
206      n = 8 ;
207    variables:
208      char cdata2(n) ;
209    data:
210      cdata2 = '\000','\001','\002','\177','\200','\201','\376','\377';
211    The rules would say that each of the 8 char constants must
212    be padded to length 8. I think this is only true if the dimension
213    is unlimited, and even then, I am not sure.
214 */
215
216static void
217gen_leafchararray(Dimsetdimset, int dimindexDatalistdata,
218                   Bytebuffercharbuf, int fillchar)
219{
220    int i;
221    size_t expectedsize,xproduct,unitsize;
222    int rank = rankfor(dimset);
223
224    ASSERT(bbLength(charbuf) == 0);
225    ASSERT((findlastunlimited(dimset) == rank
226            || findlastunlimited(dimset) == dimindex));
227
228    /*
229    There are a number of special cases that must be
230    considered, mostly driven by the need to keep consistent
231    with ncgen3.  These cases are driven by the number of
232    dimensions, which dimensions are unlimited (if any), etc.
233
234    The general rule is based on the size of the last
235    dimension, we compute the required size (after padding)
236    of each string constant. Expected size is then the size
237    of concat of the string constants after padding.
238
239    There is another special case used for back compatibility with ncgen3.
240    In the datalist, all sequences of character constants (i.e. 'X')
241    are concatenated into a single string; the result, however is not
242    concatenated with any trailing or leading string (with double quotes).
243    */
244
245    /* Rebuild the datalist to merge 'x' constants */
246    {
247 int i,cccount = 0;
248 /* Do initial walk */
249 for(i=0;i<datalistlen(data);i++) {
250     NCConstantcon = datalistith(data,i);
251     if(consttype(con) == NC_CHAR || consttype(con) == NC_BYTE) {
252 cccount++;
253     }
254 }
255 if(cccount > 1) {
256     char* accum = (char*)malloc(cccount+1);
257     int len = 0;
258     Datalistnewlist = builddatalist(datalistlen(data));
259     int lineno = 0;
260            NCConstantcon;
261     for(i=0;i<datalistlen(data);i++) {
262         con = datalistith(data,i);
263         if(consttype(con) == NC_CHAR || consttype(con) == NC_BYTE) {
264     if(len == 0)
265 lineno = constline(con);
266     accum[len] = con->value.charv;
267     len++;
268 } else {
269     if(len > 0) {
270 con = makeconst(lineno,len,accum);
271 len = 0;
272 lineno = 0;
273     }
274     dlappend(newlist,con);
275 }
276     }
277     /* deal with any unclosed strings */
278     if(len > 0) {
279 con = makeconst(lineno,len,accum);
280 len = 0;
281 lineno = 0;
282         dlappend(newlist,con);
283     }
284     free(accum);
285     data = newlist;
286 }
287    }
288
289    /* Compute crossproduct up to (but not including) the last dimension */
290    xproduct = crossproduct(dimset,dimindex,rank-1);
291
292    /* Start casing it out */
293    if(rank == 0) {
294        unitsize = 1;
295        expectedsize = (xproduct * unitsize);
296    } else if(rank == 1) {
297 unitsize = 1;
298        expectedsize = (xproduct * declsizefor(dimset,rank-1));
299    } else if(isunlimited(dimset,rank-1)) {/* last dimension is unlimited */
300        unitsize = 1;
301        expectedsize = (xproduct*declsizefor(dimset,rank-1));
302    } else { /* rank > 0 && last dim is not unlimited */
303 unitsize =  declsizefor(dimset,rank-1);
304        expectedsize = (xproduct * unitsize);
305    }
306
307    for(i=0;i<data->length;i++) {
308        NCConstantc = datalistith(data,i);
309        ASSERT(!islistconst(c));
310        if(isstringable(c->nctype)) {
311            int j;
312            size_t constsize;
313            constsize = gen_charconstant(c,charbuf,fillchar);
314            if(constsize == 0 || constsize % unitsize > 0) {
315                size_t padsize = unitsize - (constsize % unitsize);
316                for(j=0;j<padsize;j++) bbAppend(charbuf,fillchar);
317            }
318        } else {
319            semwarn(constline(c),"Encountered non-string and non-char constant in datalist; ignored");
320        }
321    }
322    /* If |databuf| > expectedsize, complain: exception is zero length */
323    if(bbLength(charbuf) == 0 && expectedsize == 1) {
324        /* this is okay */
325    } else if(bbLength(charbuf) > expectedsize) {
326        semwarn(data->data[0].lineno,"character data list too long; expected %d character constant, found %d: ",expectedsize,bbLength(charbuf));
327    } else {
328        size_t bufsize = bbLength(charbuf);
329        /* Pad to size dimproduct size */
330        if(bufsize % expectedsize > 0) {
331            size_t padsize = expectedsize - (bufsize % expectedsize);
332            for(i=0;i<padsize;i++) bbAppend(charbuf,fillchar);
333        }
334    }
335}
336
337/* Create a new string constant */
338static NCConstant*
339makeconst(int lineno, int len, char* str)
340{
341    NCConstantcon = (NCConstant*)malloc(sizeof(NCConstant));
342    con->nctype = NC_STRING;
343    con->lineno = lineno;
344    con->filled = 0;
345    con->value.stringv.len = len;
346    /* We cannot use strdup because str might have embedded nuls */
347    con->value.stringv.stringv = (char*)malloc(len+1);
348    memcpy((void*)con->value.stringv.stringv,(void*)str,len);
349    con->value.stringv.stringv[len] = '\0';
350    return con;
351}


HyperKWIC - Version 7.20DA executed at 11:37 on 27 Oct 2017 | Polyhedron Solutions - INTERNAL USE | COMMERCIAL (Any O/S) SN 4AKIed