Merge remote-tracking branch 'cabo/master'
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..f1c9628
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,14 @@
+language: c
+compiler:
+- clang
+- gcc
+sudo: false
+before_install:
+- wget http://www.cmake.org/files/v3.3/cmake-3.3.1-Linux-x86_64.tar.gz  -O /tmp/cmake.tar.gz
+- tar xzf /tmp/cmake.tar.gz
+- export PATH=$PWD/cmake-3.3.1-Linux-x86_64/bin/:$PATH
+script:
+- "./build.sh all test"
+notifications:
+  slack:
+    secure: WdgYxQrnFR5eu/eKygPuLjlFsuZxD9m2PLRWTLT85aj+18Gp2ooPjnI9UFdb1xY87+4InhWk6PvQU35j4bG0etPQtX+0H4T4Zdk/aD6KxgJBHIYGqtfZUMmdFfVpUH9cCPx99Jjw81mhKrxM+6rXiZdiWXuNhvbJOApRT6uxE2k=
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 67b9d95..195c780 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -23,6 +23,7 @@
 option ( coveralls      "Generate coveralls data" ON )
 option ( coveralls_send "Send data to coveralls site" OFF )
 option ( build_docs "Create docs using Doxygen" ${DOXYGEN_FOUND} )
+option ( no_floats "Build without floating point support" OFF )
 
 set ( dist_dir    ${CMAKE_BINARY_DIR}/dist )
 set ( prefix      ${CMAKE_INSTALL_PREFIX} )
@@ -70,6 +71,10 @@
   message ( FATAL_ERROR "unhandled compiler id: ${CMAKE_C_COMPILER_ID}" )
 endif ()
 
+if ( no_floats )
+   add_definitions(-DCBOR_NO_FLOAT)
+endif()
+
 if ( verbose )
   set ( CMAKE_VERBOSE_MAKEFILE ON )
 endif ()
diff --git a/README.md b/README.md
index 9b57776..5820858 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+[![Build Status](https://travis-ci.org/cabo/cn-cbor.png?branch=master)](https://travis-ci.org/cabo/cn-cbor)
+
 # cn-cbor: A constrained node implementation of CBOR in C
 
 This is a constrained node implementation of [CBOR](http://cbor.io) in
diff --git a/build.sh b/build.sh
index a07f37f..69dd2e9 100755
--- a/build.sh
+++ b/build.sh
@@ -1,3 +1,4 @@
+#!/bin/sh
 if [ ! -d "build" ]; then
     mkdir build
 fi
diff --git a/include/cn-cbor/cn-cbor.h b/include/cn-cbor/cn-cbor.h
index 553aa55..2973581 100644
--- a/include/cn-cbor/cn-cbor.h
+++ b/include/cn-cbor/cn-cbor.h
@@ -131,7 +131,10 @@
   /** An invalid parameter was passed to a function */
   CN_CBOR_ERR_INVALID_PARAMETER,
   /** Allocation failed */
-  CN_CBOR_ERR_OUT_OF_MEMORY
+  CN_CBOR_ERR_OUT_OF_MEMORY,
+  /** A float was encountered during parse but the library was built without
+      support for float types. */
+  CN_CBOR_ERR_FLOAT_NOT_SUPPORTED
 } cn_cbor_error;
 
 /**
@@ -213,7 +216,7 @@
  * @param[out] errp         Error, if NULL is returned
  * @return                  The parsed CBOR structure, or NULL on error
  */
-const cn_cbor* cn_cbor_decode(const uint8_t *buf, size_t len CBOR_CONTEXT, cn_cbor_errback *errp);
+cn_cbor* cn_cbor_decode(const uint8_t *buf, size_t len CBOR_CONTEXT, cn_cbor_errback *errp);
 
 /**
  * Get a value from a CBOR map that has the given string as a key.
@@ -222,7 +225,7 @@
  * @param[in]  key          The string to look up in the map
  * @return                  The matching value, or NULL if the key is not found
  */
-const cn_cbor* cn_cbor_mapget_string(const cn_cbor* cb, const char* key);
+cn_cbor* cn_cbor_mapget_string(const cn_cbor* cb, const char* key);
 
 /**
  * Get a value from a CBOR map that has the given integer as a key.
@@ -231,7 +234,7 @@
  * @param[in]  key          The int to look up in the map
  * @return                  The matching value, or NULL if the key is not found
  */
-const cn_cbor* cn_cbor_mapget_int(const cn_cbor* cb, int key);
+cn_cbor* cn_cbor_mapget_int(const cn_cbor* cb, int key);
 
 /**
  * Get the item with the given index from a CBOR array.
@@ -240,15 +243,17 @@
  * @param[in]  idx          The array index
  * @return                  The matching value, or NULL if the index is invalid
  */
-const cn_cbor* cn_cbor_index(const cn_cbor* cb, unsigned int idx);
+cn_cbor* cn_cbor_index(const cn_cbor* cb, unsigned int idx);
 
 /**
  * Free the given CBOR structure.
+ * You MUST NOT try to free a cn_cbor structure with a parent (i.e., one
+ * that is not a root in the tree).
  *
- * @param[in]  cb           The CBOR value to free
+ * @param[in]  cb           The CBOR value to free.  May be NULL, or a root object.
  * @param[in]  CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined)
  */
-void cn_cbor_free(const cn_cbor* cb CBOR_CONTEXT);
+void cn_cbor_free(cn_cbor* cb CBOR_CONTEXT);
 
 /**
  * Write a CBOR value and all of the child values.
@@ -260,10 +265,10 @@
  * @param[in]  cb         [description]
  * @return                -1 on fail, or number of bytes written
  */
-ssize_t cbor_encoder_write(uint8_t *buf,
-                           size_t buf_offset,
-                           size_t buf_size,
-                           const cn_cbor *cb);
+ssize_t cn_cbor_encoder_write(uint8_t *buf,
+			      size_t buf_offset,
+			      size_t buf_size,
+			      const cn_cbor *cb);
 
 /**
  * Create a CBOR map.
diff --git a/src/cn-cbor.c b/src/cn-cbor.c
index 5804720..a7677ae 100644
--- a/src/cn-cbor.c
+++ b/src/cn-cbor.c
@@ -20,8 +20,9 @@
 
 #define CN_CBOR_FAIL(code) do { pb->err = code;  goto fail; } while(0)
 
-void cn_cbor_free(const cn_cbor* cb CBOR_CONTEXT) {
-  cn_cbor* p = (cn_cbor*) cb;
+void cn_cbor_free(cn_cbor* cb CBOR_CONTEXT) {
+  cn_cbor* p = cb;
+  assert(!p || !p->parent);
   while (p) {
     cn_cbor* p1;
     while ((p1 = p->first_child)) { /* go down */
@@ -36,6 +37,7 @@
   }
 }
 
