Add libString.
diff --git a/src/libString/mkfile b/src/libString/mkfile
new file mode 100644
index 0000000..49803b4
--- /dev/null
+++ b/src/libString/mkfile
@@ -0,0 +1,27 @@
+PLAN9=../..
+<$PLAN9/src/mkhdr
+
+LIB=libString.a
+
+OFILES=\
+	s_alloc.$O\
+	s_append.$O\
+	s_array.$O\
+	s_copy.$O\
+	s_getline.$O\
+	s_grow.$O\
+	s_memappend.$O\
+	s_nappend.$O\
+	s_parse.$O\
+	s_putc.$O\
+	s_rdinstack.$O\
+	s_read.$O\
+	s_read_line.$O\
+	s_reset.$O\
+	s_terminate.$O\
+	s_tolower.$O\
+	s_unique.$O\
+
+HFILES=/sys/include/String.h
+
+<$PLAN9/src/mksyslib
diff --git a/src/libString/s_alloc.c b/src/libString/s_alloc.c
new file mode 100644
index 0000000..34f8916
--- /dev/null
+++ b/src/libString/s_alloc.c
@@ -0,0 +1,86 @@
+#include <u.h>
+#include <libc.h>
+#include "libString.h"
+
+#define STRLEN 128
+
+extern void
+s_free(String *sp)
+{
+	if (sp == nil)
+		return;
+	lock(&sp->lk);
+	if(--(sp->ref) != 0){
+		unlock(&sp->lk);
+		return;
+	}
+	unlock(&sp->lk);
+
+	if(sp->fixed == 0 && sp->base != nil)
+		free(sp->base);
+	free(sp);
+}
+
+/* get another reference to a string */
+extern String *
+s_incref(String *sp)
+{
+	lock(&sp->lk);
+	sp->ref++;
+	unlock(&sp->lk);
+
+	return sp;
+}
+
+/* allocate a String head */
+extern String *
+_s_alloc(void)
+{
+	String *s;
+
+	s = mallocz(sizeof *s, 1);
+	if(s == nil)
+		return s;
+	s->ref = 1;
+	s->fixed = 0;
+	return s;
+}
+
+/* create a new `short' String */
+extern String *
+s_newalloc(int len)
+{
+	String *sp;
+
+	sp = _s_alloc();
+	if(sp == nil)
+		sysfatal("s_newalloc: %r");
+	setmalloctag(sp, getcallerpc(&len));
+	if(len < STRLEN)
+		len = STRLEN;
+	sp->base = sp->ptr = malloc(len);
+	if (sp->base == nil)
+		sysfatal("s_newalloc: %r");
+	setmalloctag(sp->base, getcallerpc(&len));
+
+	sp->end = sp->base + len;
+	s_terminate(sp);
+	return sp;
+}
+
+/* create a new `short' String */
+extern String *
+s_new(void)
+{
+	String *sp;
+
+	sp = _s_alloc();
+	if(sp == nil)
+		sysfatal("s_new: %r");
+	sp->base = sp->ptr = malloc(STRLEN);
+	if (sp->base == nil)
+		sysfatal("s_new: %r");
+	sp->end = sp->base + STRLEN;
+	s_terminate(sp);
+	return sp;
+}
diff --git a/src/libString/s_append.c b/src/libString/s_append.c
new file mode 100644
index 0000000..1b02d20
--- /dev/null
+++ b/src/libString/s_append.c
@@ -0,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+#include "libString.h"
+
+/* append a char array to a String */
+String *
+s_append(String *to, char *from)
+{
+	if (to == 0)
+		to = s_new();
+	if (from == 0)
+		return to;
+	for(; *from; from++)
+		s_putc(to, *from);
+	s_terminate(to);
+	return to;
+}
diff --git a/src/libString/s_array.c b/src/libString/s_array.c
new file mode 100644
index 0000000..3cf571b
--- /dev/null
+++ b/src/libString/s_array.c
@@ -0,0 +1,17 @@
+#include <u.h>
+#include <libc.h>
+#include "libString.h"
+
+extern String*	_s_alloc(void);
+
+/* return a String containing a character array (this had better not grow) */
+extern String *
+s_array(char *cp, int len)
+{
+	String *sp = _s_alloc();
+
+	sp->base = sp->ptr = cp;
+	sp->end = sp->base + len;
+	sp->fixed = 1;
+	return sp;
+}
diff --git a/src/libString/s_copy.c b/src/libString/s_copy.c
new file mode 100644
index 0000000..5a23b3c
--- /dev/null
+++ b/src/libString/s_copy.c
@@ -0,0 +1,19 @@
+#include <u.h>
+#include <libc.h>
+#include "libString.h"
+
+
+/* return a String containing a copy of the passed char array */
+extern String*
+s_copy(char *cp)
+{
+	String *sp;
+	int len;
+
+	len = strlen(cp)+1;
+	sp = s_newalloc(len);
+	setmalloctag(sp, getcallerpc(&cp));
+	strcpy(sp->base, cp);
+	sp->ptr = sp->base + len - 1;		/* point to 0 terminator */
+	return sp;
+}
diff --git a/src/libString/s_getline.c b/src/libString/s_getline.c
new file mode 100644
index 0000000..e46dc12
--- /dev/null
+++ b/src/libString/s_getline.c
@@ -0,0 +1,72 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "libString.h"
+
+/* Append an input line to a String.
+ *
+ * Returns a pointer to the character string (or 0).
+ * Leading whitespace and newlines are removed.
+ *
+ * Empty lines and lines starting with '#' are ignored.
+ */ 
+extern char *
+s_getline(Biobuf *fp, String *to)
+{
+	int c;
+	int len=0;
+
+	s_terminate(to);
+
+	/* end of input */
+	if ((c = Bgetc(fp)) < 0)
+		return 0;
+
+	/* take care of inconsequentials */
+	for(;;) {
+		/* eat leading white */
+		while(c==' ' || c=='\t' || c=='\n' || c=='\r')
+			c = Bgetc(fp);
+
+		if(c < 0)
+			return 0;
+
+		/* take care of comments */
+		if(c == '#'){
+			do {
+				c = Bgetc(fp);
+				if(c < 0)
+					return 0;
+			} while(c != '\n');
+			continue;
+		}
+
+		/* if we got here, we've gotten something useful */
+		break;
+	}
+
+	/* gather up a line */
+	for(;;) {
+		len++;
+		switch(c) {
+		case -1:
+			s_terminate(to);
+			return len ? to->ptr-len : 0;
+		case '\\':
+			c = Bgetc(fp);
+			if (c != '\n') {
+				s_putc(to, '\\');
+				s_putc(to, c);
+			}
+			break;
+		case '\n':
+			s_terminate(to);
+			return len ? to->ptr-len : 0;
+		default:
+			s_putc(to, c);
+			break;
+		}
+		c = Bgetc(fp);
+	}
+	return 0;
+}
diff --git a/src/libString/s_grow.c b/src/libString/s_grow.c
new file mode 100644
index 0000000..5cf2a14
--- /dev/null
+++ b/src/libString/s_grow.c
@@ -0,0 +1,34 @@
+#include <u.h>
+#include <libc.h>
+#include "libString.h"
+
+/* grow a String's allocation by at least `incr' bytes */
+extern String*
+s_grow(String *s, int incr)	
+{
+	char *cp;
+	int size;
+
+	if(s->fixed)
+		sysfatal("s_grow of constant string");
+	s = s_unique(s);
+
+	/*
+	 *  take a larger increment to avoid mallocing too often
+	 */
+	size = s->end-s->base;
+	if(size/2 < incr)
+		size += incr;
+	else
+		size += size/2;
+
+	cp = realloc(s->base, size);
+	if (cp == 0)
+		sysfatal("s_grow: %r");
+	s->ptr = (s->ptr - s->base) + cp;
+	s->end = cp + size;
+	s->base = cp;
+
+	return s;
+}
+
diff --git a/src/libString/s_memappend.c b/src/libString/s_memappend.c
new file mode 100644
index 0000000..27b6985
--- /dev/null
+++ b/src/libString/s_memappend.c
@@ -0,0 +1,20 @@
+#include <u.h>
+#include <libc.h>
+#include "libString.h"
+
+/* append a char array ( of up to n characters) to a String */
+String *
+s_memappend(String *to, char *from, int n)
+{
+	char *e;
+
+	if (to == 0)
+		to = s_new();
+	if (from == 0)
+		return to;
+	for(e = from + n; from < e; from++)
+		s_putc(to, *from);
+	s_terminate(to);
+	return to;
+}
+
diff --git a/src/libString/s_nappend.c b/src/libString/s_nappend.c
new file mode 100644
index 0000000..fb41f93
--- /dev/null
+++ b/src/libString/s_nappend.c
@@ -0,0 +1,18 @@
+#include <u.h>
+#include <libc.h>
+#include "libString.h"
+
+/* append a char array ( of up to n characters) to a String */
+String *
+s_nappend(String *to, char *from, int n)
+{
+	if (to == 0)
+		to = s_new();
+	if (from == 0)
+		return to;
+	for(; n && *from; from++, n--)
+		s_putc(to, *from);
+	s_terminate(to);
+	return to;
+}
+
diff --git a/src/libString/s_parse.c b/src/libString/s_parse.c
new file mode 100644
index 0000000..376a41e
--- /dev/null
+++ b/src/libString/s_parse.c
@@ -0,0 +1,40 @@
+#include <u.h>
+#include <libc.h>
+#include "libString.h"
+
+#define isspace(c) ((c)==' ' || (c)=='\t' || (c)=='\n')
+
+/* Get the next field from a String.  The field is delimited by white space,
+ * single or double quotes.
+ */
+String *
+s_parse(String *from, String *to)
+{
+	if (*from->ptr == '\0')
+		return 0;
+	if (to == 0)
+		to = s_new();
+	if (*from->ptr == '\'') {
+		from->ptr++;
+		for (;*from->ptr != '\'' && *from->ptr != '\0'; from->ptr++)
+			s_putc(to, *from->ptr);
+		if (*from->ptr == '\'')	
+			from->ptr++;
+	} else if (*from->ptr == '"') {
+		from->ptr++;
+		for (;*from->ptr != '"' && *from->ptr != '\0'; from->ptr++)
+			s_putc(to, *from->ptr);
+		if (*from->ptr == '"')	
+			from->ptr++;
+	} else {
+		for (;!isspace(*from->ptr) && *from->ptr != '\0'; from->ptr++)
+			s_putc(to, *from->ptr);
+	}
+	s_terminate(to);
+
+	/* crunch trailing white */
+	while(isspace(*from->ptr))
+		from->ptr++;
+
+	return to;
+}
diff --git a/src/libString/s_putc.c b/src/libString/s_putc.c
new file mode 100644
index 0000000..29a385d
--- /dev/null
+++ b/src/libString/s_putc.c
@@ -0,0 +1,13 @@
+#include <u.h>
+#include <libc.h>
+#include "libString.h"
+
+void
+s_putc(String *s, int c)
+{
+	if(s->ref > 1)
+		sysfatal("can't s_putc a shared string");
+	if (s->ptr >= s->end)
+		s_grow(s, 2);
+	*(s->ptr)++ = c;
+}
diff --git a/src/libString/s_rdinstack.c b/src/libString/s_rdinstack.c
new file mode 100644
index 0000000..de12d21
--- /dev/null
+++ b/src/libString/s_rdinstack.c
@@ -0,0 +1,141 @@
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "libString.h"
+
+struct Sinstack{
+	int	depth;
+	Biobuf	*fp[32];	/* hard limit to avoid infinite recursion */
+};
+
+/* initialize */
+extern Sinstack *
+s_allocinstack(char *file)
+{
+	Sinstack *sp;
+	Biobuf *fp;
+
+	fp = Bopen(file, OREAD);
+	if(fp == nil)
+		return nil;
+
+	sp = malloc(sizeof *sp);
+	sp->depth = 0;
+	sp->fp[0] = fp;
+	return sp;
+}
+
+extern void
+s_freeinstack(Sinstack *sp)
+{
+	while(sp->depth >= 0)
+		Bterm(sp->fp[sp->depth--]);
+	free(sp);
+}
+
+/*  Append an input line to a String.
+ *
+ *  Empty lines and leading whitespace are removed.
+ */
+static char *
+rdline(Biobuf *fp, String *to)
+{
+	int c;
+	int len = 0;
+
+	c = Bgetc(fp);
+
+	/* eat leading white */
+	while(c==' ' || c=='\t' || c=='\n' || c=='\r')
+		c = Bgetc(fp);
+
+	if(c < 0)
+		return 0;
+
+	for(;;){
+		switch(c) {
+		case -1:
+			goto out;
+		case '\\':
+			c = Bgetc(fp);
+			if (c != '\n') {
+				s_putc(to, '\\');
+				s_putc(to, c);
+				len += 2;
+			}
+			break;
+		case '\r':
+			break;
+		case '\n':
+			if(len != 0)
+				goto out;
+			break;
+		default:
+			s_putc(to, c);
+			len++;
+			break;
+		}
+		c = Bgetc(fp);
+	}
+out:
+	s_terminate(to);
+	return to->ptr - len;
+}
+
+/* Append an input line to a String.
+ *
+ * Returns a pointer to the character string (or 0).
+ * Leading whitespace and newlines are removed.
+ * Lines starting with #include cause us to descend into the new file.
+ * Empty lines and other lines starting with '#' are ignored.
+ */ 
+extern char *
+s_rdinstack(Sinstack *sp, String *to)
+{
+	char *p;
+	Biobuf *fp, *nfp;
+
+	s_terminate(to);
+	fp = sp->fp[sp->depth];
+
+	for(;;){
+		p = rdline(fp, to);
+		if(p == nil){
+			if(sp->depth == 0)
+				break;
+			Bterm(fp);
+			sp->depth--;
+			return s_rdinstack(sp, to);
+		}
+
+		if(strncmp(p, "#include", 8) == 0 && (p[8] == ' ' || p[8] == '\t')){
+			to->ptr = p;
+			p += 8;
+
+			/* sanity (and looping) */
+			if(sp->depth >= nelem(sp->fp))
+				sysfatal("s_recgetline: includes too deep");
+
+			/* skip white */
+			while(*p == ' ' || *p == '\t')
+				p++;
+
+			nfp = Bopen(p, OREAD);
+			if(nfp == nil)
+				continue;
+			sp->depth++;
+			sp->fp[sp->depth] = nfp;
+			return s_rdinstack(sp, to);
+		}
+
+		/* got milk? */
+		if(*p != '#')
+			break;
+
+		/* take care of comments */
+		to->ptr = p;
+		s_terminate(to);
+	}
+	return p;
+}
diff --git a/src/libString/s_read.c b/src/libString/s_read.c
new file mode 100644
index 0000000..59581ea
--- /dev/null
+++ b/src/libString/s_read.c
@@ -0,0 +1,38 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "libString.h"
+
+enum
+{
+	Minread=	256,
+};
+
+/* Append up to 'len' input bytes to the string 'to'.
+ *
+ * Returns the number of characters read.
+ */ 
+extern int
+s_read(Biobuf *fp, String *to, int len)
+{
+	int rv;
+	int n;
+
+	if(to->ref > 1)
+		sysfatal("can't s_read a shared string");
+	for(rv = 0; rv < len; rv += n){
+		n = to->end - to->ptr;
+		if(n < Minread){
+			s_grow(to, Minread);
+			n = to->end - to->ptr;
+		}
+		if(n > len - rv)
+			n = len - rv;
+		n = Bread(fp, to->ptr, n);
+		if(n <= 0)
+			break;
+		to->ptr += n;
+	}
+	s_terminate(to);
+	return rv;
+}
diff --git a/src/libString/s_read_line.c b/src/libString/s_read_line.c
new file mode 100644
index 0000000..b1de5ac
--- /dev/null
+++ b/src/libString/s_read_line.c
@@ -0,0 +1,31 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "libString.h"
+
+/* Append an input line to a String.
+ *
+ * Returns a pointer to the character string (or 0).
+ * Trailing newline is left on.
+ */ 
+extern char *
+s_read_line(Biobuf *fp, String *to)
+{
+	char *cp;
+	int llen;
+
+	if(to->ref > 1)
+		sysfatal("can't s_read_line a shared string");
+	s_terminate(to);
+	cp = Brdline(fp, '\n');
+	if(cp == 0)
+		return 0;
+	llen = Blinelen(fp);
+	if(to->end - to->ptr < llen)
+		s_grow(to, llen);
+	memmove(to->ptr, cp, llen);
+	cp = to->ptr;
+	to->ptr += llen;
+	s_terminate(to);
+	return cp;
+}
diff --git a/src/libString/s_reset.c b/src/libString/s_reset.c
new file mode 100644
index 0000000..cd2a742
--- /dev/null
+++ b/src/libString/s_reset.c
@@ -0,0 +1,23 @@
+#include <u.h>
+#include <libc.h>
+#include "libString.h"
+
+String*
+s_reset(String *s)
+{
+	if(s != nil){
+		s = s_unique(s);
+		s->ptr = s->base;
+		*s->ptr = '\0';
+	} else
+		s = s_new();
+	return s;
+}
+
+String*
+s_restart(String *s)
+{
+	s = s_unique(s);
+	s->ptr = s->base;
+	return s;
+}
diff --git a/src/libString/s_terminate.c b/src/libString/s_terminate.c
new file mode 100644
index 0000000..dc893ab
--- /dev/null
+++ b/src/libString/s_terminate.c
@@ -0,0 +1,13 @@
+#include <u.h>
+#include <libc.h>
+#include "libString.h"
+
+void
+s_terminate(String *s)
+{
+	if(s->ref > 1)
+		sysfatal("can't s_terminate a shared string");
+	if (s->ptr >= s->end)
+		s_grow(s, 1);
+	*s->ptr = 0;
+}
diff --git a/src/libString/s_tolower.c b/src/libString/s_tolower.c
new file mode 100644
index 0000000..737fff1
--- /dev/null
+++ b/src/libString/s_tolower.c
@@ -0,0 +1,15 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include "libString.h"
+
+
+/* convert String to lower case */
+void
+s_tolower(String *sp)
+{
+	char *cp;
+
+	for(cp=sp->ptr; *cp; cp++)
+		*cp = tolower(*cp);
+}
diff --git a/src/libString/s_unique.c b/src/libString/s_unique.c
new file mode 100644
index 0000000..134411c
--- /dev/null
+++ b/src/libString/s_unique.c
@@ -0,0 +1,16 @@
+#include <u.h>
+#include <libc.h>
+#include "libString.h"
+
+String*
+s_unique(String *s)
+{
+	String *p;
+
+	if(s->ref > 1){
+		p = s;
+		s = s_clone(p);
+		s_free(p);
+	}
+	return s;
+}