From dae6be14b7a49509974c98c4a91c50cf941cd966 Mon Sep 17 00:00:00 2001
From: polecat <polecat>
Date: Sat, 23 Apr 2005 03:28:40 +0000
Subject: [PATCH] I removed those dumb platform specific makefiles.  They
 weren't doing what they ought anyway.  If there are platform specific issues,
 someone please tell me and I'll provide support for it here.  Or patch it
 yourself. And this is the big "Fix the Parser" patch.  It turns the sam_parse
 function in src/parse.c into something that actually works.  Generating the
 argument list from an incoming SAM thingy is a bit memory churn-y; perhaps
 when I have time I'll replace all those strdups with structures that simply
 track the (start,end) indices. Oh and also I moved i2p-ping to the new
 system.  Which required 0 change in code.  All I did was fix the Makefile,
 and add shared library libtool support.  Anyway, so enjoy folks.  It's rare
 I'm this productive - polecat

---
 apps/sam/c/Makefile                   |  64 +++++
 apps/sam/c/Makefile.common            |  25 --
 apps/sam/c/Makefile.cygwin            |  48 ----
 apps/sam/c/Makefile.freebsd           |  46 ---
 apps/sam/c/Makefile.linux             |  47 ---
 apps/sam/c/Makefile.mingw             |  47 ---
 apps/sam/c/examples/i2p-ping/Makefile |  73 +++--
 apps/sam/c/inc/parse.h                |  24 ++
 apps/sam/c/inc/sam.h                  |  19 +-
 apps/sam/c/inc/tinystring.h           |  48 ++++
 apps/sam/c/src/parse.c                |  78 +++++
 apps/sam/c/src/sam.c                  | 394 ++++++++++++--------------
 apps/sam/c/src/tinystring.c           | 128 +++++++++
 apps/sam/c/status                     |   4 +
 14 files changed, 574 insertions(+), 471 deletions(-)
 create mode 100644 apps/sam/c/Makefile
 delete mode 100644 apps/sam/c/Makefile.common
 delete mode 100644 apps/sam/c/Makefile.cygwin
 delete mode 100644 apps/sam/c/Makefile.freebsd
 delete mode 100644 apps/sam/c/Makefile.linux
 delete mode 100644 apps/sam/c/Makefile.mingw
 create mode 100644 apps/sam/c/inc/parse.h
 create mode 100644 apps/sam/c/inc/tinystring.h
 create mode 100644 apps/sam/c/src/parse.c
 create mode 100644 apps/sam/c/src/tinystring.c
 create mode 100644 apps/sam/c/status