+#ifndef CBOR_NO_FLOAT
 static double decode_half(int half) {
   int exp = (half >> 10) & 0x1f;
   int mant = half & 0x3ff;
@@ -45,6 +47,7 @@
   else val = mant == 0 ? INFINITY : NAN;
   return half & 0x8000 ? -val : val;
 }
+#endif /* CBOR_NO_FLOAT */
 
 /* Fix these if you can't do non-aligned reads */
 #define ntoh8p(p) (*(unsigned char*)(p))
@@ -85,6 +88,7 @@
   int ai;
   uint64_t val;
   cn_cbor* cb = NULL;
+#ifndef CBOR_NO_FLOAT
   union {
     float f;
     uint32_t u;
@@ -93,6 +97,7 @@
     double d;
     uint64_t u;
   } u64;
+#endif /* CBOR_NO_FLOAT */
 
 again:
   TAKE(pos, ebuf, 1, ib = ntoh8p(pos) );
@@ -174,16 +179,31 @@
     case VAL_TRUE:  cb->type = CN_CBOR_TRUE;  break;
     case VAL_NIL:   cb->type = CN_CBOR_NULL;  break;
     case VAL_UNDEF: cb->type = CN_CBOR_UNDEF; break;
-    case AI_2: cb->type = CN_CBOR_DOUBLE; cb->v.dbl = decode_half(val); break;
+    case AI_2:
+#ifndef CBOR_NO_FLOAT
+      cb->type = CN_CBOR_DOUBLE;
+      cb->v.dbl = decode_half(val);
+#else /*  CBOR_NO_FLOAT */
+      CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED);
+#endif /*  CBOR_NO_FLOAT */
+      break;
     case AI_4:
+#ifndef CBOR_NO_FLOAT
       cb->type = CN_CBOR_DOUBLE;
       u32.u = val;
       cb->v.dbl = u32.f;
+#else /*  CBOR_NO_FLOAT */
+      CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED);
+#endif /*  CBOR_NO_FLOAT */
       break;
     case AI_8:
+#ifndef CBOR_NO_FLOAT
       cb->type = CN_CBOR_DOUBLE;
       u64.u = val;
       cb->v.dbl = u64.d;
+#else /*  CBOR_NO_FLOAT */
+      CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED);
+#endif /*  CBOR_NO_FLOAT */
       break;
     default: cb->v.uint = val;
     }
