mirror of
https://github.com/Unidata/netcdf-c.git
synced 2024-12-21 08:39:46 +08:00
3db4f013bf
Specific changes: 1. Add dap4 code: libdap4 and dap4_test. Note that until the d4ts server problem is solved, dap4 is turned off. 2. Modify various files to support dap4 flags: configure.ac, Makefile.am, CMakeLists.txt, etc. 3. Add nc_test/test_common.sh. This centralizes the handling of the locations of various things in the build tree: e.g. where is ncgen.exe located. See nc_test/test_common.sh for details. 4. Modify .sh files to use test_common.sh 5. Obsolete separate oc2 by moving it to be part of netcdf-c. This means replacing code with netcdf-c equivalents. 5. Add --with-testserver to configure.ac to allow override of the servers to be used for --enable-dap-remote-tests. 6. There were multiple versions of nctypealignment code. Try to centralize in libdispatch/doffset.c and include/ncoffsets.h 7. Add a unit test for the ncuri code because of its complexity. 8. Move the findserver code out of libdispatch and into a separate, self contained program in ncdap_test and dap4_test. 9. Move the dispatch header files (nc{3,4}dispatch.h) to .../include because they are now shared by modules. 10. Revamp the handling of TOPSRCDIR and TOPBUILDDIR for shell scripts. 11. Make use of MREMAP if available 12. Misc. minor changes e.g. - #include <config.h> -> #include "config.h" - Add some no-install headers to /include - extern -> EXTERNL and vice versa as needed - misc header cleanup - clean up checking for misc. unix vs microsoft functions 13. Change copyright decls in some files to point to LICENSE file. 14. Add notes to RELEASENOTES.md
373 lines
9.3 KiB
C
373 lines
9.3 KiB
C
/* Copyright 2016, UCAR/Unidata.
|
|
See the LICENSE file for more information.
|
|
*/
|
|
|
|
#ifndef NCJSON_INC
|
|
#define NCJSON_INC 1
|
|
|
|
#define NCJ_DICT 1
|
|
#define NCJ_LIST 2
|
|
#define NCJ_WORD 3
|
|
#define NCJ_NUMBER 4
|
|
#define NCJ_BOOLEAN 5
|
|
|
|
/* Don't bother with unions */
|
|
typedef struct NCjson {
|
|
long sort;
|
|
char* word; /* string or (!boolean && !number) */
|
|
long long num; /* number || boolean (0=>false; !0=>true)*/
|
|
NCjson* list;
|
|
} NCjson;
|
|
|
|
#define NCJ_LBRACKET '['
|
|
#define NCJ_RBRACKET ']'
|
|
#define NCJ_LBRACE '{'
|
|
#define NCJ_RBRACE '}'
|
|
#define NCJ_COLON ':'
|
|
#define NCJ_COMMA ','
|
|
#define NCJ_QUOTE '"'
|
|
#define NCJ_TRUE "true"
|
|
#define NCJ_FALSE "false"
|
|
|
|
#define NCJ_WORD "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-$"
|
|
|
|
/*//////////////////////////////////////////////////*/
|
|
|
|
typedef struct NCJparser {
|
|
char* text;
|
|
char* pos;
|
|
char* yytext;
|
|
int errno;
|
|
struct {
|
|
char* yytext;
|
|
int token;
|
|
} pushback;
|
|
} NCJparser;
|
|
|
|
static int
|
|
NCjsonparse(char* text, NCjson** treep)
|
|
{
|
|
int status = NCJ_OK;
|
|
size_t len;
|
|
NCJparser parser = NULL;
|
|
NCjson* tree = NULL;
|
|
if(text == NULL) {status = NCJ_EINVAL; goto done;}
|
|
parser = calloc(1,sizeof(NCJparser));
|
|
if(parser == NULL) {status = NCJ_ENOMEM; goto done;}
|
|
len = strlen(text);
|
|
parser->text = (char*)malloc(len+1+1);
|
|
if(parser->text == NULL) {status = NCJ_ENOMEM; goto done;}
|
|
strcpy(parser->text,text);
|
|
parser->text[len] = '\0';
|
|
parser->text[len+1] = '\0';
|
|
tree = NCJparseR(parser);
|
|
done:
|
|
if(parser != NULL) {
|
|
nullfree(parser->text);
|
|
nullfree(parser->yytext);
|
|
free(parser);
|
|
}
|
|
if(status != NCJ_OK) {
|
|
if(tree != NULL) NCjsonfree(tree);
|
|
} else
|
|
if(treep) *treep = tree;
|
|
return status;
|
|
}
|
|
|
|
static int
|
|
NCJyytext(NCJparser* parser, char* start, ptrdiff_t pdlen)
|
|
{
|
|
size_t len = (size_t)pdlen;
|
|
if(parser->yytext == NULL)
|
|
parser->yytext = (char*)malloc(len+1);
|
|
else
|
|
parser->yytext = (char*) realloc(parser->yytext,len+1);
|
|
if(parser->yytext == NULL) return NCJ_ENOMEM;
|
|
memcpy(parser->yytext,start,len);
|
|
parser->yytext[len] = NCJ_NUL;
|
|
return NCJ_OK;
|
|
}
|
|
|
|
static void
|
|
NCJpushback(NCJparser* parser, int token)
|
|
{
|
|
parser->pushback.token = token;
|
|
parser->pushback.yytext = strdup(parser->yytext);
|
|
}
|
|
|
|
static int
|
|
NCJlex(NCJparser* parser)
|
|
{
|
|
int c;
|
|
int token = NCJ_NUL;
|
|
char* start;
|
|
char* next;
|
|
|
|
if(parser->pushback.token != NCJ_NOTOKEN) {
|
|
token = parser->pushback.token;
|
|
NCJyytext(parser,parser->pushback.yytext,strlen(parser->pushback.yytext));
|
|
nullfree(parser->pushback.yytext);
|
|
parser->pushback.yytext = NULL;
|
|
parser->pushback.token = NCJ_NOTOKEN;
|
|
return token;
|
|
}
|
|
|
|
c = *parser->pos;
|
|
if(c == NCJ_NUL) {
|
|
token = NCJ_NUL;
|
|
} else if(strchr(NCJ_WORD, c) != NULL) {
|
|
size_t len;
|
|
start = parser->pos;
|
|
next = start + 1;
|
|
for(;;) {
|
|
c = *parser->pos++;
|
|
if(strchr(NCJ_WHITESPACE,c) != NULL || c == NCJ_NUL) break;
|
|
last++;
|
|
}
|
|
if(!NCJyytext(parser,start,(next - start))) goto done;
|
|
token = NCJ_WORD;
|
|
} else if(c == NCJ_QUOTE) {
|
|
parser->pos++;
|
|
start = parser->pos;
|
|
next = start+1;
|
|
for(;;) {
|
|
c = *parser->pos++;
|
|
if(c == NCJ_QUOTE || c == NCJ_NUL) break;
|
|
last++;
|
|
}
|
|
if(c == NCJ_NUL) {
|
|
parser->errno = NCJ_ESTRING;
|
|
token = NCJ_ERR;
|
|
goto done;
|
|
}
|
|
if(!NCJyytext(parser,start,(next - start))) goto done;
|
|
token = NCJ_STRING;
|
|
} else { /* single char token */
|
|
token = *parser->pos++;
|
|
}
|
|
done:
|
|
if(parser->errno) token = NCJ_ERR;
|
|
return token;
|
|
}
|
|
|
|
/* Simple recursive descent */
|
|
|
|
static int
|
|
NCJparseR(NCJparser* parser, NCjson** listp)
|
|
{
|
|
int token = NCJ_ERR;
|
|
NCjson* list = NULL;
|
|
if((token = NCJlex(parser)) == NCJ_ERR) goto done;
|
|
switch (token) {
|
|
case NCJ_NUL;
|
|
break;
|
|
case NCJ_WORD:
|
|
NCJappend(NCJparseAtomic(parser,token),listp);
|
|
break;
|
|
case NCJ_LBRACE:
|
|
NCJappend(NCJparseMap(parser,locallist),listp);
|
|
break;
|
|
case NCJ_LBRACKET:
|
|
NCJappend(NCJparseArray(parser,NULL),)
|
|
case NCJ_STRING:
|
|
return NCJparseAtomic(parser,token);
|
|
default:
|
|
parser->errno = NCJ_EBADTOKEN;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static NCjson*
|
|
NCJparseAtomic(NCJparser* parser, int kind)
|
|
{
|
|
/* assert (kind == NCJ_WORD || kind = NCJ_QUOTE) */
|
|
NCjson* node;
|
|
if((node = NCJmakenode(parser)) == NULL)
|
|
{parser->errno = NCJ_ENOMEM; goto done;}
|
|
if(kind == NCJ_STRING)
|
|
node->sort = NCJ_WORD;
|
|
node->word = strdup(parser->yytext);
|
|
} else {
|
|
/* Try to convert to number or boolean; last resort is word */
|
|
size_t count = (last - start) + 1;
|
|
int nread = 0;
|
|
int ncvt = sscan(parser->yytext,
|
|
"%L",&node->num,&nread);
|
|
if(ncvt == 1 && nread == count) {
|
|
node->sort = NCJ_NUMBER;
|
|
} else if(strcasecmp(parser->yytext,NCJ_TRUE)==0) {
|
|
node->sort = NCJ_BOOLEAN;
|
|
node->num = 1;
|
|
} else if(strcasecmp(parser->yytext,NCJ_FALSE)==0) {
|
|
node->sort = NCJ_BOOLEAN;
|
|
node->num = 0;
|
|
} else {
|
|
node->word = strdup(parser->yytext);
|
|
node->sort = NCJ_WORD;
|
|
}
|
|
}
|
|
done:
|
|
return node;
|
|
}
|
|
|
|
static NCjson*
|
|
NCJparseArray(NCJparser* parser)
|
|
{
|
|
NCjson* head = NULL;
|
|
NCjson* last = NULL;
|
|
int token = NCJ_ERR;
|
|
#if 0
|
|
if((node = NCJmakenode(parser)) == NULL) goto done;
|
|
#endif
|
|
loop:
|
|
for(;;) {
|
|
if((token = NCJlex(parser)) == NCJ_ERR) goto done;
|
|
switch (token) {
|
|
case NCJ_NUL;
|
|
break;
|
|
case NCJ_RBRACKET:
|
|
break loop;
|
|
default:
|
|
NCJpushback(parser,token);
|
|
NCjson* o = NCJparseR(parser);
|
|
tokens.nextToken();
|
|
if(tokens.ttype == NCJ_EOF) break;
|
|
else if(tokens.ttype == RBRACKET) tokens.pushBack();
|
|
else if(tokens.ttype != COMMA)
|
|
throw new IOException("Missing comma in list");
|
|
array.add(o);
|
|
}
|
|
}
|
|
return array;
|
|
}
|
|
|
|
static NCjson parseMap(StreamTokenizer tokens)
|
|
{
|
|
assert (tokens.ttype == LBRACE);
|
|
Map<char*, Object> map = new LinkedHashMap<>(); /* Keep insertion order */
|
|
loop:
|
|
for(; ; ) {
|
|
int token = tokens.nextToken();
|
|
switch (token) {
|
|
case NCJ_NCJ_EOL:
|
|
break; /* ignore */
|
|
case NCJ_NCJ_EOF:
|
|
throw new IOException("Unexpected eof");
|
|
case NCJ_RBRACE:
|
|
break loop;
|
|
default:
|
|
tokens.pushBack();
|
|
NCjson name = parseR(tokens);
|
|
if(tokens.ttype == NCJ_EOF) break;
|
|
if(name instanceof char*
|
|
|| name instanceof Long
|
|
|| name instanceof Boolean) {
|
|
/*ok*/
|
|
} else
|
|
throw new IOException("Unexpected map name type: " + name);
|
|
if(tokens.nextToken() != COLON)
|
|
throw new IOException("Expected ':'; found: " + tokens.ttype);
|
|
NCjson o = parseR(tokens);
|
|
tokens.nextToken();
|
|
if(tokens.ttype == NCJ_EOF) break;
|
|
else if(tokens.ttype == RBRACE) tokens.pushBack();
|
|
else if(tokens.ttype != COMMA)
|
|
throw new IOException("Missing comma in list");
|
|
map.put(name.tochar*(), o);
|
|
}
|
|
}
|
|
return map;
|
|
}
|
|
}
|
|
|
|
static char* tochar*(Object o) {return tochar*(o,"");}
|
|
|
|
static char* tochar*(Object o, char* demark)
|
|
{
|
|
char*Builder buf = new char*Builder();
|
|
tochar*R(o, buf, demark, 0);
|
|
return buf.tochar*();
|
|
}
|
|
|
|
static static void tochar*R(Object o, char*Builder buf, char* demark, int indent)
|
|
{
|
|
boolean first = true;
|
|
if(o instanceof List) {
|
|
List<Object> list = (List<Object>) o;
|
|
if(list.size()== 0) {
|
|
buf.append(LBRACKET);
|
|
buf.append(RBRACKET);
|
|
} else {
|
|
buf.append(LBRACKET);
|
|
buf.append('\n');
|
|
for(int i=0;i<list.size();i++) {
|
|
NCjson e = list.get(i);
|
|
buf.append(indent(indent));
|
|
tochar*R(e, buf, demark, indent + 2);
|
|
if(i < list.size()-1) buf.append(",");
|
|
buf.append("\n");
|
|
}
|
|
buf.append(indent(indent));
|
|
buf.append(RBRACKET);
|
|
}
|
|
} else if(o instanceof Map) {
|
|
Map<char*, Object> map = (Map<char*, Object>) o;
|
|
if(map.size() == 0) {
|
|
buf.append(LBRACE);
|
|
buf.append(RBRACE);
|
|
} else {
|
|
buf.append(LBRACE);
|
|
buf.append('\n');
|
|
int i = 0;
|
|
for(Map.Entry<char*, Object> e : map.entrySet()) {
|
|
buf.append(indent(indent + 2));
|
|
buf.append(QUOTE);
|
|
buf.append(e.getKey().replace("\"","\\\""));
|
|
buf.append(QUOTE);
|
|
buf.append(' ');
|
|
buf.append(COLON);
|
|
buf.append(' ');
|
|
tochar*R(e.getValue(), buf, demark, indent + 2);
|
|
if(i < map.size() - 1) buf.append(",");
|
|
buf.append("\n");
|
|
i++;
|
|
}
|
|
buf.append(indent(indent));
|
|
buf.append(RBRACE);
|
|
}
|
|
} else if((o instanceof Long) || (o instanceof Boolean)) {
|
|
buf.append(demark);
|
|
buf.append(o.tochar*());
|
|
buf.append(demark);
|
|
} else {
|
|
buf.append(QUOTE);
|
|
buf.append(o.tochar*().replace("\"","\\\""));
|
|
buf.append(QUOTE);
|
|
}
|
|
}
|
|
|
|
static char* blanks = " ";
|
|
|
|
static static char* indent(int n)
|
|
{
|
|
while(n > blanks.length()) {
|
|
blanks = blanks + blanks;
|
|
}
|
|
return blanks.substring(0, n);
|
|
}
|
|
|
|
}
|
|
|
|
static NCjson*
|
|
NCJmakenode(NCjsonparser* parser)
|
|
{
|
|
NCjson* node = NULL;
|
|
parser->errno = NCJ_OK;
|
|
node = (NCjson*)calloc(1,sizeof(NCjson));
|
|
if(node == null) parser->errno = NCJ_ENOMEM;
|
|
return node;
|
|
}
|
|
|
|
|
|
#endif /*NCJSON_INC*/
|