diff --git a/apps/sam/c/Makefile b/apps/sam/c/Makefile
new file mode 100644
index 0000000000..72fa21cd23
--- /dev/null
+++ b/apps/sam/c/Makefile
@@ -0,0 +1,64 @@
+FLAGS+=-g
+CFLAGS+=$(FLAGS)
+LDFLAGS+=$(FLAGS)
+
+OBJS:=obj/sam.lo obj/strl.lo obj/parse.lo obj/tinystring.lo
+DEPS:=$(patsubst obj/%.lo, .deps/%.d, $(OBJS))
+DESTDIR:=$(if $(DESTDIR),$(DESTDIR)/lib,/usr/lib)
+
+MAKEFLAGS=-s -r
+
+PERL=$(shell which perl 2>/dev/null)
+ifneq ($(PERL),)
+STATUS=$(PERL) ./status
+else
+STATUS=echo
+endif
+
+LIBTOOL_LOG=libtool.log
+
+all:: cleanlog .deps/finish
+
+cleanlog:
+	echo >$(LIBTOOL_LOG)
+
+lib/libsam.so: obj/libsam.la
+	libtool --mode=install install $^ `pwd`/$@ >>$(LIBTOOL_LOG)
+
+obj/libsam-static.la: $(OBJS)
+	$(STATUS) library '(static)'
+	libtool --mode=link gcc -static $(LDFLAGS) -o $@ $^ >>$(LIBTOOL_LOG)
+
+obj/libsam.la: $(OBJS)
+	$(STATUS) library '(shared)'
+	libtool --mode=link gcc -rpath  $(DESTDIR) $(LDFLAGS) -o $@ $^ >>$(LIBTOOL_LOG)
+
+obj/%.lo: src/%.c
+	$(STATUS) compile $*
+	libtool --mode=compile gcc $(CFLAGS) -Iinc/ -c -o $@ $< >>$(LIBTOOL_LOG)
+
+$(OBJS):|obj
+obj:
+	$(STATUS) MKDIR $@
+	mkdir -p $@
+
+.deps/%.d: src/%.c
+	$(STATUS) deps $*
+	gcc -Iinc/ -MM -MT obj/$*.o $< -o $@
+
+-include $(DEPS)
+
+DEPS+=.deps/finish
+.deps/finish: lib/libsam.so
+	libtool --finish $(DESTDIR) >>$(LIBTOOL_LOG) && touch $@
+$(DEPS):|.deps
+.deps:
+	$(STATUS) MKDIR $@
+	mkdir -p $@
+
+clean:
+	$(STATUS) clean
+	libtool --mode=clean rm -f obj/*.l* lib/*.l* lib/*.so* lib/*.a >>$(LIBTOOL_LOG)
+	rm -Rf .deps libtool.log
+
+.PHONY: all cleanlog clean
\ No newline at end of file
diff --git a/apps/sam/c/Makefile.common b/apps/sam/c/Makefile.common
deleted file mode 100644
index a8b2a1a725..0000000000
--- a/apps/sam/c/Makefile.common
+++ /dev/null
@@ -1,25 +0,0 @@
-#
-# This Makefile contains instructions common to all platforms
-#
-
-#
-# Build rules
-#
-
-all: clean 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)/*.o .depend
diff --git a/apps/sam/c/Makefile.cygwin b/apps/sam/c/Makefile.cygwin
deleted file mode 100644
index 3ffb253909..0000000000
--- a/apps/sam/c/Makefile.cygwin
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# 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
-RM = rm
-
-#
-# Flags
-#
-
-CFLAGS = -g -O2 -pipe -std=c99 -Wall
-CFLAGS += -DOS=$(OS)
-CFLAGS += -I$(INCDIR)
-
-#
-# Object files
-#
-
-OBJS =	$(OBJDIR)/sam.o \
-		$(OBJDIR)/snprintf.o \
-		$(OBJDIR)/strl.o
-
-#
-# Include the make instructions common to all platforms
-#
-
-include Makefile.common
diff --git a/apps/sam/c/Makefile.freebsd b/apps/sam/c/Makefile.freebsd
deleted file mode 100644
index 3fdf8f315e..0000000000
--- a/apps/sam/c/Makefile.freebsd
+++ /dev/null
@@ -1,46 +0,0 @@
-#
-# This Makefile is compatible with GNU Make and should work on FreeBSD
-#
-
-#
-# Your operating system
-#
-
-OS = FREEBSD
-
-#
-# Directories
-#
-
-INCDIR = inc
-LIBDIR = lib
-OBJDIR = obj
-SRCDIR = src
-
-#
-# Programs
-#
-
-AR = ar
-CC = gcc
-RM = rm
-
-#
-# Flags
-#
-
-CFLAGS = -g -O2 -pipe -std=c99 -Wall
-CFLAGS += -DOS=$(OS)
-CFLAGS += -I$(INCDIR)
-
-#
-# Object files
-#
-
-OBJS =	$(OBJDIR)/sam.o
-
-#
-# Include the make instructions common to all platforms
-#
-
-include Makefile.common
diff --git a/apps/sam/c/Makefile.linux b/apps/sam/c/Makefile.linux
deleted file mode 100644
index 7eddeed32d..0000000000
--- a/apps/sam/c/Makefile.linux
+++ /dev/null
@@ -1,47 +0,0 @@
-#
-# This Makefile is compatible with GNU Make and should work on Linux
-#
-
-#
-# Your operating system
-#
-
-OS = LINUX
-
-#
-# Directories
-#
-
-INCDIR = inc
-LIBDIR = lib
-OBJDIR = obj
-SRCDIR = src
-
-#
-# Programs
-#
-
-AR = ar
-CC = gcc
-RM = rm
-
-#
-# Flags
-#
-
-CFLAGS = -g -O2 -pipe -std=c99 -Wall
-CFLAGS += -DOS=$(OS)
-CFLAGS += -I$(INCDIR)
-
-#
-# Object files
-#
-
-OBJS =	$(OBJDIR)/sam.o \
-		$(OBJDIR)/strl.o
-
-#
-# Include the make instructions common to all platforms
-#
-
-include Makefile.common
diff --git a/apps/sam/c/Makefile.mingw b/apps/sam/c/Makefile.mingw
deleted file mode 100644
index d418f175a6..0000000000
--- a/apps/sam/c/Makefile.mingw
+++ /dev/null
@@ -1,47 +0,0 @@
-#
-# 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:\MinGW\bin\ar
-CC = C:\MinGW\bin\gcc
-RM = C:\MinGW\bin\rm
-
-#
-# Flags
-#
-
-CFLAGS = -g -O2 -pipe -std=c99 -Wall
-CFLAGS += -DOS=$(OS)
-CFLAGS += -I$(INCDIR)
-
-#
-# Object files
-#
-
-OBJS =	$(OBJDIR)/sam.o \
-		$(OBJDIR)/strl.o
-
-#
-# Include the make instructions common to all platforms
-#
-
-include Makefile.common
diff --git a/apps/sam/c/examples/i2p-ping/Makefile b/apps/sam/c/examples/i2p-ping/Makefile
index b1ce9ddb96..9f5c27b5e2 100644
--- a/apps/sam/c/examples/i2p-ping/Makefile
+++ b/apps/sam/c/examples/i2p-ping/Makefile
@@ -1,39 +1,54 @@
-#
-# This Makefile is compatible with GNU Make and should work on POSIX systems
-#
+FLAGS+=-g
 
-#
-# Programs
-#
+CFLAGS = $(FLAGS) -pipe -std=c99 -Wall
+CFLAGS += -I../../inc
+LDFLAGS = $(FLAGS) -L../../lib -lsam
 
-CC = gcc
-INSTALL = install
-RM = rm
+OBJS:=i2p-ping.lo
+DEPS:=$(patsubst obj/%.lo, .deps/%.d, $(OBJS))
+DESTDIR:=$(if $(DESTDIR),$(DESTDIR)/lib,/usr/lib)
 
-#
-# Flags
-#
+MAKEFLAGS=-s -r
+PERL=$(shell which perl 2>/dev/null)
+ifneq ($(PERL),)
+STATUS=$(PERL) ../../status
+else
+STATUS=echo
+endif
 
-CFLAGS = -g -O2 -pipe -std=c99 -Wall
-CFLAGS += -I../../inc -L../../lib
-LIBS = -lsam
+LIBTOOL_LOG=libtool.log
 
-#
-# Build rules
-#
+all:: cleanlog i2p-ping
 
-all: clean i2p-ping
+cleanlog:
+	>$(LIBTOOL_LOG)
 
-i2p-ping: i2p-ping.c
-	$(CC) $(CFLAGS) -o i2p-ping.o -c i2p-ping.c
-	$(CC) $(CFLAGS) -o i2p-ping i2p-ping.o $(LIBS)
+i2p-ping: $(OBJS)
+	$(STATUS) link
+	libtool --mode=link gcc $(LDFLAGS) -o $@ $^ >>$(LIBTOOL_LOG)
 
-install: i2p-ping
-	$(INSTALL) i2p-ping $(HOME)/bin
-
-#
-# Cleanup rules
-#
+%.lo: %.c
+	$(STATUS) compile $*
+	libtool --mode=compile gcc $(CFLAGS) -Iinc/ -c -o $@ $< >>$(LIBTOOL_LOG)
+.deps/%.d: src/%.c
+	$(STATUS) deps $*
+	gcc -Iinc/ -MM -MT obj/$*.o $^ -o $@
 
 clean:
-	-$(RM) -f i2p-ping *.o
+	$(STATUS) clean
+	rm -Rf .deps obj libtool.log
+	libtool --mode=clean rm -f i2p-ping i2p-ping.lo >>$(LIBTOOL_LOG)
+
+$(OBJS):|obj
+obj:
+	$(STATUS) MKDIR $@
+	mkdir -p $@
+
+-include $(DEPS)
+$(DEPS):|.deps
+.deps:
+	$(STATUS) MKDIR $@
+	mkdir -p $@
+
+
+.PHONY: all cleanlog clean
\ No newline at end of file
diff --git a/apps/sam/c/inc/parse.h b/apps/sam/c/inc/parse.h
new file mode 100644
index 0000000000..960f2d8ca2
--- /dev/null
+++ b/apps/sam/c/inc/parse.h
@@ -0,0 +1,24 @@
+#ifndef _PARSE_HEADER_FEEP
+#define _PARSE_HEADER_FEEP
+
+#include "tinystring.h"
+
+typedef struct arg_s {
+  string_t name;
+  string_t value;
+  //  int pos;
+} arg_t;
+
+typedef struct {
+  arg_t* arg;
+  int num;
+} args_t;
+
+args_t arg_parse(const char*);
+void arg_done(args_t);
+arg_t* arg_get(args_t,int);
+arg_t* arg_find(args_t,string_t);
+
+#define AG(a,b) arg_get(a,b)
+
+#endif /* _PARSE_HEADER_FEEP */
diff --git a/apps/sam/c/inc/sam.h b/apps/sam/c/inc/sam.h
index 32cce31566..29f5139f27 100644
--- a/apps/sam/c/inc/sam.h
+++ b/apps/sam/c/inc/sam.h
@@ -121,9 +121,9 @@ bool		sam_read_buffer(sam_sess_t *session);
 const char *sam_strerror(samerr_t code);
 /* SAM controls - callbacks */
 void		(*sam_diedback)(sam_sess_t *session);
