Merge remote-tracking branch 'refs/remotes/origin/windows' into complete

# Conflicts:
#	.gitignore
#	include/cn-cbor/cn-cbor.h
#	src/cn-cbor.c
#	src/cn-encoder.c
diff --git a/.gitignore b/.gitignore
index 4e0b78a..dc37cea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,17 +5,19 @@
 
 # Emacs temp files
 *~
+.#*
+*#
 
-# Visual Studio build directories
+# Visual Stdio build directories
 Debug
 Release
 *.vcxproj
 *.vcxproj.filters
+*.vcxproj.user
 
-# Output of cmake
+# Output of CMake
 CMakeCache.txt
 CMakeFiles
 Makefile
 *.cmake
 install_manifest.txt
-
diff --git a/include/cn-cbor/cn-cbor.h b/include/cn-cbor/cn-cbor.h
index 673e902..109eb9e 100644
--- a/include/cn-cbor/cn-cbor.h
+++ b/include/cn-cbor/cn-cbor.h
@@ -29,7 +29,8 @@
 #include <stdbool.h>
 #include <stdint.h>
 #ifdef _MSC_VER
-typedef long ssize_t;
+#include <WinSock2.h>
+typedef signed long ssize_t;
 #else
 #include <unistd.h>
 #endif
@@ -441,6 +442,21 @@
                           cn_cbor* cb_value,
                           cn_cbor_errback *errp);
 
+/**
+ * Dump the object to a file pointer
+ * If buffer is NULL, then return required size to generate output
+ *
+ * @param[in]   buffer	Location to place output
+ * @param[in]   bufferSize Size of return buffer
+ * @param[in]	fp		File pointer to print on
+ * @param[in]	cb		tree to be dumped
+ * @param[in]   indent  string to use for each level of indention
+ * @param[in]   crlf    string to use for end of line marker
+ * @return				size of output generated, -1 if buffer is too small
+ */
+
+extern ssize_t cn_cbor_printer_write(char * buffer, size_t bufferSize, const cn_cbor * cb, const char * indent, const char * crlf);
+
 #ifdef  __cplusplus
 }
 #endif
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ecaefa0..2cef043 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -8,6 +8,7 @@
       cn-encoder.c
       cn-error.c
       cn-get.c
+      cn-print.c
 )
 
 if (use_context)
diff --git a/src/cn-cbor.c b/src/cn-cbor.c
index 95e3a71..44cae3c 100644
--- a/src/cn-cbor.c
+++ b/src/cn-cbor.c
@@ -17,7 +17,6 @@
 #include <WinSock2.h>  // needed for ntohl on Windows
 #else
 #include <arpa/inet.h> // needed for ntohl (e.g.) on Linux
-#endif
 
 #include "dll-export.h"
 
diff --git a/src/cn-encoder.c b/src/cn-encoder.c
index def4559..939c0e3 100644
--- a/src/cn-encoder.c
+++ b/src/cn-encoder.c
@@ -10,6 +10,7 @@
 
 #ifdef _MSC_VER
 #include <WinSock2.h>
+#define inline _inline
 #else
 #include <arpa/inet.h>
 #endif
@@ -42,23 +43,23 @@
   ssize_t size;
 } cn_write_state;
 
-#define ensure_writable(sz) if ((ws->offset<0) || (ws->offset + (sz) > ws->size)) { \
+#define ensure_writable(sz) if ((ws->buf != NULL)  && ((ws->offset<0) || (ws->offset + (sz) > ws->size))) { \
   ws->offset = -1; \
   return; \
 }
 
 #define write_byte_and_data(b, data, sz) \
-ws->buf[ws->offset++] = (b); \
-memcpy(ws->buf+ws->offset, (data), (sz)); \
-ws->offset += sz;
+    ws->buf[ws->offset++] = (b); \
+    memcpy(ws->buf + ws->offset, (data), (sz)); \
+    ws->offset += sz;
 
 #define write_byte(b) \
-ensure_writable(1); \
-ws->buf[ws->offset++] = (b); \
+{ if (ws->buf == NULL) ws->offset++; \
+else ws->buf[ws->offset++] = (b); }
 
 #define write_byte_ensured(b) \
 ensure_writable(1); \
