mirror of https://github.com/Icinga/icinga2.git
363 lines
11 KiB
C
363 lines
11 KiB
C
/*
|
|
* 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);
|
|
}
|