src/htsmsg_binary.c \
src/htsmsg_json.c \
src/htsmsg_xml.c \
+ src/misc/dbl.c \
+ src/misc/json.c \
src/settings.c \
src/htsbuf.c \
src/trap.c \
/*
* Buffer management functions
- * Copyright (C) 2008 Andreas Öman
+ * Copyright (C) 2008 Andreas Öman
*
* 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
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
-#include "htsbuf.h"
-#include "tvheadend.h"
-
-#ifndef MIN
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-#endif
-
-#ifndef MAX
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
-#endif
+#include "tvheadend.h"
+#include "htsbuf.h"
/**
*
c = MIN(hd->hd_data_len - hd->hd_data_off, len);
memcpy(buf, hd->hd_data + hd->hd_data_off, c);
+ r += c;
buf += c;
len -= c;
len -= c;
hd->hd_data_off += c;
hq->hq_size -= c;
-
+ r += c;
if(hd->hd_data_off == hd->hd_data_len)
htsbuf_data_free(hq, hd);
}
}
+void
+htsbuf_dump_raw_stderr(htsbuf_queue_t *hq)
+{
+ htsbuf_data_t *hd;
+ char n = '\n';
+
+ TAILQ_FOREACH(hd, &hq->hq_q, hd_link) {
+ if(write(2, hd->hd_data + hd->hd_data_off,
+ hd->hd_data_len - hd->hd_data_off)
+ != hd->hd_data_len - hd->hd_data_off)
+ break;
+ }
+ if(write(2, &n, 1) != 1)
+ return;
+}
+
+
+void
+htsbuf_hexdump(htsbuf_queue_t *hq, const char *prefix)
+{
+ void *r = malloc(hq->hq_size);
+ htsbuf_peek(hq, r, hq->hq_size);
+ hexdump(prefix, r, hq->hq_size);
+ free(r);
+}
+
+
/**
*
*/
-uint32_t
-htsbuf_crc32(htsbuf_queue_t *hq, uint32_t crc)
+void
+htsbuf_append_and_escape_xml(htsbuf_queue_t *hq, const char *s)
{
- htsbuf_data_t *hd;
-
- TAILQ_FOREACH(hd, &hq->hq_q, hd_link)
- crc = tvh_crc32(hd->hd_data + hd->hd_data_off,
- hd->hd_data_len - hd->hd_data_off,
- crc);
- return crc;
+ const char *c = s;
+ const char *e = s + strlen(s);
+ if(e == s)
+ return;
+
+ while(1) {
+ const char *esc;
+ switch(*c++) {
+ case '<': esc = "<"; break;
+ case '>': esc = ">"; break;
+ case '&': esc = "&"; break;
+ case '\'': esc = "'"; break;
+ case '"': esc = """; break;
+ default: esc = NULL; break;
+ }
+
+ if(esc != NULL) {
+ htsbuf_append(hq, s, c - s - 1);
+ htsbuf_append(hq, esc, strlen(esc));
+ s = c;
+ }
+
+ if(c == e) {
+ htsbuf_append(hq, s, c - s);
+ break;
+ }
+ }
}
+/**
+ *
+ */
+void
+htsbuf_append_and_escape_url(htsbuf_queue_t *hq, const char *s)
+{
+ const char *c = s;
+ const char *e = s + strlen(s);
+ char C;
+ if(e == s)
+ return;
+
+ while(1) {
+ const char *esc;
+ C = *c++;
+
+ if((C >= '0' && C <= '9') ||
+ (C >= 'a' && C <= 'z') ||
+ (C >= 'A' && C <= 'Z') ||
+ C == '_' ||
+ C == '~' ||
+ C == '.' ||
+ C == '-') {
+ esc = NULL;
+ } else {
+ static const char hexchars[16] = "0123456789ABCDEF";
+ char buf[4];
+ buf[0] = '%';
+ buf[1] = hexchars[(C >> 4) & 0xf];
+ buf[2] = hexchars[C & 0xf];;
+ buf[3] = 0;
+ esc = buf;
+ }
+
+ if(esc != NULL) {
+ htsbuf_append(hq, s, c - s - 1);
+ htsbuf_append(hq, esc, strlen(esc));
+ s = c;
+ }
+
+ if(c == e) {
+ htsbuf_append(hq, s, c - s);
+ break;
+ }
+ }
+}
+
+
+/**
+ *
+ */
+void
+htsbuf_append_and_escape_jsonstr(htsbuf_queue_t *hq, const char *str)
+{
+ const char *s = str;
+
+ htsbuf_append(hq, "\"", 1);
+
+ while(*s != 0) {
+ if(*s == '"' || *s == '\\' || *s == '\n' || *s == '\r' || *s == '\t') {
+ htsbuf_append(hq, str, s - str);
+
+ if(*s == '"')
+ htsbuf_append(hq, "\\\"", 2);
+ else if(*s == '\n')
+ htsbuf_append(hq, "\\n", 2);
+ else if(*s == '\r')
+ htsbuf_append(hq, "\\r", 2);
+ else if(*s == '\t')
+ htsbuf_append(hq, "\\t", 2);
+ else
+ htsbuf_append(hq, "\\\\", 2);
+ s++;
+ str = s;
+ } else {
+ s++;
+ }
+ }
+ htsbuf_append(hq, str, s - str);
+ htsbuf_append(hq, "\"", 1);
+}
+
+
+
+/**
+ *
+ */
+char *
+htsbuf_to_string(htsbuf_queue_t *hq)
+{
+ char *r = malloc(hq->hq_size + 1);
+ r[hq->hq_size] = 0;
+ htsbuf_read(hq, r, hq->hq_size);
+ return r;
+}
+
/*
* Buffer management functions
- * Copyright (C) 2008 Andreas Öman
+ * Copyright (C) 2008 Andreas Öman
*
* 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
#define HTSBUF_H__
#include <stdarg.h>
-#include <stddef.h>
-#include "queue.h"
#include <inttypes.h>
+#include "queue.h"
+
+
TAILQ_HEAD(htsbuf_data_queue, htsbuf_data);
typedef struct htsbuf_data {
void htsbuf_vqprintf(htsbuf_queue_t *hq, const char *fmt, va_list ap);
-void htsbuf_qprintf(htsbuf_queue_t *hq, const char *fmt, ...)
- __attribute__((format(printf,2,3)));
+void htsbuf_qprintf(htsbuf_queue_t *hq, const char *fmt, ...);
void htsbuf_append(htsbuf_queue_t *hq, const void *buf, size_t len);
void htsbuf_append_prealloc(htsbuf_queue_t *hq, const void *buf, size_t len);
-void htsbuf_appendq(htsbuf_queue_t *hq, htsbuf_queue_t *src);
-
void htsbuf_data_free(htsbuf_queue_t *hq, htsbuf_data_t *hd);
size_t htsbuf_read(htsbuf_queue_t *hq, void *buf, size_t len);
size_t htsbuf_find(htsbuf_queue_t *hq, uint8_t v);
-uint32_t htsbuf_crc32(htsbuf_queue_t *q, uint32_t crc);
+void htsbuf_appendq(htsbuf_queue_t *hq, htsbuf_queue_t *src);
+
+void htsbuf_append_and_escape_xml(htsbuf_queue_t *hq, const char *str);
+
+void htsbuf_append_and_escape_url(htsbuf_queue_t *hq, const char *s);
+
+void htsbuf_append_and_escape_jsonstr(htsbuf_queue_t *hq, const char *s);
+
+void htsbuf_dump_raw_stderr(htsbuf_queue_t *hq);
+
+char *htsbuf_to_string(htsbuf_queue_t *hq);
+
+struct rstr;
+struct rstr *htsbuf_to_rstr(htsbuf_queue_t *hq, const char *prefix);
+
+void htsbuf_hexdump(htsbuf_queue_t *hq, const char *prefix);
#endif /* HTSBUF_H__ */
/*
* Functions for manipulating HTS messages
- * Copyright (C) 2007 Andreas Öman
+ * Copyright (C) 2007 Andreas Öman
*
* 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
static void htsmsg_clear(htsmsg_t *msg);
-/*
+/**
*
*/
-static void
+void
htsmsg_field_destroy(htsmsg_t *msg, htsmsg_field_t *f)
{
TAILQ_REMOVE(&msg->hm_fields, f, hmf_link);
/*
*
*/
-static htsmsg_field_t *
+htsmsg_field_t *
htsmsg_field_find(htsmsg_t *msg, const char *name)
{
htsmsg_field_t *f;
}
/*
- *
+ *
*/
void
-htsmsg_add_u64(htsmsg_t *msg, const char *name, uint64_t u64)
+htsmsg_add_s32(htsmsg_t *msg, const char *name, int32_t s32)
{
htsmsg_field_t *f = htsmsg_field_add(msg, name, HMF_S64, HMF_NAME_ALLOCED);
- f->hmf_s64 = u64;
+ f->hmf_s64 = s32;
}
+
/*
*
*/
void
-htsmsg_add_s32(htsmsg_t *msg, const char *name, int32_t s32)
+htsmsg_add_dbl(htsmsg_t *msg, const char *name, double dbl)
{
- htsmsg_field_t *f = htsmsg_field_add(msg, name, HMF_S64, HMF_NAME_ALLOCED);
- f->hmf_s64 = s32;
+ htsmsg_field_t *f = htsmsg_field_add(msg, name, HMF_DBL, HMF_NAME_ALLOCED);
+ f->hmf_dbl = dbl;
}
HMF_NAME_ALLOCED);
assert(sub->hm_data == NULL);
- TAILQ_MOVE(&f->hmf_msg.hm_fields, &sub->hm_fields, hmf_link);
f->hmf_msg.hm_islist = sub->hm_islist;
+ TAILQ_MOVE(&f->hmf_msg.hm_fields, &sub->hm_fields, hmf_link);
free(sub);
}
case HMF_S64:
*s64p = f->hmf_s64;
break;
+ case HMF_DBL:
+ *s64p = f->hmf_dbl;
+ break;
}
return 0;
}
+
/**
*
*/
return htsmsg_get_s64(msg, name, &s64) ? def : s64;
}
-/**
- *
- */
-int
-htsmsg_get_u64(htsmsg_t *msg, const char *name, uint64_t *u64p)
-{
- htsmsg_field_t *f;
-
- if((f = htsmsg_field_find(msg, name)) == NULL)
- return HTSMSG_ERR_FIELD_NOT_FOUND;
-
- switch(f->hmf_type) {
- default:
- return HTSMSG_ERR_CONVERSION_IMPOSSIBLE;
- case HMF_STR:
- *u64p = strtoull(f->hmf_str, NULL, 0);
- break;
- case HMF_S64:
- *u64p = f->hmf_s64;
- break;
- }
- return 0;
-}
-
/*
*
/**
*
*/
-uint32_t
+int
htsmsg_get_u32_or_default(htsmsg_t *msg, const char *name, uint32_t def)
{
uint32_t u32;
return htsmsg_get_u32(msg, name, &u32) ? def : u32;
}
+
+/**
+ *
+ */
+int32_t
+htsmsg_get_s32_or_default(htsmsg_t *msg, const char *name, int32_t def)
+{
+ int32_t s32;
+ return htsmsg_get_s32(msg, name, &s32) ? def : s32;
+}
+
+
+
/*
*
*/
}
+/*
+ *
+ */
+int
+htsmsg_get_dbl(htsmsg_t *msg, const char *name, double *dblp)
+{
+ htsmsg_field_t *f;
+
+ if((f = htsmsg_field_find(msg, name)) == NULL)
+ return HTSMSG_ERR_FIELD_NOT_FOUND;
+
+ if(f->hmf_type != HMF_DBL)
+ return HTSMSG_ERR_CONVERSION_IMPOSSIBLE;
+
+ *dblp = f->hmf_dbl;
+ return 0;
+}
+
+
/*
*
*/
}
-const char *
-htsmsg_get_str_or_default(htsmsg_t *msg, const char *name, const char *def)
-{
- const char *str = htsmsg_get_str(msg, name);
- return str ?: def;
-}
-
/*
*
*/
return msg;
}
+/**
+ *
+ */
+const char *
+htsmsg_get_str_multi(htsmsg_t *msg, ...)
+{
+ va_list ap;
+ const char *n;
+ htsmsg_field_t *f;
+ va_start(ap, msg);
+
+ while((n = va_arg(ap, char *)) != NULL) {
+ if((f = htsmsg_field_find(msg, n)) == NULL)
+ return NULL;
+ else if(f->hmf_type == HMF_STR)
+ return f->hmf_str;
+ else if(f->hmf_type == HMF_MAP)
+ msg = &f->hmf_msg;
+ else
+ return NULL;
+ }
+ return NULL;
+}
+
+
+
/*
*
*/
case HMF_S64:
printf("S64) = %" PRId64 "\n", f->hmf_s64);
break;
+
+ case HMF_DBL:
+ printf("DBL) = %f\n", f->hmf_dbl);
+ break;
}
}
}
case HMF_BIN:
htsmsg_add_bin(dst, f->hmf_name, f->hmf_bin, f->hmf_binsize);
break;
+
+ case HMF_DBL:
+ htsmsg_add_dbl(dst, f->hmf_name, f->hmf_dbl);
+ break;
}
}
}
return dst;
}
+/**
+ *
+ */
+htsmsg_t *
+htsmsg_get_map_in_list(htsmsg_t *m, int num)
+{
+ htsmsg_field_t *f;
+
+ HTSMSG_FOREACH(f, m) {
+ if(!--num)
+ return htsmsg_get_map_by_field(f);
+ }
+ return NULL;
+}
+
+
+/**
+ *
+ */
+htsmsg_t *
+htsmsg_get_map_by_field_if_name(htsmsg_field_t *f, const char *name)
+{
+ if(f->hmf_type != HMF_MAP)
+ return NULL;
+ if(strcmp(f->hmf_name, name))
+ return NULL;
+ return &f->hmf_msg;
+}
+
+
+/**
+ *
+ */
+const char *
+htsmsg_get_cdata(htsmsg_t *m, const char *field)
+{
+ return htsmsg_get_str_multi(m, field, "cdata", NULL);
+}
+
/**
*
if(*mp != NULL)
htsmsg_destroy(*mp);
}
+
/*
* Functions for manipulating HTS messages
- * Copyright (C) 2007 Andreas Öman
+ * Copyright (C) 2007 Andreas Öman
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef HTSMSG_H_
-#define HTSMSG_H_
-
+#pragma once
+#include <stdlib.h>
#include <inttypes.h>
#include "queue.h"
#define HMF_STR 3
#define HMF_BIN 4
#define HMF_LIST 5
+#define HMF_DBL 6
typedef struct htsmsg_field {
TAILQ_ENTRY(htsmsg_field) hmf_link;
size_t len;
} bin;
htsmsg_t msg;
+ double dbl;
} u;
} htsmsg_field_t;
#define hmf_str u.str
#define hmf_bin u.bin.data
#define hmf_binsize u.bin.len
+#define hmf_dbl u.dbl
#define htsmsg_get_map_by_field(f) \
((f)->hmf_type == HMF_MAP ? &(f)->hmf_msg : NULL)
#define htsmsg_get_list_by_field(f) \
- ((f)->hmf_type == HMF_LIST ? &(f)->hmf_msg : NULL)
+ ((f)->hmf_type == HMF_LIST ? &(f)->hmf_msg : NULL)
#define HTSMSG_FOREACH(f, msg) TAILQ_FOREACH(f, &(msg)->hm_fields, hmf_link)
*/
htsmsg_t *htsmsg_create_list(void);
+/**
+ * Remove a given field from a msg
+ */
+void htsmsg_field_destroy(htsmsg_t *msg, htsmsg_field_t *f);
+
/**
* Destroys a message (map or list)
*/
*/
void htsmsg_add_s32(htsmsg_t *msg, const char *name, int32_t s32);
-/**
- * Add an integer field where source is unsigned 64 bit.
- */
-void htsmsg_add_u64(htsmsg_t *msg, const char *name, uint64_t u64);
-
/**
* Add an integer field where source is signed 64 bit.
*/
*/
void htsmsg_add_msg(htsmsg_t *msg, const char *name, htsmsg_t *sub);
+/**
+ * Add an field where source is a double
+ */
+void htsmsg_add_dbl(htsmsg_t *msg, const char *name, double dbl);
+
/**
* Add an field where source is a list or map message.
*
*/
int htsmsg_get_u32(htsmsg_t *msg, const char *name, uint32_t *u32p);
-/**
- * Return the field \p name as an u32.
- *
- * @return An unsigned 32 bit integer or def if the field can not be found
- * or if conversion is not possible.
- */
-uint32_t htsmsg_get_u32_or_default
- (htsmsg_t *msg, const char *name, uint32_t def);
-
/**
* Get an integer as an signed 32 bit integer.
*
*/
int htsmsg_get_s64(htsmsg_t *msg, const char *name, int64_t *s64p);
-
-/**
+/*
* Return the field \p name as an s64.
- *
- * @return A signed 64 bit integer or def if the field can not be found
- * or if conversion is not possible.
*/
-int64_t htsmsg_get_s64_or_default
- (htsmsg_t *msg, const char *name, int64_t def);
+int64_t htsmsg_get_s64_or_default(htsmsg_t *msg, const char *name, int64_t def);
-/**
- * Get an integer as an unsigned 64 bit integer.
- *
- * @return HTSMSG_ERR_FIELD_NOT_FOUND - Field does not exist
- * HTSMSG_ERR_CONVERSION_IMPOSSIBLE - Field is not an integer or
- * out of range for the requested storage.
- */
-int htsmsg_get_u64(htsmsg_t *msg, const char *name, uint64_t *u64p);
/**
* Get pointer to a binary field. No copying of data is performed.
*/
const char *htsmsg_get_str(htsmsg_t *msg, const char *name);
-/**
- * Get a field of type 'string'. No copying is done.
- *
- * @return def if the field can not be found or not of string type.
- * Otherwise a pointer to the data is returned.
- */
-const char *htsmsg_get_str_or_default
- (htsmsg_t *msg, const char *name, const char *def);
-
/**
* Get a field of type 'map'. No copying is done.
*
/**
* Traverse a hierarchy of htsmsg's to find a specific child.
*/
-htsmsg_t *htsmsg_get_map_multi(htsmsg_t *msg, ...);
+htsmsg_t *htsmsg_get_map_multi(htsmsg_t *msg, ...)
+ __attribute__((__sentinel__(0)));
+
+/**
+ * Traverse a hierarchy of htsmsg's to find a specific child.
+ */
+const char *htsmsg_get_str_multi(htsmsg_t *msg, ...)
+ __attribute__((__sentinel__(0)));
+
+/**
+ * Get a field of type 'double'.
+ *
+ * @return HTSMSG_ERR_FIELD_NOT_FOUND - Field does not exist
+ * HTSMSG_ERR_CONVERSION_IMPOSSIBLE - Field is not an integer or
+ * out of range for the requested storage.
+ */
+int htsmsg_get_dbl(htsmsg_t *msg, const char *name, double *dblp);
/**
* Given the field \p f, return a string if it is of type string, otherwise
*/
const char *htsmsg_field_get_string(htsmsg_field_t *f);
+/**
+ * Return the field \p name as an u32.
+ *
+ * @return An unsigned 32 bit integer or NULL if the field can not be found
+ * or if conversion is not possible.
+ */
+int htsmsg_get_u32_or_default(htsmsg_t *msg, const char *name, uint32_t def);
+
+/**
+ * Return the field \p name as an s32.
+ *
+ * @return A signed 32 bit integer or NULL if the field can not be found
+ * or if conversion is not possible.
+ */
+int32_t htsmsg_get_s32_or_default(htsmsg_t *msg, const char *name,
+ int32_t def);
+
/**
* Remove the given field called \p name from the message \p msg.
*/
htsmsg_field_t *htsmsg_field_add(htsmsg_t *msg, const char *name,
int type, int flags);
+/**
+ * Get a field, return NULL if it does not exist
+ */
+htsmsg_field_t *htsmsg_field_find(htsmsg_t *msg, const char *name);
+
+
/**
* Clone a message.
*/
#define HTSMSG_FOREACH(f, msg) TAILQ_FOREACH(f, &(msg)->hm_fields, hmf_link)
+/**
+ * Misc
+ */
+htsmsg_t *htsmsg_get_map_in_list(htsmsg_t *m, int num);
+
+htsmsg_t *htsmsg_get_map_by_field_if_name(htsmsg_field_t *f, const char *name);
+
+const char *htsmsg_get_cdata(htsmsg_t *m, const char *field);
+
extern void htsmsg_dtor(htsmsg_t **mp);
#define htsmsg_autodtor(n) htsmsg_t *n __attribute__((cleanup(htsmsg_dtor)))
-
-#endif /* HTSMSG_H_ */
#include "htsmsg_json.h"
#include "htsbuf.h"
+#include "misc/json.h"
+#include "misc/dbl.h"
+
/**
*
*/
static void
-htsmsg_json_encode_string(const char *str, htsbuf_queue_t *hq)
-{
- const char *s = str;
-
- htsbuf_append(hq, "\"", 1);
-
- while(*s != 0) {
- if(*s == '"' || *s == '\\' || *s == '\n' || *s == '\t' || *s == '\r') {
- htsbuf_append(hq, str, s - str);
-
- if(*s == '"')
- htsbuf_append(hq, "\\\"", 2);
- else if(*s == '\n')
- htsbuf_append(hq, "\\n", 2);
- else if(*s == '\t')
- htsbuf_append(hq, "\\t", 2);
- else if(*s == '\r')
- htsbuf_append(hq, "\\r", 2);
- else
- htsbuf_append(hq, "\\\\", 2);
- s++;
- str = s;
- } else {
- s++;
- }
- }
- htsbuf_append(hq, str, s - str);
- htsbuf_append(hq, "\"", 1);
-}
-
-
-/*
- * Note to future:
- * If your about to add support for numbers with decimal point,
- * remember to always serialize them with '.' as decimal point character
- * no matter what current locale says. This is according to the JSON spec.
- */
-static void
htsmsg_json_write(htsmsg_t *msg, htsbuf_queue_t *hq, int isarray,
int indent, int pretty)
{
htsmsg_field_t *f;
- char buf[30];
+ char buf[100];
static const char *indentor = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
htsbuf_append(hq, isarray ? "[" : "{", 1);
htsbuf_append(hq, indentor, indent < 16 ? indent : 16);
if(!isarray) {
- htsmsg_json_encode_string(f->hmf_name ?: "noname", hq);
+ htsbuf_append_and_escape_jsonstr(hq, f->hmf_name ?: "noname");
htsbuf_append(hq, ": ", 2);
}
break;
case HMF_STR:
- htsmsg_json_encode_string(f->hmf_str, hq);
+ htsbuf_append_and_escape_jsonstr(hq, f->hmf_str);
break;
case HMF_BIN:
- htsmsg_json_encode_string("binary", hq);
+ htsbuf_append_and_escape_jsonstr(hq, "binary");
break;
case HMF_S64:
htsbuf_append(hq, buf, strlen(buf));
break;
+ case HMF_DBL:
+ my_double2str(buf, sizeof(buf), f->hmf_dbl);
+ htsbuf_append(hq, buf, strlen(buf));
+ break;
+
default:
abort();
}
/**
*
*/
-int
+void
htsmsg_json_serialize(htsmsg_t *msg, htsbuf_queue_t *hq, int pretty)
{
htsmsg_json_write(msg, hq, msg->hm_islist, 2, pretty);
if(pretty)
htsbuf_append(hq, "\n", 1);
- return 0;
}
-
-static const char *htsmsg_json_parse_value(const char *s,
- htsmsg_t *parent, char *name);
-
/**
*
*/
-static char *
-htsmsg_json_parse_string(const char *s, const char **endp)
+char *
+htsmsg_json_serialize_to_str(htsmsg_t *msg, int pretty)
{
- const char *start;
- char *r, *a, *b;
- int l, esc = 0;
-
- while(*s > 0 && *s < 33)
- s++;
-
- if(*s != '"')
- return NULL;
-
- s++;
- start = s;
-
- while(1) {
- if(*s == 0)
- return NULL;
-
- if(*s == '\\') {
- esc = 1;
- /* skip the escape */
- s++;
- if (*s == 'u') s += 4;
- // Note: we could detect the lack of support here!
- } else if(*s == '"') {
-
- *endp = s + 1;
-
- /* End */
- l = s - start;
- r = malloc(l + 1);
- memcpy(r, start, l);
- r[l] = 0;
-
- if(esc) {
- /* Do deescaping inplace */
-
- a = b = r;
-
- while(*a) {
- if(*a == '\\') {
- a++;
- if(*a == 'b')
- *b++ = '\b';
- else if(*a == '\\')
- *b++ = '\\';
- else if(*a == 'f')
- *b++ = '\f';
- else if(*a == 'n')
- *b++ = '\n';
- else if(*a == 'r')
- *b++ = '\r';
- else if(*a == 't')
- *b++ = '\t';
- else if(*a == 'u') {
- /* 4 hexdigits: Not supported */
- free(r);
- return NULL;
- } else {
- *b++ = *a;
- }
- a++;
- } else {
- *b++ = *a++;
- }
- }
- *b = 0;
- }
- return r;
- }
- s++;
- }
+ htsbuf_queue_t hq;
+ char *str;
+ htsbuf_queue_init(&hq, 0);
+ htsmsg_json_serialize(msg, &hq, pretty);
+ str = htsbuf_to_string(&hq);
+ htsbuf_queue_flush(&hq);
+ return str;
}
/**
*
*/
-static htsmsg_t *
-htsmsg_json_parse_object(const char *s, const char **endp)
+static void *
+create_map(void *opaque)
{
- char *name;
- const char *s2;
- htsmsg_t *r;
-
- while(*s > 0 && *s < 33)
- s++;
-
- if(*s != '{')
- return NULL;
-
- s++;
-
- r = htsmsg_create_map();
-
- while(*s > 0 && *s < 33)
- s++;
-
- if(*s != '}') while(1) {
-
- if((name = htsmsg_json_parse_string(s, &s2)) == NULL) {
- htsmsg_destroy(r);
- return NULL;
- }
-
- s = s2;
-
- while(*s > 0 && *s < 33)
- s++;
-
- if(*s != ':') {
- htsmsg_destroy(r);
- free(name);
- return NULL;
- }
- s++;
-
- s2 = htsmsg_json_parse_value(s, r, name);
- free(name);
-
- if(s2 == NULL) {
- htsmsg_destroy(r);
- return NULL;
- }
-
- s = s2;
-
- while(*s > 0 && *s < 33)
- s++;
-
- if(*s == '}')
- break;
-
- if(*s != ',') {
- htsmsg_destroy(r);
- return NULL;
- }
- s++;
- }
-
- s++;
- *endp = s;
- return r;
+ return htsmsg_create_map();
}
-
-/**
- *
- */
-static htsmsg_t *
-htsmsg_json_parse_array(const char *s, const char **endp)
+static void *
+create_list(void *opaque)
{
- const char *s2;
- htsmsg_t *r;
-
- while(*s > 0 && *s < 33)
- s++;
-
- if(*s != '[')
- return NULL;
-
- s++;
-
- r = htsmsg_create_list();
-
- while(*s > 0 && *s < 33)
- s++;
-
- if(*s != ']') {
-
- while(1) {
-
- s2 = htsmsg_json_parse_value(s, r, NULL);
-
- if(s2 == NULL) {
- htsmsg_destroy(r);
- return NULL;
- }
-
- s = s2;
+ return htsmsg_create_list();
+}
- while(*s > 0 && *s < 33)
- s++;
+static void
+destroy_obj(void *opaque, void *obj)
+{
+ return htsmsg_destroy(obj);
+}
- if(*s == ']')
- break;
+static void
+add_obj(void *opaque, void *parent, const char *name, void *child)
+{
+ htsmsg_add_msg(parent, name, child);
+}
- if(*s != ',') {
- htsmsg_destroy(r);
- return NULL;
- }
- s++;
- }
- }
- s++;
- *endp = s;
- return r;
+static void
+add_string(void *opaque, void *parent, const char *name, char *str)
+{
+ htsmsg_add_str(parent, name, str);
+ free(str);
}
-/*
- * locale independent strtod.
- * does not support hex floats as the standard strtod
- */
-static double
-_strntod(const char *s, char decimal_point_char, char **ep)
+static void
+add_long(void *opaque, void *parent, const char *name, long v)
{
- static char locale_decimal_point_char = 0;
- char buf[64];
- const char *c;
- double d;
-
- /* ugly but very portable way to get decimal point char */
- if(locale_decimal_point_char == 0) {
- snprintf(buf, sizeof(buf), "%f", 0.0);
- locale_decimal_point_char = buf[1];
- assert(locale_decimal_point_char != 0);
- }
-
- for(c = s;
- *c != '\0' &&
- ((*c > 0 && *c < 33) || /* skip whitespace */
- (*c == decimal_point_char || strchr("+-0123456789", *c) != NULL)); c++)
- ;
-
- strncpy(buf, s, c - s);
- buf[c - s] = '\0';
-
- /* replace if specified char is not same as current locale */
- if(decimal_point_char != locale_decimal_point_char) {
- char *r = strchr(buf, decimal_point_char);
- if(r != NULL)
- *r = locale_decimal_point_char;
- }
-
- d = strtod(buf, ep);
-
- /* figure out offset in original string */
- if(ep != NULL)
- *ep = (char *)s + (*ep - buf);
-
- return d;
+ htsmsg_add_s64(parent, name, v);
}
-/**
- *
- */
-static char *
-htsmsg_json_parse_number(const char *s, double *dp)
+static void
+add_double(void *opaque, void *parent, const char *name, double v)
{
- char *ep;
- double d = _strntod(s, '.', &ep);
+ htsmsg_add_dbl(parent, name, v);
+}
- if(ep == s)
- return NULL;
+static void
+add_bool(void *opaque, void *parent, const char *name, int v)
+{
+ htsmsg_add_u32(parent, name, v);
+}
- *dp = d;
- return ep;
+static void
+add_null(void *opaque, void *parent, const char *name)
+{
}
/**
*
*/
-static const char *
-htsmsg_json_parse_value(const char *s, htsmsg_t *parent, char *name)
-{
- const char *s2;
- char *str;
- double d = 0;
- htsmsg_t *c;
-
- if((c = htsmsg_json_parse_object(s, &s2)) != NULL) {
- htsmsg_add_msg(parent, name, c);
- return s2;
- } else if((c = htsmsg_json_parse_array(s, &s2)) != NULL) {
- htsmsg_add_msg(parent, name, c);
- return s2;
- } else if((str = htsmsg_json_parse_string(s, &s2)) != NULL) {
- htsmsg_add_str(parent, name, str);
- free(str);
- return s2;
- } else if((s2 = htsmsg_json_parse_number(s, &d)) != NULL) {
- htsmsg_add_s64(parent, name, d);
- return s2;
- }
-
- if(!strncmp(s, "true", 4)) {
- htsmsg_add_u32(parent, name, 1);
- return s + 4;
- }
-
- if(!strncmp(s, "false", 5)) {
- htsmsg_add_u32(parent, name, 0);
- return s + 5;
- }
-
- if(!strncmp(s, "null", 4)) {
- /* Don't add anything */
- return s + 4;
- }
- return NULL;
-}
+static const json_deserializer_t json_to_htsmsg = {
+ .jd_create_map = create_map,
+ .jd_create_list = create_list,
+ .jd_destroy_obj = destroy_obj,
+ .jd_add_obj = add_obj,
+ .jd_add_string = add_string,
+ .jd_add_long = add_long,
+ .jd_add_double = add_double,
+ .jd_add_bool = add_bool,
+ .jd_add_null = add_null,
+};
/**
htsmsg_t *
htsmsg_json_deserialize(const char *src)
{
- const char *end;
- htsmsg_t *c;
-
- if((c = htsmsg_json_parse_object(src, &end)) != NULL)
- return c;
-
- if((c = htsmsg_json_parse_array(src, &end)) != NULL) {
- c->hm_islist = 1;
- return c;
- }
- return NULL;
+ return json_deserialize(src, &json_to_htsmsg, NULL, NULL, 0);
}
/*
* Functions converting HTSMSGs to/from JSON
- * Copyright (C) 2008 Andreas Öman
+ * Copyright (C) 2008 Andreas Öman
*
* 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
#include "htsmsg.h"
#include "htsbuf.h"
-
+struct rstr;
/**
* htsmsg_binary_deserialize
*/
htsmsg_t *htsmsg_json_deserialize(const char *src);
-int htsmsg_json_serialize(htsmsg_t *msg, htsbuf_queue_t *hq, int pretty);
+void htsmsg_json_serialize(htsmsg_t *msg, htsbuf_queue_t *hq, int pretty);
+
+char *htsmsg_json_serialize_to_str(htsmsg_t *msg, int pretty);
+
+struct rstr *htsmsg_json_serialize_to_rstr(htsmsg_t *msg, const char *prefix);
#endif /* HTSMSG_JSON_H_ */
hf->hf_fd = fd;
hf->hf_id = ++htsp->htsp_file_id;
hf->hf_path = strdup(path);
- LIST_INSERT_HEAD(&htsp->htsp_files, hf, hf_link);
+ LIST_INSERT_HEAD(&htsp->htsp_files, hf, hf_link);
htsmsg_t *rep = htsmsg_create_map();
htsmsg_add_u32(rep, "id", hf->hf_id);
if(!fstat(hf->hf_fd, &st)) {
- htsmsg_add_u64(rep, "size", st.st_size);
- htsmsg_add_u64(rep, "mtime", st.st_mtime);
+ htsmsg_add_s64(rep, "size", st.st_size);
+ htsmsg_add_s64(rep, "mtime", st.st_mtime);
}
return rep;
if(htsmsg_get_u32(in, "eventId", &eventId))
return htsp_error("Missing argument 'eventId'");
- lang = htsmsg_get_str_or_default(in, "language", htsp->htsp_language);
+ lang = htsmsg_get_str(in, "language") ?: htsp->htsp_language;
if((e = epg_broadcast_find_by_id(eventId, NULL)) == NULL)
return htsp_error("Event does not exist");
maxTime
= htsmsg_get_s64_or_default(in, "maxTime", 0);
lang
- = htsmsg_get_str_or_default(in, "language", htsp->htsp_language);
+ = htsmsg_get_str(in, "language") ?: htsp->htsp_language;
/* Use event as starting point */
if (e || ch) {
genre.code = u32;
eg = &genre;
}
- lang = htsmsg_get_str_or_default(in, "language", htsp->htsp_language);
+ lang = htsmsg_get_str(in, "language") ?: htsp->htsp_language;
full = htsmsg_get_u32_or_default(in, "full", 0);
//do the query
return NULL;
}
+
/**
* Open file
*/
htsp_method_file_read(htsp_connection_t *htsp, htsmsg_t *in)
{
htsp_file_t *hf = htsp_file_find(htsp, in);
- uint64_t off;
- uint64_t size;
+ int64_t off;
+ int64_t size;
if(hf == NULL)
return htsp_error("Unknown file id");
- if(htsmsg_get_u64(in, "size", &size))
+ if(htsmsg_get_s64(in, "size", &size))
return htsp_error("Missing field 'size'");
/* Seek (optional) */
- if (!htsmsg_get_u64(in, "offset", &off))
+ if (!htsmsg_get_s64(in, "offset", &off))
if(lseek(hf->hf_fd, off, SEEK_SET) != off)
return htsp_error("Seek error");
htsmsg_t *rep = htsmsg_create_map();
if(!fstat(hf->hf_fd, &st)) {
- htsmsg_add_u64(rep, "size", st.st_size);
- htsmsg_add_u64(rep, "mtime", st.st_mtime);
+ htsmsg_add_s64(rep, "size", st.st_size);
+ htsmsg_add_s64(rep, "mtime", st.st_mtime);
}
return rep;
}
--- /dev/null
+/*
+ * Floating point conversion functions.
+ * Not accurate but should be enough for Showtime's needs
+ *
+ * Copyright (C) 2011 Andreas Öman
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define _ISOC99_SOURCE
+
+#include <math.h>
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "dbl.h"
+
+
+double
+my_str2double(const char *str, const char **endp)
+{
+ double ret = 1.0f;
+ int n = 0, m = 0, o = 0, e = 0;
+
+ if(*str == '-') {
+ ret = -1.0f;
+ str++;
+ }
+
+ while(*str >= '0' && *str <= '9')
+ n = n * 10 + *str++ - '0';
+
+ if(*str != '.') {
+ ret *= n;
+ } else {
+
+ str++;
+
+ while(*str >= '0' && *str <= '9') {
+ o = o * 10 + *str++ - '0';
+ m--;
+ }
+
+ ret *= (n + pow(10, m) * o);
+ }
+
+ if(*str == 'e' || *str == 'E') {
+ int esign = 1;
+ str++;
+
+ if(*str == '+')
+ str++;
+ else if(*str == '-') {
+ str++;
+ esign = -1;
+ }
+
+ while(*str >= '0' && *str <= '9')
+ e = e * 10 + *str++ - '0';
+ ret *= pow(10, e * esign);
+ }
+
+ if(endp != NULL)
+ *endp = str;
+
+ return ret;
+}
+
+
+
+
+/*
+** The code that follow is based on "printf" code that dates from the
+** 1980s. It is in the public domain. The original comments are
+** included here for completeness. They are very out-of-date but
+** might be useful as an historical reference.
+**
+**************************************************************************
+**
+** The following modules is an enhanced replacement for the "printf" subroutines
+** found in the standard C library. The following enhancements are
+** supported:
+**
+** + Additional functions. The standard set of "printf" functions
+** includes printf, fprintf, sprintf, vprintf, vfprintf, and
+** vsprintf. This module adds the following:
+**
+** * snprintf -- Works like sprintf, but has an extra argument
+** which is the size of the buffer written to.
+**
+** * mprintf -- Similar to sprintf. Writes output to memory
+** obtained from malloc.
+**
+** * xprintf -- Calls a function to dispose of output.
+**
+** * nprintf -- No output, but returns the number of characters
+** that would have been output by printf.
+**
+** * A v- version (ex: vsnprintf) of every function is also
+** supplied.
+**
+** + A few extensions to the formatting notation are supported:
+**
+** * The "=" flag (similar to "-") causes the output to be
+** be centered in the appropriately sized field.
+**
+** * The %b field outputs an integer in binary notation.
+**
+** * The %c field now accepts a precision. The character output
+** is repeated by the number of times the precision specifies.
+**
+** * The %' field works like %c, but takes as its character the
+** next character of the format string, instead of the next
+** argument. For example, printf("%.78'-") prints 78 minus
+** signs, the same as printf("%.78c",'-').
+**
+** + When compiled using GCC on a SPARC, this version of printf is
+** faster than the library printf for SUN OS 4.1.
+**
+** + All functions are fully reentrant.
+**
+*/
+
+
+static char
+getdigit(double *val, int *cnt)
+{
+ int digit;
+ double d;
+ if( (*cnt)++ >= 16 ) return '0';
+ digit = (int)*val;
+ d = digit;
+ digit += '0';
+ *val = (*val - d)*10.0;
+ return (char)digit;
+}
+
+#define xGENERIC 0
+#define xFLOAT 1
+#define xEXP 2
+
+
+int
+my_double2str(char *buf, size_t bufsize, double realvalue)
+{
+ int precision = -1;
+ char *bufpt;
+ char prefix;
+ char xtype = xGENERIC;
+ int idx, exp, e2;
+ double rounder;
+ char flag_exp;
+ char flag_rtz;
+ char flag_dp;
+ char flag_alternateform = 0;
+ char flag_altform2 = 0;
+ int nsd;
+
+ if(bufsize < 8)
+ return -1;
+
+ if( precision<0 ) precision = 20; /* Set default precision */
+ if( precision>bufsize/2-10 ) precision = bufsize/2-10;
+ if( realvalue<0.0 ){
+ realvalue = -realvalue;
+ prefix = '-';
+ }else{
+ prefix = 0;
+ }
+ if( xtype==xGENERIC && precision>0 ) precision--;
+ for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1){}
+
+ if( xtype==xFLOAT ) realvalue += rounder;
+ /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
+ exp = 0;
+
+ if(isnan(realvalue)) {
+ strcpy(buf, "NaN");
+ return 0;
+ }
+
+ if( realvalue>0.0 ){
+ while( realvalue>=1e32 && exp<=350 ){ realvalue *= 1e-32; exp+=32; }
+ while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
+ while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
+ while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; }
+ while( realvalue<1.0 ){ realvalue *= 10.0; exp--; }
+ if( exp>350 ){
+ if( prefix=='-' ){
+ strcpy(buf, "-Inf");
+ }else{
+ strcpy(buf, "Inf");
+ }
+ return 0;
+ }
+ }
+ bufpt = buf;
+
+ /*
+ ** If the field type is etGENERIC, then convert to either etEXP
+ ** or etFLOAT, as appropriate.
+ */
+ flag_exp = xtype==xEXP;
+ if( xtype != xFLOAT ){
+ realvalue += rounder;
+ if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
+ }
+ if( xtype==xGENERIC ){
+ flag_rtz = !flag_alternateform;
+ if( exp<-4 || exp>precision ){
+ xtype = xEXP;
+ }else{
+ precision = precision - exp;
+ xtype = xFLOAT;
+ }
+ }else{
+ flag_rtz = 0;
+ }
+ if( xtype==xEXP ){
+ e2 = 0;
+ }else{
+ e2 = exp;
+ }
+ nsd = 0;
+ flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2;
+ /* The sign in front of the number */
+ if( prefix ){
+ *(bufpt++) = prefix;
+ }
+ /* Digits prior to the decimal point */
+ if( e2<0 ){
+ *(bufpt++) = '0';
+ }else{
+ for(; e2>=0; e2--){
+ *(bufpt++) = getdigit(&realvalue,&nsd);
+ }
+ }
+ /* The decimal point */
+ if( flag_dp ){
+ *(bufpt++) = '.';
+ }
+ /* "0" digits after the decimal point but before the first
+ ** significant digit of the number */
+ for(e2++; e2<0; precision--, e2++){
+ assert( precision>0 );
+ *(bufpt++) = '0';
+ }
+ /* Significant digits after the decimal point */
+ while( (precision--)>0 ){
+ *(bufpt++) = getdigit(&realvalue,&nsd);
+ }
+
+ /* Remove trailing zeros and the "." if no digits follow the "." */
+ if( flag_rtz && flag_dp ){
+ while( bufpt[-1]=='0' ) *(--bufpt) = 0;
+ assert( bufpt>buf );
+ if( bufpt[-1]=='.' ){
+ if( flag_altform2 ){
+ *(bufpt++) = '0';
+ }else{
+ *(--bufpt) = 0;
+ }
+ }
+ }
+ /* Add the "eNNN" suffix */
+ if( flag_exp || xtype==xEXP ){
+ *(bufpt++) = 'e';
+ if( exp<0 ){
+ *(bufpt++) = '-'; exp = -exp;
+ }else{
+ *(bufpt++) = '+';
+ }
+ if( exp>=100 ){
+ *(bufpt++) = (char)((exp/100)+'0'); /* 100's digit */
+ exp %= 100;
+ }
+ *(bufpt++) = (char)(exp/10+'0'); /* 10's digit */
+ *(bufpt++) = (char)(exp%10+'0'); /* 1's digit */
+ }
+ *bufpt = 0;
+ return 0;
+}
+
--- /dev/null
+#pragma once
+
+double my_str2double(const char *str, const char **endp);
+
+int my_double2str(char *buf, size_t bufsize, double realvalue);
--- /dev/null
+/*
+ * JSON helpers
+ * Copyright (C) 2011 Andreas Öman
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "json.h"
+#include "dbl.h"
+#include "tvheadend.h"
+
+#define NOT_THIS_TYPE ((void *)-1)
+
+static const char *json_parse_value(const char *s, void *parent,
+ const char *name,
+ const json_deserializer_t *jd,
+ void *opaque,
+ const char **failp, const char **failmsg);
+
+/**
+ * Returns a newly allocated string
+ */
+static char *
+json_parse_string(const char *s, const char **endp,
+ const char **failp, const char **failmsg)
+{
+ const char *start;
+ char *r, *a, *b;
+ int l, esc = 0;
+
+ while(*s > 0 && *s < 33)
+ s++;
+
+ if(*s != '"')
+ return NOT_THIS_TYPE;
+
+ s++;
+ start = s;
+
+ while(1) {
+ if(*s == 0) {
+ *failmsg = "Unexpected end of JSON message";
+ *failp = s;
+ return NULL;
+ }
+
+ if(*s == '\\') {
+ esc = 1;
+ } else if(*s == '"' && s[-1] != '\\') {
+
+ *endp = s + 1;
+
+ /* End */
+ l = s - start;
+ r = malloc(l + 1);
+ memcpy(r, start, l);
+ r[l] = 0;
+
+ if(esc) {
+ /* Do deescaping inplace */
+
+ a = b = r;
+
+ while(*a) {
+ if(*a == '\\') {
+ a++;
+ if(*a == 'b')
+ *b++ = '\b';
+ else if(*a == 'f')
+ *b++ = '\f';
+ else if(*a == 'n')
+ *b++ = '\n';
+ else if(*a == 'r')
+ *b++ = '\r';
+ else if(*a == 't')
+ *b++ = '\t';
+ else if(*a == 'u') {
+ // Unicode character
+ int i, v = 0;
+
+ a++;
+ for(i = 0; i < 4; i++) {
+ v = v << 4;
+ switch(a[i]) {
+ case '0' ... '9':
+ v |= a[i] - '0';
+ break;
+ case 'a' ... 'f':
+ v |= a[i] - 'a' + 10;
+ break;
+ case 'A' ... 'F':
+ v |= a[i] - 'F' + 10;
+ break;
+ default:
+ free(r);
+ *failmsg = "Incorrect escape sequence";
+ *failp = (a - r) + start;
+ return NULL;
+ }
+ }
+ a+=3;
+ b += put_utf8(b, v);
+ } else {
+ *b++ = *a;
+ }
+ a++;
+ } else {
+ *b++ = *a++;
+ }
+ }
+ *b = 0;
+ }
+ return r;
+ }
+ s++;
+ }
+}
+
+
+/**
+ *
+ */
+static void *
+json_parse_map(const char *s, const char **endp, const json_deserializer_t *jd,
+ void *opaque, const char **failp, const char **failmsg)
+
+{
+ char *name;
+ const char *s2;
+ void *r;
+
+ while(*s > 0 && *s < 33)
+ s++;
+
+ if(*s != '{')
+ return NOT_THIS_TYPE;
+
+ s++;
+
+ r = jd->jd_create_map(opaque);
+
+ while(*s > 0 && *s < 33)
+ s++;
+
+ if(*s != '}') {
+
+ while(1) {
+ name = json_parse_string(s, &s2, failp, failmsg);
+ if(name == NOT_THIS_TYPE) {
+ *failmsg = "Expected string";
+ *failp = s;
+ return NULL;
+ }
+
+ if(name == NULL)
+ return NULL;
+
+ s = s2;
+
+ while(*s > 0 && *s < 33)
+ s++;
+
+ if(*s != ':') {
+ jd->jd_destroy_obj(opaque, r);
+ free(name);
+ *failmsg = "Expected ':'";
+ *failp = s;
+ return NULL;
+ }
+ s++;
+
+ s2 = json_parse_value(s, r, name, jd, opaque, failp, failmsg);
+ free(name);
+
+ if(s2 == NULL) {
+ jd->jd_destroy_obj(opaque, r);
+ return NULL;
+ }
+
+ s = s2;
+
+ while(*s > 0 && *s < 33)
+ s++;
+
+ if(*s == '}')
+ break;
+
+ if(*s != ',') {
+ jd->jd_destroy_obj(opaque, r);
+ *failmsg = "Expected ','";
+ *failp = s;
+ return NULL;
+ }
+ s++;
+ }
+ }
+
+ s++;
+ *endp = s;
+ return r;
+}
+
+
+/**
+ *
+ */
+static void *
+json_parse_list(const char *s, const char **endp, const json_deserializer_t *jd,
+ void *opaque, const char **failp, const char **failmsg)
+{
+ const char *s2;
+ void *r;
+
+ while(*s > 0 && *s < 33)
+ s++;
+
+ if(*s != '[')
+ return NOT_THIS_TYPE;
+
+ s++;
+
+ r = jd->jd_create_list(opaque);
+
+ while(*s > 0 && *s < 33)
+ s++;
+
+ if(*s != ']') {
+
+ while(1) {
+
+ s2 = json_parse_value(s, r, NULL, jd, opaque, failp, failmsg);
+
+ if(s2 == NULL) {
+ jd->jd_destroy_obj(opaque, r);
+ return NULL;
+ }
+
+ s = s2;
+
+ while(*s > 0 && *s < 33)
+ s++;
+
+ if(*s == ']')
+ break;
+
+ if(*s != ',') {
+ jd->jd_destroy_obj(opaque, r);
+ *failmsg = "Expected ','";
+ *failp = s;
+ return NULL;
+ }
+ s++;
+ }
+ }
+ s++;
+ *endp = s;
+ return r;
+}
+
+/**
+ *
+ */
+static const char *
+json_parse_double(const char *s, double *dp)
+{
+ const char *ep;
+ while(*s > 0 && *s < 33)
+ s++;
+
+ double d = my_str2double(s, &ep);
+
+ if(ep == s)
+ return NULL;
+
+ *dp = d;
+ return ep;
+}
+
+
+/**
+ *
+ */
+static char *
+json_parse_integer(const char *s, long *lp)
+{
+ char *ep;
+ while(*s > 0 && *s < 33)
+ s++;
+ const char *s2 = s;
+ if(*s2 == '-')
+ s2++;
+ while(*s2 >= '0' && *s2 <= '9')
+ s2++;
+
+ if(*s2 == 0)
+ return NULL;
+ if(s2[0] == '.' || s2[0] == 'e' || s2[0] == 'E')
+ return NULL; // Is floating point
+
+ long v = strtol(s, &ep, 10);
+ if(v == LONG_MIN || v == LONG_MAX)
+ return NULL;
+
+ if(ep == s)
+ return NULL;
+
+ *lp = v;
+ return ep;
+}
+
+/**
+ *
+ */
+static const char *
+json_parse_value(const char *s, void *parent, const char *name,
+ const json_deserializer_t *jd, void *opaque,
+ const char **failp, const char **failmsg)
+{
+ const char *s2;
+ char *str;
+ double d = 0;
+ long l = 0;
+ void *c;
+
+ if((c = json_parse_map(s, &s2, jd, opaque, failp, failmsg)) == NULL)
+ return NULL;
+
+ if(c != NOT_THIS_TYPE) {
+ jd->jd_add_obj(opaque, parent, name, c);
+ return s2;
+ }
+
+ if((c = json_parse_list(s, &s2, jd, opaque, failp, failmsg)) == NULL)
+ return NULL;
+
+ if(c != NOT_THIS_TYPE) {
+ jd->jd_add_obj(opaque, parent, name, c);
+ return s2;
+ }
+
+ if((str = json_parse_string(s, &s2, failp, failmsg)) == NULL)
+ return NULL;
+
+ if(str != NOT_THIS_TYPE) {
+ jd->jd_add_string(opaque, parent, name, str);
+ return s2;
+ }
+
+ if((s2 = json_parse_integer(s, &l)) != NULL) {
+ jd->jd_add_long(opaque, parent, name, l);
+ return s2;
+ } else if((s2 = json_parse_double(s, &d)) != NULL) {
+ jd->jd_add_double(opaque, parent, name, d);
+ return s2;
+ }
+
+ while(*s > 0 && *s < 33)
+ s++;
+
+ if(!strncmp(s, "true", 4)) {
+ jd->jd_add_bool(opaque, parent, name, 1);
+ return s + 4;
+ }
+
+ if(!strncmp(s, "false", 5)) {
+ jd->jd_add_bool(opaque, parent, name, 0);
+ return s + 5;
+ }
+
+ if(!strncmp(s, "null", 4)) {
+ jd->jd_add_null(opaque, parent, name);
+ return s + 4;
+ }
+
+ *failmsg = "Unknown token";
+ *failp = s;
+ return NULL;
+}
+
+
+/**
+ *
+ */
+void *
+json_deserialize(const char *src, const json_deserializer_t *jd, void *opaque,
+ char *errbuf, size_t errlen)
+{
+ const char *end;
+ void *c;
+ const char *errmsg;
+ const char *errp;
+
+ c = json_parse_map(src, &end, jd, opaque, &errp, &errmsg);
+ if(c == NOT_THIS_TYPE)
+ c = json_parse_list(src, &end, jd, opaque, &errp, &errmsg);
+
+ if(c == NOT_THIS_TYPE) {
+ snprintf(errbuf, errlen, "Invalid JSON, expected '{' or '['");
+ return NULL;
+ }
+
+ if(c == NULL) {
+ size_t len = strlen(src);
+ ssize_t offset = errp - src;
+ if(offset > len || offset < 0) {
+ snprintf(errbuf, errlen, "%s at (bad) offset %d", errmsg, (int)offset);
+ } else {
+ offset -= 10;
+ if(offset < 0)
+ offset = 0;
+ snprintf(errbuf, errlen, "%s at offset %d : '%.20s'", errmsg, (int)offset,
+ src + offset);
+ }
+ }
+ return c;
+}
--- /dev/null
+#pragma once
+
+typedef struct json_deserializer {
+ void *(*jd_create_map)(void *jd_opaque);
+ void *(*jd_create_list)(void *jd_opaque);
+
+ void (*jd_destroy_obj)(void *jd_opaque, void *obj);
+
+ void (*jd_add_obj)(void *jd_opaque, void *parent,
+ const char *name, void *child);
+
+ // str must be free'd by callee
+ void (*jd_add_string)(void *jd_opaque, void *parent,
+ const char *name, char *str);
+
+ void (*jd_add_long)(void *jd_opaque, void *parent,
+ const char *name, long v);
+
+ void (*jd_add_double)(void *jd_opaque, void *parent,
+ const char *name, double d);
+
+ void (*jd_add_bool)(void *jd_opaque, void *parent,
+ const char *name, int v);
+
+ void (*jd_add_null)(void *jd_opaque, void *parent,
+ const char *name);
+
+} json_deserializer_t;
+
+void *json_deserialize(const char *src, const json_deserializer_t *jd,
+ void *opaque, char *errbuf, size_t errlen);