Upgrade htsmsg code from Showtime's codebase
We do this to get access to floating point in JSON
This commit is contained in:
parent
5a07328a9a
commit
01db78aea0
12 changed files with 1255 additions and 498 deletions
2
Makefile
2
Makefile
|
@ -94,6 +94,8 @@ SRCS = src/main.c \
|
|||
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 \
|
||||
|
|
182
src/htsbuf.c
182
src/htsbuf.c
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* 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
|
||||
|
@ -22,17 +22,9 @@
|
|||
#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 "htsbuf.h"
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -206,6 +198,7 @@ htsbuf_peek(htsbuf_queue_t *hq, void *buf, size_t len)
|
|||
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;
|
||||
|
||||
|
@ -233,7 +226,7 @@ htsbuf_drop(htsbuf_queue_t *hq, size_t len)
|
|||
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);
|
||||
}
|
||||
|
@ -314,19 +307,162 @@ htsbuf_appendq(htsbuf_queue_t *hq, htsbuf_queue_t *src)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
uint32_t
|
||||
htsbuf_crc32(htsbuf_queue_t *hq, uint32_t crc)
|
||||
void
|
||||
htsbuf_dump_raw_stderr(htsbuf_queue_t *hq)
|
||||
{
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
htsbuf_append_and_escape_xml(htsbuf_queue_t *hq, const char *s)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
29
src/htsbuf.h
29
src/htsbuf.h
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* 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
|
||||
|
@ -20,10 +20,11 @@
|
|||
#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 {
|
||||
|
@ -48,15 +49,12 @@ void htsbuf_queue_flush(htsbuf_queue_t *hq);
|
|||
|
||||
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);
|
||||
|
@ -67,6 +65,21 @@ size_t htsbuf_drop(htsbuf_queue_t *hq, 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__ */
|
||||
|
|
174
src/htsmsg.c
174
src/htsmsg.c
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* 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
|
||||
|
@ -27,10 +27,10 @@
|
|||
|
||||
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);
|
||||
|
@ -102,7 +102,7 @@ htsmsg_field_add(htsmsg_t *msg, const char *name, int type, int flags)
|
|||
/*
|
||||
*
|
||||
*/
|
||||
static htsmsg_field_t *
|
||||
htsmsg_field_t *
|
||||
htsmsg_field_find(htsmsg_t *msg, const char *name)
|
||||
{
|
||||
htsmsg_field_t *f;
|
||||
|
@ -196,16 +196,6 @@ htsmsg_add_s64(htsmsg_t *msg, const char *name, int64_t s64)
|
|||
f->hmf_s64 = s64;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void
|
||||
htsmsg_add_u64(htsmsg_t *msg, const char *name, uint64_t u64)
|
||||
{
|
||||
htsmsg_field_t *f = htsmsg_field_add(msg, name, HMF_S64, HMF_NAME_ALLOCED);
|
||||
f->hmf_s64 = u64;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
@ -217,6 +207,17 @@ htsmsg_add_s32(htsmsg_t *msg, const char *name, int32_t s32)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void
|
||||
htsmsg_add_dbl(htsmsg_t *msg, const char *name, double dbl)
|
||||
{
|
||||
htsmsg_field_t *f = htsmsg_field_add(msg, name, HMF_DBL, HMF_NAME_ALLOCED);
|
||||
f->hmf_dbl = dbl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
|
@ -267,8 +268,8 @@ htsmsg_add_msg(htsmsg_t *msg, const char *name, htsmsg_t *sub)
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -312,10 +313,14 @@ htsmsg_get_s64(htsmsg_t *msg, const char *name, int64_t *s64p)
|
|||
case HMF_S64:
|
||||
*s64p = f->hmf_s64;
|
||||
break;
|
||||
case HMF_DBL:
|
||||
*s64p = f->hmf_dbl;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -326,30 +331,6 @@ htsmsg_get_s64_or_default(htsmsg_t *msg, const char *name, int64_t def)
|
|||
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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
|
@ -373,13 +354,26 @@ htsmsg_get_u32(htsmsg_t *msg, const char *name, uint32_t *u32p)
|
|||
/**
|
||||
*
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
@ -400,6 +394,25 @@ htsmsg_get_s32(htsmsg_t *msg, const char *name, int32_t *s32p)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
@ -457,13 +470,6 @@ htsmsg_get_str(htsmsg_t *msg, const char *name)
|
|||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
@ -493,6 +499,32 @@ htsmsg_get_map_multi(htsmsg_t *msg, ...)
|
|||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
@ -565,6 +597,10 @@ htsmsg_print0(htsmsg_t *msg, int indent)
|
|||
case HMF_S64:
|
||||
printf("S64) = %" PRId64 "\n", f->hmf_s64);
|
||||
break;
|
||||
|
||||
case HMF_DBL:
|
||||
printf("DBL) = %f\n", f->hmf_dbl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -611,6 +647,10 @@ htsmsg_copy_i(htsmsg_t *src, htsmsg_t *dst)
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -623,6 +663,45 @@ htsmsg_copy(htsmsg_t *src)
|
|||
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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -633,3 +712,4 @@ htsmsg_dtor(htsmsg_t **mp)
|
|||
if(*mp != NULL)
|
||||
htsmsg_destroy(*mp);
|
||||
}
|
||||
|
||||
|
|
114
src/htsmsg.h
114
src/htsmsg.h
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* 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
|
||||
|
@ -16,9 +16,8 @@
|
|||
* 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"
|
||||
|
||||
|
@ -50,6 +49,7 @@ typedef struct htsmsg {
|
|||
#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;
|
||||
|
@ -68,6 +68,7 @@ typedef struct htsmsg_field {
|
|||
size_t len;
|
||||
} bin;
|
||||
htsmsg_t msg;
|
||||
double dbl;
|
||||
} u;
|
||||
} htsmsg_field_t;
|
||||
|
||||
|
@ -76,11 +77,12 @@ typedef struct htsmsg_field {
|
|||
#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)
|
||||
|
||||
|
@ -94,6 +96,11 @@ htsmsg_t *htsmsg_create_map(void);
|
|||
*/
|
||||
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)
|
||||
*/
|
||||
|
@ -109,11 +116,6 @@ void htsmsg_add_u32(htsmsg_t *msg, const char *name, uint32_t u32);
|
|||
*/
|
||||
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.
|
||||
*/
|
||||
|
@ -129,6 +131,11 @@ void htsmsg_add_str(htsmsg_t *msg, const char *name, const char *str);
|
|||
*/
|
||||
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.
|
||||
*
|
||||
|
@ -160,15 +167,6 @@ void htsmsg_add_binptr(htsmsg_t *msg, const char *name, const void *bin,
|
|||
*/
|
||||
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.
|
||||
*
|
||||
|
@ -187,24 +185,11 @@ int htsmsg_get_s32(htsmsg_t *msg, const char *name, int32_t *s32p);
|
|||
*/
|
||||
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.
|
||||
|
@ -236,15 +221,6 @@ htsmsg_t *htsmsg_get_list(htsmsg_t *msg, const char *name);
|
|||
*/
|
||||
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.
|
||||
*
|
||||
|
@ -256,7 +232,23 @@ htsmsg_t *htsmsg_get_map(htsmsg_t *msg, const char *name);
|
|||
/**
|
||||
* 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
|
||||
|
@ -264,6 +256,23 @@ htsmsg_t *htsmsg_get_map_multi(htsmsg_t *msg, ...);
|
|||
*/
|
||||
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.
|
||||
*/
|
||||
|
@ -288,6 +297,12 @@ void htsmsg_print(htsmsg_t *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.
|
||||
*/
|
||||
|
@ -296,8 +311,15 @@ htsmsg_t *htsmsg_copy(htsmsg_t *src);
|
|||
#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_ */
|
||||
|
|
|
@ -25,54 +25,19 @@
|
|||
|
||||
#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);
|
||||
|
@ -83,7 +48,7 @@ htsmsg_json_write(htsmsg_t *msg, htsbuf_queue_t *hq, int isarray,
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -97,11 +62,11 @@ htsmsg_json_write(htsmsg_t *msg, htsbuf_queue_t *hq, int isarray,
|
|||
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:
|
||||
|
@ -109,6 +74,11 @@ htsmsg_json_write(htsmsg_t *msg, htsbuf_queue_t *hq, int isarray,
|
|||
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();
|
||||
}
|
||||
|
@ -125,343 +95,109 @@ htsmsg_json_write(htsmsg_t *msg, htsbuf_queue_t *hq, int isarray,
|
|||
/**
|
||||
*
|
||||
*/
|
||||
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)
|
||||
{
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static htsmsg_t *
|
||||
htsmsg_json_parse_object(const char *s, const char **endp)
|
||||
char *
|
||||
htsmsg_json_serialize_to_str(htsmsg_t *msg, int pretty)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static htsmsg_t *
|
||||
htsmsg_json_parse_array(const char *s, const char **endp)
|
||||
{
|
||||
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;
|
||||
|
||||
while(*s > 0 && *s < 33)
|
||||
s++;
|
||||
|
||||
if(*s == ']')
|
||||
break;
|
||||
|
||||
if(*s != ',') {
|
||||
htsmsg_destroy(r);
|
||||
return NULL;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
}
|
||||
s++;
|
||||
*endp = s;
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 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;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static char *
|
||||
htsmsg_json_parse_number(const char *s, double *dp)
|
||||
{
|
||||
char *ep;
|
||||
double d = _strntod(s, '.', &ep);
|
||||
|
||||
if(ep == s)
|
||||
return NULL;
|
||||
|
||||
*dp = d;
|
||||
return ep;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static const char *
|
||||
htsmsg_json_parse_value(const char *s, htsmsg_t *parent, char *name)
|
||||
{
|
||||
const char *s2;
|
||||
htsbuf_queue_t hq;
|
||||
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;
|
||||
htsbuf_queue_init(&hq, 0);
|
||||
htsmsg_json_serialize(msg, &hq, pretty);
|
||||
str = htsbuf_to_string(&hq);
|
||||
htsbuf_queue_flush(&hq);
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void *
|
||||
create_map(void *opaque)
|
||||
{
|
||||
return htsmsg_create_map();
|
||||
}
|
||||
|
||||
static void *
|
||||
create_list(void *opaque)
|
||||
{
|
||||
return htsmsg_create_list();
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_obj(void *opaque, void *obj)
|
||||
{
|
||||
return htsmsg_destroy(obj);
|
||||
}
|
||||
|
||||
static void
|
||||
add_obj(void *opaque, void *parent, const char *name, void *child)
|
||||
{
|
||||
htsmsg_add_msg(parent, name, child);
|
||||
}
|
||||
|
||||
static void
|
||||
add_string(void *opaque, void *parent, const char *name, char *str)
|
||||
{
|
||||
htsmsg_add_str(parent, name, str);
|
||||
free(str);
|
||||
}
|
||||
|
||||
static void
|
||||
add_long(void *opaque, void *parent, const char *name, long v)
|
||||
{
|
||||
htsmsg_add_s64(parent, name, v);
|
||||
}
|
||||
|
||||
static void
|
||||
add_double(void *opaque, void *parent, const char *name, double v)
|
||||
{
|
||||
htsmsg_add_dbl(parent, name, v);
|
||||
}
|
||||
|
||||
static void
|
||||
add_bool(void *opaque, void *parent, const char *name, int v)
|
||||
{
|
||||
htsmsg_add_u32(parent, name, v);
|
||||
}
|
||||
|
||||
static void
|
||||
add_null(void *opaque, void *parent, const char *name)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* 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
|
||||
|
@ -21,12 +21,16 @@
|
|||
|
||||
#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_ */
|
||||
|
|
|
@ -381,14 +381,14 @@ htsp_file_open(htsp_connection_t *htsp, const char *path)
|
|||
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;
|
||||
|
@ -837,7 +837,7 @@ htsp_method_getEvent(htsp_connection_t *htsp, htsmsg_t *in)
|
|||
|
||||
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");
|
||||
|
@ -871,7 +871,7 @@ htsp_method_getEvents(htsp_connection_t *htsp, htsmsg_t *in)
|
|||
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) {
|
||||
|
@ -940,7 +940,7 @@ htsp_method_epgQuery(htsp_connection_t *htsp, htsmsg_t *in)
|
|||
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
|
||||
|
@ -1324,6 +1324,7 @@ htsp_method_change_weight(htsp_connection_t *htsp, htsmsg_t *in)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Open file
|
||||
*/
|
||||
|
@ -1358,17 +1359,17 @@ static htsmsg_t *
|
|||
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");
|
||||
|
||||
|
@ -1418,8 +1419,8 @@ htsp_method_file_stat(htsp_connection_t *htsp, htsmsg_t *in)
|
|||
|
||||
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;
|
||||
}
|
||||
|
|
295
src/misc/dbl.c
Normal file
295
src/misc/dbl.c
Normal file
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
5
src/misc/dbl.h
Normal file
5
src/misc/dbl.h
Normal file
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
double my_str2double(const char *str, const char **endp);
|
||||
|
||||
int my_double2str(char *buf, size_t bufsize, double realvalue);
|
432
src/misc/json.c
Normal file
432
src/misc/json.c
Normal file
|
@ -0,0 +1,432 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
31
src/misc/json.h
Normal file
31
src/misc/json.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#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);
|
Loading…
Add table
Reference in a new issue