aboutsummaryrefslogtreecommitdiff
path: root/src/lib9
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib9')
-rw-r--r--src/lib9/LICENSE258
-rw-r--r--src/lib9/Make.Darwin-PowerMacintosh6
-rw-r--r--src/lib9/Make.FreeBSD-3867
-rw-r--r--src/lib9/Make.HP-UX-90006
-rw-r--r--src/lib9/Make.Linux-3867
-rw-r--r--src/lib9/Make.NetBSD-3867
-rw-r--r--src/lib9/Make.OSF1-alpha6
-rw-r--r--src/lib9/Make.SunOS-sun4u2
-rw-r--r--src/lib9/Make.SunOS-sun4u-cc6
-rw-r--r--src/lib9/Make.SunOS-sun4u-gcc6
-rw-r--r--src/lib9/Makefile120
-rw-r--r--src/lib9/Makefile.MID49
-rw-r--r--src/lib9/_exits.c9
-rw-r--r--src/lib9/argv0.c4
-rw-r--r--src/lib9/await.c105
-rw-r--r--src/lib9/encodefmt.c69
-rw-r--r--src/lib9/errstr.c68
-rw-r--r--src/lib9/exits.c10
-rw-r--r--src/lib9/ffork-FreeBSD.c33
-rw-r--r--src/lib9/ffork-Linux.c39
-rw-r--r--src/lib9/getcallerpc-386.c7
-rw-r--r--src/lib9/getfields.c36
-rw-r--r--src/lib9/lib9.h246
-rw-r--r--src/lib9/lock.c54
-rw-r--r--src/lib9/malloctag.c15
-rw-r--r--src/lib9/mallocz.c14
-rw-r--r--src/lib9/mkfile2
-rw-r--r--src/lib9/nrand.c17
-rw-r--r--src/lib9/qlock.c360
-rw-r--r--src/lib9/rand.c89
-rw-r--r--src/lib9/readn.c21
-rw-r--r--src/lib9/rendez.c180
-rw-r--r--src/lib9/strecpy.c16
-rw-r--r--src/lib9/sysfatal.c20
-rw-r--r--src/lib9/tas-386.s6
-rw-r--r--src/lib9/test.c8
-rw-r--r--src/lib9/testfork.c21
-rw-r--r--src/lib9/tokenize.c106
-rw-r--r--src/lib9/u16.c52
-rw-r--r--src/lib9/u32.c109
-rw-r--r--src/lib9/u64.c126
-rw-r--r--src/lib9/wait.c30
-rw-r--r--src/lib9/werrstr.c13
43 files changed, 2365 insertions, 0 deletions
diff --git a/src/lib9/LICENSE b/src/lib9/LICENSE
new file mode 100644
index 00000000..a5d7d87d
--- /dev/null
+++ b/src/lib9/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/lib9/Make.Darwin-PowerMacintosh b/src/lib9/Make.Darwin-PowerMacintosh
new file mode 100644
index 00000000..14b8d4e7
--- /dev/null
+++ b/src/lib9/Make.Darwin-PowerMacintosh
@@ -0,0 +1,6 @@
+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
diff --git a/src/lib9/Make.FreeBSD-386 b/src/lib9/Make.FreeBSD-386
new file mode 100644
index 00000000..087ed3ab
--- /dev/null
+++ b/src/lib9/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/lib9/Make.HP-UX-9000 b/src/lib9/Make.HP-UX-9000
new file mode 100644
index 00000000..edbdc111
--- /dev/null
+++ b/src/lib9/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/lib9/Make.Linux-386 b/src/lib9/Make.Linux-386
new file mode 100644
index 00000000..74b0252c
--- /dev/null
+++ b/src/lib9/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/lib9/Make.NetBSD-386 b/src/lib9/Make.NetBSD-386
new file mode 100644
index 00000000..087ed3ab
--- /dev/null
+++ b/src/lib9/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/lib9/Make.OSF1-alpha b/src/lib9/Make.OSF1-alpha
new file mode 100644
index 00000000..3d45279b
--- /dev/null
+++ b/src/lib9/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/lib9/Make.SunOS-sun4u b/src/lib9/Make.SunOS-sun4u
new file mode 100644
index 00000000..c5fe67b8
--- /dev/null
+++ b/src/lib9/Make.SunOS-sun4u
@@ -0,0 +1,2 @@
+include Make.SunOS-sun4u-$(CC)
+NAN=nan64.$O
diff --git a/src/lib9/Make.SunOS-sun4u-cc b/src/lib9/Make.SunOS-sun4u-cc
new file mode 100644
index 00000000..829301de
--- /dev/null
+++ b/src/lib9/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/lib9/Make.SunOS-sun4u-gcc b/src/lib9/Make.SunOS-sun4u-gcc
new file mode 100644
index 00000000..5c415948
--- /dev/null
+++ b/src/lib9/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/lib9/Makefile b/src/lib9/Makefile
new file mode 100644
index 00000000..595db787
--- /dev/null
+++ b/src/lib9/Makefile
@@ -0,0 +1,120 @@
+
+# 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=lib9.a
+VERSION=2.0
+PORTPLACE=devel/lib9
+NAME=lib9
+
+OFILES=\
+ _exits.$O\
+ argv0.$O\
+ await.$O\
+ encodefmt.$O\
+ errstr.$O\
+ exits.$O\
+ ffork-$(SYSNAME).$O\
+ getcallerpc-$(OBJTYPE).$O\
+ getfields.$O\
+ lock.$O\
+ malloctag.$O\
+ mallocz.$O\
+ nrand.$O\
+ qlock.$O\
+ readn.$O\
+ rendez.$O\
+ strecpy.$O\
+ sysfatal.$O\
+ tas-$(OBJTYPE).$O\
+ tokenize.$O\
+ u16.$O\
+ u32.$O\
+ u64.$O\
+ wait.$O\
+ werrstr.$O\
+
+HFILES=\
+ lib9.h\
+
+all: $(LIB)
+
+install: $(LIB)
+ test -d $(PREFIX)/man/man3 || mkdir $(PREFIX)/man/man3
+ # install -m 0644 lib9.3 $(PREFIX)/man/man3/lib9.3
+ install -m 0644 lib9.h $(PREFIX)/include/lib9.h
+ install -m 0644 $(LIB) $(PREFIX)/lib/$(LIB)
+
+test: $(LIB) test.$O
+ $(CC) -o test test.$O $(LIB) -L$(PREFIX)/lib -lfmt -lutf
+
+testfork: $(LIB) testfork.$O
+ $(CC) -o testfork testfork.$O $(LIB) -L$(PREFIX)/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/lib9/Makefile.MID b/src/lib9/Makefile.MID
new file mode 100644
index 00000000..8b3584cb
--- /dev/null
+++ b/src/lib9/Makefile.MID
@@ -0,0 +1,49 @@
+LIB=lib9.a
+VERSION=2.0
+PORTPLACE=devel/lib9
+NAME=lib9
+
+OFILES=\
+ _exits.$O\
+ argv0.$O\
+ await.$O\
+ encodefmt.$O\
+ errstr.$O\
+ exits.$O\
+ ffork-$(SYSNAME).$O\
+ getcallerpc-$(OBJTYPE).$O\
+ getfields.$O\
+ lock.$O\
+ malloctag.$O\
+ mallocz.$O\
+ nrand.$O\
+ qlock.$O\
+ readn.$O\
+ rendez.$O\
+ strecpy.$O\
+ sysfatal.$O\
+ tas-$(OBJTYPE).$O\
+ tokenize.$O\
+ u16.$O\
+ u32.$O\
+ u64.$O\
+ wait.$O\
+ werrstr.$O\
+
+HFILES=\
+ lib9.h\
+
+all: $(LIB)
+
+install: $(LIB)
+ test -d $(PREFIX)/man/man3 || mkdir $(PREFIX)/man/man3
+ # install -m 0644 lib9.3 $(PREFIX)/man/man3/lib9.3
+ install -m 0644 lib9.h $(PREFIX)/include/lib9.h
+ install -m 0644 $(LIB) $(PREFIX)/lib/$(LIB)
+
+test: $(LIB) test.$O
+ $(CC) -o test test.$O $(LIB) -L$(PREFIX)/lib -lfmt -lutf
+
+testfork: $(LIB) testfork.$O
+ $(CC) -o testfork testfork.$O $(LIB) -L$(PREFIX)/lib -lfmt -lutf
+
diff --git a/src/lib9/_exits.c b/src/lib9/_exits.c
new file mode 100644
index 00000000..35ff4e67
--- /dev/null
+++ b/src/lib9/_exits.c
@@ -0,0 +1,9 @@
+#include <lib9.h>
+
+void
+_exits(char *s)
+{
+ if(s && *s)
+ _exit(1);
+ _exit(0);
+}
diff --git a/src/lib9/argv0.c b/src/lib9/argv0.c
new file mode 100644
index 00000000..2c846f4d
--- /dev/null
+++ b/src/lib9/argv0.c
@@ -0,0 +1,4 @@
+#include <lib9.h>
+
+char *argv0;
+
diff --git a/src/lib9/await.c b/src/lib9/await.c
new file mode 100644
index 00000000..9df7faa5
--- /dev/null
+++ b/src/lib9/await.c
@@ -0,0 +1,105 @@
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <string.h>
+#include <errno.h>
+#include <lib9.h>
+
+static struct {
+ int sig;
+ char *str;
+} tab[] = {
+ SIGHUP, "hangup",
+ SIGINT, "interrupt",
+ SIGQUIT, "quit",
+ SIGILL, "sys: trap: illegal instruction",
+ SIGTRAP, "sys: trace trap",
+ SIGABRT, "sys: abort",
+#ifdef SIGEMT
+ SIGEMT, "sys: emulate instruction executed",
+#endif
+ SIGFPE, "sys: fp: trap",
+ SIGKILL, "sys: kill",
+ SIGBUS, "sys: bus error",
+ SIGSEGV, "sys: segmentation violation",
+ SIGALRM, "alarm",
+ SIGTERM, "kill",
+ SIGURG, "sys: urgent condition on socket",
+ SIGSTOP, "sys: stop",
+ SIGTSTP, "sys: tstp",
+ SIGCONT, "sys: cont",
+ SIGCHLD, "sys: child",
+ SIGTTIN, "sys: ttin",
+ SIGTTOU, "sys: ttou",
+ SIGIO, "sys: i/o possible on fd",
+ SIGXCPU, "sys: cpu time limit exceeded",
+ SIGXFSZ, "sys: file size limit exceeded",
+ SIGVTALRM, "sys: virtual time alarm",
+ SIGPROF, "sys: profiling timer alarm",
+ SIGWINCH, "sys: window size change",
+#ifdef SIGINFO
+ SIGINFO, "sys: status request",
+#endif
+ SIGUSR1, "sys: usr1",
+ SIGUSR2, "sys: usr2",
+};
+
+static char*
+_p9sigstr(int sig, char *tmp)
+{
+ int i;
+
+ for(i=0; i<nelem(tab); i++)
+ if(tab[i].sig == sig)
+ return tab[i].str;
+ sprint(tmp, "sys: signal %d", sig);
+ return tmp;
+}
+
+/*
+static int
+_p9strsig(char *s)
+{
+ int i;
+
+ for(i=0; i<nelem(tab); i++)
+ if(strcmp(s, tab[i].str) == 0)
+ return tab[i].sig;
+ return 0;
+}
+*/
+
+int
+await(char *str, int n)
+{
+ int pid, status, cd;
+ struct rusage ru;
+ char buf[128], tmp[64];
+ ulong u, s;
+
+ for(;;){
+ pid = wait3(&status, 0, &ru);
+ if(pid < 0)
+ return -1;
+ u = ru.ru_utime.tv_sec*1000+((ru.ru_utime.tv_usec+500)/1000);
+ s = ru.ru_stime.tv_sec*1000+((ru.ru_stime.tv_usec+500)/1000);
+ if(WIFEXITED(status)){
+ status = WEXITSTATUS(status);
+ if(status)
+ snprint(buf, sizeof buf, "%d %lu %lu %lu %d", pid, u, s, u+s, status);
+ else
+ snprint(buf, sizeof buf, "%d %lu %lu %lu ''", pid, u, s, u+s);
+ strecpy(str, str+n, buf);
+ return strlen(str);
+ }
+ if(WIFSIGNALED(status)){
+ cd = WCOREDUMP(status);
+ USED(cd);
+ snprint(buf, sizeof buf, "%d %lu %lu %lu '%s'", pid, u, s, u+s, _p9sigstr(WTERMSIG(status), tmp));
+ strecpy(str, str+n, buf);
+ return strlen(str);
+ }
+ }
+}
diff --git a/src/lib9/encodefmt.c b/src/lib9/encodefmt.c
new file mode 100644
index 00000000..9a5cbfc4
--- /dev/null
+++ b/src/lib9/encodefmt.c
@@ -0,0 +1,69 @@
+#include <lib9.h>
+
+int
+encodefmt(Fmt *f)
+{
+ char *out;
+ char *buf;
+ int len;
+ int ilen;
+ int rv;
+ uchar *b;
+ char obuf[64]; // rsc optimization
+
+ if(!(f->flags&FmtPrec) || f->prec < 1)
+ goto error;
+
+ b = va_arg(f->args, uchar*);
+
+ ilen = f->prec;
+ f->prec = 0;
+ f->flags &= ~FmtPrec;
+ switch(f->r){
+ case '<':
+ len = (8*ilen+4)/5 + 3;
+ break;
+ case '[':
+ len = (8*ilen+5)/6 + 4;
+ break;
+ case 'H':
+ len = 2*ilen + 1;
+ break;
+ default:
+ goto error;
+ }
+
+ if(len > sizeof(obuf)){
+ buf = malloc(len);
+ if(buf == nil)
+ goto error;
+ } else
+ buf = obuf;
+
+ // convert
+ out = buf;
+ switch(f->r){
+ case '<':
+ rv = enc32(out, len, b, ilen);
+ break;
+ case '[':
+ rv = enc64(out, len, b, ilen);
+ break;
+ case 'H':
+ rv = enc16(out, len, b, ilen);
+ break;
+ default:
+ rv = -1;
+ break;
+ }
+ if(rv < 0)
+ goto error;
+
+ fmtstrcpy(f, buf);
+ if(buf != obuf)
+ free(buf);
+ return 0;
+
+error:
+ return fmtstrcpy(f, "<encodefmt>");
+}
diff --git a/src/lib9/errstr.c b/src/lib9/errstr.c
new file mode 100644
index 00000000..e576b12b
--- /dev/null
+++ b/src/lib9/errstr.c
@@ -0,0 +1,68 @@
+/*
+ * We assume there's only one error buffer for the whole system.
+ * If you use ffork, you need to provide a _syserrstr. Since most
+ * people will use libthread (which provides a _syserrstr), this is
+ * okay.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <lib9.h>
+
+enum
+{
+ EPLAN9 = 0x19283745,
+};
+
+char *(*_syserrstr)(void);
+static char xsyserr[ERRMAX];
+static char*
+getsyserr(void)
+{
+ char *s;
+
+ s = nil;
+ if(_syserrstr)
+ s = (*_syserrstr)();
+ if(s == nil)
+ s = xsyserr;
+ return s;
+}
+
+int
+errstr(char *err, uint n)
+{
+ char tmp[ERRMAX];
+ char *syserr;
+
+ syserr = getsyserr();
+ if(errno != EPLAN9)
+ strcpy(syserr, strerror(errno));
+
+ strecpy(tmp, tmp+ERRMAX, syserr);
+ strecpy(syserr, syserr+ERRMAX, err);
+ strecpy(err, err+n, tmp);
+ errno = EPLAN9;
+ return 0;
+}
+
+void
+rerrstr(char *err, uint n)
+{
+ char *syserr;
+
+ syserr = getsyserr();
+ if(errno != EPLAN9)
+ strcpy(syserr, strerror(errno));
+ strecpy(err, err+n, syserr);
+}
+
+/* replaces __errfmt in libfmt */
+
+int
+__errfmt(Fmt *f)
+{
+ if(errno == EPLAN9)
+ return fmtstrcpy(f, getsyserr());
+ return fmtstrcpy(f, strerror(errno));
+}
diff --git a/src/lib9/exits.c b/src/lib9/exits.c
new file mode 100644
index 00000000..a449f68e
--- /dev/null
+++ b/src/lib9/exits.c
@@ -0,0 +1,10 @@
+#include <lib9.h>
+
+void
+exits(char *s)
+{
+ if(s && *s)
+ exit(1);
+ exit(0);
+}
+
diff --git a/src/lib9/ffork-FreeBSD.c b/src/lib9/ffork-FreeBSD.c
new file mode 100644
index 00000000..a7c82e64
--- /dev/null
+++ b/src/lib9/ffork-FreeBSD.c
@@ -0,0 +1,33 @@
+#include <lib9.h>
+
+extern int __isthreaded;
+int
+ffork(int flags, void(*fn)(void*), void *arg)
+{
+ void *p;
+
+ __isthreaded = 1;
+ p = malloc(16384);
+ if(p == nil)
+ return -1;
+ memset(p, 0xFE, 16384);
+ return rfork_thread(RFPROC|flags, (char*)p+16000, (int(*)(void*))fn, arg);
+}
+
+/*
+ * For FreeBSD libc.
+ */
+
+typedef struct {
+ volatile long access_lock;
+ volatile long lock_owner;
+ volatile char *fname;
+ volatile int lineno;
+} spinlock_t;
+
+void
+_spinlock(spinlock_t *lk)
+{
+ lock((Lock*)&lk->access_lock);
+}
+
diff --git a/src/lib9/ffork-Linux.c b/src/lib9/ffork-Linux.c
new file mode 100644
index 00000000..aad80041
--- /dev/null
+++ b/src/lib9/ffork-Linux.c
@@ -0,0 +1,39 @@
+#include <sched.h>
+#include <signal.h>
+#include <lib9.h>
+
+int fforkstacksize = 16384;
+
+int
+ffork(int flags, void (*fn)(void*), void *arg)
+{
+ char *p;
+ int cloneflag, pid;
+
+ p = malloc(fforkstacksize);
+ if(p == nil)
+ return -1;
+ cloneflag = 0;
+ flags &= ~RFPROC;
+ if(flags&RFMEM){
+ cloneflag |= CLONE_VM;
+ flags &= ~RFMEM;
+ }
+ if(!(flags&RFFDG))
+ cloneflag |= CLONE_FILES;
+ else
+ flags &= ~RFFDG;
+ if(!(flags&RFNOWAIT))
+ cloneflag |= SIGCHLD;
+ else
+ flags &= ~RFNOWAIT;
+ if(flags){
+ fprint(2, "unknown rfork flags %x\n", flags);
+ return -1;
+ }
+ pid = clone((int(*)(void*))fn, p+fforkstacksize-16, cloneflag, arg);
+ if(pid < 0)
+ free(p);
+ return pid;
+}
+
diff --git a/src/lib9/getcallerpc-386.c b/src/lib9/getcallerpc-386.c
new file mode 100644
index 00000000..1367370e
--- /dev/null
+++ b/src/lib9/getcallerpc-386.c
@@ -0,0 +1,7 @@
+#include <lib9.h>
+
+ulong
+getcallerpc(void *x)
+{
+ return (((ulong*)(x))[-1]);
+}
diff --git a/src/lib9/getfields.c b/src/lib9/getfields.c
new file mode 100644
index 00000000..79f7abad
--- /dev/null
+++ b/src/lib9/getfields.c
@@ -0,0 +1,36 @@
+#include <lib9.h>
+
+int
+getfields(char *str, char **args, int max, int mflag, char *set)
+{
+ Rune r;
+ int nr, intok, narg;
+
+ if(max <= 0)
+ return 0;
+
+ narg = 0;
+ args[narg] = str;
+ if(!mflag)
+ narg++;
+ intok = 0;
+ for(;; str += nr) {
+ nr = chartorune(&r, str);
+ if(r == 0)
+ break;
+ if(utfrune(set, r)) {
+ if(narg >= max)
+ break;
+ *str = 0;
+ intok = 0;
+ args[narg] = str + nr;
+ if(!mflag)
+ narg++;
+ } else {
+ if(!intok && mflag)
+ narg++;
+ intok = 1;
+ }
+ }
+ return narg;
+}
diff --git a/src/lib9/lib9.h b/src/lib9/lib9.h
new file mode 100644
index 00000000..bb7e5404
--- /dev/null
+++ b/src/lib9/lib9.h
@@ -0,0 +1,246 @@
+/*
+ * Lib9 is miscellany from the Plan 9 C library that doesn't
+ * fit into libutf or into libfmt, but is still missing from traditional
+ * Unix C libraries.
+ */
+#ifndef _LIB9H_
+#define _LIB9H_ 1
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#ifndef _FMTH_
+# include <fmt.h>
+#endif
+
+#define nil ((void*)0)
+#define nelem(x) (sizeof(x)/sizeof((x)[0]))
+
+#define _NEEDUCHAR 1
+#define _NEEDUSHORT 1
+#define _NEEDUINT 1
+#define _NEEDULONG 1
+
+#if defined(__linux__)
+# include <sys/types.h>
+# if defined(__USE_MISC)
+# undef _NEEDUSHORT
+# undef _NEEDUINT
+# undef _NEEDULONG
+# endif
+#endif
+#if defined(__FreeBSD__)
+# include <sys/types.h>
+# if !defined(_POSIX_SOURCE)
+# undef _NEEDUSHORT
+# undef _NEEDUINT
+# endif
+#endif
+
+typedef signed char schar;
+typedef unsigned int u32int;
+#ifdef _NEEDUCHAR
+ typedef unsigned char uchar;
+#endif
+#ifdef _NEEDUSHORT
+ typedef unsigned short ushort;
+#endif
+#ifdef _NEEDUINT
+ typedef unsigned int uint;
+#endif
+#ifdef _NEEDULONG
+ typedef unsigned long ulong;
+#endif
+typedef unsigned long long uvlong;
+typedef long long vlong;
+
+/* rfork to create new process running fn(arg) */
+
+#if defined(__FreeBSD__)
+#undef RFFDG
+#undef RFNOTEG
+#undef RFPROC
+#undef RFMEM
+#undef RFNOWAIT
+#undef RFCFDG
+#endif
+
+enum
+{
+/* RFNAMEG = (1<<0), */
+/* RFENVG = (1<<1), */
+ RFFDG = (1<<2),
+ RFNOTEG = (1<<3),
+ RFPROC = (1<<4),
+ RFMEM = (1<<5),
+ RFNOWAIT = (1<<6),
+/* RFCNAMEG = (1<<10), */
+/* RFCENVG = (1<<11), */
+ RFCFDG = (1<<12),
+/* RFREND = (1<<13), */
+/* RFNOMNT = (1<<14) */
+};
+extern int ffork(int, void(*)(void*), void*);
+
+/* wait for processes */
+#define wait _p9wait
+typedef struct Waitmsg Waitmsg;
+struct Waitmsg
+{
+ int pid; /* of loved one */
+ ulong time[3]; /* of loved one & descendants */
+ char *msg;
+};
+extern int await(char*, int);
+extern Waitmsg* wait(void);
+
+/* synchronization */
+typedef struct Lock Lock;
+struct Lock
+{
+ int val;
+};
+
+extern int _tas(void*);
+extern void lock(Lock*);
+extern void unlock(Lock*);
+extern int canlock(Lock*);
+
+typedef struct QLp QLp;
+struct QLp
+{
+ int inuse;
+ QLp *next;
+ int state;
+};
+
+typedef struct QLock QLock;
+struct QLock
+{
+ Lock lock;
+ int locked;
+ QLp *head;
+ QLp *tail;
+};
+
+extern void qlock(QLock*);
+extern void qunlock(QLock*);
+extern int canqlock(QLock*);
+extern void _qlockinit(ulong (*)(ulong, ulong));
+
+typedef struct RWLock RWLock;
+struct RWLock
+{
+ Lock lock;
+ int readers;
+ int writer;
+ QLp *head;
+ QLp *tail;
+};
+
+extern void rlock(RWLock*);
+extern void runlock(RWLock*);
+extern int canrlock(RWLock*);
+extern void wlock(RWLock*);
+extern void wunlock(RWLock*);
+extern int canwlock(RWLock*);
+
+typedef struct Rendez Rendez;
+struct Rendez
+{
+ QLock *l;
+ QLp *head;
+ QLp *tail;
+};
+
+extern void rsleep(Rendez*);
+extern int rwakeup(Rendez*);
+extern int rwakeupall(Rendez*);
+
+extern ulong rendezvous(ulong, ulong);
+
+/* one of a kind */
+extern void sysfatal(char*, ...);
+extern int nrand(int);
+extern void setmalloctag(void*, ulong);
+extern void setrealloctag(void*, ulong);
+extern void *mallocz(ulong, int);
+extern long readn(int, void*, long);
+extern void exits(char*);
+extern void _exits(char*);
+extern ulong getcallerpc(void*);
+
+/* string routines */
+extern char* strecpy(char*, char*, char*);
+extern int tokenize(char*, char**, int);
+extern int cistrncmp(char*, char*, int);
+extern int cistrcmp(char*, char*);
+extern char* cistrstr(char*, char*);
+extern int getfields(char*, char**, int, int, char*);
+extern int gettokens(char *, char **, int, char *);
+
+/* formatting helpers */
+extern int dec64(uchar*, int, char*, int);
+extern int enc64(char*, int, uchar*, int);
+extern int dec32(uchar*, int, char*, int);
+extern int enc32(char*, int, uchar*, int);
+extern int dec16(uchar*, int, char*, int);
+extern int enc16(char*, int, uchar*, int);
+extern int encodefmt(Fmt*);
+
+/* error string */
+enum
+{
+ ERRMAX = 128
+};
+extern void rerrstr(char*, uint);
+extern void werrstr(char*, ...);
+extern int errstr(char*, uint);
+
+/* compiler directives on plan 9 */
+#define USED(x) if(x){}else{}
+#define SET(x) ((x)=0)
+
+/* command line */
+extern char *argv0;
+#define ARGBEGIN for((argv0||(argv0=*argv)),argv++,argc--;\
+ argv[0] && argv[0][0]=='-' && argv[0][1];\
+ argc--, argv++) {\
+ char *_args, *_argt;\
+ Rune _argc;\
+ _args = &argv[0][1];\
+ if(_args[0]=='-' && _args[1]==0){\
+ argc--; argv++; break;\
+ }\
+ _argc = 0;\
+ while(*_args && (_args += chartorune(&_argc, _args)))\
+ switch(_argc)
+#define ARGEND SET(_argt);USED(_argt);USED(_argc);USED(_args);}USED(argv);USED(argc);
+#define ARGF() (_argt=_args, _args="",\
+ (*_argt? _argt: argv[1]? (argc--, *++argv): 0))
+#define EARGF(x) (_argt=_args, _args="",\
+ (*_argt? _argt: argv[1]? (argc--, *++argv): ((x), abort(), (char*)0)))
+
+#define ARGC() _argc
+
+#define OREAD O_RDONLY
+#define OWRITE O_WRONLY
+#define AEXIST 0
+#define AREAD 4
+#define AWRITE 2
+#define AEXEC 1
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* _LIB9H_ */
diff --git a/src/lib9/lock.c b/src/lib9/lock.c
new file mode 100644
index 00000000..2da73626
--- /dev/null
+++ b/src/lib9/lock.c
@@ -0,0 +1,54 @@
+#include <unistd.h>
+#include <sched.h>
+#include <lib9.h>
+
+int _ntas;
+static int
+_xtas(void *v)
+{
+ int x;
+
+_ntas++;
+ x = _tas(v);
+ if(x == 0 || x == 0xCAFEBABE)
+ return x;
+ fprint(2, "%d: tas %p got %ux\n", getpid(), v, x);
+ abort();
+}
+
+int
+canlock(Lock *l)
+{
+ return !_xtas(&l->val);
+}
+
+void
+unlock(Lock *l)
+{
+ l->val = 0;
+}
+
+void
+lock(Lock *lk)
+{
+ int i;
+
+ /* once fast */
+ if(!_xtas(&lk->val))
+ return;
+ /* a thousand times pretty fast */
+ for(i=0; i<1000; i++){
+ if(!_xtas(&lk->val))
+ return;
+ sched_yield();
+ }
+ /* now nice and slow */
+ for(i=0; i<1000; i++){
+ if(!_xtas(&lk->val))
+ return;
+ usleep(100*1000);
+ }
+ /* take your time */
+ while(_xtas(&lk->val))
+ usleep(1000*1000);
+}
diff --git a/src/lib9/malloctag.c b/src/lib9/malloctag.c
new file mode 100644
index 00000000..e5682bc7
--- /dev/null
+++ b/src/lib9/malloctag.c
@@ -0,0 +1,15 @@
+#include <lib9.h>
+
+void
+setmalloctag(void *v, ulong t)
+{
+ USED(v);
+ USED(t);
+}
+
+void
+setrealloctag(void *v, ulong t)
+{
+ USED(v);
+ USED(t);
+}
diff --git a/src/lib9/mallocz.c b/src/lib9/mallocz.c
new file mode 100644
index 00000000..c6313008
--- /dev/null
+++ b/src/lib9/mallocz.c
@@ -0,0 +1,14 @@
+#include <unistd.h>
+#include <string.h>
+#include <lib9.h>
+
+void*
+mallocz(unsigned long n, int clr)
+{
+ void *v;
+
+ v = malloc(n);
+ if(clr && v)
+ memset(v, 0, n);
+ return v;
+}
diff --git a/src/lib9/mkfile b/src/lib9/mkfile
new file mode 100644
index 00000000..703f6b06
--- /dev/null
+++ b/src/lib9/mkfile
@@ -0,0 +1,2 @@
+<../libutf/mkfile
+
diff --git a/src/lib9/nrand.c b/src/lib9/nrand.c
new file mode 100644
index 00000000..cf9c17c3
--- /dev/null
+++ b/src/lib9/nrand.c
@@ -0,0 +1,17 @@
+#include <lib9.h>
+
+#define MASK 0x7fffffffL
+
+int
+nrand(int n)
+{
+ long slop, v;
+
+ if(n < 0)
+ return n;
+ slop = MASK % n;
+ do
+ v = lrand();
+ while(v <= slop);
+ return v % n;
+}
diff --git a/src/lib9/qlock.c b/src/lib9/qlock.c
new file mode 100644
index 00000000..55a18466
--- /dev/null
+++ b/src/lib9/qlock.c
@@ -0,0 +1,360 @@
+#include <lib9.h>
+
+static struct {
+ QLp *p;
+ QLp x[1024];
+} ql = {
+ ql.x
+};
+
+enum
+{
+ Queuing,
+ QueuingR,
+ QueuingW,
+ Sleeping,
+};
+
+static ulong (*_rendezvousp)(ulong, ulong) = rendezvous;
+
+/* this gets called by the thread library ONLY to get us to use its rendezvous */
+void
+_qlockinit(ulong (*r)(ulong, ulong))
+{
+ _rendezvousp = r;
+}
+
+/* find a free shared memory location to queue ourselves in */
+static QLp*
+getqlp(void)
+{
+ QLp *p, *op;
+
+ op = ql.p;
+ for(p = op+1; ; p++){
+ if(p == &ql.x[nelem(ql.x)])
+ p = ql.x;
+ if(p == op)
+ abort();
+ if(_tas(&(p->inuse)) == 0){
+ ql.p = p;
+ p->next = nil;
+ break;
+ }
+ }
+ return p;
+}
+
+void
+qlock(QLock *q)
+{
+ QLp *p, *mp;
+
+ lock(&q->lock);
+ if(!q->locked){
+ q->locked = 1;
+ unlock(&q->lock);
+ return;
+ }
+
+
+ /* chain into waiting list */
+ mp = getqlp();
+ p = q->tail;
+ if(p == nil)
+ q->head = mp;
+ else
+ p->next = mp;
+ q->tail = mp;
+ mp->state = Queuing;
+ unlock(&q->lock);
+
+ /* wait */
+ while((*_rendezvousp)((ulong)mp, 1) == ~0)
+ ;
+ mp->inuse = 0;
+}
+
+void
+qunlock(QLock *q)
+{
+ QLp *p;
+
+ lock(&q->lock);
+ p = q->head;
+ if(p != nil){
+ /* wakeup head waiting process */
+ q->head = p->next;
+ if(q->head == nil)
+ q->tail = nil;
+ unlock(&q->lock);
+ while((*_rendezvousp)((ulong)p, 0x12345) == ~0)
+ ;
+ return;
+ }
+ q->locked = 0;
+ unlock(&q->lock);
+}
+
+int
+canqlock(QLock *q)
+{
+ if(!canlock(&q->lock))
+ return 0;
+ if(!q->locked){
+ q->locked = 1;
+ unlock(&q->lock);
+ return 1;
+ }
+ unlock(&q->lock);
+ return 0;
+}
+
+void
+rlock(RWLock *q)
+{
+ QLp *p, *mp;
+
+ lock(&q->lock);
+ if(q->writer == 0 && q->head == nil){
+ /* no writer, go for it */
+ q->readers++;
+ unlock(&q->lock);
+ return;
+ }
+
+ mp = getqlp();
+ p = q->tail;
+ if(p == 0)
+ q->head = mp;
+ else
+ p->next = mp;
+ q->tail = mp;
+ mp->next = nil;
+ mp->state = QueuingR;
+ unlock(&q->lock);
+
+ /* wait in kernel */
+ while((*_rendezvousp)((ulong)mp, 1) == ~0)
+ ;
+ mp->inuse = 0;
+}
+
+int
+canrlock(RWLock *q)
+{
+ lock(&q->lock);
+ if (q->writer == 0 && q->head == nil) {
+ /* no writer; go for it */
+ q->readers++;
+ unlock(&q->lock);
+ return 1;
+ }
+ unlock(&q->lock);
+ return 0;
+}
+
+void
+runlock(RWLock *q)
+{
+ QLp *p;
+
+ lock(&q->lock);
+ if(q->readers <= 0)
+ abort();
+ p = q->head;
+ if(--(q->readers) > 0 || p == nil){
+ unlock(&q->lock);
+ return;
+ }
+
+ /* start waiting writer */
+ if(p->state != QueuingW)
+ abort();
+ q->head = p->next;
+ if(q->head == 0)
+ q->tail = 0;
+ q->writer = 1;
+ unlock(&q->lock);
+
+ /* wakeup waiter */
+ while((*_rendezvousp)((ulong)p, 0) == ~0)
+ ;
+}
+
+void
+wlock(RWLock *q)
+{
+ QLp *p, *mp;
+
+ lock(&q->lock);
+ if(q->readers == 0 && q->writer == 0){
+ /* noone waiting, go for it */
+ q->writer = 1;
+ unlock(&q->lock);
+ return;
+ }
+
+ /* wait */
+ p = q->tail;
+ mp = getqlp();
+ if(p == nil)
+ q->head = mp;
+ else
+ p->next = mp;
+ q->tail = mp;
+ mp->next = nil;
+ mp->state = QueuingW;
+ unlock(&q->lock);
+
+ /* wait in kernel */
+ while((*_rendezvousp)((ulong)mp, 1) == ~0)
+ ;
+ mp->inuse = 0;
+}
+
+int
+canwlock(RWLock *q)
+{
+ lock(&q->lock);
+ if (q->readers == 0 && q->writer == 0) {
+ /* no one waiting; go for it */
+ q->writer = 1;
+ unlock(&q->lock);
+ return 1;
+ }
+ unlock(&q->lock);
+ return 0;
+}
+
+void
+wunlock(RWLock *q)
+{
+ QLp *p;
+
+ lock(&q->lock);
+ if(q->writer == 0)
+ abort();
+ p = q->head;
+ if(p == nil){
+ q->writer = 0;
+ unlock(&q->lock);
+ return;
+ }
+ if(p->state == QueuingW){
+ /* start waiting writer */
+ q->head = p->next;
+ if(q->head == nil)
+ q->tail = nil;
+ unlock(&q->lock);
+ while((*_rendezvousp)((ulong)p, 0) == ~0)
+ ;
+ return;
+ }
+
+ if(p->state != QueuingR)
+ abort();
+
+ /* wake waiting readers */
+ while(q->head != nil && q->head->state == QueuingR){
+ p = q->head;
+ q->head = p->next;
+ q->readers++;
+ while((*_rendezvousp)((ulong)p, 0) == ~0)
+ ;
+ }
+ if(q->head == nil)
+ q->tail = nil;
+ q->writer = 0;
+ unlock(&q->lock);
+}
+
+void
+rsleep(Rendez *r)
+{
+ QLp *t, *me;
+
+ if(!r->l)
+ abort();
+ lock(&r->l->lock);
+ /* we should hold the qlock */
+ if(!r->l->locked)
+ abort();
+
+ /* add ourselves to the wait list */
+ me = getqlp();
+ me->state = Sleeping;
+ if(r->head == nil)
+ r->head = me;
+ else
+ r->tail->next = me;
+ me->next = nil;
+ r->tail = me;
+
+ /* pass the qlock to the next guy */
+ t = r->l->head;
+ if(t){
+ r->l->head = t->next;
+ if(r->l->head == nil)
+ r->l->tail = nil;
+ unlock(&r->l->lock);
+ while((*_rendezvousp)((ulong)t, 0x12345) == ~0)
+ ;
+ }else{
+ r->l->locked = 0;
+ unlock(&r->l->lock);
+ }
+
+ /* wait for a wakeup */
+ while((*_rendezvousp)((ulong)me, 0x23456) == ~0)
+ ;
+ me->inuse = 0;
+ if(!r->l->locked)
+ abort();
+}
+
+int
+rwakeup(Rendez *r)
+{
+ QLp *t;
+
+ /*
+ * take off wait and put on front of queue
+ * put on front so guys that have been waiting will not get starved
+ */
+
+ if(!r->l)
+ abort();
+ lock(&r->l->lock);
+ if(!r->l->locked)
+ abort();
+
+ t = r->head;
+ if(t == nil){
+ unlock(&r->l->lock);
+ return 0;
+ }
+
+ r->head = t->next;
+ if(r->head == nil)
+ r->tail = nil;
+
+ t->next = r->l->head;
+ r->l->head = t;
+ if(r->l->tail == nil)
+ r->l->tail = t;
+
+ t->state = Queuing;
+ unlock(&r->l->lock);
+ return 1;
+}
+
+int
+rwakeupall(Rendez *r)
+{
+ int i;
+
+ for(i=0; rwakeup(r); i++)
+ ;
+ return i;
+}
diff --git a/src/lib9/rand.c b/src/lib9/rand.c
new file mode 100644
index 00000000..34f77eca
--- /dev/null
+++ b/src/lib9/rand.c
@@ -0,0 +1,89 @@
+#include <lib9.h>
+
+/*
+ * algorithm by
+ * D. P. Mitchell & J. A. Reeds
+ */
+
+#define LEN 607
+#define TAP 273
+#define MASK 0x7fffffffL
+#define A 48271
+#define M 2147483647
+#define Q 44488
+#define R 3399
+#define NORM (1.0/(1.0+MASK))
+
+static ulong rng_vec[LEN];
+static ulong* rng_tap = rng_vec;
+static ulong* rng_feed = 0;
+static Lock lk;
+
+static void
+isrand(long seed)
+{
+ long lo, hi, x;
+ int i;
+
+ rng_tap = rng_vec;
+ rng_feed = rng_vec+LEN-TAP;
+ seed = seed%M;
+ if(seed < 0)
+ seed += M;
+ if(seed == 0)
+ seed = 89482311;
+ x = seed;
+ /*
+ * Initialize by x[n+1] = 48271 * x[n] mod (2**31 - 1)
+ */
+ for(i = -20; i < LEN; i++) {
+ hi = x / Q;
+ lo = x % Q;
+ x = A*lo - R*hi;
+ if(x < 0)
+ x += M;
+ if(i >= 0)
+ rng_vec[i] = x;
+ }
+}
+
+void
+srand(long seed)
+{
+ lock(&lk);
+ isrand(seed);
+ unlock(&lk);
+}
+
+long
+lrand(void)
+{
+ ulong x;
+
+ lock(&lk);
+
+ rng_tap--;
+ if(rng_tap < rng_vec) {
+ if(rng_feed == 0) {
+ isrand(1);
+ rng_tap--;
+ }
+ rng_tap += LEN;
+ }
+ rng_feed--;
+ if(rng_feed < rng_vec)
+ rng_feed += LEN;
+ x = (*rng_feed + *rng_tap) & MASK;
+ *rng_feed = x;
+
+ unlock(&lk);
+
+ return x;
+}
+
+int
+rand(void)
+{
+ return lrand() & 0x7fff;
+}
+
diff --git a/src/lib9/readn.c b/src/lib9/readn.c
new file mode 100644
index 00000000..e7b9d138
--- /dev/null
+++ b/src/lib9/readn.c
@@ -0,0 +1,21 @@
+#include <lib9.h>
+
+long
+readn(int f, void *av, long n)
+{
+ char *a;
+ long m, t;
+
+ a = av;
+ t = 0;
+ while(t < n){
+ m = read(f, a+t, n-t);
+ if(m <= 0){
+ if(t == 0)
+ return m;
+ break;
+ }
+ t += m;
+ }
+ return t;
+}
diff --git a/src/lib9/rendez.c b/src/lib9/rendez.c
new file mode 100644
index 00000000..320bd11a
--- /dev/null
+++ b/src/lib9/rendez.c
@@ -0,0 +1,180 @@
+/*
+ NAME
+ rendezvous - user level process synchronization
+
+ SYNOPSIS
+ ulong rendezvous(ulong tag, ulong value)
+
+ DESCRIPTION
+ The rendezvous system call allows two processes to synchro-
+ nize and exchange a value. In conjunction with the shared
+ memory system calls (see segattach(2) and fork(2)), it
+ enables parallel programs to control their scheduling.
+
+ Two processes wishing to synchronize call rendezvous with a
+ common tag, typically an address in memory they share. One
+ process will arrive at the rendezvous first; it suspends
+ execution until a second arrives. When a second process
+ meets the rendezvous the value arguments are exchanged
+ between the processes and returned as the result of the
+ respective rendezvous system calls. Both processes are
+ awakened when the rendezvous succeeds.
+
+ The set of tag values which two processes may use to
+ rendezvous-their tag space-is inherited when a process
+ forks, unless RFREND is set in the argument to rfork; see
+ fork(2).
+
+ If a rendezvous is interrupted the return value is ~0, so
+ that value should not be used in normal communication.
+
+ * This simulates rendezvous with shared memory, pause, and SIGUSR1.
+ */
+
+#include <signal.h>
+#include <lib9.h>
+
+enum
+{
+ VOUSHASH = 257,
+};
+
+typedef struct Vous Vous;
+struct Vous
+{
+ Vous *link;
+ Lock lk;
+ int pid;
+ ulong val;
+ ulong tag;
+};
+
+static void
+ign(int x)
+{
+ USED(x);
+}
+
+void /*__attribute__((constructor))*/
+ignusr1(void)
+{
+ signal(SIGUSR1, ign);
+}
+
+static Vous vouspool[2048];
+static int nvousused;
+static Vous *vousfree;
+static Vous *voushash[VOUSHASH];
+static Lock vouslock;
+
+static Vous*
+getvous(void)
+{
+ Vous *v;
+
+ if(vousfree){
+ v = vousfree;
+ vousfree = v->link;
+ }else if(nvousused < nelem(vouspool))
+ v = &vouspool[nvousused++];
+ else
+ abort();
+ return v;
+}
+
+static void
+putvous(Vous *v)
+{
+ lock(&vouslock);
+ v->link = vousfree;
+ vousfree = v;
+ unlock(&vouslock);
+}
+
+static Vous*
+findvous(ulong tag, ulong val, int pid)
+{
+ int h;
+ Vous *v, **l;
+
+ lock(&vouslock);
+ h = tag%VOUSHASH;
+ for(l=&voushash[h], v=*l; v; l=&(*l)->link, v=*l){
+ if(v->tag == tag){
+ *l = v->link;
+ unlock(&vouslock);
+ return v;
+ }
+ }
+ v = getvous();
+ v->pid = pid;
+ v->link = voushash[h];
+ v->val = val;
+ v->tag = tag;
+ lock(&v->lk);
+ voushash[h] = v;
+ unlock(&vouslock);
+ return v;
+}
+
+#define DBG 0
+ulong
+rendezvous(ulong tag, ulong val)
+{
+ int me, vpid;
+ ulong rval;
+ Vous *v;
+ sigset_t mask;
+
+ me = getpid();
+ v = findvous(tag, val, me);
+ if(v->pid == me){
+ if(DBG)fprint(2, "pid is %d tag %lux, sleeping\n", me, tag);
+ /*
+ * No rendezvous partner was found; the next guy
+ * through will find v and wake us, so we must go
+ * to sleep.
+ *
+ * To go to sleep:
+ * 1. disable USR1 signals.
+ * 2. unlock v->lk (tells waker okay to signal us).
+ * 3. atomically suspend and enable USR1 signals.
+ *
+ * The call to ignusr1() could be done once at
+ * process creation instead of every time through rendezvous.
+ */
+ v->val = val;
+ ignusr1();
+ sigprocmask(SIG_SETMASK, NULL, &mask);
+ sigaddset(&mask, SIGUSR1);
+ sigprocmask(SIG_SETMASK, &mask, NULL);
+ sigdelset(&mask, SIGUSR1);
+ unlock(&v->lk);
+ sigsuspend(&mask);
+ rval = v->val;
+ if(DBG)fprint(2, "pid is %d, awake\n", me);
+ putvous(v);
+ }else{
+ /*
+ * Found someone to meet. Wake him:
+ *
+ * A. lock v->lk (waits for him to get to his step 2)
+ * B. send a USR1
+ *
+ * He won't get the USR1 until he suspends, which
+ * means it must wake him up (it can't get delivered
+ * before he sleeps).
+ */
+ vpid = v->pid;
+ lock(&v->lk);
+ rval = v->val;
+ v->val = val;
+ unlock(&v->lk);
+ if(kill(vpid, SIGUSR1) < 0){
+ if(DBG)fprint(2, "pid is %d, kill %d failed: %r\n", me, vpid);
+ abort();
+ }
+ }
+ return rval;
+}
+
diff --git a/src/lib9/strecpy.c b/src/lib9/strecpy.c
new file mode 100644
index 00000000..7d2f2277
--- /dev/null
+++ b/src/lib9/strecpy.c
@@ -0,0 +1,16 @@
+#include <lib9.h>
+
+char*
+strecpy(char *to, char *e, char *from)
+{
+ if(to >= e)
+ return to;
+ to = memccpy(to, from, '\0', e - to);
+ if(to == nil){
+ to = e - 1;
+ *to = '\0';
+ }else{
+ to--;
+ }
+ return to;
+}
diff --git a/src/lib9/sysfatal.c b/src/lib9/sysfatal.c
new file mode 100644
index 00000000..f9ab6985
--- /dev/null
+++ b/src/lib9/sysfatal.c
@@ -0,0 +1,20 @@
+#include <lib9.h>
+
+void (*_sysfatal)(char*, ...);
+
+void
+sysfatal(char *fmt, ...)
+{
+ char buf[256];
+ va_list arg;
+
+ va_start(arg, fmt);
+ if(_sysfatal)
+ (*_sysfatal)(fmt, arg);
+ vseprint(buf, buf+sizeof buf, fmt, arg);
+ va_end(arg);
+
+ fprint(2, "%s; %s\n", argv0 ? argv0 : "<prog>", buf);
+ exits("fatal");
+}
+
diff --git a/src/lib9/tas-386.s b/src/lib9/tas-386.s
new file mode 100644
index 00000000..7a62d2d3
--- /dev/null
+++ b/src/lib9/tas-386.s
@@ -0,0 +1,6 @@
+.globl _tas
+_tas:
+ movl $0xCAFEBABE, %eax
+ movl 4(%esp), %ecx
+ xchgl %eax, 0(%ecx)
+ ret
diff --git a/src/lib9/test.c b/src/lib9/test.c
new file mode 100644
index 00000000..3a358c6c
--- /dev/null
+++ b/src/lib9/test.c
@@ -0,0 +1,8 @@
+#include <lib9.h>
+
+int
+main(int argc, char **argv)
+{
+ werrstr("hello world");
+ print("%r\n");
+}
diff --git a/src/lib9/testfork.c b/src/lib9/testfork.c
new file mode 100644
index 00000000..a5e63718
--- /dev/null
+++ b/src/lib9/testfork.c
@@ -0,0 +1,21 @@
+#include <lib9.h>
+
+void
+sayhi(void *v)
+{
+ USED(v);
+
+ print("hello from subproc\n");
+ print("rendez got %lu from main\n", rendezvous(0x1234, 1234));
+ exits(0);
+}
+
+int
+main(int argc, char **argv)
+{
+ print("hello from main\n");
+ ffork(RFMEM|RFPROC, sayhi, nil);
+
+ print("rendez got %lu from subproc\n", rendezvous(0x1234, 0));
+ exits(0);
+}
diff --git a/src/lib9/tokenize.c b/src/lib9/tokenize.c
new file mode 100644
index 00000000..6fa9fc73
--- /dev/null
+++ b/src/lib9/tokenize.c
@@ -0,0 +1,106 @@
+#include <lib9.h>
+
+static char qsep[] = " \t\r\n";
+
+static char*
+qtoken(char *s, char *sep)
+{
+ int quoting;
+ char *t;
+
+ quoting = 0;
+ t = s; /* s is output string, t is input string */
+ while(*t!='\0' && (quoting || utfrune(sep, *t)==nil)){
+ if(*t != '\''){
+ *s++ = *t++;
+ continue;
+ }
+ /* *t is a quote */
+ if(!quoting){
+ quoting = 1;
+ t++;
+ continue;
+ }
+ /* quoting and we're on a quote */
+ if(t[1] != '\''){
+ /* end of quoted section; absorb closing quote */
+ t++;
+ quoting = 0;
+ continue;
+ }
+ /* doubled quote; fold one quote into two */
+ t++;
+ *s++ = *t++;
+ }
+ if(*s != '\0'){
+ *s = '\0';
+ if(t == s)
+ t++;
+ }
+ return t;
+}
+
+static char*
+etoken(char *t, char *sep)
+{
+ int quoting;
+
+ /* move to end of next token */
+ quoting = 0;
+ while(*t!='\0' && (quoting || utfrune(sep, *t)==nil)){
+ if(*t != '\''){
+ t++;
+ continue;
+ }
+ /* *t is a quote */
+ if(!quoting){
+ quoting = 1;
+ t++;
+ continue;
+ }
+ /* quoting and we're on a quote */
+ if(t[1] != '\''){
+ /* end of quoted section; absorb closing quote */
+ t++;
+ quoting = 0;
+ continue;
+ }
+ /* doubled quote; fold one quote into two */
+ t += 2;
+ }
+ return t;
+}
+
+int
+gettokens(char *s, char **args, int maxargs, char *sep)
+{
+ int nargs;
+
+ for(nargs=0; nargs<maxargs; nargs++){
+ while(*s!='\0' && utfrune(sep, *s)!=nil)
+ *s++ = '\0';
+ if(*s == '\0')
+ break;
+ args[nargs] = s;
+ s = etoken(s, sep);
+ }
+
+ return nargs;
+}
+
+int
+tokenize(char *s, char **args, int maxargs)
+{
+ int nargs;
+
+ for(nargs=0; nargs<maxargs; nargs++){
+ while(*s!='\0' && utfrune(qsep, *s)!=nil)
+ s++;
+ if(*s == '\0')
+ break;
+ args[nargs] = s;
+ s = qtoken(s, qsep);
+ }
+
+ return nargs;
+}
diff --git a/src/lib9/u16.c b/src/lib9/u16.c
new file mode 100644
index 00000000..d9f41e46
--- /dev/null
+++ b/src/lib9/u16.c
@@ -0,0 +1,52 @@
+#include <lib9.h>
+static char t16e[] = "0123456789ABCDEF";
+
+int
+dec16(uchar *out, int lim, char *in, int n)
+{
+ int c, w = 0, i = 0;
+ uchar *start = out;
+ uchar *eout = out + lim;
+
+ while(n-- > 0){
+ c = *in++;
+ if('0' <= c && c <= '9')
+ c = c - '0';
+ else if('a' <= c && c <= 'z')
+ c = c - 'a' + 10;
+ else if('A' <= c && c <= 'Z')
+ c = c - 'A' + 10;
+ else
+ continue;
+ w = (w<<4) + c;
+ i++;
+ if(i == 2){
+ if(out + 1 > eout)
+ goto exhausted;
+ *out++ = w;
+ w = 0;
+ i = 0;
+ }
+ }
+exhausted:
+ return out - start;
+}
+
+int
+enc16(char *out, int lim, uchar *in, int n)
+{
+ uint c;
+ char *eout = out + lim;
+ char *start = out;
+
+ while(n-- > 0){
+ c = *in++;
+ if(out + 2 >= eout)
+ goto exhausted;
+ *out++ = t16e[c>>4];
+ *out++ = t16e[c&0xf];
+ }
+exhausted:
+ *out = 0;
+ return out - start;
+}
diff --git a/src/lib9/u32.c b/src/lib9/u32.c
new file mode 100644
index 00000000..1eb0c6e0
--- /dev/null
+++ b/src/lib9/u32.c
@@ -0,0 +1,109 @@
+#include <lib9.h>
+
+int
+dec32(uchar *dest, int ndest, char *src, int nsrc)
+{
+ char *s, *tab;
+ uchar *start;
+ int i, u[8];
+
+ if(ndest+1 < (5*nsrc+7)/8)
+ return -1;
+ start = dest;
+ tab = "23456789abcdefghijkmnpqrstuvwxyz";
+ while(nsrc>=8){
+ for(i=0; i<8; i++){
+ s = strchr(tab,(int)src[i]);
+ u[i] = s ? s-tab : 0;
+ }
+ *dest++ = (u[0]<<3) | (0x7 & (u[1]>>2));
+ *dest++ = ((0x3 & u[1])<<6) | (u[2]<<1) | (0x1 & (u[3]>>4));
+ *dest++ = ((0xf & u[3])<<4) | (0xf & (u[4]>>1));
+ *dest++ = ((0x1 & u[4])<<7) | (u[5]<<2) | (0x3 & (u[6]>>3));
+ *dest++ = ((0x7 & u[6])<<5) | u[7];
+ src += 8;
+ nsrc -= 8;
+ }
+ if(nsrc > 0){
+ if(nsrc == 1 || nsrc == 3 || nsrc == 6)
+ return -1;
+ for(i=0; i<nsrc; i++){
+ s = strchr(tab,(int)src[i]);
+ u[i] = s ? s-tab : 0;
+ }
+ *dest++ = (u[0]<<3) | (0x7 & (u[1]>>2));
+ if(nsrc == 2)
+ goto out;
+ *dest++ = ((0x3 & u[1])<<6) | (u[2]<<1) | (0x1 & (u[3]>>4));
+ if(nsrc == 4)
+ goto out;
+ *dest++ = ((0xf & u[3])<<4) | (0xf & (u[4]>>1));
+ if(nsrc == 5)
+ goto out;
+ *dest++ = ((0x1 & u[4])<<7) | (u[5]<<2) | (0x3 & (u[6]>>3));
+ }
+out:
+ return dest-start;
+}
+
+int
+enc32(char *dest, int ndest, uchar *src, int nsrc)
+{
+ char *tab, *start;
+ int j;
+
+ if(ndest <= (8*nsrc+4)/5 )
+ return -1;
+ start = dest;
+ tab = "23456789abcdefghijkmnpqrstuvwxyz";
+ while(nsrc>=5){
+ j = (0x1f & (src[0]>>3));
+ *dest++ = tab[j];
+ j = (0x1c & (src[0]<<2)) | (0x03 & (src[1]>>6));
+ *dest++ = tab[j];
+ j = (0x1f & (src[1]>>1));
+ *dest++ = tab[j];
+ j = (0x10 & (src[1]<<4)) | (0x0f & (src[2]>>4));
+ *dest++ = tab[j];
+ j = (0x1e & (src[2]<<1)) | (0x01 & (src[3]>>7));
+ *dest++ = tab[j];
+ j = (0x1f & (src[3]>>2));
+ *dest++ = tab[j];
+ j = (0x18 & (src[3]<<3)) | (0x07 & (src[4]>>5));
+ *dest++ = tab[j];
+ j = (0x1f & (src[4]));
+ *dest++ = tab[j];
+ src += 5;
+ nsrc -= 5;
+ }
+ if(nsrc){
+ j = (0x1f & (src[0]>>3));
+ *dest++ = tab[j];
+ j = (0x1c & (src[0]<<2));
+ if(nsrc == 1)
+ goto out;
+ j |= (0x03 & (src[1]>>6));
+ *dest++ = tab[j];
+ j = (0x1f & (src[1]>>1));
+ if(nsrc == 2)
+ goto out;
+ *dest++ = tab[j];
+ j = (0x10 & (src[1]<<4));
+ if(nsrc == 3)
+ goto out;
+ j |= (0x0f & (src[2]>>4));
+ *dest++ = tab[j];
+ j = (0x1e & (src[2]<<1));
+ if(nsrc == 4)
+ goto out;
+ j |= (0x01 & (src[3]>>7));
+ *dest++ = tab[j];
+ j = (0x1f & (src[3]>>2));
+ *dest++ = tab[j];
+ j = (0x18 & (src[3]<<3));
+out:
+ *dest++ = tab[j];
+ }
+ *dest = 0;
+ return dest-start;
+}
diff --git a/src/lib9/u64.c b/src/lib9/u64.c
new file mode 100644
index 00000000..a17bdf1d
--- /dev/null
+++ b/src/lib9/u64.c
@@ -0,0 +1,126 @@
+#include <lib9.h>
+
+enum {
+ INVAL= 255
+};
+
+static uchar t64d[256] = {
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL, 62,INVAL,INVAL,INVAL, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,
+ INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL,INVAL
+};
+static char t64e[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+int
+dec64(uchar *out, int lim, char *in, int n)
+{
+ ulong b24;
+ uchar *start = out;
+ uchar *e = out + lim;
+ int i, c;
+
+ b24 = 0;
+ i = 0;
+ while(n-- > 0){
+
+ c = t64d[*(uchar*)in++];
+ if(c == INVAL)
+ continue;
+ switch(i){
+ case 0:
+ b24 = c<<18;
+ break;
+ case 1:
+ b24 |= c<<12;
+ break;
+ case 2:
+ b24 |= c<<6;
+ break;
+ case 3:
+ if(out + 3 > e)
+ goto exhausted;
+
+ b24 |= c;
+ *out++ = b24>>16;
+ *out++ = b24>>8;
+ *out++ = b24;
+ i = -1;
+ break;
+ }
+ i++;
+ }
+ switch(i){
+ case 2:
+ if(out + 1 > e)
+ goto exhausted;
+ *out++ = b24>>16;
+ break;
+ case 3:
+ if(out + 2 > e)
+ goto exhausted;
+ *out++ = b24>>16;
+ *out++ = b24>>8;
+ break;
+ }
+exhausted:
+ return out - start;
+}
+
+int
+enc64(char *out, int lim, uchar *in, int n)
+{
+ int i;
+ ulong b24;
+ char *start = out;
+ char *e = out + lim;
+
+ for(i = n/3; i > 0; i--){
+ b24 = (*in++)<<16;
+ b24 |= (*in++)<<8;
+ b24 |= *in++;
+ if(out + 4 >= e)
+ goto exhausted;
+ *out++ = t64e[(b24>>18)];
+ *out++ = t64e[(b24>>12)&0x3f];
+ *out++ = t64e[(b24>>6)&0x3f];
+ *out++ = t64e[(b24)&0x3f];
+ }
+
+ switch(n%3){
+ case 2:
+ b24 = (*in++)<<16;
+ b24 |= (*in)<<8;
+ if(out + 4 >= e)
+ goto exhausted;
+ *out++ = t64e[(b24>>18)];
+ *out++ = t64e[(b24>>12)&0x3f];
+ *out++ = t64e[(b24>>6)&0x3f];
+ *out++ = '=';
+ break;
+ case 1:
+ b24 = (*in)<<16;
+ if(out + 4 >= e)
+ goto exhausted;
+ *out++ = t64e[(b24>>18)];
+ *out++ = t64e[(b24>>12)&0x3f];
+ *out++ = '=';
+ *out++ = '=';
+ break;
+ }
+exhausted:
+ *out = 0;
+ return out - start;
+}
diff --git a/src/lib9/wait.c b/src/lib9/wait.c
new file mode 100644
index 00000000..14af7156
--- /dev/null
+++ b/src/lib9/wait.c
@@ -0,0 +1,30 @@
+#include <lib9.h>
+
+Waitmsg*
+wait(void)
+{
+ int n, l;
+ char buf[512], *fld[5];
+ Waitmsg *w;
+
+ n = await(buf, sizeof buf-1);
+ if(n < 0)
+ return nil;
+ buf[n] = '\0';
+ if(tokenize(buf, fld, nelem(fld)) != nelem(fld)){
+ werrstr("couldn't parse wait message");
+ return nil;
+ }
+ l = strlen(fld[4])+1;
+ w = malloc(sizeof(Waitmsg)+l);
+ if(w == nil)
+ return nil;
+ w->pid = atoi(fld[0]);
+ w->time[0] = atoi(fld[1]);
+ w->time[1] = atoi(fld[2]);
+ w->time[2] = atoi(fld[3]);
+ w->msg = (char*)&w[1];
+ memmove(w->msg, fld[4], l);
+ return w;
+}
+
diff --git a/src/lib9/werrstr.c b/src/lib9/werrstr.c
new file mode 100644
index 00000000..7fa1f2ea
--- /dev/null
+++ b/src/lib9/werrstr.c
@@ -0,0 +1,13 @@
+#include <lib9.h>
+
+void
+werrstr(char *fmt, ...)
+{
+ va_list arg;
+ char buf[ERRMAX];
+
+ va_start(arg, fmt);
+ vseprint(buf, buf+ERRMAX, fmt, arg);
+ va_end(arg);
+ errstr(buf, ERRMAX);
+}