]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
Upgrade htsmsg code from Showtime's codebase
authorAndreas Öman <andreas@lonelycoder.com>
Mon, 19 Nov 2012 12:16:58 +0000 (13:16 +0100)
committerAndreas Öman <andreas@lonelycoder.com>
Mon, 19 Nov 2012 13:50:16 +0000 (14:50 +0100)
We do this to get access to floating point in JSON

12 files changed:
Makefile
src/htsbuf.c
src/htsbuf.h
src/htsmsg.c
src/htsmsg.h
src/htsmsg_json.c
src/htsmsg_json.h
src/htsp_server.c
src/misc/dbl.c [new file with mode: 0644]
src/misc/dbl.h [new file with mode: 0644]
src/misc/json.c [new file with mode: 0644]
src/misc/json.h [new file with mode: 0644]

index b1ed4b52466fc0aa050296164df09a9a1cb82236..01f372df2533389c16cb4073882c8063e7895155 100644 (file)
--- a/Makefile
+++ b/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 \
index ff859c18f84fac7a8639bfd9e24d49f40ad8d299..03f49a3a1ef6cb6a9e497d9dbcb747c82483a732 100644 (file)
@@ -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
 #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"
 
 /**
  *
@@ -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)
 }
 
 
+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 = "&lt;";   break;
+    case '>':  esc = "&gt;";   break;
+    case '&':  esc = "&amp;";  break;
+    case '\'': esc = "&apos;"; break;
+    case '"':  esc = "&quot;"; 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;
+}
+
index 13aaedb5448f6e5393c3f4b20dd8fc221badd119..ae9a1322b135d334ae3aa5569e6b99430f43ef2c 100644 (file)
@@ -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
 #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__ */
index 5dd67e3a30e8f20b3ad633df452fb038f0c7055d..935c30b65a41cd8eb2c8b2a003d26afb38ddc625 100644 (file)
@@ -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
 
 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;
@@ -197,23 +197,24 @@ htsmsg_add_s64(htsmsg_t *msg, const char *name, int64_t s64)
 }
 
 /*
- * 
+ *
  */
 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;
 }
 
 
@@ -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);
 }
+
index d7d2bb05e0301de02c810ba1fcdf87c77bc450eb..82fb7fda84af554c1181839b70ec4ec2617ea9a1 100644 (file)
@@ -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_ */
index ff1aa6f7bcaff432d70b640b3df5b0427bc87e59..3fda47c1ed8963769c4f165fa3450688a82ae7a9 100644 (file)
 
 #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,326 +95,102 @@ 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)
+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,
+};
 
 
 /**
@@ -453,15 +199,5 @@ htsmsg_json_parse_value(const char *s, htsmsg_t *parent, char *name)
 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);
 }
index da6b250481c07619b85693ffbcf67c2516de03f1..fae3c45719ae83007e71e77891381059028c7733 100644 (file)
@@ -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
 
 #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_ */
index 05ff068ff8feaa693f5d178ab36746b783bf530d..d13b59deec0054c5bf3c87dcd1fe249a1b364bb7 100644 (file)
@@ -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;
 }
diff --git a/src/misc/dbl.c b/src/misc/dbl.c
new file mode 100644 (file)
index 0000000..05e5a6d
--- /dev/null
@@ -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;
+}
+
diff --git a/src/misc/dbl.h b/src/misc/dbl.h
new file mode 100644 (file)
index 0000000..fc05259
--- /dev/null
@@ -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);
diff --git a/src/misc/json.c b/src/misc/json.c
new file mode 100644 (file)
index 0000000..59f40eb
--- /dev/null
@@ -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;
+}
diff --git a/src/misc/json.h b/src/misc/json.h
new file mode 100644 (file)
index 0000000..8fc1bf2
--- /dev/null
@@ -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);