-void		(*sam_logback)(char *str);
-void		(*sam_namingback)(sam_sess_t *session, char *name,
-				sam_pubkey_t pubkey, samerr_t result);
+void		(*sam_logback)(const char *str);
+void            (*sam_namingback)(sam_sess_t *session, const char *name, 
+                                  sam_pubkey_t pubkey, samerr_t result, const char* message);
 
 /* Stream commands */
 void		sam_stream_close(sam_sess_t *session, sam_sid_t stream_id);
@@ -131,14 +131,15 @@ sam_sid_t	sam_stream_connect(sam_sess_t *session, const sam_pubkey_t dest);
 samerr_t	sam_stream_send(sam_sess_t *session, sam_sid_t stream_id,
 				const void *data, size_t size);
 /* Stream commands - callbacks */
-void		(*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id,
-				samerr_t reason);
+void            (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id, 
+                                 samerr_t reason, const char* message);
+
 void		(*sam_connectback)(sam_sess_t *session, sam_sid_t stream_id,
-				sam_pubkey_t dest);
-void		(*sam_databack)(sam_sess_t *session, sam_sid_t stream_id,
+                                   sam_pubkey_t dest);
+    void		(*sam_databack)(sam_sess_t *session, sam_sid_t stream_id,
 				void *data, size_t size);