@@ -218,7 +238,7 @@
   return 0;
 }
 
-const cn_cbor* cn_cbor_decode(const unsigned char* buf, size_t len CBOR_CONTEXT, cn_cbor_errback *errp) {
+cn_cbor* cn_cbor_decode(const unsigned char* buf, size_t len CBOR_CONTEXT, cn_cbor_errback *errp) {
   cn_cbor catcher = {CN_CBOR_INVALID, 0, {0}, 0, NULL, NULL, NULL, NULL};
   struct parse_buf pb;
   cn_cbor* ret;
diff --git a/src/cn-encoder.c b/src/cn-encoder.c
index 3182409..3365535 100644
--- a/src/cn-encoder.c
+++ b/src/cn-encoder.c
@@ -112,6 +112,7 @@
   }
 }
 
+#ifndef CBOR_NO_FLOAT
 static void _write_double(cn_write_state *ws, double val)
 {
   float float_val = val;
@@ -173,6 +174,7 @@
 
   }
 }
+#endif /* CBOR_NO_FLOAT */
 
 // TODO: make public?
 typedef void (*cn_visit_func)(const cn_cbor *cb, int depth, void *context);
@@ -270,7 +272,9 @@
     break;
 
   case CN_CBOR_DOUBLE:
+#ifndef CBOR_NO_FLOAT
     CHECK(_write_double(ws, cb->v.dbl));
+#endif /* CBOR_NO_FLOAT */
     break;
 
   case CN_CBOR_INVALID:
@@ -287,10 +291,10 @@
   write_byte_ensured(IB_BREAK);
 }
 
-ssize_t cbor_encoder_write(uint8_t *buf,
-                           size_t buf_offset,
-                           size_t buf_size,
-                           const cn_cbor *cb)
+ssize_t cn_cbor_encoder_write(uint8_t *buf,
+			      size_t buf_offset,
+			      size_t buf_size,
+			      const cn_cbor *cb)
 {
   cn_write_state ws = { buf, buf_offset, buf_size };
   _visit(cb, _encoder_visitor, _encoder_breaker, &ws);
diff --git a/src/cn-error.c b/src/cn-error.c
index d4407d0..4953cc9 100644
--- a/src/cn-error.c
+++ b/src/cn-error.c
@@ -8,5 +8,6 @@
  "CN_CBOR_ERR_RESERVED_AI",
  "CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING",
  "CN_CBOR_ERR_INVALID_PARAMETER",
- "CN_CBOR_ERR_OUT_OF_MEMORY"
+ "CN_CBOR_ERR_OUT_OF_MEMORY",
+ "CN_CBOR_ERR_FLOAT_NOT_SUPPORTED"
 };
diff --git a/src/cn-get.c b/src/cn-get.c
index f4585b9..cc276a5 100644
--- a/src/cn-get.c
+++ b/src/cn-get.c
@@ -4,7 +4,7 @@
 
 #include "cn-cbor/cn-cbor.h"
 
-const cn_cbor* cn_cbor_mapget_int(const cn_cbor* cb, int key) {
+cn_cbor* cn_cbor_mapget_int(const cn_cbor* cb, int key) {
   cn_cbor* cp;
   assert(cb);
   for (cp = cb->first_child; cp && cp->next; cp = cp->next->next) {
@@ -25,7 +25,7 @@
   return NULL;
 }
 
-const cn_cbor* cn_cbor_mapget_string(const cn_cbor* cb, const char* key) {
+cn_cbor* cn_cbor_mapget_string(const cn_cbor* cb, const char* key) {
   cn_cbor *cp;
   int keylen;
   assert(cb);
@@ -48,7 +48,7 @@
   return NULL;
 }
 
-const cn_cbor* cn_cbor_index(const cn_cbor* cb, unsigned int idx) {
+cn_cbor* cn_cbor_index(const cn_cbor* cb, unsigned int idx) {
   cn_cbor *cp;
   unsigned int i = 0;
   assert(cb);
diff --git a/test/cbor_test.c b/test/cbor_test.c
index 4918a87..3326497 100644
--- a/test/cbor_test.c
+++ b/test/cbor_test.c
@@ -60,6 +60,7 @@
     ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING], "CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING");
     ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_INVALID_PARAMETER], "CN_CBOR_ERR_INVALID_PARAMETER");
     ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_OUT_OF_MEMORY], "CN_CBOR_ERR_OUT_OF_MEMORY");
+    ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_FLOAT_NOT_SUPPORTED], "CN_CBOR_ERR_FLOAT_NOT_SUPPORTED");
 }
 
 CTEST(cbor, parse)