-write_byte(b); \
+write_byte(b);
 
 static uint8_t _xlate[] = {
   IB_FALSE,    /* CN_CBOR_FALSE */
@@ -186,7 +187,7 @@
 
 // TODO: make public?
 typedef void (*cn_visit_func)(const cn_cbor *cb, int depth, void *context);
-static void _visit(const cn_cbor *cb,
+void _visit(const cn_cbor *cb,
                    cn_visit_func visitor,
                    cn_visit_func breaker,
                    void *context)
@@ -202,17 +203,25 @@
         depth++;
       } else{
         // Empty indefinite
+#ifdef CN_INCLUDE_DUMPER
+          breaker(p, depth, context);
+#else
         if (is_indefinite(p)) {
-          breaker(p->parent, depth, context);
+          breaker(p, depth, context);
         }
+#endif
         if (p->next) {
           p = p->next;
         } else {
           while (p->parent) {
             depth--;
+#ifdef CN_INCLUDE_DUMPER
+            breaker(p->parent, depth, context);
+#else
             if (is_indefinite(p->parent)) {
               breaker(p->parent, depth, context);
             }
+#endif
             if (p->parent->next) {
               p = p->parent->next;
               goto visit;
@@ -296,7 +305,13 @@
   cn_write_state *ws = context;
   UNUSED_PARAM(cb);
   UNUSED_PARAM(depth);
-  write_byte_ensured(IB_BREAK);
+#ifdef CN_INCLUDE_DUMPER
+  if (is_indefinite(cb)) {
+#endif
+      write_byte_ensured(IB_BREAK);
+#ifdef CN_INCLUDE_DUMPER
+  }
+#endif
 }
 
 ssize_t cn_cbor_encoder_write(uint8_t *buf,
diff --git a/src/cn-print.c b/src/cn-print.c
new file mode 100644
index 0000000..2eb76f4
--- /dev/null
+++ b/src/cn-print.c
@@ -0,0 +1,253 @@
+#ifndef CN_PRINT_C
+#define CN_PRINT_C
+#define CN_INCLUDE_DUMPER
+#ifdef CN_INCLUDE_DUMPER
+#define _CRT_SECURE_NO_WARNINGS 1
+
+#include <stdio.h>
+#include <stdio.h>
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+#ifdef EMACS_INDENTATION_HELPER
+} /* Duh. */
+#endif
+
+#include <stdio.h>
+#ifdef MSV_CRT
+#include <winsock2.h>
+#else
+#define _snprintf snprintf
+#endif
+#include <string.h>
+#include <stdbool.h>
+#include <assert.h>
+
+#include "cn-cbor/cn-cbor.h"
+#include "cbor.h"
+
+typedef struct _write_state
+{
+	char * rgbOutput;
+	ssize_t ib;
+	size_t cbLeft;
+	uint8_t * rgFlags;
+	const char * szIndentWith;
+	const char * szEndOfLine;
+} cn_write_state;
+
+typedef void(*cn_visit_func)(const cn_cbor *cb, int depth, void *context);
+extern void _visit(const cn_cbor *cb,
+	cn_visit_func visitor,
+	cn_visit_func breaker,
+	void *context);
+
+const char RgchHex[] = { '0', '1', '2', '3', '4', '5', '6', '7',
+'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+bool _isWritable(cn_write_state * ws, size_t cb)
+{
+	if (ws->rgbOutput == NULL) return true;
+	if ((ws->ib < 0) || (ws->ib + cb > ws->cbLeft)) {
+		ws->ib = -1;
+		return false;
+	}
+	return true;
+}
+
+void write_data(cn_write_state * ws, const char * sz, size_t cb)
+{
+	if (_isWritable(ws, cb)) {
+		if (ws->rgbOutput != NULL) memcpy(ws->rgbOutput + ws->ib, sz, cb);
+		ws->ib += cb;
+	}
+}
+
+void _doIndent(cn_write_state * ws, int depth)
+{
+	int i;
+	char * sz = ws->rgbOutput + ws->ib;
+	size_t cbIndentWith = strlen(ws->szIndentWith);
+	int cbIndent = depth * cbIndentWith;
+
+
+	if (ws->rgbOutput == NULL) {
+		ws->ib += cbIndent;
+		return;
+	}
+
+	if (_isWritable(ws, cbIndent)) {
+		for (i = 0; i < depth; i++) {
+			memcpy(sz, ws->szIndentWith, cbIndentWith);
+			sz += cbIndentWith;
+		}
+	}
+
+	ws->ib += cbIndent;
+
+	return;
+}
+
+void _print_encoder(const cn_cbor * cb, int depth, void * context)
+{
+	int i;
+	char rgchT[256];
+	int cch;
+	cn_write_state * ws = (cn_write_state *)context;
+	uint8_t flags = ws->rgFlags[depth];
+
+	if (flags & 1) {
+		write_data(ws, ", ", 2);
+		ws->rgFlags[depth] &= 0xfe;
+
+		if (ws->szIndentWith) {
+			write_data(ws, ws->szEndOfLine, strlen(ws->szEndOfLine));
+			_doIndent(ws, depth);
+		}
+	}
+
+	if (flags & 2) {
+		write_data(ws, ": ", 2);
+		ws->rgFlags[depth] &= 0xfd;
+	}
+
+	switch (cb->type) {
+	case CN_CBOR_BYTES_CHUNKED:
+	case CN_CBOR_TEXT_CHUNKED:
+	  break;
+
+	case CN_CBOR_ARRAY:
+		write_data(ws, "[", 1);
+		ws->rgFlags[depth] |= 4;
+
+		if (ws->szIndentWith) {
+			write_data(ws, ws->szEndOfLine, strlen(ws->szEndOfLine));
+			_doIndent(ws, depth + 1);
+		}
+		break;
+
+	case CN_CBOR_MAP:
+		write_data(ws, "{", 1);
+		ws->rgFlags[depth] |= 8;
+
+		if (ws->szIndentWith) {
+			write_data(ws, ws->szEndOfLine, strlen(ws->szEndOfLine));
+			_doIndent(ws, depth + 1);
+		}
+		break;
+
+	case CN_CBOR_TAG:
+	case CN_CBOR_UINT:
+	case CN_CBOR_SIMPLE:
+	  cch = _snprintf(rgchT, sizeof(rgchT), "%u", (unsigned int) cb->v.uint);
+		write_data(ws, rgchT, cch);
+		break;
+
+	case CN_CBOR_FALSE:
+		write_data(ws, "false", 5);
+		break;
+
+	case CN_CBOR_TRUE:
+		write_data(ws, "true", 4);
+		break;
+
+	case CN_CBOR_NULL:
+		write_data(ws, "null", 4);
+		break;
+
+	case CN_CBOR_UNDEF:
+		write_data(ws, "undef", 5);
+		break;
+
+	case CN_CBOR_INT:
+	  cch = _snprintf(rgchT, sizeof(rgchT), "%d", (unsigned int) cb->v.sint);
+		write_data(ws, rgchT, cch);
+		break;
+
+	case CN_CBOR_DOUBLE:
+		cch = _snprintf(rgchT, sizeof(rgchT), "%f", cb->v.dbl);
+		write_data(ws, rgchT, cch);
+		break;
+
+	case CN_CBOR_INVALID:
+		write_data(ws, "invalid", 7);
+		break;
+
+	case CN_CBOR_TEXT:
+		write_data(ws, "\"", 1);
+		write_data(ws, cb->v.str, cb->length);
+		write_data(ws, "\"", 1);
+		break;
+
+	case CN_CBOR_BYTES:
+		write_data(ws, "h'", 2);
+		for (i = 0; i < cb->length; i++) {
+			write_data(ws, &RgchHex[(cb->v.str[i] / 16) & 0xf], 1);
+			write_data(ws, &RgchHex[cb->v.str[i] & 0xf], 1);
+		}
+		write_data(ws, "\'", 1);
+		break;
+	}
+
+	if (depth > 0) {
+		if (ws->rgFlags[depth - 1] & 4) ws->rgFlags[depth] |= 1;
+		else if (ws->rgFlags[depth - 1] & 8) {
+			if (flags & 2) ws->rgFlags[depth] |= 1;
+			else ws->rgFlags[depth] |= 2;
+		}
+	}
+}
+
+void _print_breaker(const cn_cbor * cb, int depth, void * context)
+{
+	cn_write_state * ws = (cn_write_state *)context;
+
+	switch (cb->type) {
+	case CN_CBOR_ARRAY:
+		if (ws->szIndentWith) {
+			write_data(ws, ws->szEndOfLine, strlen(ws->szEndOfLine));
+			_doIndent(ws, depth);
+		}
+
+		write_data(ws, "]", 1);
+		ws->rgFlags[depth + 1] = 0;
+		break;
+
+	case CN_CBOR_MAP:
+		if (ws->szIndentWith) {
+			write_data(ws, ws->szEndOfLine, strlen(ws->szEndOfLine));
+			_doIndent(ws, depth);
+		}
+
+		write_data(ws, "}", 1);
+		ws->rgFlags[depth + 1] = 0;
+		break;
+
+	default:
+	  break;
+	}
+}
+
+ssize_t cn_cbor_printer_write(char * rgbBuffer, size_t cbBuffer, const cn_cbor * cb, const char * szIndentWith, const char * szEndOfLine)
+{
+	uint8_t flags[128] = { 0 };
+	char rgchZero[1] = { 0 };
+
+	cn_write_state ws = { rgbBuffer, 0, cbBuffer, flags, szIndentWith, szEndOfLine };
+	_visit(cb, _print_encoder, _print_breaker, &ws);
+	write_data(&ws, rgchZero, 1);
+
+	return ws.ib;
+}
+
+#ifdef EMACS_INDENTATION_HELPER
+{ /* Duh. */
+#endif
+#ifdef _cplusplus
+}	/* extern "C" */
+#endif
+
+#endif // CN_INCLUDE_DUMPER
+#endif // CN_PRINT_C
+