-void		(*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
-				samerr_t result);
+void            (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
+                                  samerr_t result, const char* message);
 
 /* Stream send queue (experimental) */
 void		sam_sendq_add(sam_sess_t *session, sam_sid_t stream_id,
diff --git a/apps/sam/c/inc/tinystring.h b/apps/sam/c/inc/tinystring.h
new file mode 100644
index 0000000000..f518e0ebc6
--- /dev/null
+++ b/apps/sam/c/inc/tinystring.h
@@ -0,0 +1,48 @@
+#ifndef TINYSTRING_HEADER
+#define TINYSTRING_HEADER
+
+#include <sys/types.h>
+
+#ifndef bool
+#define bool short int
+#endif
+
+struct string_s;
+#define string_t struct string_s*
+//Mysteeeerious *waggles mysteriously*
+
+/*{
+  char* data;
+  long int size;
+} *string_t;
+*/
+
+string_t string_create(const char*);
+string_t string_ncreate(const char* cstr,long int length);
+
+string_t string_wrap(const char*); 
+//Does not malloc, do NOT pass to string_free
+
+string_t string_fmt(const char* fmt, ...);
+string_t string_cat(string_t,string_t);
+
+/* Source Dest */
+void string_copy(string_t,string_t);
+void string_copy_raw(string_t,void*,size_t);
+
+const char* string_data(string_t);
+long int string_size(string_t);
+
+void string_free(string_t);
+
+bool string_equal(string_t,string_t);
+bool string_equali(string_t,string_t);
+int string_cmp(string_t,string_t);
+int string_cmpi(string_t,string_t);
+
+#define _sw(a) string_wrap(a)
+#define _scr(a,b,c) string_copy_raw(a,b,c)
+
+#define string_is(a,b) (! strncmp(string_data(a),(b),string_size(a)))
+
+#endif /* TINYSTRING_HEADER */
diff --git a/apps/sam/c/src/parse.c b/apps/sam/c/src/parse.c
new file mode 100644
index 0000000000..6fcb33fc63
--- /dev/null
+++ b/apps/sam/c/src/parse.c
@@ -0,0 +1,78 @@
+#include "parse.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <malloc.h>
+#define _GNU_SOURCE
+#include <string.h>
+
+args_t arg_parse(const char* line_raw) {
+    args_t self;
+    int numargs = 0;
+    const char *end, *last;
+    /* First pass to count how many args... */
+    end = line_raw;
+    while(*end && isspace(*end)) ++end;
+    //Skip initial space...
+    for(;;) {
+        while(*end && !isspace(*end)) ++end;
+        //Go to end of argument
+        ++numargs;
+        while(*end && isspace(*end)) ++end;
+        //Go to end of space after argument
+        if(!*end) break;
+    }
+    self.num = numargs; // One more # args than spaces.
+    self.arg = malloc(sizeof(arg_t)*numargs);
+
+    /* Second pass to assign args.  (Lemee alone, is more efficient than a linked list!) */
+    last = line_raw;
+    numargs = 0; //Now numargs is which current arg.
+    end = line_raw;
+    while(*end && isspace(*end)) ++end;
+    //Skip initial space...
+    for(;;) {
+        arg_t* nextarg = self.arg + numargs;;
+        const char* isbinary;
+        while(*end && !isspace(*end)) ++end;
+        //Go to end of argument
+        isbinary = strchr(last,'='); //Is there a value?
+        
+        //Make sure not to pass end in our search for =
+        if(isbinary && (isbinary < end)) {
+            nextarg->name = string_ncreate(last,isbinary-last);
+            nextarg->value = string_ncreate(isbinary+1,end-isbinary-1);
+        } else {
+            nextarg->name = string_ncreate(last,end-last);
+            nextarg->value = string_create(NULL);
+        }
+        ++numargs;
+        while(*end && isspace(*end)) ++end;
+        //Go to end of space after argument
+        if(!*end) break;
+        last = end;
+    }
+    return self;
+}
+
+void arg_done(args_t self) {
+    free(self.arg);
+    self.arg = NULL;
+    self.num = 0;
+}
+
+arg_t* arg_get(args_t self, int index) {
+    if(index >= self.num) return NULL;
+    return self.arg + index;
+}
+
+arg_t* arg_find(args_t self,string_t testname) {
+    int index;
+    for(index=0;index<self.num;++index) {
+        if(string_equali(self.arg[index].name,testname)) {
+            return self.arg + index;
+        }
+    }
+
+    return NULL;
+}
diff --git a/apps/sam/c/src/sam.c b/apps/sam/c/src/sam.c
index 9fe35486ed..d953558857 100644
--- a/apps/sam/c/src/sam.c
+++ b/apps/sam/c/src/sam.c
@@ -30,6 +30,10 @@
 
 #include "sam.h"
 #include "platform.h"
+#include "parse.h"
+#include "tinystring.h"
+
+#include <assert.h>
 
 static bool			sam_hello(sam_sess_t *session);
 static void			sam_log(const char *format, ...);
@@ -57,7 +61,7 @@ static ssize_t		sam_write(sam_sess_t *session, const void *buf, size_t n);
  */
 
 /* a peer closed the connection */
-void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id, samerr_t reason)
+void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id, samerr_t reason, const char* message)
 	= NULL;
 
 /* a peer connected to us */