@@ -92,6 +93,7 @@
         "f6",	      // null
         "f7",	      // undefined
         "f8ff",     // simple(255)
+#ifndef CBOR_NO_FLOAT
         "f93c00",     // 1.0
         "f9bc00",     // -1.0
         "f903ff",     // 6.097555160522461e-05
@@ -101,6 +103,7 @@
         "fa47800000", // 65536.0
         "fb3ff199999999999a",     // 1.1
         "f97e00",   // NaN
+#endif /* CBOR_NO_FLOAT */
         "5f42010243030405ff",     // (_ h'0102', h'030405')
         "7f61616161ff",           // (_ "a", "a")
         "9fff",                   // [_ ]
@@ -108,7 +111,7 @@
         "9f009f00ff00ff",         // [_ 0, [_ 0], 0]
         "bf61610161629f0203ffff", // {_ "a": 1, "b": [_ 2, 3]}
     };
-    const cn_cbor *cb;
+    cn_cbor *cb;
     buffer b;
     size_t i;
     unsigned char encoded[1024];
@@ -122,7 +125,7 @@
         ASSERT_EQUAL(err.err, CN_CBOR_NO_ERROR);
         ASSERT_NOT_NULL(cb);
 
-        enc_sz = cbor_encoder_write(encoded, 0, sizeof(encoded), cb);
+        enc_sz = cn_cbor_encoder_write(encoded, 0, sizeof(encoded), cb);
         ASSERT_DATA(b.ptr, b.sz, encoded, enc_sz);
         free(b.ptr);
         cn_cbor_free(cb CONTEXT_NULL);
