Add libString.
diff --git a/include/html.h b/include/html.h
new file mode 100644
index 0000000..019ad73
--- /dev/null
+++ b/include/html.h
@@ -0,0 +1,629 @@
+#ifndef _HTML_H_
+#define _HTML_H_ 1
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ #pragma lib "libhtml.a"
+ #pragma src "/sys/src/libhtml"
+*/
+
+// UTILS
+extern uchar*	fromStr(Rune* buf, int n, int chset);
+extern Rune*	toStr(uchar* buf, int n, int chset);
+
+// Common LEX and BUILD enums
+
+// Media types
+enum
+{
+	ApplMsword,
+	ApplOctets,
+	ApplPdf,
+	ApplPostscript,
+	ApplRtf,
+	ApplFramemaker,
+	ApplMsexcel,
+	ApplMspowerpoint,
+	UnknownType,
+	Audio32kadpcm,
+	AudioBasic,
+	ImageCgm,
+	ImageG3fax,
+	ImageGif,
+	ImageIef,
+	ImageJpeg,
+	ImagePng,
+	ImageTiff,
+	ImageXBit,
+	ImageXBit2,
+	ImageXBitmulti,
+	ImageXXBitmap,
+	ModelVrml,
+	MultiDigest,
+	MultiMixed,
+	TextCss,
+	TextEnriched,
+	TextHtml,
+	TextJavascript,
+	TextPlain,
+	TextRichtext,
+	TextSgml,
+	TextTabSeparatedValues,
+	TextXml,
+	VideoMpeg,
+	VideoQuicktime,
+	NMEDIATYPES
+};
+
+// HTTP methods
+enum
+{
+	HGet,
+	HPost
+};
+
+// Charsets
+enum
+{
+	UnknownCharset,
+	US_Ascii,
+	ISO_8859_1,
+	UTF_8,
+	Unicode,
+	NCHARSETS
+};
+
+// Frame Target IDs
+enum {
+	FTtop,
+	FTself,
+	FTparent,
+	FTblank
+};
+
+// LEX
+typedef struct Token Token;
+typedef struct Attr Attr;
+
+// BUILD
+
+typedef struct Item Item;
+typedef struct Itext Itext;
+typedef struct Irule Irule;
+typedef struct Iimage Iimage;
+typedef struct Iformfield Iformfield;
+typedef struct Itable Itable;
+typedef struct Ifloat Ifloat;
+typedef struct Ispacer Ispacer;
+typedef struct Genattr Genattr;
+typedef struct SEvent SEvent;
+typedef struct Formfield Formfield;
+typedef struct Option Option;
+typedef struct Form Form;
+typedef struct Table Table;
+typedef struct Tablecol Tablecol;
+typedef struct Tablerow Tablerow;
+typedef struct Tablecell Tablecell;
+typedef struct Align Align;
+typedef struct Dimen Dimen;
+typedef struct Anchor Anchor;
+typedef struct DestAnchor DestAnchor;
+typedef struct Map Map;
+typedef struct Area Area;
+typedef struct Background Background;
+typedef struct Kidinfo Kidinfo;
+typedef struct Docinfo Docinfo;
+typedef struct Stack Stack;
+typedef struct Pstate Pstate;
+typedef struct ItemSource ItemSource;
+typedef struct Lay Lay;	// defined in Layout module
+
+// Alignment types
+enum {
+	ALnone = 0, ALleft, ALcenter, ALright, ALjustify,
+	ALchar, ALtop, ALmiddle, ALbottom, ALbaseline
+};
+
+struct Align
+{
+	uchar	halign;	// one of ALnone, ALleft, etc.
+	uchar	valign;	// one of ALnone, ALtop, etc.
+};
+
+// A Dimen holds a dimension specification, especially for those
+// cases when a number can be followed by a % or a * to indicate
+// percentage of total or relative weight.
+// Dnone means no dimension was specified
+
+// To fit in a word, use top bits to identify kind, rest for value
+enum {
+	Dnone =		0,
+	Dpixels =		(1<<29),
+	Dpercent =	(2<<29),
+	Drelative =	(3<<29),
+	Dkindmask =	(3<<29),
+	Dspecmask =	(~Dkindmask)
+};
+
+struct Dimen
+{
+	int	kindspec;		// kind | spec
+};
+
+// Background is either an image or a color.
+// If both are set, the image has precedence.
+struct Background
+{
+	Rune*	image;	// url
+	int		color;
+};
+
+
+// There are about a half dozen Item variants.
+// The all look like this at the start (using Plan 9 C's
+// anonymous structure member mechanism),
+// and then the tag field dictates what extra fields there are.
+struct Item
+{
+	Item*	next;		// successor in list of items
+	int		width;	// width in pixels (0 for floating items)
+	int		height;	// height in pixels
+	int		ascent;	// ascent (from top to baseline) in pixels
+	int		anchorid;	// if nonzero, which anchor we're in
+	int		state;	// flags and values (see below)
+	Genattr*	genattr;	// generic attributes and events
+	int		tag;		// variant discriminator: Itexttag, etc.
+};
+
+// Item variant tags
+enum {
+	Itexttag,
+	Iruletag,
+	Iimagetag,
+	Iformfieldtag,
+	Itabletag,
+	Ifloattag,
+	Ispacertag
+};
+
+struct Itext
+{
+	Item _item;				// (with tag ==Itexttag)
+	Rune*	s;			// the characters
+	int		fnt;			// style*NumSize+size (see font stuff, below)
+	int		fg;			// Pixel (color) for text
+	uchar	voff;			// Voffbias+vertical offset from baseline, in pixels (+ve == down)
+	uchar	ul;			// ULnone, ULunder, or ULmid
+};
+
+struct Irule
+{
+	Item _item;				// (with tag ==Iruletag)
+	uchar	align;		// alignment spec
+	uchar	noshade;		// if true, don't shade
+	int		size;			// size attr (rule height)
+	Dimen	wspec;		// width spec
+};
+
+
+struct Iimage
+{
+	Item _item;				// (with tag ==Iimagetag)
+	Rune*	imsrc;		// image src url
+	int		imwidth;		// spec width (actual, if no spec)
+	int		imheight;		// spec height (actual, if no spec)
+	Rune*	altrep;		// alternate representation, in absence of image
+	Map*	map;			// if non-nil, client side map
+	int		ctlid;			// if animated
+	uchar	align;		// vertical alignment
+	uchar	hspace;		// in pixels; buffer space on each side
+	uchar	vspace;		// in pixels; buffer space on top and bottom
+	uchar	border;		// in pixels: border width to draw around image
+	Iimage*	nextimage;	// next in list of document's images
+};
+
+
+struct Iformfield
+{
+	Item _item;				// (with tag ==Iformfieldtag)
+	Formfield*	formfield;
+};
+
+
+struct Itable
+{
+	Item _item;				// (with tag ==Itabletag)
+	Table*	table;
+};
+
+
+struct Ifloat
+{
+	Item _item;				// (with tag ==Ifloattag)
+	Item*	item;			// table or image item that floats
+	int		x;			// x coord of top (from right, if ALright)
+	int		y;			// y coord of top
+	uchar	side;			// margin it floats to: ALleft or ALright
+	uchar	infloats;		// true if this has been added to a lay.floats
+	Ifloat*	nextfloat;		// in list of floats
+};
+
+
+struct Ispacer
+{
+	Item _item;				// (with tag ==Ispacertag)
+	int		spkind;		// ISPnull, etc.
+};
+
+// Item state flags and value fields
+enum {
+	IFbrk =			0x80000000,	// forced break before this item
+	IFbrksp =			0x40000000,	// add 1 line space to break (IFbrk set too)
+	IFnobrk =			0x20000000,	// break not allowed before this item
+	IFcleft =			0x10000000,	// clear left floats (IFbrk set too)
+	IFcright =			0x08000000,	// clear right floats (IFbrk set too)
+	IFwrap =			0x04000000,	// in a wrapping (non-pre) line
+	IFhang =			0x02000000,	// in a hanging (into left indent) item
+	IFrjust =			0x01000000,	// right justify current line
+	IFcjust =			0x00800000,	// center justify current line
+	IFsmap =			0x00400000,	// image is server-side map
+	IFindentshift =		8,
+	IFindentmask =		(255<<IFindentshift),	// current indent, in tab stops
+	IFhangmask =		255			// current hang into left indent, in 1/10th tabstops
+};
+
+// Bias added to Itext's voff field
+enum { Voffbias = 128 };
+
+// Spacer kinds
+enum {
+	ISPnull,			// 0 height and width
+	ISPvline,			// height and ascent of current font
+	ISPhspace,		// width of space in current font
+	ISPgeneral		// other purposes (e.g., between markers and list)
+};
+
+// Generic attributes and events (not many elements will have any of these set)
+struct Genattr
+{
+	Rune*	id;
+	Rune*	class;
+	Rune*	style;
+	Rune*	title;
+	SEvent*	events;
+};
+
+struct SEvent
+{
+	SEvent*	next;		// in list of events
+	int		type;		// SEonblur, etc.
+	Rune*	script;
+};
+
+enum {
+	SEonblur, SEonchange, SEonclick, SEondblclick,
+	SEonfocus, SEonkeypress, SEonkeyup, SEonload,
+	SEonmousedown, SEonmousemove, SEonmouseout,
+	SEonmouseover, SEonmouseup, SEonreset, SEonselect,
+	SEonsubmit, SEonunload,
+	Numscriptev
+};
+
+// Form field types
+enum {
+	Ftext,
+	Fpassword,
+	Fcheckbox,
+	Fradio,
+	Fsubmit,
+	Fhidden,
+	Fimage,
+	Freset,
+	Ffile,
+	Fbutton,
+	Fselect,
+	Ftextarea
+};
+
+// Information about a field in a form
+struct Formfield
+{
+	Formfield*	next;		// in list of fields for a form
+	int			ftype;	// Ftext, Fpassword, etc.
+	int			fieldid;	// serial no. of field within its form
+	Form*		form;	// containing form
+	Rune*		name;	// name attr
+	Rune*		value;	// value attr
+	int			size;		// size attr
+	int			maxlength;	// maxlength attr
+	int			rows;	// rows attr
+	int			cols;		// cols attr
+	uchar		flags;	// FFchecked, etc.
+	Option*		options;	// for Fselect fields
+	Item*		image;	// image item, for Fimage fields
+	int			ctlid;		// identifies control for this field in layout
+	SEvent*		events;	// same as genattr->events of containing item
+};
+
+enum {
+	FFchecked =	(1<<7),
+	FFmultiple =	(1<<6)
+};
+
+// Option holds info about an option in a "select" form field
+struct Option
+{
+	Option*	next;			// next in list of options for a field
+	int		selected;		// true if selected initially
+	Rune*	value;		// value attr
+	Rune*	display;		// display string
+};
+
+// Form holds info about a form
+struct Form
+{
+	Form*		next;		// in list of forms for document
+	int			formid;	// serial no. of form within its doc
+	Rune*		name;	// name or id attr (netscape uses name, HTML 4.0 uses id)
+	Rune*		action;	// action attr
+	int			target;	// target attr as targetid
+	int			method;	// HGet or HPost
+	int			nfields;	// number of fields
+	Formfield*	fields;	// field's forms, in input order
+};
+
+// Flags used in various table structures
+enum {
+	TFparsing =	(1<<7),
+	TFnowrap =	(1<<6),
+	TFisth =		(1<<5)
+};
+
+
+// Information about a table
+struct Table
+{
+	Table*		next;			// next in list of document's tables
+	int			tableid;		// serial no. of table within its doc
+	Tablerow*	rows;		// array of row specs (list during parsing)
+	int			nrow;		// total number of rows
+	Tablecol*		cols;			// array of column specs
+	int			ncol;			// total number of columns
+	Tablecell*		cells;			// list of unique cells
+	int			ncell;		// total number of cells
+	Tablecell***	grid;			// 2-D array of cells
+	Align		align;		// alignment spec for whole table
+	Dimen		width;		// width spec for whole table
+	int			border;		// border attr
+	int			cellspacing;	// cellspacing attr
+	int			cellpadding;	// cellpadding attr
+	Background	background;	// table background
+	Item*		caption;		// linked list of Items, giving caption
+	uchar		caption_place;	// ALtop or ALbottom
+	Lay*			caption_lay;	// layout of caption
+	int			totw;			// total width
+	int			toth;			// total height
+	int			caph;		// caption height
+	int			availw;		// used for previous 3 sizes
+	Token*		tabletok;		// token that started the table
+	uchar		flags;		// Lchanged, perhaps
+};
+
+
+struct Tablecol
+{
+	int		width;
+	Align	align;
+	Point		pos;
+};
+
+
+struct Tablerow
+{
+	Tablerow*	next;			// Next in list of rows, during parsing
+	Tablecell*		cells;			// Cells in row, linked through nextinrow
+	int			height;
+	int			ascent;
+	Align		align;
+	Background	background;
+	Point			pos;
+	uchar		flags;		// 0 or TFparsing
+};
+
+
+// A Tablecell is one cell of a table.
+// It may span multiple rows and multiple columns.
+// Cells are linked on two lists: the list for all the cells of
+// a document (the next pointers), and the list of all the
+// cells that start in a given row (the nextinrow pointers)
+struct Tablecell
+{
+	Tablecell*		next;			// next in list of table's cells
+	Tablecell*		nextinrow;	// next in list of row's cells
+	int			cellid;		// serial no. of cell within table
+	Item*		content;		// contents before layout
+	Lay*			lay;			// layout of cell
+	int			rowspan;		// number of rows spanned by this cell
+	int			colspan;		// number of cols spanned by this cell
+	Align		align;		// alignment spec
+	uchar		flags;		// TFparsing, TFnowrap, TFisth
+	Dimen		wspec;		// suggested width
+	int			hspec;		// suggested height
+	Background	background;	// cell background
+	int			minw;		// minimum possible width
+	int			maxw;		// maximum width
+	int			ascent;		// cell's ascent
+	int			row;			// row of upper left corner
+	int			col;			// col of upper left corner
+	Point			pos;			// nw corner of cell contents, in cell
+};
+
+// Anchor is for info about hyperlinks that go somewhere
+struct Anchor
+{
+	Anchor*		next;		// next in list of document's anchors
+	int			index;	// serial no. of anchor within its doc
+	Rune*		name;	// name attr
+	Rune*		href;		// href attr
+	int			target;	// target attr as targetid
+};
+
+
+// DestAnchor is for info about hyperlinks that are destinations
+struct DestAnchor
+{
+	DestAnchor*	next;		// next in list of document's destanchors
+	int			index;	// serial no. of anchor within its doc
+	Rune*		name;	// name attr
+	Item*		item;		// the destination
+};
+
+
+// Maps (client side)
+struct Map
+{
+	Map*	next;			// next in list of document's maps
+	Rune*	name;		// map name
+	Area*	areas;		// list of map areas
+};
+
+
+struct Area
+{
+	Area*		next;		// next in list of a map's areas
+	int			shape;	// SHrect, etc.
+	Rune*		href;		// associated hypertext link
+	int			target;	// associated target frame
+	Dimen*		coords;	// array of coords for shape
+	int			ncoords;	// size of coords array
+};
+
+// Area shapes
+enum {
+	SHrect, SHcircle, SHpoly
+};
+
+// Fonts are represented by integers: style*NumSize + size
+
+// Font styles
+enum {
+	FntR,			// roman
+	FntI,			// italic
+	FntB,			// bold
+	FntT,			// typewriter
+	NumStyle
+};
+
+// Font sizes
+enum {
+	Tiny,
+	Small,
+	Normal,
+	Large,
+	Verylarge,
+	NumSize
+};
+
+enum {
+	NumFnt = (NumStyle*NumSize),
+	DefFnt = (FntR*NumSize+Normal)
+};
+
+// Lines are needed through some text items, for underlining or strikethrough
+enum {
+	ULnone, ULunder, ULmid
+};
+
+// Kidinfo flags
+enum {
+	FRnoresize =	(1<<0),
+	FRnoscroll =	(1<<1),
+	FRhscroll = 	(1<<2),
+	FRvscroll =	(1<<3),
+	FRhscrollauto = (1<<4),
+	FRvscrollauto =	(1<<5)
+};
+
+// Information about child frame or frameset
+struct Kidinfo
+{
+	Kidinfo*		next;		// in list of kidinfos for a frameset
+	int			isframeset;
+
+	// fields for "frame"
+	Rune*		src;		// only nil if a "dummy" frame or this is frameset
+	Rune*		name;	// always non-empty if this isn't frameset
+	int			marginw;
+	int			marginh;
+	int			framebd;
+	int			flags;
+
+	// fields for "frameset"
+	Dimen*		rows;	// array of row dimensions
+	int			nrows;	// length of rows
+	Dimen*		cols;		// array of col dimensions
+	int			ncols;	// length of cols
+	Kidinfo*		kidinfos;
+	Kidinfo*		nextframeset;	// parsing stack
+};
+
+
+// Document info (global information about HTML page)
+struct Docinfo
+{
+	// stuff from HTTP headers, doc head, and body tag
+	Rune*		src;				// original source of doc
+	Rune*		base;			// base URL of doc
+	Rune*		doctitle;			// from <title> element
+	Background	background;		// background specification
+	Iimage*		backgrounditem;	// Image Item for doc background image, or nil
+	int			text;				// doc foreground (text) color
+	int			link;				// unvisited hyperlink color
+	int			vlink;			// visited hyperlink color
+	int			alink;			// highlighting hyperlink color
+	int			target;			// target frame default
+	int			chset;			// ISO_8859, etc.
+	int			mediatype;		// TextHtml, etc.
+	int			scripttype;		// TextJavascript, etc.
+	int			hasscripts;		// true if scripts used
+	Rune*		refresh;			// content of <http-equiv=Refresh ...>
+	Kidinfo*		kidinfo;			// if a frameset
+	int			frameid;			// id of document frame
+
+	// info needed to respond to user actions
+	Anchor*		anchors;			// list of href anchors
+	DestAnchor*	dests;			// list of destination anchors
+	Form*		forms;			// list of forms
+	Table*		tables;			// list of tables
+	Map*		maps;			// list of maps
+	Iimage*		images;			// list of image items (through nextimage links)
+};
+
+extern int			dimenkind(Dimen d);
+extern int			dimenspec(Dimen d);
+extern void		freedocinfo(Docinfo* d);
+extern void		freeitems(Item* ithead);
+extern Item*		parsehtml(uchar* data, int datalen, Rune* src, int mtype, int chset, Docinfo** pdi);
+extern void		printitems(Item* items, char* msg);
+extern int			targetid(Rune* s);
+extern Rune*		targetname(int targid);
+extern int			validitems(Item* i);
+
+#pragma varargck	type "I"	Item*
+
+// Control print output
+extern int			warn;
+extern int			dbglex;
+extern int			dbgbuild;
+
+// To be provided by caller
+// emalloc and erealloc should not return if can't get memory.
+// emalloc should zero its memory.
+extern void*	emalloc(ulong);
+extern void*	erealloc(void* p, ulong size);
+#ifdef __cpluspplus
+}
+#endif
+#endif
diff --git a/include/libString.h b/include/libString.h
new file mode 100644
index 0000000..d848706
--- /dev/null
+++ b/include/libString.h
@@ -0,0 +1,46 @@
+/*
+#pragma	src	"/sys/src/libString"
+#pragma	lib	"libString.a"
+*/
+
+/* extensible Strings */
+typedef struct String {
+	Lock	lk;
+	char	*base;	/* base of String */
+	char	*end;	/* end of allocated space+1 */
+	char	*ptr;	/* ptr into String */
+	short	ref;
+	uchar	fixed;
+} String;
+
+#define s_clone(s) s_copy((s)->base)
+#define s_to_c(s) ((s)->base)
+#define s_len(s) ((s)->ptr-(s)->base)
+
+extern String*	s_append(String*, char*);
+extern String*	s_array(char*, int);
+extern String*	s_copy(char*);
+extern void	s_free(String*);
+extern String*	s_incref(String*);	
+extern String*	s_memappend(String*, char*, int);
+extern String*	s_nappend(String*, char*, int);
+extern String*	s_new(void);
+extern String*	s_newalloc(int);
+extern String*	s_parse(String*, String*);
+extern String*	s_reset(String*);
+extern String*	s_restart(String*);
+extern void	s_terminate(String*);
+extern void	s_tolower(String*);
+extern void	s_putc(String*, int);
+extern String*	s_unique(String*);
+extern String*	s_grow(String*, int);
+
+#ifdef BGETC
+extern int	s_read(Biobuf*, String*, int);
+extern char	*s_read_line(Biobuf*, String*);
+extern char	*s_getline(Biobuf*, String*);
+typedef struct Sinstack Sinstack;
+extern char	*s_rdinstack(Sinstack*, String*);
+extern Sinstack	*s_allocinstack(char*);
+extern void	s_freeinstack(Sinstack*);
+#endif BGETC
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;
+}