@@ -76,15 +80,14 @@ void (*sam_dgramback)(sam_sess_t *session, sam_pubkey_t dest, void *data,
 void (*sam_diedback)(sam_sess_t *session) = NULL;
 
 /* logging callback */
-void (*sam_logback)(char *str) = NULL;
+void (*sam_logback)(const char *str) = NULL;
 
 /* naming lookup reply - `pubkey' will be NULL if `result' isn't SAM_OK */
-void (*sam_namingback)(sam_sess_t *session, char *name, sam_pubkey_t pubkey,
-	samerr_t result) = NULL;
+void (*sam_namingback)(sam_sess_t *session, const char *name, sam_pubkey_t pubkey, samerr_t result, const char* message) = NULL;
 
 /* our connection to a peer has completed */
 void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
-	samerr_t result) = NULL;
+	samerr_t result, const char* message) = NULL;
 
 /* a peer sent some raw data (`data' MUST be freed) */
 void (*sam_rawback)(sam_sess_t *session, void *data, size_t size) = NULL;
@@ -290,13 +293,13 @@ static void sam_log(const char *format, ...)
  */
 void sam_naming_lookup(sam_sess_t *session, const char *name)
 {
-	assert(session != NULL);
-	char cmd[SAM_CMD_LEN];
+    assert(session != NULL);
+    char cmd[SAM_CMD_LEN];
 
-	snprintf(cmd, sizeof cmd, "NAMING LOOKUP NAME=%s\n", name);
-	sam_write(session, cmd, strlen(cmd));
+    snprintf(cmd, sizeof cmd, "NAMING LOOKUP NAME=%s\n", name);
+    sam_write(session, cmd, strlen(cmd));
 
-	return;
+    return;
 }
 
 /*
@@ -304,241 +307,192 @@ void sam_naming_lookup(sam_sess_t *session, const char *name)
  *
  * s - string of data that we read (read past tense)
  */