@@ -133,7 +136,7 @@
 CTEST(cbor, parse_normalize)
 {
     cn_cbor_errback err;
-    char *tests[] = {
+    char *basic_tests[] = {
       "00", "00",                       // 0
       "1800", "00",
       "1818", "1818",
@@ -146,6 +149,8 @@
       "c600", "c600",                   // 6(0) (undefined tag)
       "d80600", "c600",
       "d9000600", "c600",
+    };
+    char *float_tests[] = {
       "fb3ff0000000000000", "f93c00",   // 1.0
       "fbbff0000000000000", "f9bc00",   // -1.0
       "fb40f86a0000000000", "fa47c35000", // 100000.0
@@ -154,27 +159,48 @@
       "fb3e78000000000000", "fa33c00000", //  8.940696716308594e-08
       "fb3e80000000000000", "f90002",   // 1.1920928955078125e-07
     };
-    const cn_cbor *cb;
+    cn_cbor *cb;
     buffer b, b2;
     size_t i;
     unsigned char encoded[1024];
     ssize_t enc_sz;
 
-    for (i=0; i<sizeof(tests)/sizeof(char*); ) {
-        ASSERT_TRUE(parse_hex(tests[i++], &b));
-        ASSERT_TRUE(parse_hex(tests[i++], &b2));
+    for (i=0; i<sizeof(basic_tests)/sizeof(char*); i+=2) {
+        ASSERT_TRUE(parse_hex(basic_tests[i], &b));
+        ASSERT_TRUE(parse_hex(basic_tests[i+1], &b2));
         err.err = CN_CBOR_NO_ERROR;
         cb = cn_cbor_decode(b.ptr, b.sz CONTEXT_NULL, &err);
-        CTEST_LOG("%s: %s", tests[i], cn_cbor_error_str[err.err]);
+        CTEST_LOG("%s: %s", basic_tests[i], cn_cbor_error_str[err.err]);
         ASSERT_EQUAL(err.err, CN_CBOR_NO_ERROR);
         ASSERT_NOT_NULL(cb);
 
-        enc_sz = cbor_encoder_write(encoded, 0, sizeof(encoded), cb);
+        enc_sz = cn_cbor_encoder_write(encoded, 0, sizeof(encoded), cb);
         ASSERT_DATA(b2.ptr, b2.sz, encoded, enc_sz);
         free(b.ptr);
         free(b2.ptr);
         cn_cbor_free(cb CONTEXT_NULL);
     }
+
+    for (i=0; i<sizeof(float_tests)/sizeof(char*); i+=2) {
+        ASSERT_TRUE(parse_hex(float_tests[i], &b));
+        ASSERT_TRUE(parse_hex(float_tests[i+1], &b2));
+        err.err = CN_CBOR_NO_ERROR;
+        cb = cn_cbor_decode(b.ptr, b.sz CONTEXT_NULL, &err);
+        CTEST_LOG("%s: %s", float_tests[i], cn_cbor_error_str[err.err]);
+#ifndef CBOR_NO_FLOAT
+        ASSERT_EQUAL(err.err, CN_CBOR_NO_ERROR);
+        ASSERT_NOT_NULL(cb);
+#else /* CBOR_NO_FLOAT */
+        ASSERT_EQUAL(err.err, CN_CBOR_ERR_FLOAT_NOT_SUPPORTED);
+        ASSERT_NULL(cb);
+#endif /* CBOR_NO_FLOAT */
+
+        /* enc_sz = cn_cbor_encoder_write(encoded, 0, sizeof(encoded), cb); */
+        /* ASSERT_DATA(b2.ptr, b2.sz, encoded, enc_sz); */
+        free(b.ptr);
+        free(b2.ptr);
+        cn_cbor_free(cb CONTEXT_NULL);
+    }
 }
 
 typedef struct _cbor_failure
@@ -195,13 +221,13 @@
         {"1c", CN_CBOR_ERR_RESERVED_AI},
         {"7f4100", CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING},
     };
-    const cn_cbor *cb;
+    cn_cbor *cb;
     buffer b;
     size_t i;
     uint8_t buf[10];
     cn_cbor inv = {CN_CBOR_INVALID, 0, {0}, 0, NULL, NULL, NULL, NULL};
 
-    ASSERT_EQUAL(-1, cbor_encoder_write(buf, 0, sizeof(buf), &inv));
+    ASSERT_EQUAL(-1, cn_cbor_encoder_write(buf, 0, sizeof(buf), &inv));
 
     for (i=0; i<sizeof(tests)/sizeof(cbor_failure); i++) {
         ASSERT_TRUE(parse_hex(tests[i].hex, &b));
@@ -217,6 +243,7 @@
 // Decoder loses float size information
 CTEST(cbor, float)
 {
+#ifndef CBOR_NO_FLOAT
     cn_cbor_errback err;
     char *tests[] = {
         "f90001", // 5.960464477539063e-08
@@ -226,7 +253,7 @@
         "f9fc00", // -Inf
         "f97c00", // Inf
     };
-    const cn_cbor *cb;
+    cn_cbor *cb;
     buffer b;
     size_t i;
     unsigned char encoded[1024];
@@ -237,19 +264,20 @@
         cb = cn_cbor_decode(b.ptr, b.sz CONTEXT_NULL, &err);
         ASSERT_NOT_NULL(cb);
 
-        enc_sz = cbor_encoder_write(encoded, 0, sizeof(encoded), cb);
+        enc_sz = cn_cbor_encoder_write(encoded, 0, sizeof(encoded), cb);
         ASSERT_DATA(b.ptr, b.sz, encoded, enc_sz);
 
         free(b.ptr);
         cn_cbor_free(cb CONTEXT_NULL);
     }
+#endif /* CBOR_NO_FLOAT */
 }
 
 CTEST(cbor, getset)
 {
     buffer b;
-    const cn_cbor *cb;
-    const cn_cbor *val;
+    cn_cbor *cb;
+    cn_cbor *val;
     cn_cbor_errback err;
 
     ASSERT_TRUE(parse_hex("a40000436363630262626201616100", &b));
@@ -397,6 +425,6 @@
   ASSERT_NOT_NULL(cdata);
 
   ASSERT_TRUE(cn_cbor_mapput_int(map, 0, cdata, CONTEXT_NULL_COMMA NULL));
-  enc_sz = cbor_encoder_write(encoded, 0, sizeof(encoded), map);
+  enc_sz = cn_cbor_encoder_write(encoded, 0, sizeof(encoded), map);
   ASSERT_EQUAL(7, enc_sz);
 }
diff --git a/test/test.c b/test/test.c
index 42c6cf6..d24992f 100644
--- a/test/test.c
+++ b/test/test.c
@@ -98,6 +98,7 @@
   "CN_CBOR_ERR_RESERVED_AI",
   "CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING",
   "CN_CBOR_ERR_OUT_OF_MEMORY",
+  "CN_CBOR_ERR_FLOAT_NOT_SUPPORTED",
 };
 
 static void cn_cbor_decode_test(const unsigned char *buf, int len) {
@@ -114,7 +115,7 @@
   char *bufend;
   unsigned char *s = load_file("cases.cbor", &end);
   printf("%zd\n", end-s);
-  const cn_cbor *cb = cn_cbor_decode(s, end-s CBOR_CONTEXT_PARAM, 0);
+  cn_cbor *cb = cn_cbor_decode(s, end-s CBOR_CONTEXT_PARAM, 0);
   if (cb) {
     dump(cb, buf, &bufend, 0);
     *bufend = 0;