From 65cd70a85bb58e10fa223a4d4f496b31c096e20b Mon Sep 17 00:00:00 2001
From: mpc <mpc>
Date: Sun, 20 Jun 2004 11:44:53 +0000
Subject: [PATCH] LibSAM

---
 apps/sam/c/LICENSE                 |   27 +
 apps/sam/c/Makefile.cygwin         |   65 ++
 apps/sam/c/Makefile.debian         |   65 ++
 apps/sam/c/Makefile.linux          |   64 ++
 apps/sam/c/Makefile.mingw          |   64 ++
 apps/sam/c/doc/donate.txt          |    7 +
 apps/sam/c/doc/license.txt         |   27 +
 apps/sam/c/doc/todo.txt            |    9 +
 apps/sam/c/doc/whatsnew.txt        |   26 +
 apps/sam/c/examples/build.bat      |    8 +
 apps/sam/c/examples/build.sh       |    8 +
 apps/sam/c/examples/dgram-client.c |  125 ++++
 apps/sam/c/examples/dgram-server.c |  109 +++
 apps/sam/c/inc/platform.h          |  140 ++++
 apps/sam/c/inc/sam.h               |  123 ++++
 apps/sam/c/inc/snprintf.h          |   49 ++
 apps/sam/c/inc/strl.h              |   47 ++
 apps/sam/c/src/sam.c               | 1047 ++++++++++++++++++++++++++++
 apps/sam/c/src/snprintf.c          |  851 ++++++++++++++++++++++
 apps/sam/c/src/strl.c              |   84 +++
 20 files changed, 2945 insertions(+)
 create mode 100644 apps/sam/c/LICENSE
 create mode 100644 apps/sam/c/Makefile.cygwin
 create mode 100644 apps/sam/c/Makefile.debian
 create mode 100644 apps/sam/c/Makefile.linux
 create mode 100644 apps/sam/c/Makefile.mingw
 create mode 100644 apps/sam/c/doc/donate.txt
 create mode 100644 apps/sam/c/doc/license.txt
 create mode 100644 apps/sam/c/doc/todo.txt
 create mode 100644 apps/sam/c/doc/whatsnew.txt
 create mode 100644 apps/sam/c/examples/build.bat
 create mode 100644 apps/sam/c/examples/build.sh
 create mode 100644 apps/sam/c/examples/dgram-client.c
 create mode 100644 apps/sam/c/examples/dgram-server.c
 create mode 100644 apps/sam/c/inc/platform.h
 create mode 100644 apps/sam/c/inc/sam.h
 create mode 100644 apps/sam/c/inc/snprintf.h
 create mode 100644 apps/sam/c/inc/strl.h
 create mode 100644 apps/sam/c/src/sam.c
 create mode 100644 apps/sam/c/src/snprintf.c
 create mode 100644 apps/sam/c/src/strl.c

