Initial revision
diff --git a/src/libbio/LICENSE b/src/libbio/LICENSE
new file mode 100644
index 0000000..a5d7d87
--- /dev/null
+++ b/src/libbio/LICENSE
@@ -0,0 +1,258 @@
+The Plan 9 software is provided under the terms of the
+Lucent Public License, Version 1.02, reproduced below,
+with the following exceptions:
+
+1. No right is granted to create derivative works of or
+   to redistribute (other than with the Plan 9 Operating System)
+   the screen imprinter fonts identified in subdirectory
+   /lib/font/bit/lucida and printer fonts (Lucida Sans Unicode, Lucida
+   Sans Italic, Lucida Sans Demibold, Lucida Typewriter, Lucida Sans
+   Typewriter83), identified in subdirectory /sys/lib/postscript/font.
+   These directories contain material copyrights by B&H Inc. and Y&Y Inc.
+
+2. The printer fonts identified in subdirectory /sys/lib/ghostscript/font
+   are subject to the GNU GPL, reproduced in the file /LICENSE.gpl.
+
+3. The ghostscript program in the subdirectory /sys/src/cmd/gs is
+   covered by the Aladdin Free Public License, reproduced in the file
+   /LICENSE.afpl.
+
+===================================================================
+
+Lucent Public License Version 1.02
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS PUBLIC
+LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE
+PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+  a. in the case of Lucent Technologies Inc. ("LUCENT"), the Original
+     Program, and
+  b. in the case of each Contributor,
+
+     i. changes to the Program, and
+    ii. additions to the Program;
+
+    where such changes and/or additions to the Program were added to the
+    Program by such Contributor itself or anyone acting on such
+    Contributor's behalf, and the Contributor explicitly consents, in
+    accordance with Section 3C, to characterization of the changes and/or
+    additions as Contributions.
+
+"Contributor" means LUCENT and any other entity that has Contributed a
+Contribution to the Program.
+
+"Distributor" means a Recipient that distributes the Program,
+modifications to the Program, or any part thereof.
+
+"Licensed Patents" mean patent claims licensable by a Contributor
+which are necessarily infringed by the use or sale of its Contribution
+alone or when combined with the Program.
+
+"Original Program" means the original version of the software
+accompanying this Agreement as released by LUCENT, including source
+code, object code and documentation, if any.
+
+"Program" means the Original Program and Contributions or any part
+thereof
+
+"Recipient" means anyone who receives the Program under this
+Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+ a. Subject to the terms of this Agreement, each Contributor hereby
+    grants Recipient a non-exclusive, worldwide, royalty-free copyright
+    license to reproduce, prepare derivative works of, publicly display,
+    publicly perform, distribute and sublicense the Contribution of such
+    Contributor, if any, and such derivative works, in source code and
+    object code form.
+    
+ b. Subject to the terms of this Agreement, each Contributor hereby
+    grants Recipient a non-exclusive, worldwide, royalty-free patent
+    license under Licensed Patents to make, use, sell, offer to sell,
+    import and otherwise transfer the Contribution of such Contributor, if
+    any, in source code and object code form. The patent license granted
+    by a Contributor shall also apply to the combination of the
+    Contribution of that Contributor and the Program if, at the time the
+    Contribution is added by the Contributor, such addition of the
+    Contribution causes such combination to be covered by the Licensed
+    Patents. The patent license granted by a Contributor shall not apply
+    to (i) any other combinations which include the Contribution, nor to
+    (ii) Contributions of other Contributors. No hardware per se is
+    licensed hereunder.
+    
+ c. Recipient understands that although each Contributor grants the
+    licenses to its Contributions set forth herein, no assurances are
+    provided by any Contributor that the Program does not infringe the
+    patent or other intellectual property rights of any other entity. Each
+    Contributor disclaims any liability to Recipient for claims brought by
+    any other entity based on infringement of intellectual property rights
+    or otherwise. As a condition to exercising the rights and licenses
+    granted hereunder, each Recipient hereby assumes sole responsibility
+    to secure any other intellectual property rights needed, if any. For
+    example, if a third party patent license is required to allow
+    Recipient to distribute the Program, it is Recipient's responsibility
+    to acquire that license before distributing the Program.
+
+ d. Each Contributor represents that to its knowledge it has sufficient
+    copyright rights in its Contribution, if any, to grant the copyright
+    license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A. Distributor may choose to distribute the Program in any form under
+this Agreement or under its own license agreement, provided that:
+
+ a. it complies with the terms and conditions of this Agreement;
+
+ b. if the Program is distributed in source code or other tangible
+    form, a copy of this Agreement or Distributor's own license agreement
+    is included with each copy of the Program; and
+
+ c. if distributed under Distributor's own license agreement, such
+    license agreement:
+
+      i. effectively disclaims on behalf of all Contributors all warranties
+         and conditions, express and implied, including warranties or
+         conditions of title and non-infringement, and implied warranties or
+         conditions of merchantability and fitness for a particular purpose;
+     ii. effectively excludes on behalf of all Contributors all liability
+         for damages, including direct, indirect, special, incidental and
+         consequential damages, such as lost profits; and
+    iii. states that any provisions which differ from this Agreement are
+         offered by that Contributor alone and not by any other party.
+
+B. Each Distributor must include the following in a conspicuous
+   location in the Program:
+
+   Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights
+   Reserved.
+
+C. In addition, each Contributor must identify itself as the
+originator of its Contribution in a manner that reasonably allows
+subsequent Recipients to identify the originator of the Contribution.
+Also, each Contributor must agree that the additions and/or changes
+are intended to be a Contribution. Once a Contribution is contributed,
+it may not thereafter be revoked.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain
+responsibilities with respect to end users, business partners and the
+like. While this license is intended to facilitate the commercial use
+of the Program, the Distributor who includes the Program in a
+commercial product offering should do so in a manner which does not
+create potential liability for Contributors. Therefore, if a
+Distributor includes the Program in a commercial product offering,
+such Distributor ("Commercial Distributor") hereby agrees to defend
+and indemnify every Contributor ("Indemnified Contributor") against
+any losses, damages and costs (collectively"Losses") arising from
+claims, lawsuits and other legal actions brought by a third party
+against the Indemnified Contributor to the extent caused by the acts
+or omissions of such Commercial Distributor in connection with its
+distribution of the Program in a commercial product offering. The
+obligations in this section do not apply to any claims or Losses
+relating to any actual or alleged intellectual property infringement.
+In order to qualify, an Indemnified Contributor must: a) promptly
+notify the Commercial Distributor in writing of such claim, and b)
+allow the Commercial Distributor to control, and cooperate with the
+Commercial Distributor in, the defense and any related settlement
+negotiations. The Indemnified Contributor may participate in any such
+claim at its own expense.
+
+For example, a Distributor might include the Program in a commercial
+product offering, Product X. That Distributor is then a Commercial
+Distributor. If that Commercial Distributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Distributor's responsibility
+alone. Under this section, the Commercial Distributor would have to
+defend claims against the Contributors related to those performance
+claims and warranties, and if a court requires any Contributor to pay
+any damages as a result, the Commercial Distributor must pay those
+damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
+PROVIDED ON AN"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
+WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
+OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
+responsible for determining the appropriateness of using and
+distributing the Program and assumes all risks associated with its
+exercise of rights under this Agreement, including but not limited to
+the risks and costs of program errors, compliance with applicable
+laws, damage to or loss of data, programs or equipment, and
+unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR
+ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
+WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
+DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. EXPORT CONTROL
+
+Recipient agrees that Recipient alone is responsible for compliance
+with the United States export administration regulations (and the
+export control laws and regulation of any other countries).
+
+8. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further
+action by the parties hereto, such provision shall be reformed to the
+minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against a Contributor with
+respect to a patent applicable to software (including a cross-claim or
+counterclaim in a lawsuit), then any patent licenses granted by that
+Contributor to such Recipient under this Agreement shall terminate as
+of the date such litigation is filed. In addition, if Recipient
+institutes patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Program
+itself (excluding combinations of the Program with other software or
+hardware) infringes such Recipient's patent(s), then such Recipient's
+rights granted under Section 2(b) shall terminate as of the date such
+litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it
+fails to comply with any of the material terms or conditions of this
+Agreement and does not cure such failure in a reasonable period of
+time after becoming aware of such noncompliance. If all Recipient's
+rights under this Agreement terminate, Recipient agrees to cease use
+and distribution of the Program as soon as reasonably practicable.
+However, Recipient's obligations under this Agreement and any licenses
+granted by Recipient relating to the Program shall continue and
+survive.
+
+LUCENT may publish new versions (including revisions) of this
+Agreement from time to time. Each new version of the Agreement will be
+given a distinguishing version number. The Program (including
+Contributions) may always be distributed subject to the version of the
+Agreement under which it was received. In addition, after a new
+version of the Agreement is published, Contributor may elect to
+distribute the Program (including its Contributions) under the new
+version. No one other than LUCENT has the right to modify this
+Agreement. Except as expressly stated in Sections 2(a) and 2(b) above,
+Recipient receives no rights or licenses to the intellectual property
+of any Contributor under this Agreement, whether expressly, by
+implication, estoppel or otherwise. All rights in the Program not
+expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and
+the intellectual property laws of the United States of America. No
+party to this Agreement will bring a legal action under this Agreement
+more than one year after the cause of action arose. Each party waives
+its rights to a jury trial in any resulting litigation.
+
diff --git a/src/libbio/Make.FreeBSD-386 b/src/libbio/Make.FreeBSD-386
new file mode 100644
index 0000000..087ed3a
--- /dev/null
+++ b/src/libbio/Make.FreeBSD-386
@@ -0,0 +1,7 @@
+CC=gcc
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I$(PREFIX)/include
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O	# default, can be overriden by Make.$(SYSNAME)
+NAN=nan64.$O
diff --git a/src/libbio/Make.HP-UX-9000 b/src/libbio/Make.HP-UX-9000
new file mode 100644
index 0000000..edbdc11
--- /dev/null
+++ b/src/libbio/Make.HP-UX-9000
@@ -0,0 +1,6 @@
+CC=cc
+CFLAGS=-O -c -Ae -I.
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O
diff --git a/src/libbio/Make.Linux-386 b/src/libbio/Make.Linux-386
new file mode 100644
index 0000000..74b0252
--- /dev/null
+++ b/src/libbio/Make.Linux-386
@@ -0,0 +1,7 @@
+CC=gcc
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I.
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O	# default, can be overriden by Make.$(SYSNAME)
+NAN=nan64.$O
diff --git a/src/libbio/Make.NetBSD-386 b/src/libbio/Make.NetBSD-386
new file mode 100644
index 0000000..087ed3a
--- /dev/null
+++ b/src/libbio/Make.NetBSD-386
@@ -0,0 +1,7 @@
+CC=gcc
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c -I. -I$(PREFIX)/include
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O	# default, can be overriden by Make.$(SYSNAME)
+NAN=nan64.$O
diff --git a/src/libbio/Make.OSF1-alpha b/src/libbio/Make.OSF1-alpha
new file mode 100644
index 0000000..3d45279
--- /dev/null
+++ b/src/libbio/Make.OSF1-alpha
@@ -0,0 +1,6 @@
+CC=cc
+CFLAGS+=-g -c -I.
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O
diff --git a/src/libbio/Make.SunOS-sun4u b/src/libbio/Make.SunOS-sun4u
new file mode 100644
index 0000000..c5fe67b
--- /dev/null
+++ b/src/libbio/Make.SunOS-sun4u
@@ -0,0 +1,2 @@
+include Make.SunOS-sun4u-$(CC)
+NAN=nan64.$O
diff --git a/src/libbio/Make.SunOS-sun4u-cc b/src/libbio/Make.SunOS-sun4u-cc
new file mode 100644
index 0000000..829301d
--- /dev/null
+++ b/src/libbio/Make.SunOS-sun4u-cc
@@ -0,0 +1,6 @@
+CC=cc
+CFLAGS+=-g -c -I. -O
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O
diff --git a/src/libbio/Make.SunOS-sun4u-gcc b/src/libbio/Make.SunOS-sun4u-gcc
new file mode 100644
index 0000000..5c41594
--- /dev/null
+++ b/src/libbio/Make.SunOS-sun4u-gcc
@@ -0,0 +1,6 @@
+CC=gcc
+CFLAGS+=-Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -O2 -g -c
+O=o
+AR=ar
+ARFLAGS=rvc
+NAN=nan64.$O
diff --git a/src/libbio/Makefile b/src/libbio/Makefile
new file mode 100644
index 0000000..2d1a0ca
--- /dev/null
+++ b/src/libbio/Makefile
@@ -0,0 +1,108 @@
+
+# this works in gnu make
+SYSNAME:=${shell uname}
+OBJTYPE:=${shell uname -m | sed 's;i.86;386;; s;/.*;;; s; ;;g'}
+
+# this works in bsd make
+SYSNAME!=uname
+OBJTYPE!=uname -m | sed 's;i.86;386;; s;/.*;;; s; ;;g'
+
+# the gnu rules will mess up bsd but not vice versa,
+# hence the gnu rules come first.
+
+include Make.$(SYSNAME)-$(OBJTYPE)
+
+PREFIX=/usr/local
+
+NUKEFILES=
+
+TGZFILES=
+
+LIB=libbio.a
+VERSION=2.0
+PORTPLACE=devel/libbio
+NAME=libbio
+
+OFILES=\
+	bbuffered.$O\
+	bfildes.$O\
+	bflush.$O\
+	bgetc.$O\
+	bgetd.$O\
+	binit.$O\
+	boffset.$O\
+	bprint.$O\
+	bputc.$O\
+	brdline.$O\
+	brdstr.$O\
+	bread.$O\
+	bseek.$O\
+	bwrite.$O\
+	bgetrune.$O\
+	bputrune.$O\
+
+HFILES=\
+	bio.h\
+
+all: $(LIB)
+
+install: $(LIB)
+	test -d $(PREFIX)/man/man3 || mkdir $(PREFIX)/man/man3
+	install -m 0644 bio.3 $(PREFIX)/man/man3/bio.3
+	install -m 0644 bio.h $(PREFIX)/include
+	install -m 0644 $(LIB) $(PREFIX)/lib/$(LIB)
+
+bcat: bcat.$O $(LIB)
+	$(CC) -o bcat bcat.$O $(LIB) -L/usr/local/lib -lfmt -lutf
+
+$(LIB): $(OFILES)
+	$(AR) $(ARFLAGS) $(LIB) $(OFILES)
+
+NUKEFILES+=$(LIB)
+.c.$O:
+	$(CC) $(CFLAGS) -I$(PREFIX)/include $*.c
+
+%.$O: %.c
+	$(CC) $(CFLAGS) -I$(PREFIX)/include $*.c
+
+
+$(OFILES): $(HFILES)
+
+tgz:
+	rm -rf $(NAME)-$(VERSION)
+	mkdir $(NAME)-$(VERSION)
+	cp Makefile Make.* README LICENSE NOTICE *.[ch137] rpm.spec bundle.ports $(TGZFILES) $(NAME)-$(VERSION)
+	tar cf - $(NAME)-$(VERSION) | gzip >$(NAME)-$(VERSION).tgz
+	rm -rf $(NAME)-$(VERSION)
+
+clean:
+	rm -f $(OFILES) $(LIB)
+
+nuke:
+	rm -f $(OFILES) *.tgz *.rpm $(NUKEFILES)
+
+rpm:
+	make tgz
+	cp $(NAME)-$(VERSION).tgz /usr/src/RPM/SOURCES
+	rpm -ba rpm.spec
+	cp /usr/src/RPM/SRPMS/$(NAME)-$(VERSION)-1.src.rpm .
+	cp /usr/src/RPM/RPMS/i586/$(NAME)-$(VERSION)-1.i586.rpm .
+	scp *.rpm rsc@amsterdam.lcs.mit.edu:public_html/software
+
+PORTDIR=/usr/ports/$(PORTPLACE)
+
+ports:
+	make tgz
+	rm -rf $(PORTDIR)
+	mkdir $(PORTDIR)
+	cp $(NAME)-$(VERSION).tgz /usr/ports/distfiles
+	cat bundle.ports | (cd $(PORTDIR) && awk '$$1=="---" && $$3=="---" { ofile=$$2; next} {if(ofile) print >ofile}')
+	(cd $(PORTDIR); make makesum)
+	(cd $(PORTDIR); make)
+	(cd $(PORTDIR); /usr/local/bin/portlint)
+	rm -rf $(PORTDIR)/work
+	shar `find $(PORTDIR)` > ports.shar
+	(cd $(PORTDIR); tar cf - *) | gzip >$(NAME)-$(VERSION)-ports.tgz
+	scp *.tgz rsc@amsterdam.lcs.mit.edu:public_html/software
+
+.phony: all clean nuke install tgz rpm ports
diff --git a/src/libbio/Makefile.MID b/src/libbio/Makefile.MID
new file mode 100644
index 0000000..610659b
--- /dev/null
+++ b/src/libbio/Makefile.MID
@@ -0,0 +1,37 @@
+LIB=libbio.a
+VERSION=2.0
+PORTPLACE=devel/libbio
+NAME=libbio
+
+OFILES=\
+	bbuffered.$O\
+	bfildes.$O\
+	bflush.$O\
+	bgetc.$O\
+	bgetd.$O\
+	binit.$O\
+	boffset.$O\
+	bprint.$O\
+	bputc.$O\
+	brdline.$O\
+	brdstr.$O\
+	bread.$O\
+	bseek.$O\
+	bwrite.$O\
+	bgetrune.$O\
+	bputrune.$O\
+
+HFILES=\
+	bio.h\
+
+all: $(LIB)
+
+install: $(LIB)
+	test -d $(PREFIX)/man/man3 || mkdir $(PREFIX)/man/man3
+	install -m 0644 bio.3 $(PREFIX)/man/man3/bio.3
+	install -m 0644 bio.h $(PREFIX)/include
+	install -m 0644 $(LIB) $(PREFIX)/lib/$(LIB)
+
+bcat: bcat.$O $(LIB)
+	$(CC) -o bcat bcat.$O $(LIB) -L/usr/local/lib -lfmt -lutf
+
diff --git a/src/libbio/NOTICE b/src/libbio/NOTICE
new file mode 100644
index 0000000..9911f99
--- /dev/null
+++ b/src/libbio/NOTICE
@@ -0,0 +1,27 @@
+Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+Portions Copyright © 1995-1997 C H Forsyth (forsyth@caldo.demon.co.uk).  All rights reserved.
+Portions Copyright © 1997-1999 Vita Nuova Limited.  All rights reserved.
+Portions Copyright © 2000-2002 Vita Nuova Holdings Limited (www.vitanuova.com).  All rights reserved.
+
+Under a licence agreement with Lucent Technologies Inc. effective 1st March 2000,
+Vita Nuova Holdings Limited has the right to determine (within a specified scope)
+the form and content of sublicences for this software.
+
+Vita Nuova Holdings Limited now makes this software available as Free
+Software under the terms of the `GNU General Public LIcense, Version 2'
+(see the file LICENCE or http://www.fsf.org/copyleft/gpl.html for
+the full terms and conditions).  One of the conditions of that licence
+is that you must keep intact all notices that refer to that licence and to the absence of
+of any warranty: for this software, note that includes this NOTICE file in particular.
+  
+This suite of programs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+`GNU General Public License' for more details.
+
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory.  If you take code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices above.
diff --git a/src/libbio/README b/src/libbio/README
new file mode 100644
index 0000000..8efb3b6
--- /dev/null
+++ b/src/libbio/README
@@ -0,0 +1,7 @@
+This is a Unix port of the Plan 9 buffered I/O library,
+originally done for the Inferno operating system.
+
+Russ Cox repackaged this to build as a standalone
+Unix library.  Send comments about packaging to
+Russ Cox <rsc@post.harvard.edu>
+
diff --git a/src/libbio/bbuffered.c b/src/libbio/bbuffered.c
new file mode 100644
index 0000000..dfc0cf5
--- /dev/null
+++ b/src/libbio/bbuffered.c
@@ -0,0 +1,20 @@
+#include	"lib9.h"
+#include	<bio.h>
+
+int
+Bbuffered(Biobuf *bp)
+{
+	switch(bp->state) {
+	case Bracteof:
+	case Bractive:
+		return -bp->icount;
+
+	case Bwactive:
+		return bp->bsize + bp->ocount;
+
+	case Binactive:
+		return 0;
+	}
+	fprint(2, "Bbuffered: unknown state %d\n", bp->state);
+	return 0;
+}
diff --git a/src/libbio/bcat.c b/src/libbio/bcat.c
new file mode 100644
index 0000000..dea346a
--- /dev/null
+++ b/src/libbio/bcat.c
@@ -0,0 +1,42 @@
+#include <fmt.h>
+#include "bio.h"
+
+Biobuf bout;
+
+void
+bcat(Biobuf *b, char *name)
+{
+	char buf[1000];
+	int n;
+
+	while((n = Bread(b, buf, sizeof buf)) > 0){
+		if(Bwrite(&bout, buf, n) < 0)
+			fprint(2, "writing during %s: %r\n", name);
+	}
+	if(n < 0)
+		fprint(2, "reading %s: %r\n", name);	
+}
+
+int
+main(int argc, char **argv)
+{
+	int i;
+	Biobuf b, *bp;
+
+	Binit(&bout, 1, O_WRONLY);
+
+	if(argc == 1){
+		Binit(&b, 0, O_RDONLY);
+		bcat(&b, "<stdin>");
+	}else{
+		for(i=1; i<argc; i++){
+			if((bp = Bopen(argv[i], O_RDONLY)) == 0){
+				fprint(2, "Bopen %s: %r\n", argv[i]);
+				continue;
+			}
+			bcat(bp, argv[i]);
+			Bterm(bp);
+		}
+	}
+	exit(0);
+}
diff --git a/src/libbio/bfildes.c b/src/libbio/bfildes.c
new file mode 100644
index 0000000..5188180
--- /dev/null
+++ b/src/libbio/bfildes.c
@@ -0,0 +1,9 @@
+#include	"lib9.h"
+#include	<bio.h>
+
+int
+Bfildes(Biobuf *bp)
+{
+
+	return bp->fid;
+}
diff --git a/src/libbio/bflush.c b/src/libbio/bflush.c
new file mode 100644
index 0000000..0ab8126
--- /dev/null
+++ b/src/libbio/bflush.c
@@ -0,0 +1,33 @@
+#include	"lib9.h"
+#include	<bio.h>
+
+int
+Bflush(Biobuf *bp)
+{
+	int n, c;
+
+	switch(bp->state) {
+	case Bwactive:
+		n = bp->bsize+bp->ocount;
+		if(n == 0)
+			return 0;
+		c = write(bp->fid, bp->bbuf, n);
+		if(n == c) {
+			bp->offset += n;
+			bp->ocount = -bp->bsize;
+			return 0;
+		}
+		bp->state = Binactive;
+		bp->ocount = 0;
+		break;
+
+	case Bracteof:
+		bp->state = Bractive;
+
+	case Bractive:
+		bp->icount = 0;
+		bp->gbuf = bp->ebuf;
+		return 0;
+	}
+	return Beof;
+}
diff --git a/src/libbio/bgetc.c b/src/libbio/bgetc.c
new file mode 100644
index 0000000..4c8ae90
--- /dev/null
+++ b/src/libbio/bgetc.c
@@ -0,0 +1,52 @@
+#include	"lib9.h"
+#include	<bio.h>
+
+int
+Bgetc(Biobuf *bp)
+{
+	int i;
+
+loop:
+	i = bp->icount;
+	if(i != 0) {
+		bp->icount = i+1;
+		return bp->ebuf[i];
+	}
+	if(bp->state != Bractive) {
+		if(bp->state == Bracteof)
+			bp->state = Bractive;
+		return Beof;
+	}
+	/*
+	 * get next buffer, try to keep Bungetsize
+	 * characters pre-catenated from the previous
+	 * buffer to allow that many ungets.
+	 */
+	memmove(bp->bbuf-Bungetsize, bp->ebuf-Bungetsize, Bungetsize);
+	i = read(bp->fid, bp->bbuf, bp->bsize);
+	bp->gbuf = bp->bbuf;
+	if(i <= 0) {
+		if(i < 0)
+			bp->state = Binactive;
+		return Beof;
+	}
+	if(i < bp->bsize) {
+		memmove(bp->ebuf-i-Bungetsize, bp->bbuf-Bungetsize, i+Bungetsize);
+		bp->gbuf = bp->ebuf-i;
+	}
+	bp->icount = -i;
+	bp->offset += i;
+	goto loop;
+}
+
+int
+Bungetc(Biobuf *bp)
+{
+
+	if(bp->state == Bracteof)
+		bp->state = Bractive;
+	if(bp->state != Bractive)
+		return Beof;
+	bp->icount--;
+	return 1;
+}
diff --git a/src/libbio/bgetd.c b/src/libbio/bgetd.c
new file mode 100644
index 0000000..e7dd305
--- /dev/null
+++ b/src/libbio/bgetd.c
@@ -0,0 +1,36 @@
+#include "lib9.h"
+#include <bio.h>
+
+struct	bgetd
+{
+	Biobuf*	b;
+	int		eof;
+};
+
+static int
+Bgetdf(void *vp)
+{
+	int c;
+	struct bgetd *bg = vp;
+
+	c = Bgetc(bg->b);
+	if(c == Beof)
+		bg->eof = 1;
+	return c;
+}
+
+int
+Bgetd(Biobuf *bp, double *dp)
+{
+	double d;
+	struct bgetd b;
+
+	b.b = bp;
+	b.eof = 0;
+	d = fmtcharstod(Bgetdf, &b);
+	if(b.eof)
+		return -1;
+	Bungetc(bp);
+	*dp = d;
+	return 1;
+}
diff --git a/src/libbio/bgetrune.c b/src/libbio/bgetrune.c
new file mode 100644
index 0000000..f1b065b
--- /dev/null
+++ b/src/libbio/bgetrune.c
@@ -0,0 +1,47 @@
+#include	"lib9.h"
+#include	<bio.h>
+#include	<utf.h>
+
+long
+Bgetrune(Biobuf *bp)
+{
+	int c, i;
+	Rune rune;
+	char str[4];
+
+	c = Bgetc(bp);
+	if(c < Runeself) {		/* one char */
+		bp->runesize = 1;
+		return c;
+	}
+	str[0] = c;
+
+	for(i=1;;) {
+		c = Bgetc(bp);
+		if(c < 0)
+			return c;
+		str[i++] = c;
+
+		if(fullrune(str, i)) {
+			bp->runesize = chartorune(&rune, str);
+			while(i > bp->runesize) {
+				Bungetc(bp);
+				i--;
+			}
+			return rune;
+		}
+	}
+}
+
+int
+Bungetrune(Biobuf *bp)
+{
+
+	if(bp->state == Bracteof)
+		bp->state = Bractive;
+	if(bp->state != Bractive)
+		return Beof;
+	bp->icount -= bp->runesize;
+	bp->runesize = 0;
+	return 1;
+}
diff --git a/src/libbio/binit.c b/src/libbio/binit.c
new file mode 100644
index 0000000..d76168b
--- /dev/null
+++ b/src/libbio/binit.c
@@ -0,0 +1,142 @@
+#include	"lib9.h"
+#include	<bio.h>
+
+enum
+{
+	MAXBUFS	= 20
+};
+
+static	Biobuf*	wbufs[MAXBUFS];
+static	int		atexitflag;
+
+static
+void
+batexit(void)
+{
+	Biobuf *bp;
+	int i;
+
+	for(i=0; i<MAXBUFS; i++) {
+		bp = wbufs[i];
+		if(bp != 0) {
+			wbufs[i] = 0;
+			Bflush(bp);
+		}
+	}
+}
+
+static
+void
+deinstall(Biobuf *bp)
+{
+	int i;
+
+	for(i=0; i<MAXBUFS; i++)
+		if(wbufs[i] == bp)
+			wbufs[i] = 0;
+}
+
+static
+void
+install(Biobuf *bp)
+{
+	int i;
+
+	deinstall(bp);
+	for(i=0; i<MAXBUFS; i++)
+		if(wbufs[i] == 0) {
+			wbufs[i] = bp;
+			break;
+		}
+	if(atexitflag == 0) {
+		atexitflag = 1;
+		atexit(batexit);
+	}
+}
+
+int
+Binits(Biobuf *bp, int f, int mode, unsigned char *p, int size)
+{
+
+	p += Bungetsize;	/* make room for Bungets */
+	size -= Bungetsize;
+
+	switch(mode) {
+	default:
+		fprint(2, "Bopen: unknown mode %d\n", mode);
+		return Beof;
+
+	case OREAD:
+		bp->state = Bractive;
+		bp->ocount = 0;
+		break;
+
+	case OWRITE:
+		install(bp);
+		bp->state = Bwactive;
+		bp->ocount = -size;
+		break;
+	}
+	bp->bbuf = p;
+	bp->ebuf = p+size;
+	bp->bsize = size;
+	bp->icount = 0;
+	bp->gbuf = bp->ebuf;
+	bp->fid = f;
+	bp->flag = 0;
+	bp->rdline = 0;
+	bp->offset = 0;
+/*	bp->runesize = 0; */
+	return 0;
+}
+
+
+int
+Binit(Biobuf *bp, int f, int mode)
+{
+	return Binits(bp, f, mode, bp->b, sizeof(bp->b));
+}
+
+Biobuf*
+Bopen(char *name, int mode)
+{
+	Biobuf *bp;
+	int f;
+
+	switch(mode) {
+	default:
+		fprint(2, "Bopen: unknown mode %d\n", mode);
+		return 0;
+
+	case OREAD:
+		f = open(name, OREAD);
+		if(f < 0)
+			return 0;
+		break;
+
+	case OWRITE:
+		f = creat(name, 0666);
+		if(f < 0)
+			return 0;
+	}
+	bp = malloc(sizeof(Biobuf));
+	if(bp == 0)
+		return 0;
+	Binits(bp, f, mode, bp->b, sizeof(bp->b));
+	bp->flag = Bmagic;
+	return bp;
+}
+
+int
+Bterm(Biobuf *bp)
+{
+
+	deinstall(bp);
+	Bflush(bp);
+	if(bp->flag == Bmagic) {
+		bp->flag = 0;
+		close(bp->fid);
+		free(bp);
+	}
+	return 0;
+}
diff --git a/src/libbio/bio.3 b/src/libbio/bio.3
new file mode 100644
index 0000000..cf50463
--- /dev/null
+++ b/src/libbio/bio.3
@@ -0,0 +1,336 @@
+.TH BIO 3
+.SH NAME
+Bopen, Binit, Binits, Brdline, Brdstr, Bgetc, Bgetd, Bungetc, Bread, Bseek, Boffset, Bfildes, Blinelen, Bputc, Bprint, Bvprint, Bwrite, Bflush, Bterm, Bbuffered \- buffered input/output
+.SH SYNOPSIS
+.ta \w'Biobuf* 'u
+.B #include <fmt.h>
+.B #include <bio.h>
+.PP
+.B
+Biobuf* Bopen(char *file, int mode)
+.PP
+.B
+int	Binit(Biobuf *bp, int fd, int mode)
+.PP
+.B
+int	Bterm(Biobuf *bp)
+.PP
+.B
+int	Bprint(Biobuf *bp, char *format, ...)
+.PP
+.B
+int	Bvprint(Biobuf *bp, char *format, va_list arglist);
+.PP
+.B
+void*	Brdline(Biobuf *bp, int delim)
+.PP
+.B
+char*	Brdstr(Biobuf *bp, int delim, int nulldelim)
+.PP
+.B
+int	Blinelen(Biobuf *bp)
+.PP
+.B
+off_t	Boffset(Biobuf *bp)
+.PP
+.B
+int	Bfildes(Biobuf *bp)
+.PP
+.B
+int	Bgetc(Biobuf *bp)
+.PP
+.B
+long	Bgetrune(Biobufhdr *bp)
+.PP
+.B
+int	Bgetd(Biobuf *bp, double *d)
+.PP
+.B
+int	Bungetc(Biobuf *bp)
+.PP
+.B
+int	Bungetrune(Biobufhdr *bp)
+.PP
+.B
+off_t	Bseek(Biobuf *bp, off_t n, int type)
+.PP
+.B
+int	Bputc(Biobuf *bp, int c)
+.PP
+.B
+int	Bputrune(Biobufhdr *bp, long c)
+.PP
+.B
+long	Bread(Biobuf *bp, void *addr, long nbytes)
+.PP
+.B
+long	Bwrite(Biobuf *bp, void *addr, long nbytes)
+.PP
+.B
+int	Bflush(Biobuf *bp)
+.PP
+.B
+int	Bbuffered(Biobuf *bp)
+.PP
+.SH DESCRIPTION
+These routines implement fast buffered I/O.
+I/O on different file descriptors is independent.
+.PP
+.I Bopen
+opens
+.I file
+for mode
+.B O_RDONLY
+or creates for mode
+.BR O_WRONLY .
+It calls
+.IR malloc (3)
+to allocate a buffer.
+.PP
+.I Binit
+initializes a buffer
+with the open file descriptor passed in
+by the user.
+.PP
+Arguments
+of types pointer to Biobuf and pointer to Biobuf
+can be used interchangeably in the following routines.
+.PP
+.IR Bopen ,
+.IR Binit ,
+or
+.I Binits
+should be called before any of the
+other routines on that buffer.
+.I Bfildes
+returns the integer file descriptor of the associated open file.
+.PP
+.I Bterm
+flushes the buffer for
+.IR bp .
+If the buffer was allocated by
+.IR Bopen ,
+the buffer is
+.I freed
+and the file is closed.
+.PP
+.I Brdline
+reads a string from the file associated with
+.I bp
+up to and including the first
+.I delim
+character.
+The delimiter character at the end of the line is
+not altered.
+.I Brdline
+returns a pointer to the start of the line or
+.L 0
+on end-of-file or read error.
+.I Blinelen
+returns the length (including the delimiter)
+of the most recent string returned by
+.IR Brdline .
+.PP
+.I Brdstr
+returns a
+.IR malloc (3)-allocated
+buffer containing the next line of input delimited by
+.IR delim ,
+terminated by a NUL (0) byte.
+Unlike
+.IR Brdline ,
+which returns when its buffer is full even if no delimiter has been found,
+.I Brdstr
+will return an arbitrarily long line in a single call.
+If
+.I nulldelim
+is set, the terminal delimiter will be overwritten with a NUL.
+After a successful call to
+.IR Brdstr ,
+the return value of
+.I Blinelen
+will be the length of the returned buffer, excluding the NUL.
+.PP
+.I Bgetc
+returns the next byte from
+.IR bp ,
+or a negative value
+at end of file.
+.I Bungetc
+may be called immediately after
+.I Bgetc
+to allow the same byte to be reread.
+.PP
+.I Bgetrune
+calls
+.I Bgetc
+to read the bytes of the next
+.SM UTF
+sequence in the input stream and returns the value of the rune
+represented by the sequence.
+It returns a negative value
+at end of file.
+.I Bungetrune
+may be called immediately after
+.I Bgetrune
+to allow the same
+.SM UTF
+sequence to be reread as either bytes or a rune.
+.I Bungetc
+and
+.I Bungetrune
+may back up a maximum of five bytes.
+.PP
+.I Bgetd
+uses
+.I fmtcharstod
+(undocumented)
+and
+.I Bgetc
+to read the formatted
+floating-point number in the input stream,
+skipping initial blanks and tabs.
+The value is stored in
+.BR *d.
+.PP
+.I Bread
+reads
+.I nbytes
+of data from
+.I bp
+into memory starting at
+.IR addr .
+The number of bytes read is returned on success
+and a negative value is returned if a read error occurred.
+.PP
+.I Bseek
+applies
+.IR lseek (2)
+to
+.IR bp .
+It returns the new file offset.
+.I Boffset
+returns the file offset of the next character to be processed.
+.PP
+.I Bputc
+outputs the low order 8 bits of
+.I c
+on
+.IR bp .
+If this causes a
+.IR write
+to occur and there is an error,
+a negative value is returned.
+Otherwise, a zero is returned.
+.PP
+.I Bputrune
+calls
+.I Bputc
+to output the low order
+16 bits of
+.I c
+as a rune
+in
+.SM UTF
+format
+on the output stream.
+.PP
+.I Bprint
+is a buffered interface to
+.IR print (2).
+If this causes a
+.IR write
+to occur and there is an error,
+a negative value
+.RB ( Beof )
+is returned.
+Otherwise, the number of bytes output is returned.
+.I Bvprint
+does the same except it takes as argument a
+.B va_list
+parameter, so it can be called within a variadic function.
+.PP
+.I Bwrite
+outputs
+.I nbytes
+of data starting at
+.I addr
+to
+.IR bp .
+If this causes a
+.IR write
+to occur and there is an error,
+a negative value is returned.
+Otherwise, the number of bytes written is returned.
+.PP
+.I Bflush
+causes any buffered output associated with
+.I bp
+to be written.
+The return is as for
+.IR Bputc .
+.I Bflush
+is called on
+exit for every buffer still open
+for writing.
+.PP
+.I Bbuffered
+returns the number of bytes in the buffer.
+When reading, this is the number of bytes still available from the last
+read on the file; when writing, it is the number of bytes ready to be
+written.
+.PP
+This library uses
+.IR fmt (3)
+for diagnostic messages about internal errors,
+as well as for the implementation of
+.I Bprint
+and
+.IR Bvprint .
+It uses
+.IR utf (3)
+for the implementation of
+.I Bgetrune
+and
+.IR Bputrune .
+.SH SEE ALSO
+.IR atexit (3).
+.IR open (2),
+.IR print (3),
+.IR utf (7)
+.SH DIAGNOSTICS
+.I Bio
+routines that return integers yield
+.B Beof
+if 
+.I bp
+is not the descriptor of an open file.
+.I Bopen
+returns zero if the file cannot be opened in the given mode.
+.SH HISTORY
+The
+.IR bio (3)
+library originally appeared in Plan 9.
+This is a port of the Plan 9 bio library.
+.SH BUGS
+.I Brdline
+returns an error on strings longer than the buffer associated
+with the file
+and also if the end-of-file is encountered
+before a delimiter.
+.I Blinelen
+will tell how many characters are available
+in these cases.
+In the case of a true end-of-file,
+.I Blinelen
+will return zero.
+At the cost of allocating a buffer,
+.I Brdstr
+sidesteps these issues.
+.PP
+The data returned by
+.I Brdline
+may be overwritten by calls to any other
+.I bio
+routine on the same
+.IR bp.
diff --git a/src/libbio/bio.h b/src/libbio/bio.h
new file mode 100644
index 0000000..c481239
--- /dev/null
+++ b/src/libbio/bio.h
@@ -0,0 +1,79 @@
+#ifndef _BIOH_
+#define _BIOH_ 1
+
+#include <sys/types.h>	/* for off_t */
+#include <fcntl.h>	/* for O_RDONLY, O_WRONLY */
+
+typedef	struct	Biobuf	Biobuf;
+
+enum
+{
+	Bsize		= 8*1024,
+	Bungetsize	= 4,		/* space for ungetc */
+	Bmagic		= 0x314159,
+	Beof		= -1,
+	Bbad		= -2,
+
+	Binactive	= 0,		/* states */
+	Bractive,
+	Bwactive,
+	Bracteof,
+
+	Bend
+};
+
+struct	Biobuf
+{
+	int	icount;		/* neg num of bytes at eob */
+	int	ocount;		/* num of bytes at bob */
+	int	rdline;		/* num of bytes after rdline */
+	int	runesize;		/* num of bytes of last getrune */
+	int	state;		/* r/w/inactive */
+	int	fid;		/* open file */
+	int	flag;		/* magic if malloc'ed */
+	off_t	offset;		/* offset of buffer in file */
+	int	bsize;		/* size of buffer */
+	unsigned char*	bbuf;		/* pointer to beginning of buffer */
+	unsigned char*	ebuf;		/* pointer to end of buffer */
+	unsigned char*	gbuf;		/* pointer to good data in buf */
+	unsigned char	b[Bungetsize+Bsize];
+};
+
+#define	BGETC(bp)\
+	((bp)->icount?(bp)->bbuf[(bp)->bsize+(bp)->icount++]:Bgetc((bp)))
+#define	BPUTC(bp,c)\
+	((bp)->ocount?(bp)->bbuf[(bp)->bsize+(bp)->ocount++]=(c),0:Bputc((bp),(c)))
+#define	BOFFSET(bp)\
+	(((bp)->state==Bractive)?\
+		(bp)->offset + (bp)->icount:\
+	(((bp)->state==Bwactive)?\
+		(bp)->offset + ((bp)->bsize + (bp)->ocount):\
+		-1))
+#define	BLINELEN(bp)\
+	(bp)->rdline
+#define	BFILDES(bp)\
+	(bp)->fid
+
+int	Bbuffered(Biobuf*);
+int	Bfildes(Biobuf*);
+int	Bflush(Biobuf*);
+int	Bgetc(Biobuf*);
+int	Bgetd(Biobuf*, double*);
+int	Binit(Biobuf*, int, int);
+int	Binits(Biobuf*, int, int, unsigned char*, int);
+int	Blinelen(Biobuf*);
+off_t	Boffset(Biobuf*);
+Biobuf*	Bopen(char*, int);
+int	Bprint(Biobuf*, char*, ...);
+int	Bputc(Biobuf*, int);
+void*	Brdline(Biobuf*, int);
+long	Bread(Biobuf*, void*, long);
+off_t	Bseek(Biobuf*, off_t, int);
+int	Bterm(Biobuf*);
+int	Bungetc(Biobuf*);
+long	Bwrite(Biobuf*, void*, long);
+char*	Brdstr(Biobuf*, int, int);
+long	Bgetrune(Biobuf*);
+int	Bputrune(Biobuf*, long);
+
+#endif
diff --git a/src/libbio/boffset.c b/src/libbio/boffset.c
new file mode 100644
index 0000000..df28aaf
--- /dev/null
+++ b/src/libbio/boffset.c
@@ -0,0 +1,25 @@
+#include	"lib9.h"
+#include	<bio.h>
+
+off_t
+Boffset(Biobuf *bp)
+{
+	off_t n;
+
+	switch(bp->state) {
+	default:
+		fprint(2, "Boffset: unknown state %d\n", bp->state);
+		n = Beof;
+		break;
+
+	case Bracteof:
+	case Bractive:
+		n = bp->offset + bp->icount;
+		break;
+
+	case Bwactive:
+		n = bp->offset + (bp->bsize + bp->ocount);
+		break;
+	}
+	return n;
+}
diff --git a/src/libbio/bprint.c b/src/libbio/bprint.c
new file mode 100644
index 0000000..81e71e5
--- /dev/null
+++ b/src/libbio/bprint.c
@@ -0,0 +1,28 @@
+#include	"lib9.h"
+#include	<bio.h>
+
+int
+Bprint(Biobuf *bp, char *fmt, ...)
+{
+	va_list	ap;
+	char *ip, *ep, *out;
+	int n;
+
+	ep = (char*)bp->ebuf;
+	ip = ep + bp->ocount;
+	va_start(ap, fmt);
+	out = vseprint(ip, ep, fmt, ap);
+	va_end(ap);
+	if(out == 0 || out >= ep-5) {
+		Bflush(bp);
+		ip = ep + bp->ocount;
+		va_start(ap, fmt);
+		out = vseprint(ip, ep, fmt, ap);
+		va_end(ap);
+		if(out >= ep-5)
+			return Beof;
+	}
+	n = out-ip;
+	bp->ocount += n;
+	return n;
+}
diff --git a/src/libbio/bputc.c b/src/libbio/bputc.c
new file mode 100644
index 0000000..5f0fba5
--- /dev/null
+++ b/src/libbio/bputc.c
@@ -0,0 +1,29 @@
+#include	"lib9.h"
+#include	<bio.h>
+
+int
+Bputc(Biobuf *bp, int c)
+{
+	int i, j;
+
+loop:
+	i = bp->ocount;
+	j = i+1;
+	if(i != 0) {
+		bp->ocount = j;
+		bp->ebuf[i] = c;
+		return 0;
+	}
+	if(bp->state != Bwactive)
+		return Beof;
+	j = write(bp->fid, bp->bbuf, bp->bsize);
+	if(j == bp->bsize) {
+		bp->ocount = -bp->bsize;
+		bp->offset += j;
+		goto loop;
+	}
+	fprint(2, "Bputc: write error\n");
+	bp->state = Binactive;
+	bp->ocount = 0;
+	return Beof;
+}
diff --git a/src/libbio/bputrune.c b/src/libbio/bputrune.c
new file mode 100644
index 0000000..a2eaa83
--- /dev/null
+++ b/src/libbio/bputrune.c
@@ -0,0 +1,23 @@
+#include	"lib9.h"
+#include	<bio.h>
+#include	<utf.h>
+
+int
+Bputrune(Biobuf *bp, long c)
+{
+	Rune rune;
+	char str[4];
+	int n;
+
+	rune = c;
+	if(rune < Runeself) {
+		Bputc(bp, rune);
+		return 1;
+	}
+	n = runetochar(str, &rune);
+	if(n == 0)
+		return Bbad;
+	if(Bwrite(bp, str, n) != n)
+		return Beof;
+	return n;
+}
diff --git a/src/libbio/brdline.c b/src/libbio/brdline.c
new file mode 100644
index 0000000..4ac6316
--- /dev/null
+++ b/src/libbio/brdline.c
@@ -0,0 +1,94 @@
+#include	"lib9.h"
+#include	<bio.h>
+
+void*
+Brdline(Biobuf *bp, int delim)
+{
+	char *ip, *ep;
+	int i, j;
+
+	i = -bp->icount;
+	if(i == 0) {
+		/*
+		 * eof or other error
+		 */
+		if(bp->state != Bractive) {
+			if(bp->state == Bracteof)
+				bp->state = Bractive;
+			bp->rdline = 0;
+			bp->gbuf = bp->ebuf;
+			return 0;
+		}
+	}
+
+	/*
+	 * first try in remainder of buffer (gbuf doesn't change)
+	 */
+	ip = (char*)bp->ebuf - i;
+	ep = memchr(ip, delim, i);
+	if(ep) {
+		j = (ep - ip) + 1;
+		bp->rdline = j;
+		bp->icount += j;
+		return ip;
+	}
+
+	/*
+	 * copy data to beginning of buffer
+	 */
+	if(i < bp->bsize)
+		memmove(bp->bbuf, ip, i);
+	bp->gbuf = bp->bbuf;
+
+	/*
+	 * append to buffer looking for the delim
+	 */
+	ip = (char*)bp->bbuf + i;
+	while(i < bp->bsize) {
+		j = read(bp->fid, ip, bp->bsize-i);
+		if(j <= 0) {
+			/*
+			 * end of file with no delim
+			 */
+			memmove(bp->ebuf-i, bp->bbuf, i);
+			bp->rdline = i;
+			bp->icount = -i;
+			bp->gbuf = bp->ebuf-i;
+			return 0;
+		}
+		bp->offset += j;
+		i += j;
+		ep = memchr(ip, delim, j);
+		if(ep) {
+			/*
+			 * found in new piece
+			 * copy back up and reset everything
+			 */
+			ip = (char*)bp->ebuf - i;
+			if(i < bp->bsize){
+				memmove(ip, bp->bbuf, i);
+				bp->gbuf = (unsigned char*)ip;
+			}
+			j = (ep - (char*)bp->bbuf) + 1;
+			bp->rdline = j;
+			bp->icount = j - i;
+			return ip;
+		}
+		ip += j;
+	}
+
+	/*
+	 * full buffer without finding
+	 */
+	bp->rdline = bp->bsize;
+	bp->icount = -bp->bsize;
+	bp->gbuf = bp->bbuf;
+	return 0;
+}
+
+int
+Blinelen(Biobuf *bp)
+{
+
+	return bp->rdline;
+}
diff --git a/src/libbio/brdstr.c b/src/libbio/brdstr.c
new file mode 100644
index 0000000..b3612a5
--- /dev/null
+++ b/src/libbio/brdstr.c
@@ -0,0 +1,112 @@
+#include	"lib9.h"
+#include	<bio.h>
+
+static char*
+badd(char *p, int *np, char *data, int ndata, int delim, int nulldelim)
+{
+	int n;
+
+	n = *np;
+	p = realloc(p, n+ndata+1);
+	if(p){
+		memmove(p+n, data, ndata);
+		n += ndata;
+		if(n>0 && nulldelim && p[n-1]==delim)
+			p[--n] = '\0';
+		else
+			p[n] = '\0';
+		*np = n;
+	}
+	return p;
+}
+
+char*
+Brdstr(Biobuf *bp, int delim, int nulldelim)
+{
+	char *ip, *ep, *p;
+	int i, j;
+
+	i = -bp->icount;
+	bp->rdline = 0;
+	if(i == 0) {
+		/*
+		 * eof or other error
+		 */
+		if(bp->state != Bractive) {
+			if(bp->state == Bracteof)
+				bp->state = Bractive;
+			bp->gbuf = bp->ebuf;
+			return nil;
+		}
+	}
+
+	/*
+	 * first try in remainder of buffer (gbuf doesn't change)
+	 */
+	ip = (char*)bp->ebuf - i;
+	ep = memchr(ip, delim, i);
+	if(ep) {
+		j = (ep - ip) + 1;
+		bp->icount += j;
+		return badd(nil, &bp->rdline, ip, j, delim, nulldelim);
+	}
+
+	/*
+	 * copy data to beginning of buffer
+	 */
+	if(i < bp->bsize)
+		memmove(bp->bbuf, ip, i);
+	bp->gbuf = bp->bbuf;
+
+	/*
+	 * append to buffer looking for the delim
+	 */
+	p = nil;
+	for(;;){
+		ip = (char*)bp->bbuf + i;
+		while(i < bp->bsize) {
+			j = read(bp->fid, ip, bp->bsize-i);
+			if(j <= 0 && i == 0)
+				return p;
+			if(j <= 0 && i > 0){
+				/*
+				 * end of file but no delim. pretend we got a delim
+				 * by making the delim \0 and smashing it with nulldelim.
+				 */
+				j = 1;
+				ep = ip;
+				delim = '\0';
+				nulldelim = 1;
+				*ep = delim;	/* there will be room for this */
+			}else{
+				bp->offset += j;
+				ep = memchr(ip, delim, j);
+			}
+			i += j;
+			if(ep) {
+				/*
+				 * found in new piece
+				 * copy back up and reset everything
+				 */
+				ip = (char*)bp->ebuf - i;
+				if(i < bp->bsize){
+					memmove(ip, bp->bbuf, i);
+					bp->gbuf = (unsigned char*)ip;
+				}
+				j = (ep - (char*)bp->bbuf) + 1;
+				bp->icount = j - i;
+				return badd(p, &bp->rdline, ip, j, delim, nulldelim);
+			}
+			ip += j;
+		}
+	
+		/*
+		 * full buffer without finding; add to user string and continue
+		 */
+		p = badd(p, &bp->rdline, (char*)bp->bbuf, bp->bsize, 0, 0);
+		i = 0;
+		bp->icount = 0;
+		bp->gbuf = bp->ebuf;
+	}
+	return 0;	/* never happens */
+}
diff --git a/src/libbio/bread.c b/src/libbio/bread.c
new file mode 100644
index 0000000..0254d01
--- /dev/null
+++ b/src/libbio/bread.c
@@ -0,0 +1,45 @@
+#include	"lib9.h"
+#include	<bio.h>
+
+long
+Bread(Biobuf *bp, void *ap, long count)
+{
+	long c;
+	unsigned char *p;
+	int i, n, ic;
+
+	p = ap;
+	c = count;
+	ic = bp->icount;
+
+	while(c > 0) {
+		n = -ic;
+		if(n > c)
+			n = c;
+		if(n == 0) {
+			if(bp->state != Bractive)
+				break;
+			i = read(bp->fid, bp->bbuf, bp->bsize);
+			if(i <= 0) {
+				bp->state = Bracteof;
+				if(i < 0)
+					bp->state = Binactive;
+				break;
+			}
+			bp->gbuf = bp->bbuf;
+			bp->offset += i;
+			if(i < bp->bsize) {
+				memmove(bp->ebuf-i, bp->bbuf, i);
+				bp->gbuf = bp->ebuf-i;
+			}
+			ic = -i;
+			continue;
+		}
+		memmove(p, bp->ebuf+ic, n);
+		c -= n;
+		ic += n;
+		p += n;
+	}
+	bp->icount = ic;
+	return count-c;
+}
diff --git a/src/libbio/bseek.c b/src/libbio/bseek.c
new file mode 100644
index 0000000..f4325fb
--- /dev/null
+++ b/src/libbio/bseek.c
@@ -0,0 +1,56 @@
+#include	"lib9.h"
+#include	<bio.h>
+
+off_t
+Bseek(Biobuf *bp, off_t offset, int base)
+{
+	off_t n, d;
+
+	switch(bp->state) {
+	default:
+		fprint(2, "Bseek: unknown state %d\n", bp->state);
+		return Beof;
+
+	case Bracteof:
+		bp->state = Bractive;
+		bp->icount = 0;
+		bp->gbuf = bp->ebuf;
+
+	case Bractive:
+		n = offset;
+		if(base == 1) {
+			n += Boffset(bp);
+			base = 0;
+		}
+
+		/*
+		 * try to seek within buffer
+		 */
+		if(base == 0) {
+			d = n - Boffset(bp);
+			bp->icount += d;
+			if(d >= 0) {
+				if(bp->icount <= 0)
+					return n;
+			} else {
+				if(bp->ebuf - bp->gbuf >= -bp->icount)
+					return n;
+			}
+		}
+
+		/*
+		 * reset the buffer
+		 */
+		n = lseek(bp->fid, n, base);
+		bp->icount = 0;
+		bp->gbuf = bp->ebuf;
+		break;
+
+	case Bwactive:
+		Bflush(bp);
+		n = lseek(bp->fid, offset, base);
+		break;
+	}
+	bp->offset = n;
+	return n;
+}
diff --git a/src/libbio/bundle.ports b/src/libbio/bundle.ports
new file mode 100644
index 0000000..bea32d3
--- /dev/null
+++ b/src/libbio/bundle.ports
@@ -0,0 +1,45 @@
+--- Makefile ---
+# New ports collection makefile for: libbio
+# Date Created:		11 Feb 2003
+# Whom:			rsc
+#
+# THIS LINE NEEDS REPLACING.  IT'S HERE TO GET BY PORTLINT
+# $FreeBSD: ports/devel/libbio/Makefile,v 1.1 2003/02/12 00:51:22 rsc Exp $
+
+PORTNAME=	libbio
+PORTVERSION=	2.0
+CATEGORIES=	devel
+MASTER_SITES=	http://pdos.lcs.mit.edu/~rsc/software/
+EXTRACT_SUFX=	.tgz
+
+MAINTAINER=	rsc@post.harvard.edu
+
+DEPENDS=	${PORTSDIR}/devel/libfmt ${PORTSDIR}/devel/libutf
+
+MAN3=		bio.3
+USE_REINPLACE=	yes
+
+.include <bsd.port.pre.mk>
+
+post-patch:
+	${REINPLACE_CMD} -e 's,$$(PREFIX),${PREFIX},g' ${WRKSRC}/Makefile
+
+.include <bsd.port.post.mk>
+--- pkg-comment ---
+Simple buffered I/O library from Plan 9
+--- pkg-descr ---
+Libbio is a port of Plan 9's formatted I/O library.
+It provides most of the same functionality as stdio or sfio,
+but with a simpler interface and smaller footprint.
+
+WWW: http://pdos.lcs.mit.edu/~rsc/software/#libbio
+http://plan9.bell-labs.com/magic/man2html/2/bio
+
+Russ Cox
+rsc@post.harvard.edu
+--- pkg-plist ---
+lib/libbio.a
+include/bio.h
+--- /dev/null ---
+This is just a way to make sure blank lines don't
+creep into pkg-plist.
diff --git a/src/libbio/bwrite.c b/src/libbio/bwrite.c
new file mode 100644
index 0000000..2dfeaaa
--- /dev/null
+++ b/src/libbio/bwrite.c
@@ -0,0 +1,38 @@
+#include	"lib9.h"
+#include	<bio.h>
+
+long
+Bwrite(Biobuf *bp, void *ap, long count)
+{
+	long c;
+	unsigned char *p;
+	int i, n, oc;
+
+	p = ap;
+	c = count;
+	oc = bp->ocount;
+
+	while(c > 0) {
+		n = -oc;
+		if(n > c)
+			n = c;
+		if(n == 0) {
+			if(bp->state != Bwactive)
+				return Beof;
+			i = write(bp->fid, bp->bbuf, bp->bsize);
+			if(i != bp->bsize) {
+				bp->state = Binactive;
+				return Beof;
+			}
+			bp->offset += i;
+			oc = -bp->bsize;
+			continue;
+		}
+		memmove(bp->ebuf+oc, p, n);
+		oc += n;
+		c -= n;
+		p += n;
+	}
+	bp->ocount = oc;
+	return count-c;
+}
diff --git a/src/libbio/lib9.h b/src/libbio/lib9.h
new file mode 100644
index 0000000..843f755
--- /dev/null
+++ b/src/libbio/lib9.h
@@ -0,0 +1,12 @@
+#include <fmt.h>
+#include <fcntl.h>
+#include	<string.h>
+#include	<unistd.h>
+#include <stdlib.h>
+
+#define OREAD O_RDONLY
+#define OWRITE O_WRONLY
+
+#include <utf.h>
+
+#define nil ((void*)0)
diff --git a/src/libbio/mkfile b/src/libbio/mkfile
new file mode 100644
index 0000000..bb99a25
--- /dev/null
+++ b/src/libbio/mkfile
@@ -0,0 +1 @@
+<../libutf/mkfile
diff --git a/src/libbio/rpm.spec b/src/libbio/rpm.spec
new file mode 100644
index 0000000..5cb9830
--- /dev/null
+++ b/src/libbio/rpm.spec
@@ -0,0 +1,30 @@
+Summary: Simple buffered I/O library from Plan 9
+Name: libbio
+Version: 2.0
+Release: 1
+Group: Development/C
+Copyright: LGPL
+Packager: Russ Cox <rsc@post.harvard.edu>
+Source: http://pdos.lcs.mit.edu/~rsc/software/libbio-2.0.tgz
+URL: http://pdos.lcs.mit.edu/~rsc/software/#libbio
+Requires: libfmt libutf
+
+%description
+Libbio is a port of Plan 9's formatted I/O library.
+It provides most of the same functionality as stdio or sfio,
+but with a simpler interface and smaller footprint.
+
+http://plan9.bell-labs.com/magic/man2html/2/bio
+%prep
+%setup
+
+%build
+make
+
+%install
+make install
+
+%files
+/usr/local/include/bio.h
+/usr/local/lib/libbio.a
+/usr/local/man/man3/bio.3