+bool sam_parse_args(sam_sess_t *session, args_t args);
 static void sam_parse(sam_sess_t *session, char *s)
 {
-	assert(session != NULL);
-#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_RAW_RECEIVED_REPLY "RAW RECEIVED"
-#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"
+    //Wrapper for ease of memory management
+    args_t args;
+    assert(session != NULL);
+    args = arg_parse(s);
+    if(!sam_parse_args(session, args)) {
+        SAMLOG("Unknown SAM command received: %s", s);
+    }
+    arg_done(args);
+}
 
-	/*
-	 * TODO: add raw parsing
-	 */
+long int strtol_checked(const char* str) {
+    static char* end = NULL;
+    long int ret = strtol(str,&end,10);
+    assert(str != end || "No number found at all!");    
+    return ret;
+}
 
-	if (strncmp(s, SAM_DGRAM_RECEIVED_REPLY,
-			strlen(SAM_DGRAM_RECEIVED_REPLY)) == 0) {
-		char *p;
+bool sam_parse_args(sam_sess_t *session, args_t args)
+{
+    arg_t* arg; // The current argument being examined...
+    const char* message = NULL; // Almost EVERYTHING can have a message...
+	
+    if(args.num <= 0) return 0;
+
+#define ARG_IS(a,b) string_equal(AG(args,a)->name,string_wrap(b))
+#define ARG_FIND(a) arg_find(args,_sw(a))
+
+    // Almost EVERYTHING can have a message...
+    arg = ARG_FIND("MESSAGE");
+    if(arg) {
+        message = string_data(arg->value);
+    }
+
+    if(ARG_IS(0,"DATAGRAM") &&
+       ARG_IS(1,"RECEIVED")) {
+        sam_pubkey_t dest;
+        size_t size;
+        void *data;
+
+        arg = ARG_FIND("DESTINATION");
+        assert(arg != NULL);
+        _scr(arg->value, dest, sizeof dest);
+
+        arg = ARG_FIND("SIZE");
+        assert(arg != NULL);
+        size = strtol_checked(string_data(arg->value));
+
+        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 (data == NULL) {
+	    SAMLOGS("Out of memory");
+	    abort();
+        }
+        if (sam_read2(session, data, size) != -1) {
+	    char* p = data + size;
+	    *p = '\0';  /* see above NUL note */
+	    sam_dgramback(session, dest, data, size); /* `data' must be freed */
+        } else
+	    free(data);
+	  
+    } else if (ARG_IS(0,"NAMING") &&
+               ARG_IS(1, "REPLY")) {
+        if(NULL == (arg = ARG_FIND("RESULT"))) {
+            SAMLOGS("Naming reply with no result");
+            return 0;
+        }
+
+        if (string_is(arg->value,"OK")) {
+            sam_pubkey_t pubkey;
+            arg = ARG_FIND("VALUE");
+            assert(arg != NULL);
+            _scr(arg->value, pubkey, sizeof pubkey);
+            arg = ARG_FIND("NAME");
+            assert(arg != NULL);
+
+            sam_namingback(session, string_data(arg->value), pubkey, SAM_OK, message);
+        } else if(string_is(arg->value,"INVALID_KEY")) {
+            arg_t* namearg = ARG_FIND("NAME");
+            assert(namearg != NULL);
+            sam_namingback(session, string_data(namearg->value), NULL,
+                           SAM_INVALID_KEY, message);
+        } else if(string_is(arg->value,"KEY_NOT_FOUND")) {
+            arg_t* namearg = ARG_FIND("NAME");
+            assert(namearg != NULL);
+            sam_namingback(session, string_data(namearg->value), NULL,
+                           SAM_KEY_NOT_FOUND, message);
+        } else {
+            arg_t* namearg = ARG_FIND("NAME");
+            assert(namearg != NULL);
+            sam_namingback(session, string_data(namearg->value), NULL,
+                           SAM_UNKNOWN, message);
+        }
+        
+    } else if (ARG_IS(0,"STREAM")) {
+        sam_sid_t stream_id;
+        arg = ARG_FIND("ID");
+        assert(arg != 0);
+        stream_id = strtol_checked(string_data(arg->value));
+
+        if(ARG_IS(1,"CLOSED")) {
+            arg = ARG_FIND("RESULT");
+            assert(arg != NULL);
+            if (string_is(arg->value,"OK")) {
+                sam_closeback(session, stream_id, SAM_OK, message);
+            } else if (string_is(arg->value,"CANT_REACH_PEER")) {
+                sam_closeback(session, stream_id, SAM_CANT_REACH_PEER, message);
+            } else if (string_is(arg->value,"I2P_ERROR")) {
+                sam_closeback(session, stream_id, SAM_I2P_ERROR, message);
+            } else if (string_is(arg->value,"PEER_NOT_FOUND")) {
+                sam_closeback(session, stream_id, SAM_PEER_NOT_FOUND, message);
+            } else if (string_is(arg->value,"TIMEOUT")) {
+                sam_closeback(session, stream_id, SAM_TIMEOUT, message);
+            } else {
+                sam_closeback(session, stream_id, SAM_UNKNOWN, message);
+            }
+
+	} else if(ARG_IS(1,"CONNECTED")) {
 		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 (data == NULL) {
-			SAMLOGS("Out of memory");
-			abort();
-		}
-		if (sam_read2(session, data, size) != -1) {
-			p = data + size;
-			*p = '\0';  /* see above NUL note */
-			sam_dgramback(session, 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++;
+                arg = ARG_FIND("DESTINATION");
+                assert(arg != NULL);
+                _scr(arg->value, dest, sizeof dest);
 
-		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(session, 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(session, 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(session, name, NULL, SAM_KEY_NOT_FOUND);
-
-		} else {
-			q = strchr(p, ' ');  /* ' 'MES.. (optional) */
-			if (q != NULL)
-				*q = '\0';
-			strlcpy(name, p, sizeof name);
-			sam_namingback(session, 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(session, stream_id, SAM_OK);
-		else if (strncmp(p, "CANT_REACH_PEER", strlen("CANT_REACH_PEER")) == 0)
-			sam_closeback(session, stream_id, SAM_CANT_REACH_PEER);
-		else if (strncmp(p, "I2P_ERROR", strlen("I2P_ERROR")) == 0)
-			sam_closeback(session, stream_id, SAM_I2P_ERROR);
-		else if (strncmp(p, "PEER_NOT_FOUND", strlen("PEER_NOT_FOUND")) == 0)
-			sam_closeback(session, stream_id, SAM_PEER_NOT_FOUND);
-		else if (strncmp(p, "TIMEOUT", strlen("TIMEOUT")) == 0)
-			sam_closeback(session, stream_id, SAM_TIMEOUT);
-		else
-			sam_closeback(session, 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(session, 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;
+	} else if(ARG_IS(1,"RECEIVED")) {
 		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 */
+                arg = ARG_FIND("SIZE");
+                assert(arg != NULL);
+                size = strtol_checked(string_data(arg->value));
+
+		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 (data == NULL) {
 			SAMLOGS("Out of memory");
 			abort();
 		}
 		if (sam_read2(session, data, size) != -1) {
-			p = data + size;
+			char* p = data + size;
 			*p = '\0';  /* see above NUL note */
 			sam_databack(session, 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(session, stream_id, SAM_OK);
-		else if (strncmp(s, SAM_STREAM_STATUS_REPLY_CRP,
-				strlen(SAM_STREAM_STATUS_REPLY_CRP)) == 0)
-			sam_statusback(session, stream_id, SAM_CANT_REACH_PEER);
-		else if (strncmp(s, SAM_STREAM_STATUS_REPLY_I2E,
-				strlen(SAM_STREAM_STATUS_REPLY_I2E)) == 0)
-			sam_statusback(session, stream_id, SAM_I2P_ERROR);
-		else if (strncmp(s, SAM_STREAM_STATUS_REPLY_IK,
-				strlen(SAM_STREAM_STATUS_REPLY_IK)) == 0)
-			sam_statusback(session, stream_id, SAM_INVALID_KEY);
-		else if (strncmp(s, SAM_STREAM_STATUS_REPLY_TO,
-				strlen(SAM_STREAM_STATUS_REPLY_TO)) == 0)
-			sam_statusback(session, stream_id, SAM_TIMEOUT);
-		else
-			sam_statusback(session, stream_id, SAM_UNKNOWN);
+	} else if(ARG_IS(1,"STATUS")) {
+            arg = ARG_FIND("RESULT");
+            assert(arg != NULL);
+            if (string_is(arg->value,"OK")) {
+                sam_statusback(session, stream_id, SAM_OK, message);
+            } else if (string_is(arg->value,"CANT_REACH_PEER")) {
+                sam_statusback(session, stream_id, 
+                               SAM_CANT_REACH_PEER, message);
+            } else if (string_is(arg->value,"I2P_ERROR")) {
+                sam_statusback(session, stream_id, SAM_I2P_ERROR, message);
+            } else if (string_is(arg->value,"INVALID_KEY")) {
+                sam_statusback(session, stream_id, SAM_INVALID_KEY, message);
+            } else if (string_is(arg->value,"TIMEOUT")) {
+                sam_statusback(session, stream_id, SAM_TIMEOUT, message);
+            } else {
+                sam_statusback(session, stream_id, SAM_UNKNOWN, message);
+            }
+	}
+    } else
+        return 0;
+    return -1;
+}
 
-		return;
+#undef ARG_IS
+#undef ARG_FIND
 
-	} else
-		SAMLOG("Unknown SAM command received: %s", s);
-
-	return;
-}
 
 /*
  * Sends data to a destination in a raw packet
diff --git a/apps/sam/c/src/tinystring.c b/apps/sam/c/src/tinystring.c
new file mode 100644
index 0000000000..395a50a885
--- /dev/null
+++ b/apps/sam/c/src/tinystring.c
@@ -0,0 +1,128 @@
+#include "tinystring.h"
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <malloc.h>
+#define _GNU_SOURCE
+#include <string.h>
+
+#ifndef min
+#define min(a,b) ((a) > (b) ? (b) : (a))
+#endif
+
+extern char *strndup(const char *s, size_t n);
+
+
+struct string_s {
+    const char* data;
+    long int size;
+    bool _no_del; //SIGH...
+};
+
+string_t string_ncreate(const char* cstr,long int length) {
+    string_t self = malloc(sizeof(struct string_s));
+    self->size = length;
+    if(cstr) self->data = strndup(cstr,length);
+    else self->data = NULL;
+    self->_no_del = 0;
+    return self;
+}
+
+string_t string_create(const char* cstr) {
+    if(!cstr)
+        return string_ncreate(NULL, 0);
+    return string_ncreate(cstr, strlen(cstr));
+}
+
+string_t string_nwrap(const char* cstr, long int length) {
+    static struct string_s self;
+    self.size = length;
+    self.data = cstr;
+    self._no_del = 1;
+    return &self;
+}
+
+string_t string_wrap(const char* cstr) {
+    if(!cstr)
+        return string_nwrap(NULL, 0);
+    return string_nwrap(cstr, strlen(cstr));
+}
+
+string_t string_fmt(const char* fmt, ...) {
+    va_list args;
+    FILE* tmp = tmpfile();
+    string_t self = malloc(sizeof(struct string_s));
+    char* data;
+    va_start(args, fmt);
+    vfprintf(tmp, fmt, args);
+    va_end(args);
+
+    self->size = ftell(tmp);
+
+    rewind(tmp);
+    data = malloc(self->size);
+    fread(data, self->size, sizeof(char), tmp);
+
+    fclose(tmp);
+    self->data = data;
+    return self;
+}
+
+string_t string_cat(string_t head,string_t tail) {
+    //There are two ways to skin a cat...
+    string_t self = malloc(sizeof(struct string_s));
+    char* data;
+    self->size = head->size+tail->size;
+    data = malloc(self->size);
+    memcpy(data, head->data, head->size);
+    memcpy(data+head->size,tail->data,tail->size);
+    self->data = data;
+    return self;
+}
+
+/* Source Dest */
+void string_copy(string_t src,string_t dest) {
+    dest->data = realloc((char*)dest->data,src->size);
+    memcpy((char*)dest->data,src->data,dest->size);
+}
+
+void string_copy_raw(string_t src, void* dest,size_t size) {
+    size = min(src->size,size);
+    memcpy(dest,src->data,size);
+}
+
+const char* string_data(string_t self) {
+    return self->data;
+}
+
+long int string_size(string_t self) {
+    return self->size;
+}
+
+void string_free(string_t self) {
+    if(!self->_no_del)
+        free((char*)self->data);
+
+    free(self);
+}
+
+#ifndef min
+#define min(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+bool string_equal(string_t this,string_t that) {
+    return !memcmp(this->data,that->data,min(this->size,that->size));
+}
+
+bool string_equali(string_t this,string_t that) {
+    return !strncasecmp(this->data,that->data,min(this->size,that->size));
+}
+
+int string_cmp(string_t this,string_t that) {
+    return memcmp(this->data,that->data,min(this->size,that->size));
+}
+
+int string_cmpi(string_t this,string_t that) {
+    return strncasecmp(this->data,that->data,min(this->size,that->size));
+}
diff --git a/apps/sam/c/status b/apps/sam/c/status
new file mode 100644
index 0000000000..519f653d1c
--- /dev/null
+++ b/apps/sam/c/status
@@ -0,0 +1,4 @@
+#!/usr/bin/env perl
+
+printf "%-8s ",uc(shift @ARGV);
+print join(' ', @ARGV),"\n";
-- 
GitLab