diff --git a/apps/sam/c/LICENSE b/apps/sam/c/LICENSE
new file mode 100644
index 0000000000..ed1daacf69
--- /dev/null
+++ b/apps/sam/c/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+    * Neither the name of the author nor the names of any contributors
+may be used to endorse or promote products derived from this software
+without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/apps/sam/c/Makefile.cygwin b/apps/sam/c/Makefile.cygwin
new file mode 100644
index 0000000000..46930684e3
--- /dev/null
+++ b/apps/sam/c/Makefile.cygwin
@@ -0,0 +1,65 @@
+#
+# This Makefile is compatible with GNU Make and should work on Cygwin
+#
+
+#
+# Your operating system
+#
+
+OS = CYGWIN
+
+#
+# Directories
+#
+
+INCDIR = inc
+LIBDIR = lib
+OBJDIR = obj
+SRCDIR = src
+
+#
+# Programs
+#
+
+AR = ar
+CC = gcc
+
+#
+# Flags
+#
+
+CFLAGS = -g -march=i486 -O2 -pipe -std=c99 -Wall
+CFLAGS += -DOS=$(OS)
+CFLAGS += -I$(INCDIR)
+
+#
+# Object files
+#
+
+OBJS =	$(OBJDIR)/sam.o \
+		$(OBJDIR)/snprintf.o \
+		$(OBJDIR)/strl.o
+
+#
+# Build rules
+#
+
+all: depend libsam
+
+depend:
+	$(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend
+
+$(OBJDIR)/%.o: $(SRCDIR)/%.c
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+libsam: $(OBJS)
+	$(AR) rcs $(LIBDIR)/libsam.a $(OBJS)
+
+#
+# Cleanup rules
+#
+
+clean:
+	-rm -f $(LIBDIR)/libsam.a $(OBJDIR)/* .depend
+
+tidy: clean
diff --git a/apps/sam/c/Makefile.debian b/apps/sam/c/Makefile.debian
new file mode 100644
index 0000000000..8f7b9f3901
--- /dev/null
+++ b/apps/sam/c/Makefile.debian
@@ -0,0 +1,65 @@
+#
+# This Makefile is compatible with GNU Make and should work on Linux
+# (Tested on Debian 3.0)
+#
+
+#
+# Your operating system
+#
+
+OS = LINUX
+
+#
+# Directories
+#
+
+INCDIR = inc
+LIBDIR = lib
+OBJDIR = obj
+SRCDIR = src
+
+#
+# Programs
+#
+
+AR = ar
+CC = gcc-3.0
+
+#
+# Flags
+#
+
+CFLAGS = -g -march=i486 -O2 -pipe -std=c99 -Wall
+CFLAGS += -DOS=$(OS)
+CFLAGS += -I$(INCDIR)
+
+#
+# Object files
+#
+
+OBJS =	$(OBJDIR)/sam.o \
+		$(OBJDIR)/strl.o
+
+#
+# Build rules
+#
+
+all: depend libsam
+
+depend:
+	$(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend
+
+$(OBJDIR)/%.o: $(SRCDIR)/%.c
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+libsam: $(OBJS)
+	$(AR) rcs $(LIBDIR)/libsam.a $(OBJS)
+
+#
+# Cleanup rules
+#
+
+clean:
+	-rm -f $(LIBDIR)/libsam.a $(OBJDIR)/* .depend
+
+tidy: clean
diff --git a/apps/sam/c/Makefile.linux b/apps/sam/c/Makefile.linux
new file mode 100644
index 0000000000..a3e5b779bf
--- /dev/null
+++ b/apps/sam/c/Makefile.linux
@@ -0,0 +1,64 @@
+#
+# This Makefile is compatible with GNU Make and should work on Linux (generic)
+#
+
+#
+# Your operating system
+#
+
+OS = LINUX
+
+#
+# Directories
+#
+
+INCDIR = inc
+LIBDIR = lib
+OBJDIR = obj
+SRCDIR = src
+
+#
+# Programs
+#
+
+AR = ar
+CC = gcc
+
+#
+# Flags
+#
+
+CFLAGS = -g -march=i486 -O2 -pipe -std=c99 -Wall
+CFLAGS += -DOS=$(OS)
+CFLAGS += -I$(INCDIR)
+
+#
+# Object files
+#
+
+OBJS =	$(OBJDIR)/sam.o \
+		$(OBJDIR)/strl.o
+
+#
+# Build rules
+#
+
+all: depend libsam
+
+depend:
+	$(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend
+
+$(OBJDIR)/%.o: $(SRCDIR)/%.c
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+libsam: $(OBJS)
+	$(AR) rcs $(LIBDIR)/libsam.a $(OBJS)
+
+#
+# Cleanup rules
+#
+
+clean:
+	-rm -f $(LIBDIR)/libsam.a $(OBJDIR)/* .depend
+
+tidy: clean
diff --git a/apps/sam/c/Makefile.mingw b/apps/sam/c/Makefile.mingw
new file mode 100644
index 0000000000..4d0a4e571a
--- /dev/null
+++ b/apps/sam/c/Makefile.mingw
@@ -0,0 +1,64 @@
+#
+# This Makefile is compatible with GNU Make and should work on Windows (MingW)
+#
+
+#
+# Your operating system
+#
+
+OS = MINGW
+
+#
+# Directories
+#
+
+INCDIR = inc
+LIBDIR = lib
+OBJDIR = obj
+SRCDIR = src
+
+#
+# Programs
+#
+
+AR = C:\Dev-Cpp\bin\ar
+CC = C:\Dev-Cpp\bin\gcc
+
+#
+# Flags
+#
+
+CFLAGS = -g -march=i486 -O2 -pipe -std=c99 -Wall
+CFLAGS += -DOS=$(OS)
+CFLAGS += -I$(INCDIR)
+
+#
+# Object files
+#
+
+OBJS =	$(OBJDIR)/sam.o \
+		$(OBJDIR)/strl.o
+
+#
+# Build rules
+#
+
+all: depend libsam
+
+depend:
+	$(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend
+
+$(OBJDIR)/%.o: $(SRCDIR)/%.c
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+libsam: $(OBJS)
+	$(AR) rcs $(LIBDIR)/libsam.a $(OBJS)
+
+#
+# Cleanup rules
+#
+
+clean:
+	-rm -f $(LIBDIR)/libsam.a $(OBJDIR)/* .depend
+
+tidy: clean
diff --git a/apps/sam/c/doc/donate.txt b/apps/sam/c/doc/donate.txt
new file mode 100644
index 0000000000..b74604b67e
--- /dev/null
+++ b/apps/sam/c/doc/donate.txt
@@ -0,0 +1,7 @@
+If you would like to make a donation to the author of this library, you can use
+the following methods:
+
+	* E-Gold account number 1043280
+	* Paypal email mpc@innographx.com
+
+If you want to use some other method, just ask.
diff --git a/apps/sam/c/doc/license.txt b/apps/sam/c/doc/license.txt
new file mode 100644
index 0000000000..ed1daacf69
--- /dev/null
+++ b/apps/sam/c/doc/license.txt
@@ -0,0 +1,27 @@
+Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+    * Neither the name of the author nor the names of any contributors
+may be used to endorse or promote products derived from this software
+without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/apps/sam/c/doc/todo.txt b/apps/sam/c/doc/todo.txt
new file mode 100644
index 0000000000..6dc2bef8f5
--- /dev/null
+++ b/apps/sam/c/doc/todo.txt
@@ -0,0 +1,9 @@
+I need to do these things:
+
+* SAM raw support
+* Write an instruction manual
+
+Anyone can help with these things:
+
+* Fix the example dgram-client.c
+* Compile on as many platforms as possible
diff --git a/apps/sam/c/doc/whatsnew.txt b/apps/sam/c/doc/whatsnew.txt
new file mode 100644
index 0000000000..5b86737ca0
--- /dev/null
+++ b/apps/sam/c/doc/whatsnew.txt
@@ -0,0 +1,26 @@
+v1.15	2004-06-
+	* Fixed some fatal bugs in datagram handling
+	* Added another error return type named SAM_TOO_BIG - some functions now
+		return samerr_t instead of bool now
+
+v1.10	2004-06-16
+	* Changed sam_strerror() to work the same as the standard library strerror()
+	* Ported to native MS Windows (uses the Mingw development environment)
+	* Fixed a probable bug in the Cygwin port
+
+v1.05	2004-06-09
+	* Added an example datagram client/server program in the examples directory
+	* sam_read_buffer() now returns bool true if it read anything
+	* Added repliable datagram support - sam_connect() now has another argument
+	* Replaced strstr() with the more appropriate strncmp() in many places
+	* Fixed a parsing error for STREAM CLOSED
+	* Removed the old sam_naming_lookup() and renamed sam_naming_lookup_async()
+		to sam_naming_lookup() to replace it
+	* Fixed a bug in sam_stream_close() where a '\n' was not being sent after
+		the SAM command
+	* Fixed a bug where the stream ID was being improperly incremented in
+		sam_stream_connect()
+	* Added generic Linux Makefile for non-Debian distributions
+
+v1.00	2004-06-02
+	* First public release
diff --git a/apps/sam/c/examples/build.bat b/apps/sam/c/examples/build.bat
new file mode 100644
index 0000000000..c236e99e38
--- /dev/null
+++ b/apps/sam/c/examples/build.bat
@@ -0,0 +1,8 @@
+@echo off
+
+del dgram-server.o dgram-server.exe
+C:\Dev-Cpp\bin\gcc -I../inc -o dgram-server.o -c dgram-server.c
+C:\Dev-Cpp\bin\gcc -I../inc -L../lib -o dgram-server.exe dgram-server.o -lsam -lwsock32
+del dgram-client.o dgram-client.exe
+C:\Dev-Cpp\bin\gcc -I../inc -o dgram-client.o -c dgram-client.c
+C:\Dev-Cpp\bin\gcc -I../inc -L../lib -o dgram-client.exe dgram-client.o -lsam -lwsock32
\ No newline at end of file
diff --git a/apps/sam/c/examples/build.sh b/apps/sam/c/examples/build.sh
new file mode 100644
index 0000000000..8ee0b9fb7f
--- /dev/null
+++ b/apps/sam/c/examples/build.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+rm dgram-server.o dgram-server
+gcc -I../inc -o dgram-server.o -c dgram-server.c
+gcc -I../inc -L../lib -o dgram-server dgram-server.o -lsam
+rm dgram-client.o dgram-client
+gcc -I../inc -o dgram-client.o -c dgram-client.c
+gcc -I../inc -L../lib -o dgram-client dgram-client.o -lsam
diff --git a/apps/sam/c/examples/dgram-client.c b/apps/sam/c/examples/dgram-client.c
new file mode 100644
index 0000000000..c2fe6acc0f
--- /dev/null
+++ b/apps/sam/c/examples/dgram-client.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the author nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "sam.h"
+
+static void dgramback(sam_pubkey_t dest, void *data, size_t size);
+static void diedback(void);
+static void logback(char *s);
+static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result);
+
+/*
+ * This is an extremely simple echo client which shows you how LibSAM
+ * datagrams work.  We lookup the name 'dgram-server' then send some data to
+ * him.  If everything works, we should get the same data back.
+ */
+int main(int argc, char* argv[])
+{
+	samerr_t rc;
+	char errstr[SAM_ERRMSG_LEN];
+
+	/* Hook up the callback functions */
+	sam_dgramback = &dgramback;
+	sam_diedback = &diedback;
+	sam_logback = &logback;
+	sam_namingback = &namingback;
+
+	/* Connect to the SAM server */
+	rc = sam_connect("localhost", 7656, "dgram-client", SAM_DGRAM, 0);
+	if (rc != SAM_OK) {
+		fprintf(stderr, "SAM connection failed: %s\n", sam_strerror(rc));
+		exit(1);
+	}
+	/*
+	 * This is equivalent to doing a DNS lookup on the normal internet.  Note
+	 * that the dgram-server must already be running for this to work.
+	 * When the lookup completes, we send them some data (see namingback()).
+	 */
+	sam_naming_lookup("dgram-server");
+
+	/*
+	 * Wait for something to happen, then invoke the appropriate callback
+	 */
+	while (true)
+		sam_read_buffer();
+
+	return 0;
+}
+
+/*
+ * When we receive some data, print it out to the screen
+ */
+static void dgramback(sam_pubkey_t dest, void *data, size_t size)
+{
+	printf("Datagram received: %s\n", (char *)data);
+	free(data);
+}
+
+/*
+ * This is called whenever the SAM connection fails (like if the I2P router is
+ * shut down)
+ */
+static void diedback(void)
+{
+	fprintf(stderr, "Lost SAM connection!\n");
+	exit(1);
+}
+
+/*
+ * The logging callback prints any logging messages from LibSAM
+ */
+static void logback(char *s)
+{
+	fprintf(stderr, "LibSAM: %s\n", s);
+}
+
+/*
+ * When a name is resolved, send data to that destination address
+ */
+static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result)
+{
+	char *data = "Hello, invisible world!";
+
+	printf("I got %s's base 64 destination, so now I will send him some " \
+		"data...\n", name);
+	sam_dgram_send(pubkey, data, strlen(data));
+	/*
+	 * ^^^ An extra NUL is appended to the data by LibSAM, so it is not
+	 * necessary to send trailing NULs over the wire for strings.  This doesn't
+	 * cause problems with binary data, because the NUL isn't included in `size'
+	 * in sam_dgramback().
+	 * That is why we use strlen(data) instead of strlen(data) + 1.
+	 */
+	puts("Datagram sent");
+}
diff --git a/apps/sam/c/examples/dgram-server.c b/apps/sam/c/examples/dgram-server.c
new file mode 100644
index 0000000000..8a6ec54afe
--- /dev/null
+++ b/apps/sam/c/examples/dgram-server.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the author nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "sam.h"
+
+static void dgramback(sam_pubkey_t dest, void *data, size_t size);
+static void diedback(void);
+static void logback(char *s);
+static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result);
+
+/*
+ * This is an extremely simple echo server which shows you how LibSAM
+ * datagrams work.  We echo back every datagram that is sent to us.
+ */
+int main(int argc, char* argv[])
+{
+	samerr_t rc;
+	char errstr[SAM_ERRMSG_LEN];
+
+	/* Hook up the callback functions */
+	sam_dgramback = &dgramback;
+	sam_diedback = &diedback;
+	sam_logback = &logback;
+	sam_namingback = &namingback;
+
+	/* Connect to the SAM server */
+	rc = sam_connect("127.0.0.1", 7656, "dgram-server", SAM_DGRAM, 0);
+	if (rc != SAM_OK) {
+		fprintf(stderr, "SAM connection failed: %s\n", sam_strerror(rc));
+		exit(1);
+	}
+
+	/*
+	 * At this point we just keep polling the buffer, which causes the
+	 * appropriate callbacks to be called whenever something happens
+	 */
+	while (true)
+		sam_read_buffer();
+
+	return 0;
+}
+
+/*
+ * When we receive some data, we just ECHO the exact same data back to them
+ */
+static void dgramback(sam_pubkey_t dest, void *data, size_t size)
+{
+	puts("Echoing datagram");
+	sam_dgram_send(dest, data, size);
+	free(data);
+}
+
+/*
+ * This is called whenever the SAM connection fails (like if the I2P router is
+ * shut down)
+ */
+static void diedback(void)
+{
+	fprintf(stderr, "Lost SAM connection!\n");
+	exit(1);
+}
+
+/*
+ * The logging callback prints any logging messages from LibSAM
+ */
+static void logback(char *s)
+{
+	fprintf(stderr, "LibSAM: %s\n", s);
+}
+
+/*
+ * Not used, but the function has to be in the program anyway
+ */
+static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result)
+{
+	assert(false);  /* we don't do any naming lookups in this program */
+}
diff --git a/apps/sam/c/inc/platform.h b/apps/sam/c/inc/platform.h
new file mode 100644
index 0000000000..c8487e702f
--- /dev/null
+++ b/apps/sam/c/inc/platform.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the author nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PLATFORM_H
+#define PLATFORM_H
+
+/*
+ * Operating system
+ */
+#define FREEBSD	0  // FreeBSD (untested)
+#define MINGW	1  // Windows native (Mingw)
+#define LINUX	2  // Linux
+#define CYGWIN	3  // Cygwin
+
+#if OS == MINGW
+	#define INET_ADDRSTRLEN 16
+	#define NO_GETHOSTBYNAME2
+	#define NO_INET_ATON  /* implies NO_INET_PTON */
+	#define NO_INET_NTOP
+	#define NO_STRL
+	#define NO_Z_FORMAT
+	#define WINSOCK
+#endif
+
+#if OS == LINUX
+	#define NO_GETHOSTBYNAME2
+	#define NO_STRL
+	#define NO_Z_FORMAT
+#endif
+
+#if OS == CYGWIN
+	#define FAST32_IS_LONG
+	#define INET_ADDRSTRLEN 16
+	#define NO_GETHOSTBYNAME2
+	#define NO_INET_NTOP
+	#define NO_INET_PTON
+	#define NO_SNPRINTF
+	#define NO_STRL
+	#define NO_VSNPRINTF
+	#define NO_Z_FORMAT
+#endif
+
+/*
+ * Standard C99 includes - if your compiler doesn't have these, it's time to
+ * upgrade
+ */
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/*
+ * System includes
+ */
+#ifdef WINSOCK
+	#include <winsock.h>
+#else
+	#include <arpa/inet.h>
+	#include <sys/select.h>
+	#include <sys/types.h>
+	#include <sys/socket.h>
+#endif
+#include <assert.h>
+#include <errno.h>
+#ifndef WINSOCK
+	#include <netdb.h>
+#endif
+#if defined NO_SNPRINTF || defined NO_VSNPRINTF
+	#include "snprintf.h"
+#endif
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef NO_STRL
+	#include "strl.h"
+#endif
+#ifdef WINSOCK
+	#include <windows.h>
+#else
+	#include <unistd.h>
+#endif
+
+/*
+ * Platform-dependent variable types
+ */
+#ifdef WINSOCK
+	typedef SOCKET socket_t;
+	typedef signed long ssize_t;
+#else
+	typedef int socket_t;
+#endif
+
+/*
+ * Prints out the file name, line number, and function name before log message
+ */
+#define SAMLOG(format, ...) sam_log("%s:%d:%s: " \
+	format, __FILE__, __LINE__, __func__, __VA_ARGS__)
+
+/*
+ * This is the same as above, except that it doesn't accept any va args
+ */
+#define SAMLOGS(str) sam_log("%s:%d:%s: " str, __FILE__, __LINE__, __func__)
+
+/*
+ * Set this to true if you want the raw SAM commands to be printed on stdout
+ * (useful for debugging)
+ */
+#define SAM_WIRETAP false
+#if SAM_WIRETAP
+	#include <ctype.h>
+#endif
+
+#endif  /* PLATFORM_H */
diff --git a/apps/sam/c/inc/sam.h b/apps/sam/c/inc/sam.h
new file mode 100644
index 0000000000..88af116945
--- /dev/null
+++ b/apps/sam/c/inc/sam.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the author nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SAM_H
+#define SAM_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Lengths
+ */
+#define SAM_CMD_LEN 128  /* the maximum length a SAM command can be */
+#define SAM_DGRAM_PAYLOAD_MAX 31774  /* max size of a single datagram packet */
+#define SAM_ERRMSG_LEN 23  /* the longest message returned from sam_strerror */
+#define SAM_LOGMSG_LEN 256	/* the longest log message */
+#define SAM_NAME_LEN 256  /* the longest `name' arg for naming lookup callback*/
+#define SAM_STREAM_PAYLOAD_MAX 32768  /* max size of a single stream packet */
+#define SAM_PKCMD_LEN (SAM_PUBKEY_LEN + SAM_CMD_LEN)/*a public key SAM command*/
+#define SAM_PUBKEY_LEN 517  /* it's actually 516, but +1 for '\0' */
+#define SAM_REPLY_LEN 1024  /* the maximum length a SAM non-data reply can be */
+
+/*
+ * Shorten some standard variable types
+ */
+typedef signed char schar_t;
+typedef unsigned char uchar_t;
+typedef unsigned int uint_t;
+typedef unsigned long ulong_t;
+typedef unsigned short ushort_t;
+
+typedef enum {SAM_STREAM, SAM_DGRAM, SAM_RAW} sam_conn_t;  /* SAM connection */
+
+typedef char sam_pubkey_t[SAM_PUBKEY_LEN];  /* base 64 public key */
+
+typedef struct {
+	void *data;
+	size_t size;
+} sam_sendq_t;  /* sending queue to encourage large stream packet sizes */
+
+typedef int_fast32_t sam_sid_t;  /* stream id number */
+
+typedef enum {  /* see sam_strerror() for detailed descriptions of these */
+	/* error codes from SAM itself (SAM_OK is not an actual "error") */
+	SAM_OK, SAM_CANT_REACH_PEER, SAM_DUPLICATED_DEST, SAM_I2P_ERROR,
+	SAM_INVALID_KEY, SAM_KEY_NOT_FOUND, SAM_PEER_NOT_FOUND, SAM_TIMEOUT,
+	SAM_UNKNOWN,
+	/* error codes from libsam */
+	SAM_BAD_VERSION, SAM_CALLBACKS_UNSET, SAM_SOCKET_ERROR, SAM_TOO_BIG
+} samerr_t;
+
+/*
+ * Public functions
+ */
+
+/* SAM controls */
+extern bool		sam_close(void);
+extern samerr_t	sam_connect(const char *samhost, uint16_t samport,
+					const char *destname, sam_conn_t style, uint_t tunneldepth);
+extern void		sam_naming_lookup(const char *name);
+extern bool		sam_read_buffer(void);
+extern char		*sam_strerror(samerr_t code);
+/* SAM controls - callbacks */
+extern void		(*sam_diedback)(void);
+extern void		(*sam_logback)(char *str);
+extern void		(*sam_namingback)(char *name, sam_pubkey_t pubkey,
+					samerr_t result);
+
+/* Stream commands */
+extern void		sam_stream_close(sam_sid_t stream_id);
+extern sam_sid_t sam_stream_connect(const sam_pubkey_t dest);
+extern samerr_t	sam_stream_send(sam_sid_t stream_id, const void *data,
+					size_t size);
+/* Stream commands - callbacks */
+extern void		(*sam_closeback)(sam_sid_t stream_id, samerr_t reason);
+extern void		(*sam_connectback)(sam_sid_t stream_id, sam_pubkey_t dest);
+extern void		(*sam_databack)(sam_sid_t stream_id, void *data, size_t size);
+extern void		(*sam_statusback)(sam_sid_t stream_id, samerr_t result);
+
+/* Stream send queue */
+extern samerr_t	sam_sendq_add(sam_sendq_t *sendq, const void *data,
+					size_t dsize);
+extern sam_sendq_t *sam_sendq_create(void);
+extern void		sam_sendq_send(sam_sendq_t *sendq, sam_sid_t stream_id);
+
+/* Datagram commands */
+extern samerr_t	sam_dgram_send(const sam_pubkey_t dest, const void *data,
+					size_t size);
+
+/* Datagram commands - callbacks */
+extern void		(*sam_dgramback)(sam_pubkey_t dest, void *data, size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+#endif  /* SAM_H */
diff --git a/apps/sam/c/inc/snprintf.h b/apps/sam/c/inc/snprintf.h
new file mode 100644
index 0000000000..9faf1961a0
--- /dev/null
+++ b/apps/sam/c/inc/snprintf.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the author nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Note: The snprintf.c file retains its original license (at the top of
+ * snprintf.c)
+ */
+
+#ifndef SNPRINTF_H
+#define SNPRINTF_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdarg.h>  /* for va_list */
+int snprintf (char *str, size_t count, const char *fmt, ...);
+int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
+
+#ifdef __cplusplus
+}
+#endif
+#endif  /* SNPRINTF_H */
diff --git a/apps/sam/c/inc/strl.h b/apps/sam/c/inc/strl.h
new file mode 100644
index 0000000000..fece64212d
--- /dev/null
+++ b/apps/sam/c/inc/strl.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the author nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Note: The strl.c file retains its original license (at the top of strl.c)
+ */
+
+#ifndef STRL_H
+#define STRL_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern size_t strlcat(char *dst, const char *src, size_t siz);
+extern size_t strlcpy(char *dst, const char *src, size_t siz);
+
+#ifdef __cplusplus
+}
+#endif
+#endif  /* STRL_H */
diff --git a/apps/sam/c/src/sam.c b/apps/sam/c/src/sam.c
new file mode 100644
index 0000000000..262e86ab17
--- /dev/null
+++ b/apps/sam/c/src/sam.c
@@ -0,0 +1,1047 @@
+/*
+ * Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the author nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "platform.h"
+#include "sam.h"
+
+static bool			sam_hello(void);
+static void			sam_log(const char *format, ...);
+static void			sam_parse(char *s);
+static ssize_t		sam_read1(char *buf, size_t n);
+static ssize_t		sam_read2(void *buf, size_t n);
+static bool			sam_readable(void);
+static samerr_t		sam_session_create(const char *destname, sam_conn_t style,
+						uint_t tunneldepth);
+static bool			sam_socket_connect(const char *host, uint16_t port);
+static bool			sam_socket_resolve(const char *hostname, char *ipaddr);
+static ssize_t		sam_write(const void *buf, size_t n);
+
+/*
+ * Callback functions
+ * Note: if you add a new callback be sure to check for non-NULL in sam_connect
+ */
+/* a peer closed the connection */
+void (*sam_closeback)(sam_sid_t stream_id, samerr_t reason) = NULL;
+/* a peer connected to us */
+void (*sam_connectback)(sam_sid_t stream_id, sam_pubkey_t dest) = NULL;
+/* a peer sent some stream data (`data' MUST be freed) */
+void (*sam_databack)(sam_sid_t stream_id, void *data, size_t size) = NULL;
+/* a peer sent some datagram data (`data' MUST be freed) */
+void (*sam_dgramback)(sam_pubkey_t dest, void *data, size_t size) = NULL;
+/* we lost the connection to the SAM host */
+void (*sam_diedback)(void) = NULL;
+/* logging callback */
+void (*sam_logback)(char *str) = NULL;
+/* naming lookup reply - `pubkey' will be NULL if `result' isn't SAM_OK */
+void (*sam_namingback)(char *name, sam_pubkey_t pubkey,
+	samerr_t result) = NULL;
+/* our connection to a peer has completed */
+void (*sam_statusback)(sam_sid_t stream_id, samerr_t result) = NULL;
+
+static socket_t samd;  /* The socket descriptor we're using for
+								communications with SAM */
+static bool samd_connected = false;  /* Whether we're connected with SAM */
+
+/*
+ * Closes the connection to the SAM host
+ *
+ * Returns: true on success, false on failure
+ */
+bool sam_close(void)
+{
+	if (!samd_connected)
+		return true;
+
+#ifdef WINSOCK
+	if (closesocket(samd) == 0) {
+		samd_connected = false;
+		return true;
+#else
+	if (close(samd) == 0) {
+		samd_connected = false;
+		return true;
+#endif
+	} else {
+		SAMLOG("Failed closing the SAM connection (%s)", strerror(errno));
+		return false;
+	}
+}
+
+/*
+ * Connects to the SAM host
+ *
+ * samhost - SAM host
+ * samport - SAM port
+ * destname - destination name for this program, or TRANSIENT for a random dest
+ * tunneldepth - length of the I2P tunnels created by this program (longer is
+ *		more anonymous, but slower)
+ *
+ * Returns: true on success, false on failure
+ */
+samerr_t sam_connect(const char *samhost, uint16_t samport,
+	const char *destname, sam_conn_t style, uint_t tunneldepth)
+{
+	samerr_t rc;
+
+	if (style == SAM_STREAM) {
+		if (sam_closeback == NULL || sam_connectback == NULL ||
+				sam_databack == NULL || sam_diedback == NULL ||
+				sam_logback == NULL || sam_namingback == NULL ||
+				sam_statusback == NULL) {
+			SAMLOGS("Please set callback functions before connecting");
+			return SAM_CALLBACKS_UNSET;
+		}
+	} else if (style == SAM_DGRAM) {
+		if (sam_dgramback == NULL || sam_diedback == NULL ||
+				sam_logback == NULL || sam_namingback == NULL) {
+			SAMLOGS("Please set callback functions before connecting");
+			return SAM_CALLBACKS_UNSET;
+		}
+	} else if (style == SAM_RAW) {
+		assert(false);  /* not implemented yet */
+	} else {
+		assert(false);  /* unknown style */
+	}
+
+#ifdef WINSOCK
+	/*
+	 * Is Windows retarded or what?
+	 */
+	WORD wVersionRequested;
+	WSADATA wsaData;
+	int err;
+
+	wVersionRequested = MAKEWORD(1, 1);
+	err = WSAStartup(wVersionRequested, &wsaData);
+	if (err != 0) {
+		SAMLOGS("WSAStartup() failed");
+    	return SAM_SOCKET_ERROR;
+	}
+	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1 ) {
+		SAMLOGS("Bad WinSock version");
+    	return SAM_SOCKET_ERROR;
+	}
+#endif
+
+	if (!sam_socket_connect(samhost, samport)) {
+		SAMLOG("Couldn't connect to SAM at %s:%u (%s)",
+			samhost, samport, strerror(errno));
+		SAMLOGS("Is your I2P router running?");
+		return SAM_SOCKET_ERROR;
+	}
+
+	if (!sam_hello())
+		return SAM_BAD_VERSION;
+
+	rc = sam_session_create(destname, style, tunneldepth);
+	if (rc != SAM_OK)
+		return rc;
+
+	SAMLOG("SAM connection open to %s:%u", samhost, samport);
+	return SAM_OK;
+}
+
+/*
+ * Sends data to a destination in a datagram
+ *
+ * dest - base 64 destination of who we're sending to
+ * data - the data we're sending
+ * size - the size of the data
+ *
+ * Returns: true on success, false on failure
+ */
+samerr_t sam_dgram_send(const sam_pubkey_t dest, const void *data, size_t size)
+{
+	char cmd[SAM_PKCMD_LEN];
+
+	if (size < 1 || size > SAM_DGRAM_PAYLOAD_MAX) {
+#ifdef NO_Z_FORMAT
+		SAMLOG("Invalid data send size (%u bytes)", size);
+#else
+		SAMLOG("Invalid data send size (%z bytes)", size);
+#endif
+		return SAM_TOO_BIG;
+	}
+#ifdef NO_Z_FORMAT
+	snprintf(cmd, sizeof cmd, "DATAGRAM SEND DESTINATION=%s SIZE=%u\n",
+		dest, size);
+#else
+	snprintf(cmd, sizeof cmd, "DATAGRAM SEND DESTINATION=%s SIZE=%z\n",
+		dest, size);
+#endif
+	sam_write(cmd, strlen(cmd));
+	sam_write(data, size);
+
+	return SAM_OK;
+}
+
+/*
+ * Sends the first SAM handshake command and checks the reply
+ * (call sam_session_create() after this)
+ *
+ * Returns: true on success, false on reply failure
+ */
+static bool sam_hello(void)
+{
+#define SAM_HELLO_CMD	"HELLO VERSION MIN=1.0 MAX=1.0\n"
+#define SAM_HELLO_REPLY	"HELLO REPLY RESULT=OK VERSION=1.0"
+	char reply[SAM_REPLY_LEN];
+
+	sam_write(SAM_HELLO_CMD, strlen(SAM_HELLO_CMD));
+	sam_read1(reply, SAM_REPLY_LEN);
+	if (strncmp(reply, SAM_HELLO_REPLY, strlen(SAM_HELLO_REPLY)) == 0)
+		return true;
+	else {
+		SAMLOGS("HELLO failed");
+		return false;
+	}
+}
+
+/*
+ * Converts va arg logs into a standard string and runs the callback
+ */
+static void sam_log(const char *format, ...)
+{
+	va_list ap;
+	char s[SAM_LOGMSG_LEN];
+
+	va_start(ap, format);
+	vsnprintf(s, sizeof s, format, ap);
+	va_end(ap);
+	/*strlcat(s, "\n", sizeof s);*/
+	sam_logback(s);
+
+	return;
+}
+
+/*
+ * Performs a base 64 public key lookup on the specified `name'
+ *
+ * name - name to lookup, or ME to lookup our own name
+ */
+void sam_naming_lookup(const char *name)
+{
+	char cmd[SAM_CMD_LEN];
+
+	snprintf(cmd, sizeof cmd, "NAMING LOOKUP NAME=%s\n", name);
+	sam_write(cmd, strlen(cmd));
+
+	return;
+}
+
+/*
+ * Parses incoming data and calls the appropriate callback
+ *
+ * s - string of data that we read (read past tense)
+ */
+static void sam_parse(char *s)
+{
+#define SAM_DGRAM_RECEIVED_REPLY "DATAGRAM RECEIVED"
+#define SAM_NAMING_REPLY "NAMING REPLY"
+#define SAM_NAMING_REPLY_OK "NAMING REPLY RESULT=OK"
+#define SAM_NAMING_REPLY_IK "NAMING REPLY RESULT=INVALID_KEY"
+#define SAM_NAMING_REPLY_KNF "NAMING REPLY RESULT=KEY_NOT_FOUND"
+#define SAM_STREAM_CLOSED_REPLY "STREAM CLOSED"
+#define SAM_STREAM_CONNECTED_REPLY "STREAM CONNECTED"
+#define SAM_STREAM_RECEIVED_REPLY "STREAM RECEIVED"
+#define SAM_STREAM_STATUS_REPLY "STREAM STATUS"
+#define SAM_STREAM_STATUS_REPLY_OK "STREAM STATUS RESULT=OK"
+#define SAM_STREAM_STATUS_REPLY_CRP "STREAM STATUS RESULT=CANT_REACH_PEER"
+#define SAM_STREAM_STATUS_REPLY_I2E "STREAM STATUS RESULT=I2P_ERROR"
+#define SAM_STREAM_STATUS_REPLY_IK "STREAM STATUS RESULT=INVALID_KEY"
+#define SAM_STREAM_STATUS_REPLY_TO "STREAM STATUS RESULT=TIMEOUT"
+
+	if (strncmp(s, SAM_DGRAM_RECEIVED_REPLY,
+			strlen(SAM_DGRAM_RECEIVED_REPLY)) == 0) {
+		char *p;
+		sam_pubkey_t dest;
+		size_t size;
+		void *data;
+
+		p = strchr(s, '=');  /* DESTINATION= */
+		assert(p != NULL);
+		p++;
+		strlcpy(dest, p, sizeof dest);
+		p = strchr(p, '=');  /* SIZE= */
+		assert(p != NULL);
+		p++;
+		size = strtol(p, NULL, 10);
+		assert(size != 0);
+		data = malloc(size + 1);  /* +1 for NUL termination, so when we are
+									receiving a string it will just work and it
+									won't be necessary to send NUL.  When binary
+									data is sent, the extra NUL character will
+									just be ignored by the client program,
+									because it is not added to the size */
+		if (sam_read2(data, size) != -1) {
+			p = data + size;
+			*p = '\0';  /* see above NUL note */
+			sam_dgramback(dest, data, size);  /* `data' must be freed */
+		} else
+			free(data);
+
+		return;
+
+	} else if (strncmp(s, SAM_NAMING_REPLY, strlen(SAM_NAMING_REPLY)) == 0) {
+		char *p;
+		char *q;
+		char name[SAM_NAME_LEN];
+
+		p = strchr(s, '=');  /* can't use strrchar because of option
+								MESSAGE= */
+		assert(p != NULL);  /* RESULT= */
+		p++;
+		p = strchr(p, '=');  /* NAME= */
+		assert(p != NULL);
+		p++;
+
+		if (strncmp(s, SAM_NAMING_REPLY_OK, strlen(SAM_NAMING_REPLY_OK)) == 0) {
+			sam_pubkey_t pubkey;
+
+			q = strchr(p, ' ');  /* ' 'VAL.. */
+			assert(q != NULL);
+			*q = '\0';
+			q++;
+			q = strchr(q, '=');  /* VALUE= */
+			assert(q != NULL);
+			q++;
+			strlcpy(name, p, sizeof name);
+			strlcpy(pubkey, q, sizeof pubkey);
+			sam_namingback(name, pubkey, SAM_OK);
+
+		} else if (strncmp(s, SAM_NAMING_REPLY_IK,
+				strlen(SAM_NAMING_REPLY_IK)) == 0) {
+			q = strchr(p, ' ');  /* ' 'MES.. (optional) */
+			if (q != NULL)
+				*q = '\0';
+			strlcpy(name, p, sizeof name);
+			sam_namingback(name, NULL, SAM_INVALID_KEY);
+
+		} else if (strncmp(s, SAM_NAMING_REPLY_KNF,
+				strlen(SAM_NAMING_REPLY_KNF)) == 0) {
+			q = strchr(p, ' ');  /* ' 'MES.. (optional) */
+			if (q != NULL)
+				*q = '\0';
+			strlcpy(name, p, sizeof name);
+			sam_namingback(name, NULL, SAM_KEY_NOT_FOUND);
+
+		} else {
+			q = strchr(p, ' ');  /* ' 'MES.. (optional) */
+			if (q != NULL)
+				*q = '\0';
+			strlcpy(name, p, sizeof name);
+			sam_namingback(name, NULL, SAM_UNKNOWN);
+		}
+
+		return;
+
+	} else if (strncmp(s, SAM_STREAM_CLOSED_REPLY,
+			strlen(SAM_STREAM_CLOSED_REPLY)) == 0) {
+		char *p;
+		sam_sid_t stream_id;
+
+		p = strchr(s, '=');  /* can't use strrchar because of option MESSAGE= */
+		assert(p != NULL);  /* ID= */
+		p++;
+		stream_id = strtol(p, NULL, 10);
+		assert(stream_id != 0);
+		p = strchr(p, '=');  /* RESULT= */
+		assert(p != NULL);
+		p++;
+		if (strncmp(p, "OK", strlen("OK")) == 0)
+			sam_closeback(stream_id, SAM_OK);
+		else if (strncmp(p, "CANT_REACH_PEER", strlen("CANT_REACH_PEER")) == 0)
+			sam_closeback(stream_id, SAM_CANT_REACH_PEER);
+		else if (strncmp(p, "I2P_ERROR", strlen("I2P_ERROR")) == 0)
+			sam_closeback(stream_id, SAM_I2P_ERROR);
+		else if (strncmp(p, "PEER_NOT_FOUND", strlen("PEER_NOT_FOUND")) == 0)
+			sam_closeback(stream_id, SAM_PEER_NOT_FOUND);
+		else if (strncmp(p, "TIMEOUT", strlen("TIMEOUT")) == 0)
+			sam_closeback(stream_id, SAM_TIMEOUT);
+		else
+			sam_closeback(stream_id, SAM_UNKNOWN);
+
+		return;
+
+	} else if (strncmp(s, SAM_STREAM_CONNECTED_REPLY,
+			strlen(SAM_STREAM_CONNECTED_REPLY)) == 0) {
+		char *p;
+		sam_sid_t stream_id;
+		sam_pubkey_t dest;
+
+		p = strrchr(s, '=');  /* ID= */
+		assert(p != NULL);
+		*p = '\0';
+		p++;
+		stream_id = strtol(p, NULL, 10);
+		assert(stream_id != 0);
+		p = strstr(s, "N=");  /* DESTINATION= */
+		p += 2;
+		strlcpy(dest, p, sizeof dest);
+		sam_connectback(stream_id, dest);
+	
+		return;
+
+	} else if (strncmp(s, SAM_STREAM_RECEIVED_REPLY,
+			strlen(SAM_STREAM_RECEIVED_REPLY)) == 0) {
+		char *p;
+		sam_sid_t stream_id;
+		size_t size;
+		void *data;
+
+		p = strrchr(s, '=');  /* SIZE= */
+		assert(p != NULL);
+		p++;
+		size = strtol(p, NULL, 10);
+		assert(size != 0);
+		p -= 6;
+		*p = '\0';
+		p = strrchr(s, '=');  /* ID= */
+		assert(p != NULL);
+		p++;
+		stream_id = strtol(p, NULL, 10);
+		assert(stream_id != 0);
+		data = malloc(size + 1);  /* +1 for NUL termination, so when we are
+									receiving a string it will just work and it
+									won't be necessary to send NUL.  When binary
+									data is sent, the extra NUL character will
+									just be ignored by the client program,
+									because it is not added to the size */
+		if (sam_read2(data, size) != -1) {
+			p = data + size;
+			*p = '\0';  /* see above NUL note */
+			sam_databack(stream_id, data, size);  /* `data' must be freed */
+		} else
+			free(data);
+
+		return;
+
+	} else if (strncmp(s, SAM_STREAM_STATUS_REPLY,
+			strlen(SAM_STREAM_STATUS_REPLY)) == 0) {
+		char *p;
+		sam_sid_t stream_id;
+
+		p = strchr(s, '=');  /* can't use strrchar because of option MESSAGE= */
+		assert(p != NULL);  /* RESULT= */
+		p++;
+		p = strchr(p, '=');  /* ID= */
+		assert(p != NULL);
+		p++;
+		stream_id = strtol(p, NULL, 10);
+		assert(stream_id != 0);
+		if (strncmp(s, SAM_STREAM_STATUS_REPLY_OK,
+				strlen(SAM_STREAM_STATUS_REPLY_OK)) == 0)
+			sam_statusback(stream_id, SAM_OK);
+		else if (strncmp(s, SAM_STREAM_STATUS_REPLY_CRP,
+				strlen(SAM_STREAM_STATUS_REPLY_CRP)) == 0)
+			sam_statusback(stream_id, SAM_CANT_REACH_PEER);
+		else if (strncmp(s, SAM_STREAM_STATUS_REPLY_I2E,
+				strlen(SAM_STREAM_STATUS_REPLY_I2E)) == 0)
+			sam_statusback(stream_id, SAM_I2P_ERROR);
+		else if (strncmp(s, SAM_STREAM_STATUS_REPLY_IK,
+				strlen(SAM_STREAM_STATUS_REPLY_IK)) == 0)
+			sam_statusback(stream_id, SAM_INVALID_KEY);
+		else if (strncmp(s, SAM_STREAM_STATUS_REPLY_TO,
+				strlen(SAM_STREAM_STATUS_REPLY_TO)) == 0)
+			sam_statusback(stream_id, SAM_TIMEOUT);
+		else
+			sam_statusback(stream_id, SAM_UNKNOWN);
+
+		return;
+
+	} else
+		SAMLOG("Unknown SAM command received: %s", s);
+
+	return;
+}
+
+/*
+ * Reads and callbacks everything in the SAM network buffer until it is clear
+ *
+ * Returns: true if we read anything, or false if nothing was there
+ */
+bool sam_read_buffer(void)
+{
+	bool read_something = false;
+	char reply[SAM_REPLY_LEN];
+
+	if (sam_readable()) {
+		do {
+			sam_read1(reply, SAM_REPLY_LEN);
+			read_something = true;
+			sam_parse(reply);
+		} while (sam_readable());
+	}
+
+	return read_something;
+}
+
+/*
+ * SAM command reader
+ *
+ * Reads up to `n' bytes of a SAM response into the buffer `buf' OR (preferred)
+ * up to the first '\n' which is replaced with a NUL.  The resulting buffer will
+ * always be NUL-terminated.
+ * Warning: A full buffer will cause the last character read to be lost (it is
+ * 		replaced with a NUL in the buffer).
+ *
+ * buf - buffer
+ * n - number of bytes available in `buf', the buffer
+ *
+ * Returns: number of bytes read, or -1 on error
+ */
+static ssize_t sam_read1(char *buf, size_t n)
+{
+	size_t nleft;
+	ssize_t nread;
+	char *p;
+
+	*buf = '\0';  /* this forces `buf' to be a string even if there is a
+					 sam_read1 error return */
+	if (!samd_connected) {
+		SAMLOGS("Cannot read from SAM because the SAM connection is closed");
+		sam_diedback();
+		return -1;
+	}
+	assert(n > 0);
+	p = buf;
+	nleft = n;
+	while (nleft > 0) {
+		nread = recv(samd, p, 1, 0);
+		if (nread == -1) {
+			if (errno == EINTR)  /* see Unix Network Pgming vol 1, Sec. 5.9 */
+				continue;
+			else {
+				SAMLOG("recv() failed: %s", strerror(errno));
+				sam_close();
+				sam_diedback();
+				return -1;
+			}
+		} else if (nread == 0) {  /* EOF */
+			SAMLOGS("Connection closed by the SAM host");
+			sam_close();
+			sam_diedback();
+			return -1;
+		}
+		assert(nread == 1);
+		nleft--;
+		if (*p == '\n') {  /* end of SAM response */
+			*p = '\0';
+#if SAM_WIRETAP
+			printf("<<<< %s\n", buf);
+#endif
+			return n - nleft;
+		}
+		p++;
+	}
+	/* buffer full, which is considered an error */
+	SAMLOGS("sam_read1() buf full");
+	p--;
+	*p = '\0';  /* yes, this causes the loss of the last character */
+
+	return n - nleft;  /* return >= 0 */
+}
+
+/*
+ * SAM payload reader
+ *
+ * Reads exactly `n' bytes of a SAM data payload into the buffer `buf'
+ *
+ * buf - buffer
+ * n - number of bytes available in `buf'
+ *
+ * Returns: number of bytes read, or -1 on error
+ */
+static ssize_t sam_read2(void *buf, size_t n)
+{
+	size_t nleft;
+	ssize_t nread;
+	void *p;
+
+	if (!samd_connected) {
+		SAMLOGS("Cannot read from SAM because the SAM connection is closed");
+		sam_diedback();
+		return -1;
+	}
+	assert(n > 0);
+	p = buf;
+	nleft = n;
+	while (nleft > 0) {
+		nread = recv(samd, p, nleft, 0);
+		if (nread == -1) {
+			if (errno == EINTR)  /* see Unix Network Pgming vol 1, Sec. 5.9 */
+				continue;
+			else {
+				SAMLOG("recv() failed: %s", strerror(errno));
+				sam_close();
+				sam_diedback();
+				return -1;
+			}
+		} else if (nread == 0) {  /* EOF */
+			SAMLOGS("Connection closed by the SAM host");
+			sam_close();
+			sam_diedback();
+			return -1;
+		}
+		nleft -= nread;
+		p += nread;
+	}
+#if SAM_WIRETAP
+	printf("<<<< (read2() %d bytes)\n", n);
+#endif
+	assert(nleft == 0);/*  <---\                       */
+	return n - nleft;  /* should be equal to initial n */
+}
+
+/*
+ * Checks if there is incoming data waiting to be read from the SAM connection
+ *
+ * Returns: true if data is waiting, false otherwise
+ */
+static bool sam_readable(void)
+{
+	fd_set rset;  /* set of readable descriptors */
+	struct timeval tv;
+	int rc;
+
+	if (!samd_connected) {
+		SAMLOGS("Cannot read from SAM because the SAM connection is closed");
+		sam_diedback();
+		return false;
+	}
+	/* it seems like there should be a better way to do this (i.e. not select)*/
+	FD_ZERO(&rset);
+	FD_SET(samd, &rset);
+	tv.tv_sec = 0;
+	tv.tv_usec = 10;
+	rc = select(samd + 1, &rset, NULL, NULL, &tv);
+	if (rc == 0)
+		return false;
+	else if (rc > 0)
+		return true;
+	else {
+		SAMLOG("select() failed: %s", strerror(errno));
+		return false;
+	}
+}
+
+/*
+ * Adds data to the send queue
+ *
+ * sendq - the send queue
+ * data - data to add
+ * dsize - the size of the data
+ *
+ * Returns: true on success, false on error
+ */
+samerr_t sam_sendq_add(sam_sendq_t *sendq, const void *data, size_t dsize)
+{
+	assert(dsize >= 0);
+	if (dsize == 0) {
+		SAMLOGS("dsize is 0 - adding nothing");
+		return SAM_OK;
+	} else if (sendq->size + dsize > SAM_STREAM_PAYLOAD_MAX) {
+		SAMLOGS("The queue size would exceed the maximum SAM payload size -" \
+			" will not add to the queue");
+		return SAM_TOO_BIG;
+	}
+
+	sendq->data = realloc(sendq->data, sendq->size + dsize);
+	memcpy(sendq->data + sendq->size, data, dsize);
+	sendq->size += dsize;
+
+	return SAM_OK;
+}
+
+/*
+ * Creates a data queue for use with sam_stream_send
+ *
+ * Returns: pointer to the newly created send queue
+ */
+sam_sendq_t *sam_sendq_create(void)
+{
+	sam_sendq_t *sendq;
+
+	sendq = malloc(sizeof(sam_sendq_t));
+	sendq->data = NULL;
+	sendq->size = 0;
+
+	return sendq;
+}
+
+/*
+ * Sends the data in the send queue to the specified stream
+ *
+ * sendq - the send queue
+ * stream_id - stream number to send to
+ */
+void sam_sendq_send(sam_sendq_t *sendq, sam_sid_t stream_id)
+{
+	sam_stream_send(stream_id, sendq->data, sendq->size);
+	free(sendq);
+
+	return;
+}
+
+/*
+ * Sends the second SAM handshake command and checks the reply
+ *
+ * destname - destination name for this program, or TRANSIENT to create a
+ * 		random temporary destination
+ * tunneldepth - length of the I2P tunnels created by this program
+ *
+ * Returns: SAM error code
+ */
+static samerr_t sam_session_create(const char *destname, sam_conn_t style,
+	uint_t tunneldepth)
+{
+#define SAM_SESSTATUS_REPLY_OK "SESSION STATUS RESULT=OK"
+#define SAM_SESSTATUS_REPLY_DD "SESSION STATUS RESULT=DUPLICATED_DEST"
+#define SAM_SESSTATUS_REPLY_I2E "SESSION STATUS RESULT=I2P_ERROR"
+#define SAM_SESSTATUS_REPLY_IK "SESSION STATUS RESULT=INVALID_KEY"
+	char cmd[SAM_CMD_LEN * 2];
+	char reply[SAM_REPLY_LEN];
+
+	if (style == SAM_STREAM) {
+		snprintf(cmd, sizeof cmd,
+			"SESSION CREATE STYLE=STREAM DESTINATION=%s " \
+			"tunnels.depthInbound=%u " \
+			"tunnels.depthOutbound=%u\n",
+			destname, tunneldepth, tunneldepth);
+	} else if (style == SAM_DGRAM) {
+		snprintf(cmd, sizeof cmd,
+			"SESSION CREATE STYLE=DATAGRAM DESTINATION=%s " \
+			"i2cp.messageReliability=BestEffort " \
+			"tunnels.depthInbound=%u " \
+			"tunnels.depthOutbound=%u\n",
+			destname, tunneldepth, tunneldepth);
+	} else { /* SAM_RAW */
+		assert(false);  /* unimplemented */
+	}
+
+	sam_write(cmd, strlen(cmd));
+	sam_read1(reply, SAM_REPLY_LEN);
+	if (strncmp(reply, SAM_SESSTATUS_REPLY_OK,
+			strlen(SAM_SESSTATUS_REPLY_OK)) == 0)
+		return SAM_OK;
+	else if (strncmp(reply, SAM_SESSTATUS_REPLY_DD,
+			strlen(SAM_SESSTATUS_REPLY_DD)) == 0)
+		return SAM_DUPLICATED_DEST;
+	else if (strncmp(reply, SAM_SESSTATUS_REPLY_I2E,
+			strlen(SAM_SESSTATUS_REPLY_I2E)) == 0)
+		return SAM_I2P_ERROR;
+	else if (strncmp(reply, SAM_SESSTATUS_REPLY_IK,
+			strlen(SAM_SESSTATUS_REPLY_IK)) == 0)
+		return SAM_INVALID_KEY;
+	else
+		return SAM_UNKNOWN;
+}
+
+/*
+ * Connects to a remote host and returns a connected descriptor
+ *
+ * host - host name or ip address
+ * port - port number
+ *
+ * Returns: true on sucess, false on error, with errno set
+ */
+bool sam_socket_connect(const char *host, uint16_t port)
+{
+	struct sockaddr_in hostaddr;
+	int rc;
+	char ipaddr[INET_ADDRSTRLEN];
+
+	samd = socket(AF_INET, SOCK_STREAM, 0);
+#ifdef WINSOCK
+	if (samd == INVALID_SOCKET)
+		return false;
+#else
+	if (samd == -1)
+		return false;
+#endif
+	memset(&hostaddr, 0, sizeof hostaddr);
+	hostaddr.sin_family = AF_INET;
+	hostaddr.sin_port = htons(port);
+	if (!sam_socket_resolve(host, ipaddr))
+		return false;
+#ifdef NO_INET_ATON
+	rc = hostaddr.sin_addr.s_addr = inet_addr(ipaddr);
+#elif defined NO_INET_PTON
+	rc = inet_aton(ipaddr, &hostaddr.sin_addr);
+#else
+	rc = inet_pton(AF_INET, ipaddr, &hostaddr.sin_addr);
+#endif
+	if (rc == 0) {
+		errno = EINVAL;
+		return false;
+	} else if (rc == -1)
+		return false;
+
+	if (connect(samd, (struct sockaddr *)&hostaddr, sizeof hostaddr) == -1)
+		return false;
+
+	samd_connected = true;
+	return true;
+}
+
+/*
+ * Perform a DNS lookup on a hostname
+ *
+ * hostname - hostname to resolve
+ * ipaddr - filled with the ip address
+ *
+ * Returns: true on success, false on failure
+ */
+static bool sam_socket_resolve(const char *hostname, char *ipaddr)
+{
+	struct hostent *h;
+	struct in_addr a;
+
+retry:
+#ifdef NO_GETHOSTBYNAME2
+	h = gethostbyname(hostname);
+#else
+	h = gethostbyname2(hostname, AF_INET);
+#endif
+	if (h == NULL) {
+		if (h_errno == TRY_AGAIN) {
+#ifdef WINSOCK
+			Sleep(1000);
+#else
+			sleep(1);
+#endif
+			goto retry;
+		} else {
+			SAMLOG("DNS resolution failed for %s", hostname);
+			errno = ENOENT;
+			return false;
+		}
+	}
+	a.s_addr = ((struct in_addr *)h->h_addr)->s_addr;
+#ifdef NO_INET_NTOP
+	char *tmp;
+	tmp = inet_ntoa(a);
+	strlcpy(ipaddr, tmp, INET_ADDRSTRLEN);  /* inet_ntoa() was very poorly designed */
+	return true;
+#else
+	if (inet_ntop(AF_INET, &a, ipaddr, INET_ADDRSTRLEN) != NULL) {
+		return true;
+	} else {
+		SAMLOG("inet_ntop() failed: %s", strerror(errno));
+		return false;
+	}
+#endif
+}		
+
+/*
+ * Closes the specified stream number
+ *
+ * stream_id - stream number to close
+ */
+void sam_stream_close(sam_sid_t stream_id)
+{
+	char cmd[SAM_CMD_LEN];
+
+#ifdef FAST32_IS_LONG
+	snprintf(cmd, sizeof cmd, "STREAM CLOSE ID=%ld\n", stream_id);
+#else
+	snprintf(cmd, sizeof cmd, "STREAM CLOSE ID=%d\n", stream_id);
+#endif
+	sam_write(cmd, strlen(cmd));
+
+	return;
+}
+
+/*
+ * Opens a new stream connection to public key destination `dest'
+ *
+ * dest - base 64 destination
+ *
+ * Returns: stream id number
+ */
+sam_sid_t sam_stream_connect(const sam_pubkey_t dest)
+{
+	char cmd[SAM_PKCMD_LEN];
+	static sam_sid_t id = 0;
+
+	id++;  /* increment the id for the connection */
+#ifdef FAST32_IS_LONG
+	snprintf(cmd, sizeof cmd, "STREAM CONNECT ID=%ld DESTINATION=%s\n",
+		id, dest);
+#else
+	snprintf(cmd, sizeof cmd, "STREAM CONNECT ID=%d DESTINATION=%s\n",
+		id, dest);
+#endif
+	sam_write(cmd, strlen(cmd));
+
+	return id;
+}
+
+/*
+ * Sends data to a SAM stream
+ *
+ * stream_id - the stream number to send to
+ * data - the data to send
+ * size - the size of the data
+ *
+ * Returns: true on success, false on failure
+ */
+samerr_t sam_stream_send(sam_sid_t stream_id, const void *data, size_t size)
+{
+	char cmd[SAM_CMD_LEN];
+
+	if (size < 1 || size > SAM_STREAM_PAYLOAD_MAX) {
+#ifdef NO_Z_FORMAT
+		SAMLOG("Invalid data send size (%u bytes) for stream %d",
+			size, stream_id);
+#else
+		SAMLOG("Invalid data send size (%z bytes) for stream %d",
+			size, stream_id);
+#endif
+		return SAM_TOO_BIG;
+	}
+#ifdef NO_Z_FORMAT
+	#ifdef FAST32_IS_LONG
+		snprintf(cmd, sizeof cmd, "STREAM SEND ID=%ld SIZE=%u\n",
+			stream_id, size);
+	#else
+		snprintf(cmd, sizeof cmd, "STREAM SEND ID=%d SIZE=%u\n",
+			stream_id, size);
+	#endif
+#else
+	snprintf(cmd, sizeof cmd, "STREAM SEND ID=%d SIZE=%z\n",
+		stream_id, size);
+#endif
+	sam_write(cmd, strlen(cmd));
+	sam_write(data, size);
+
+	return SAM_OK;
+}
+
+/*
+ * Converts a SAM error code into a short error message
+ *
+ * code - the error code
+ *
+ * Returns: error string
+ */
+char *sam_strerror(samerr_t code)
+{
+	switch (code) {
+		case SAM_OK:				/* Operation completed succesfully */
+			return "Ok";
+		case SAM_CANT_REACH_PEER:	/* The peer exists, but cannot be reached */
+			return "Can't reach peer";
+		case SAM_DUPLICATED_DEST:	/* The specified Destination is already in
+										use by another SAM instance */
+			return "Duplicated destination";
+		case SAM_I2P_ERROR:			/* A generic I2P error (e.g. I2CP
+										disconnection, etc.) */
+			return "I2P error";
+		case SAM_INVALID_KEY:		/* The specified key is not valid (bad
+										format, etc.) */
+			return "Invalid key";
+		case SAM_KEY_NOT_FOUND:		/* The naming system can't resolve the
+										given name */
+			return "Key not found";
+		case SAM_PEER_NOT_FOUND:	/* The peer cannot be found on the network*/
+			return "Peer not found";
+		case SAM_TIMEOUT:			/* Timeout while waiting for an event (e.g.
+										peer answer) */
+			return "Timeout";
+		/*
+		 * SAM_UNKNOWN deliberately left out (goes to default)
+		 */
+		case SAM_BAD_VERSION:		/* sam_hello() had an unexpected reply */
+			return "Bad SAM version";
+		case SAM_CALLBACKS_UNSET:	/* Some callbacks are still set to NULL */
+			return "Callbacks unset";
+		case SAM_SOCKET_ERROR:		/* TCP/IP connection to the SAM host:port
+										failed */
+			return "Socket error";
+		case SAM_TOO_BIG:			/* A function was passed too much data */
+			return "Data size too big";
+		default:					/* An unexpected error happened */
+			return "Unknown error";
+	}
+}
+
+/*
+ * Sends `n' bytes to the SAM host
+ *
+ * buf - buffer with the data in it
+ * n - bytes to send
+ *
+ * Returns: `n', or -1 on error
+ */
+static ssize_t sam_write(const void *buf, size_t n)
+{
+	size_t nleft;
+	ssize_t nwritten;
+	const char *p;
+
+	if (!samd_connected) {
+		SAMLOGS("Cannot write to SAM because the SAM connection is closed");
+		sam_diedback();
+		return -1;
+	}
+#if SAM_WIRETAP
+	const uchar_t *cp = buf;
+	for (size_t x = 0; x < n; x++) {
+		if (isprint(cp[x]))
+			printf("%c,", cp[x]);
+		else
+			printf("%03d,", cp[x]);
+	}
+	printf("\n");
+#endif
+	p = buf;
+	nleft = n;
+	while (nleft > 0) {
+		nwritten = send(samd, p, nleft, 0);
+		if (nwritten <= 0) {
+			if (errno == EINTR)  /* see Unix Network Pgming vol 1, Sec. 5.9 */
+				continue;
+			else {
+				SAMLOG("send() failed: %s", strerror(errno));
+				sam_close();
+				sam_diedback();
+				return -1;
+			}
+		}
+		nleft -= nwritten;
+		p += nwritten;
+	}
+#if SAM_WIRETAP
+	printf(">>>> (write() %d bytes)\n", n);
+#endif
+
+	return n;
+}
diff --git a/apps/sam/c/src/snprintf.c b/apps/sam/c/src/snprintf.c
new file mode 100644
index 0000000000..1f407a4f95
--- /dev/null
+++ b/apps/sam/c/src/snprintf.c
@@ -0,0 +1,851 @@
+/*
+ * Copyright Patrick Powell 1995
+ * This code is based on code written by Patrick Powell (papowell@astart.com)
+ * It may be used for any purpose as long as this notice remains intact
+ * on all source code distributions
+ */
+
+/**************************************************************
+ * Original:
+ * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
+ * A bombproof version of doprnt (dopr) included.
+ * Sigh.  This sort of thing is always nasty do deal with.  Note that
+ * the version here does not include floating point...
+ *
+ * snprintf() is used instead of sprintf() as it does limit checks
+ * for string length.  This covers a nasty loophole.
+ *
+ * The other functions are there to prevent NULL pointers from
+ * causing nast effects.
+ *
+ * More Recently:
+ *  Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
+ *  This was ugly.  It is still ugly.  I opted out of floating point
+ *  numbers, but the formatter understands just about everything
+ *  from the normal C string format, at least as far as I can tell from
+ *  the Solaris 2.5 printf(3S) man page.
+ *
+ *  Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
+ *    Ok, added some minimal floating point support, which means this
+ *    probably requires libm on most operating systems.  Don't yet
+ *    support the exponent (e,E) and sigfig (g,G).  Also, fmtint()
+ *    was pretty badly broken, it just wasn't being exercised in ways
+ *    which showed it, so that's been fixed.  Also, formated the code
+ *    to mutt conventions, and removed dead code left over from the
+ *    original.  Also, there is now a builtin-test, just compile with:
+ *           gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
+ *    and run snprintf for results.
+ * 
+ *  Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
+ *    The PGP code was using unsigned hexadecimal formats. 
+ *    Unfortunately, unsigned formats simply didn't work.
+ *
+ *  Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
+ *    The original code assumed that both snprintf() and vsnprintf() were
+ *    missing.  Some systems only have snprintf() but not vsnprintf(), so
+ *    the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
+ *
+ *  Andrew Tridgell (tridge@samba.org) Oct 1998
+ *    fixed handling of %.0f
+ *    added test for HAVE_LONG_DOUBLE
+ *
+ *  Russ Allbery <rra@stanford.edu> 2000-08-26
+ *    fixed return value to comply with C99
+ *    fixed handling of snprintf(NULL, ...)
+ *
+ *  Hrvoje Niksic <hniksic@arsdigita.com> 2000-11-04
+ *    include <stdio.h> for NULL.
+ *    added support for long long.
+ *    don't declare argument types to (v)snprintf if stdarg is not used.
+ *
+ **************************************************************/
+
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+
+#ifndef NULL
+# define NULL 0
+#endif
+
+/* varargs declarations: */
+
+#include <stdarg.h>
+#define HAVE_STDARGS    /* let's hope that works everywhere (mj) */
+#define VA_LOCAL_DECL   va_list ap
+#define VA_START(f)     va_start(ap, f)
+#define VA_SHIFT(v,t)  ;   /* no-op for ANSI */
+#define VA_END          va_end(ap)
+
+#ifdef HAVE_LONG_DOUBLE
+#define LDOUBLE long double
+#else
+#define LDOUBLE double
+#endif
+
+#ifdef HAVE_LONG_LONG
+# define LLONG long long
+#else
+# define LLONG long
+#endif
+
+int snprintf (char *str, size_t count, const char *fmt, ...);
+int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
+
+static int dopr (char *buffer, size_t maxlen, const char *format, 
+                 va_list args);
+static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
+		   char *value, int flags, int min, int max);
+static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
+		   LLONG value, int base, int min, int max, int flags);
+static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
+		  LDOUBLE fvalue, int min, int max, int flags);
+static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
+
+/*
+ * dopr(): poor man's version of doprintf
+ */
+
+/* format read states */
+#define DP_S_DEFAULT 0
+#define DP_S_FLAGS   1
+#define DP_S_MIN     2
+#define DP_S_DOT     3
+#define DP_S_MAX     4
+#define DP_S_MOD     5
+#define DP_S_MOD_L   6
+#define DP_S_CONV    7
+#define DP_S_DONE    8
+
+/* format flags - Bits */
+#define DP_F_MINUS 	(1 << 0)
+#define DP_F_PLUS  	(1 << 1)
+#define DP_F_SPACE 	(1 << 2)
+#define DP_F_NUM   	(1 << 3)
+#define DP_F_ZERO  	(1 << 4)
+#define DP_F_UP    	(1 << 5)
+#define DP_F_UNSIGNED 	(1 << 6)
+
+/* Conversion Flags */
+#define DP_C_SHORT   1
+#define DP_C_LONG    2
+#define DP_C_LLONG   3
+#define DP_C_LDOUBLE 4
+
+#define char_to_int(p) (p - '0')
+#define MAX(p,q) ((p >= q) ? p : q)
+#define MIN(p,q) ((p <= q) ? p : q)
+
+static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
+{
+  char ch;
+  LLONG value;
+  LDOUBLE fvalue;
+  char *strvalue;
+  int min;
+  int max;
+  int state;
+  int flags;
+  int cflags;
+  int total;
+  size_t currlen;
+  
+  state = DP_S_DEFAULT;
+  currlen = flags = cflags = min = 0;
+  max = -1;
+  ch = *format++;
+  total = 0;
+
+  while (state != DP_S_DONE)
+  {
+    if (ch == '\0')
+      state = DP_S_DONE;
+
+    switch(state) 
+    {
+    case DP_S_DEFAULT:
+      if (ch == '%') 
+	state = DP_S_FLAGS;
+      else 
+	total += dopr_outch (buffer, &currlen, maxlen, ch);
+      ch = *format++;
+      break;
+    case DP_S_FLAGS:
+      switch (ch) 
+      {
+      case '-':
+	flags |= DP_F_MINUS;
+        ch = *format++;
+	break;
+      case '+':
+	flags |= DP_F_PLUS;
+        ch = *format++;
+	break;
+      case ' ':
+	flags |= DP_F_SPACE;
+        ch = *format++;
+	break;
+      case '#':
+	flags |= DP_F_NUM;
+        ch = *format++;
+	break;
+      case '0':
+	flags |= DP_F_ZERO;
+        ch = *format++;
+	break;
+      default:
+	state = DP_S_MIN;
+	break;
+      }
+      break;
+    case DP_S_MIN:
+      if (isdigit((unsigned char)ch)) 
+      {
+	min = 10*min + char_to_int (ch);
+	ch = *format++;
+      } 
+      else if (ch == '*') 
+      {
+	min = va_arg (args, int);
+	ch = *format++;
+	state = DP_S_DOT;
+      } 
+      else 
+	state = DP_S_DOT;
+      break;
+    case DP_S_DOT:
+      if (ch == '.') 
+      {
+	state = DP_S_MAX;
+	ch = *format++;
+      } 
+      else 
+	state = DP_S_MOD;
+      break;
+    case DP_S_MAX:
+      if (isdigit((unsigned char)ch)) 
+      {
+	if (max < 0)
+	  max = 0;
+	max = 10*max + char_to_int (ch);
+	ch = *format++;
+      } 
+      else if (ch == '*') 
+      {
+	max = va_arg (args, int);
+	ch = *format++;
+	state = DP_S_MOD;
+      } 
+      else 
+	state = DP_S_MOD;
+      break;
+    case DP_S_MOD:
+      switch (ch) 
+      {
+      case 'h':
+	cflags = DP_C_SHORT;
+	ch = *format++;
+	break;
+      case 'l':
+	cflags = DP_C_LONG;
+	ch = *format++;
+	break;
+      case 'L':
+	cflags = DP_C_LDOUBLE;
+	ch = *format++;
+	break;
+      default:
+	break;
+      }
+      if (cflags != DP_C_LONG)
+        state = DP_S_CONV;
+      else
+        state = DP_S_MOD_L;
+      break;
+    case DP_S_MOD_L:
+      switch (ch)
+      {
+      case 'l':
+        cflags = DP_C_LLONG;
+        ch = *format++;
+        break;
+      default:
+        break;
+      }
+      state = DP_S_CONV;
+      break;
+    case DP_S_CONV:
+      switch (ch) 
+      {
+      case 'd':
+      case 'i':
+	if (cflags == DP_C_SHORT) 
+	  value = (short int) va_arg (args, int);
+	else if (cflags == DP_C_LONG)
+	  value = va_arg (args, long int);
+        else if (cflags == DP_C_LLONG)
+          value = va_arg (args, LLONG);
+	else
+	  value = va_arg (args, int);
+	total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+	break;
+      case 'o':
+	flags |= DP_F_UNSIGNED;
+	if (cflags == DP_C_SHORT)
+	  value = (unsigned short int) va_arg (args, unsigned int);
+	else if (cflags == DP_C_LONG)
+	  value = va_arg (args, unsigned long int);
+        else if (cflags == DP_C_LLONG)
+          value = va_arg (args, unsigned LLONG);
+	else
+	  value = va_arg (args, unsigned int);
+	total += fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
+	break;
+      case 'u':
+	flags |= DP_F_UNSIGNED;
+	if (cflags == DP_C_SHORT)
+	  value = (unsigned short int) va_arg (args, unsigned int);
+	else if (cflags == DP_C_LONG)
+	  value = va_arg (args, unsigned long int);
+        else if (cflags == DP_C_LLONG)
+          value = va_arg (args, unsigned LLONG);
+	else
+	  value = va_arg (args, unsigned int);
+	total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+	break;
+      case 'X':
+	flags |= DP_F_UP;
+      case 'x':
+	flags |= DP_F_UNSIGNED;
+	if (cflags == DP_C_SHORT)
+	  value = (unsigned short int) va_arg (args, unsigned int);
+	else if (cflags == DP_C_LONG)
+	  value = va_arg (args, unsigned long int);
+        else if (cflags == DP_C_LLONG)
+          value = va_arg (args, unsigned LLONG);
+	else
+	  value = va_arg (args, unsigned int);
+	total += fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
+	break;
+      case 'f':
+	if (cflags == DP_C_LDOUBLE)
+	  fvalue = va_arg (args, LDOUBLE);
+	else
+	  fvalue = va_arg (args, double);
+	/* um, floating point? */
+	total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
+	break;
+      case 'E':
+	flags |= DP_F_UP;
+      case 'e':
+	if (cflags == DP_C_LDOUBLE)
+	  fvalue = va_arg (args, LDOUBLE);
+	else
+	  fvalue = va_arg (args, double);
+	break;
+      case 'G':
+	flags |= DP_F_UP;
+      case 'g':
+	if (cflags == DP_C_LDOUBLE)
+	  fvalue = va_arg (args, LDOUBLE);
+	else
+	  fvalue = va_arg (args, double);
+	break;
+      case 'c':
+	total += dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
+	break;
+      case 's':
+	strvalue = va_arg (args, char *);
+	total += fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
+	break;
+      case 'p':
+	strvalue = va_arg (args, void *);
+	total += fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min,
+                         max, flags);
+	break;
+      case 'n':
+	if (cflags == DP_C_SHORT) 
+	{
+	  short int *num;
+	  num = va_arg (args, short int *);
+	  *num = currlen;
+        } 
+	else if (cflags == DP_C_LONG) 
+	{
+	  long int *num;
+	  num = va_arg (args, long int *);
+	  *num = currlen;
+        }
+        else if (cflags == DP_C_LLONG) 
+        {
+          LLONG *num;
+          num = va_arg (args, LLONG *);
+          *num = currlen;
+        } 
+	else 
+	{
+	  int *num;
+	  num = va_arg (args, int *);
+	  *num = currlen;
+        }
+	break;
+      case '%':
+	total += dopr_outch (buffer, &currlen, maxlen, ch);
+	break;
+      case 'w':
+	/* not supported yet, treat as next char */
+	ch = *format++;
+	break;
+      default:
+	/* Unknown, skip */
+	break;
+      }
+      ch = *format++;
+      state = DP_S_DEFAULT;
+      flags = cflags = min = 0;
+      max = -1;
+      break;
+    case DP_S_DONE:
+      break;
+    default:
+      /* hmm? */
+      break; /* some picky compilers need this */
+    }
+  }
+  if (buffer != NULL)
+  {
+    if (currlen < maxlen - 1) 
+      buffer[currlen] = '\0';
+    else 
+      buffer[maxlen - 1] = '\0';
+  }
+  return total;
+}
+
+static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
+                   char *value, int flags, int min, int max)
+{
+  int padlen, strln;     /* amount to pad */
+  int cnt = 0;
+  int total = 0;
+  char null[] = "<NULL>";
+  
+  if (value == 0)
+  {
+    value = null;
+  }
+
+  for (strln = 0; value[strln]; ++strln); /* strlen */
+  if (max >= 0 && max < strln)
+    strln = max;
+  padlen = min - strln;
+  if (padlen < 0) 
+    padlen = 0;
+  if (flags & DP_F_MINUS) 
+    padlen = -padlen; /* Left Justify */
+
+  while (padlen > 0)
+  {
+    total += dopr_outch (buffer, currlen, maxlen, ' ');
+    --padlen;
+  }
+  while (*value && ((max < 0) || (cnt < max)))
+  {
+    total += dopr_outch (buffer, currlen, maxlen, *value++);
+    ++cnt;
+  }
+  while (padlen < 0)
+  {
+    total += dopr_outch (buffer, currlen, maxlen, ' ');
+    ++padlen;
+  }
+  return total;
+}
+
+/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
+
+static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
+		   LLONG value, int base, int min, int max, int flags)
+{
+  int signvalue = 0;
+  unsigned LLONG uvalue;
+  char convert[24];
+  unsigned int place = 0;
+  int spadlen = 0; /* amount to space pad */
+  int zpadlen = 0; /* amount to zero pad */
+  const char *digits;
+  int total = 0;
+  
+  if (max < 0)
+    max = 0;
+
+  uvalue = value;
+
+  if(!(flags & DP_F_UNSIGNED))
+  {
+    if( value < 0 ) {
+      signvalue = '-';
+      uvalue = -value;
+    }
+    else
+      if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
+	signvalue = '+';
+    else
+      if (flags & DP_F_SPACE)
+	signvalue = ' ';
+  }
+  
+  if (flags & DP_F_UP)
+    /* Should characters be upper case? */
+    digits = "0123456789ABCDEF";
+  else
+    digits = "0123456789abcdef";
+
+  do {
+    convert[place++] = digits[uvalue % (unsigned)base];
+    uvalue = (uvalue / (unsigned)base );
+  } while(uvalue && (place < sizeof (convert)));
+  if (place == sizeof (convert)) place--;
+  convert[place] = 0;
+
+  zpadlen = max - place;
+  spadlen = min - MAX ((unsigned int)max, place) - (signvalue ? 1 : 0);
+  if (zpadlen < 0) zpadlen = 0;
+  if (spadlen < 0) spadlen = 0;
+  if (flags & DP_F_ZERO)
+  {
+    zpadlen = MAX(zpadlen, spadlen);
+    spadlen = 0;
+  }
+  if (flags & DP_F_MINUS) 
+    spadlen = -spadlen; /* Left Justifty */
+
+#ifdef DEBUG_SNPRINTF
+  dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
+      zpadlen, spadlen, min, max, place));
+#endif
+
+  /* Spaces */
+  while (spadlen > 0) 
+  {
+    total += dopr_outch (buffer, currlen, maxlen, ' ');
+    --spadlen;
+  }
+
+  /* Sign */
+  if (signvalue) 
+    total += dopr_outch (buffer, currlen, maxlen, signvalue);
+
+  /* Zeros */
+  if (zpadlen > 0) 
+  {
+    while (zpadlen > 0)
+    {
+      total += dopr_outch (buffer, currlen, maxlen, '0');
+      --zpadlen;
+    }
+  }
+
+  /* Digits */
+  while (place > 0) 
+    total += dopr_outch (buffer, currlen, maxlen, convert[--place]);
+  
+  /* Left Justified spaces */
+  while (spadlen < 0) {
+    total += dopr_outch (buffer, currlen, maxlen, ' ');
+    ++spadlen;
+  }
+
+  return total;
+}
+
+static LDOUBLE abs_val (LDOUBLE value)
+{
+  LDOUBLE result = value;
+
+  if (value < 0)
+    result = -value;
+
+  return result;
+}
+
+static LDOUBLE pow10 (int exp)
+{
+  LDOUBLE result = 1;
+
+  while (exp)
+  {
+    result *= 10;
+    exp--;
+  }
+  
+  return result;
+}
+
+static long round (LDOUBLE value)
+{
+  long intpart;
+
+  intpart = value;
+  value = value - intpart;
+  if (value >= 0.5)
+    intpart++;
+
+  return intpart;
+}
+
+static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
+		  LDOUBLE fvalue, int min, int max, int flags)
+{
+  int signvalue = 0;
+  LDOUBLE ufvalue;
+  char iconvert[20];
+  char fconvert[20];
+  int iplace = 0;
+  int fplace = 0;
+  int padlen = 0; /* amount to pad */
+  int zpadlen = 0; 
+  int caps = 0;
+  int total = 0;
+  LLONG intpart;
+  LLONG fracpart;
+  
+  /* 
+   * AIX manpage says the default is 0, but Solaris says the default
+   * is 6, and sprintf on AIX defaults to 6
+   */
+  if (max < 0)
+    max = 6;
+
+  ufvalue = abs_val (fvalue);
+
+  if (fvalue < 0)
+    signvalue = '-';
+  else
+    if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
+      signvalue = '+';
+    else
+      if (flags & DP_F_SPACE)
+	signvalue = ' ';
+
+#if 0
+  if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+#endif
+
+  intpart = ufvalue;
+
+  /* 
+   * Sorry, we only support 9 digits past the decimal because of our 
+   * conversion method
+   */
+  if (max > 9)
+    max = 9;
+
+  /* We "cheat" by converting the fractional part to integer by
+   * multiplying by a factor of 10
+   */
+  fracpart = round ((pow10 (max)) * (ufvalue - intpart));
+
+  if (fracpart >= pow10 (max))
+  {
+    intpart++;
+    fracpart -= pow10 (max);
+  }
+
+#ifdef DEBUG_SNPRINTF
+  dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
+#endif
+
+  /* Convert integer part */
+  do {
+    iconvert[iplace++] =
+      (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
+    intpart = (intpart / 10);
+  } while(intpart && (iplace < 20));
+  if (iplace == 20) iplace--;
+  iconvert[iplace] = 0;
+
+  /* Convert fractional part */
+  do {
+    fconvert[fplace++] =
+      (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
+    fracpart = (fracpart / 10);
+  } while(fracpart && (fplace < 20));
+  if (fplace == 20) fplace--;
+  fconvert[fplace] = 0;
+
+  /* -1 for decimal point, another -1 if we are printing a sign */
+  padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); 
+  zpadlen = max - fplace;
+  if (zpadlen < 0)
+    zpadlen = 0;
+  if (padlen < 0) 
+    padlen = 0;
+  if (flags & DP_F_MINUS) 
+    padlen = -padlen; /* Left Justifty */
+
+  if ((flags & DP_F_ZERO) && (padlen > 0)) 
+  {
+    if (signvalue) 
+    {
+      total += dopr_outch (buffer, currlen, maxlen, signvalue);
+      --padlen;
+      signvalue = 0;
+    }
+    while (padlen > 0)
+    {
+      total += dopr_outch (buffer, currlen, maxlen, '0');
+      --padlen;
+    }
+  }
+  while (padlen > 0)
+  {
+    total += dopr_outch (buffer, currlen, maxlen, ' ');
+    --padlen;
+  }
+  if (signvalue) 
+    total += dopr_outch (buffer, currlen, maxlen, signvalue);
+
+  while (iplace > 0) 
+    total += dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
+
+  /*
+   * Decimal point.  This should probably use locale to find the correct
+   * char to print out.
+   */
+  if (max > 0)
+  {
+    total += dopr_outch (buffer, currlen, maxlen, '.');
+
+    while (fplace > 0) 
+      total += dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
+  }
+
+  while (zpadlen > 0)
+  {
+    total += dopr_outch (buffer, currlen, maxlen, '0');
+    --zpadlen;
+  }
+
+  while (padlen < 0) 
+  {
+    total += dopr_outch (buffer, currlen, maxlen, ' ');
+    ++padlen;
+  }
+
+  return total;
+}
+
+static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
+{
+  if (*currlen + 1 < maxlen)
+    buffer[(*currlen)++] = c;
+  return 1;
+}
+
+int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
+{
+  if (str != NULL)
+    str[0] = 0;
+  return dopr(str, count, fmt, args);
+}
+
+/* VARARGS3 */
+#ifdef HAVE_STDARGS
+int snprintf (char *str,size_t count,const char *fmt,...)
+#else
+int snprintf (va_alist) va_dcl
+#endif
+{
+#ifndef HAVE_STDARGS
+  char *str;
+  size_t count;
+  char *fmt;
+#endif
+  VA_LOCAL_DECL;
+  int total;
+    
+  VA_START (fmt);
+  VA_SHIFT (str, char *);
+  VA_SHIFT (count, size_t );
+  VA_SHIFT (fmt, char *);
+  total = vsnprintf(str, count, fmt, ap);
+  VA_END;
+  return total;
+}
+
+#ifdef TEST_SNPRINTF
+#ifndef LONG_STRING
+#define LONG_STRING 1024
+#endif
+int main (void)
+{
+  char buf1[LONG_STRING];
+  char buf2[LONG_STRING];
+  char *fp_fmt[] = {
+    "%-1.5f",
+    "%1.5f",
+    "%123.9f",
+    "%10.5f",
+    "% 10.5f",
+    "%+22.9f",
+    "%+4.9f",
+    "%01.3f",
+    "%4f",
+    "%3.1f",
+    "%3.2f",
+    "%.0f",
+    "%.1f",
+    NULL
+  };
+  double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, 
+    0.9996, 1.996, 4.136, 0};
+  char *int_fmt[] = {
+    "%-1.5d",
+    "%1.5d",
+    "%123.9d",
+    "%5.5d",
+    "%10.5d",
+    "% 10.5d",
+    "%+22.33d",
+    "%01.3d",
+    "%4d",
+    NULL
+  };
+  long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
+  int x, y;
+  int fail = 0;
+  int num = 0;
+
+  printf ("Testing snprintf format codes against system sprintf...\n");
+
+  for (x = 0; fp_fmt[x] != NULL ; x++)
+    for (y = 0; fp_nums[y] != 0 ; y++)
+    {
+      snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
+      sprintf (buf2, fp_fmt[x], fp_nums[y]);
+      if (strcmp (buf1, buf2))
+      {
+	printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n", 
+	    fp_fmt[x], buf1, buf2);
+	fail++;
+      }
+      num++;
+    }
+
+  for (x = 0; int_fmt[x] != NULL ; x++)
+    for (y = 0; int_nums[y] != 0 ; y++)
+    {
+      snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
+      sprintf (buf2, int_fmt[x], int_nums[y]);
+      if (strcmp (buf1, buf2))
+      {
+	printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n", 
+	    int_fmt[x], buf1, buf2);
+	fail++;
+      }
+      num++;
+    }
+  printf ("%d tests failed out of %d.\n", fail, num);
+}
+#endif /* SNPRINTF_TEST */
diff --git a/apps/sam/c/src/strl.c b/apps/sam/c/src/strl.c
new file mode 100644
index 0000000000..792ba5ae90
--- /dev/null
+++ b/apps/sam/c/src/strl.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stddef.h>
+#include <string.h>
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left).  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+size_t
+strlcat(char *dst, const char *src, size_t siz)
+{
+	register char *d = dst;
+	register const char *s = src;
+	register size_t n = siz;
+	size_t dlen;
+
+	/* Find the end of dst and adjust bytes left but don't go past end */
+	while (n-- != 0 && *d != '\0')
+		d++;
+	dlen = d - dst;
+	n = siz - dlen;
+
+	if (n == 0)
+		return(dlen + strlen(s));
+	while (*s != '\0') {
+		if (n != 1) {
+			*d++ = *s;
+			n--;
+		}
+		s++;
+	}
+	*d = '\0';
+
+	return(dlen + (s - src));	/* count does not include NUL */
+}
+
+/*
+ * Copy src to string dst of size siz.  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+strlcpy(char *dst, const char *src, size_t siz)
+{
+	register char *d = dst;
+	register const char *s = src;
+	register size_t n = siz;
+
+	/* Copy as many bytes as will fit */
+	if (n != 0 && --n != 0) {
+		do {
+			if ((*d++ = *s++) == 0)
+				break;
+		} while (--n != 0);
+	}
+
+	/* Not enough room in dst, add NUL and traverse rest of src */
+	if (n == 0) {
+		if (siz != 0)
+			*d = '\0';		/* NUL-terminate dst */
+		while (*s++)
+			;
+	}
+
+	return(s - src - 1);	/* count does not include NUL */
+}
-- 
GitLab