netcdf-c/libdispatch/json.c

373 lines
9.3 KiB
C
Raw Normal View History

/* Copyright 2018, UCAR/Unidata.
See the COPYRIGHT file for more information.
Primary change: add dap4 support 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
2017-03-09 08:01:10 +08:00
*/
#ifndef NCJSON_INC
#define NCJSON_INC 1
#define NCJ_DICT 1
Primary change: add dap4 support 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
2017-03-09 08:01:10 +08:00
#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;
Primary change: add dap4 support 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
2017-03-09 08:01:10 +08:00
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++;
Primary change: add dap4 support 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
2017-03-09 08:01:10 +08:00
}
if(c == NCJ_NUL) {
parser->errno = NCJ_ESTRING;
token = NCJ_ERR;
goto done;
}
Primary change: add dap4 support 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
2017-03-09 08:01:10 +08:00
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;
Primary change: add dap4 support 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
2017-03-09 08:01:10 +08:00
}
/* 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;
Primary change: add dap4 support 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
2017-03-09 08:01:10 +08:00
}
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*/