aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/html.h629
-rw-r--r--include/libString.h46
-rw-r--r--src/libString/mkfile27
-rw-r--r--src/libString/s_alloc.c86
-rw-r--r--src/libString/s_append.c17
-rw-r--r--src/libString/s_array.c17
-rw-r--r--src/libString/s_copy.c19
-rw-r--r--src/libString/s_getline.c72
-rw-r--r--src/libString/s_grow.c34
-rw-r--r--src/libString/s_memappend.c20
-rw-r--r--src/libString/s_nappend.c18
-rw-r--r--src/libString/s_parse.c40
-rw-r--r--src/libString/s_putc.c13
-rw-r--r--src/libString/s_rdinstack.c141
-rw-r--r--src/libString/s_read.c38
-rw-r--r--src/libString/s_read_line.c31
-rw-r--r--src/libString/s_reset.c23
-rw-r--r--src/libString/s_terminate.c13
-rw-r--r--src/libString/s_tolower.c15
-rw-r--r--src/libString/s_unique.c16
20 files changed, 1315 insertions, 0 deletions
diff --git a/include/html.h b/include/html.h
new file mode 100644
index 00000000..019ad732
--- /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 00000000..d8487066
--- /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 00000000..49803b45
--- /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 00000000..34f89164
--- /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 00000000..1b02d20e
--- /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 00000000..3cf571b1
--- /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 00000000..5a23b3c2
--- /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 00000000..e46dc125
--- /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 00000000..5cf2a141
--- /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 00000000..27b69850
--- /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 00000000..fb41f932
--- /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 00000000..376a41e2
--- /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 00000000..29a385d7
--- /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 00000000..de12d219
--- /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 00000000..59581eaf
--- /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 00000000..b1de5ac4
--- /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 00000000..cd2a7421
--- /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 00000000..dc893ab2
--- /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 00000000..737fff1f
--- /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 00000000..134411c9
--- /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;
+}