2015-08-29 05:09:35 +00:00
|
|
|
/*
|
2015-10-22 05:13:26 +00:00
|
|
|
* Apple // emulator for *ix
|
2015-08-29 05:09:35 +00:00
|
|
|
*
|
|
|
|
* This software package is subject to the GNU General Public License
|
2015-10-22 05:13:26 +00:00
|
|
|
* version 3 or later (your choice) as published by the Free Software
|
2015-08-29 05:09:35 +00:00
|
|
|
* Foundation.
|
|
|
|
*
|
2015-10-22 05:13:26 +00:00
|
|
|
* Copyright 2013-2015 Aaron Culliney
|
2015-08-29 05:09:35 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "json_parse.h"
|
|
|
|
|
|
|
|
#define JSON_LENGTH 16
|
|
|
|
#define DEFAULT_NUMTOK 16
|
|
|
|
|
2016-02-13 22:02:35 +00:00
|
|
|
static int _json_createFromString(const char *jsonString, INOUT JSON_s *parsedData, ssize_t jsonLen) {
|
|
|
|
|
|
|
|
jsmnerr_t errCount = JSMN_ERROR_NOMEM;
|
|
|
|
do {
|
|
|
|
jsmn_parser parser = { 0 };
|
|
|
|
|
|
|
|
if (!parsedData) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
parsedData->jsonString = strdup(jsonString);
|
|
|
|
parsedData->jsonTokens = NULL;
|
|
|
|
|
|
|
|
unsigned int numTokens = DEFAULT_NUMTOK;
|
|
|
|
do {
|
|
|
|
if (!parsedData->jsonTokens) {
|
|
|
|
parsedData->jsonTokens = CALLOC(numTokens, sizeof(jsmntok_t));
|
|
|
|
if (!parsedData->jsonTokens) {
|
|
|
|
ERRLOG("WHOA3 : %s", strerror(errno));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//LOG("reallocating json tokens ...");
|
|
|
|
numTokens <<= 1;
|
|
|
|
jsmntok_t *newTokens = REALLOC(parsedData->jsonTokens, numTokens * sizeof(jsmntok_t));
|
|
|
|
if (!newTokens) {
|
|
|
|
ERRLOG("WHOA4 : %s", strerror(errno));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
memset(newTokens, '\0', numTokens * sizeof(jsmntok_t));
|
|
|
|
parsedData->jsonTokens = newTokens;
|
|
|
|
}
|
|
|
|
jsmn_init(&parser);
|
|
|
|
errCount = jsmn_parse(&parser, parsedData->jsonString, jsonLen, parsedData->jsonTokens, numTokens-(numTokens/2));
|
|
|
|
} while (errCount == JSMN_ERROR_NOMEM);
|
|
|
|
|
|
|
|
if (errCount == JSMN_ERROR_NOMEM) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (errCount < 0) {
|
|
|
|
ERRLOG("%s", "OOPS error parsing JSON : ");
|
|
|
|
switch (errCount) {
|
|
|
|
case JSMN_ERROR_NOMEM:
|
|
|
|
assert(0 && "should not happen");
|
|
|
|
break;
|
|
|
|
case JSMN_ERROR_INVAL:
|
|
|
|
ERRLOG("%s", "Invalid character inside JSON string");
|
|
|
|
break;
|
|
|
|
case JSMN_ERROR_PART:
|
|
|
|
ERRLOG("%s", "String is not a complete JSON packet, moar bytes expected");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ERRLOG("UNKNOWN errCount : %d", errCount);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
parsedData->numTokens = errCount;
|
|
|
|
|
|
|
|
} while (0);
|
|
|
|
|
|
|
|
if (errCount < 0) {
|
|
|
|
if (parsedData) {
|
|
|
|
json_destroy(parsedData);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return errCount;
|
|
|
|
}
|
|
|
|
|
2015-08-29 05:09:35 +00:00
|
|
|
int json_createFromFile(const char *filePath, INOUT JSON_s *parsedData) {
|
|
|
|
|
|
|
|
int fd = -1;
|
|
|
|
ssize_t jsonIdx = 0;
|
|
|
|
ssize_t jsonLen = 0;
|
|
|
|
|
|
|
|
char *jsonString = NULL;
|
|
|
|
|
|
|
|
do {
|
2016-02-26 05:04:23 +00:00
|
|
|
if (!filePath) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-08-29 05:09:35 +00:00
|
|
|
if (!parsedData) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMP_FAILURE_RETRY(fd = open(filePath, O_RDONLY));
|
|
|
|
if (fd < 0) {
|
|
|
|
ERRLOG("Error opening file : %s", strerror(errno));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
jsonLen = JSON_LENGTH*2;
|
2015-12-31 06:16:57 +00:00
|
|
|
jsonString = MALLOC(jsonLen);
|
2015-08-29 05:09:35 +00:00
|
|
|
if (jsonString == NULL) {
|
|
|
|
ERRLOG("WHOA : %s", strerror(errno));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t bytesRead = 0;
|
|
|
|
do {
|
|
|
|
TEMP_FAILURE_RETRY(bytesRead = read(fd, jsonString+jsonIdx, JSON_LENGTH));
|
|
|
|
if (bytesRead < 0) {
|
|
|
|
ERRLOG("Error reading file : %s", strerror(errno));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (bytesRead) {
|
|
|
|
jsonIdx += bytesRead;
|
|
|
|
if (jsonLen - jsonIdx < JSON_LENGTH) {
|
|
|
|
//LOG("reallocating json string ...");
|
|
|
|
jsonLen <<= 1;
|
2015-12-31 06:16:57 +00:00
|
|
|
char *newString = REALLOC(jsonString, jsonLen);
|
2015-08-29 05:09:35 +00:00
|
|
|
if (!newString) {
|
|
|
|
ERRLOG("WHOA2 : %s", strerror(errno));
|
|
|
|
bytesRead = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
jsonString = newString;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (bytesRead);
|
|
|
|
|
|
|
|
if (bytesRead < 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
jsonLen = jsonIdx;
|
|
|
|
TEMP_FAILURE_RETRY(close(fd));
|
2016-02-13 22:02:35 +00:00
|
|
|
fd = -1;
|
2015-08-29 05:09:35 +00:00
|
|
|
|
|
|
|
// now parse the string
|
2016-02-13 22:02:35 +00:00
|
|
|
jsmnerr_t errCount = _json_createFromString(jsonString, parsedData, jsonLen);
|
|
|
|
FREE(jsonString);
|
|
|
|
return errCount;
|
2015-08-29 05:09:35 +00:00
|
|
|
|
2016-02-13 22:02:35 +00:00
|
|
|
} while (0);
|
2015-08-29 05:09:35 +00:00
|
|
|
|
2016-02-13 22:02:35 +00:00
|
|
|
if (fd > 0) {
|
|
|
|
TEMP_FAILURE_RETRY(close(fd));
|
|
|
|
fd = -1;
|
|
|
|
}
|
2015-08-29 05:09:35 +00:00
|
|
|
|
2016-02-13 22:02:35 +00:00
|
|
|
if (jsonString) {
|
|
|
|
FREE(jsonString);
|
|
|
|
}
|
|
|
|
|
|
|
|
return JSMN_ERROR_NOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
int json_createFromString(const char *jsonString, INOUT JSON_s *parsedData) {
|
|
|
|
return _json_createFromString(jsonString, parsedData, strlen(jsonString));
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool _json_mapGetStringValue(const JSON_s *map, const char *key, INOUT char **val, INOUT int *len) {
|
|
|
|
bool foundMatch = false;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (!val) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!len) {
|
2015-08-29 05:09:35 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-02-13 22:02:35 +00:00
|
|
|
*val = NULL;
|
|
|
|
*len = -1;
|
|
|
|
|
|
|
|
int tokCount = map->numTokens;
|
|
|
|
if (tokCount < 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
int idx=0;
|
|
|
|
const int keySize = strlen(key);
|
|
|
|
|
|
|
|
// should begin as map ...
|
|
|
|
if (map->jsonTokens[idx].type != JSMN_OBJECT) {
|
|
|
|
ERRLOG("Map JSON : must start with begin map token");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++idx;
|
|
|
|
|
|
|
|
// Linear parse/search for key ...
|
|
|
|
for (int skip=0; idx<tokCount; idx+=skip) {
|
|
|
|
jsmntok_t keyTok = map->jsonTokens[idx];
|
|
|
|
|
|
|
|
assert(keyTok.parent == 0);
|
|
|
|
|
|
|
|
if (keyTok.type != JSMN_STRING) {
|
|
|
|
ERRLOG("Map JSON : expecting a string key at map position %d", idx);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
int start = keyTok.start;
|
|
|
|
int end = keyTok.end;
|
|
|
|
assert(end >= start && "bug");
|
|
|
|
const int size = end - start;
|
|
|
|
|
|
|
|
++idx;
|
|
|
|
jsmntok_t valTok = map->jsonTokens[idx];
|
|
|
|
if (size == keySize) {
|
|
|
|
foundMatch = (strncmp(key, &map->jsonString[start], size) == 0);
|
|
|
|
if (foundMatch) {
|
|
|
|
start = valTok.start;
|
|
|
|
end = valTok.end;
|
|
|
|
assert(end >= start && "bug");
|
|
|
|
*len = end - start;
|
|
|
|
*val = &map->jsonString[start];
|
2015-08-29 05:09:35 +00:00
|
|
|
break;
|
2016-02-13 22:02:35 +00:00
|
|
|
}
|
2015-08-29 05:09:35 +00:00
|
|
|
}
|
2016-02-13 22:02:35 +00:00
|
|
|
|
|
|
|
skip = valTok.skip;
|
|
|
|
}
|
|
|
|
} while (0);
|
|
|
|
|
|
|
|
return foundMatch;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool json_mapCopyStringValue(const JSON_s *map, const char *key, INOUT char **val) {
|
|
|
|
int len = 0;
|
|
|
|
bool foundMatch = _json_mapGetStringValue(map, key, val, &len);
|
|
|
|
if (foundMatch) {
|
|
|
|
*val = len>0 ? strndup(*val, len) : strdup("");
|
|
|
|
}
|
|
|
|
return foundMatch;
|
|
|
|
}
|
|
|
|
|
2016-02-26 05:04:23 +00:00
|
|
|
bool json_mapParseLongValue(const JSON_s *map, const char *key, INOUT long *val, const long base) {
|
2016-02-13 22:02:35 +00:00
|
|
|
bool foundMatch = false;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (!val) {
|
2015-08-29 05:09:35 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-02-13 22:02:35 +00:00
|
|
|
int len = 0;
|
|
|
|
char *str = NULL;
|
|
|
|
foundMatch = _json_mapGetStringValue(map, key, &str, &len);
|
|
|
|
if (foundMatch) {
|
|
|
|
char ch = str[len];
|
|
|
|
str[len] = '\0';
|
|
|
|
*val = strtol(str, NULL, base);
|
|
|
|
str[len] = ch;
|
|
|
|
}
|
2015-08-29 05:09:35 +00:00
|
|
|
} while (0);
|
|
|
|
|
2016-02-13 22:02:35 +00:00
|
|
|
return foundMatch;
|
|
|
|
}
|
2015-08-29 05:09:35 +00:00
|
|
|
|
2016-02-13 22:02:35 +00:00
|
|
|
bool json_mapParseFloatValue(const JSON_s *map, const char *key, INOUT float *val) {
|
|
|
|
bool foundMatch = false;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (!val) {
|
|
|
|
break;
|
2015-08-29 05:09:35 +00:00
|
|
|
}
|
2016-02-13 22:02:35 +00:00
|
|
|
|
|
|
|
int len = 0;
|
|
|
|
char *str = NULL;
|
|
|
|
foundMatch = _json_mapGetStringValue(map, key, &str, &len);
|
|
|
|
if (foundMatch) {
|
|
|
|
char ch = str[len];
|
|
|
|
str[len] = '\0';
|
|
|
|
*val = strtof(str, NULL);
|
|
|
|
str[len] = ch;
|
2015-08-29 05:09:35 +00:00
|
|
|
}
|
2016-02-13 22:02:35 +00:00
|
|
|
} while (0);
|
2015-08-29 05:09:35 +00:00
|
|
|
|
2016-02-13 22:02:35 +00:00
|
|
|
return foundMatch;
|
2015-08-29 05:09:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void json_destroy(JSON_s *parsedData) {
|
2016-02-13 22:02:35 +00:00
|
|
|
STRDUP_FREE(parsedData->jsonString);
|
2015-08-29 05:09:35 +00:00
|
|
|
FREE(parsedData->jsonTokens);
|
|
|
|
}
|
|
|
|
|