mirror of https://github.com/Icinga/icinga2.git
parent
dd0a2ab590
commit
7559273359
|
@ -25,7 +25,7 @@ mkclass_target(sysloglogger.ti sysloglogger.thpp)
|
|||
set(base_SOURCES
|
||||
application.cpp application.thpp array.cpp configerror.cpp console.cpp context.cpp
|
||||
convert.cpp debuginfo.cpp dictionary.cpp dynamicobject.cpp dynamicobject.thpp dynamictype.cpp
|
||||
exception.cpp fifo.cpp filelogger.cpp filelogger.thpp logger.cpp logger.thpp
|
||||
exception.cpp fifo.cpp filelogger.cpp filelogger.thpp json.cpp logger.cpp logger.thpp
|
||||
netstring.cpp networkstream.cpp object.cpp objectlock.cpp process.cpp
|
||||
ringbuffer.cpp scriptfunction.cpp scriptfunctionwrapper.cpp
|
||||
scriptutils.cpp scriptvariable.cpp serializer.cpp socket.cpp stacktrace.cpp
|
||||
|
@ -41,7 +41,7 @@ endif()
|
|||
|
||||
add_library(base SHARED ${base_SOURCES})
|
||||
|
||||
target_link_libraries(base ${CMAKE_DL_LIBS} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} cJSON mmatch)
|
||||
target_link_libraries(base ${CMAKE_DL_LIBS} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} mmatch yajl)
|
||||
|
||||
if(HAVE_LIBEXECINFO)
|
||||
target_link_libraries(base execinfo)
|
||||
|
@ -56,6 +56,9 @@ link_directories(${icinga2_BINARY_DIR}/third-party/execvpe)
|
|||
include_directories(${icinga2_SOURCE_DIR}/third-party/mmatch)
|
||||
link_directories(${icinga2_BINARY_DIR}/third-party/mmatch)
|
||||
|
||||
include_directories(${icinga2_BINARY_DIR}/third-party/yajl/yajl/include)
|
||||
link_directories(${icinga2_BINARY_DIR}/third-party/yajl)
|
||||
|
||||
if(UNIX OR CYGWIN)
|
||||
target_link_libraries(base execvpe)
|
||||
endif()
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include "base/array.hpp"
|
||||
#include "base/objectlock.hpp"
|
||||
#include "base/debug.hpp"
|
||||
#include <cJSON.h>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
using namespace icinga;
|
||||
|
@ -198,46 +197,3 @@ Array::Ptr Array::ShallowClone(void) const
|
|||
CopyTo(clone);
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a JSON object to an array.
|
||||
*
|
||||
* @param json The JSON object.
|
||||
* @returns An array that is equivalent to the JSON object.
|
||||
*/
|
||||
Array::Ptr Array::FromJson(cJSON *json)
|
||||
{
|
||||
Array::Ptr array = make_shared<Array>();
|
||||
|
||||
ASSERT(json->type == cJSON_Array);
|
||||
|
||||
for (cJSON *i = json->child; i != NULL; i = i->next) {
|
||||
array->Add(Value::FromJson(i));
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this array to a JSON object.
|
||||
*
|
||||
* @returns A JSON object that is equivalent to the array. Values that
|
||||
* cannot be represented in JSON are omitted.
|
||||
*/
|
||||
cJSON *Array::ToJson(void) const
|
||||
{
|
||||
cJSON *json = cJSON_CreateArray();
|
||||
|
||||
try {
|
||||
ObjectLock olock(this);
|
||||
|
||||
BOOST_FOREACH(const Value& value, m_Data) {
|
||||
cJSON_AddItemToArray(json, value.ToJson());
|
||||
}
|
||||
} catch (...) {
|
||||
cJSON_Delete(json);
|
||||
throw;
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
|
|
@ -65,9 +65,6 @@ public:
|
|||
void CopyTo(const Array::Ptr& dest) const;
|
||||
Array::Ptr ShallowClone(void) const;
|
||||
|
||||
static Array::Ptr FromJson(cJSON *json);
|
||||
cJSON *ToJson(void) const;
|
||||
|
||||
private:
|
||||
std::vector<Value> m_Data; /**< The data for the array. */
|
||||
};
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include "base/dictionary.hpp"
|
||||
#include "base/objectlock.hpp"
|
||||
#include "base/debug.hpp"
|
||||
#include <cJSON.h>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
using namespace icinga;
|
||||
|
@ -213,46 +212,3 @@ Dictionary::Ptr Dictionary::ShallowClone(void) const
|
|||
CopyTo(clone);
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a JSON object to a dictionary.
|
||||
*
|
||||
* @param json The JSON object.
|
||||
* @returns A dictionary that is equivalent to the JSON object.
|
||||
*/
|
||||
Dictionary::Ptr Dictionary::FromJson(cJSON *json)
|
||||
{
|
||||
Dictionary::Ptr dictionary = make_shared<Dictionary>();
|
||||
|
||||
ASSERT(json->type == cJSON_Object);
|
||||
|
||||
for (cJSON *i = json->child; i != NULL; i = i->next) {
|
||||
dictionary->Set(i->string, Value::FromJson(i));
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this dictionary to a JSON object.
|
||||
*
|
||||
* @returns A JSON object that is equivalent to the dictionary. Values that
|
||||
* cannot be represented in JSON are omitted.
|
||||
*/
|
||||
cJSON *Dictionary::ToJson(void) const
|
||||
{
|
||||
cJSON *json = cJSON_CreateObject();
|
||||
|
||||
try {
|
||||
ObjectLock olock(this);
|
||||
|
||||
BOOST_FOREACH(const Dictionary::Pair& kv, m_Data) {
|
||||
cJSON_AddItemToObject(json, kv.first.CStr(), kv.second.ToJson());
|
||||
}
|
||||
} catch (...) {
|
||||
cJSON_Delete(json);
|
||||
throw;
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
|
|
@ -64,9 +64,6 @@ public:
|
|||
void CopyTo(const Dictionary::Ptr& dest) const;
|
||||
Dictionary::Ptr ShallowClone(void) const;
|
||||
|
||||
static Dictionary::Ptr FromJson(cJSON *json);
|
||||
cJSON *ToJson(void) const;
|
||||
|
||||
private:
|
||||
std::map<String, Value> m_Data; /**< The data for the dictionary. */
|
||||
};
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "base/dynamictype.hpp"
|
||||
#include "base/serializer.hpp"
|
||||
#include "base/netstring.hpp"
|
||||
#include "base/json.hpp"
|
||||
#include "base/stdiostream.hpp"
|
||||
#include "base/debug.hpp"
|
||||
#include "base/objectlock.hpp"
|
||||
|
@ -266,7 +267,7 @@ void DynamicObject::DumpObjects(const String& filename, int attributeTypes)
|
|||
|
||||
persistentObject->Set("update", update);
|
||||
|
||||
String json = JsonSerialize(persistentObject);
|
||||
String json = JsonEncode(persistentObject);
|
||||
|
||||
NetString::WriteStringToStream(sfp, json);
|
||||
}
|
||||
|
@ -290,7 +291,7 @@ void DynamicObject::DumpObjects(const String& filename, int attributeTypes)
|
|||
|
||||
void DynamicObject::RestoreObject(const String& message, int attributeTypes)
|
||||
{
|
||||
Dictionary::Ptr persistentObject = JsonDeserialize(message);
|
||||
Dictionary::Ptr persistentObject = JsonDecode(message);
|
||||
|
||||
String type = persistentObject->Get("type");
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#include "base/i2-base.hpp"
|
||||
#include "base/dynamicobject.thpp"
|
||||
#include "base/object.hpp"
|
||||
#include "base/serializer.hpp"
|
||||
#include "base/type.hpp"
|
||||
#include "base/dictionary.hpp"
|
||||
#include "base/debuginfo.hpp"
|
||||
#include <boost/signals2.hpp>
|
||||
|
|
|
@ -0,0 +1,338 @@
|
|||
/******************************************************************************
|
||||
* Icinga 2 *
|
||||
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU General Public License *
|
||||
* as published by the Free Software Foundation; either version 2 *
|
||||
* of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the Free Software Foundation *
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||
******************************************************************************/
|
||||
|
||||
#include "base/json.hpp"
|
||||
#include "base/debug.hpp"
|
||||
#include "base/dictionary.hpp"
|
||||
#include "base/array.hpp"
|
||||
#include "base/objectlock.hpp"
|
||||
#include "base/convert.hpp"
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/exception_ptr.hpp>
|
||||
#include <yajl/yajl_gen.h>
|
||||
#include <yajl/yajl_parse.h>
|
||||
#include <stack>
|
||||
|
||||
using namespace icinga;
|
||||
|
||||
static void Encode(yajl_gen handle, const Value& value);
|
||||
|
||||
static void EncodeDictionary(yajl_gen handle, const Dictionary::Ptr& dict)
|
||||
{
|
||||
yajl_gen_map_open(handle);
|
||||
|
||||
ObjectLock olock(dict);
|
||||
BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
|
||||
yajl_gen_string(handle, reinterpret_cast<const unsigned char *>(kv.first.CStr()), kv.first.GetLength());
|
||||
Encode(handle, kv.second);
|
||||
}
|
||||
|
||||
yajl_gen_map_close(handle);
|
||||
}
|
||||
|
||||
static void EncodeArray(yajl_gen handle, const Array::Ptr& arr)
|
||||
{
|
||||
yajl_gen_array_open(handle);
|
||||
|
||||
ObjectLock olock(arr);
|
||||
BOOST_FOREACH(const Value& value, arr) {
|
||||
Encode(handle, value);
|
||||
}
|
||||
|
||||
yajl_gen_array_close(handle);
|
||||
}
|
||||
|
||||
static void Encode(yajl_gen handle, const Value& value)
|
||||
{
|
||||
String str;
|
||||
|
||||
switch (value.GetType()) {
|
||||
case ValueNumber:
|
||||
if (yajl_gen_double(handle, static_cast<double>(value)) == yajl_gen_invalid_number)
|
||||
yajl_gen_double(handle, 0);
|
||||
|
||||
break;
|
||||
case ValueString:
|
||||
str = value;
|
||||
yajl_gen_string(handle, reinterpret_cast<const unsigned char *>(str.CStr()), str.GetLength());
|
||||
|
||||
break;
|
||||
case ValueObject:
|
||||
if (value.IsObjectType<Dictionary>())
|
||||
EncodeDictionary(handle, value);
|
||||
else if (value.IsObjectType<Array>())
|
||||
EncodeArray(handle, value);
|
||||
else
|
||||
yajl_gen_null(handle);
|
||||
|
||||
break;
|
||||
case ValueEmpty:
|
||||
yajl_gen_null(handle);
|
||||
|
||||
break;
|
||||
default:
|
||||
VERIFY(!"Invalid variant type.");
|
||||
}
|
||||
}
|
||||
|
||||
String icinga::JsonEncode(const Value& value)
|
||||
{
|
||||
yajl_gen handle = yajl_gen_alloc(NULL);
|
||||
Encode(handle, value);
|
||||
|
||||
const unsigned char *buf;
|
||||
size_t len;
|
||||
yajl_gen_get_buf(handle, &buf, &len);
|
||||
|
||||
String result = String(buf, buf + len);
|
||||
|
||||
yajl_gen_free(handle);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct JsonElement
|
||||
{
|
||||
String Key;
|
||||
bool KeySet;
|
||||
Value Value;
|
||||
|
||||
JsonElement(void)
|
||||
: KeySet(false)
|
||||
{ }
|
||||
};
|
||||
|
||||
struct JsonContext
|
||||
{
|
||||
public:
|
||||
void Push(const Value& value)
|
||||
{
|
||||
JsonElement element;
|
||||
element.Value = value;
|
||||
|
||||
m_Stack.push(element);
|
||||
}
|
||||
|
||||
JsonElement Pop(void)
|
||||
{
|
||||
JsonElement value = m_Stack.top();
|
||||
m_Stack.pop();
|
||||
return value;
|
||||
}
|
||||
|
||||
void AddValue(const Value& value)
|
||||
{
|
||||
if (m_Stack.empty()) {
|
||||
JsonElement element;
|
||||
element.Value = value;
|
||||
m_Stack.push(element);
|
||||
return;
|
||||
}
|
||||
|
||||
JsonElement& element = m_Stack.top();
|
||||
|
||||
if (element.Value.IsObjectType<Dictionary>()) {
|
||||
if (!element.KeySet) {
|
||||
element.Key = value;
|
||||
element.KeySet = true;
|
||||
} else {
|
||||
Dictionary::Ptr dict = element.Value;
|
||||
dict->Set(element.Key, value);
|
||||
element.KeySet = false;
|
||||
}
|
||||
} else if (element.Value.IsObjectType<Array>()) {
|
||||
Array::Ptr arr = element.Value;
|
||||
arr->Add(value);
|
||||
} else {
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Cannot add value to JSON element."));
|
||||
}
|
||||
}
|
||||
|
||||
Value GetValue(void) const
|
||||
{
|
||||
ASSERT(m_Stack.size() == 1);
|
||||
return m_Stack.top().Value;
|
||||
}
|
||||
|
||||
void SaveException(void)
|
||||
{
|
||||
m_Exception = boost::current_exception();
|
||||
}
|
||||
|
||||
void ThrowException(void) const
|
||||
{
|
||||
boost::rethrow_exception(m_Exception);
|
||||
}
|
||||
|
||||
private:
|
||||
std::stack<JsonElement> m_Stack;
|
||||
Value m_Key;
|
||||
boost::exception_ptr m_Exception;
|
||||
};
|
||||
|
||||
static int DecodeNull(void *ctx)
|
||||
{
|
||||
JsonContext *context = static_cast<JsonContext *>(ctx);
|
||||
|
||||
try {
|
||||
context->AddValue(Empty);
|
||||
} catch (...) {
|
||||
context->SaveException();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int DecodeBoolean(void *ctx, int value)
|
||||
{
|
||||
JsonContext *context = static_cast<JsonContext *>(ctx);
|
||||
|
||||
try {
|
||||
context->AddValue(value);
|
||||
} catch (...) {
|
||||
context->SaveException();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int DecodeNumber(void *ctx, const char *str, size_t len)
|
||||
{
|
||||
JsonContext *context = static_cast<JsonContext *>(ctx);
|
||||
|
||||
try {
|
||||
String jstr = String(str, str + len);
|
||||
context->AddValue(Convert::ToDouble(jstr));
|
||||
} catch (...) {
|
||||
context->SaveException();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int DecodeString(void *ctx, const unsigned char *str, size_t len)
|
||||
{
|
||||
JsonContext *context = static_cast<JsonContext *>(ctx);
|
||||
|
||||
try {
|
||||
context->AddValue(String(reinterpret_cast<const char *>(str), reinterpret_cast<const char *>(str) + len));
|
||||
} catch (...) {
|
||||
context->SaveException();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int DecodeStartMap(void *ctx)
|
||||
{
|
||||
JsonContext *context = static_cast<JsonContext *>(ctx);
|
||||
|
||||
try {
|
||||
context->Push(make_shared<Dictionary>());
|
||||
} catch (...) {
|
||||
context->SaveException();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int DecodeEndMap(void *ctx)
|
||||
{
|
||||
JsonContext *context = static_cast<JsonContext *>(ctx);
|
||||
|
||||
try {
|
||||
context->AddValue(context->Pop().Value);
|
||||
} catch (...) {
|
||||
context->SaveException();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int DecodeStartArray(void *ctx)
|
||||
{
|
||||
JsonContext *context = static_cast<JsonContext *>(ctx);
|
||||
|
||||
try {
|
||||
context->Push(make_shared<Array>());
|
||||
} catch (...) {
|
||||
context->SaveException();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int DecodeEndArray(void *ctx)
|
||||
{
|
||||
JsonContext *context = static_cast<JsonContext *>(ctx);
|
||||
|
||||
try {
|
||||
context->AddValue(context->Pop().Value);
|
||||
} catch (...) {
|
||||
context->SaveException();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
Value icinga::JsonDecode(const String& data)
|
||||
{
|
||||
static const yajl_callbacks callbacks = {
|
||||
DecodeNull,
|
||||
DecodeBoolean,
|
||||
NULL,
|
||||
NULL,
|
||||
DecodeNumber,
|
||||
DecodeString,
|
||||
DecodeStartMap,
|
||||
DecodeString,
|
||||
DecodeEndMap,
|
||||
DecodeStartArray,
|
||||
DecodeEndArray
|
||||
};
|
||||
|
||||
yajl_handle handle;
|
||||
JsonContext context;
|
||||
|
||||
handle = yajl_alloc(&callbacks, NULL, &context);
|
||||
|
||||
yajl_parse(handle, reinterpret_cast<const unsigned char *>(data.CStr()), data.GetLength());
|
||||
|
||||
if (yajl_complete_parse(handle) != yajl_status_ok) {
|
||||
unsigned char *internal_err_str = yajl_get_error(handle, 1, reinterpret_cast<const unsigned char *>(data.CStr()), data.GetLength());
|
||||
String msg = reinterpret_cast<char *>(internal_err_str);
|
||||
yajl_free_error(handle, internal_err_str);
|
||||
|
||||
yajl_free(handle);
|
||||
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument(msg));
|
||||
}
|
||||
|
||||
yajl_free(handle);
|
||||
|
||||
return context.GetValue();
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/******************************************************************************
|
||||
* Icinga 2 *
|
||||
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU General Public License *
|
||||
* as published by the Free Software Foundation; either version 2 *
|
||||
* of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the Free Software Foundation *
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef JSON_H
|
||||
#define JSON_H
|
||||
|
||||
#include "base/i2-base.hpp"
|
||||
#include "base/value.hpp"
|
||||
|
||||
namespace icinga
|
||||
{
|
||||
|
||||
I2_BASE_API String JsonEncode(const Value& value);
|
||||
I2_BASE_API Value JsonDecode(const String& data);
|
||||
|
||||
}
|
||||
|
||||
#endif /* JSON_H */
|
|
@ -23,7 +23,7 @@
|
|||
#include "base/convert.hpp"
|
||||
#include "base/array.hpp"
|
||||
#include "base/dictionary.hpp"
|
||||
#include "base/serializer.hpp"
|
||||
#include "base/json.hpp"
|
||||
#include "base/logger.hpp"
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
|
@ -135,7 +135,7 @@ void ScriptUtils::Log(const std::vector<Value>& arguments)
|
|||
if (message.IsString())
|
||||
::Log(severity, facility, message);
|
||||
else
|
||||
::Log(severity, facility, JsonSerialize(message));
|
||||
::Log(severity, facility, JsonEncode(message));
|
||||
}
|
||||
|
||||
Array::Ptr ScriptUtils::Range(const std::vector<Value>& arguments)
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "base/logger.hpp"
|
||||
#include "base/stdiostream.hpp"
|
||||
#include "base/netstring.hpp"
|
||||
#include "base/json.hpp"
|
||||
#include "base/convert.hpp"
|
||||
#include <boost/foreach.hpp>
|
||||
#include <fstream>
|
||||
|
@ -124,7 +125,7 @@ void ScriptVariable::WriteVariablesFile(const String& filename)
|
|||
|
||||
persistentVariable->Set("value", value);
|
||||
|
||||
String json = JsonSerialize(persistentVariable);
|
||||
String json = JsonEncode(persistentVariable);
|
||||
|
||||
NetString::WriteStringToStream(sfp, json);
|
||||
}
|
||||
|
|
|
@ -22,55 +22,9 @@
|
|||
#include "base/application.hpp"
|
||||
#include "base/objectlock.hpp"
|
||||
#include <boost/foreach.hpp>
|
||||
#include <cJSON.h>
|
||||
|
||||
using namespace icinga;
|
||||
|
||||
/**
|
||||
* Serializes a Value into a JSON string.
|
||||
*
|
||||
* @returns A string representing the Value.
|
||||
*/
|
||||
String icinga::JsonSerialize(const Value& value)
|
||||
{
|
||||
cJSON *json = value.ToJson();
|
||||
|
||||
char *jsonString;
|
||||
|
||||
#ifdef _DEBUG
|
||||
jsonString = cJSON_Print(json);
|
||||
#else /* _DEBUG */
|
||||
jsonString = cJSON_PrintUnformatted(json);
|
||||
#endif /* _DEBUG */
|
||||
|
||||
cJSON_Delete(json);
|
||||
|
||||
String result = jsonString;
|
||||
|
||||
free(jsonString);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the string representation of a Value.
|
||||
*
|
||||
* @param data A JSON string obtained from JsonSerialize
|
||||
* @returns The newly deserialized Value.
|
||||
*/
|
||||
Value icinga::JsonDeserialize(const String& data)
|
||||
{
|
||||
cJSON *json = cJSON_Parse(data.CStr());
|
||||
|
||||
if (!json)
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error("Invalid JSON String: " + data));
|
||||
|
||||
Value value = Value::FromJson(json);
|
||||
cJSON_Delete(json);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static Array::Ptr SerializeArray(const Array::Ptr& input, int attributeTypes)
|
||||
{
|
||||
Array::Ptr result = make_shared<Array>();
|
||||
|
|
|
@ -21,20 +21,12 @@
|
|||
#define SERIALIZER_H
|
||||
|
||||
#include "base/i2-base.hpp"
|
||||
#include "base/type.hpp"
|
||||
#include "base/value.hpp"
|
||||
|
||||
namespace icinga
|
||||
{
|
||||
|
||||
enum FieldAttribute
|
||||
{
|
||||
FAConfig = 1,
|
||||
FAState = 2
|
||||
};
|
||||
|
||||
I2_BASE_API String JsonSerialize(const Value& value);
|
||||
I2_BASE_API Value JsonDeserialize(const String& data);
|
||||
|
||||
I2_BASE_API Value Serialize(const Value& value, int attributeTypes = FAState);
|
||||
I2_BASE_API Value Deserialize(const Value& value, bool safe_mode = false, int attributeTypes = FAState);
|
||||
I2_BASE_API Value Deserialize(const Object::Ptr& object, const Value& value, bool safe_mode = false, int attributeTypes = FAState);
|
||||
|
|
|
@ -30,6 +30,12 @@
|
|||
namespace icinga
|
||||
{
|
||||
|
||||
enum FieldAttribute
|
||||
{
|
||||
FAConfig = 1,
|
||||
FAState = 2
|
||||
};
|
||||
|
||||
struct Field
|
||||
{
|
||||
int ID;
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include "base/array.hpp"
|
||||
#include "base/dictionary.hpp"
|
||||
#include "base/type.hpp"
|
||||
#include <cJSON.h>
|
||||
|
||||
using namespace icinga;
|
||||
|
||||
|
@ -137,65 +136,6 @@ bool Value::ToBool(void) const
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts a JSON object into a variant.
|
||||
*
|
||||
* @param json The JSON object.
|
||||
*/
|
||||
Value Value::FromJson(cJSON *json)
|
||||
{
|
||||
if (json->type == cJSON_Number)
|
||||
return json->valuedouble;
|
||||
else if (json->type == cJSON_String)
|
||||
return String(json->valuestring);
|
||||
else if (json->type == cJSON_True)
|
||||
return 1;
|
||||
else if (json->type == cJSON_False)
|
||||
return 0;
|
||||
else if (json->type == cJSON_Object)
|
||||
return Dictionary::FromJson(json);
|
||||
else if (json->type == cJSON_Array)
|
||||
return Array::FromJson(json);
|
||||
else if (json->type == cJSON_NULL)
|
||||
return Value();
|
||||
else
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Unsupported JSON type."));
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the variant.
|
||||
*
|
||||
* @returns A JSON object representing this variant.
|
||||
*/
|
||||
cJSON *Value::ToJson(void) const
|
||||
{
|
||||
switch (GetType()) {
|
||||
case ValueNumber:
|
||||
return cJSON_CreateNumber(boost::get<double>(m_Value));
|
||||
|
||||
case ValueString:
|
||||
return cJSON_CreateString(boost::get<String>(m_Value).CStr());
|
||||
|
||||
case ValueObject:
|
||||
if (IsObjectType<Dictionary>()) {
|
||||
Dictionary::Ptr dictionary = *this;
|
||||
return dictionary->ToJson();
|
||||
} else if (IsObjectType<Array>()) {
|
||||
Array::Ptr array = *this;
|
||||
return array->ToJson();
|
||||
} else {
|
||||
return cJSON_CreateNull();
|
||||
}
|
||||
|
||||
case ValueEmpty:
|
||||
return cJSON_CreateNull();
|
||||
|
||||
default:
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error("Invalid variant type."));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the value.
|
||||
*
|
||||
|
|
|
@ -126,9 +126,6 @@ public:
|
|||
return (dynamic_pointer_cast<T>(boost::get<Object::Ptr>(m_Value)) != NULL);
|
||||
}
|
||||
|
||||
static Value FromJson(cJSON *json);
|
||||
cJSON *ToJson(void) const;
|
||||
|
||||
ValueType GetType(void) const;
|
||||
String GetTypeName(void) const;
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#include "base/application.hpp"
|
||||
#include "base/tlsutility.hpp"
|
||||
#include "base/convert.hpp"
|
||||
#include "base/serializer.hpp"
|
||||
#include "base/json.hpp"
|
||||
#include "base/netstring.hpp"
|
||||
#include "base/stdiostream.hpp"
|
||||
#include "base/debug.hpp"
|
||||
|
@ -112,7 +112,7 @@ void AgentUtility::PrintAgentsJson(std::ostream& fp)
|
|||
result->Set(agent->Get("endpoint"), agent);
|
||||
}
|
||||
|
||||
fp << JsonSerialize(result);
|
||||
fp << JsonEncode(result);
|
||||
}
|
||||
|
||||
bool AgentUtility::AddAgent(const String& name)
|
||||
|
@ -206,7 +206,7 @@ bool AgentUtility::WriteAgentToRepository(const String& filename, const Dictiona
|
|||
String tempFilename = filename + ".tmp";
|
||||
|
||||
std::ofstream fp(tempFilename.CStr(), std::ofstream::out | std::ostream::trunc);
|
||||
fp << JsonSerialize(item);
|
||||
fp << JsonEncode(item);
|
||||
fp.close();
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -235,7 +235,7 @@ Dictionary::Ptr AgentUtility::GetAgentFromRepository(const String& filename)
|
|||
|
||||
fp.close();
|
||||
|
||||
return JsonDeserialize(content);
|
||||
return JsonDecode(content);
|
||||
}
|
||||
|
||||
std::vector<Dictionary::Ptr> AgentUtility::GetAgents(void)
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#include "base/convert.hpp"
|
||||
#include "base/dynamicobject.hpp"
|
||||
#include "base/dynamictype.hpp"
|
||||
#include "base/serializer.hpp"
|
||||
#include "base/json.hpp"
|
||||
#include "base/netstring.hpp"
|
||||
#include "base/stdiostream.hpp"
|
||||
#include "base/debug.hpp"
|
||||
|
@ -116,7 +116,7 @@ int ObjectListCommand::Run(const boost::program_options::variables_map& vm, cons
|
|||
|
||||
void ObjectListCommand::PrintObject(std::ostream& fp, bool& first, const String& message, std::map<String, int>& type_count, const String& name_filter, const String& type_filter)
|
||||
{
|
||||
Dictionary::Ptr object = JsonDeserialize(message);
|
||||
Dictionary::Ptr object = JsonDecode(message);
|
||||
|
||||
Dictionary::Ptr properties = object->Get("properties");
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "base/logger.hpp"
|
||||
#include "base/application.hpp"
|
||||
#include "base/convert.hpp"
|
||||
#include "base/serializer.hpp"
|
||||
#include "base/json.hpp"
|
||||
#include "base/netstring.hpp"
|
||||
#include "base/stdiostream.hpp"
|
||||
#include "base/debug.hpp"
|
||||
|
@ -101,7 +101,7 @@ void RepositoryUtility::PrintObjects(std::ostream& fp, const String& type)
|
|||
|
||||
if (obj) {
|
||||
fp << "Object Name: " << object << "\n";
|
||||
fp << JsonSerialize(obj);
|
||||
fp << JsonEncode(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -157,7 +157,7 @@ void RepositoryUtility::PrintChangeLog(std::ostream& fp)
|
|||
std::cout << "Changes to be committed:\n";
|
||||
|
||||
BOOST_FOREACH(const Value& entry, changelog) {
|
||||
std::cout << JsonSerialize(entry) << "\n"; //TODO better formatting
|
||||
std::cout << JsonEncode(entry) << "\n"; //TODO better formatting
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -267,7 +267,7 @@ bool RepositoryUtility::WriteObjectToRepositoryChangeLog(const String& path, con
|
|||
String tempPath = path + ".tmp";
|
||||
|
||||
std::ofstream fp(tempPath.CStr(), std::ofstream::out | std::ostream::trunc);
|
||||
fp << JsonSerialize(item);
|
||||
fp << JsonEncode(item);
|
||||
fp.close();
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -296,7 +296,7 @@ Dictionary::Ptr RepositoryUtility::GetObjectFromRepositoryChangeLog(const String
|
|||
|
||||
fp.close();
|
||||
|
||||
return JsonDeserialize(content);
|
||||
return JsonDecode(content);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#include "base/convert.hpp"
|
||||
#include "base/dynamicobject.hpp"
|
||||
#include "base/dynamictype.hpp"
|
||||
#include "base/serializer.hpp"
|
||||
#include "base/json.hpp"
|
||||
#include "base/netstring.hpp"
|
||||
#include "base/stdiostream.hpp"
|
||||
#include "base/debug.hpp"
|
||||
|
@ -92,7 +92,7 @@ int VariableGetCommand::Run(const boost::program_options::variables_map& vm, con
|
|||
String message;
|
||||
|
||||
while (NetString::ReadStringFromStream(sfp, &message)) {
|
||||
Dictionary::Ptr variable = JsonDeserialize(message);
|
||||
Dictionary::Ptr variable = JsonDecode(message);
|
||||
|
||||
if (variable->Get("name") == ap[0]) {
|
||||
std::cout << variable->Get("value") << "\n";
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#include "base/convert.hpp"
|
||||
#include "base/dynamicobject.hpp"
|
||||
#include "base/dynamictype.hpp"
|
||||
#include "base/serializer.hpp"
|
||||
#include "base/json.hpp"
|
||||
#include "base/netstring.hpp"
|
||||
#include "base/stdiostream.hpp"
|
||||
#include "base/debug.hpp"
|
||||
|
@ -90,7 +90,7 @@ int VariableListCommand::Run(const boost::program_options::variables_map& vm, co
|
|||
|
||||
void VariableListCommand::PrintVariable(std::ostream& fp, const String& message)
|
||||
{
|
||||
Dictionary::Ptr variable = JsonDeserialize(message);
|
||||
Dictionary::Ptr variable = JsonDecode(message);
|
||||
|
||||
std::cout << variable->Get("name") << " = " << variable->Get("value") << "\n";
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "base/exception.hpp"
|
||||
#include "base/stdiostream.hpp"
|
||||
#include "base/netstring.hpp"
|
||||
#include "base/json.hpp"
|
||||
#include "base/configerror.hpp"
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
|
@ -310,7 +311,7 @@ void ConfigItem::WriteObjectsFile(const String& filename)
|
|||
}
|
||||
persistentItem->Set("debug_hints", item->GetDebugHints());
|
||||
|
||||
String json = JsonSerialize(persistentItem);
|
||||
String json = JsonEncode(persistentItem);
|
||||
|
||||
NetString::WriteStringToStream(sfp, json);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#include "config/applyrule.hpp"
|
||||
#include "config/objectrule.hpp"
|
||||
#include "base/array.hpp"
|
||||
#include "base/serializer.hpp"
|
||||
#include "base/json.hpp"
|
||||
#include "base/scriptfunction.hpp"
|
||||
#include "base/scriptvariable.hpp"
|
||||
#include "base/utility.hpp"
|
||||
|
@ -84,7 +84,7 @@ void Expression::DumpOperand(std::ostream& stream, const Value& operand, int ind
|
|||
Expression::Ptr left = operand;
|
||||
left->Dump(stream, indent);
|
||||
} else {
|
||||
stream << String(indent, ' ') << JsonSerialize(operand) << "\n";
|
||||
stream << String(indent, ' ') << JsonEncode(operand) << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,7 +215,7 @@ Value Expression::OpIn(const Expression *expr, const Dictionary::Ptr& locals, De
|
|||
if (right.IsEmpty())
|
||||
return false;
|
||||
else if (!right.IsObjectType<Array>())
|
||||
BOOST_THROW_EXCEPTION(ConfigError("Invalid right side argument for 'in' operator: " + JsonSerialize(right)));
|
||||
BOOST_THROW_EXCEPTION(ConfigError("Invalid right side argument for 'in' operator: " + JsonEncode(right)));
|
||||
|
||||
Value left = expr->EvaluateOperand1(locals);
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include "base/utility.hpp"
|
||||
#include "base/exception.hpp"
|
||||
#include "base/initialize.hpp"
|
||||
#include "base/serializer.hpp"
|
||||
#include "base/json.hpp"
|
||||
#include <fstream>
|
||||
|
||||
using namespace icinga;
|
||||
|
@ -1540,7 +1542,7 @@ Value ApiEvents::UpdateRepositoryAPIHandler(const MessageOrigin& origin, const D
|
|||
String repositoryTempFile = repositoryFile + ".tmp";
|
||||
|
||||
std::ofstream fp(repositoryTempFile.CStr(), std::ofstream::out | std::ostream::trunc);
|
||||
fp << JsonSerialize(params);
|
||||
fp << JsonEncode(params);
|
||||
fp.close();
|
||||
|
||||
#ifdef _WIN32
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include "base/convert.hpp"
|
||||
#include "base/utility.hpp"
|
||||
#include "base/debug.hpp"
|
||||
#include "base/serializer.hpp"
|
||||
#include "base/json.hpp"
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
using namespace icinga;
|
||||
|
@ -123,7 +123,7 @@ Service::Ptr Host::GetServiceByShortName(const Value& name)
|
|||
|
||||
return Service::GetByNamePair(dict->Get("host"), dict->Get("service"));
|
||||
} else {
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Host/Service name pair is invalid: " + JsonSerialize(name)));
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Host/Service name pair is invalid: " + JsonEncode(name)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "base/exception.hpp"
|
||||
#include "base/application.hpp"
|
||||
#include "base/statsfunction.hpp"
|
||||
#include "base/json.hpp"
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <fstream>
|
||||
|
@ -151,7 +152,7 @@ void IcingaStatusWriter::StatusTimerHandler(void)
|
|||
|
||||
statusfp << std::fixed;
|
||||
|
||||
statusfp << JsonSerialize(GetStatusData());
|
||||
statusfp << JsonEncode(GetStatusData());
|
||||
|
||||
statusfp.close();
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
#include "base/logger.hpp"
|
||||
#include "base/exception.hpp"
|
||||
#include "base/utility.hpp"
|
||||
#include "base/serializer.hpp"
|
||||
#include "base/json.hpp"
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
@ -382,7 +382,7 @@ void LivestatusQuery::PrintResultSet(std::ostream& fp, const Array::Ptr& rs) con
|
|||
fp << m_Separators[0];
|
||||
}
|
||||
} else if (m_OutputFormat == "json") {
|
||||
fp << JsonSerialize(rs);
|
||||
fp << JsonEncode(rs);
|
||||
} else if (m_OutputFormat == "python") {
|
||||
PrintPythonArray(fp, rs);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "remote/endpoint.hpp"
|
||||
#include "base/convert.hpp"
|
||||
#include "base/netstring.hpp"
|
||||
#include "base/json.hpp"
|
||||
#include "base/dynamictype.hpp"
|
||||
#include "base/logger.hpp"
|
||||
#include "base/objectlock.hpp"
|
||||
|
@ -461,7 +462,7 @@ void ApiListener::PersistMessage(const Dictionary::Ptr& message, const DynamicOb
|
|||
Dictionary::Ptr pmessage = make_shared<Dictionary>();
|
||||
pmessage->Set("timestamp", ts);
|
||||
|
||||
pmessage->Set("message", JsonSerialize(message));
|
||||
pmessage->Set("message", JsonEncode(message));
|
||||
|
||||
Dictionary::Ptr secname = make_shared<Dictionary>();
|
||||
secname->Set("type", secobj->GetType()->GetName());
|
||||
|
@ -470,7 +471,7 @@ void ApiListener::PersistMessage(const Dictionary::Ptr& message, const DynamicOb
|
|||
|
||||
boost::mutex::scoped_lock lock(m_LogLock);
|
||||
if (m_LogFile) {
|
||||
NetString::WriteStringToStream(m_LogFile, JsonSerialize(pmessage));
|
||||
NetString::WriteStringToStream(m_LogFile, JsonEncode(pmessage));
|
||||
m_LogMessageCount++;
|
||||
SetLogMessageTimestamp(ts);
|
||||
|
||||
|
@ -683,7 +684,7 @@ void ApiListener::ReplayLog(const ApiClient::Ptr& client)
|
|||
if (!NetString::ReadStringFromStream(logStream, &message))
|
||||
break;
|
||||
|
||||
pmessage = JsonDeserialize(message);
|
||||
pmessage = JsonDecode(message);
|
||||
} catch (const std::exception&) {
|
||||
Log(LogWarning, "ApiListener")
|
||||
<< "Unexpected end-of-file for cluster log: " << path;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#include "remote/jsonrpc.hpp"
|
||||
#include "base/netstring.hpp"
|
||||
#include "base/serializer.hpp"
|
||||
#include "base/json.hpp"
|
||||
//#include <iostream>
|
||||
|
||||
using namespace icinga;
|
||||
|
@ -31,7 +31,7 @@ using namespace icinga;
|
|||
*/
|
||||
void JsonRpc::SendMessage(const Stream::Ptr& stream, const Dictionary::Ptr& message)
|
||||
{
|
||||
String json = JsonSerialize(message);
|
||||
String json = JsonEncode(message);
|
||||
//std::cerr << ">> " << json << std::endl;
|
||||
NetString::WriteStringToStream(stream, json);
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ Dictionary::Ptr JsonRpc::ReadMessage(const Stream::Ptr& stream)
|
|||
return Dictionary::Ptr();
|
||||
|
||||
//std::cerr << "<< " << jsonString << std::endl;
|
||||
Value value = JsonDeserialize(jsonString);
|
||||
Value value = JsonDecode(jsonString);
|
||||
|
||||
if (!value.IsObjectType<Dictionary>()) {
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("JSON-RPC"
|
||||
|
|
|
@ -19,10 +19,10 @@ include(BoostTestTargets)
|
|||
|
||||
set(base_test_SOURCES
|
||||
base-array.cpp base-convert.cpp base-dictionary.cpp base-fifo.cpp
|
||||
base-match.cpp base-netstring.cpp base-object.cpp base-serialize.cpp
|
||||
base-shellescape.cpp base-stacktrace.cpp base-stream.cpp
|
||||
base-string.cpp base-timer.cpp base-type.cpp base-value.cpp
|
||||
icinga-perfdata.cpp test.cpp
|
||||
base-json.cpp base-match.cpp base-netstring.cpp base-object.cpp
|
||||
base-serialize.cpp base-shellescape.cpp base-stacktrace.cpp
|
||||
base-stream.cpp base-string.cpp base-timer.cpp base-type.cpp
|
||||
base-value.cpp icinga-perfdata.cpp test.cpp
|
||||
)
|
||||
|
||||
set_property(SOURCE test.cpp PROPERTY EXCLUDE_UNITY_BUILD TRUE)
|
||||
|
@ -54,6 +54,7 @@ add_boost_test(base
|
|||
base_dictionary/json
|
||||
base_fifo/construct
|
||||
base_fifo/io
|
||||
base_json/invalid1
|
||||
base_match/tolong
|
||||
base_netstring/netstring
|
||||
base_object/construct
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#include "base/array.hpp"
|
||||
#include "base/objectlock.hpp"
|
||||
#include "base/serializer.hpp"
|
||||
#include "base/json.hpp"
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
|
@ -133,10 +133,10 @@ BOOST_AUTO_TEST_CASE(json)
|
|||
array->Add(2);
|
||||
array->Add(5);
|
||||
|
||||
String json = JsonSerialize(array);
|
||||
String json = JsonEncode(array);
|
||||
BOOST_CHECK(json.GetLength() > 0);
|
||||
|
||||
Array::Ptr deserialized = JsonDeserialize(json);
|
||||
Array::Ptr deserialized = JsonDecode(json);
|
||||
BOOST_CHECK(deserialized);
|
||||
BOOST_CHECK(deserialized->GetLength() == 3);
|
||||
BOOST_CHECK(deserialized->Get(0) == 7);
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#include "base/dictionary.hpp"
|
||||
#include "base/objectlock.hpp"
|
||||
#include "base/serializer.hpp"
|
||||
#include "base/json.hpp"
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
|
@ -172,9 +172,9 @@ BOOST_AUTO_TEST_CASE(json)
|
|||
dictionary->Set("test1", 7);
|
||||
dictionary->Set("test2", "hello world");
|
||||
|
||||
String json = JsonSerialize(dictionary);
|
||||
String json = JsonEncode(dictionary);
|
||||
BOOST_CHECK(json.GetLength() > 0);
|
||||
Dictionary::Ptr deserialized = JsonDeserialize(json);
|
||||
Dictionary::Ptr deserialized = JsonDecode(json);
|
||||
BOOST_CHECK(deserialized->GetLength() == 2);
|
||||
BOOST_CHECK(deserialized->Get("test1") == 7);
|
||||
BOOST_CHECK(deserialized->Get("test2") == "hello world");
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/******************************************************************************
|
||||
* Icinga 2 *
|
||||
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU General Public License *
|
||||
* as published by the Free Software Foundation; either version 2 *
|
||||
* of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the Free Software Foundation *
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||
******************************************************************************/
|
||||
|
||||
#include "icinga/perfdatavalue.hpp"
|
||||
#include "base/dictionary.hpp"
|
||||
#include "base/objectlock.hpp"
|
||||
#include "base/json.hpp"
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
|
||||
using namespace icinga;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(base_json)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(invalid1)
|
||||
{
|
||||
BOOST_CHECK_THROW(JsonDecode("\"1.7"), std::exception);
|
||||
BOOST_CHECK_THROW(JsonDecode("{8: \"test\"}"), std::exception);
|
||||
BOOST_CHECK_THROW(JsonDecode("{\"test\": \"test\""), std::exception);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -15,8 +15,8 @@
|
|||
# along with this program; if not, write to the Free Software Foundation
|
||||
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
add_subdirectory(cJSON)
|
||||
add_subdirectory(mmatch)
|
||||
add_subdirectory(yajl)
|
||||
|
||||
if(UNIX OR CYGWIN)
|
||||
add_subdirectory(execvpe)
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
# Icinga 2
|
||||
# Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org)
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation
|
||||
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
add_library(cJSON SHARED cJSON.c cJSON.h)
|
||||
|
||||
set_target_properties (
|
||||
cJSON PROPERTIES
|
||||
DEFINE_SYMBOL I2_CJSON_BUILD
|
||||
FOLDER Lib
|
||||
)
|
||||
|
||||
if(UNIX AND NOT HAIKU)
|
||||
target_link_libraries(cJSON m)
|
||||
endif()
|
||||
|
||||
install(
|
||||
TARGETS cJSON
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2
|
||||
)
|
|
@ -1,522 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2009 Dave Gamble
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* cJSON */
|
||||
/* JSON parser in C. */
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <float.h>
|
||||
#include <limits.h>
|
||||
#include <ctype.h>
|
||||
#include "cJSON.h"
|
||||
|
||||
static const char *ep;
|
||||
|
||||
const char *cJSON_GetErrorPtr() {return ep;}
|
||||
|
||||
static int cJSON_strcasecmp(const char *s1,const char *s2)
|
||||
{
|
||||
if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
|
||||
for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0;
|
||||
return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
|
||||
}
|
||||
|
||||
static void *(*cJSON_malloc)(size_t sz) = malloc;
|
||||
static void (*cJSON_free)(void *ptr) = free;
|
||||
|
||||
static char* cJSON_strdup(const char* str)
|
||||
{
|
||||
size_t len;
|
||||
char* copy;
|
||||
|
||||
len = strlen(str) + 1;
|
||||
if (!(copy = (char*)cJSON_malloc(len))) return 0;
|
||||
memcpy(copy,str,len);
|
||||
return copy;
|
||||
}
|
||||
|
||||
void cJSON_InitHooks(cJSON_Hooks* hooks)
|
||||
{
|
||||
if (!hooks) { /* Reset hooks */
|
||||
cJSON_malloc = malloc;
|
||||
cJSON_free = free;
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
|
||||
cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
|
||||
}
|
||||
|
||||
/* Internal constructor. */
|
||||
static cJSON *cJSON_New_Item()
|
||||
{
|
||||
cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
|
||||
if (node) memset(node,0,sizeof(cJSON));
|
||||
return node;
|
||||
}
|
||||
|
||||
/* Delete a cJSON structure. */
|
||||
void cJSON_Delete(cJSON *c)
|
||||
{
|
||||
cJSON *next;
|
||||
while (c)
|
||||
{
|
||||
next=c->next;
|
||||
if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
|
||||
if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
|
||||
if (c->string) cJSON_free(c->string);
|
||||
cJSON_free(c);
|
||||
c=next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse the input text to generate a number, and populate the result into item. */
|
||||
static const char *parse_number(cJSON *item,const char *num)
|
||||
{
|
||||
double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
|
||||
|
||||
/* Could use sscanf for this? */
|
||||
if (*num=='-') sign=-1,num++; /* Has sign? */
|
||||
if (*num=='0') num++; /* is zero */
|
||||
if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */
|
||||
if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */
|
||||
if (*num=='e' || *num=='E') /* Exponent? */
|
||||
{ num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */
|
||||
while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */
|
||||
}
|
||||
|
||||
n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */
|
||||
|
||||
item->valuedouble=n;
|
||||
item->valueint=(int)n;
|
||||
item->type=cJSON_Number;
|
||||
return num;
|
||||
}
|
||||
|
||||
/* Render the number nicely from the given item into a string. */
|
||||
static char *print_number(cJSON *item)
|
||||
{
|
||||
char *str;
|
||||
double d=item->valuedouble;
|
||||
if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
|
||||
{
|
||||
str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */
|
||||
if (str) sprintf(str,"%d",item->valueint);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (d != d)
|
||||
{
|
||||
str=(char*)cJSON_malloc(2);
|
||||
if (str)
|
||||
strcpy(str, "0");
|
||||
}
|
||||
else
|
||||
{
|
||||
str = (char*)cJSON_malloc(64 + (int)log10(fabs(d))); /* This is a nice tradeoff. */
|
||||
if (str)
|
||||
{
|
||||
if (fabs(floor(d) - d) <= DBL_EPSILON) sprintf(str, "%.0f", d);
|
||||
else sprintf(str, "%.*e", (int)log10(d) + 6, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/* Parse the input text into an unescaped cstring, and populate item. */
|
||||
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
|
||||
static const char *parse_string(cJSON *item,const char *str)
|
||||
{
|
||||
const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2;
|
||||
if (*str!='\"') {ep=str;return 0;} /* not a string! */
|
||||
|
||||
while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */
|
||||
|
||||
out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */
|
||||
if (!out) return 0;
|
||||
|
||||
ptr=str+1;ptr2=out;
|
||||
while (*ptr!='\"' && *ptr)
|
||||
{
|
||||
if (*ptr!='\\') *ptr2++=*ptr++;
|
||||
else
|
||||
{
|
||||
ptr++;
|
||||
switch (*ptr)
|
||||
{
|
||||
case 'b': *ptr2++='\b'; break;
|
||||
case 'f': *ptr2++='\f'; break;
|
||||
case 'n': *ptr2++='\n'; break;
|
||||
case 'r': *ptr2++='\r'; break;
|
||||
case 't': *ptr2++='\t'; break;
|
||||
case 'u': /* transcode utf16 to utf8. */
|
||||
sscanf(ptr+1,"%4x",&uc);ptr+=4; /* get the unicode char. */
|
||||
|
||||
if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; // check for invalid.
|
||||
|
||||
if (uc>=0xD800 && uc<=0xDBFF) // UTF16 surrogate pairs.
|
||||
{
|
||||
if (ptr[1]!='\\' || ptr[2]!='u') break; // missing second-half of surrogate.
|
||||
sscanf(ptr+3,"%4x",&uc2);ptr+=6;
|
||||
if (uc2<0xDC00 || uc2>0xDFFF) break; // invalid second-half of surrogate.
|
||||
uc=0x10000 | ((uc&0x3FF)<<10) | (uc2&0x3FF);
|
||||
}
|
||||
|
||||
len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len;
|
||||
|
||||
switch (len) {
|
||||
case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
|
||||
case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
|
||||
case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
|
||||
case 1: *--ptr2 =(uc | firstByteMark[len]);
|
||||
}
|
||||
ptr2+=len;
|
||||
break;
|
||||
default: *ptr2++=*ptr; break;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
*ptr2=0;
|
||||
if (*ptr=='\"') ptr++;
|
||||
item->valuestring=out;
|
||||
item->type=cJSON_String;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/* Render the cstring provided to an escaped version that can be printed. */
|
||||
static char *print_string_ptr(const char *str)
|
||||
{
|
||||
const char *ptr;char *ptr2,*out;int len=0;unsigned char token;
|
||||
|
||||
if (!str) return cJSON_strdup("");
|
||||
ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;}
|
||||
|
||||
out=(char*)cJSON_malloc(len+3);
|
||||
if (!out) return 0;
|
||||
|
||||
ptr2=out;ptr=str;
|
||||
*ptr2++='\"';
|
||||
while (*ptr)
|
||||
{
|
||||
if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
|
||||
else
|
||||
{
|
||||
*ptr2++='\\';
|
||||
switch (token=*ptr++)
|
||||
{
|
||||
case '\\': *ptr2++='\\'; break;
|
||||
case '\"': *ptr2++='\"'; break;
|
||||
case '\b': *ptr2++='b'; break;
|
||||
case '\f': *ptr2++='f'; break;
|
||||
case '\n': *ptr2++='n'; break;
|
||||
case '\r': *ptr2++='r'; break;
|
||||
case '\t': *ptr2++='t'; break;
|
||||
default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */
|
||||
}
|
||||
}
|
||||
}
|
||||
*ptr2++='\"';*ptr2++=0;
|
||||
return out;
|
||||
}
|
||||
/* Invote print_string_ptr (which is useful) on an item. */
|
||||
static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);}
|
||||
|
||||
/* Predeclare these prototypes. */
|
||||
static const char *parse_value(cJSON *item,const char *value);
|
||||
static char *print_value(cJSON *item,int depth,int fmt);
|
||||
static const char *parse_array(cJSON *item,const char *value);
|
||||
static char *print_array(cJSON *item,int depth,int fmt);
|
||||
static const char *parse_object(cJSON *item,const char *value);
|
||||
static char *print_object(cJSON *item,int depth,int fmt);
|
||||
|
||||
/* Utility to jump whitespace and cr/lf */
|
||||
static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
|
||||
|
||||
/* Parse an object - create a new root, and populate. */
|
||||
cJSON *cJSON_Parse(const char *value)
|
||||
{
|
||||
cJSON *c=cJSON_New_Item();
|
||||
ep=0;
|
||||
if (!c) return 0; /* memory fail */
|
||||
|
||||
if (!parse_value(c,skip(value))) {cJSON_Delete(c);return 0;}
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Render a cJSON item/entity/structure to text. */
|
||||
char *cJSON_Print(cJSON *item) {return print_value(item,0,1);}
|
||||
char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);}
|
||||
|
||||
/* Parser core - when encountering text, process appropriately. */
|
||||
static const char *parse_value(cJSON *item,const char *value)
|
||||
{
|
||||
if (!value) return 0; /* Fail on null. */
|
||||
if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; }
|
||||
if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; }
|
||||
if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; }
|
||||
if (*value=='\"') { return parse_string(item,value); }
|
||||
if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); }
|
||||
if (*value=='[') { return parse_array(item,value); }
|
||||
if (*value=='{') { return parse_object(item,value); }
|
||||
|
||||
ep=value;return 0; /* failure. */
|
||||
}
|
||||
|
||||
/* Render a value to text. */
|
||||
static char *print_value(cJSON *item,int depth,int fmt)
|
||||
{
|
||||
char *out=0;
|
||||
if (!item) return 0;
|
||||
switch ((item->type)&255)
|
||||
{
|
||||
case cJSON_NULL: out=cJSON_strdup("null"); break;
|
||||
case cJSON_False: out=cJSON_strdup("false");break;
|
||||
case cJSON_True: out=cJSON_strdup("true"); break;
|
||||
case cJSON_Number: out=print_number(item);break;
|
||||
case cJSON_String: out=print_string(item);break;
|
||||
case cJSON_Array: out=print_array(item,depth,fmt);break;
|
||||
case cJSON_Object: out=print_object(item,depth,fmt);break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Build an array from input text. */
|
||||
static const char *parse_array(cJSON *item,const char *value)
|
||||
{
|
||||
cJSON *child;
|
||||
if (*value!='[') {ep=value;return 0;} /* not an array! */
|
||||
|
||||
item->type=cJSON_Array;
|
||||
value=skip(value+1);
|
||||
if (*value==']') return value+1; /* empty array. */
|
||||
|
||||
item->child=child=cJSON_New_Item();
|
||||
if (!item->child) return 0; /* memory fail */
|
||||
value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */
|
||||
if (!value) return 0;
|
||||
|
||||
while (*value==',')
|
||||
{
|
||||
cJSON *new_item;
|
||||
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
|
||||
child->next=new_item;new_item->prev=child;child=new_item;
|
||||
value=skip(parse_value(child,skip(value+1)));
|
||||
if (!value) return 0; /* memory fail */
|
||||
}
|
||||
|
||||
if (*value==']') return value+1; /* end of array */
|
||||
ep=value;return 0; /* malformed. */
|
||||
}
|
||||
|
||||
/* Render an array to text */
|
||||
static char *print_array(cJSON *item,int depth,int fmt)
|
||||
{
|
||||
char **entries;
|
||||
char *out=0,*ptr,*ret;int len=5;
|
||||
cJSON *child=item->child;
|
||||
int numentries=0,i=0,fail=0;
|
||||
|
||||
/* How many entries in the array? */
|
||||
while (child) numentries++,child=child->next;
|
||||
/* Allocate an array to hold the values for each */
|
||||
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
|
||||
if (!entries) return 0;
|
||||
memset(entries,0,numentries*sizeof(char*));
|
||||
/* Retrieve all the results: */
|
||||
child=item->child;
|
||||
while (child && !fail)
|
||||
{
|
||||
ret=print_value(child,depth+1,fmt);
|
||||
entries[i++]=ret;
|
||||
if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
|
||||
child=child->next;
|
||||
}
|
||||
|
||||
/* If we didn't fail, try to malloc the output string */
|
||||
if (!fail) out=(char*)cJSON_malloc(len);
|
||||
/* If that fails, we fail. */
|
||||
if (!out) fail=1;
|
||||
|
||||
/* Handle failure. */
|
||||
if (fail)
|
||||
{
|
||||
for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);
|
||||
cJSON_free(entries);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Compose the output array. */
|
||||
*out='[';
|
||||
ptr=out+1;*ptr=0;
|
||||
for (i=0;i<numentries;i++)
|
||||
{
|
||||
strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
|
||||
if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
|
||||
cJSON_free(entries[i]);
|
||||
}
|
||||
cJSON_free(entries);
|
||||
*ptr++=']';*ptr++=0;
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Build an object from the text. */
|
||||
static const char *parse_object(cJSON *item,const char *value)
|
||||
{
|
||||
cJSON *child;
|
||||
if (*value!='{') {ep=value;return 0;} /* not an object! */
|
||||
|
||||
item->type=cJSON_Object;
|
||||
value=skip(value+1);
|
||||
if (*value=='}') return value+1; /* empty array. */
|
||||
|
||||
item->child=child=cJSON_New_Item();
|
||||
if (!item->child) return 0;
|
||||
value=skip(parse_string(child,skip(value)));
|
||||
if (!value) return 0;
|
||||
child->string=child->valuestring;child->valuestring=0;
|
||||
if (*value!=':') {ep=value;return 0;} /* fail! */
|
||||
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
|
||||
if (!value) return 0;
|
||||
|
||||
while (*value==',')
|
||||
{
|
||||
cJSON *new_item;
|
||||
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
|
||||
child->next=new_item;new_item->prev=child;child=new_item;
|
||||
value=skip(parse_string(child,skip(value+1)));
|
||||
if (!value) return 0;
|
||||
child->string=child->valuestring;child->valuestring=0;
|
||||
if (*value!=':') {ep=value;return 0;} /* fail! */
|
||||
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
|
||||
if (!value) return 0;
|
||||
}
|
||||
|
||||
if (*value=='}') return value+1; /* end of array */
|
||||
ep=value;return 0; /* malformed. */
|
||||
}
|
||||
|
||||
/* Render an object to text. */
|
||||
static char *print_object(cJSON *item,int depth,int fmt)
|
||||
{
|
||||
char **entries=0,**names=0;
|
||||
char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
|
||||
cJSON *child=item->child;
|
||||
int numentries=0,fail=0;
|
||||
/* Count the number of entries. */
|
||||
while (child) numentries++,child=child->next;
|
||||
/* Allocate space for the names and the objects */
|
||||
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
|
||||
if (!entries) return 0;
|
||||
names=(char**)cJSON_malloc(numentries*sizeof(char*));
|
||||
if (!names) {cJSON_free(entries);return 0;}
|
||||
memset(entries,0,sizeof(char*)*numentries);
|
||||
memset(names,0,sizeof(char*)*numentries);
|
||||
|
||||
/* Collect all the results into our arrays: */
|
||||
child=item->child;depth++;if (fmt) len+=depth;
|
||||
while (child)
|
||||
{
|
||||
names[i]=str=print_string_ptr(child->string);
|
||||
entries[i++]=ret=print_value(child,depth,fmt);
|
||||
if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
|
||||
child=child->next;
|
||||
}
|
||||
|
||||
/* Try to allocate the output string */
|
||||
if (!fail) out=(char*)cJSON_malloc(len);
|
||||
if (!out) fail=1;
|
||||
|
||||
/* Handle failure */
|
||||
if (fail)
|
||||
{
|
||||
for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);}
|
||||
cJSON_free(names);cJSON_free(entries);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Compose the output: */
|
||||
*out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
|
||||
for (i=0;i<numentries;i++)
|
||||
{
|
||||
if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
|
||||
strcpy(ptr,names[i]);ptr+=strlen(names[i]);
|
||||
*ptr++=':';if (fmt) *ptr++='\t';
|
||||
strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
|
||||
if (i!=numentries-1) *ptr++=',';
|
||||
if (fmt) *ptr++='\n';*ptr=0;
|
||||
cJSON_free(names[i]);cJSON_free(entries[i]);
|
||||
}
|
||||
|
||||
cJSON_free(names);cJSON_free(entries);
|
||||
if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
|
||||
*ptr++='}';*ptr++=0;
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Get Array size/item / object item. */
|
||||
int cJSON_GetArraySize(cJSON *array) {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
|
||||
cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;}
|
||||
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
|
||||
|
||||
/* Utility for array list handling. */
|
||||
static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
|
||||
/* Utility for handling references. */
|
||||
static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;}
|
||||
|
||||
/* Add item to array/object. */
|
||||
void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
|
||||
void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
|
||||
void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));}
|
||||
void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));}
|
||||
|
||||
cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0;
|
||||
if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;}
|
||||
void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));}
|
||||
cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;}
|
||||
void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}
|
||||
|
||||
/* Replace array/object items with new ones. */
|
||||
void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;
|
||||
newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;
|
||||
if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}
|
||||
void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}}
|
||||
|
||||
/* Create basic types: */
|
||||
cJSON *cJSON_CreateNull() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;}
|
||||
cJSON *cJSON_CreateTrue() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;}
|
||||
cJSON *cJSON_CreateFalse() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;}
|
||||
cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;}
|
||||
cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;}
|
||||
cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;}
|
||||
cJSON *cJSON_CreateArray() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;}
|
||||
cJSON *cJSON_CreateObject() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;}
|
||||
|
||||
/* Create Arrays: */
|
||||
cJSON *cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
|
||||
cJSON *cJSON_CreateFloatArray(float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
|
||||
cJSON *cJSON_CreateDoubleArray(double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
|
||||
cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
|
|
@ -1,135 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2009 Dave Gamble
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef cJSON__h
|
||||
#define cJSON__h
|
||||
|
||||
#include "base/visibility.hpp"
|
||||
|
||||
#ifdef I2_CJSON_BUILD
|
||||
# define I2_CJSON_API I2_EXPORT
|
||||
#else
|
||||
# define I2_CJSON_API I2_IMPORT
|
||||
#endif /* I2_CJSON_BUILD */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* cJSON Types: */
|
||||
#define cJSON_False 0
|
||||
#define cJSON_True 1
|
||||
#define cJSON_NULL 2
|
||||
#define cJSON_Number 3
|
||||
#define cJSON_String 4
|
||||
#define cJSON_Array 5
|
||||
#define cJSON_Object 6
|
||||
|
||||
#define cJSON_IsReference 256
|
||||
|
||||
/* The cJSON structure: */
|
||||
typedef struct cJSON {
|
||||
struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
|
||||
struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
|
||||
|
||||
int type; /* The type of the item, as above. */
|
||||
|
||||
char *valuestring; /* The item's string, if type==cJSON_String */
|
||||
int valueint; /* The item's number, if type==cJSON_Number */
|
||||
double valuedouble; /* The item's number, if type==cJSON_Number */
|
||||
|
||||
char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
|
||||
} cJSON;
|
||||
|
||||
typedef struct cJSON_Hooks {
|
||||
void *(*malloc_fn)(size_t sz);
|
||||
void (*free_fn)(void *ptr);
|
||||
} cJSON_Hooks;
|
||||
|
||||
/* Supply malloc, realloc and free functions to cJSON */
|
||||
extern I2_CJSON_API void cJSON_InitHooks(cJSON_Hooks* hooks);
|
||||
|
||||
|
||||
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
|
||||
extern I2_CJSON_API cJSON *cJSON_Parse(const char *value);
|
||||
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
|
||||
extern I2_CJSON_API char *cJSON_Print(cJSON *item);
|
||||
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
|
||||
extern I2_CJSON_API char *cJSON_PrintUnformatted(cJSON *item);
|
||||
/* Delete a cJSON entity and all subentities. */
|
||||
extern I2_CJSON_API void cJSON_Delete(cJSON *c);
|
||||
|
||||
/* Returns the number of items in an array (or object). */
|
||||
extern I2_CJSON_API int cJSON_GetArraySize(cJSON *array);
|
||||
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
|
||||
extern I2_CJSON_API cJSON *cJSON_GetArrayItem(cJSON *array,int item);
|
||||
/* Get item "string" from object. Case insensitive. */
|
||||
extern I2_CJSON_API cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
|
||||
|
||||
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
|
||||
extern I2_CJSON_API const char *cJSON_GetErrorPtr();
|
||||
|
||||
/* These calls create a cJSON item of the appropriate type. */
|
||||
extern I2_CJSON_API cJSON *cJSON_CreateNull();
|
||||
extern I2_CJSON_API cJSON *cJSON_CreateTrue();
|
||||
extern I2_CJSON_API cJSON *cJSON_CreateFalse();
|
||||
extern I2_CJSON_API cJSON *cJSON_CreateBool(int b);
|
||||
extern I2_CJSON_API cJSON *cJSON_CreateNumber(double num);
|
||||
extern I2_CJSON_API cJSON *cJSON_CreateString(const char *string);
|
||||
extern I2_CJSON_API cJSON *cJSON_CreateArray();
|
||||
extern I2_CJSON_API cJSON *cJSON_CreateObject();
|
||||
|
||||
/* These utilities create an Array of count items. */
|
||||
extern I2_CJSON_API cJSON *cJSON_CreateIntArray(int *numbers,int count);
|
||||
extern I2_CJSON_API cJSON *cJSON_CreateFloatArray(float *numbers,int count);
|
||||
extern I2_CJSON_API cJSON *cJSON_CreateDoubleArray(double *numbers,int count);
|
||||
extern I2_CJSON_API cJSON *cJSON_CreateStringArray(const char **strings,int count);
|
||||
|
||||
/* Append item to the specified array/object. */
|
||||
extern I2_CJSON_API void cJSON_AddItemToArray(cJSON *array, cJSON *item);
|
||||
extern I2_CJSON_API void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
|
||||
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
|
||||
extern I2_CJSON_API void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
|
||||
extern I2_CJSON_API void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
|
||||
|
||||
/* Remove/Detatch items from Arrays/Objects. */
|
||||
extern I2_CJSON_API cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
|
||||
extern I2_CJSON_API void cJSON_DeleteItemFromArray(cJSON *array,int which);
|
||||
extern I2_CJSON_API cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
|
||||
extern I2_CJSON_API void cJSON_DeleteItemFromObject(cJSON *object,const char *string);
|
||||
|
||||
/* Update array items. */
|
||||
extern I2_CJSON_API void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
|
||||
extern I2_CJSON_API void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
|
||||
|
||||
#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
|
||||
#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
|
||||
#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
|
||||
#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
|
||||
#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,3 @@
|
|||
.DS_Store
|
||||
Makefile
|
||||
/build/
|
|
@ -0,0 +1,23 @@
|
|||
Short story (If you already have ruby and cmake):
|
||||
|
||||
./configure && make install
|
||||
|
||||
When things go wrong:
|
||||
|
||||
attain CMake (http://www.cmake.org) and ruby (http://ruby-lang.org) and
|
||||
try again.
|
||||
|
||||
OR, attain CMake and build by hand:
|
||||
|
||||
1. mkdir build
|
||||
2. cd build
|
||||
3. cmake ..
|
||||
4. make
|
||||
5. build output left in yajl-X.Y.Z
|
||||
|
||||
NOTE: for 64-bit systems where lib64 is used you can pass the cmake
|
||||
variable LIB_SUFFIX to cause installation into the system's 'lib64'
|
||||
directory.
|
||||
|
||||
best,
|
||||
lloyd
|
|
@ -0,0 +1,27 @@
|
|||
YAJL has been successfully built using Visual Studio 8. CMake, a
|
||||
build file generator, is used to build the software. CMake supports
|
||||
several different build environments, so you may either build YAJL
|
||||
using the IDE via the following steps:
|
||||
|
||||
1. acquire cmake (http://www.cmake.org)
|
||||
2. mkdir build
|
||||
3. cd build
|
||||
4. cmake ..
|
||||
5. devenv YetAnotherJSONParser.sln /project ALL_BUILD /build Release
|
||||
6. build output is left in build/yajl-X.Y.Z
|
||||
|
||||
Or you can build from the command line using nmake:
|
||||
|
||||
1. Click Start > Programs > Microsoft Visual Studio > Visual Studio
|
||||
Tools > Visual Studio Command Prompt -- for your version of Visual
|
||||
Studio, which will open a command prompt. You may verify that the
|
||||
compiler is in your path by typing "cl /?" at the prompt.
|
||||
2. cd C:\path\to\yajl\source\
|
||||
3. mkdir build
|
||||
4. cd build
|
||||
5. cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Release ..
|
||||
6. nmake
|
||||
7. nmake install
|
||||
|
||||
Earlier versions of visual studio and other build generators haven't
|
||||
been thoroughly tested, but should work without any major issues.
|
|
@ -0,0 +1,46 @@
|
|||
# Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
|
||||
|
||||
PROJECT(YetAnotherJSONParser C)
|
||||
|
||||
SET (YAJL_MAJOR 2)
|
||||
SET (YAJL_MINOR 1)
|
||||
SET (YAJL_MICRO 0)
|
||||
|
||||
IF (WIN32)
|
||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W4")
|
||||
ADD_DEFINITIONS(-DWIN32)
|
||||
|
||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4996 /wd4255 /wd4130 /wd4100 /wd4711")
|
||||
SET(CMAKE_C_FLAGS_DEBUG "/D DEBUG /Od /Z7")
|
||||
SET(CMAKE_C_FLAGS_RELEASE "/D NDEBUG /O2")
|
||||
ELSE (WIN32)
|
||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
|
||||
IF(CMAKE_COMPILER_IS_GNUCC)
|
||||
INCLUDE(CheckCCompilerFlag)
|
||||
CHECK_C_COMPILER_FLAG(-fvisibility=hidden HAVE_GCC_VISIBILITY)
|
||||
IF(HAVE_GCC_VISIBILITY)
|
||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden")
|
||||
ENDIF(HAVE_GCC_VISIBILITY)
|
||||
ENDIF(CMAKE_COMPILER_IS_GNUCC)
|
||||
SET(CMAKE_C_FLAGS
|
||||
"${CMAKE_C_FLAGS} -std=c99 -pedantic -Wpointer-arith -Wno-format-y2k -Wstrict-prototypes -Wmissing-declarations -Wnested-externs -Wextra -Wundef -Wwrite-strings -Wold-style-definition -Wredundant-decls -Wno-unused-parameter -Wno-sign-compare -Wmissing-prototypes")
|
||||
|
||||
SET(CMAKE_C_FLAGS_DEBUG "-DDEBUG")
|
||||
SET(CMAKE_C_FLAGS_RELEASE "-DNDEBUG -Wuninitialized")
|
||||
ENDIF (WIN32)
|
||||
|
||||
ADD_SUBDIRECTORY(src)
|
|
@ -0,0 +1,13 @@
|
|||
Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@ -0,0 +1,189 @@
|
|||
2.1.0
|
||||
* @nonodename, @patperry - fixed some compiler warnings
|
||||
* @yep, @emaste - documentation improvements
|
||||
* @sgravrock - build fix for NetBSD (and whenever sh != bash)
|
||||
* @rotty, @brimstone3, @lloyd - allow client to reset generator
|
||||
* @sgravrock - remove bash dependencies
|
||||
* @lloyd - add api tests
|
||||
* @rflynn - remove ruby dependency
|
||||
* @cloderic - nmake install works on windows
|
||||
* @shahbag - build fix for qnx
|
||||
* @breese - debugging improvements
|
||||
* @lloyd - json_verify supports -s flag for stream processing
|
||||
* @lloyd - json_reformat supports -s flag for stream processing
|
||||
|
||||
2.0.4
|
||||
* @jcekstrom - additional checking in integer parsing
|
||||
* @jcekstrom - fix a bug in yajl_tree that would cause valid json integersto fail to parse
|
||||
* @plaguemorin - fix a memory leak in yajl_tree (error strings were being leaked)
|
||||
* @7AC - reset errno
|
||||
* @ConradIrwin - include flags to reformatter to allow toggling of escape solidus option
|
||||
|
||||
2.0.3
|
||||
* John Stamp generation of a pkgconfig file at build time.
|
||||
* @robzuber bugfix in yajl_tree_get()
|
||||
* @lloyd - fix for compilation on 64 bit windows
|
||||
|
||||
2.0.2
|
||||
* lth fix typos in yajl_tree.h macros YAJL_IS_INTEGER and YAJL_IS_DOUBLE,
|
||||
contributed by Artem S Vybornov.
|
||||
* lth add #ifdef __cplusplus wrappers to yajl_tree to allow proper
|
||||
usage from many populer C++ compilers.
|
||||
|
||||
2.0.1
|
||||
* lth generator flag to allow client to specify they want
|
||||
escaped solidi '/'. issue #28
|
||||
* lth crash fix when yajl_parse() is never called. issue #27
|
||||
|
||||
2.0.0
|
||||
* lth YAJL is now ISC licensed: http://en.wikipedia.org/wiki/ISC_license
|
||||
* lth 20-35% (osx and linux respectively) parsing performance
|
||||
improvement attained by tweaking string scanning (idea: @michaelrhanson).
|
||||
* Florian Forster & lth - yajl_tree interface introduced as a higher level
|
||||
interface to the parser (eats JSON, poops a memory representation)
|
||||
* lth require a C99 compiler
|
||||
* lth integers are now represented with long long (64bit+) on all platforms.
|
||||
* lth size_t now used throughout to represent buffer lengths, so you can
|
||||
safely manage buffers greater than 4GB.
|
||||
* gno semantic improvements to yajl's API regarding partial value parsing and
|
||||
trailing garbage
|
||||
* lth new configuration mechanism for yajl, see yajl_config() and
|
||||
yajl_gen_config()
|
||||
* gno more allocation checking in more places
|
||||
* gno remove usage of strtol, replace with custom implementation that cares
|
||||
not about your locale.
|
||||
* lth yajl_parse_complete renamed to yajl_complete_parse.
|
||||
* lth add a switch to validate utf8 strings as they are generated.
|
||||
* lth tests are a lot quieter in their output.
|
||||
* lth addition of a little in tree performance benchmark, `perftest` in
|
||||
perf/perftest.c
|
||||
|
||||
1.0.12
|
||||
* Conrad Irwin - Parse null bytes correctly
|
||||
* Mirek Rusin - fix LLVM warnings
|
||||
* gno - Don't generate numbers for keys. closes #13
|
||||
* lth - various win32 fixes, including build documentation improvements
|
||||
* John Stamp - Don't export private symbols.
|
||||
* John Stamp - Install yajl_version.h, not the template.
|
||||
* John Stamp - Don't use -fPIC for static lib. Cmake will automatically add it for the shared.
|
||||
* lth 0 fix paths embedded in dylib upon installation on osx. closes #11
|
||||
|
||||
1.0.11
|
||||
* lth remove -Wno-missing-field-initializers for greater gcc compat (3.4.6)
|
||||
|
||||
1.0.10
|
||||
* Brian Maher - yajl is now buildable without a c++ compiler present
|
||||
* Brian Maher - fix header installation on OSX with cmake 2.8.0 installed
|
||||
* lth & vitali - allow builder to specify alternate lib directory
|
||||
for installation (i.e. lib64)
|
||||
* Vitali Lovich - yajl version number now programatically accessible
|
||||
* lth - prevent cmake from embedding rpaths in binaries. Static linking
|
||||
makes this unneccesary.
|
||||
|
||||
1.0.9
|
||||
* lth - fix inverted logic causing yajl_gen_double() to always fail on
|
||||
win32 (thanks to Fredrik Kihlander for the report)
|
||||
|
||||
1.0.8
|
||||
* Randall E. Barker - move dllexport defnitions so dlls with proper
|
||||
exports can again be generated on windows
|
||||
* lth - add yajl_get_bytes_consumed() which allows the client to
|
||||
determine the offset as an error, as well as determine how
|
||||
many bytes of an input buffer were consumed.
|
||||
* lth - fixes to keep "error offset" up to date (like when the
|
||||
client callback returns 0)
|
||||
* Brian Maher - allow client to specify a printing function in
|
||||
generation
|
||||
|
||||
1.0.7
|
||||
* lth fix win32 build (isinf and isnan)
|
||||
|
||||
1.0.6
|
||||
* lth fix several compiler warnings
|
||||
* lth fix generation of invalid json from yajl_gen_double
|
||||
(NaN is not JSON)
|
||||
* jstamp support for combining short options in tools
|
||||
* jstamp exit properly on errors from tools
|
||||
* octo test success no longer depends on integer size
|
||||
* max fix configure --prefix
|
||||
|
||||
1.0.5
|
||||
* lth several performance improvements related to function
|
||||
inlinin'
|
||||
|
||||
1.0.4
|
||||
* lth fix broken utf8 validation for three & four byte represenations.
|
||||
thanks to http://github.com/brianmario and
|
||||
http://github.com/technoweenie
|
||||
|
||||
1.0.3
|
||||
* lth fix syntax error in cplusplus extern "C" statements for wider
|
||||
compiler support
|
||||
|
||||
1.0.2
|
||||
* lth update doxygen documentation with new sample code, passing NULL
|
||||
for allocation functions added in 1.0.0
|
||||
|
||||
1.0.1
|
||||
* lth resolve crash in json_reformatter due to incorrectly ordered
|
||||
parameters.
|
||||
|
||||
1.0.0
|
||||
* lth add 'make install' rules, thaks to Andrei Soroker for the
|
||||
contribution.
|
||||
* lth client may override allocation routines at generator or parser
|
||||
allocation time
|
||||
* tjw add yajl_parse_complete routine to allow client to explicitly
|
||||
specify end-of-input, solving the "lonely number" case, where
|
||||
json text consists only of an element with no explicit syntactic
|
||||
end.
|
||||
* tjw many new test cases
|
||||
* tjw cleanup of code for symmetry and ease of reading
|
||||
* lth integration of patches from Robert Varga which cleanup
|
||||
compilation warnings on 64 bit linux
|
||||
|
||||
0.4.0
|
||||
* lth buffer overflow bug in yajl_gen_double s/%lf/%g/ - thanks to
|
||||
Eric Bergstrome
|
||||
* lth yajl_number callback to allow passthrough of arbitrary precision
|
||||
numbers to client. Thanks to Hatem Nassrat.
|
||||
* lth yajl_integer now deals in long, instead of long long. This
|
||||
combined with yajl_number improves compiler compatibility while
|
||||
maintaining precision.
|
||||
* lth better ./configure && make experience (still requires cmake and
|
||||
ruby)
|
||||
* lth fix handling of special characters hex 0F and 1F in yajl_encode
|
||||
(thanks to Robert Geiger)
|
||||
* lth allow leading zeros in exponents (thanks to Hatem Nassrat)
|
||||
|
||||
0.3.0
|
||||
* lth doxygen documentation (html & man) generated as part of the
|
||||
build
|
||||
* lth many documentation updates.
|
||||
* lth fix to work with older versions of cmake (don't use LOOSE_LOOP
|
||||
constructs)
|
||||
* lth work around different behavior of freebsd 4 scanf. initialize
|
||||
parameter to scanf to zero.
|
||||
* lth all tests run 32x with ranging buffer sizes to stress stream
|
||||
parsing
|
||||
* lth yajl_test accepts -b option to allow read buffer size to be
|
||||
set
|
||||
* lth option to validate UTF8 added to parser (argument in
|
||||
yajl_parser_cfg)
|
||||
* lth fix buffer overrun when chunk ends inside \u escaped text
|
||||
* lth support client cancelation
|
||||
|
||||
0.2.2
|
||||
* lth on windows build debug with C7 symbols and no pdb files.
|
||||
|
||||
0.2.1
|
||||
* fix yajl_reformat and yajl_verify to work on arbitrarily sized
|
||||
inputs.
|
||||
* fix win32 build break, clean up all errors and warnings.
|
||||
* fix optimized build flags.
|
||||
|
||||
0.2.0
|
||||
* optionally support comments in input text
|
||||
|
||||
0.1.0
|
||||
* Initial release
|
|
@ -0,0 +1,74 @@
|
|||
**********************************************************************
|
||||
This is YAJL 2. For the legacy version of YAJL see
|
||||
https://github.com/lloyd/yajl/tree/1.x
|
||||
**********************************************************************
|
||||
|
||||
Welcome to Yet Another JSON Library (YAJL)
|
||||
|
||||
## Why does the world need another C library for parsing JSON?
|
||||
|
||||
Good question. In a review of current C JSON parsing libraries I was
|
||||
unable to find one that satisfies my requirements. Those are,
|
||||
0. written in C
|
||||
1. portable
|
||||
2. robust -- as close to "crash proof" as possible
|
||||
3. data representation independent
|
||||
4. fast
|
||||
5. generates verbose, useful error messages including context of where
|
||||
the error occurs in the input text.
|
||||
6. can parse JSON data off a stream, incrementally
|
||||
7. simple to use
|
||||
8. tiny
|
||||
|
||||
Numbers 3, 5, 6, and 7 were particularly hard to find, and were what
|
||||
caused me to ultimately create YAJL. This document is a tour of some
|
||||
of the more important aspects of YAJL.
|
||||
|
||||
## YAJL is Free.
|
||||
|
||||
Permissive licensing means you can use it in open source and
|
||||
commercial products alike without any fees. My request beyond the
|
||||
licensing is that if you find bugs drop me a email, or better yet,
|
||||
fork and fix.
|
||||
|
||||
Porting YAJL should be trivial, the implementation is ANSI C. If you
|
||||
port to new systems I'd love to hear of it and integrate your patches.
|
||||
|
||||
## YAJL is data representation independent.
|
||||
|
||||
BYODR! Many JSON libraries impose a structure based data representation
|
||||
on you. This is a benefit in some cases and a drawback in others.
|
||||
YAJL uses callbacks to remain agnostic of the in-memory representation.
|
||||
So if you wish to build up an in-memory representation, you may do so
|
||||
using YAJL, but you must bring the code that defines and populates the
|
||||
in memory structure.
|
||||
|
||||
This also means that YAJL can be used by other (higher level) JSON
|
||||
libraries if so desired.
|
||||
|
||||
## YAJL supports stream parsing
|
||||
|
||||
This means you do not need to hold the whole JSON representation in
|
||||
textual form in memory. This makes YAJL ideal for filtering projects,
|
||||
where you're converting YAJL from one form to another (i.e. XML). The
|
||||
included JSON pretty printer is an example of such a filter program.
|
||||
|
||||
## YAJL is fast
|
||||
|
||||
Minimal memory copying is performed. YAJL, when possible, returns
|
||||
pointers into the client provided text (i.e. for strings that have no
|
||||
embedded escape chars, hopefully the common case). I've put a lot of
|
||||
effort into profiling and tuning performance, but I have ignored a
|
||||
couple possible performance improvements to keep the interface clean,
|
||||
small, and flexible. My hope is that YAJL will perform comparably to
|
||||
the fastest JSON parser out there.
|
||||
|
||||
YAJL should impose both minimal CPU and memory requirements on your
|
||||
application.
|
||||
|
||||
## YAJL is tiny.
|
||||
|
||||
Fat free. No whip.
|
||||
|
||||
enjoy,
|
||||
Lloyd - July, 2007
|
|
@ -0,0 +1,9 @@
|
|||
* add a test for 0x1F bug
|
||||
* numeric overflow in integers and double
|
||||
* line and char offsets in the lexer and in error messages
|
||||
* testing:
|
||||
a. the permuter
|
||||
b. some performance comparison against json_checker.
|
||||
* investigate pull instead of push parsing
|
||||
* Handle memory allocation failures gracefully
|
||||
* cygwin/msys support on win32
|
|
@ -0,0 +1,62 @@
|
|||
# Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
SET (SRCS yajl.c yajl_lex.c yajl_parser.c yajl_buf.c
|
||||
yajl_encode.c yajl_gen.c yajl_alloc.c
|
||||
yajl_tree.c yajl_version.c
|
||||
)
|
||||
SET (HDRS yajl_parser.h yajl_lex.h yajl_buf.h yajl_encode.h yajl_alloc.h)
|
||||
SET (PUB_HDRS api/yajl_parse.h api/yajl_gen.h api/yajl_common.h api/yajl_tree.h)
|
||||
|
||||
# useful when fixing lexer bugs.
|
||||
#ADD_DEFINITIONS(-DYAJL_LEXER_DEBUG)
|
||||
|
||||
# Ensure defined when building YAJL (as opposed to using it from
|
||||
# another project). Used to ensure correct function export when
|
||||
# building win32 DLL.
|
||||
ADD_DEFINITIONS(-DYAJL_BUILD)
|
||||
|
||||
# set up some paths
|
||||
SET (incDir ${CMAKE_CURRENT_BINARY_DIR}/../${YAJL_DIST_NAME}/include/yajl)
|
||||
|
||||
ADD_LIBRARY(yajl SHARED ${SRCS} ${HDRS} ${PUB_HDRS})
|
||||
|
||||
#### setup shared library version number
|
||||
SET_TARGET_PROPERTIES(yajl PROPERTIES
|
||||
INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR}/icinga2
|
||||
DEFINE_SYMBOL YAJL_SHARED
|
||||
SOVERSION ${YAJL_MAJOR}
|
||||
VERSION ${YAJL_MAJOR}.${YAJL_MINOR}.${YAJL_MICRO}
|
||||
FOLDER Lib)
|
||||
|
||||
#### build up an sdk as a post build step
|
||||
|
||||
# create some directories
|
||||
FILE(MAKE_DIRECTORY ${incDir})
|
||||
|
||||
# generate build-time source
|
||||
CONFIGURE_FILE(api/yajl_version.h.cmake ${incDir}/yajl_version.h)
|
||||
|
||||
# copy public headers to output directory
|
||||
FOREACH (header ${PUB_HDRS})
|
||||
SET (header "${CMAKE_CURRENT_SOURCE_DIR}/${header}")
|
||||
|
||||
EXEC_PROGRAM(${CMAKE_COMMAND} ARGS -E copy_if_different \"${header}\" \"${incDir}\")
|
||||
ENDFOREACH (header ${PUB_HDRS})
|
||||
|
||||
INCLUDE_DIRECTORIES(${incDir}/..)
|
||||
|
||||
INSTALL(TARGETS yajl
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2)
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __YAJL_COMMON_H__
|
||||
#define __YAJL_COMMON_H__
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define YAJL_MAX_DEPTH 128
|
||||
|
||||
/* msft dll export gunk. To build a DLL on windows, you
|
||||
* must define WIN32, YAJL_SHARED, and YAJL_BUILD. To use a shared
|
||||
* DLL, you must define YAJL_SHARED and WIN32 */
|
||||
#if (defined(_WIN32) || defined(WIN32)) && defined(YAJL_SHARED)
|
||||
# ifdef YAJL_BUILD
|
||||
# define YAJL_API __declspec(dllexport)
|
||||
# else
|
||||
# define YAJL_API __declspec(dllimport)
|
||||
# endif
|
||||
#else
|
||||
# if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 303
|
||||
# define YAJL_API __attribute__ ((visibility("default")))
|
||||
# else
|
||||
# define YAJL_API
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/** pointer to a malloc function, supporting client overriding memory
|
||||
* allocation routines */
|
||||
typedef void * (*yajl_malloc_func)(void *ctx, size_t sz);
|
||||
|
||||
/** pointer to a free function, supporting client overriding memory
|
||||
* allocation routines */
|
||||
typedef void (*yajl_free_func)(void *ctx, void * ptr);
|
||||
|
||||
/** pointer to a realloc function which can resize an allocation. */
|
||||
typedef void * (*yajl_realloc_func)(void *ctx, void * ptr, size_t sz);
|
||||
|
||||
/** A structure which can be passed to yajl_*_alloc routines to allow the
|
||||
* client to specify memory allocation functions to be used. */
|
||||
typedef struct
|
||||
{
|
||||
/** pointer to a function that can allocate uninitialized memory */
|
||||
yajl_malloc_func malloc;
|
||||
/** pointer to a function that can resize memory allocations */
|
||||
yajl_realloc_func realloc;
|
||||
/** pointer to a function that can free memory allocated using
|
||||
* reallocFunction or mallocFunction */
|
||||
yajl_free_func free;
|
||||
/** a context pointer that will be passed to above allocation routines */
|
||||
void * ctx;
|
||||
} yajl_alloc_funcs;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file yajl_gen.h
|
||||
* Interface to YAJL's JSON generation facilities.
|
||||
*/
|
||||
|
||||
#include <yajl/yajl_common.h>
|
||||
|
||||
#ifndef __YAJL_GEN_H__
|
||||
#define __YAJL_GEN_H__
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/** generator status codes */
|
||||
typedef enum {
|
||||
/** no error */
|
||||
yajl_gen_status_ok = 0,
|
||||
/** at a point where a map key is generated, a function other than
|
||||
* yajl_gen_string was called */
|
||||
yajl_gen_keys_must_be_strings,
|
||||
/** YAJL's maximum generation depth was exceeded. see
|
||||
* YAJL_MAX_DEPTH */
|
||||
yajl_max_depth_exceeded,
|
||||
/** A generator function (yajl_gen_XXX) was called while in an error
|
||||
* state */
|
||||
yajl_gen_in_error_state,
|
||||
/** A complete JSON document has been generated */
|
||||
yajl_gen_generation_complete,
|
||||
/** yajl_gen_double was passed an invalid floating point value
|
||||
* (infinity or NaN). */
|
||||
yajl_gen_invalid_number,
|
||||
/** A print callback was passed in, so there is no internal
|
||||
* buffer to get from */
|
||||
yajl_gen_no_buf,
|
||||
/** returned from yajl_gen_string() when the yajl_gen_validate_utf8
|
||||
* option is enabled and an invalid was passed by client code.
|
||||
*/
|
||||
yajl_gen_invalid_string
|
||||
} yajl_gen_status;
|
||||
|
||||
/** an opaque handle to a generator */
|
||||
typedef struct yajl_gen_t * yajl_gen;
|
||||
|
||||
/** a callback used for "printing" the results. */
|
||||
typedef void (*yajl_print_t)(void * ctx,
|
||||
const char * str,
|
||||
size_t len);
|
||||
|
||||
/** configuration parameters for the parser, these may be passed to
|
||||
* yajl_gen_config() along with option specific argument(s). In general,
|
||||
* all configuration parameters default to *off*. */
|
||||
typedef enum {
|
||||
/** generate indented (beautiful) output */
|
||||
yajl_gen_beautify = 0x01,
|
||||
/**
|
||||
* Set an indent string which is used when yajl_gen_beautify
|
||||
* is enabled. Maybe something like \\t or some number of
|
||||
* spaces. The default is four spaces ' '.
|
||||
*/
|
||||
yajl_gen_indent_string = 0x02,
|
||||
/**
|
||||
* Set a function and context argument that should be used to
|
||||
* output generated json. the function should conform to the
|
||||
* yajl_print_t prototype while the context argument is a
|
||||
* void * of your choosing.
|
||||
*
|
||||
* example:
|
||||
* yajl_gen_config(g, yajl_gen_print_callback, myFunc, myVoidPtr);
|
||||
*/
|
||||
yajl_gen_print_callback = 0x04,
|
||||
/**
|
||||
* Normally the generator does not validate that strings you
|
||||
* pass to it via yajl_gen_string() are valid UTF8. Enabling
|
||||
* this option will cause it to do so.
|
||||
*/
|
||||
yajl_gen_validate_utf8 = 0x08,
|
||||
/**
|
||||
* the forward solidus (slash or '/' in human) is not required to be
|
||||
* escaped in json text. By default, YAJL will not escape it in the
|
||||
* iterest of saving bytes. Setting this flag will cause YAJL to
|
||||
* always escape '/' in generated JSON strings.
|
||||
*/
|
||||
yajl_gen_escape_solidus = 0x10
|
||||
} yajl_gen_option;
|
||||
|
||||
/** allow the modification of generator options subsequent to handle
|
||||
* allocation (via yajl_alloc)
|
||||
* \returns zero in case of errors, non-zero otherwise
|
||||
*/
|
||||
YAJL_API int yajl_gen_config(yajl_gen g, yajl_gen_option opt, ...);
|
||||
|
||||
/** allocate a generator handle
|
||||
* \param allocFuncs an optional pointer to a structure which allows
|
||||
* the client to overide the memory allocation
|
||||
* used by yajl. May be NULL, in which case
|
||||
* malloc/free/realloc will be used.
|
||||
*
|
||||
* \returns an allocated handle on success, NULL on failure (bad params)
|
||||
*/
|
||||
YAJL_API yajl_gen yajl_gen_alloc(const yajl_alloc_funcs * allocFuncs);
|
||||
|
||||
/** free a generator handle */
|
||||
YAJL_API void yajl_gen_free(yajl_gen handle);
|
||||
|
||||
YAJL_API yajl_gen_status yajl_gen_integer(yajl_gen hand, long long int number);
|
||||
/** generate a floating point number. number may not be infinity or
|
||||
* NaN, as these have no representation in JSON. In these cases the
|
||||
* generator will return 'yajl_gen_invalid_number' */
|
||||
YAJL_API yajl_gen_status yajl_gen_double(yajl_gen hand, double number);
|
||||
YAJL_API yajl_gen_status yajl_gen_number(yajl_gen hand,
|
||||
const char * num,
|
||||
size_t len);
|
||||
YAJL_API yajl_gen_status yajl_gen_string(yajl_gen hand,
|
||||
const unsigned char * str,
|
||||
size_t len);
|
||||
YAJL_API yajl_gen_status yajl_gen_null(yajl_gen hand);
|
||||
YAJL_API yajl_gen_status yajl_gen_bool(yajl_gen hand, int boolean);
|
||||
YAJL_API yajl_gen_status yajl_gen_map_open(yajl_gen hand);
|
||||
YAJL_API yajl_gen_status yajl_gen_map_close(yajl_gen hand);
|
||||
YAJL_API yajl_gen_status yajl_gen_array_open(yajl_gen hand);
|
||||
YAJL_API yajl_gen_status yajl_gen_array_close(yajl_gen hand);
|
||||
|
||||
/** access the null terminated generator buffer. If incrementally
|
||||
* outputing JSON, one should call yajl_gen_clear to clear the
|
||||
* buffer. This allows stream generation. */
|
||||
YAJL_API yajl_gen_status yajl_gen_get_buf(yajl_gen hand,
|
||||
const unsigned char ** buf,
|
||||
size_t * len);
|
||||
|
||||
/** clear yajl's output buffer, but maintain all internal generation
|
||||
* state. This function will not "reset" the generator state, and is
|
||||
* intended to enable incremental JSON outputing. */
|
||||
YAJL_API void yajl_gen_clear(yajl_gen hand);
|
||||
|
||||
/** Reset the generator state. Allows a client to generate multiple
|
||||
* json entities in a stream. The "sep" string will be inserted to
|
||||
* separate the previously generated entity from the current,
|
||||
* NULL means *no separation* of entites (clients beware, generating
|
||||
* multiple JSON numbers, for instance, will result in inscrutable
|
||||
* output) */
|
||||
YAJL_API void yajl_gen_reset(yajl_gen hand, const char * sep);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,226 @@
|
|||
/*
|
||||
* Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file yajl_parse.h
|
||||
* Interface to YAJL's JSON stream parsing facilities.
|
||||
*/
|
||||
|
||||
#include <yajl/yajl_common.h>
|
||||
|
||||
#ifndef __YAJL_PARSE_H__
|
||||
#define __YAJL_PARSE_H__
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/** error codes returned from this interface */
|
||||
typedef enum {
|
||||
/** no error was encountered */
|
||||
yajl_status_ok,
|
||||
/** a client callback returned zero, stopping the parse */
|
||||
yajl_status_client_canceled,
|
||||
/** An error occured during the parse. Call yajl_get_error for
|
||||
* more information about the encountered error */
|
||||
yajl_status_error
|
||||
} yajl_status;
|
||||
|
||||
/** attain a human readable, english, string for an error */
|
||||
YAJL_API const char * yajl_status_to_string(yajl_status code);
|
||||
|
||||
/** an opaque handle to a parser */
|
||||
typedef struct yajl_handle_t * yajl_handle;
|
||||
|
||||
/** yajl is an event driven parser. this means as json elements are
|
||||
* parsed, you are called back to do something with the data. The
|
||||
* functions in this table indicate the various events for which
|
||||
* you will be called back. Each callback accepts a "context"
|
||||
* pointer, this is a void * that is passed into the yajl_parse
|
||||
* function which the client code may use to pass around context.
|
||||
*
|
||||
* All callbacks return an integer. If non-zero, the parse will
|
||||
* continue. If zero, the parse will be canceled and
|
||||
* yajl_status_client_canceled will be returned from the parse.
|
||||
*
|
||||
* \attention {
|
||||
* A note about the handling of numbers:
|
||||
*
|
||||
* yajl will only convert numbers that can be represented in a
|
||||
* double or a 64 bit (long long) int. All other numbers will
|
||||
* be passed to the client in string form using the yajl_number
|
||||
* callback. Furthermore, if yajl_number is not NULL, it will
|
||||
* always be used to return numbers, that is yajl_integer and
|
||||
* yajl_double will be ignored. If yajl_number is NULL but one
|
||||
* of yajl_integer or yajl_double are defined, parsing of a
|
||||
* number larger than is representable in a double or 64 bit
|
||||
* integer will result in a parse error.
|
||||
* }
|
||||
*/
|
||||
typedef struct {
|
||||
int (* yajl_null)(void * ctx);
|
||||
int (* yajl_boolean)(void * ctx, int boolVal);
|
||||
int (* yajl_integer)(void * ctx, long long integerVal);
|
||||
int (* yajl_double)(void * ctx, double doubleVal);
|
||||
/** A callback which passes the string representation of the number
|
||||
* back to the client. Will be used for all numbers when present */
|
||||
int (* yajl_number)(void * ctx, const char * numberVal,
|
||||
size_t numberLen);
|
||||
|
||||
/** strings are returned as pointers into the JSON text when,
|
||||
* possible, as a result, they are _not_ null padded */
|
||||
int (* yajl_string)(void * ctx, const unsigned char * stringVal,
|
||||
size_t stringLen);
|
||||
|
||||
int (* yajl_start_map)(void * ctx);
|
||||
int (* yajl_map_key)(void * ctx, const unsigned char * key,
|
||||
size_t stringLen);
|
||||
int (* yajl_end_map)(void * ctx);
|
||||
|
||||
int (* yajl_start_array)(void * ctx);
|
||||
int (* yajl_end_array)(void * ctx);
|
||||
} yajl_callbacks;
|
||||
|
||||
/** allocate a parser handle
|
||||
* \param callbacks a yajl callbacks structure specifying the
|
||||
* functions to call when different JSON entities
|
||||
* are encountered in the input text. May be NULL,
|
||||
* which is only useful for validation.
|
||||
* \param afs memory allocation functions, may be NULL for to use
|
||||
* C runtime library routines (malloc and friends)
|
||||
* \param ctx a context pointer that will be passed to callbacks.
|
||||
*/
|
||||
YAJL_API yajl_handle yajl_alloc(const yajl_callbacks * callbacks,
|
||||
yajl_alloc_funcs * afs,
|
||||
void * ctx);
|
||||
|
||||
|
||||
/** configuration parameters for the parser, these may be passed to
|
||||
* yajl_config() along with option specific argument(s). In general,
|
||||
* all configuration parameters default to *off*. */
|
||||
typedef enum {
|
||||
/** Ignore javascript style comments present in
|
||||
* JSON input. Non-standard, but rather fun
|
||||
* arguments: toggled off with integer zero, on otherwise.
|
||||
*
|
||||
* example:
|
||||
* yajl_config(h, yajl_allow_comments, 1); // turn comment support on
|
||||
*/
|
||||
yajl_allow_comments = 0x01,
|
||||
/**
|
||||
* When set the parser will verify that all strings in JSON input are
|
||||
* valid UTF8 and will emit a parse error if this is not so. When set,
|
||||
* this option makes parsing slightly more expensive (~7% depending
|
||||
* on processor and compiler in use)
|
||||
*
|
||||
* example:
|
||||
* yajl_config(h, yajl_dont_validate_strings, 1); // disable utf8 checking
|
||||
*/
|
||||
yajl_dont_validate_strings = 0x02,
|
||||
/**
|
||||
* By default, upon calls to yajl_complete_parse(), yajl will
|
||||
* ensure the entire input text was consumed and will raise an error
|
||||
* otherwise. Enabling this flag will cause yajl to disable this
|
||||
* check. This can be useful when parsing json out of a that contains more
|
||||
* than a single JSON document.
|
||||
*/
|
||||
yajl_allow_trailing_garbage = 0x04,
|
||||
/**
|
||||
* Allow multiple values to be parsed by a single handle. The
|
||||
* entire text must be valid JSON, and values can be seperated
|
||||
* by any kind of whitespace. This flag will change the
|
||||
* behavior of the parser, and cause it continue parsing after
|
||||
* a value is parsed, rather than transitioning into a
|
||||
* complete state. This option can be useful when parsing multiple
|
||||
* values from an input stream.
|
||||
*/
|
||||
yajl_allow_multiple_values = 0x08,
|
||||
/**
|
||||
* When yajl_complete_parse() is called the parser will
|
||||
* check that the top level value was completely consumed. I.E.,
|
||||
* if called whilst in the middle of parsing a value
|
||||
* yajl will enter an error state (premature EOF). Setting this
|
||||
* flag suppresses that check and the corresponding error.
|
||||
*/
|
||||
yajl_allow_partial_values = 0x10
|
||||
} yajl_option;
|
||||
|
||||
/** allow the modification of parser options subsequent to handle
|
||||
* allocation (via yajl_alloc)
|
||||
* \returns zero in case of errors, non-zero otherwise
|
||||
*/
|
||||
YAJL_API int yajl_config(yajl_handle h, yajl_option opt, ...);
|
||||
|
||||
/** free a parser handle */
|
||||
YAJL_API void yajl_free(yajl_handle handle);
|
||||
|
||||
/** Parse some json!
|
||||
* \param hand - a handle to the json parser allocated with yajl_alloc
|
||||
* \param jsonText - a pointer to the UTF8 json text to be parsed
|
||||
* \param jsonTextLength - the length, in bytes, of input text
|
||||
*/
|
||||
YAJL_API yajl_status yajl_parse(yajl_handle hand,
|
||||
const unsigned char * jsonText,
|
||||
size_t jsonTextLength);
|
||||
|
||||
/** Parse any remaining buffered json.
|
||||
* Since yajl is a stream-based parser, without an explicit end of
|
||||
* input, yajl sometimes can't decide if content at the end of the
|
||||
* stream is valid or not. For example, if "1" has been fed in,
|
||||
* yajl can't know whether another digit is next or some character
|
||||
* that would terminate the integer token.
|
||||
*
|
||||
* \param hand - a handle to the json parser allocated with yajl_alloc
|
||||
*/
|
||||
YAJL_API yajl_status yajl_complete_parse(yajl_handle hand);
|
||||
|
||||
/** get an error string describing the state of the
|
||||
* parse.
|
||||
*
|
||||
* If verbose is non-zero, the message will include the JSON
|
||||
* text where the error occured, along with an arrow pointing to
|
||||
* the specific char.
|
||||
*
|
||||
* \returns A dynamically allocated string will be returned which should
|
||||
* be freed with yajl_free_error
|
||||
*/
|
||||
YAJL_API unsigned char * yajl_get_error(yajl_handle hand, int verbose,
|
||||
const unsigned char * jsonText,
|
||||
size_t jsonTextLength);
|
||||
|
||||
/**
|
||||
* get the amount of data consumed from the last chunk passed to YAJL.
|
||||
*
|
||||
* In the case of a successful parse this can help you understand if
|
||||
* the entire buffer was consumed (which will allow you to handle
|
||||
* "junk at end of input").
|
||||
*
|
||||
* In the event an error is encountered during parsing, this function
|
||||
* affords the client a way to get the offset into the most recent
|
||||
* chunk where the error occured. 0 will be returned if no error
|
||||
* was encountered.
|
||||
*/
|
||||
YAJL_API size_t yajl_get_bytes_consumed(yajl_handle hand);
|
||||
|
||||
/** free an error returned from yajl_get_error */
|
||||
YAJL_API void yajl_free_error(yajl_handle hand, unsigned char * str);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2011 Florian Forster <ff at octo.it>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file yajl_tree.h
|
||||
*
|
||||
* Parses JSON data and returns the data in tree form.
|
||||
*
|
||||
* \author Florian Forster
|
||||
* \date August 2010
|
||||
*
|
||||
* This interface makes quick parsing and extraction of
|
||||
* smallish JSON docs trivial:
|
||||
*
|
||||
* \include example/parse_config.c
|
||||
*/
|
||||
|
||||
#ifndef YAJL_TREE_H
|
||||
#define YAJL_TREE_H 1
|
||||
|
||||
#include <yajl/yajl_common.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** possible data types that a yajl_val_s can hold */
|
||||
typedef enum {
|
||||
yajl_t_string = 1,
|
||||
yajl_t_number = 2,
|
||||
yajl_t_object = 3,
|
||||
yajl_t_array = 4,
|
||||
yajl_t_true = 5,
|
||||
yajl_t_false = 6,
|
||||
yajl_t_null = 7,
|
||||
/** The any type isn't valid for yajl_val_s.type, but can be
|
||||
* used as an argument to routines like yajl_tree_get().
|
||||
*/
|
||||
yajl_t_any = 8
|
||||
} yajl_type;
|
||||
|
||||
#define YAJL_NUMBER_INT_VALID 0x01
|
||||
#define YAJL_NUMBER_DOUBLE_VALID 0x02
|
||||
|
||||
/** A pointer to a node in the parse tree */
|
||||
typedef struct yajl_val_s * yajl_val;
|
||||
|
||||
/**
|
||||
* A JSON value representation capable of holding one of the seven
|
||||
* types above. For "string", "number", "object", and "array"
|
||||
* additional data is available in the union. The "YAJL_IS_*"
|
||||
* and "YAJL_GET_*" macros below allow type checking and convenient
|
||||
* value extraction.
|
||||
*/
|
||||
struct yajl_val_s
|
||||
{
|
||||
/** Type of the value contained. Use the "YAJL_IS_*" macros to check for a
|
||||
* specific type. */
|
||||
yajl_type type;
|
||||
/** Type-specific data. You may use the "YAJL_GET_*" macros to access these
|
||||
* members. */
|
||||
union
|
||||
{
|
||||
char * string;
|
||||
struct {
|
||||
long long i; /*< integer value, if representable. */
|
||||
double d; /*< double value, if representable. */
|
||||
char *r; /*< unparsed number in string form. */
|
||||
/** Signals whether the \em i and \em d members are
|
||||
* valid. See \c YAJL_NUMBER_INT_VALID and
|
||||
* \c YAJL_NUMBER_DOUBLE_VALID. */
|
||||
unsigned int flags;
|
||||
} number;
|
||||
struct {
|
||||
const char **keys; /*< Array of keys */
|
||||
yajl_val *values; /*< Array of values. */
|
||||
size_t len; /*< Number of key-value-pairs. */
|
||||
} object;
|
||||
struct {
|
||||
yajl_val *values; /*< Array of elements. */
|
||||
size_t len; /*< Number of elements. */
|
||||
} array;
|
||||
} u;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse a string.
|
||||
*
|
||||
* Parses an null-terminated string containing JSON data and returns a pointer
|
||||
* to the top-level value (root of the parse tree).
|
||||
*
|
||||
* \param input Pointer to a null-terminated utf8 string containing
|
||||
* JSON data.
|
||||
* \param error_buffer Pointer to a buffer in which an error message will
|
||||
* be stored if \em yajl_tree_parse fails, or
|
||||
* \c NULL. The buffer will be initialized before
|
||||
* parsing, so its content will be destroyed even if
|
||||
* \em yajl_tree_parse succeeds.
|
||||
* \param error_buffer_size Size of the memory area pointed to by
|
||||
* \em error_buffer_size. If \em error_buffer_size is
|
||||
* \c NULL, this argument is ignored.
|
||||
*
|
||||
* \returns Pointer to the top-level value or \c NULL on error. The memory
|
||||
* pointed to must be freed using \em yajl_tree_free. In case of an error, a
|
||||
* null terminated message describing the error in more detail is stored in
|
||||
* \em error_buffer if it is not \c NULL.
|
||||
*/
|
||||
YAJL_API yajl_val yajl_tree_parse (const char *input,
|
||||
char *error_buffer, size_t error_buffer_size);
|
||||
|
||||
|
||||
/**
|
||||
* Free a parse tree returned by "yajl_tree_parse".
|
||||
*
|
||||
* \param v Pointer to a JSON value returned by "yajl_tree_parse". Passing NULL
|
||||
* is valid and results in a no-op.
|
||||
*/
|
||||
YAJL_API void yajl_tree_free (yajl_val v);
|
||||
|
||||
/**
|
||||
* Access a nested value inside a tree.
|
||||
*
|
||||
* \param parent the node under which you'd like to extract values.
|
||||
* \param path A null terminated array of strings, each the name of an object key
|
||||
* \param type the yajl_type of the object you seek, or yajl_t_any if any will do.
|
||||
*
|
||||
* \returns a pointer to the found value, or NULL if we came up empty.
|
||||
*
|
||||
* Future Ideas: it'd be nice to move path to a string and implement support for
|
||||
* a teeny tiny micro language here, so you can extract array elements, do things
|
||||
* like .first and .last, even .length. Inspiration from JSONPath and css selectors?
|
||||
* No it wouldn't be fast, but that's not what this API is about.
|
||||
*/
|
||||
YAJL_API yajl_val yajl_tree_get(yajl_val parent, const char ** path, yajl_type type);
|
||||
|
||||
/* Various convenience macros to check the type of a `yajl_val` */
|
||||
#define YAJL_IS_STRING(v) (((v) != NULL) && ((v)->type == yajl_t_string))
|
||||
#define YAJL_IS_NUMBER(v) (((v) != NULL) && ((v)->type == yajl_t_number))
|
||||
#define YAJL_IS_INTEGER(v) (YAJL_IS_NUMBER(v) && ((v)->u.number.flags & YAJL_NUMBER_INT_VALID))
|
||||
#define YAJL_IS_DOUBLE(v) (YAJL_IS_NUMBER(v) && ((v)->u.number.flags & YAJL_NUMBER_DOUBLE_VALID))
|
||||
#define YAJL_IS_OBJECT(v) (((v) != NULL) && ((v)->type == yajl_t_object))
|
||||
#define YAJL_IS_ARRAY(v) (((v) != NULL) && ((v)->type == yajl_t_array ))
|
||||
#define YAJL_IS_TRUE(v) (((v) != NULL) && ((v)->type == yajl_t_true ))
|
||||
#define YAJL_IS_FALSE(v) (((v) != NULL) && ((v)->type == yajl_t_false ))
|
||||
#define YAJL_IS_NULL(v) (((v) != NULL) && ((v)->type == yajl_t_null ))
|
||||
|
||||
/** Given a yajl_val_string return a ptr to the bare string it contains,
|
||||
* or NULL if the value is not a string. */
|
||||
#define YAJL_GET_STRING(v) (YAJL_IS_STRING(v) ? (v)->u.string : NULL)
|
||||
|
||||
/** Get the string representation of a number. You should check type first,
|
||||
* perhaps using YAJL_IS_NUMBER */
|
||||
#define YAJL_GET_NUMBER(v) ((v)->u.number.r)
|
||||
|
||||
/** Get the double representation of a number. You should check type first,
|
||||
* perhaps using YAJL_IS_DOUBLE */
|
||||
#define YAJL_GET_DOUBLE(v) ((v)->u.number.d)
|
||||
|
||||
/** Get the 64bit (long long) integer representation of a number. You should
|
||||
* check type first, perhaps using YAJL_IS_INTEGER */
|
||||
#define YAJL_GET_INTEGER(v) ((v)->u.number.i)
|
||||
|
||||
/** Get a pointer to a yajl_val_object or NULL if the value is not an object. */
|
||||
#define YAJL_GET_OBJECT(v) (YAJL_IS_OBJECT(v) ? &(v)->u.object : NULL)
|
||||
|
||||
/** Get a pointer to a yajl_val_array or NULL if the value is not an object. */
|
||||
#define YAJL_GET_ARRAY(v) (YAJL_IS_ARRAY(v) ? &(v)->u.array : NULL)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* YAJL_TREE_H */
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef YAJL_VERSION_H_
|
||||
#define YAJL_VERSION_H_
|
||||
|
||||
#include <yajl/yajl_common.h>
|
||||
|
||||
#define YAJL_MAJOR ${YAJL_MAJOR}
|
||||
#define YAJL_MINOR ${YAJL_MINOR}
|
||||
#define YAJL_MICRO ${YAJL_MICRO}
|
||||
|
||||
#define YAJL_VERSION ((YAJL_MAJOR * 10000) + (YAJL_MINOR * 100) + YAJL_MICRO)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern int YAJL_API yajl_version(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* YAJL_VERSION_H_ */
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "api/yajl_parse.h"
|
||||
#include "yajl_lex.h"
|
||||
#include "yajl_parser.h"
|
||||
#include "yajl_alloc.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
|
||||
const char *
|
||||
yajl_status_to_string(yajl_status stat)
|
||||
{
|
||||
const char * statStr = "unknown";
|
||||
switch (stat) {
|
||||
case yajl_status_ok:
|
||||
statStr = "ok, no error";
|
||||
break;
|
||||
case yajl_status_client_canceled:
|
||||
statStr = "client canceled parse";
|
||||
break;
|
||||
case yajl_status_error:
|
||||
statStr = "parse error";
|
||||
break;
|
||||
}
|
||||
return statStr;
|
||||
}
|
||||
|
||||
yajl_handle
|
||||
yajl_alloc(const yajl_callbacks * callbacks,
|
||||
yajl_alloc_funcs * afs,
|
||||
void * ctx)
|
||||
{
|
||||
yajl_handle hand = NULL;
|
||||
yajl_alloc_funcs afsBuffer;
|
||||
|
||||
/* first order of business is to set up memory allocation routines */
|
||||
if (afs != NULL) {
|
||||
if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
yajl_set_default_alloc_funcs(&afsBuffer);
|
||||
afs = &afsBuffer;
|
||||
}
|
||||
|
||||
hand = (yajl_handle) YA_MALLOC(afs, sizeof(struct yajl_handle_t));
|
||||
|
||||
/* copy in pointers to allocation routines */
|
||||
memcpy((void *) &(hand->alloc), (void *) afs, sizeof(yajl_alloc_funcs));
|
||||
|
||||
hand->callbacks = callbacks;
|
||||
hand->ctx = ctx;
|
||||
hand->lexer = NULL;
|
||||
hand->bytesConsumed = 0;
|
||||
hand->decodeBuf = yajl_buf_alloc(&(hand->alloc));
|
||||
hand->flags = 0;
|
||||
yajl_bs_init(hand->stateStack, &(hand->alloc));
|
||||
yajl_bs_push(hand->stateStack, yajl_state_start);
|
||||
|
||||
return hand;
|
||||
}
|
||||
|
||||
int
|
||||
yajl_config(yajl_handle h, yajl_option opt, ...)
|
||||
{
|
||||
int rv = 1;
|
||||
va_list ap;
|
||||
va_start(ap, opt);
|
||||
|
||||
switch(opt) {
|
||||
case yajl_allow_comments:
|
||||
case yajl_dont_validate_strings:
|
||||
case yajl_allow_trailing_garbage:
|
||||
case yajl_allow_multiple_values:
|
||||
case yajl_allow_partial_values:
|
||||
if (va_arg(ap, int)) h->flags |= opt;
|
||||
else h->flags &= ~opt;
|
||||
break;
|
||||
default:
|
||||
rv = 0;
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
yajl_free(yajl_handle handle)
|
||||
{
|
||||
yajl_bs_free(handle->stateStack);
|
||||
yajl_buf_free(handle->decodeBuf);
|
||||
if (handle->lexer) {
|
||||
yajl_lex_free(handle->lexer);
|
||||
handle->lexer = NULL;
|
||||
}
|
||||
YA_FREE(&(handle->alloc), handle);
|
||||
}
|
||||
|
||||
yajl_status
|
||||
yajl_parse(yajl_handle hand, const unsigned char * jsonText,
|
||||
size_t jsonTextLen)
|
||||
{
|
||||
yajl_status status;
|
||||
|
||||
/* lazy allocation of the lexer */
|
||||
if (hand->lexer == NULL) {
|
||||
hand->lexer = yajl_lex_alloc(&(hand->alloc),
|
||||
hand->flags & yajl_allow_comments,
|
||||
!(hand->flags & yajl_dont_validate_strings));
|
||||
}
|
||||
|
||||
status = yajl_do_parse(hand, jsonText, jsonTextLen);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
yajl_status
|
||||
yajl_complete_parse(yajl_handle hand)
|
||||
{
|
||||
/* The lexer is lazy allocated in the first call to parse. if parse is
|
||||
* never called, then no data was provided to parse at all. This is a
|
||||
* "premature EOF" error unless yajl_allow_partial_values is specified.
|
||||
* allocating the lexer now is the simplest possible way to handle this
|
||||
* case while preserving all the other semantics of the parser
|
||||
* (multiple values, partial values, etc). */
|
||||
if (hand->lexer == NULL) {
|
||||
hand->lexer = yajl_lex_alloc(&(hand->alloc),
|
||||
hand->flags & yajl_allow_comments,
|
||||
!(hand->flags & yajl_dont_validate_strings));
|
||||
}
|
||||
|
||||
return yajl_do_finish(hand);
|
||||
}
|
||||
|
||||
unsigned char *
|
||||
yajl_get_error(yajl_handle hand, int verbose,
|
||||
const unsigned char * jsonText, size_t jsonTextLen)
|
||||
{
|
||||
return yajl_render_error_string(hand, jsonText, jsonTextLen, verbose);
|
||||
}
|
||||
|
||||
size_t
|
||||
yajl_get_bytes_consumed(yajl_handle hand)
|
||||
{
|
||||
if (!hand) return 0;
|
||||
else return hand->bytesConsumed;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
yajl_free_error(yajl_handle hand, unsigned char * str)
|
||||
{
|
||||
/* use memory allocation functions if set */
|
||||
YA_FREE(&(hand->alloc), str);
|
||||
}
|
||||
|
||||
/* XXX: add utility routines to parse from file */
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file yajl_alloc.h
|
||||
* default memory allocation routines for yajl which use malloc/realloc and
|
||||
* free
|
||||
*/
|
||||
|
||||
#include "yajl_alloc.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
static void * yajl_internal_malloc(void *ctx, size_t sz)
|
||||
{
|
||||
(void)ctx;
|
||||
return malloc(sz);
|
||||
}
|
||||
|
||||
static void * yajl_internal_realloc(void *ctx, void * previous,
|
||||
size_t sz)
|
||||
{
|
||||
(void)ctx;
|
||||
return realloc(previous, sz);
|
||||
}
|
||||
|
||||
static void yajl_internal_free(void *ctx, void * ptr)
|
||||
{
|
||||
(void)ctx;
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf)
|
||||
{
|
||||
yaf->malloc = yajl_internal_malloc;
|
||||
yaf->free = yajl_internal_free;
|
||||
yaf->realloc = yajl_internal_realloc;
|
||||
yaf->ctx = NULL;
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file yajl_alloc.h
|
||||
* default memory allocation routines for yajl which use malloc/realloc and
|
||||
* free
|
||||
*/
|
||||
|
||||
#ifndef __YAJL_ALLOC_H__
|
||||
#define __YAJL_ALLOC_H__
|
||||
|
||||
#include "api/yajl_common.h"
|
||||
|
||||
#define YA_MALLOC(afs, sz) (afs)->malloc((afs)->ctx, (sz))
|
||||
#define YA_FREE(afs, ptr) (afs)->free((afs)->ctx, (ptr))
|
||||
#define YA_REALLOC(afs, ptr, sz) (afs)->realloc((afs)->ctx, (ptr), (sz))
|
||||
|
||||
void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "yajl_buf.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define YAJL_BUF_INIT_SIZE 2048
|
||||
|
||||
struct yajl_buf_t {
|
||||
size_t len;
|
||||
size_t used;
|
||||
unsigned char * data;
|
||||
yajl_alloc_funcs * alloc;
|
||||
};
|
||||
|
||||
static
|
||||
void yajl_buf_ensure_available(yajl_buf buf, size_t want)
|
||||
{
|
||||
size_t need;
|
||||
|
||||
assert(buf != NULL);
|
||||
|
||||
/* first call */
|
||||
if (buf->data == NULL) {
|
||||
buf->len = YAJL_BUF_INIT_SIZE;
|
||||
buf->data = (unsigned char *) YA_MALLOC(buf->alloc, buf->len);
|
||||
buf->data[0] = 0;
|
||||
}
|
||||
|
||||
need = buf->len;
|
||||
|
||||
while (want >= (need - buf->used)) need <<= 1;
|
||||
|
||||
if (need != buf->len) {
|
||||
buf->data = (unsigned char *) YA_REALLOC(buf->alloc, buf->data, need);
|
||||
buf->len = need;
|
||||
}
|
||||
}
|
||||
|
||||
yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc)
|
||||
{
|
||||
yajl_buf b = YA_MALLOC(alloc, sizeof(struct yajl_buf_t));
|
||||
memset((void *) b, 0, sizeof(struct yajl_buf_t));
|
||||
b->alloc = alloc;
|
||||
return b;
|
||||
}
|
||||
|
||||
void yajl_buf_free(yajl_buf buf)
|
||||
{
|
||||
assert(buf != NULL);
|
||||
if (buf->data) YA_FREE(buf->alloc, buf->data);
|
||||
YA_FREE(buf->alloc, buf);
|
||||
}
|
||||
|
||||
void yajl_buf_append(yajl_buf buf, const void * data, size_t len)
|
||||
{
|
||||
yajl_buf_ensure_available(buf, len);
|
||||
if (len > 0) {
|
||||
assert(data != NULL);
|
||||
memcpy(buf->data + buf->used, data, len);
|
||||
buf->used += len;
|
||||
buf->data[buf->used] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void yajl_buf_clear(yajl_buf buf)
|
||||
{
|
||||
buf->used = 0;
|
||||
if (buf->data) buf->data[buf->used] = 0;
|
||||
}
|
||||
|
||||
const unsigned char * yajl_buf_data(yajl_buf buf)
|
||||
{
|
||||
return buf->data;
|
||||
}
|
||||
|
||||
size_t yajl_buf_len(yajl_buf buf)
|
||||
{
|
||||
return buf->used;
|
||||
}
|
||||
|
||||
void
|
||||
yajl_buf_truncate(yajl_buf buf, size_t len)
|
||||
{
|
||||
assert(len <= buf->used);
|
||||
buf->used = len;
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __YAJL_BUF_H__
|
||||
#define __YAJL_BUF_H__
|
||||
|
||||
#include "api/yajl_common.h"
|
||||
#include "yajl_alloc.h"
|
||||
|
||||
/*
|
||||
* Implementation/performance notes. If this were moved to a header
|
||||
* only implementation using #define's where possible we might be
|
||||
* able to sqeeze a little performance out of the guy by killing function
|
||||
* call overhead. YMMV.
|
||||
*/
|
||||
|
||||
/**
|
||||
* yajl_buf is a buffer with exponential growth. the buffer ensures that
|
||||
* you are always null padded.
|
||||
*/
|
||||
typedef struct yajl_buf_t * yajl_buf;
|
||||
|
||||
/* allocate a new buffer */
|
||||
yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc);
|
||||
|
||||
/* free the buffer */
|
||||
void yajl_buf_free(yajl_buf buf);
|
||||
|
||||
/* append a number of bytes to the buffer */
|
||||
void yajl_buf_append(yajl_buf buf, const void * data, size_t len);
|
||||
|
||||
/* empty the buffer */
|
||||
void yajl_buf_clear(yajl_buf buf);
|
||||
|
||||
/* get a pointer to the beginning of the buffer */
|
||||
const unsigned char * yajl_buf_data(yajl_buf buf);
|
||||
|
||||
/* get the length of the buffer */
|
||||
size_t yajl_buf_len(yajl_buf buf);
|
||||
|
||||
/* truncate the buffer */
|
||||
void yajl_buf_truncate(yajl_buf buf, size_t len);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* A header only implementation of a simple stack of bytes, used in YAJL
|
||||
* to maintain parse state.
|
||||
*/
|
||||
|
||||
#ifndef __YAJL_BYTESTACK_H__
|
||||
#define __YAJL_BYTESTACK_H__
|
||||
|
||||
#include "api/yajl_common.h"
|
||||
|
||||
#define YAJL_BS_INC 128
|
||||
|
||||
typedef struct yajl_bytestack_t
|
||||
{
|
||||
unsigned char * stack;
|
||||
size_t size;
|
||||
size_t used;
|
||||
yajl_alloc_funcs * yaf;
|
||||
} yajl_bytestack;
|
||||
|
||||
/* initialize a bytestack */
|
||||
#define yajl_bs_init(obs, _yaf) { \
|
||||
(obs).stack = NULL; \
|
||||
(obs).size = 0; \
|
||||
(obs).used = 0; \
|
||||
(obs).yaf = (_yaf); \
|
||||
} \
|
||||
|
||||
|
||||
/* initialize a bytestack */
|
||||
#define yajl_bs_free(obs) \
|
||||
if ((obs).stack) (obs).yaf->free((obs).yaf->ctx, (obs).stack);
|
||||
|
||||
#define yajl_bs_current(obs) \
|
||||
(assert((obs).used > 0), (obs).stack[(obs).used - 1])
|
||||
|
||||
#define yajl_bs_push(obs, byte) { \
|
||||
if (((obs).size - (obs).used) == 0) { \
|
||||
(obs).size += YAJL_BS_INC; \
|
||||
(obs).stack = (obs).yaf->realloc((obs).yaf->ctx,\
|
||||
(void *) (obs).stack, (obs).size);\
|
||||
} \
|
||||
(obs).stack[((obs).used)++] = (byte); \
|
||||
}
|
||||
|
||||
/* removes the top item of the stack, returns nothing */
|
||||
#define yajl_bs_pop(obs) { ((obs).used)--; }
|
||||
|
||||
#define yajl_bs_set(obs, byte) \
|
||||
(obs).stack[((obs).used) - 1] = (byte);
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "yajl_encode.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static void CharToHex(unsigned char c, char * hexBuf)
|
||||
{
|
||||
const char * hexchar = "0123456789ABCDEF";
|
||||
hexBuf[0] = hexchar[c >> 4];
|
||||
hexBuf[1] = hexchar[c & 0x0F];
|
||||
}
|
||||
|
||||
void
|
||||
yajl_string_encode(const yajl_print_t print,
|
||||
void * ctx,
|
||||
const unsigned char * str,
|
||||
size_t len,
|
||||
int escape_solidus)
|
||||
{
|
||||
size_t beg = 0;
|
||||
size_t end = 0;
|
||||
char hexBuf[7];
|
||||
hexBuf[0] = '\\'; hexBuf[1] = 'u'; hexBuf[2] = '0'; hexBuf[3] = '0';
|
||||
hexBuf[6] = 0;
|
||||
|
||||
while (end < len) {
|
||||
const char * escaped = NULL;
|
||||
switch (str[end]) {
|
||||
case '\r': escaped = "\\r"; break;
|
||||
case '\n': escaped = "\\n"; break;
|
||||
case '\\': escaped = "\\\\"; break;
|
||||
/* it is not required to escape a solidus in JSON:
|
||||
* read sec. 2.5: http://www.ietf.org/rfc/rfc4627.txt
|
||||
* specifically, this production from the grammar:
|
||||
* unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
|
||||
*/
|
||||
case '/': if (escape_solidus) escaped = "\\/"; break;
|
||||
case '"': escaped = "\\\""; break;
|
||||
case '\f': escaped = "\\f"; break;
|
||||
case '\b': escaped = "\\b"; break;
|
||||
case '\t': escaped = "\\t"; break;
|
||||
default:
|
||||
if ((unsigned char) str[end] < 32) {
|
||||
CharToHex(str[end], hexBuf + 4);
|
||||
escaped = hexBuf;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (escaped != NULL) {
|
||||
print(ctx, (const char *) (str + beg), end - beg);
|
||||
print(ctx, escaped, (unsigned int)strlen(escaped));
|
||||
beg = ++end;
|
||||
} else {
|
||||
++end;
|
||||
}
|
||||
}
|
||||
print(ctx, (const char *) (str + beg), end - beg);
|
||||
}
|
||||
|
||||
static void hexToDigit(unsigned int * val, const unsigned char * hex)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i=0;i<4;i++) {
|
||||
unsigned char c = hex[i];
|
||||
if (c >= 'A') c = (c & ~0x20) - 7;
|
||||
c -= '0';
|
||||
assert(!(c & 0xF0));
|
||||
*val = (*val << 4) | c;
|
||||
}
|
||||
}
|
||||
|
||||
static void Utf32toUtf8(unsigned int codepoint, char * utf8Buf)
|
||||
{
|
||||
if (codepoint < 0x80) {
|
||||
utf8Buf[0] = (char) codepoint;
|
||||
utf8Buf[1] = 0;
|
||||
} else if (codepoint < 0x0800) {
|
||||
utf8Buf[0] = (char) ((codepoint >> 6) | 0xC0);
|
||||
utf8Buf[1] = (char) ((codepoint & 0x3F) | 0x80);
|
||||
utf8Buf[2] = 0;
|
||||
} else if (codepoint < 0x10000) {
|
||||
utf8Buf[0] = (char) ((codepoint >> 12) | 0xE0);
|
||||
utf8Buf[1] = (char) (((codepoint >> 6) & 0x3F) | 0x80);
|
||||
utf8Buf[2] = (char) ((codepoint & 0x3F) | 0x80);
|
||||
utf8Buf[3] = 0;
|
||||
} else if (codepoint < 0x200000) {
|
||||
utf8Buf[0] =(char)((codepoint >> 18) | 0xF0);
|
||||
utf8Buf[1] =(char)(((codepoint >> 12) & 0x3F) | 0x80);
|
||||
utf8Buf[2] =(char)(((codepoint >> 6) & 0x3F) | 0x80);
|
||||
utf8Buf[3] =(char)((codepoint & 0x3F) | 0x80);
|
||||
utf8Buf[4] = 0;
|
||||
} else {
|
||||
utf8Buf[0] = '?';
|
||||
utf8Buf[1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void yajl_string_decode(yajl_buf buf, const unsigned char * str,
|
||||
size_t len)
|
||||
{
|
||||
size_t beg = 0;
|
||||
size_t end = 0;
|
||||
|
||||
while (end < len) {
|
||||
if (str[end] == '\\') {
|
||||
char utf8Buf[5];
|
||||
const char * unescaped = "?";
|
||||
yajl_buf_append(buf, str + beg, end - beg);
|
||||
switch (str[++end]) {
|
||||
case 'r': unescaped = "\r"; break;
|
||||
case 'n': unescaped = "\n"; break;
|
||||
case '\\': unescaped = "\\"; break;
|
||||
case '/': unescaped = "/"; break;
|
||||
case '"': unescaped = "\""; break;
|
||||
case 'f': unescaped = "\f"; break;
|
||||
case 'b': unescaped = "\b"; break;
|
||||
case 't': unescaped = "\t"; break;
|
||||
case 'u': {
|
||||
unsigned int codepoint = 0;
|
||||
hexToDigit(&codepoint, str + ++end);
|
||||
end+=3;
|
||||
/* check if this is a surrogate */
|
||||
if ((codepoint & 0xFC00) == 0xD800) {
|
||||
end++;
|
||||
if (str[end] == '\\' && str[end + 1] == 'u') {
|
||||
unsigned int surrogate = 0;
|
||||
hexToDigit(&surrogate, str + end + 2);
|
||||
codepoint =
|
||||
(((codepoint & 0x3F) << 10) |
|
||||
((((codepoint >> 6) & 0xF) + 1) << 16) |
|
||||
(surrogate & 0x3FF));
|
||||
end += 5;
|
||||
} else {
|
||||
unescaped = "?";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Utf32toUtf8(codepoint, utf8Buf);
|
||||
unescaped = utf8Buf;
|
||||
|
||||
if (codepoint == 0) {
|
||||
yajl_buf_append(buf, unescaped, 1);
|
||||
beg = ++end;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert("this should never happen" == NULL);
|
||||
}
|
||||
yajl_buf_append(buf, unescaped, (unsigned int)strlen(unescaped));
|
||||
beg = ++end;
|
||||
} else {
|
||||
end++;
|
||||
}
|
||||
}
|
||||
yajl_buf_append(buf, str + beg, end - beg);
|
||||
}
|
||||
|
||||
#define ADV_PTR s++; if (!(len--)) return 0;
|
||||
|
||||
int yajl_string_validate_utf8(const unsigned char * s, size_t len)
|
||||
{
|
||||
if (!len) return 1;
|
||||
if (!s) return 0;
|
||||
|
||||
while (len--) {
|
||||
/* single byte */
|
||||
if (*s <= 0x7f) {
|
||||
/* noop */
|
||||
}
|
||||
/* two byte */
|
||||
else if ((*s >> 5) == 0x6) {
|
||||
ADV_PTR;
|
||||
if (!((*s >> 6) == 0x2)) return 0;
|
||||
}
|
||||
/* three byte */
|
||||
else if ((*s >> 4) == 0x0e) {
|
||||
ADV_PTR;
|
||||
if (!((*s >> 6) == 0x2)) return 0;
|
||||
ADV_PTR;
|
||||
if (!((*s >> 6) == 0x2)) return 0;
|
||||
}
|
||||
/* four byte */
|
||||
else if ((*s >> 3) == 0x1e) {
|
||||
ADV_PTR;
|
||||
if (!((*s >> 6) == 0x2)) return 0;
|
||||
ADV_PTR;
|
||||
if (!((*s >> 6) == 0x2)) return 0;
|
||||
ADV_PTR;
|
||||
if (!((*s >> 6) == 0x2)) return 0;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __YAJL_ENCODE_H__
|
||||
#define __YAJL_ENCODE_H__
|
||||
|
||||
#include "yajl_buf.h"
|
||||
#include "api/yajl_gen.h"
|
||||
|
||||
void yajl_string_encode(const yajl_print_t printer,
|
||||
void * ctx,
|
||||
const unsigned char * str,
|
||||
size_t length,
|
||||
int escape_solidus);
|
||||
|
||||
void yajl_string_decode(yajl_buf buf, const unsigned char * str,
|
||||
size_t length);
|
||||
|
||||
int yajl_string_validate_utf8(const unsigned char * s, size_t len);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,362 @@
|
|||
/*
|
||||
* Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "api/yajl_gen.h"
|
||||
#include "yajl_buf.h"
|
||||
#include "yajl_encode.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
typedef enum {
|
||||
yajl_gen_start,
|
||||
yajl_gen_map_start,
|
||||
yajl_gen_map_key,
|
||||
yajl_gen_map_val,
|
||||
yajl_gen_array_start,
|
||||
yajl_gen_in_array,
|
||||
yajl_gen_complete,
|
||||
yajl_gen_error
|
||||
} yajl_gen_state;
|
||||
|
||||
struct yajl_gen_t
|
||||
{
|
||||
unsigned int flags;
|
||||
unsigned int depth;
|
||||
const char * indentString;
|
||||
yajl_gen_state state[YAJL_MAX_DEPTH];
|
||||
yajl_print_t print;
|
||||
void * ctx; /* yajl_buf */
|
||||
/* memory allocation routines */
|
||||
yajl_alloc_funcs alloc;
|
||||
};
|
||||
|
||||
int
|
||||
yajl_gen_config(yajl_gen g, yajl_gen_option opt, ...)
|
||||
{
|
||||
int rv = 1;
|
||||
va_list ap;
|
||||
va_start(ap, opt);
|
||||
|
||||
switch(opt) {
|
||||
case yajl_gen_beautify:
|
||||
case yajl_gen_validate_utf8:
|
||||
case yajl_gen_escape_solidus:
|
||||
if (va_arg(ap, int)) g->flags |= opt;
|
||||
else g->flags &= ~opt;
|
||||
break;
|
||||
case yajl_gen_indent_string: {
|
||||
const char *indent = va_arg(ap, const char *);
|
||||
g->indentString = indent;
|
||||
for (; *indent; indent++) {
|
||||
if (*indent != '\n'
|
||||
&& *indent != '\v'
|
||||
&& *indent != '\f'
|
||||
&& *indent != '\t'
|
||||
&& *indent != '\r'
|
||||
&& *indent != ' ')
|
||||
{
|
||||
g->indentString = NULL;
|
||||
rv = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case yajl_gen_print_callback:
|
||||
yajl_buf_free(g->ctx);
|
||||
g->print = va_arg(ap, const yajl_print_t);
|
||||
g->ctx = va_arg(ap, void *);
|
||||
break;
|
||||
default:
|
||||
rv = 0;
|
||||
}
|
||||
|
||||
va_end(ap);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
yajl_gen
|
||||
yajl_gen_alloc(const yajl_alloc_funcs * afs)
|
||||
{
|
||||
yajl_gen g = NULL;
|
||||
yajl_alloc_funcs afsBuffer;
|
||||
|
||||
/* first order of business is to set up memory allocation routines */
|
||||
if (afs != NULL) {
|
||||
if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
yajl_set_default_alloc_funcs(&afsBuffer);
|
||||
afs = &afsBuffer;
|
||||
}
|
||||
|
||||
g = (yajl_gen) YA_MALLOC(afs, sizeof(struct yajl_gen_t));
|
||||
if (!g) return NULL;
|
||||
|
||||
memset((void *) g, 0, sizeof(struct yajl_gen_t));
|
||||
/* copy in pointers to allocation routines */
|
||||
memcpy((void *) &(g->alloc), (void *) afs, sizeof(yajl_alloc_funcs));
|
||||
|
||||
g->print = (yajl_print_t)&yajl_buf_append;
|
||||
g->ctx = yajl_buf_alloc(&(g->alloc));
|
||||
g->indentString = " ";
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
void
|
||||
yajl_gen_reset(yajl_gen g, const char * sep)
|
||||
{
|
||||
g->depth = 0;
|
||||
memset((void *) &(g->state), 0, sizeof(g->state));
|
||||
if (sep != NULL) g->print(g->ctx, sep, strlen(sep));
|
||||
}
|
||||
|
||||
void
|
||||
yajl_gen_free(yajl_gen g)
|
||||
{
|
||||
if (g->print == (yajl_print_t)&yajl_buf_append) yajl_buf_free((yajl_buf)g->ctx);
|
||||
YA_FREE(&(g->alloc), g);
|
||||
}
|
||||
|
||||
#define INSERT_SEP \
|
||||
if (g->state[g->depth] == yajl_gen_map_key || \
|
||||
g->state[g->depth] == yajl_gen_in_array) { \
|
||||
g->print(g->ctx, ",", 1); \
|
||||
if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); \
|
||||
} else if (g->state[g->depth] == yajl_gen_map_val) { \
|
||||
g->print(g->ctx, ":", 1); \
|
||||
if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, " ", 1); \
|
||||
}
|
||||
|
||||
#define INSERT_WHITESPACE \
|
||||
if ((g->flags & yajl_gen_beautify)) { \
|
||||
if (g->state[g->depth] != yajl_gen_map_val) { \
|
||||
unsigned int _i; \
|
||||
for (_i=0;_i<g->depth;_i++) \
|
||||
g->print(g->ctx, \
|
||||
g->indentString, \
|
||||
(unsigned int)strlen(g->indentString)); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ENSURE_NOT_KEY \
|
||||
if (g->state[g->depth] == yajl_gen_map_key || \
|
||||
g->state[g->depth] == yajl_gen_map_start) { \
|
||||
return yajl_gen_keys_must_be_strings; \
|
||||
} \
|
||||
|
||||
/* check that we're not complete, or in error state. in a valid state
|
||||
* to be generating */
|
||||
#define ENSURE_VALID_STATE \
|
||||
if (g->state[g->depth] == yajl_gen_error) { \
|
||||
return yajl_gen_in_error_state;\
|
||||
} else if (g->state[g->depth] == yajl_gen_complete) { \
|
||||
return yajl_gen_generation_complete; \
|
||||
}
|
||||
|
||||
#define INCREMENT_DEPTH \
|
||||
if (++(g->depth) >= YAJL_MAX_DEPTH) return yajl_max_depth_exceeded;
|
||||
|
||||
#define DECREMENT_DEPTH \
|
||||
if (--(g->depth) >= YAJL_MAX_DEPTH) return yajl_gen_generation_complete;
|
||||
|
||||
#define APPENDED_ATOM \
|
||||
switch (g->state[g->depth]) { \
|
||||
case yajl_gen_start: \
|
||||
g->state[g->depth] = yajl_gen_complete; \
|
||||
break; \
|
||||
case yajl_gen_map_start: \
|
||||
case yajl_gen_map_key: \
|
||||
g->state[g->depth] = yajl_gen_map_val; \
|
||||
break; \
|
||||
case yajl_gen_array_start: \
|
||||
g->state[g->depth] = yajl_gen_in_array; \
|
||||
break; \
|
||||
case yajl_gen_map_val: \
|
||||
g->state[g->depth] = yajl_gen_map_key; \
|
||||
break; \
|
||||
default: \
|
||||
break; \
|
||||
} \
|
||||
|
||||
#define FINAL_NEWLINE \
|
||||
if ((g->flags & yajl_gen_beautify) && g->state[g->depth] == yajl_gen_complete) \
|
||||
g->print(g->ctx, "\n", 1);
|
||||
|
||||
yajl_gen_status
|
||||
yajl_gen_integer(yajl_gen g, long long int number)
|
||||
{
|
||||
char i[32];
|
||||
ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
|
||||
sprintf(i, "%lld", number);
|
||||
g->print(g->ctx, i, (unsigned int)strlen(i));
|
||||
APPENDED_ATOM;
|
||||
FINAL_NEWLINE;
|
||||
return yajl_gen_status_ok;
|
||||
}
|
||||
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
#include <float.h>
|
||||
#define isnan _isnan
|
||||
#define isinf !_finite
|
||||
#endif
|
||||
|
||||
yajl_gen_status
|
||||
yajl_gen_double(yajl_gen g, double number)
|
||||
{
|
||||
char i[32];
|
||||
ENSURE_VALID_STATE; ENSURE_NOT_KEY;
|
||||
if (isnan(number) || isinf(number)) return yajl_gen_invalid_number;
|
||||
INSERT_SEP; INSERT_WHITESPACE;
|
||||
sprintf(i, "%.20g", number);
|
||||
if (strspn(i, "0123456789-") == strlen(i)) {
|
||||
strcat(i, ".0");
|
||||
}
|
||||
g->print(g->ctx, i, (unsigned int)strlen(i));
|
||||
APPENDED_ATOM;
|
||||
FINAL_NEWLINE;
|
||||
return yajl_gen_status_ok;
|
||||
}
|
||||
|
||||
yajl_gen_status
|
||||
yajl_gen_number(yajl_gen g, const char * s, size_t l)
|
||||
{
|
||||
ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
|
||||
g->print(g->ctx, s, l);
|
||||
APPENDED_ATOM;
|
||||
FINAL_NEWLINE;
|
||||
return yajl_gen_status_ok;
|
||||
}
|
||||
|
||||
yajl_gen_status
|
||||
yajl_gen_string(yajl_gen g, const unsigned char * str,
|
||||
size_t len)
|
||||
{
|
||||
// if validation is enabled, check that the string is valid utf8
|
||||
// XXX: This checking could be done a little faster, in the same pass as
|
||||
// the string encoding
|
||||
if (g->flags & yajl_gen_validate_utf8) {
|
||||
if (!yajl_string_validate_utf8(str, len)) {
|
||||
return yajl_gen_invalid_string;
|
||||
}
|
||||
}
|
||||
ENSURE_VALID_STATE; INSERT_SEP; INSERT_WHITESPACE;
|
||||
g->print(g->ctx, "\"", 1);
|
||||
yajl_string_encode(g->print, g->ctx, str, len, g->flags & yajl_gen_escape_solidus);
|
||||
g->print(g->ctx, "\"", 1);
|
||||
APPENDED_ATOM;
|
||||
FINAL_NEWLINE;
|
||||
return yajl_gen_status_ok;
|
||||
}
|
||||
|
||||
yajl_gen_status
|
||||
yajl_gen_null(yajl_gen g)
|
||||
{
|
||||
ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
|
||||
g->print(g->ctx, "null", strlen("null"));
|
||||
APPENDED_ATOM;
|
||||
FINAL_NEWLINE;
|
||||
return yajl_gen_status_ok;
|
||||
}
|
||||
|
||||
yajl_gen_status
|
||||
yajl_gen_bool(yajl_gen g, int boolean)
|
||||
{
|
||||
const char * val = boolean ? "true" : "false";
|
||||
|
||||
ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
|
||||
g->print(g->ctx, val, (unsigned int)strlen(val));
|
||||
APPENDED_ATOM;
|
||||
FINAL_NEWLINE;
|
||||
return yajl_gen_status_ok;
|
||||
}
|
||||
|
||||
yajl_gen_status
|
||||
yajl_gen_map_open(yajl_gen g)
|
||||
{
|
||||
ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
|
||||
INCREMENT_DEPTH;
|
||||
|
||||
g->state[g->depth] = yajl_gen_map_start;
|
||||
g->print(g->ctx, "{", 1);
|
||||
if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1);
|
||||
FINAL_NEWLINE;
|
||||
return yajl_gen_status_ok;
|
||||
}
|
||||
|
||||
yajl_gen_status
|
||||
yajl_gen_map_close(yajl_gen g)
|
||||
{
|
||||
ENSURE_VALID_STATE;
|
||||
DECREMENT_DEPTH;
|
||||
|
||||
if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1);
|
||||
APPENDED_ATOM;
|
||||
INSERT_WHITESPACE;
|
||||
g->print(g->ctx, "}", 1);
|
||||
FINAL_NEWLINE;
|
||||
return yajl_gen_status_ok;
|
||||
}
|
||||
|
||||
yajl_gen_status
|
||||
yajl_gen_array_open(yajl_gen g)
|
||||
{
|
||||
ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
|
||||
INCREMENT_DEPTH;
|
||||
g->state[g->depth] = yajl_gen_array_start;
|
||||
g->print(g->ctx, "[", 1);
|
||||
if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1);
|
||||
FINAL_NEWLINE;
|
||||
return yajl_gen_status_ok;
|
||||
}
|
||||
|
||||
yajl_gen_status
|
||||
yajl_gen_array_close(yajl_gen g)
|
||||
{
|
||||
ENSURE_VALID_STATE;
|
||||
DECREMENT_DEPTH;
|
||||
if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1);
|
||||
APPENDED_ATOM;
|
||||
INSERT_WHITESPACE;
|
||||
g->print(g->ctx, "]", 1);
|
||||
FINAL_NEWLINE;
|
||||
return yajl_gen_status_ok;
|
||||
}
|
||||
|
||||
yajl_gen_status
|
||||
yajl_gen_get_buf(yajl_gen g, const unsigned char ** buf,
|
||||
size_t * len)
|
||||
{
|
||||
if (g->print != (yajl_print_t)&yajl_buf_append) return yajl_gen_no_buf;
|
||||
*buf = yajl_buf_data((yajl_buf)g->ctx);
|
||||
*len = yajl_buf_len((yajl_buf)g->ctx);
|
||||
return yajl_gen_status_ok;
|
||||
}
|
||||
|
||||
void
|
||||
yajl_gen_clear(yajl_gen g)
|
||||
{
|
||||
if (g->print == (yajl_print_t)&yajl_buf_append) yajl_buf_clear((yajl_buf)g->ctx);
|
||||
}
|
|
@ -0,0 +1,763 @@
|
|||
/*
|
||||
* Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "yajl_lex.h"
|
||||
#include "yajl_buf.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef YAJL_LEXER_DEBUG
|
||||
static const char *
|
||||
tokToStr(yajl_tok tok)
|
||||
{
|
||||
switch (tok) {
|
||||
case yajl_tok_bool: return "bool";
|
||||
case yajl_tok_colon: return "colon";
|
||||
case yajl_tok_comma: return "comma";
|
||||
case yajl_tok_eof: return "eof";
|
||||
case yajl_tok_error: return "error";
|
||||
case yajl_tok_left_brace: return "brace";
|
||||
case yajl_tok_left_bracket: return "bracket";
|
||||
case yajl_tok_null: return "null";
|
||||
case yajl_tok_integer: return "integer";
|
||||
case yajl_tok_double: return "double";
|
||||
case yajl_tok_right_brace: return "brace";
|
||||
case yajl_tok_right_bracket: return "bracket";
|
||||
case yajl_tok_string: return "string";
|
||||
case yajl_tok_string_with_escapes: return "string_with_escapes";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Impact of the stream parsing feature on the lexer:
|
||||
*
|
||||
* YAJL support stream parsing. That is, the ability to parse the first
|
||||
* bits of a chunk of JSON before the last bits are available (still on
|
||||
* the network or disk). This makes the lexer more complex. The
|
||||
* responsibility of the lexer is to handle transparently the case where
|
||||
* a chunk boundary falls in the middle of a token. This is
|
||||
* accomplished is via a buffer and a character reading abstraction.
|
||||
*
|
||||
* Overview of implementation
|
||||
*
|
||||
* When we lex to end of input string before end of token is hit, we
|
||||
* copy all of the input text composing the token into our lexBuf.
|
||||
*
|
||||
* Every time we read a character, we do so through the readChar function.
|
||||
* readChar's responsibility is to handle pulling all chars from the buffer
|
||||
* before pulling chars from input text
|
||||
*/
|
||||
|
||||
struct yajl_lexer_t {
|
||||
/* the overal line and char offset into the data */
|
||||
size_t lineOff;
|
||||
size_t charOff;
|
||||
|
||||
/* error */
|
||||
yajl_lex_error error;
|
||||
|
||||
/* a input buffer to handle the case where a token is spread over
|
||||
* multiple chunks */
|
||||
yajl_buf buf;
|
||||
|
||||
/* in the case where we have data in the lexBuf, bufOff holds
|
||||
* the current offset into the lexBuf. */
|
||||
size_t bufOff;
|
||||
|
||||
/* are we using the lex buf? */
|
||||
unsigned int bufInUse;
|
||||
|
||||
/* shall we allow comments? */
|
||||
unsigned int allowComments;
|
||||
|
||||
/* shall we validate utf8 inside strings? */
|
||||
unsigned int validateUTF8;
|
||||
|
||||
yajl_alloc_funcs * alloc;
|
||||
};
|
||||
|
||||
#define readChar(lxr, txt, off) \
|
||||
(((lxr)->bufInUse && yajl_buf_len((lxr)->buf) && lxr->bufOff < yajl_buf_len((lxr)->buf)) ? \
|
||||
(*((const unsigned char *) yajl_buf_data((lxr)->buf) + ((lxr)->bufOff)++)) : \
|
||||
((txt)[(*(off))++]))
|
||||
|
||||
#define unreadChar(lxr, off) ((*(off) > 0) ? (*(off))-- : ((lxr)->bufOff--))
|
||||
|
||||
yajl_lexer
|
||||
yajl_lex_alloc(yajl_alloc_funcs * alloc,
|
||||
unsigned int allowComments, unsigned int validateUTF8)
|
||||
{
|
||||
yajl_lexer lxr = (yajl_lexer) YA_MALLOC(alloc, sizeof(struct yajl_lexer_t));
|
||||
memset((void *) lxr, 0, sizeof(struct yajl_lexer_t));
|
||||
lxr->buf = yajl_buf_alloc(alloc);
|
||||
lxr->allowComments = allowComments;
|
||||
lxr->validateUTF8 = validateUTF8;
|
||||
lxr->alloc = alloc;
|
||||
return lxr;
|
||||
}
|
||||
|
||||
void
|
||||
yajl_lex_free(yajl_lexer lxr)
|
||||
{
|
||||
yajl_buf_free(lxr->buf);
|
||||
YA_FREE(lxr->alloc, lxr);
|
||||
return;
|
||||
}
|
||||
|
||||
/* a lookup table which lets us quickly determine three things:
|
||||
* VEC - valid escaped control char
|
||||
* note. the solidus '/' may be escaped or not.
|
||||
* IJC - invalid json char
|
||||
* VHC - valid hex char
|
||||
* NFP - needs further processing (from a string scanning perspective)
|
||||
* NUC - needs utf8 checking when enabled (from a string scanning perspective)
|
||||
*/
|
||||
#define VEC 0x01
|
||||
#define IJC 0x02
|
||||
#define VHC 0x04
|
||||
#define NFP 0x08
|
||||
#define NUC 0x10
|
||||
|
||||
static const char charLookupTable[256] =
|
||||
{
|
||||
/*00*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC ,
|
||||
/*08*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC ,
|
||||
/*10*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC ,
|
||||
/*18*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC ,
|
||||
|
||||
/*20*/ 0 , 0 , NFP|VEC|IJC, 0 , 0 , 0 , 0 , 0 ,
|
||||
/*28*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , VEC ,
|
||||
/*30*/ VHC , VHC , VHC , VHC , VHC , VHC , VHC , VHC ,
|
||||
/*38*/ VHC , VHC , 0 , 0 , 0 , 0 , 0 , 0 ,
|
||||
|
||||
/*40*/ 0 , VHC , VHC , VHC , VHC , VHC , VHC , 0 ,
|
||||
/*48*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
|
||||
/*50*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
|
||||
/*58*/ 0 , 0 , 0 , 0 , NFP|VEC|IJC, 0 , 0 , 0 ,
|
||||
|
||||
/*60*/ 0 , VHC , VEC|VHC, VHC , VHC , VHC , VEC|VHC, 0 ,
|
||||
/*68*/ 0 , 0 , 0 , 0 , 0 , 0 , VEC , 0 ,
|
||||
/*70*/ 0 , 0 , VEC , 0 , VEC , 0 , 0 , 0 ,
|
||||
/*78*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
|
||||
|
||||
NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC ,
|
||||
NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC ,
|
||||
NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC ,
|
||||
NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC ,
|
||||
|
||||
NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC ,
|
||||
NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC ,
|
||||
NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC ,
|
||||
NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC ,
|
||||
|
||||
NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC ,
|
||||
NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC ,
|
||||
NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC ,
|
||||
NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC ,
|
||||
|
||||
NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC ,
|
||||
NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC ,
|
||||
NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC ,
|
||||
NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC
|
||||
};
|
||||
|
||||
/** process a variable length utf8 encoded codepoint.
|
||||
*
|
||||
* returns:
|
||||
* yajl_tok_string - if valid utf8 char was parsed and offset was
|
||||
* advanced
|
||||
* yajl_tok_eof - if end of input was hit before validation could
|
||||
* complete
|
||||
* yajl_tok_error - if invalid utf8 was encountered
|
||||
*
|
||||
* NOTE: on error the offset will point to the first char of the
|
||||
* invalid utf8 */
|
||||
#define UTF8_CHECK_EOF if (*offset >= jsonTextLen) { return yajl_tok_eof; }
|
||||
|
||||
static yajl_tok
|
||||
yajl_lex_utf8_char(yajl_lexer lexer, const unsigned char * jsonText,
|
||||
size_t jsonTextLen, size_t * offset,
|
||||
unsigned char curChar)
|
||||
{
|
||||
if (curChar <= 0x7f) {
|
||||
/* single byte */
|
||||
return yajl_tok_string;
|
||||
} else if ((curChar >> 5) == 0x6) {
|
||||
/* two byte */
|
||||
UTF8_CHECK_EOF;
|
||||
curChar = readChar(lexer, jsonText, offset);
|
||||
if ((curChar >> 6) == 0x2) return yajl_tok_string;
|
||||
} else if ((curChar >> 4) == 0x0e) {
|
||||
/* three byte */
|
||||
UTF8_CHECK_EOF;
|
||||
curChar = readChar(lexer, jsonText, offset);
|
||||
if ((curChar >> 6) == 0x2) {
|
||||
UTF8_CHECK_EOF;
|
||||
curChar = readChar(lexer, jsonText, offset);
|
||||
if ((curChar >> 6) == 0x2) return yajl_tok_string;
|
||||
}
|
||||
} else if ((curChar >> 3) == 0x1e) {
|
||||
/* four byte */
|
||||
UTF8_CHECK_EOF;
|
||||
curChar = readChar(lexer, jsonText, offset);
|
||||
if ((curChar >> 6) == 0x2) {
|
||||
UTF8_CHECK_EOF;
|
||||
curChar = readChar(lexer, jsonText, offset);
|
||||
if ((curChar >> 6) == 0x2) {
|
||||
UTF8_CHECK_EOF;
|
||||
curChar = readChar(lexer, jsonText, offset);
|
||||
if ((curChar >> 6) == 0x2) return yajl_tok_string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return yajl_tok_error;
|
||||
}
|
||||
|
||||
/* lex a string. input is the lexer, pointer to beginning of
|
||||
* json text, and start of string (offset).
|
||||
* a token is returned which has the following meanings:
|
||||
* yajl_tok_string: lex of string was successful. offset points to
|
||||
* terminating '"'.
|
||||
* yajl_tok_eof: end of text was encountered before we could complete
|
||||
* the lex.
|
||||
* yajl_tok_error: embedded in the string were unallowable chars. offset
|
||||
* points to the offending char
|
||||
*/
|
||||
#define STR_CHECK_EOF \
|
||||
if (*offset >= jsonTextLen) { \
|
||||
tok = yajl_tok_eof; \
|
||||
goto finish_string_lex; \
|
||||
}
|
||||
|
||||
/** scan a string for interesting characters that might need further
|
||||
* review. return the number of chars that are uninteresting and can
|
||||
* be skipped.
|
||||
* (lth) hi world, any thoughts on how to make this routine faster? */
|
||||
static size_t
|
||||
yajl_string_scan(const unsigned char * buf, size_t len, int utf8check)
|
||||
{
|
||||
unsigned char mask = IJC|NFP|(utf8check ? NUC : 0);
|
||||
size_t skip = 0;
|
||||
while (skip < len && !(charLookupTable[*buf] & mask))
|
||||
{
|
||||
skip++;
|
||||
buf++;
|
||||
}
|
||||
return skip;
|
||||
}
|
||||
|
||||
static yajl_tok
|
||||
yajl_lex_string(yajl_lexer lexer, const unsigned char * jsonText,
|
||||
size_t jsonTextLen, size_t * offset)
|
||||
{
|
||||
yajl_tok tok = yajl_tok_error;
|
||||
int hasEscapes = 0;
|
||||
|
||||
for (;;) {
|
||||
unsigned char curChar;
|
||||
|
||||
/* now jump into a faster scanning routine to skip as much
|
||||
* of the buffers as possible */
|
||||
{
|
||||
const unsigned char * p;
|
||||
size_t len;
|
||||
|
||||
if ((lexer->bufInUse && yajl_buf_len(lexer->buf) &&
|
||||
lexer->bufOff < yajl_buf_len(lexer->buf)))
|
||||
{
|
||||
p = ((const unsigned char *) yajl_buf_data(lexer->buf) +
|
||||
(lexer->bufOff));
|
||||
len = yajl_buf_len(lexer->buf) - lexer->bufOff;
|
||||
lexer->bufOff += yajl_string_scan(p, len, lexer->validateUTF8);
|
||||
}
|
||||
else if (*offset < jsonTextLen)
|
||||
{
|
||||
p = jsonText + *offset;
|
||||
len = jsonTextLen - *offset;
|
||||
*offset += yajl_string_scan(p, len, lexer->validateUTF8);
|
||||
}
|
||||
}
|
||||
|
||||
STR_CHECK_EOF;
|
||||
|
||||
curChar = readChar(lexer, jsonText, offset);
|
||||
|
||||
/* quote terminates */
|
||||
if (curChar == '"') {
|
||||
tok = yajl_tok_string;
|
||||
break;
|
||||
}
|
||||
/* backslash escapes a set of control chars, */
|
||||
else if (curChar == '\\') {
|
||||
hasEscapes = 1;
|
||||
STR_CHECK_EOF;
|
||||
|
||||
/* special case \u */
|
||||
curChar = readChar(lexer, jsonText, offset);
|
||||
if (curChar == 'u') {
|
||||
unsigned int i = 0;
|
||||
|
||||
for (i=0;i<4;i++) {
|
||||
STR_CHECK_EOF;
|
||||
curChar = readChar(lexer, jsonText, offset);
|
||||
if (!(charLookupTable[curChar] & VHC)) {
|
||||
/* back up to offending char */
|
||||
unreadChar(lexer, offset);
|
||||
lexer->error = yajl_lex_string_invalid_hex_char;
|
||||
goto finish_string_lex;
|
||||
}
|
||||
}
|
||||
} else if (!(charLookupTable[curChar] & VEC)) {
|
||||
/* back up to offending char */
|
||||
unreadChar(lexer, offset);
|
||||
lexer->error = yajl_lex_string_invalid_escaped_char;
|
||||
goto finish_string_lex;
|
||||
}
|
||||
}
|
||||
/* when not validating UTF8 it's a simple table lookup to determine
|
||||
* if the present character is invalid */
|
||||
else if(charLookupTable[curChar] & IJC) {
|
||||
/* back up to offending char */
|
||||
unreadChar(lexer, offset);
|
||||
lexer->error = yajl_lex_string_invalid_json_char;
|
||||
goto finish_string_lex;
|
||||
}
|
||||
/* when in validate UTF8 mode we need to do some extra work */
|
||||
else if (lexer->validateUTF8) {
|
||||
yajl_tok t = yajl_lex_utf8_char(lexer, jsonText, jsonTextLen,
|
||||
offset, curChar);
|
||||
|
||||
if (t == yajl_tok_eof) {
|
||||
tok = yajl_tok_eof;
|
||||
goto finish_string_lex;
|
||||
} else if (t == yajl_tok_error) {
|
||||
lexer->error = yajl_lex_string_invalid_utf8;
|
||||
goto finish_string_lex;
|
||||
}
|
||||
}
|
||||
/* accept it, and move on */
|
||||
}
|
||||
finish_string_lex:
|
||||
/* tell our buddy, the parser, wether he needs to process this string
|
||||
* again */
|
||||
if (hasEscapes && tok == yajl_tok_string) {
|
||||
tok = yajl_tok_string_with_escapes;
|
||||
}
|
||||
|
||||
return tok;
|
||||
}
|
||||
|
||||
#define RETURN_IF_EOF if (*offset >= jsonTextLen) return yajl_tok_eof;
|
||||
|
||||
static yajl_tok
|
||||
yajl_lex_number(yajl_lexer lexer, const unsigned char * jsonText,
|
||||
size_t jsonTextLen, size_t * offset)
|
||||
{
|
||||
/** XXX: numbers are the only entities in json that we must lex
|
||||
* _beyond_ in order to know that they are complete. There
|
||||
* is an ambiguous case for integers at EOF. */
|
||||
|
||||
unsigned char c;
|
||||
|
||||
yajl_tok tok = yajl_tok_integer;
|
||||
|
||||
RETURN_IF_EOF;
|
||||
c = readChar(lexer, jsonText, offset);
|
||||
|
||||
/* optional leading minus */
|
||||
if (c == '-') {
|
||||
RETURN_IF_EOF;
|
||||
c = readChar(lexer, jsonText, offset);
|
||||
}
|
||||
|
||||
/* a single zero, or a series of integers */
|
||||
if (c == '0') {
|
||||
RETURN_IF_EOF;
|
||||
c = readChar(lexer, jsonText, offset);
|
||||
} else if (c >= '1' && c <= '9') {
|
||||
do {
|
||||
RETURN_IF_EOF;
|
||||
c = readChar(lexer, jsonText, offset);
|
||||
} while (c >= '0' && c <= '9');
|
||||
} else {
|
||||
unreadChar(lexer, offset);
|
||||
lexer->error = yajl_lex_missing_integer_after_minus;
|
||||
return yajl_tok_error;
|
||||
}
|
||||
|
||||
/* optional fraction (indicates this is floating point) */
|
||||
if (c == '.') {
|
||||
int numRd = 0;
|
||||
|
||||
RETURN_IF_EOF;
|
||||
c = readChar(lexer, jsonText, offset);
|
||||
|
||||
while (c >= '0' && c <= '9') {
|
||||
numRd++;
|
||||
RETURN_IF_EOF;
|
||||
c = readChar(lexer, jsonText, offset);
|
||||
}
|
||||
|
||||
if (!numRd) {
|
||||
unreadChar(lexer, offset);
|
||||
lexer->error = yajl_lex_missing_integer_after_decimal;
|
||||
return yajl_tok_error;
|
||||
}
|
||||
tok = yajl_tok_double;
|
||||
}
|
||||
|
||||
/* optional exponent (indicates this is floating point) */
|
||||
if (c == 'e' || c == 'E') {
|
||||
RETURN_IF_EOF;
|
||||
c = readChar(lexer, jsonText, offset);
|
||||
|
||||
/* optional sign */
|
||||
if (c == '+' || c == '-') {
|
||||
RETURN_IF_EOF;
|
||||
c = readChar(lexer, jsonText, offset);
|
||||
}
|
||||
|
||||
if (c >= '0' && c <= '9') {
|
||||
do {
|
||||
RETURN_IF_EOF;
|
||||
c = readChar(lexer, jsonText, offset);
|
||||
} while (c >= '0' && c <= '9');
|
||||
} else {
|
||||
unreadChar(lexer, offset);
|
||||
lexer->error = yajl_lex_missing_integer_after_exponent;
|
||||
return yajl_tok_error;
|
||||
}
|
||||
tok = yajl_tok_double;
|
||||
}
|
||||
|
||||
/* we always go "one too far" */
|
||||
unreadChar(lexer, offset);
|
||||
|
||||
return tok;
|
||||
}
|
||||
|
||||
static yajl_tok
|
||||
yajl_lex_comment(yajl_lexer lexer, const unsigned char * jsonText,
|
||||
size_t jsonTextLen, size_t * offset)
|
||||
{
|
||||
unsigned char c;
|
||||
|
||||
yajl_tok tok = yajl_tok_comment;
|
||||
|
||||
RETURN_IF_EOF;
|
||||
c = readChar(lexer, jsonText, offset);
|
||||
|
||||
/* either slash or star expected */
|
||||
if (c == '/') {
|
||||
/* now we throw away until end of line */
|
||||
do {
|
||||
RETURN_IF_EOF;
|
||||
c = readChar(lexer, jsonText, offset);
|
||||
} while (c != '\n');
|
||||
} else if (c == '*') {
|
||||
/* now we throw away until end of comment */
|
||||
for (;;) {
|
||||
RETURN_IF_EOF;
|
||||
c = readChar(lexer, jsonText, offset);
|
||||
if (c == '*') {
|
||||
RETURN_IF_EOF;
|
||||
c = readChar(lexer, jsonText, offset);
|
||||
if (c == '/') {
|
||||
break;
|
||||
} else {
|
||||
unreadChar(lexer, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
lexer->error = yajl_lex_invalid_char;
|
||||
tok = yajl_tok_error;
|
||||
}
|
||||
|
||||
return tok;
|
||||
}
|
||||
|
||||
yajl_tok
|
||||
yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText,
|
||||
size_t jsonTextLen, size_t * offset,
|
||||
const unsigned char ** outBuf, size_t * outLen)
|
||||
{
|
||||
yajl_tok tok = yajl_tok_error;
|
||||
unsigned char c;
|
||||
size_t startOffset = *offset;
|
||||
|
||||
*outBuf = NULL;
|
||||
*outLen = 0;
|
||||
|
||||
for (;;) {
|
||||
assert(*offset <= jsonTextLen);
|
||||
|
||||
if (*offset >= jsonTextLen) {
|
||||
tok = yajl_tok_eof;
|
||||
goto lexed;
|
||||
}
|
||||
|
||||
c = readChar(lexer, jsonText, offset);
|
||||
|
||||
switch (c) {
|
||||
case '{':
|
||||
tok = yajl_tok_left_bracket;
|
||||
goto lexed;
|
||||
case '}':
|
||||
tok = yajl_tok_right_bracket;
|
||||
goto lexed;
|
||||
case '[':
|
||||
tok = yajl_tok_left_brace;
|
||||
goto lexed;
|
||||
case ']':
|
||||
tok = yajl_tok_right_brace;
|
||||
goto lexed;
|
||||
case ',':
|
||||
tok = yajl_tok_comma;
|
||||
goto lexed;
|
||||
case ':':
|
||||
tok = yajl_tok_colon;
|
||||
goto lexed;
|
||||
case '\t': case '\n': case '\v': case '\f': case '\r': case ' ':
|
||||
startOffset++;
|
||||
break;
|
||||
case 't': {
|
||||
const char * want = "rue";
|
||||
do {
|
||||
if (*offset >= jsonTextLen) {
|
||||
tok = yajl_tok_eof;
|
||||
goto lexed;
|
||||
}
|
||||
c = readChar(lexer, jsonText, offset);
|
||||
if (c != *want) {
|
||||
unreadChar(lexer, offset);
|
||||
lexer->error = yajl_lex_invalid_string;
|
||||
tok = yajl_tok_error;
|
||||
goto lexed;
|
||||
}
|
||||
} while (*(++want));
|
||||
tok = yajl_tok_bool;
|
||||
goto lexed;
|
||||
}
|
||||
case 'f': {
|
||||
const char * want = "alse";
|
||||
do {
|
||||
if (*offset >= jsonTextLen) {
|
||||
tok = yajl_tok_eof;
|
||||
goto lexed;
|
||||
}
|
||||
c = readChar(lexer, jsonText, offset);
|
||||
if (c != *want) {
|
||||
unreadChar(lexer, offset);
|
||||
lexer->error = yajl_lex_invalid_string;
|
||||
tok = yajl_tok_error;
|
||||
goto lexed;
|
||||
}
|
||||
} while (*(++want));
|
||||
tok = yajl_tok_bool;
|
||||
goto lexed;
|
||||
}
|
||||
case 'n': {
|
||||
const char * want = "ull";
|
||||
do {
|
||||
if (*offset >= jsonTextLen) {
|
||||
tok = yajl_tok_eof;
|
||||
goto lexed;
|
||||
}
|
||||
c = readChar(lexer, jsonText, offset);
|
||||
if (c != *want) {
|
||||
unreadChar(lexer, offset);
|
||||
lexer->error = yajl_lex_invalid_string;
|
||||
tok = yajl_tok_error;
|
||||
goto lexed;
|
||||
}
|
||||
} while (*(++want));
|
||||
tok = yajl_tok_null;
|
||||
goto lexed;
|
||||
}
|
||||
case '"': {
|
||||
tok = yajl_lex_string(lexer, (const unsigned char *) jsonText,
|
||||
jsonTextLen, offset);
|
||||
goto lexed;
|
||||
}
|
||||
case '-':
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9': {
|
||||
/* integer parsing wants to start from the beginning */
|
||||
unreadChar(lexer, offset);
|
||||
tok = yajl_lex_number(lexer, (const unsigned char *) jsonText,
|
||||
jsonTextLen, offset);
|
||||
goto lexed;
|
||||
}
|
||||
case '/':
|
||||
/* hey, look, a probable comment! If comments are disabled
|
||||
* it's an error. */
|
||||
if (!lexer->allowComments) {
|
||||
unreadChar(lexer, offset);
|
||||
lexer->error = yajl_lex_unallowed_comment;
|
||||
tok = yajl_tok_error;
|
||||
goto lexed;
|
||||
}
|
||||
/* if comments are enabled, then we should try to lex
|
||||
* the thing. possible outcomes are
|
||||
* - successful lex (tok_comment, which means continue),
|
||||
* - malformed comment opening (slash not followed by
|
||||
* '*' or '/') (tok_error)
|
||||
* - eof hit. (tok_eof) */
|
||||
tok = yajl_lex_comment(lexer, (const unsigned char *) jsonText,
|
||||
jsonTextLen, offset);
|
||||
if (tok == yajl_tok_comment) {
|
||||
/* "error" is silly, but that's the initial
|
||||
* state of tok. guilty until proven innocent. */
|
||||
tok = yajl_tok_error;
|
||||
yajl_buf_clear(lexer->buf);
|
||||
lexer->bufInUse = 0;
|
||||
startOffset = *offset;
|
||||
break;
|
||||
}
|
||||
/* hit error or eof, bail */
|
||||
goto lexed;
|
||||
default:
|
||||
lexer->error = yajl_lex_invalid_char;
|
||||
tok = yajl_tok_error;
|
||||
goto lexed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lexed:
|
||||
/* need to append to buffer if the buffer is in use or
|
||||
* if it's an EOF token */
|
||||
if (tok == yajl_tok_eof || lexer->bufInUse) {
|
||||
if (!lexer->bufInUse) yajl_buf_clear(lexer->buf);
|
||||
lexer->bufInUse = 1;
|
||||
yajl_buf_append(lexer->buf, jsonText + startOffset, *offset - startOffset);
|
||||
lexer->bufOff = 0;
|
||||
|
||||
if (tok != yajl_tok_eof) {
|
||||
*outBuf = yajl_buf_data(lexer->buf);
|
||||
*outLen = yajl_buf_len(lexer->buf);
|
||||
lexer->bufInUse = 0;
|
||||
}
|
||||
} else if (tok != yajl_tok_error) {
|
||||
*outBuf = jsonText + startOffset;
|
||||
*outLen = *offset - startOffset;
|
||||
}
|
||||
|
||||
/* special case for strings. skip the quotes. */
|
||||
if (tok == yajl_tok_string || tok == yajl_tok_string_with_escapes)
|
||||
{
|
||||
assert(*outLen >= 2);
|
||||
(*outBuf)++;
|
||||
*outLen -= 2;
|
||||
}
|
||||
|
||||
|
||||
#ifdef YAJL_LEXER_DEBUG
|
||||
if (tok == yajl_tok_error) {
|
||||
printf("lexical error: %s\n",
|
||||
yajl_lex_error_to_string(yajl_lex_get_error(lexer)));
|
||||
} else if (tok == yajl_tok_eof) {
|
||||
printf("EOF hit\n");
|
||||
} else {
|
||||
printf("lexed %s: '", tokToStr(tok));
|
||||
fwrite(*outBuf, 1, *outLen, stdout);
|
||||
printf("'\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
return tok;
|
||||
}
|
||||
|
||||
const char *
|
||||
yajl_lex_error_to_string(yajl_lex_error error)
|
||||
{
|
||||
switch (error) {
|
||||
case yajl_lex_e_ok:
|
||||
return "ok, no error";
|
||||
case yajl_lex_string_invalid_utf8:
|
||||
return "invalid bytes in UTF8 string.";
|
||||
case yajl_lex_string_invalid_escaped_char:
|
||||
return "inside a string, '\\' occurs before a character "
|
||||
"which it may not.";
|
||||
case yajl_lex_string_invalid_json_char:
|
||||
return "invalid character inside string.";
|
||||
case yajl_lex_string_invalid_hex_char:
|
||||
return "invalid (non-hex) character occurs after '\\u' inside "
|
||||
"string.";
|
||||
case yajl_lex_invalid_char:
|
||||
return "invalid char in json text.";
|
||||
case yajl_lex_invalid_string:
|
||||
return "invalid string in json text.";
|
||||
case yajl_lex_missing_integer_after_exponent:
|
||||
return "malformed number, a digit is required after the exponent.";
|
||||
case yajl_lex_missing_integer_after_decimal:
|
||||
return "malformed number, a digit is required after the "
|
||||
"decimal point.";
|
||||
case yajl_lex_missing_integer_after_minus:
|
||||
return "malformed number, a digit is required after the "
|
||||
"minus sign.";
|
||||
case yajl_lex_unallowed_comment:
|
||||
return "probable comment found in input text, comments are "
|
||||
"not enabled.";
|
||||
}
|
||||
return "unknown error code";
|
||||
}
|
||||
|
||||
|
||||
/** allows access to more specific information about the lexical
|
||||
* error when yajl_lex_lex returns yajl_tok_error. */
|
||||
yajl_lex_error
|
||||
yajl_lex_get_error(yajl_lexer lexer)
|
||||
{
|
||||
if (lexer == NULL) return (yajl_lex_error) -1;
|
||||
return lexer->error;
|
||||
}
|
||||
|
||||
size_t yajl_lex_current_line(yajl_lexer lexer)
|
||||
{
|
||||
return lexer->lineOff;
|
||||
}
|
||||
|
||||
size_t yajl_lex_current_char(yajl_lexer lexer)
|
||||
{
|
||||
return lexer->charOff;
|
||||
}
|
||||
|
||||
yajl_tok yajl_lex_peek(yajl_lexer lexer, const unsigned char * jsonText,
|
||||
size_t jsonTextLen, size_t offset)
|
||||
{
|
||||
const unsigned char * outBuf;
|
||||
size_t outLen;
|
||||
size_t bufLen = yajl_buf_len(lexer->buf);
|
||||
size_t bufOff = lexer->bufOff;
|
||||
unsigned int bufInUse = lexer->bufInUse;
|
||||
yajl_tok tok;
|
||||
|
||||
tok = yajl_lex_lex(lexer, jsonText, jsonTextLen, &offset,
|
||||
&outBuf, &outLen);
|
||||
|
||||
lexer->bufOff = bufOff;
|
||||
lexer->bufInUse = bufInUse;
|
||||
yajl_buf_truncate(lexer->buf, bufLen);
|
||||
|
||||
return tok;
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __YAJL_LEX_H__
|
||||
#define __YAJL_LEX_H__
|
||||
|
||||
#include "api/yajl_common.h"
|
||||
|
||||
typedef enum {
|
||||
yajl_tok_bool,
|
||||
yajl_tok_colon,
|
||||
yajl_tok_comma,
|
||||
yajl_tok_eof,
|
||||
yajl_tok_error,
|
||||
yajl_tok_left_brace,
|
||||
yajl_tok_left_bracket,
|
||||
yajl_tok_null,
|
||||
yajl_tok_right_brace,
|
||||
yajl_tok_right_bracket,
|
||||
|
||||
/* we differentiate between integers and doubles to allow the
|
||||
* parser to interpret the number without re-scanning */
|
||||
yajl_tok_integer,
|
||||
yajl_tok_double,
|
||||
|
||||
/* we differentiate between strings which require further processing,
|
||||
* and strings that do not */
|
||||
yajl_tok_string,
|
||||
yajl_tok_string_with_escapes,
|
||||
|
||||
/* comment tokens are not currently returned to the parser, ever */
|
||||
yajl_tok_comment
|
||||
} yajl_tok;
|
||||
|
||||
typedef struct yajl_lexer_t * yajl_lexer;
|
||||
|
||||
yajl_lexer yajl_lex_alloc(yajl_alloc_funcs * alloc,
|
||||
unsigned int allowComments,
|
||||
unsigned int validateUTF8);
|
||||
|
||||
void yajl_lex_free(yajl_lexer lexer);
|
||||
|
||||
/**
|
||||
* run/continue a lex. "offset" is an input/output parameter.
|
||||
* It should be initialized to zero for a
|
||||
* new chunk of target text, and upon subsetquent calls with the same
|
||||
* target text should passed with the value of the previous invocation.
|
||||
*
|
||||
* the client may be interested in the value of offset when an error is
|
||||
* returned from the lexer. This allows the client to render useful
|
||||
* error messages.
|
||||
*
|
||||
* When you pass the next chunk of data, context should be reinitialized
|
||||
* to zero.
|
||||
*
|
||||
* Finally, the output buffer is usually just a pointer into the jsonText,
|
||||
* however in cases where the entity being lexed spans multiple chunks,
|
||||
* the lexer will buffer the entity and the data returned will be
|
||||
* a pointer into that buffer.
|
||||
*
|
||||
* This behavior is abstracted from client code except for the performance
|
||||
* implications which require that the client choose a reasonable chunk
|
||||
* size to get adequate performance.
|
||||
*/
|
||||
yajl_tok yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText,
|
||||
size_t jsonTextLen, size_t * offset,
|
||||
const unsigned char ** outBuf, size_t * outLen);
|
||||
|
||||
/** have a peek at the next token, but don't move the lexer forward */
|
||||
yajl_tok yajl_lex_peek(yajl_lexer lexer, const unsigned char * jsonText,
|
||||
size_t jsonTextLen, size_t offset);
|
||||
|
||||
|
||||
typedef enum {
|
||||
yajl_lex_e_ok = 0,
|
||||
yajl_lex_string_invalid_utf8,
|
||||
yajl_lex_string_invalid_escaped_char,
|
||||
yajl_lex_string_invalid_json_char,
|
||||
yajl_lex_string_invalid_hex_char,
|
||||
yajl_lex_invalid_char,
|
||||
yajl_lex_invalid_string,
|
||||
yajl_lex_missing_integer_after_decimal,
|
||||
yajl_lex_missing_integer_after_exponent,
|
||||
yajl_lex_missing_integer_after_minus,
|
||||
yajl_lex_unallowed_comment
|
||||
} yajl_lex_error;
|
||||
|
||||
const char * yajl_lex_error_to_string(yajl_lex_error error);
|
||||
|
||||
/** allows access to more specific information about the lexical
|
||||
* error when yajl_lex_lex returns yajl_tok_error. */
|
||||
yajl_lex_error yajl_lex_get_error(yajl_lexer lexer);
|
||||
|
||||
/** get the current offset into the most recently lexed json string. */
|
||||
size_t yajl_lex_current_offset(yajl_lexer lexer);
|
||||
|
||||
/** get the number of lines lexed by this lexer instance */
|
||||
size_t yajl_lex_current_line(yajl_lexer lexer);
|
||||
|
||||
/** get the number of chars lexed by this lexer instance since the last
|
||||
* \n or \r */
|
||||
size_t yajl_lex_current_char(yajl_lexer lexer);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,498 @@
|
|||
/*
|
||||
* Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "api/yajl_parse.h"
|
||||
#include "yajl_lex.h"
|
||||
#include "yajl_parser.h"
|
||||
#include "yajl_encode.h"
|
||||
#include "yajl_bytestack.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
|
||||
#define MAX_VALUE_TO_MULTIPLY ((LLONG_MAX / 10) + (LLONG_MAX % 10))
|
||||
|
||||
/* same semantics as strtol */
|
||||
long long
|
||||
yajl_parse_integer(const unsigned char *number, unsigned int length)
|
||||
{
|
||||
long long ret = 0;
|
||||
long sign = 1;
|
||||
const unsigned char *pos = number;
|
||||
if (*pos == '-') { pos++; sign = -1; }
|
||||
if (*pos == '+') { pos++; }
|
||||
|
||||
while (pos < number + length) {
|
||||
if ( ret > MAX_VALUE_TO_MULTIPLY ) {
|
||||
errno = ERANGE;
|
||||
return sign == 1 ? LLONG_MAX : LLONG_MIN;
|
||||
}
|
||||
ret *= 10;
|
||||
if (LLONG_MAX - ret < (*pos - '0')) {
|
||||
errno = ERANGE;
|
||||
return sign == 1 ? LLONG_MAX : LLONG_MIN;
|
||||
}
|
||||
if (*pos < '0' || *pos > '9') {
|
||||
errno = ERANGE;
|
||||
return sign == 1 ? LLONG_MAX : LLONG_MIN;
|
||||
}
|
||||
ret += (*pos++ - '0');
|
||||
}
|
||||
|
||||
return sign * ret;
|
||||
}
|
||||
|
||||
unsigned char *
|
||||
yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText,
|
||||
size_t jsonTextLen, int verbose)
|
||||
{
|
||||
size_t offset = hand->bytesConsumed;
|
||||
unsigned char * str;
|
||||
const char * errorType = NULL;
|
||||
const char * errorText = NULL;
|
||||
char text[72];
|
||||
const char * arrow = " (right here) ------^\n";
|
||||
|
||||
if (yajl_bs_current(hand->stateStack) == yajl_state_parse_error) {
|
||||
errorType = "parse";
|
||||
errorText = hand->parseError;
|
||||
} else if (yajl_bs_current(hand->stateStack) == yajl_state_lexical_error) {
|
||||
errorType = "lexical";
|
||||
errorText = yajl_lex_error_to_string(yajl_lex_get_error(hand->lexer));
|
||||
} else {
|
||||
errorType = "unknown";
|
||||
}
|
||||
|
||||
{
|
||||
size_t memneeded = 0;
|
||||
memneeded += strlen(errorType);
|
||||
memneeded += strlen(" error");
|
||||
if (errorText != NULL) {
|
||||
memneeded += strlen(": ");
|
||||
memneeded += strlen(errorText);
|
||||
}
|
||||
str = (unsigned char *) YA_MALLOC(&(hand->alloc), memneeded + 2);
|
||||
if (!str) return NULL;
|
||||
str[0] = 0;
|
||||
strcat((char *) str, errorType);
|
||||
strcat((char *) str, " error");
|
||||
if (errorText != NULL) {
|
||||
strcat((char *) str, ": ");
|
||||
strcat((char *) str, errorText);
|
||||
}
|
||||
strcat((char *) str, "\n");
|
||||
}
|
||||
|
||||
/* now we append as many spaces as needed to make sure the error
|
||||
* falls at char 41, if verbose was specified */
|
||||
if (verbose) {
|
||||
size_t start, end, i;
|
||||
size_t spacesNeeded;
|
||||
|
||||
spacesNeeded = (offset < 30 ? 40 - offset : 10);
|
||||
start = (offset >= 30 ? offset - 30 : 0);
|
||||
end = (offset + 30 > jsonTextLen ? jsonTextLen : offset + 30);
|
||||
|
||||
for (i=0;i<spacesNeeded;i++) text[i] = ' ';
|
||||
|
||||
for (;start < end;start++, i++) {
|
||||
if (jsonText[start] != '\n' && jsonText[start] != '\r')
|
||||
{
|
||||
text[i] = jsonText[start];
|
||||
}
|
||||
else
|
||||
{
|
||||
text[i] = ' ';
|
||||
}
|
||||
}
|
||||
assert(i <= 71);
|
||||
text[i++] = '\n';
|
||||
text[i] = 0;
|
||||
{
|
||||
char * newStr = (char *)
|
||||
YA_MALLOC(&(hand->alloc), (unsigned int)(strlen((char *) str) +
|
||||
strlen((char *) text) +
|
||||
strlen(arrow) + 1));
|
||||
if (newStr) {
|
||||
newStr[0] = 0;
|
||||
strcat((char *) newStr, (char *) str);
|
||||
strcat((char *) newStr, text);
|
||||
strcat((char *) newStr, arrow);
|
||||
}
|
||||
YA_FREE(&(hand->alloc), str);
|
||||
str = (unsigned char *) newStr;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/* check for client cancelation */
|
||||
#define _CC_CHK(x) \
|
||||
if (!(x)) { \
|
||||
yajl_bs_set(hand->stateStack, yajl_state_parse_error); \
|
||||
hand->parseError = \
|
||||
"client cancelled parse via callback return value"; \
|
||||
return yajl_status_client_canceled; \
|
||||
}
|
||||
|
||||
|
||||
yajl_status
|
||||
yajl_do_finish(yajl_handle hand)
|
||||
{
|
||||
yajl_status stat;
|
||||
stat = yajl_do_parse(hand,(const unsigned char *) " ",1);
|
||||
|
||||
if (stat != yajl_status_ok) return stat;
|
||||
|
||||
switch(yajl_bs_current(hand->stateStack))
|
||||
{
|
||||
case yajl_state_parse_error:
|
||||
case yajl_state_lexical_error:
|
||||
return yajl_status_error;
|
||||
case yajl_state_got_value:
|
||||
case yajl_state_parse_complete:
|
||||
return yajl_status_ok;
|
||||
default:
|
||||
if (!(hand->flags & yajl_allow_partial_values))
|
||||
{
|
||||
yajl_bs_set(hand->stateStack, yajl_state_parse_error);
|
||||
hand->parseError = "premature EOF";
|
||||
return yajl_status_error;
|
||||
}
|
||||
return yajl_status_ok;
|
||||
}
|
||||
}
|
||||
|
||||
yajl_status
|
||||
yajl_do_parse(yajl_handle hand, const unsigned char * jsonText,
|
||||
size_t jsonTextLen)
|
||||
{
|
||||
yajl_tok tok;
|
||||
const unsigned char * buf;
|
||||
size_t bufLen;
|
||||
size_t * offset = &(hand->bytesConsumed);
|
||||
|
||||
*offset = 0;
|
||||
|
||||
around_again:
|
||||
switch (yajl_bs_current(hand->stateStack)) {
|
||||
case yajl_state_parse_complete:
|
||||
if (hand->flags & yajl_allow_multiple_values) {
|
||||
yajl_bs_set(hand->stateStack, yajl_state_got_value);
|
||||
goto around_again;
|
||||
}
|
||||
if (!(hand->flags & yajl_allow_trailing_garbage)) {
|
||||
if (*offset != jsonTextLen) {
|
||||
tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen,
|
||||
offset, &buf, &bufLen);
|
||||
if (tok != yajl_tok_eof) {
|
||||
yajl_bs_set(hand->stateStack, yajl_state_parse_error);
|
||||
hand->parseError = "trailing garbage";
|
||||
}
|
||||
goto around_again;
|
||||
}
|
||||
}
|
||||
return yajl_status_ok;
|
||||
case yajl_state_lexical_error:
|
||||
case yajl_state_parse_error:
|
||||
return yajl_status_error;
|
||||
case yajl_state_start:
|
||||
case yajl_state_got_value:
|
||||
case yajl_state_map_need_val:
|
||||
case yajl_state_array_need_val:
|
||||
case yajl_state_array_start: {
|
||||
/* for arrays and maps, we advance the state for this
|
||||
* depth, then push the state of the next depth.
|
||||
* If an error occurs during the parsing of the nesting
|
||||
* enitity, the state at this level will not matter.
|
||||
* a state that needs pushing will be anything other
|
||||
* than state_start */
|
||||
|
||||
yajl_state stateToPush = yajl_state_start;
|
||||
|
||||
tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen,
|
||||
offset, &buf, &bufLen);
|
||||
|
||||
switch (tok) {
|
||||
case yajl_tok_eof:
|
||||
return yajl_status_ok;
|
||||
case yajl_tok_error:
|
||||
yajl_bs_set(hand->stateStack, yajl_state_lexical_error);
|
||||
goto around_again;
|
||||
case yajl_tok_string:
|
||||
if (hand->callbacks && hand->callbacks->yajl_string) {
|
||||
_CC_CHK(hand->callbacks->yajl_string(hand->ctx,
|
||||
buf, bufLen));
|
||||
}
|
||||
break;
|
||||
case yajl_tok_string_with_escapes:
|
||||
if (hand->callbacks && hand->callbacks->yajl_string) {
|
||||
yajl_buf_clear(hand->decodeBuf);
|
||||
yajl_string_decode(hand->decodeBuf, buf, bufLen);
|
||||
_CC_CHK(hand->callbacks->yajl_string(
|
||||
hand->ctx, yajl_buf_data(hand->decodeBuf),
|
||||
yajl_buf_len(hand->decodeBuf)));
|
||||
}
|
||||
break;
|
||||
case yajl_tok_bool:
|
||||
if (hand->callbacks && hand->callbacks->yajl_boolean) {
|
||||
_CC_CHK(hand->callbacks->yajl_boolean(hand->ctx,
|
||||
*buf == 't'));
|
||||
}
|
||||
break;
|
||||
case yajl_tok_null:
|
||||
if (hand->callbacks && hand->callbacks->yajl_null) {
|
||||
_CC_CHK(hand->callbacks->yajl_null(hand->ctx));
|
||||
}
|
||||
break;
|
||||
case yajl_tok_left_bracket:
|
||||
if (hand->callbacks && hand->callbacks->yajl_start_map) {
|
||||
_CC_CHK(hand->callbacks->yajl_start_map(hand->ctx));
|
||||
}
|
||||
stateToPush = yajl_state_map_start;
|
||||
break;
|
||||
case yajl_tok_left_brace:
|
||||
if (hand->callbacks && hand->callbacks->yajl_start_array) {
|
||||
_CC_CHK(hand->callbacks->yajl_start_array(hand->ctx));
|
||||
}
|
||||
stateToPush = yajl_state_array_start;
|
||||
break;
|
||||
case yajl_tok_integer:
|
||||
if (hand->callbacks) {
|
||||
if (hand->callbacks->yajl_number) {
|
||||
_CC_CHK(hand->callbacks->yajl_number(
|
||||
hand->ctx,(const char *) buf, bufLen));
|
||||
} else if (hand->callbacks->yajl_integer) {
|
||||
long long int i = 0;
|
||||
errno = 0;
|
||||
i = yajl_parse_integer(buf, bufLen);
|
||||
if ((i == LLONG_MIN || i == LLONG_MAX) &&
|
||||
errno == ERANGE)
|
||||
{
|
||||
yajl_bs_set(hand->stateStack,
|
||||
yajl_state_parse_error);
|
||||
hand->parseError = "integer overflow" ;
|
||||
/* try to restore error offset */
|
||||
if (*offset >= bufLen) *offset -= bufLen;
|
||||
else *offset = 0;
|
||||
goto around_again;
|
||||
}
|
||||
_CC_CHK(hand->callbacks->yajl_integer(hand->ctx,
|
||||
i));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case yajl_tok_double:
|
||||
if (hand->callbacks) {
|
||||
if (hand->callbacks->yajl_number) {
|
||||
_CC_CHK(hand->callbacks->yajl_number(
|
||||
hand->ctx, (const char *) buf, bufLen));
|
||||
} else if (hand->callbacks->yajl_double) {
|
||||
double d = 0.0;
|
||||
yajl_buf_clear(hand->decodeBuf);
|
||||
yajl_buf_append(hand->decodeBuf, buf, bufLen);
|
||||
buf = yajl_buf_data(hand->decodeBuf);
|
||||
errno = 0;
|
||||
d = strtod((char *) buf, NULL);
|
||||
if ((d == HUGE_VAL || d == -HUGE_VAL) &&
|
||||
errno == ERANGE)
|
||||
{
|
||||
yajl_bs_set(hand->stateStack,
|
||||
yajl_state_parse_error);
|
||||
hand->parseError = "numeric (floating point) "
|
||||
"overflow";
|
||||
/* try to restore error offset */
|
||||
if (*offset >= bufLen) *offset -= bufLen;
|
||||
else *offset = 0;
|
||||
goto around_again;
|
||||
}
|
||||
_CC_CHK(hand->callbacks->yajl_double(hand->ctx,
|
||||
d));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case yajl_tok_right_brace: {
|
||||
if (yajl_bs_current(hand->stateStack) ==
|
||||
yajl_state_array_start)
|
||||
{
|
||||
if (hand->callbacks &&
|
||||
hand->callbacks->yajl_end_array)
|
||||
{
|
||||
_CC_CHK(hand->callbacks->yajl_end_array(hand->ctx));
|
||||
}
|
||||
yajl_bs_pop(hand->stateStack);
|
||||
goto around_again;
|
||||
}
|
||||
/* intentional fall-through */
|
||||
}
|
||||
case yajl_tok_colon:
|
||||
case yajl_tok_comma:
|
||||
case yajl_tok_right_bracket:
|
||||
yajl_bs_set(hand->stateStack, yajl_state_parse_error);
|
||||
hand->parseError =
|
||||
"unallowed token at this point in JSON text";
|
||||
goto around_again;
|
||||
default:
|
||||
yajl_bs_set(hand->stateStack, yajl_state_parse_error);
|
||||
hand->parseError = "invalid token, internal error";
|
||||
goto around_again;
|
||||
}
|
||||
/* got a value. transition depends on the state we're in. */
|
||||
{
|
||||
yajl_state s = yajl_bs_current(hand->stateStack);
|
||||
if (s == yajl_state_start || s == yajl_state_got_value) {
|
||||
yajl_bs_set(hand->stateStack, yajl_state_parse_complete);
|
||||
} else if (s == yajl_state_map_need_val) {
|
||||
yajl_bs_set(hand->stateStack, yajl_state_map_got_val);
|
||||
} else {
|
||||
yajl_bs_set(hand->stateStack, yajl_state_array_got_val);
|
||||
}
|
||||
}
|
||||
if (stateToPush != yajl_state_start) {
|
||||
yajl_bs_push(hand->stateStack, stateToPush);
|
||||
}
|
||||
|
||||
goto around_again;
|
||||
}
|
||||
case yajl_state_map_start:
|
||||
case yajl_state_map_need_key: {
|
||||
/* only difference between these two states is that in
|
||||
* start '}' is valid, whereas in need_key, we've parsed
|
||||
* a comma, and a string key _must_ follow */
|
||||
tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen,
|
||||
offset, &buf, &bufLen);
|
||||
switch (tok) {
|
||||
case yajl_tok_eof:
|
||||
return yajl_status_ok;
|
||||
case yajl_tok_error:
|
||||
yajl_bs_set(hand->stateStack, yajl_state_lexical_error);
|
||||
goto around_again;
|
||||
case yajl_tok_string_with_escapes:
|
||||
if (hand->callbacks && hand->callbacks->yajl_map_key) {
|
||||
yajl_buf_clear(hand->decodeBuf);
|
||||
yajl_string_decode(hand->decodeBuf, buf, bufLen);
|
||||
buf = yajl_buf_data(hand->decodeBuf);
|
||||
bufLen = yajl_buf_len(hand->decodeBuf);
|
||||
}
|
||||
/* intentional fall-through */
|
||||
case yajl_tok_string:
|
||||
if (hand->callbacks && hand->callbacks->yajl_map_key) {
|
||||
_CC_CHK(hand->callbacks->yajl_map_key(hand->ctx, buf,
|
||||
bufLen));
|
||||
}
|
||||
yajl_bs_set(hand->stateStack, yajl_state_map_sep);
|
||||
goto around_again;
|
||||
case yajl_tok_right_bracket:
|
||||
if (yajl_bs_current(hand->stateStack) ==
|
||||
yajl_state_map_start)
|
||||
{
|
||||
if (hand->callbacks && hand->callbacks->yajl_end_map) {
|
||||
_CC_CHK(hand->callbacks->yajl_end_map(hand->ctx));
|
||||
}
|
||||
yajl_bs_pop(hand->stateStack);
|
||||
goto around_again;
|
||||
}
|
||||
default:
|
||||
yajl_bs_set(hand->stateStack, yajl_state_parse_error);
|
||||
hand->parseError =
|
||||
"invalid object key (must be a string)";
|
||||
goto around_again;
|
||||
}
|
||||
}
|
||||
case yajl_state_map_sep: {
|
||||
tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen,
|
||||
offset, &buf, &bufLen);
|
||||
switch (tok) {
|
||||
case yajl_tok_colon:
|
||||
yajl_bs_set(hand->stateStack, yajl_state_map_need_val);
|
||||
goto around_again;
|
||||
case yajl_tok_eof:
|
||||
return yajl_status_ok;
|
||||
case yajl_tok_error:
|
||||
yajl_bs_set(hand->stateStack, yajl_state_lexical_error);
|
||||
goto around_again;
|
||||
default:
|
||||
yajl_bs_set(hand->stateStack, yajl_state_parse_error);
|
||||
hand->parseError = "object key and value must "
|
||||
"be separated by a colon (':')";
|
||||
goto around_again;
|
||||
}
|
||||
}
|
||||
case yajl_state_map_got_val: {
|
||||
tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen,
|
||||
offset, &buf, &bufLen);
|
||||
switch (tok) {
|
||||
case yajl_tok_right_bracket:
|
||||
if (hand->callbacks && hand->callbacks->yajl_end_map) {
|
||||
_CC_CHK(hand->callbacks->yajl_end_map(hand->ctx));
|
||||
}
|
||||
yajl_bs_pop(hand->stateStack);
|
||||
goto around_again;
|
||||
case yajl_tok_comma:
|
||||
yajl_bs_set(hand->stateStack, yajl_state_map_need_key);
|
||||
goto around_again;
|
||||
case yajl_tok_eof:
|
||||
return yajl_status_ok;
|
||||
case yajl_tok_error:
|
||||
yajl_bs_set(hand->stateStack, yajl_state_lexical_error);
|
||||
goto around_again;
|
||||
default:
|
||||
yajl_bs_set(hand->stateStack, yajl_state_parse_error);
|
||||
hand->parseError = "after key and value, inside map, "
|
||||
"I expect ',' or '}'";
|
||||
/* try to restore error offset */
|
||||
if (*offset >= bufLen) *offset -= bufLen;
|
||||
else *offset = 0;
|
||||
goto around_again;
|
||||
}
|
||||
}
|
||||
case yajl_state_array_got_val: {
|
||||
tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen,
|
||||
offset, &buf, &bufLen);
|
||||
switch (tok) {
|
||||
case yajl_tok_right_brace:
|
||||
if (hand->callbacks && hand->callbacks->yajl_end_array) {
|
||||
_CC_CHK(hand->callbacks->yajl_end_array(hand->ctx));
|
||||
}
|
||||
yajl_bs_pop(hand->stateStack);
|
||||
goto around_again;
|
||||
case yajl_tok_comma:
|
||||
yajl_bs_set(hand->stateStack, yajl_state_array_need_val);
|
||||
goto around_again;
|
||||
case yajl_tok_eof:
|
||||
return yajl_status_ok;
|
||||
case yajl_tok_error:
|
||||
yajl_bs_set(hand->stateStack, yajl_state_lexical_error);
|
||||
goto around_again;
|
||||
default:
|
||||
yajl_bs_set(hand->stateStack, yajl_state_parse_error);
|
||||
hand->parseError =
|
||||
"after array element, I expect ',' or ']'";
|
||||
goto around_again;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abort();
|
||||
return yajl_status_error;
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __YAJL_PARSER_H__
|
||||
#define __YAJL_PARSER_H__
|
||||
|
||||
#include "api/yajl_parse.h"
|
||||
#include "yajl_bytestack.h"
|
||||
#include "yajl_buf.h"
|
||||
#include "yajl_lex.h"
|
||||
|
||||
|
||||
typedef enum {
|
||||
yajl_state_start = 0,
|
||||
yajl_state_parse_complete,
|
||||
yajl_state_parse_error,
|
||||
yajl_state_lexical_error,
|
||||
yajl_state_map_start,
|
||||
yajl_state_map_sep,
|
||||
yajl_state_map_need_val,
|
||||
yajl_state_map_got_val,
|
||||
yajl_state_map_need_key,
|
||||
yajl_state_array_start,
|
||||
yajl_state_array_got_val,
|
||||
yajl_state_array_need_val,
|
||||
yajl_state_got_value,
|
||||
} yajl_state;
|
||||
|
||||
struct yajl_handle_t {
|
||||
const yajl_callbacks * callbacks;
|
||||
void * ctx;
|
||||
yajl_lexer lexer;
|
||||
const char * parseError;
|
||||
/* the number of bytes consumed from the last client buffer,
|
||||
* in the case of an error this will be an error offset, in the
|
||||
* case of an error this can be used as the error offset */
|
||||
size_t bytesConsumed;
|
||||
/* temporary storage for decoded strings */
|
||||
yajl_buf decodeBuf;
|
||||
/* a stack of states. access with yajl_state_XXX routines */
|
||||
yajl_bytestack stateStack;
|
||||
/* memory allocation routines */
|
||||
yajl_alloc_funcs alloc;
|
||||
/* bitfield */
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
yajl_status
|
||||
yajl_do_parse(yajl_handle handle, const unsigned char * jsonText,
|
||||
size_t jsonTextLen);
|
||||
|
||||
yajl_status
|
||||
yajl_do_finish(yajl_handle handle);
|
||||
|
||||
unsigned char *
|
||||
yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText,
|
||||
size_t jsonTextLen, int verbose);
|
||||
|
||||
/* A little built in integer parsing routine with the same semantics as strtol
|
||||
* that's unaffected by LOCALE. */
|
||||
long long
|
||||
yajl_parse_integer(const unsigned char *number, unsigned int length);
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,503 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2011 Florian Forster <ff at octo.it>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "api/yajl_tree.h"
|
||||
#include "api/yajl_parse.h"
|
||||
|
||||
#include "yajl_parser.h"
|
||||
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
#define snprintf sprintf_s
|
||||
#endif
|
||||
|
||||
#define STATUS_CONTINUE 1
|
||||
#define STATUS_ABORT 0
|
||||
|
||||
struct stack_elem_s;
|
||||
typedef struct stack_elem_s stack_elem_t;
|
||||
struct stack_elem_s
|
||||
{
|
||||
char * key;
|
||||
yajl_val value;
|
||||
stack_elem_t *next;
|
||||
};
|
||||
|
||||
struct context_s
|
||||
{
|
||||
stack_elem_t *stack;
|
||||
yajl_val root;
|
||||
char *errbuf;
|
||||
size_t errbuf_size;
|
||||
};
|
||||
typedef struct context_s context_t;
|
||||
|
||||
#define RETURN_ERROR(ctx,retval,...) { \
|
||||
if ((ctx)->errbuf != NULL) \
|
||||
snprintf ((ctx)->errbuf, (ctx)->errbuf_size, __VA_ARGS__); \
|
||||
return (retval); \
|
||||
}
|
||||
|
||||
static yajl_val value_alloc (yajl_type type)
|
||||
{
|
||||
yajl_val v;
|
||||
|
||||
v = malloc (sizeof (*v));
|
||||
if (v == NULL) return (NULL);
|
||||
memset (v, 0, sizeof (*v));
|
||||
v->type = type;
|
||||
|
||||
return (v);
|
||||
}
|
||||
|
||||
static void yajl_object_free (yajl_val v)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!YAJL_IS_OBJECT(v)) return;
|
||||
|
||||
for (i = 0; i < v->u.object.len; i++)
|
||||
{
|
||||
free((char *) v->u.object.keys[i]);
|
||||
v->u.object.keys[i] = NULL;
|
||||
yajl_tree_free (v->u.object.values[i]);
|
||||
v->u.object.values[i] = NULL;
|
||||
}
|
||||
|
||||
free((void*) v->u.object.keys);
|
||||
free(v->u.object.values);
|
||||
free(v);
|
||||
}
|
||||
|
||||
static void yajl_array_free (yajl_val v)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!YAJL_IS_ARRAY(v)) return;
|
||||
|
||||
for (i = 0; i < v->u.array.len; i++)
|
||||
{
|
||||
yajl_tree_free (v->u.array.values[i]);
|
||||
v->u.array.values[i] = NULL;
|
||||
}
|
||||
|
||||
free(v->u.array.values);
|
||||
free(v);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parsing nested objects and arrays is implemented using a stack. When a new
|
||||
* object or array starts (a curly or a square opening bracket is read), an
|
||||
* appropriate value is pushed on the stack. When the end of the object is
|
||||
* reached (an appropriate closing bracket has been read), the value is popped
|
||||
* off the stack and added to the enclosing object using "context_add_value".
|
||||
*/
|
||||
static int context_push(context_t *ctx, yajl_val v)
|
||||
{
|
||||
stack_elem_t *stack;
|
||||
|
||||
stack = malloc (sizeof (*stack));
|
||||
if (stack == NULL)
|
||||
RETURN_ERROR (ctx, ENOMEM, "Out of memory");
|
||||
memset (stack, 0, sizeof (*stack));
|
||||
|
||||
assert ((ctx->stack == NULL)
|
||||
|| YAJL_IS_OBJECT (v)
|
||||
|| YAJL_IS_ARRAY (v));
|
||||
|
||||
stack->value = v;
|
||||
stack->next = ctx->stack;
|
||||
ctx->stack = stack;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static yajl_val context_pop(context_t *ctx)
|
||||
{
|
||||
stack_elem_t *stack;
|
||||
yajl_val v;
|
||||
|
||||
if (ctx->stack == NULL)
|
||||
RETURN_ERROR (ctx, NULL, "context_pop: "
|
||||
"Bottom of stack reached prematurely");
|
||||
|
||||
stack = ctx->stack;
|
||||
ctx->stack = stack->next;
|
||||
|
||||
v = stack->value;
|
||||
|
||||
free (stack);
|
||||
|
||||
return (v);
|
||||
}
|
||||
|
||||
static int object_add_keyval(context_t *ctx,
|
||||
yajl_val obj, char *key, yajl_val value)
|
||||
{
|
||||
const char **tmpk;
|
||||
yajl_val *tmpv;
|
||||
|
||||
/* We're checking for NULL in "context_add_value" or its callers. */
|
||||
assert (ctx != NULL);
|
||||
assert (obj != NULL);
|
||||
assert (key != NULL);
|
||||
assert (value != NULL);
|
||||
|
||||
/* We're assuring that "obj" is an object in "context_add_value". */
|
||||
assert(YAJL_IS_OBJECT(obj));
|
||||
|
||||
tmpk = realloc((void *) obj->u.object.keys, sizeof(*(obj->u.object.keys)) * (obj->u.object.len + 1));
|
||||
if (tmpk == NULL)
|
||||
RETURN_ERROR(ctx, ENOMEM, "Out of memory");
|
||||
obj->u.object.keys = tmpk;
|
||||
|
||||
tmpv = realloc(obj->u.object.values, sizeof (*obj->u.object.values) * (obj->u.object.len + 1));
|
||||
if (tmpv == NULL)
|
||||
RETURN_ERROR(ctx, ENOMEM, "Out of memory");
|
||||
obj->u.object.values = tmpv;
|
||||
|
||||
obj->u.object.keys[obj->u.object.len] = key;
|
||||
obj->u.object.values[obj->u.object.len] = value;
|
||||
obj->u.object.len++;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int array_add_value (context_t *ctx,
|
||||
yajl_val array, yajl_val value)
|
||||
{
|
||||
yajl_val *tmp;
|
||||
|
||||
/* We're checking for NULL pointers in "context_add_value" or its
|
||||
* callers. */
|
||||
assert (ctx != NULL);
|
||||
assert (array != NULL);
|
||||
assert (value != NULL);
|
||||
|
||||
/* "context_add_value" will only call us with array values. */
|
||||
assert(YAJL_IS_ARRAY(array));
|
||||
|
||||
tmp = realloc(array->u.array.values,
|
||||
sizeof(*(array->u.array.values)) * (array->u.array.len + 1));
|
||||
if (tmp == NULL)
|
||||
RETURN_ERROR(ctx, ENOMEM, "Out of memory");
|
||||
array->u.array.values = tmp;
|
||||
array->u.array.values[array->u.array.len] = value;
|
||||
array->u.array.len++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a value to the value on top of the stack or the "root" member in the
|
||||
* context if the end of the parsing process is reached.
|
||||
*/
|
||||
static int context_add_value (context_t *ctx, yajl_val v)
|
||||
{
|
||||
/* We're checking for NULL values in all the calling functions. */
|
||||
assert (ctx != NULL);
|
||||
assert (v != NULL);
|
||||
|
||||
/*
|
||||
* There are three valid states in which this function may be called:
|
||||
* - There is no value on the stack => This is the only value. This is the
|
||||
* last step done when parsing a document. We assign the value to the
|
||||
* "root" member and return.
|
||||
* - The value on the stack is an object. In this case store the key on the
|
||||
* stack or, if the key has already been read, add key and value to the
|
||||
* object.
|
||||
* - The value on the stack is an array. In this case simply add the value
|
||||
* and return.
|
||||
*/
|
||||
if (ctx->stack == NULL)
|
||||
{
|
||||
assert (ctx->root == NULL);
|
||||
ctx->root = v;
|
||||
return (0);
|
||||
}
|
||||
else if (YAJL_IS_OBJECT (ctx->stack->value))
|
||||
{
|
||||
if (ctx->stack->key == NULL)
|
||||
{
|
||||
if (!YAJL_IS_STRING (v))
|
||||
RETURN_ERROR (ctx, EINVAL, "context_add_value: "
|
||||
"Object key is not a string (%#04x)",
|
||||
v->type);
|
||||
|
||||
ctx->stack->key = v->u.string;
|
||||
v->u.string = NULL;
|
||||
free(v);
|
||||
return (0);
|
||||
}
|
||||
else /* if (ctx->key != NULL) */
|
||||
{
|
||||
char * key;
|
||||
|
||||
key = ctx->stack->key;
|
||||
ctx->stack->key = NULL;
|
||||
return (object_add_keyval (ctx, ctx->stack->value, key, v));
|
||||
}
|
||||
}
|
||||
else if (YAJL_IS_ARRAY (ctx->stack->value))
|
||||
{
|
||||
return (array_add_value (ctx, ctx->stack->value, v));
|
||||
}
|
||||
else
|
||||
{
|
||||
RETURN_ERROR (ctx, EINVAL, "context_add_value: Cannot add value to "
|
||||
"a value of type %#04x (not a composite type)",
|
||||
ctx->stack->value->type);
|
||||
}
|
||||
}
|
||||
|
||||
static int handle_string (void *ctx,
|
||||
const unsigned char *string, size_t string_length)
|
||||
{
|
||||
yajl_val v;
|
||||
|
||||
v = value_alloc (yajl_t_string);
|
||||
if (v == NULL)
|
||||
RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory");
|
||||
|
||||
v->u.string = malloc (string_length + 1);
|
||||
if (v->u.string == NULL)
|
||||
{
|
||||
free (v);
|
||||
RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory");
|
||||
}
|
||||
memcpy(v->u.string, string, string_length);
|
||||
v->u.string[string_length] = 0;
|
||||
|
||||
return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
|
||||
}
|
||||
|
||||
static int handle_number (void *ctx, const char *string, size_t string_length)
|
||||
{
|
||||
yajl_val v;
|
||||
char *endptr;
|
||||
|
||||
v = value_alloc(yajl_t_number);
|
||||
if (v == NULL)
|
||||
RETURN_ERROR((context_t *) ctx, STATUS_ABORT, "Out of memory");
|
||||
|
||||
v->u.number.r = malloc(string_length + 1);
|
||||
if (v->u.number.r == NULL)
|
||||
{
|
||||
free(v);
|
||||
RETURN_ERROR((context_t *) ctx, STATUS_ABORT, "Out of memory");
|
||||
}
|
||||
memcpy(v->u.number.r, string, string_length);
|
||||
v->u.number.r[string_length] = 0;
|
||||
|
||||
v->u.number.flags = 0;
|
||||
|
||||
errno = 0;
|
||||
v->u.number.i = yajl_parse_integer((const unsigned char *) v->u.number.r,
|
||||
strlen(v->u.number.r));
|
||||
if (errno == 0)
|
||||
v->u.number.flags |= YAJL_NUMBER_INT_VALID;
|
||||
|
||||
endptr = NULL;
|
||||
errno = 0;
|
||||
v->u.number.d = strtod(v->u.number.r, &endptr);
|
||||
if ((errno == 0) && (endptr != NULL) && (*endptr == 0))
|
||||
v->u.number.flags |= YAJL_NUMBER_DOUBLE_VALID;
|
||||
|
||||
return ((context_add_value(ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
|
||||
}
|
||||
|
||||
static int handle_start_map (void *ctx)
|
||||
{
|
||||
yajl_val v;
|
||||
|
||||
v = value_alloc(yajl_t_object);
|
||||
if (v == NULL)
|
||||
RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory");
|
||||
|
||||
v->u.object.keys = NULL;
|
||||
v->u.object.values = NULL;
|
||||
v->u.object.len = 0;
|
||||
|
||||
return ((context_push (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
|
||||
}
|
||||
|
||||
static int handle_end_map (void *ctx)
|
||||
{
|
||||
yajl_val v;
|
||||
|
||||
v = context_pop (ctx);
|
||||
if (v == NULL)
|
||||
return (STATUS_ABORT);
|
||||
|
||||
return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
|
||||
}
|
||||
|
||||
static int handle_start_array (void *ctx)
|
||||
{
|
||||
yajl_val v;
|
||||
|
||||
v = value_alloc(yajl_t_array);
|
||||
if (v == NULL)
|
||||
RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory");
|
||||
|
||||
v->u.array.values = NULL;
|
||||
v->u.array.len = 0;
|
||||
|
||||
return ((context_push (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
|
||||
}
|
||||
|
||||
static int handle_end_array (void *ctx)
|
||||
{
|
||||
yajl_val v;
|
||||
|
||||
v = context_pop (ctx);
|
||||
if (v == NULL)
|
||||
return (STATUS_ABORT);
|
||||
|
||||
return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
|
||||
}
|
||||
|
||||
static int handle_boolean (void *ctx, int boolean_value)
|
||||
{
|
||||
yajl_val v;
|
||||
|
||||
v = value_alloc (boolean_value ? yajl_t_true : yajl_t_false);
|
||||
if (v == NULL)
|
||||
RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory");
|
||||
|
||||
return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
|
||||
}
|
||||
|
||||
static int handle_null (void *ctx)
|
||||
{
|
||||
yajl_val v;
|
||||
|
||||
v = value_alloc (yajl_t_null);
|
||||
if (v == NULL)
|
||||
RETURN_ERROR ((context_t *) ctx, STATUS_ABORT, "Out of memory");
|
||||
|
||||
return ((context_add_value (ctx, v) == 0) ? STATUS_CONTINUE : STATUS_ABORT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Public functions
|
||||
*/
|
||||
yajl_val yajl_tree_parse (const char *input,
|
||||
char *error_buffer, size_t error_buffer_size)
|
||||
{
|
||||
static const yajl_callbacks callbacks =
|
||||
{
|
||||
/* null = */ handle_null,
|
||||
/* boolean = */ handle_boolean,
|
||||
/* integer = */ NULL,
|
||||
/* double = */ NULL,
|
||||
/* number = */ handle_number,
|
||||
/* string = */ handle_string,
|
||||
/* start map = */ handle_start_map,
|
||||
/* map key = */ handle_string,
|
||||
/* end map = */ handle_end_map,
|
||||
/* start array = */ handle_start_array,
|
||||
/* end array = */ handle_end_array
|
||||
};
|
||||
|
||||
yajl_handle handle;
|
||||
yajl_status status;
|
||||
char * internal_err_str;
|
||||
context_t ctx = { NULL, NULL, NULL, 0 };
|
||||
|
||||
ctx.errbuf = error_buffer;
|
||||
ctx.errbuf_size = error_buffer_size;
|
||||
|
||||
if (error_buffer != NULL)
|
||||
memset (error_buffer, 0, error_buffer_size);
|
||||
|
||||
handle = yajl_alloc (&callbacks, NULL, &ctx);
|
||||
yajl_config(handle, yajl_allow_comments, 1);
|
||||
|
||||
status = yajl_parse(handle,
|
||||
(unsigned char *) input,
|
||||
strlen (input));
|
||||
status = yajl_complete_parse (handle);
|
||||
if (status != yajl_status_ok) {
|
||||
if (error_buffer != NULL && error_buffer_size > 0) {
|
||||
internal_err_str = (char *) yajl_get_error(handle, 1,
|
||||
(const unsigned char *) input,
|
||||
strlen(input));
|
||||
snprintf(error_buffer, error_buffer_size, "%s", internal_err_str);
|
||||
YA_FREE(&(handle->alloc), internal_err_str);
|
||||
}
|
||||
yajl_free (handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
yajl_free (handle);
|
||||
return (ctx.root);
|
||||
}
|
||||
|
||||
yajl_val yajl_tree_get(yajl_val n, const char ** path, yajl_type type)
|
||||
{
|
||||
if (!path) return NULL;
|
||||
while (n && *path) {
|
||||
size_t i;
|
||||
size_t len;
|
||||
|
||||
if (n->type != yajl_t_object) return NULL;
|
||||
len = n->u.object.len;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!strcmp(*path, n->u.object.keys[i])) {
|
||||
n = n->u.object.values[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == len) return NULL;
|
||||
path++;
|
||||
}
|
||||
if (n && type != yajl_t_any && type != n->type) n = NULL;
|
||||
return n;
|
||||
}
|
||||
|
||||
void yajl_tree_free (yajl_val v)
|
||||
{
|
||||
if (v == NULL) return;
|
||||
|
||||
if (YAJL_IS_STRING(v))
|
||||
{
|
||||
free(v->u.string);
|
||||
free(v);
|
||||
}
|
||||
else if (YAJL_IS_NUMBER(v))
|
||||
{
|
||||
free(v->u.number.r);
|
||||
free(v);
|
||||
}
|
||||
else if (YAJL_GET_OBJECT(v))
|
||||
{
|
||||
yajl_object_free(v);
|
||||
}
|
||||
else if (YAJL_GET_ARRAY(v))
|
||||
{
|
||||
yajl_array_free(v);
|
||||
}
|
||||
else /* if (yajl_t_true or yajl_t_false or yajl_t_null) */
|
||||
{
|
||||
free(v);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#include <yajl/yajl_version.h>
|
||||
|
||||
int yajl_version(void)
|
||||
{
|
||||
return YAJL_VERSION;
|
||||
}
|
||||
|
Loading…
Reference in New Issue