summaryrefslogtreecommitdiff
path: root/lib9
diff options
context:
space:
mode:
Diffstat (limited to 'lib9')
-rw-r--r--lib9/NOTICE29
-rw-r--r--lib9/argv0.c1
-rw-r--r--lib9/charstod.c68
-rw-r--r--lib9/cistrcmp.c25
-rw-r--r--lib9/cistrncmp.c27
-rw-r--r--lib9/cistrstr.c22
-rw-r--r--lib9/cleanname.c51
-rw-r--r--lib9/convD2M.c94
-rw-r--r--lib9/convM2D.c93
-rw-r--r--lib9/convM2S.c314
-rw-r--r--lib9/convS2M.c385
-rw-r--r--lib9/create.c32
-rw-r--r--lib9/dirstat-Nt.c64
-rw-r--r--lib9/dirstat-posix.c79
-rw-r--r--lib9/dirwstat.c15
-rw-r--r--lib9/dofmt.c532
-rw-r--r--lib9/dorfmt.c58
-rw-r--r--lib9/errfmt.c24
-rw-r--r--lib9/errstr-Nt.c65
-rw-r--r--lib9/errstr-Plan9.c8
-rw-r--r--lib9/errstr-posix.c37
-rw-r--r--lib9/exits.c17
-rw-r--r--lib9/fcallfmt.c246
-rw-r--r--lib9/fltfmt.c324
-rw-r--r--lib9/fmt.c188
-rw-r--r--lib9/fmtdef.h98
-rw-r--r--lib9/fmtfd.c43
-rw-r--r--lib9/fmtlock.c24
-rw-r--r--lib9/fmtprint.c44
-rw-r--r--lib9/fmtquote.c259
-rw-r--r--lib9/fmtrune.c37
-rw-r--r--lib9/fmtstr.c23
-rw-r--r--lib9/fmtvprint.c43
-rw-r--r--lib9/fprint.c26
-rw-r--r--lib9/getcallerpc-FreeBSD-386.S7
-rw-r--r--lib9/getcallerpc-Hp-s800.s12
-rw-r--r--lib9/getcallerpc-Irix-mips.s9
-rw-r--r--lib9/getcallerpc-Linux-386.S7
-rw-r--r--lib9/getcallerpc-MacOSX-power.s19
-rw-r--r--lib9/getcallerpc-Solaris-386.s8
-rw-r--r--lib9/getcallerpc-Solaris-sparc.s11
-rw-r--r--lib9/getcallerpc-Unixware-386.s8
-rw-r--r--lib9/getfields.c35
-rw-r--r--lib9/getuser-Nt.c9
-rw-r--r--lib9/getuser-posix.c22
-rw-r--r--lib9/getwd-Nt.c24
-rw-r--r--lib9/getwd-posix.c9
-rw-r--r--lib9/lock-Hp-s800.s38
-rw-r--r--lib9/lock-Irix-mips.s19
-rw-r--r--lib9/lock-MacOSX-power.s16
-rw-r--r--lib9/lock-Nt-386.c15
-rw-r--r--lib9/lock-Solaris-386.s56
-rw-r--r--lib9/lock-Solaris-sparc.s20
-rw-r--r--lib9/lock-Unixware-386.s56
-rw-r--r--lib9/lock.c141
-rw-r--r--lib9/mkfile85
-rw-r--r--lib9/mkfile-Nt8
-rw-r--r--lib9/mkfile-Plan92
-rw-r--r--lib9/mkfile-Posix8
-rw-r--r--lib9/nulldir.c8
-rw-r--r--lib9/pow10.c44
-rw-r--r--lib9/print.c26
-rw-r--r--lib9/qsort.c122
-rw-r--r--lib9/readn.c21
-rw-r--r--lib9/rerrstr.c12
-rw-r--r--lib9/rune.c162
-rw-r--r--lib9/runestrlen.c13
-rw-r--r--lib9/sbrk-posix.c48
-rw-r--r--lib9/seek.c9
-rw-r--r--lib9/seprint.c26
-rw-r--r--lib9/setbinmode-Nt.c9
-rw-r--r--lib9/smprint.c26
-rw-r--r--lib9/snprint.c27
-rw-r--r--lib9/sprint.c35
-rw-r--r--lib9/strdup.c12
-rw-r--r--lib9/strecpy.c16
-rw-r--r--lib9/strtoll.c81
-rw-r--r--lib9/sysfatal.c28
-rw-r--r--lib9/tokenize.c58
-rw-r--r--lib9/u16.c52
-rw-r--r--lib9/u32.c109
-rw-r--r--lib9/u64.c126
-rw-r--r--lib9/utfecpy.c20
-rw-r--r--lib9/utflen.c22
-rw-r--r--lib9/utfnlen.c25
-rw-r--r--lib9/utfrrune.c30
-rw-r--r--lib9/utfrune.c29
-rw-r--r--lib9/vfprint.c46
-rw-r--r--lib9/vseprint.c35
-rw-r--r--lib9/vsmprint.c74
-rw-r--r--lib9/vsnprint.c34
91 files changed, 5424 insertions, 0 deletions
diff --git a/lib9/NOTICE b/lib9/NOTICE
new file mode 100644
index 00000000..d2099067
--- /dev/null
+++ b/lib9/NOTICE
@@ -0,0 +1,29 @@
+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 substantial 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 below. It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+ Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
+ Revisions Copyright © 2000-2006 Vita Nuova Holdings Limited (www.vitanuova.com). All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/lib9/argv0.c b/lib9/argv0.c
new file mode 100644
index 00000000..10eb75cf
--- /dev/null
+++ b/lib9/argv0.c
@@ -0,0 +1 @@
+char* argv0 = 0;
diff --git a/lib9/charstod.c b/lib9/charstod.c
new file mode 100644
index 00000000..53140695
--- /dev/null
+++ b/lib9/charstod.c
@@ -0,0 +1,68 @@
+#include "lib9.h"
+
+/*
+ * Reads a floating-point number by interpreting successive characters
+ * returned by (*f)(vp). The last call it makes to f terminates the
+ * scan, so is not a character in the number. It may therefore be
+ * necessary to back up the input stream up one byte after calling charstod.
+ */
+
+double
+charstod(int(*f)(void*), void *vp)
+{
+ double num, dem;
+ int neg, eneg, dig, exp, c;
+
+ num = 0;
+ neg = 0;
+ dig = 0;
+ exp = 0;
+ eneg = 0;
+
+ c = (*f)(vp);
+ while(c == ' ' || c == '\t')
+ c = (*f)(vp);
+ if(c == '-' || c == '+'){
+ if(c == '-')
+ neg = 1;
+ c = (*f)(vp);
+ }
+ while(c >= '0' && c <= '9'){
+ num = num*10 + c-'0';
+ c = (*f)(vp);
+ }
+ if(c == '.')
+ c = (*f)(vp);
+ while(c >= '0' && c <= '9'){
+ num = num*10 + c-'0';
+ dig++;
+ c = (*f)(vp);
+ }
+ if(c == 'e' || c == 'E'){
+ c = (*f)(vp);
+ if(c == '-' || c == '+'){
+ if(c == '-'){
+ dig = -dig;
+ eneg = 1;
+ }
+ c = (*f)(vp);
+ }
+ while(c >= '0' && c <= '9'){
+ exp = exp*10 + c-'0';
+ c = (*f)(vp);
+ }
+ }
+ exp -= dig;
+ if(exp < 0){
+ exp = -exp;
+ eneg = !eneg;
+ }
+ dem = pow10(exp);
+ if(eneg)
+ num /= dem;
+ else
+ num *= dem;
+ if(neg)
+ return -num;
+ return num;
+}
diff --git a/lib9/cistrcmp.c b/lib9/cistrcmp.c
new file mode 100644
index 00000000..fc7d84e4
--- /dev/null
+++ b/lib9/cistrcmp.c
@@ -0,0 +1,25 @@
+#include "lib9.h"
+
+int
+cistrcmp(char *s1, char *s2)
+{
+ int c1, c2;
+
+ while(*s1){
+ c1 = *(uchar*)s1++;
+ c2 = *(uchar*)s2++;
+
+ if(c1 == c2)
+ continue;
+
+ if(c1 >= 'A' && c1 <= 'Z')
+ c1 -= 'A' - 'a';
+
+ if(c2 >= 'A' && c2 <= 'Z')
+ c2 -= 'A' - 'a';
+
+ if(c1 != c2)
+ return c1 - c2;
+ }
+ return -*s2;
+}
diff --git a/lib9/cistrncmp.c b/lib9/cistrncmp.c
new file mode 100644
index 00000000..1d55d71b
--- /dev/null
+++ b/lib9/cistrncmp.c
@@ -0,0 +1,27 @@
+#include "lib9.h"
+
+int
+cistrncmp(char *s1, char *s2, int n)
+{
+ int c1, c2;
+
+ while(*s1 && n-- > 0){
+ c1 = *(uchar*)s1++;
+ c2 = *(uchar*)s2++;
+
+ if(c1 == c2)
+ continue;
+
+ if(c1 >= 'A' && c1 <= 'Z')
+ c1 -= 'A' - 'a';
+
+ if(c2 >= 'A' && c2 <= 'Z')
+ c2 -= 'A' - 'a';
+
+ if(c1 != c2)
+ return c1 - c2;
+ }
+ if(n <= 0)
+ return 0;
+ return -*s2;
+}
diff --git a/lib9/cistrstr.c b/lib9/cistrstr.c
new file mode 100644
index 00000000..20d3205d
--- /dev/null
+++ b/lib9/cistrstr.c
@@ -0,0 +1,22 @@
+#include "lib9.h"
+
+char*
+cistrstr(char *s, char *sub)
+{
+ int c, csub, n;
+
+ csub = *sub;
+ if(csub == '\0')
+ return s;
+ if(csub >= 'A' && csub <= 'Z')
+ csub -= 'A' - 'a';
+ sub++;
+ n = strlen(sub);
+ for(; c = *s; s++){
+ if(c >= 'A' && c <= 'Z')
+ c -= 'A' - 'a';
+ if(c == csub && cistrncmp(s+1, sub, n) == 0)
+ return s;
+ }
+ return nil;
+}
diff --git a/lib9/cleanname.c b/lib9/cleanname.c
new file mode 100644
index 00000000..baf11557
--- /dev/null
+++ b/lib9/cleanname.c
@@ -0,0 +1,51 @@
+#include <lib9.h>
+
+/*
+ * In place, rewrite name to compress multiple /, eliminate ., and process ..
+ */
+#define SEP(x) ((x)=='/' || (x) == 0)
+char*
+cleanname(char *name)
+{
+ char *p, *q, *dotdot;
+ int rooted;
+
+ rooted = name[0] == '/';
+
+ /*
+ * invariants:
+ * p points at beginning of path element we're considering.
+ * q points just past the last path element we wrote (no slash).
+ * dotdot points just past the point where .. cannot backtrack
+ * any further (no slash).
+ */
+ p = q = dotdot = name+rooted;
+ while(*p) {
+ if(p[0] == '/') /* null element */
+ p++;
+ else if(p[0] == '.' && SEP(p[1]))
+ p += 1; /* don't count the separator in case it is nul */
+ else if(p[0] == '.' && p[1] == '.' && SEP(p[2])) {
+ p += 2;
+ if(q > dotdot) { /* can backtrack */
+ while(--q > dotdot && *q != '/')
+ ;
+ } else if(!rooted) { /* /.. is / but ./../ is .. */
+ if(q != name)
+ *q++ = '/';
+ *q++ = '.';
+ *q++ = '.';
+ dotdot = q;
+ }
+ } else { /* real path element */
+ if(q != name+rooted)
+ *q++ = '/';
+ while((*q = *p) != '/' && *q != 0)
+ p++, q++;
+ }
+ }
+ if(q == name) /* empty string is really ``.'' */
+ *q++ = '.';
+ *q = '\0';
+ return name;
+}
diff --git a/lib9/convD2M.c b/lib9/convD2M.c
new file mode 100644
index 00000000..29f0cd55
--- /dev/null
+++ b/lib9/convD2M.c
@@ -0,0 +1,94 @@
+#include "lib9.h"
+#include "fcall.h"
+
+uint
+sizeD2M(Dir *d)
+{
+ char *sv[4];
+ int i, ns;
+
+ sv[0] = d->name;
+ sv[1] = d->uid;
+ sv[2] = d->gid;
+ sv[3] = d->muid;
+
+ ns = 0;
+ for(i = 0; i < 4; i++)
+ if(sv[i])
+ ns += strlen(sv[i]);
+
+ return STATFIXLEN + ns;
+}
+
+uint
+convD2M(Dir *d, uchar *buf, uint nbuf)
+{
+ uchar *p, *ebuf;
+ char *sv[4];
+ int i, ns, nsv[4], ss;
+
+ if(nbuf < BIT16SZ)
+ return 0;
+
+ p = buf;
+ ebuf = buf + nbuf;
+
+ sv[0] = d->name;
+ sv[1] = d->uid;
+ sv[2] = d->gid;
+ sv[3] = d->muid;
+
+ ns = 0;
+ for(i = 0; i < 4; i++){
+ if(sv[i])
+ nsv[i] = strlen(sv[i]);
+ else
+ nsv[i] = 0;
+ ns += nsv[i];
+ }
+
+ ss = STATFIXLEN + ns;
+
+ /* set size befor erroring, so user can know how much is needed */
+ /* note that length excludes count field itself */
+ PBIT16(p, ss-BIT16SZ);
+ p += BIT16SZ;
+
+ if(ss > nbuf)
+ return BIT16SZ;
+
+ PBIT16(p, d->type);
+ p += BIT16SZ;
+ PBIT32(p, d->dev);
+ p += BIT32SZ;
+ PBIT8(p, d->qid.type);
+ p += BIT8SZ;
+ PBIT32(p, d->qid.vers);
+ p += BIT32SZ;
+ PBIT64(p, d->qid.path);
+ p += BIT64SZ;
+ PBIT32(p, d->mode);
+ p += BIT32SZ;
+ PBIT32(p, d->atime);
+ p += BIT32SZ;
+ PBIT32(p, d->mtime);
+ p += BIT32SZ;
+ PBIT64(p, d->length);
+ p += BIT64SZ;
+
+ for(i = 0; i < 4; i++){
+ ns = nsv[i];
+ if(p + ns + BIT16SZ > ebuf)
+ return 0;
+ PBIT16(p, ns);
+ p += BIT16SZ;
+ if(ns)
+ memmove(p, sv[i], ns);
+ p += ns;
+ }
+
+ if(ss != p - buf)
+ return 0;
+
+ return p - buf;
+}
diff --git a/lib9/convM2D.c b/lib9/convM2D.c
new file mode 100644
index 00000000..156bd763
--- /dev/null
+++ b/lib9/convM2D.c
@@ -0,0 +1,93 @@
+#include "lib9.h"
+#include "fcall.h"
+
+int
+statcheck(uchar *buf, uint nbuf)
+{
+ uchar *ebuf;
+ int i;
+
+ ebuf = buf + nbuf;
+
+ if(nbuf < STATFIXLEN || nbuf != BIT16SZ + GBIT16(buf))
+ return -1;
+
+ buf += STATFIXLEN - 4 * BIT16SZ;
+
+ for(i = 0; i < 4; i++){
+ if(buf + BIT16SZ > ebuf)
+ return -1;
+ buf += BIT16SZ + GBIT16(buf);
+ }
+
+ if(buf != ebuf)
+ return -1;
+
+ return 0;
+}
+
+static char nullstring[] = "";
+
+uint
+convM2D(uchar *buf, uint nbuf, Dir *d, char *strs)
+{
+ uchar *p, *ebuf;
+ char *sv[4];
+ int i, ns;
+
+ if(nbuf < STATFIXLEN)
+ return 0;
+
+ p = buf;
+ ebuf = buf + nbuf;
+
+ p += BIT16SZ; /* ignore size */
+ d->type = GBIT16(p);
+ p += BIT16SZ;
+ d->dev = GBIT32(p);
+ p += BIT32SZ;
+ d->qid.type = GBIT8(p);
+ p += BIT8SZ;
+ d->qid.vers = GBIT32(p);
+ p += BIT32SZ;
+ d->qid.path = GBIT64(p);
+ p += BIT64SZ;
+ d->mode = GBIT32(p);
+ p += BIT32SZ;
+ d->atime = GBIT32(p);
+ p += BIT32SZ;
+ d->mtime = GBIT32(p);
+ p += BIT32SZ;
+ d->length = GBIT64(p);
+ p += BIT64SZ;
+
+ for(i = 0; i < 4; i++){
+ if(p + BIT16SZ > ebuf)
+ return 0;
+ ns = GBIT16(p);
+ p += BIT16SZ;
+ if(p + ns > ebuf)
+ return 0;
+ if(strs){
+ sv[i] = strs;
+ memmove(strs, p, ns);
+ strs += ns;
+ *strs++ = '\0';
+ }
+ p += ns;
+ }
+
+ if(strs){
+ d->name = sv[0];
+ d->uid = sv[1];
+ d->gid = sv[2];
+ d->muid = sv[3];
+ }else{
+ d->name = nullstring;
+ d->uid = nullstring;
+ d->gid = nullstring;
+ d->muid = nullstring;
+ }
+
+ return p - buf;
+}
diff --git a/lib9/convM2S.c b/lib9/convM2S.c
new file mode 100644
index 00000000..443d1957
--- /dev/null
+++ b/lib9/convM2S.c
@@ -0,0 +1,314 @@
+#include "lib9.h"
+#include "fcall.h"
+
+static
+uchar*
+gstring(uchar *p, uchar *ep, char **s)
+{
+ uint n;
+
+ if(p+BIT16SZ > ep)
+ return nil;
+ n = GBIT16(p);
+ p += BIT16SZ - 1;
+ if(p+n+1 > ep)
+ return nil;
+ /* move it down, on top of count, to make room for '\0' */
+ memmove(p, p + 1, n);
+ p[n] = '\0';
+ *s = (char*)p;
+ p += n+1;
+ return p;
+}
+
+static
+uchar*
+gqid(uchar *p, uchar *ep, Qid *q)
+{
+ if(p+QIDSZ > ep)
+ return nil;
+ q->type = GBIT8(p);
+ p += BIT8SZ;
+ q->vers = GBIT32(p);
+ p += BIT32SZ;
+ q->path = GBIT64(p);
+ p += BIT64SZ;
+ return p;
+}
+
+/*
+ * no syntactic checks.
+ * three causes for error:
+ * 1. message size field is incorrect
+ * 2. input buffer too short for its own data (counts too long, etc.)
+ * 3. too many names or qids
+ * gqid() and gstring() return nil if they would reach beyond buffer.
+ * main switch statement checks range and also can fall through
+ * to test at end of routine.
+ */
+uint
+convM2S(uchar *ap, uint nap, Fcall *f)
+{
+ uchar *p, *ep;
+ uint i, size;
+
+ p = ap;
+ ep = p + nap;
+
+ if(p+BIT32SZ+BIT8SZ+BIT16SZ > ep)
+ return 0;
+ size = GBIT32(p);
+ p += BIT32SZ;
+
+ if(size < BIT32SZ+BIT8SZ+BIT16SZ)
+ return 0;
+
+ f->type = GBIT8(p);
+ p += BIT8SZ;
+ f->tag = GBIT16(p);
+ p += BIT16SZ;
+
+ switch(f->type)
+ {
+ default:
+ return 0;
+
+ case Tversion:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->msize = GBIT32(p);
+ p += BIT32SZ;
+ p = gstring(p, ep, &f->version);
+ break;
+
+ case Tflush:
+ if(p+BIT16SZ > ep)
+ return 0;
+ f->oldtag = GBIT16(p);
+ p += BIT16SZ;
+ break;
+
+ case Tauth:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->afid = GBIT32(p);
+ p += BIT32SZ;
+ p = gstring(p, ep, &f->uname);
+ if(p == nil)
+ break;
+ p = gstring(p, ep, &f->aname);
+ if(p == nil)
+ break;
+ break;
+
+ case Tattach:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->afid = GBIT32(p);
+ p += BIT32SZ;
+ p = gstring(p, ep, &f->uname);
+ if(p == nil)
+ break;
+ p = gstring(p, ep, &f->aname);
+ if(p == nil)
+ break;
+ break;
+
+ case Twalk:
+ if(p+BIT32SZ+BIT32SZ+BIT16SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ f->newfid = GBIT32(p);
+ p += BIT32SZ;
+ f->nwname = GBIT16(p);
+ p += BIT16SZ;
+ if(f->nwname > MAXWELEM)
+ return 0;
+ for(i=0; i<f->nwname; i++){
+ p = gstring(p, ep, &f->wname[i]);
+ if(p == nil)
+ break;
+ }
+ break;
+
+ case Topen:
+ if(p+BIT32SZ+BIT8SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ f->mode = GBIT8(p);
+ p += BIT8SZ;
+ break;
+
+ case Tcreate:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ p = gstring(p, ep, &f->name);
+ if(p == nil)
+ break;
+ if(p+BIT32SZ+BIT8SZ > ep)
+ return 0;
+ f->perm = GBIT32(p);
+ p += BIT32SZ;
+ f->mode = GBIT8(p);
+ p += BIT8SZ;
+ break;
+
+ case Tread:
+ if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ f->offset = GBIT64(p);
+ p += BIT64SZ;
+ f->count = GBIT32(p);
+ p += BIT32SZ;
+ break;
+
+ case Twrite:
+ if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ f->offset = GBIT64(p);
+ p += BIT64SZ;
+ f->count = GBIT32(p);
+ p += BIT32SZ;
+ if(p+f->count > ep)
+ return 0;
+ f->data = (char*)p;
+ p += f->count;
+ break;
+
+ case Tclunk:
+ case Tremove:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ break;
+
+ case Tstat:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ break;
+
+ case Twstat:
+ if(p+BIT32SZ+BIT16SZ > ep)
+ return 0;
+ f->fid = GBIT32(p);
+ p += BIT32SZ;
+ f->nstat = GBIT16(p);
+ p += BIT16SZ;
+ if(p+f->nstat > ep)
+ return 0;
+ f->stat = p;
+ p += f->nstat;
+ break;
+
+/*
+ */
+ case Rversion:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->msize = GBIT32(p);
+ p += BIT32SZ;
+ p = gstring(p, ep, &f->version);
+ break;
+
+ case Rerror:
+ p = gstring(p, ep, &f->ename);
+ break;
+
+ case Rflush:
+ break;
+
+ case Rauth:
+ p = gqid(p, ep, &f->aqid);
+ if(p == nil)
+ break;
+ break;
+
+ case Rattach:
+ p = gqid(p, ep, &f->qid);
+ if(p == nil)
+ break;
+ break;
+
+ case Rwalk:
+ if(p+BIT16SZ > ep)
+ return 0;
+ f->nwqid = GBIT16(p);
+ p += BIT16SZ;
+ if(f->nwqid > MAXWELEM)
+ return 0;
+ for(i=0; i<f->nwqid; i++){
+ p = gqid(p, ep, &f->wqid[i]);
+ if(p == nil)
+ break;
+ }
+ break;
+
+ case Ropen:
+ case Rcreate:
+ p = gqid(p, ep, &f->qid);
+ if(p == nil)
+ break;
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->iounit = GBIT32(p);
+ p += BIT32SZ;
+ break;
+
+ case Rread:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->count = GBIT32(p);
+ p += BIT32SZ;
+ if(p+f->count > ep)
+ return 0;
+ f->data = (char*)p;
+ p += f->count;
+ break;
+
+ case Rwrite:
+ if(p+BIT32SZ > ep)
+ return 0;
+ f->count = GBIT32(p);
+ p += BIT32SZ;
+ break;
+
+ case Rclunk:
+ case Rremove:
+ break;
+
+ case Rstat:
+ if(p+BIT16SZ > ep)
+ return 0;
+ f->nstat = GBIT16(p);
+ p += BIT16SZ;
+ if(p+f->nstat > ep)
+ return 0;
+ f->stat = p;
+ p += f->nstat;
+ break;
+
+ case Rwstat:
+ break;
+ }
+
+ if(p==nil || p>ep)
+ return 0;
+ if(ap+size == p)
+ return size;
+ return 0;
+}
diff --git a/lib9/convS2M.c b/lib9/convS2M.c
new file mode 100644
index 00000000..7035373f
--- /dev/null
+++ b/lib9/convS2M.c
@@ -0,0 +1,385 @@
+#include "lib9.h"
+#include "fcall.h"
+
+static
+uchar*
+pstring(uchar *p, char *s)
+{
+ uint n;
+
+ if(s == nil){
+ PBIT16(p, 0);
+ p += BIT16SZ;
+ return p;
+ }
+
+ n = strlen(s);
+ PBIT16(p, n);
+ p += BIT16SZ;
+ memmove(p, s, n);
+ p += n;
+ return p;
+}
+
+static
+uchar*
+pqid(uchar *p, Qid *q)
+{
+ PBIT8(p, q->type);
+ p += BIT8SZ;
+ PBIT32(p, q->vers);
+ p += BIT32SZ;
+ PBIT64(p, q->path);
+ p += BIT64SZ;
+ return p;
+}
+
+static
+uint
+stringsz(char *s)
+{
+ if(s == nil)
+ return BIT16SZ;
+
+ return BIT16SZ+strlen(s);
+}
+
+uint
+sizeS2M(Fcall *f)
+{
+ uint n;
+ int i;
+
+ n = 0;
+ n += BIT32SZ; /* size */
+ n += BIT8SZ; /* type */
+ n += BIT16SZ; /* tag */
+
+ switch(f->type)
+ {
+ default:
+ return 0;
+
+ case Tversion:
+ n += BIT32SZ;
+ n += stringsz(f->version);
+ break;
+
+ case Tflush:
+ n += BIT16SZ;
+ break;
+
+ case Tauth:
+ n += BIT32SZ;
+ n += stringsz(f->uname);
+ n += stringsz(f->aname);
+ break;
+
+ case Tattach:
+ n += BIT32SZ;
+ n += BIT32SZ;
+ n += stringsz(f->uname);
+ n += stringsz(f->aname);
+ break;
+
+ case Twalk:
+ n += BIT32SZ;
+ n += BIT32SZ;
+ n += BIT16SZ;
+ for(i=0; i<f->nwname; i++)
+ n += stringsz(f->wname[i]);
+ break;
+
+ case Topen:
+ n += BIT32SZ;
+ n += BIT8SZ;
+ break;
+
+ case Tcreate:
+ n += BIT32SZ;
+ n += stringsz(f->name);
+ n += BIT32SZ;
+ n += BIT8SZ;
+ break;
+
+ case Tread:
+ n += BIT32SZ;
+ n += BIT64SZ;
+ n += BIT32SZ;
+ break;
+
+ case Twrite:
+ n += BIT32SZ;
+ n += BIT64SZ;
+ n += BIT32SZ;
+ n += f->count;
+ break;
+
+ case Tclunk:
+ case Tremove:
+ n += BIT32SZ;
+ break;
+
+ case Tstat:
+ n += BIT32SZ;
+ break;
+
+ case Twstat:
+ n += BIT32SZ;
+ n += BIT16SZ;
+ n += f->nstat;
+ break;
+/*
+ */
+
+ case Rversion:
+ n += BIT32SZ;
+ n += stringsz(f->version);
+ break;
+
+ case Rerror:
+ n += stringsz(f->ename);
+ break;
+
+ case Rflush:
+ break;
+
+ case Rauth:
+ n += QIDSZ;
+ break;
+
+ case Rattach:
+ n += QIDSZ;
+ break;
+
+ case Rwalk:
+ n += BIT16SZ;
+ n += f->nwqid*QIDSZ;
+ break;
+
+ case Ropen:
+ case Rcreate:
+ n += QIDSZ;
+ n += BIT32SZ;
+ break;
+
+ case Rread:
+ n += BIT32SZ;
+ n += f->count;
+ break;
+
+ case Rwrite:
+ n += BIT32SZ;
+ break;
+
+ case Rclunk:
+ break;
+
+ case Rremove:
+ break;
+
+ case Rstat:
+ n += BIT16SZ;
+ n += f->nstat;
+ break;
+
+ case Rwstat:
+ break;
+ }
+ return n;
+}
+
+uint
+convS2M(Fcall *f, uchar *ap, uint nap)
+{
+ uchar *p;
+ uint i, size;
+
+ size = sizeS2M(f);
+ if(size == 0)
+ return 0;
+ if(size > nap)
+ return 0;
+
+ p = (uchar*)ap;
+
+ PBIT32(p, size);
+ p += BIT32SZ;
+ PBIT8(p, f->type);
+ p += BIT8SZ;
+ PBIT16(p, f->tag);
+ p += BIT16SZ;
+
+ switch(f->type)
+ {
+ default:
+ return 0;
+
+ case Tversion:
+ PBIT32(p, f->msize);
+ p += BIT32SZ;
+ p = pstring(p, f->version);
+ break;
+
+ case Tflush:
+ PBIT16(p, f->oldtag);
+ p += BIT16SZ;
+ break;
+
+ case Tauth:
+ PBIT32(p, f->afid);
+ p += BIT32SZ;
+ p = pstring(p, f->uname);
+ p = pstring(p, f->aname);
+ break;
+
+ case Tattach:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT32(p, f->afid);
+ p += BIT32SZ;
+ p = pstring(p, f->uname);
+ p = pstring(p, f->aname);
+ break;
+
+ case Twalk:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT32(p, f->newfid);
+ p += BIT32SZ;
+ PBIT16(p, f->nwname);
+ p += BIT16SZ;
+ if(f->nwname > MAXWELEM)
+ return 0;
+ for(i=0; i<f->nwname; i++)
+ p = pstring(p, f->wname[i]);
+ break;
+
+ case Topen:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT8(p, f->mode);
+ p += BIT8SZ;
+ break;
+
+ case Tcreate:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ p = pstring(p, f->name);
+ PBIT32(p, f->perm);
+ p += BIT32SZ;
+ PBIT8(p, f->mode);
+ p += BIT8SZ;
+ break;
+
+ case Tread:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT64(p, f->offset);
+ p += BIT64SZ;
+ PBIT32(p, f->count);
+ p += BIT32SZ;
+ break;
+
+ case Twrite:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT64(p, f->offset);
+ p += BIT64SZ;
+ PBIT32(p, f->count);
+ p += BIT32SZ;
+ memmove(p, f->data, f->count);
+ p += f->count;
+ break;
+
+ case Tclunk:
+ case Tremove:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ break;
+
+ case Tstat:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ break;
+
+ case Twstat:
+ PBIT32(p, f->fid);
+ p += BIT32SZ;
+ PBIT16(p, f->nstat);
+ p += BIT16SZ;
+ memmove(p, f->stat, f->nstat);
+ p += f->nstat;
+ break;
+/*
+ */
+
+ case Rversion:
+ PBIT32(p, f->msize);
+ p += BIT32SZ;
+ p = pstring(p, f->version);
+ break;
+
+ case Rerror:
+ p = pstring(p, f->ename);
+ break;
+
+ case Rflush:
+ break;
+
+ case Rauth:
+ p = pqid(p, &f->aqid);
+ break;
+
+ case Rattach:
+ p = pqid(p, &f->qid);
+ break;
+
+ case Rwalk:
+ PBIT16(p, f->nwqid);
+ p += BIT16SZ;
+ if(f->nwqid > MAXWELEM)
+ return 0;
+ for(i=0; i<f->nwqid; i++)
+ p = pqid(p, &f->wqid[i]);
+ break;
+
+ case Ropen:
+ case Rcreate:
+ p = pqid(p, &f->qid);
+ PBIT32(p, f->iounit);
+ p += BIT32SZ;
+ break;
+
+ case Rread:
+ PBIT32(p, f->count);
+ p += BIT32SZ;
+ memmove(p, f->data, f->count);
+ p += f->count;
+ break;
+
+ case Rwrite:
+ PBIT32(p, f->count);
+ p += BIT32SZ;
+ break;
+
+ case Rclunk:
+ break;
+
+ case Rremove:
+ break;
+
+ case Rstat:
+ PBIT16(p, f->nstat);
+ p += BIT16SZ;
+ memmove(p, f->stat, f->nstat);
+ p += f->nstat;
+ break;
+
+ case Rwstat:
+ break;
+ }
+ if(size != p-ap)
+ return 0;
+ return size;
+}
diff --git a/lib9/create.c b/lib9/create.c
new file mode 100644
index 00000000..de624f61
--- /dev/null
+++ b/lib9/create.c
@@ -0,0 +1,32 @@
+#include "lib9.h"
+#include <sys/types.h>
+#include <fcntl.h>
+
+int
+create(char *f, int mode, int perm)
+{
+ int m;
+
+ m = 0;
+ switch(mode & 3){
+ case OREAD:
+ case OEXEC:
+ m = O_RDONLY;
+ break;
+ case OWRITE:
+ m = O_WRONLY;
+ break;
+ case ORDWR:
+ m = O_RDWR;
+ break;
+ }
+ m |= O_CREAT|O_TRUNC;
+
+ if(perm & DMDIR){
+ if(mkdir(f, perm&0777) < 0)
+ return -1;
+ perm &= ~DMDIR;
+ m &= 3;
+ }
+ return open(f, m, perm);
+}
diff --git a/lib9/dirstat-Nt.c b/lib9/dirstat-Nt.c
new file mode 100644
index 00000000..84db4bda
--- /dev/null
+++ b/lib9/dirstat-Nt.c
@@ -0,0 +1,64 @@
+#include "lib9.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define ISTYPE(s, t) (((s)->st_mode&S_IFMT) == t)
+
+static char nullstring[] = "";
+static char Enovmem[] = "out of memory";
+
+static Dir*
+statconv(struct stat *s, char *name)
+{
+ Dir *dir;
+
+#ifdef NO
+ extern char* GetNameFromID(int);
+
+ uid = GetNameFromID(s->st_uid), NAMELEN);
+ gid = GetNameFromID(s->st_gid), NAMELEN);
+#endif
+ dir = malloc(sizeof(Dir));
+ if(dir == nil){
+ werrstr(Enovmem);
+ return nil;
+ }
+ dir->name = name;
+ dir->uid = dir->gid = dir->muid = nullstring;
+ dir->qid.type = ISTYPE(s, _S_IFDIR)? QTDIR: QTFILE;
+ dir->qid.path = s->st_ino;
+ dir->qid.vers = s->st_mtime;
+ dir->mode = (dir->qid.type<<24)|(s->st_mode&0777);
+ dir->atime = s->st_atime;
+ dir->mtime = s->st_mtime;
+ dir->length = s->st_size;
+ dir->dev = s->st_dev;
+ dir->type = ISTYPE(s, _S_IFIFO)? '|': 'M';
+ return dir;
+}
+
+Dir*
+dirfstat(int fd)
+{
+ struct stat sbuf;
+
+ if(fstat(fd, &sbuf) < 0)
+ return nil;
+ return statconv(&sbuf, nullstring);
+}
+
+Dir*
+dirstat(char *f)
+{
+ struct stat sbuf;
+ char *p;
+
+ if(stat(f, &sbuf) < 0)
+ return nil;
+ p = strrchr(f, '/');
+ if(p)
+ p++;
+ else
+ p = nullstring;
+ return statconv(&sbuf, p);
+}
diff --git a/lib9/dirstat-posix.c b/lib9/dirstat-posix.c
new file mode 100644
index 00000000..8925b6da
--- /dev/null
+++ b/lib9/dirstat-posix.c
@@ -0,0 +1,79 @@
+#include "lib9.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <grp.h>
+
+static char nullstring[] = "";
+static char Enovmem[] = "out of memory";
+
+static Dir*
+statconv(struct stat *s, char *name)
+{
+ struct passwd *p;
+ struct group *g;
+ Dir *dir;
+ int str;
+ char *n;
+
+ str = 0;
+ p = getpwuid(s->st_uid);
+ if(p)
+ str += strlen(p->pw_name)+1;
+ g = getgrgid(s->st_gid);
+ if(g)
+ str += strlen(g->gr_name)+1;
+ dir = malloc(sizeof(Dir)+str);
+ if(dir == nil){
+ werrstr(Enovmem);
+ return nil;
+ }
+ n = (char*)dir+sizeof(Dir);
+ dir->name = name;
+ dir->uid = dir->gid = dir->muid = nullstring;
+ if(p){
+ dir->uid = n;
+ strcpy(n, p->pw_name);
+ n += strlen(p->pw_name)+1;
+ }
+ if(g){
+ dir->gid = n;
+ strcpy(n, g->gr_name);
+ }
+ dir->qid.type = S_ISDIR(s->st_mode)? QTDIR: QTFILE;
+ dir->qid.path = s->st_ino;
+ dir->qid.vers = s->st_mtime;
+ dir->mode = (dir->qid.type<<24)|(s->st_mode&0777);
+ dir->atime = s->st_atime;
+ dir->mtime = s->st_mtime;
+ dir->length = s->st_size;
+ dir->dev = s->st_dev;
+ dir->type = S_ISFIFO(s->st_mode)? '|': 'M';
+ return dir;
+}
+
+Dir*
+dirfstat(int fd)
+{
+ struct stat sbuf;
+
+ if(fstat(fd, &sbuf) < 0)
+ return nil;
+ return statconv(&sbuf, nullstring);
+}
+
+Dir*
+dirstat(char *f)
+{
+ struct stat sbuf;
+ char *p;
+
+ if(stat(f, &sbuf) < 0)
+ return nil;
+ p = strrchr(f, '/');
+ if(p)
+ p++;
+ else
+ p = nullstring;
+ return statconv(&sbuf, p);
+}
diff --git a/lib9/dirwstat.c b/lib9/dirwstat.c
new file mode 100644
index 00000000..76587b9f
--- /dev/null
+++ b/lib9/dirwstat.c
@@ -0,0 +1,15 @@
+#include "lib9.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+
+int
+dirfwstat(int fd, Dir *d)
+{
+ return -1;
+}
+
+int
+dirwstat(char *name, Dir *d)
+{
+ return -1;
+}
diff --git a/lib9/dofmt.c b/lib9/dofmt.c
new file mode 100644
index 00000000..0f47c85d
--- /dev/null
+++ b/lib9/dofmt.c
@@ -0,0 +1,532 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "lib9.h"
+#include "fmtdef.h"
+
+/* format the output into f->to and return the number of characters fmted */
+int
+dofmt(Fmt *f, char *fmt)
+{
+ Rune rune, *rt, *rs;
+ int r;
+ char *t, *s;
+ int n, nfmt;
+
+ nfmt = f->nfmt;
+ for(;;){
+ if(f->runes){
+ rt = f->to;
+ rs = f->stop;
+ while((r = *(uchar*)fmt) && r != '%'){
+ if(r < Runeself)
+ fmt++;
+ else{
+ fmt += chartorune(&rune, fmt);
+ r = rune;
+ }
+ FMTRCHAR(f, rt, rs, r);
+ }
+ fmt++;
+ f->nfmt += rt - (Rune *)f->to;
+ f->to = rt;
+ if(!r)
+ return f->nfmt - nfmt;
+ f->stop = rs;
+ }else{
+ t = f->to;
+ s = f->stop;
+ while((r = *(uchar*)fmt) && r != '%'){
+ if(r < Runeself){
+ FMTCHAR(f, t, s, r);
+ fmt++;
+ }else{
+ n = chartorune(&rune, fmt);
+ if(t + n > s){
+ t = _fmtflush(f, t, n);
+ if(t != nil)
+ s = f->stop;
+ else
+ return -1;
+ }
+ while(n--)
+ *t++ = *fmt++;
+ }
+ }
+ fmt++;
+ f->nfmt += t - (char *)f->to;
+ f->to = t;
+ if(!r)
+ return f->nfmt - nfmt;
+ f->stop = s;
+ }
+
+ fmt = _fmtdispatch(f, fmt, 0);
+ if(fmt == nil)
+ return -1;
+ }
+ return 0; /* not reached */
+}
+
+void *
+_fmtflush(Fmt *f, void *t, int len)
+{
+ if(f->runes)
+ f->nfmt += (Rune*)t - (Rune*)f->to;
+ else
+ f->nfmt += (char*)t - (char *)f->to;
+ f->to = t;
+ if(f->flush == 0 || (*f->flush)(f) == 0 || (char*)f->to + len > (char*)f->stop){
+ f->stop = f->to;
+ return nil;
+ }
+ return f->to;
+}
+
+/*
+ * put a formatted block of memory sz bytes long of n runes into the output buffer,
+ * left/right justified in a field of at least f->width charactes
+ */
+int
+_fmtpad(Fmt *f, int n)
+{
+ char *t, *s;
+ int i;
+
+ t = f->to;
+ s = f->stop;
+ for(i = 0; i < n; i++)
+ FMTCHAR(f, t, s, ' ');
+ f->nfmt += t - (char *)f->to;
+ f->to = t;
+ return 0;
+}
+
+int
+_rfmtpad(Fmt *f, int n)
+{
+ Rune *t, *s;
+ int i;
+
+ t = f->to;
+ s = f->stop;
+ for(i = 0; i < n; i++)
+ FMTRCHAR(f, t, s, ' ');
+ f->nfmt += t - (Rune *)f->to;
+ f->to = t;
+ return 0;
+}
+
+int
+_fmtcpy(Fmt *f, void *vm, int n, int sz)
+{
+ Rune *rt, *rs, r;
+ char *t, *s, *m, *me;
+ ulong fl;
+ int nc, w;
+
+ m = vm;
+ me = m + sz;
+ w = f->width;
+ fl = f->flags;
+ if((fl & FmtPrec) && n > f->prec)
+ n = f->prec;
+ if(f->runes){
+ if(!(fl & FmtLeft) && _rfmtpad(f, w - n) < 0)
+ return -1;
+ rt = f->to;
+ rs = f->stop;
+ for(nc = n; nc > 0; nc--){
+ r = *(uchar*)m;
+ if(r < Runeself)
+ m++;
+ else if((me - m) >= UTFmax || fullrune(m, me-m))
+ m += chartorune(&r, m);
+ else
+ break;
+ FMTRCHAR(f, rt, rs, r);
+ }
+ f->nfmt += rt - (Rune *)f->to;
+ f->to = rt;
+ if(m < me)
+ return -1;
+ if(fl & FmtLeft && _rfmtpad(f, w - n) < 0)
+ return -1;
+ }else{
+ if(!(fl & FmtLeft) && _fmtpad(f, w - n) < 0)
+ return -1;
+ t = f->to;
+ s = f->stop;
+ for(nc = n; nc > 0; nc--){
+ r = *(uchar*)m;
+ if(r < Runeself)
+ m++;
+ else if((me - m) >= UTFmax || fullrune(m, me-m))
+ m += chartorune(&r, m);
+ else
+ break;
+ FMTRUNE(f, t, s, r);
+ }
+ f->nfmt += t - (char *)f->to;
+ f->to = t;
+ if(fl & FmtLeft && _fmtpad(f, w - n) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+int
+_fmtrcpy(Fmt *f, void *vm, int n)
+{
+ Rune r, *m, *me, *rt, *rs;
+ char *t, *s;
+ ulong fl;
+ int w;
+
+ m = vm;
+ w = f->width;
+ fl = f->flags;
+ if((fl & FmtPrec) && n > f->prec)
+ n = f->prec;
+ if(f->runes){
+ if(!(fl & FmtLeft) && _rfmtpad(f, w - n) < 0)
+ return -1;
+ rt = f->to;
+ rs = f->stop;
+ for(me = m + n; m < me; m++)
+ FMTRCHAR(f, rt, rs, *m);
+ f->nfmt += rt - (Rune *)f->to;
+ f->to = rt;
+ if(fl & FmtLeft && _rfmtpad(f, w - n) < 0)
+ return -1;
+ }else{
+ if(!(fl & FmtLeft) && _fmtpad(f, w - n) < 0)
+ return -1;
+ t = f->to;
+ s = f->stop;
+ for(me = m + n; m < me; m++){
+ r = *m;
+ FMTRUNE(f, t, s, r);
+ }
+ f->nfmt += t - (char *)f->to;
+ f->to = t;
+ if(fl & FmtLeft && _fmtpad(f, w - n) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+/* fmt out one character */
+int
+_charfmt(Fmt *f)
+{
+ char x[1];
+
+ x[0] = va_arg(f->args, int);
+ f->prec = 1;
+ return _fmtcpy(f, x, 1, 1);
+}
+
+/* fmt out one rune */
+int
+_runefmt(Fmt *f)
+{
+ Rune x[1];
+
+ x[0] = va_arg(f->args, int);
+ return _fmtrcpy(f, x, 1);
+}
+
+/* public helper routine: fmt out a null terminated string already in hand */
+int
+fmtstrcpy(Fmt *f, char *s)
+{
+ int p, i;
+ if(!s)
+ return _fmtcpy(f, "<nil>", 5, 5);
+ /* if precision is specified, make sure we don't wander off the end */
+ if(f->flags & FmtPrec){
+ p = f->prec;
+ for(i = 0; i < p; i++)
+ if(s[i] == 0)
+ break;
+ return _fmtcpy(f, s, utfnlen(s, i), i); /* BUG?: won't print a partial rune at end */
+ }
+
+ return _fmtcpy(f, s, utflen(s), strlen(s));
+}
+
+/* fmt out a null terminated utf string */
+int
+_strfmt(Fmt *f)
+{
+ char *s;
+
+ s = va_arg(f->args, char *);
+ return fmtstrcpy(f, s);
+}
+
+/* public helper routine: fmt out a null terminated rune string already in hand */
+int
+fmtrunestrcpy(Fmt *f, Rune *s)
+{
+ Rune *e;
+ int n, p;
+
+ if(!s)
+ return _fmtcpy(f, "<nil>", 5, 5);
+ /* if precision is specified, make sure we don't wander off the end */
+ if(f->flags & FmtPrec){
+ p = f->prec;
+ for(n = 0; n < p; n++)
+ if(s[n] == 0)
+ break;
+ }else{
+ for(e = s; *e; e++)
+ ;
+ n = e - s;
+ }
+ return _fmtrcpy(f, s, n);
+}
+
+/* fmt out a null terminated rune string */
+int
+_runesfmt(Fmt *f)
+{
+ Rune *s;
+
+ s = va_arg(f->args, Rune *);
+ return fmtrunestrcpy(f, s);
+}
+
+/* fmt a % */
+int
+_percentfmt(Fmt *f)
+{
+ Rune x[1];
+
+ x[0] = f->r;
+ f->prec = 1;
+ return _fmtrcpy(f, x, 1);
+}
+
+/* fmt an integer */
+int
+_ifmt(Fmt *f)
+{
+ char buf[70], *p, *conv;
+ uvlong vu;
+ ulong u;
+ int neg, base, i, n, fl, w, isv;
+
+ neg = 0;
+ fl = f->flags;
+ isv = 0;
+ vu = 0;
+ u = 0;
+ if(f->r == 'p'){
+ u = (ulong)va_arg(f->args, void*);
+ f->r = 'x';
+ fl |= FmtUnsigned;
+ }else if(fl & FmtVLong){
+ isv = 1;
+ if(fl & FmtUnsigned)
+ vu = va_arg(f->args, uvlong);
+ else
+ vu = va_arg(f->args, vlong);
+ }else if(fl & FmtLong){
+ if(fl & FmtUnsigned)
+ u = va_arg(f->args, ulong);
+ else
+ u = va_arg(f->args, long);
+ }else if(fl & FmtByte){
+ if(fl & FmtUnsigned)
+ u = (uchar)va_arg(f->args, int);
+ else
+ u = (char)va_arg(f->args, int);
+ }else if(fl & FmtShort){
+ if(fl & FmtUnsigned)
+ u = (ushort)va_arg(f->args, int);
+ else
+ u = (short)va_arg(f->args, int);
+ }else{
+ if(fl & FmtUnsigned)
+ u = va_arg(f->args, uint);
+ else
+ u = va_arg(f->args, int);
+ }
+ conv = "0123456789abcdef";
+ switch(f->r){
+ case 'd':
+ base = 10;
+ break;
+ case 'x':
+ base = 16;
+ break;
+ case 'X':
+ base = 16;
+ conv = "0123456789ABCDEF";
+ break;
+ case 'b':
+ base = 2;
+ break;
+ case 'o':
+ base = 8;
+ break;
+ default:
+ return -1;
+ }
+ if(!(fl & FmtUnsigned)){
+ if(isv && (vlong)vu < 0){
+ vu = -(vlong)vu;
+ neg = 1;
+ }else if(!isv && (long)u < 0){
+ u = -(long)u;
+ neg = 1;
+ }
+ }
+ p = buf + sizeof buf - 1;
+ n = 0;
+ if(isv){
+ while(vu){
+ i = vu % base;
+ vu /= base;
+ if((fl & FmtComma) && n % 4 == 3){
+ *p-- = ',';
+ n++;
+ }
+ *p-- = conv[i];
+ n++;
+ }
+ }else{
+ while(u){
+ i = u % base;
+ u /= base;
+ if((fl & FmtComma) && n % 4 == 3){
+ *p-- = ',';
+ n++;
+ }
+ *p-- = conv[i];
+ n++;
+ }
+ }
+ if(n == 0){
+ *p-- = '0';
+ n = 1;
+ }
+ for(w = f->prec; n < w && p > buf+3; n++)
+ *p-- = '0';
+ if(neg || (fl & (FmtSign|FmtSpace)))
+ n++;
+ if(fl & FmtSharp){
+ if(base == 16)
+ n += 2;
+ else if(base == 8){
+ if(p[1] == '0')
+ fl &= ~FmtSharp;
+ else
+ n++;
+ }
+ }
+ if((fl & FmtZero) && !(fl & FmtLeft)){
+ for(w = f->width; n < w && p > buf+3; n++)
+ *p-- = '0';
+ f->width = 0;
+ }
+ if(fl & FmtSharp){
+ if(base == 16)
+ *p-- = f->r;
+ if(base == 16 || base == 8)
+ *p-- = '0';
+ }
+ if(neg)
+ *p-- = '-';
+ else if(fl & FmtSign)
+ *p-- = '+';
+ else if(fl & FmtSpace)
+ *p-- = ' ';
+ f->flags &= ~FmtPrec;
+ return _fmtcpy(f, p + 1, n, n);
+}
+
+int
+_countfmt(Fmt *f)
+{
+ void *p;
+ ulong fl;
+
+ fl = f->flags;
+ p = va_arg(f->args, void*);
+ if(fl & FmtVLong){
+ *(vlong*)p = f->nfmt;
+ }else if(fl & FmtLong){
+ *(long*)p = f->nfmt;
+ }else if(fl & FmtByte){
+ *(char*)p = f->nfmt;
+ }else if(fl & FmtShort){
+ *(short*)p = f->nfmt;
+ }else{
+ *(int*)p = f->nfmt;
+ }
+ return 0;
+}
+
+int
+_flagfmt(Fmt *f)
+{
+ switch(f->r){
+ case ',':
+ f->flags |= FmtComma;
+ break;
+ case '-':
+ f->flags |= FmtLeft;
+ break;
+ case '+':
+ f->flags |= FmtSign;
+ break;
+ case '#':
+ f->flags |= FmtSharp;
+ break;
+ case ' ':
+ f->flags |= FmtSpace;
+ break;
+ case 'u':
+ f->flags |= FmtUnsigned;
+ break;
+ case 'h':
+ if(f->flags & FmtShort)
+ f->flags |= FmtByte;
+ f->flags |= FmtShort;
+ break;
+ case 'l':
+ if(f->flags & FmtLong)
+ f->flags |= FmtVLong;
+ f->flags |= FmtLong;
+ break;
+ }
+ return 1;
+}
+
+/* default error format */
+int
+_badfmt(Fmt *f)
+{
+ char x[3];
+
+ x[0] = '%';
+ x[1] = f->r;
+ x[2] = '%';
+ f->prec = 3;
+ _fmtcpy(f, x, 3, 3);
+ return 0;
+}
diff --git a/lib9/dorfmt.c b/lib9/dorfmt.c
new file mode 100644
index 00000000..645973a8
--- /dev/null
+++ b/lib9/dorfmt.c
@@ -0,0 +1,58 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "lib9.h"
+#include "fmtdef.h"
+
+/* format the output into f->to and return the number of characters fmted */
+
+int
+dorfmt(Fmt *f, Rune *fmt)
+{
+ Rune *rt, *rs;
+ int r;
+ char *t, *s;
+ int nfmt;
+
+ nfmt = f->nfmt;
+ for(;;){
+ if(f->runes){
+ rt = f->to;
+ rs = f->stop;
+ while((r = *fmt++) && r != '%'){
+ FMTRCHAR(f, rt, rs, r);
+ }
+ f->nfmt += rt - (Rune *)f->to;
+ f->to = rt;
+ if(!r)
+ return f->nfmt - nfmt;
+ f->stop = rs;
+ }else{
+ t = f->to;
+ s = f->stop;
+ while((r = *fmt++) && r != '%'){
+ FMTRUNE(f, t, f->stop, r);
+ }
+ f->nfmt += t - (char *)f->to;
+ f->to = t;
+ if(!r)
+ return f->nfmt - nfmt;
+ f->stop = s;
+ }
+
+ fmt = _fmtdispatch(f, fmt, 1);
+ if(fmt == nil)
+ return -1;
+ }
+ return 0; /* not reached */
+}
diff --git a/lib9/errfmt.c b/lib9/errfmt.c
new file mode 100644
index 00000000..7ea2ad18
--- /dev/null
+++ b/lib9/errfmt.c
@@ -0,0 +1,24 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "lib9.h"
+#include "fmtdef.h"
+
+int
+errfmt(Fmt *f)
+{
+ char buf[ERRMAX];
+
+ rerrstr(buf, sizeof buf);
+ return _fmtcpy(f, buf, utflen(buf), strlen(buf));
+}
diff --git a/lib9/errstr-Nt.c b/lib9/errstr-Nt.c
new file mode 100644
index 00000000..53c35323
--- /dev/null
+++ b/lib9/errstr-Nt.c
@@ -0,0 +1,65 @@
+#include "lib9.h"
+#include <Windows.h>
+
+static char errstring[ERRMAX];
+
+enum
+{
+ Magic = 0xffffff
+};
+
+static void
+winerror(int e, char *buf, uint nerr)
+{
+ int r;
+ char buf2[ERRMAX], *p, *q;
+
+ r = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
+ 0, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ buf2, sizeof(buf2), 0);
+
+ if(r == 0)
+ snprint(buf2, ERRMAX, "windows error %d", e);
+
+ q = buf2;
+ for(p = buf2; *p; p++) {
+ if(*p == '\r')
+ continue;
+ if(*p == '\n')
+ *q++ = ' ';
+ else
+ *q++ = *p;
+ }
+ *q = '\0';
+ utfecpy(buf, buf+nerr, buf2);
+}
+
+void
+werrstr(char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ vseprint(errstring, errstring+sizeof(errstring), fmt, arg);
+ va_end(arg);
+ SetLastError(Magic);
+}
+
+int
+errstr(char *buf, uint nerr)
+{
+ DWORD le;
+
+ le = GetLastError();
+ if(le == Magic)
+ utfecpy(buf, buf+nerr, errstring);
+ else
+ winerror(le, buf, nerr);
+ return 1;
+}
+
+void
+oserrstr(char *buf, uint nerr)
+{
+ winerror(GetLastError(), buf, nerr);
+}
diff --git a/lib9/errstr-Plan9.c b/lib9/errstr-Plan9.c
new file mode 100644
index 00000000..56e867c4
--- /dev/null
+++ b/lib9/errstr-Plan9.c
@@ -0,0 +1,8 @@
+#include "lib9.h"
+
+void
+oserrstr(char *buf, uint nerr)
+{
+ *buf = 0;
+ errstr(buf, nerr);
+}
diff --git a/lib9/errstr-posix.c b/lib9/errstr-posix.c
new file mode 100644
index 00000000..eacc7754
--- /dev/null
+++ b/lib9/errstr-posix.c
@@ -0,0 +1,37 @@
+#include "lib9.h"
+
+#include <errno.h>
+
+static char errstring[ERRMAX];
+
+enum
+{
+ Magic = 0xffffff
+};
+
+void
+werrstr(char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ vseprint(errstring, errstring+sizeof(errstring), fmt, arg);
+ va_end(arg);
+ errno = Magic;
+}
+
+void
+oserrstr(char *buf, uint nerr)
+{
+ utfecpy(buf, buf+nerr, strerror(errno));
+}
+
+int
+errstr(char *buf, uint nerr)
+{
+ if(errno == Magic)
+ utfecpy(buf, buf+nerr, errstring);
+ else
+ oserrstr(buf, nerr);
+ return 1;
+}
diff --git a/lib9/exits.c b/lib9/exits.c
new file mode 100644
index 00000000..2bedbf09
--- /dev/null
+++ b/lib9/exits.c
@@ -0,0 +1,17 @@
+#include "lib9.h"
+
+void
+exits(char *s)
+{
+ if(s == 0 || *s == 0)
+ exit(0);
+ exit(1);
+}
+
+void
+_exits(char *s)
+{
+ if(s == 0 || *s == 0)
+ _exit(0);
+ _exit(1);
+}
diff --git a/lib9/fcallfmt.c b/lib9/fcallfmt.c
new file mode 100644
index 00000000..e35872ef
--- /dev/null
+++ b/lib9/fcallfmt.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+
+#include <lib9.h>
+#include "fcall.h"
+
+static uint dumpsome(char*, char*, char*, long);
+static void fdirconv(char*, char*, Dir*);
+static char *qidtype(char*, uchar);
+
+#define QIDFMT "(%.16llux %lud %s)"
+
+int
+fcallfmt(Fmt *fmt)
+{
+ Fcall *f;
+ int fid, type, tag, i;
+ char buf[512], tmp[200];
+ char *p, *e;
+ Dir *d;
+ Qid *q;
+
+ e = buf+sizeof(buf);
+ f = va_arg(fmt->args, Fcall*);
+ type = f->type;
+ fid = f->fid;
+ tag = f->tag;
+ switch(type){
+ case Tversion: /* 100 */
+ seprint(buf, e, "Tversion tag %ud msize %ud version '%s'", tag, f->msize, f->version);
+ break;
+ case Rversion:
+ seprint(buf, e, "Rversion tag %ud msize %ud version '%s'", tag, f->msize, f->version);
+ break;
+ case Tauth: /* 102 */
+ seprint(buf, e, "Tauth tag %ud afid %d uname %s aname %s", tag,
+ f->afid, f->uname, f->aname);
+ break;
+ case Rauth:
+ seprint(buf, e, "Rauth tag %ud qid " QIDFMT, tag,
+ f->aqid.path, f->aqid.vers, qidtype(tmp, f->aqid.type));
+ break;
+ case Tattach: /* 104 */
+ seprint(buf, e, "Tattach tag %ud fid %d afid %d uname %s aname %s", tag,
+ fid, f->afid, f->uname, f->aname);
+ break;
+ case Rattach:
+ seprint(buf, e, "Rattach tag %ud qid " QIDFMT, tag,
+ f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type));
+ break;
+ case Rerror: /* 107; 106 (Terror) illegal */
+ seprint(buf, e, "Rerror tag %ud ename %s", tag, f->ename);
+ break;
+ case Tflush: /* 108 */
+ seprint(buf, e, "Tflush tag %ud oldtag %ud", tag, f->oldtag);
+ break;
+ case Rflush:
+ seprint(buf, e, "Rflush tag %ud", tag);
+ break;
+ case Twalk: /* 110 */
+ p = seprint(buf, e, "Twalk tag %ud fid %d newfid %d nwname %d ", tag, fid, f->newfid, f->nwname);
+ if(f->nwname <= MAXWELEM)
+ for(i=0; i<f->nwname; i++)
+ p = seprint(p, e, "%d:%s ", i, f->wname[i]);
+ break;
+ case Rwalk:
+ p = seprint(buf, e, "Rwalk tag %ud nwqid %ud ", tag, f->nwqid);
+ if(f->nwqid <= MAXWELEM)
+ for(i=0; i<f->nwqid; i++){
+ q = &f->wqid[i];
+ p = seprint(p, e, "%d:" QIDFMT " ", i,
+ q->path, q->vers, qidtype(tmp, q->type));
+ }
+ break;
+ case Topen: /* 112 */
+ seprint(buf, e, "Topen tag %ud fid %ud mode %d", tag, fid, f->mode);
+ break;
+ case Ropen:
+ seprint(buf, e, "Ropen tag %ud qid " QIDFMT " iounit %ud ", tag,
+ f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit);
+ break;
+ case Tcreate: /* 114 */
+ seprint(buf, e, "Tcreate tag %ud fid %ud name %s perm %M mode %d", tag, fid, f->name, (ulong)f->perm, f->mode);
+ break;
+ case Rcreate:
+ seprint(buf, e, "Rcreate tag %ud qid " QIDFMT " iounit %ud ", tag,
+ f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), f->iounit);
+ break;
+ case Tread: /* 116 */
+ seprint(buf, e, "Tread tag %ud fid %d offset %lld count %ud",
+ tag, fid, f->offset, f->count);
+ break;
+ case Rread:
+ p = seprint(buf, e, "Rread tag %ud count %ud ", tag, f->count);
+ dumpsome(p, e, f->data, f->count);
+ break;
+ case Twrite: /* 118 */
+ p = seprint(buf, e, "Twrite tag %ud fid %d offset %lld count %ud ",
+ tag, fid, f->offset, f->count);
+ dumpsome(p, e, f->data, f->count);
+ break;
+ case Rwrite:
+ seprint(buf, e, "Rwrite tag %ud count %ud", tag, f->count);
+ break;
+ case Tclunk: /* 120 */
+ seprint(buf, e, "Tclunk tag %ud fid %ud", tag, fid);
+ break;
+ case Rclunk:
+ seprint(buf, e, "Rclunk tag %ud", tag);
+ break;
+ case Tremove: /* 122 */
+ seprint(buf, e, "Tremove tag %ud fid %ud", tag, fid);
+ break;
+ case Rremove:
+ seprint(buf, e, "Rremove tag %ud", tag);
+ break;
+ case Tstat: /* 124 */
+ seprint(buf, e, "Tstat tag %ud fid %ud", tag, fid);
+ break;
+ case Rstat:
+ p = seprint(buf, e, "Rstat tag %ud ", tag);
+ if(f->nstat > sizeof tmp)
+ seprint(p, e, " stat(%d bytes)", f->nstat);
+ else{
+ d = (Dir*)tmp;
+ convM2D(f->stat, f->nstat, d, (char*)(d+1));
+ seprint(p, e, " stat ");
+ fdirconv(p+6, e, d);
+ }
+ break;
+ case Twstat: /* 126 */
+ p = seprint(buf, e, "Twstat tag %ud fid %ud", tag, fid);
+ if(f->nstat > sizeof tmp)
+ seprint(p, e, " stat(%d bytes)", f->nstat);
+ else{
+ d = (Dir*)tmp;
+ convM2D(f->stat, f->nstat, d, (char*)(d+1));
+ seprint(p, e, " stat ");
+ fdirconv(p+6, e, d);
+ }
+ break;
+ case Rwstat:
+ seprint(buf, e, "Rwstat tag %ud", tag);
+ break;
+ default:
+ seprint(buf, e, "unknown type %d", type);
+ }
+ return fmtstrcpy(fmt, buf);
+}
+
+static char*
+qidtype(char *s, uchar t)
+{
+ char *p;
+
+ p = s;
+ if(t & QTDIR)
+ *p++ = 'd';
+ if(t & QTAPPEND)
+ *p++ = 'a';
+ if(t & QTEXCL)
+ *p++ = 'l';
+ if(t & QTAUTH)
+ *p++ = 'A';
+ *p = '\0';
+ return s;
+}
+
+int
+dirfmt(Fmt *fmt)
+{
+ char buf[160];
+
+ fdirconv(buf, buf+sizeof buf, va_arg(fmt->args, Dir*));
+ return fmtstrcpy(fmt, buf);
+}
+
+static void
+fdirconv(char *buf, char *e, Dir *d)
+{
+ char tmp[16];
+
+ seprint(buf, e, "'%s' '%s' '%s' '%s' "
+ "q " QIDFMT " m %#luo "
+ "at %ld mt %ld l %lld "
+ "t %d d %d",
+ d->name, d->uid, d->gid, d->muid,
+ d->qid.path, d->qid.vers, qidtype(tmp, d->qid.type), d->mode,
+ d->atime, d->mtime, d->length,
+ d->type, d->dev);
+}
+
+/*
+ * dump out count (or DUMPL, if count is bigger) bytes from
+ * buf to ans, as a string if they are all printable,
+ * else as a series of hex bytes
+ */
+#define DUMPL 64
+
+static uint
+dumpsome(char *ans, char *e, char *buf, long count)
+{
+ int i, printable;
+ char *p;
+
+ if(buf == nil){
+ seprint(ans, e, "<no data>");
+ return strlen(ans);
+ }
+ printable = 1;
+ if(count > DUMPL)
+ count = DUMPL;
+ for(i=0; i<count && printable; i++)
+ if((buf[i]<32 && buf[i] !='\n' && buf[i] !='\t') || (uchar)buf[i]>127)
+ printable = 0;
+ p = ans;
+ *p++ = '\'';
+ if(printable){
+ if(count > e-p-2)
+ count = e-p-2;
+ memmove(p, buf, count);
+ p += count;
+ }else{
+ if(2*count > e-p-2)
+ count = (e-p-2)/2;
+ for(i=0; i<count; i++){
+ if(i>0 && i%4==0)
+ *p++ = ' ';
+ sprint(p, "%2.2ux", buf[i]);
+ p += 2;
+ }
+ }
+ *p++ = '\'';
+ *p = 0;
+ return p - ans;
+}
diff --git a/lib9/fltfmt.c b/lib9/fltfmt.c
new file mode 100644
index 00000000..739a4bf7
--- /dev/null
+++ b/lib9/fltfmt.c
@@ -0,0 +1,324 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "lib9.h"
+#include <ctype.h>
+#include "fmtdef.h"
+
+enum
+{
+ FDIGIT = 30,
+ FDEFLT = 6,
+ NSIGNIF = 17,
+};
+
+static int
+xadd(char *a, int n, int v)
+{
+ char *b;
+ int c;
+
+ if(n < 0 || n >= NSIGNIF)
+ return 0;
+ for(b = a+n; b >= a; b--) {
+ c = *b + v;
+ if(c <= '9') {
+ *b = c;
+ return 0;
+ }
+ *b = '0';
+ v = 1;
+ }
+ *a = '1'; // overflow adding
+ return 1;
+}
+
+static int
+xsub(char *a, int n, int v)
+{
+ char *b;
+ int c;
+
+ for(b = a+n; b >= a; b--) {
+ c = *b - v;
+ if(c >= '0') {
+ *b = c;
+ return 0;
+ }
+ *b = '9';
+ v = 1;
+ }
+ *a = '9'; // underflow subtracting
+ return 1;
+}
+
+static void
+xdtoa(Fmt *fmt, char *s2, double f)
+{
+ char s1[NSIGNIF+10];
+ double g, h;
+ int e, d, i, n;
+ int c1, c2, c3, c4, ucase, sign, chr, prec;
+
+ prec = FDEFLT;
+ if(fmt->flags & FmtPrec)
+ prec = fmt->prec;
+ if(prec > FDIGIT)
+ prec = FDIGIT;
+ if(isNaN(f)) {
+ strcpy(s2, "NaN");
+ return;
+ }
+ if(isInf(f, 1)) {
+ strcpy(s2, "+Inf");
+ return;
+ }
+ if(isInf(f, -1)) {
+ strcpy(s2, "-Inf");
+ return;
+ }
+ sign = 0;
+ if(f < 0) {
+ f = -f;
+ sign++;
+ }
+ ucase = 0;
+ chr = fmt->r;
+ if(isupper(chr)) {
+ ucase = 1;
+ chr = tolower(chr);
+ }
+
+ e = 0;
+ g = f;
+ if(g != 0) {
+ frexp(f, &e);
+ e = e * .301029995664;
+ if(e >= -150 && e <= +150) {
+ d = 0;
+ h = f;
+ } else {
+ d = e/2;
+ h = f * pow10(-d);
+ }
+ g = h * pow10(d-e);
+ while(g < 1) {
+ e--;
+ g = h * pow10(d-e);
+ }
+ while(g >= 10) {
+ e++;
+ g = h * pow10(d-e);
+ }
+ }
+
+ /*
+ * convert NSIGNIF digits and convert
+ * back to get accuracy.
+ */
+ for(i=0; i<NSIGNIF; i++) {
+ d = g;
+ s1[i] = d + '0';
+ g = (g - d) * 10;
+ }
+ s1[i] = 0;
+
+ /*
+ * try decimal rounding to eliminate 9s
+ */
+ c2 = prec + 1;
+ if(chr == 'f')
+ c2 += e;
+ if(c2 >= NSIGNIF-2) {
+ strcpy(s2, s1);
+ d = e;
+ s1[NSIGNIF-2] = '0';
+ s1[NSIGNIF-1] = '0';
+ sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
+ g = strtod(s1, nil);
+ if(g == f)
+ goto found;
+ if(xadd(s1, NSIGNIF-3, 1)) {
+ e++;
+ sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
+ }
+ g = strtod(s1, nil);
+ if(g == f)
+ goto found;
+ strcpy(s1, s2);
+ e = d;
+ }
+
+ /*
+ * convert back so s1 gets exact answer
+ */
+ for(;;) {
+ sprint(s1+NSIGNIF, "e%d", e-NSIGNIF+1);
+ g = strtod(s1, nil);
+ if(f > g) {
+ if(xadd(s1, NSIGNIF-1, 1))
+ e--;
+ continue;
+ }
+ if(f < g) {
+ if(xsub(s1, NSIGNIF-1, 1))
+ e++;
+ continue;
+ }
+ break;
+ }
+
+found:
+ /*
+ * sign
+ */
+ d = 0;
+ i = 0;
+ if(sign)
+ s2[d++] = '-';
+ else if(fmt->flags & FmtSign)
+ s2[d++] = '+';
+ else if(fmt->flags & FmtSpace)
+ s2[d++] = ' ';
+
+ /*
+ * copy into final place
+ * c1 digits of leading '0'
+ * c2 digits from conversion
+ * c3 digits of trailing '0'
+ * c4 digits after '.'
+ */
+ c1 = 0;
+ c2 = prec + 1;
+ c3 = 0;
+ c4 = prec;
+ switch(chr) {
+ default:
+ if(xadd(s1, c2, 5))
+ e++;
+ break;
+ case 'g':
+ /*
+ * decide on 'e' of 'f' style convers
+ */
+ if(xadd(s1, c2, 5))
+ e++;
+ if(e >= -5 && e <= prec) {
+ c1 = -e - 1;
+ c4 = prec - e;
+ chr = 'h'; // flag for 'f' style
+ }
+ break;
+ case 'f':
+ if(xadd(s1, c2+e, 5))
+ e++;
+ c1 = -e;
+ if(c1 > prec)
+ c1 = c2;
+ c2 += e;
+ break;
+ }
+
+ /*
+ * clean up c1 c2 and c3
+ */
+ if(c1 < 0)
+ c1 = 0;
+ if(c2 < 0)
+ c2 = 0;
+ if(c2 > NSIGNIF) {
+ c3 = c2-NSIGNIF;
+ c2 = NSIGNIF;
+ }
+
+ /*
+ * copy digits
+ */
+ while(c1 > 0) {
+ if(c1+c2+c3 == c4)
+ s2[d++] = '.';
+ s2[d++] = '0';
+ c1--;
+ }
+ while(c2 > 0) {
+ if(c2+c3 == c4)
+ s2[d++] = '.';
+ s2[d++] = s1[i++];
+ c2--;
+ }
+ while(c3 > 0) {
+ if(c3 == c4)
+ s2[d++] = '.';
+ s2[d++] = '0';
+ c3--;
+ }
+
+ /*
+ * strip trailing '0' on g conv
+ */
+ if(fmt->flags & FmtSharp) {
+ if(0 == c4)
+ s2[d++] = '.';
+ } else
+ if(chr == 'g' || chr == 'h') {
+ for(n=d-1; n>=0; n--)
+ if(s2[n] != '0')
+ break;
+ for(i=n; i>=0; i--)
+ if(s2[i] == '.') {
+ d = n;
+ if(i != n)
+ d++;
+ break;
+ }
+ }
+ if(chr == 'e' || chr == 'g') {
+ if(ucase)
+ s2[d++] = 'E';
+ else
+ s2[d++] = 'e';
+ c1 = e;
+ if(c1 < 0) {
+ s2[d++] = '-';
+ c1 = -c1;
+ } else
+ s2[d++] = '+';
+ if(c1 >= 100) {
+ s2[d++] = c1/100 + '0';
+ c1 = c1%100;
+ }
+ s2[d++] = c1/10 + '0';
+ s2[d++] = c1%10 + '0';
+ }
+ s2[d] = 0;
+}
+
+int
+_floatfmt(Fmt *fmt, double f)
+{
+ char s[FDIGIT+10];
+
+ xdtoa(fmt, s, f);
+ fmt->flags &= FmtWidth|FmtLeft;
+ _fmtcpy(fmt, s, strlen(s), strlen(s));
+ return 0;
+}
+
+int
+_efgfmt(Fmt *f)
+{
+ double d;
+
+ d = va_arg(f->args, double);
+ return _floatfmt(f, d);
+}
diff --git a/lib9/fmt.c b/lib9/fmt.c
new file mode 100644
index 00000000..11aac065
--- /dev/null
+++ b/lib9/fmt.c
@@ -0,0 +1,188 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "lib9.h"
+#include "fmtdef.h"
+
+enum
+{
+ Maxfmt = 64
+};
+
+typedef struct Convfmt Convfmt;
+struct Convfmt
+{
+ int c;
+ volatile Fmts fmt; /* for spin lock in fmtfmt; avoids race due to write order */
+};
+
+struct
+{
+ /* lock by calling _fmtlock, _fmtunlock */
+ int nfmt;
+ Convfmt fmt[Maxfmt];
+} fmtalloc;
+
+static Convfmt knownfmt[] = {
+ ' ', _flagfmt,
+ '#', _flagfmt,
+ '%', _percentfmt,
+ '+', _flagfmt,
+ ',', _flagfmt,
+ '-', _flagfmt,
+ 'C', _runefmt,
+ 'S', _runesfmt,
+ 'X', _ifmt,
+ 'b', _ifmt,
+ 'c', _charfmt,
+ 'd', _ifmt,
+ 'h', _flagfmt,
+ 'l', _flagfmt,
+ 'n', _countfmt,
+ 'o', _ifmt,
+ 'p', _ifmt,
+ 'r', errfmt,
+ 's', _strfmt,
+ 'u', _flagfmt,
+ 'x', _ifmt,
+ 0, nil,
+};
+
+int (*doquote)(int);
+
+static Fmts
+fmtfmt(int c)
+{
+ Convfmt *p, *ep;
+
+ ep = &fmtalloc.fmt[fmtalloc.nfmt];
+ for(p=fmtalloc.fmt; p<ep; p++)
+ if(p->c == c)
+ return p->fmt;
+
+ /* is this a predefined format char? */
+ for(p=knownfmt; p->c; p++)
+ if(p->c == c){
+ /* no need to lock; fmtinstall is idempotent */
+ fmtinstall(p->c, p->fmt);
+ while(p->fmt == nil) /* loop until value is updated */
+ ;
+ return p->fmt;
+ }
+
+ return _badfmt;
+}
+
+int
+fmtinstall(int c, Fmts f)
+{
+ Convfmt *p, *ep;
+
+ if(c<=0 || c>=65536)
+ return -1;
+ if(!f)
+ f = _badfmt;
+
+ _fmtlock();
+
+ ep = &fmtalloc.fmt[fmtalloc.nfmt];
+ for(p=fmtalloc.fmt; p<ep; p++)
+ if(p->c == c)
+ break;
+
+ if(p == &fmtalloc.fmt[Maxfmt]){
+ _fmtunlock();
+ return -1;
+ }
+
+ p->fmt = f;
+ if(p == ep){ /* installing a new format character */
+ fmtalloc.nfmt++;
+ p->c = c;
+ }
+
+ _fmtunlock();
+ return 0;
+}
+
+void*
+_fmtdispatch(Fmt *f, void *fmt, int isrunes)
+{
+ Rune rune, r;
+ int i, n;
+
+ f->flags = 0;
+ f->width = f->prec = 0;
+
+ for(;;){
+ if(isrunes){
+ r = *(Rune*)fmt;
+ fmt = (Rune*)fmt + 1;
+ }else{
+ fmt = (char*)fmt + chartorune(&rune, fmt);
+ r = rune;
+ }
+ f->r = r;
+ switch(r){
+ case '\0':
+ return nil;
+ case '.':
+ f->flags |= FmtWidth|FmtPrec;
+ continue;
+ case '0':
+ if(!(f->flags & FmtWidth)){
+ f->flags |= FmtZero;
+ continue;
+ }
+ /* fall through */
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ i = 0;
+ while(r >= '0' && r <= '9'){
+ i = i * 10 + r - '0';
+ if(isrunes){
+ r = *(Rune*)fmt;
+ fmt = (Rune*)fmt + 1;
+ }else{
+ r = *(char*)fmt;
+ fmt = (char*)fmt + 1;
+ }
+ }
+ if(isrunes)
+ fmt = (Rune*)fmt - 1;
+ else
+ fmt = (char*)fmt - 1;
+ numflag:
+ if(f->flags & FmtWidth){
+ f->flags |= FmtPrec;
+ f->prec = i;
+ }else{
+ f->flags |= FmtWidth;
+ f->width = i;
+ }
+ continue;
+ case '*':
+ i = va_arg(f->args, int);
+ if(i < 0){
+ i = -i;
+ f->flags |= FmtLeft;
+ }
+ goto numflag;
+ }
+ n = (*fmtfmt(r))(f);
+ if(n < 0)
+ return nil;
+ if(n == 0)
+ return fmt;
+ }
+}
diff --git a/lib9/fmtdef.h b/lib9/fmtdef.h
new file mode 100644
index 00000000..071041cf
--- /dev/null
+++ b/lib9/fmtdef.h
@@ -0,0 +1,98 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+/*
+ * dofmt -- format to a buffer
+ * the number of characters formatted is returned,
+ * or -1 if there was an error.
+ * if the buffer is ever filled, flush is called.
+ * it should reset the buffer and return whether formatting should continue.
+ */
+
+typedef int (*Fmts)(Fmt*);
+
+typedef struct Quoteinfo Quoteinfo;
+struct Quoteinfo
+{
+ int quoted; /* if set, string must be quoted */
+ int nrunesin; /* number of input runes that can be accepted */
+ int nbytesin; /* number of input bytes that can be accepted */
+ int nrunesout; /* number of runes that will be generated */
+ int nbytesout; /* number of bytes that will be generated */
+};
+
+void *_fmtflush(Fmt*, void*, int);
+void *_fmtdispatch(Fmt*, void*, int);
+int _floatfmt(Fmt*, double);
+int _fmtpad(Fmt*, int);
+int _rfmtpad(Fmt*, int);
+int _fmtFdFlush(Fmt*);
+
+int _efgfmt(Fmt*);
+int _charfmt(Fmt*);
+int _countfmt(Fmt*);
+int _flagfmt(Fmt*);
+int _percentfmt(Fmt*);
+int _ifmt(Fmt*);
+int _runefmt(Fmt*);
+int _runesfmt(Fmt*);
+int _strfmt(Fmt*);
+int _badfmt(Fmt*);
+int _fmtcpy(Fmt*, void*, int, int);
+int _fmtrcpy(Fmt*, void*, int n);
+
+void _fmtlock(void);
+void _fmtunlock(void);
+
+#define FMTCHAR(f, t, s, c)\
+ do{\
+ if(t + 1 > (char*)s){\
+ t = _fmtflush(f, t, 1);\
+ if(t != nil)\
+ s = f->stop;\
+ else\
+ return -1;\
+ }\
+ *t++ = c;\
+ }while(0)
+
+#define FMTRCHAR(f, t, s, c)\
+ do{\
+ if(t + 1 > (Rune*)s){\
+ t = _fmtflush(f, t, sizeof(Rune));\
+ if(t != nil)\
+ s = f->stop;\
+ else\
+ return -1;\
+ }\
+ *t++ = c;\
+ }while(0)
+
+#define FMTRUNE(f, t, s, r)\
+ do{\
+ Rune _rune;\
+ int _runelen;\
+ if(t + UTFmax > (char*)s && t + (_runelen = runelen(r)) > (char*)s){\
+ t = _fmtflush(f, t, _runelen);\
+ if(t != nil)\
+ s = f->stop;\
+ else\
+ return -1;\
+ }\
+ if(r < Runeself)\
+ *t++ = r;\
+ else{\
+ _rune = r;\
+ t += runetochar(t, &_rune);\
+ }\
+ }while(0)
diff --git a/lib9/fmtfd.c b/lib9/fmtfd.c
new file mode 100644
index 00000000..ea476294
--- /dev/null
+++ b/lib9/fmtfd.c
@@ -0,0 +1,43 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "lib9.h"
+#include "fmtdef.h"
+
+/*
+ * public routine for final flush of a formatting buffer
+ * to a file descriptor; returns total char count.
+ */
+int
+fmtfdflush(Fmt *f)
+{
+ if(_fmtFdFlush(f) <= 0)
+ return -1;
+ return f->nfmt;
+}
+
+/*
+ * initialize an output buffer for buffered printing
+ */
+int
+fmtfdinit(Fmt *f, int fd, char *buf, int size)
+{
+ f->runes = 0;
+ f->start = buf;
+ f->to = buf;
+ f->stop = buf + size;
+ f->flush = _fmtFdFlush;
+ f->farg = (void*)fd;
+ f->nfmt = 0;
+ return 0;
+}
diff --git a/lib9/fmtlock.c b/lib9/fmtlock.c
new file mode 100644
index 00000000..66e3b724
--- /dev/null
+++ b/lib9/fmtlock.c
@@ -0,0 +1,24 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "lib9.h"
+
+void
+_fmtlock(void)
+{
+}
+
+void
+_fmtunlock(void)
+{
+}
diff --git a/lib9/fmtprint.c b/lib9/fmtprint.c
new file mode 100644
index 00000000..c93cb315
--- /dev/null
+++ b/lib9/fmtprint.c
@@ -0,0 +1,44 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "lib9.h"
+#include "fmtdef.h"
+
+
+/*
+ * format a string into the output buffer
+ * designed for formats which themselves call fmt,
+ * but ignore any width flags
+ */
+int
+fmtprint(Fmt *f, char *fmt, ...)
+{
+ va_list va;
+ int n;
+
+ f->flags = 0;
+ f->width = 0;
+ f->prec = 0;
+ va = f->args;
+ va_start(f->args, fmt);
+ n = dofmt(f, fmt);
+ va_end(f->args);
+ f->flags = 0;
+ f->width = 0;
+ f->prec = 0;
+ f->args = va;
+ if(n >= 0)
+ return 0;
+ return n;
+}
+
diff --git a/lib9/fmtquote.c b/lib9/fmtquote.c
new file mode 100644
index 00000000..3e6338bf
--- /dev/null
+++ b/lib9/fmtquote.c
@@ -0,0 +1,259 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "lib9.h"
+#include "fmtdef.h"
+
+/*
+ * How many bytes of output UTF will be produced by quoting (if necessary) this string?
+ * How many runes? How much of the input will be consumed?
+ * The parameter q is filled in by _quotesetup.
+ * The string may be UTF or Runes (s or r).
+ * Return count does not include NUL.
+ * Terminate the scan at the first of:
+ * NUL in input
+ * count exceeded in input
+ * count exceeded on output
+ * *ninp is set to number of input bytes accepted.
+ * nin may be <0 initially, to avoid checking input by count.
+ */
+void
+_quotesetup(char *s, Rune *r, int nin, int nout, Quoteinfo *q, int sharp, int runesout)
+{
+ int w;
+ Rune c;
+
+ q->quoted = 0;
+ q->nbytesout = 0;
+ q->nrunesout = 0;
+ q->nbytesin = 0;
+ q->nrunesin = 0;
+ if(sharp || nin==0 || (s && *s=='\0') || (r && *r=='\0')){
+ if(nout < 2)
+ return;
+ q->quoted = 1;
+ q->nbytesout = 2;
+ q->nrunesout = 2;
+ }
+ for(; nin!=0; nin-=w){
+ if(s)
+ w = chartorune(&c, s);
+ else{
+ c = *r;
+ w = runelen(c);
+ }
+
+ if(c == '\0')
+ break;
+ if(runesout){
+ if(q->nrunesout+1 > nout)
+ break;
+ }else{
+ if(q->nbytesout+w > nout)
+ break;
+ }
+
+ if((c <= L' ') || (c == L'\'') || (doquote!=nil && doquote(c))){
+ if(!q->quoted){
+ if(runesout){
+ if(1+q->nrunesout+1+1 > nout) /* no room for quotes */
+ break;
+ }else{
+ if(1+q->nbytesout+w+1 > nout) /* no room for quotes */
+ break;
+ }
+ q->nrunesout += 2; /* include quotes */
+ q->nbytesout += 2; /* include quotes */
+ q->quoted = 1;
+ }
+ if(c == '\'') {
+ if(runesout){
+ if(1+q->nrunesout+1 > nout) /* no room for quotes */
+ break;
+ }else{
+ if(1+q->nbytesout+w > nout) /* no room for quotes */
+ break;
+ }
+ q->nbytesout++;
+ q->nrunesout++; /* quotes reproduce as two characters */
+ }
+ }
+
+ /* advance input */
+ if(s)
+ s += w;
+ else
+ r++;
+ q->nbytesin += w;
+ q->nrunesin++;
+
+ /* advance output */
+ q->nbytesout += w;
+ q->nrunesout++;
+ }
+}
+
+static int
+qstrfmt(char *sin, Rune *rin, Quoteinfo *q, Fmt *f)
+{
+ Rune r, *rm, *rme;
+ char *t, *s, *m, *me;
+ Rune *rt, *rs;
+ ulong fl;
+ int nc, w;
+
+ m = sin;
+ me = m + q->nbytesin;
+ rm = rin;
+ rme = rm + q->nrunesin;
+
+ w = f->width;
+ fl = f->flags;
+ if(f->runes){
+ if(!(fl & FmtLeft) && _rfmtpad(f, w - q->nrunesout) < 0)
+ return -1;
+ }else{
+ if(!(fl & FmtLeft) && _fmtpad(f, w - q->nbytesout) < 0)
+ return -1;
+ }
+ t = f->to;
+ s = f->stop;
+ rt = f->to;
+ rs = f->stop;
+ if(f->runes)
+ FMTRCHAR(f, rt, rs, '\'');
+ else
+ FMTRUNE(f, t, s, '\'');
+ for(nc = q->nrunesin; nc > 0; nc--){
+ if(sin){
+ r = *(uchar*)m;
+ if(r < Runeself)
+ m++;
+ else if((me - m) >= UTFmax || fullrune(m, me-m))
+ m += chartorune(&r, m);
+ else
+ break;
+ }else{
+ if(rm >= rme)
+ break;
+ r = *rm++;
+ }
+ if(f->runes){
+ FMTRCHAR(f, rt, rs, r);
+ if(r == '\'')
+ FMTRCHAR(f, rt, rs, r);
+ }else{
+ FMTRUNE(f, t, s, r);
+ if(r == '\'')
+ FMTRUNE(f, t, s, r);
+ }
+ }
+
+ if(f->runes){
+ FMTRCHAR(f, rt, rs, '\'');
+ USED(rs);
+ f->nfmt += rt - (Rune *)f->to;
+ f->to = rt;
+ if(fl & FmtLeft && _rfmtpad(f, w - q->nrunesout) < 0)
+ return -1;
+ }else{
+ FMTRUNE(f, t, s, '\'');
+ USED(s);
+ f->nfmt += t - (char *)f->to;
+ f->to = t;
+ if(fl & FmtLeft && _fmtpad(f, w - q->nbytesout) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+int
+_quotestrfmt(int runesin, Fmt *f)
+{
+ int outlen;
+ Rune *r;
+ char *s;
+ Quoteinfo q;
+
+ f->flags &= ~FmtPrec; /* ignored for %q %Q, so disable for %s %S in easy case */
+ if(runesin){
+ r = va_arg(f->args, Rune *);
+ s = nil;
+ }else{
+ s = va_arg(f->args, char *);
+ r = nil;
+ }
+ if(!s && !r)
+ return _fmtcpy(f, "<nil>", 5, 5);
+
+ if(f->flush)
+ outlen = 0x7FFFFFFF; /* if we can flush, no output limit */
+ else if(f->runes)
+ outlen = (Rune*)f->stop - (Rune*)f->to;
+ else
+ outlen = (char*)f->stop - (char*)f->to;
+
+ _quotesetup(s, r, -1, outlen, &q, f->flags&FmtSharp, f->runes);
+//print("bytes in %d bytes out %d runes in %d runesout %d\n", q.nbytesin, q.nbytesout, q.nrunesin, q.nrunesout);
+
+ if(runesin){
+ if(!q.quoted)
+ return _fmtrcpy(f, r, q.nrunesin);
+ return qstrfmt(nil, r, &q, f);
+ }
+
+ if(!q.quoted)
+ return _fmtcpy(f, s, q.nrunesin, q.nbytesin);
+ return qstrfmt(s, nil, &q, f);
+}
+
+int
+quotestrfmt(Fmt *f)
+{
+ return _quotestrfmt(0, f);
+}
+
+int
+quoterunestrfmt(Fmt *f)
+{
+ return _quotestrfmt(1, f);
+}
+
+void
+quotefmtinstall(void)
+{
+ fmtinstall('q', quotestrfmt);
+ fmtinstall('Q', quoterunestrfmt);
+}
+
+int
+_needsquotes(char *s, int *quotelenp)
+{
+ Quoteinfo q;
+
+ _quotesetup(s, nil, -1, 0x7FFFFFFF, &q, 0, 0);
+ *quotelenp = q.nbytesout;
+
+ return q.quoted;
+}
+
+int
+_runeneedsquotes(Rune *r, int *quotelenp)
+{
+ Quoteinfo q;
+
+ _quotesetup(nil, r, -1, 0x7FFFFFFF, &q, 0, 0);
+ *quotelenp = q.nrunesout;
+
+ return q.quoted;
+}
diff --git a/lib9/fmtrune.c b/lib9/fmtrune.c
new file mode 100644
index 00000000..65e620e6
--- /dev/null
+++ b/lib9/fmtrune.c
@@ -0,0 +1,37 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "lib9.h"
+#include "fmtdef.h"
+
+int
+fmtrune(Fmt *f, int r)
+{
+ Rune *rt;
+ char *t;
+ int n;
+
+ if(f->runes){
+ rt = f->to;
+ FMTRCHAR(f, rt, f->stop, r);
+ f->to = rt;
+ n = 1;
+ }else{
+ t = f->to;
+ FMTRUNE(f, t, f->stop, r);
+ n = t - (char*)f->to;
+ f->to = t;
+ }
+ f->nfmt += n;
+ return 0;
+}
diff --git a/lib9/fmtstr.c b/lib9/fmtstr.c
new file mode 100644
index 00000000..9f94d134
--- /dev/null
+++ b/lib9/fmtstr.c
@@ -0,0 +1,23 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "lib9.h"
+
+char*
+fmtstrflush(Fmt *f)
+{
+ if(f->start == nil)
+ return nil;
+ *(char*)f->to = '\0';
+ return f->start;
+}
diff --git a/lib9/fmtvprint.c b/lib9/fmtvprint.c
new file mode 100644
index 00000000..6682cd33
--- /dev/null
+++ b/lib9/fmtvprint.c
@@ -0,0 +1,43 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "lib9.h"
+#include "fmtdef.h"
+
+
+/*
+ * format a string into the output buffer
+ * designed for formats which themselves call fmt,
+ * but ignore any width flags
+ */
+int
+fmtvprint(Fmt *f, char *fmt, va_list args)
+{
+ va_list va;
+ int n;
+
+ f->flags = 0;
+ f->width = 0;
+ f->prec = 0;
+ va = f->args;
+ f->args = args;
+ n = dofmt(f, fmt);
+ f->flags = 0;
+ f->width = 0;
+ f->prec = 0;
+ f->args = va;
+ if(n >= 0)
+ return 0;
+ return n;
+}
+
diff --git a/lib9/fprint.c b/lib9/fprint.c
new file mode 100644
index 00000000..1cbb34c7
--- /dev/null
+++ b/lib9/fprint.c
@@ -0,0 +1,26 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "lib9.h"
+
+int
+fprint(int fd, char *fmt, ...)
+{
+ int n;
+ va_list args;
+
+ va_start(args, fmt);
+ n = vfprint(fd, fmt, args);
+ va_end(args);
+ return n;
+}
diff --git a/lib9/getcallerpc-FreeBSD-386.S b/lib9/getcallerpc-FreeBSD-386.S
new file mode 100644
index 00000000..134c2847
--- /dev/null
+++ b/lib9/getcallerpc-FreeBSD-386.S
@@ -0,0 +1,7 @@
+ .file "getcallerpc-FreeBSD-386.S"
+
+ .type getcallerpc,@function
+ .global getcallerpc
+getcallerpc:
+ movl 4(%ebp), %eax
+ ret
diff --git a/lib9/getcallerpc-Hp-s800.s b/lib9/getcallerpc-Hp-s800.s
new file mode 100644
index 00000000..2a1f4080
--- /dev/null
+++ b/lib9/getcallerpc-Hp-s800.s
@@ -0,0 +1,12 @@
+ .LEVEL 1.1
+
+ .SPACE $TEXT$,SORT=8
+ .SUBSPA $CODE$,QUAD=0,ALIGN=8,ACCESS=0x2c,CODE_ONLY,SORT=24
+getcallerpc
+ .PROC
+ .CALLINFO FRAME=0,ARGS_SAVED
+ .ENTRY
+ LDI 0,%r28
+ .EXIT
+ BV,N %r0(%r2)
+ .PROCEND
diff --git a/lib9/getcallerpc-Irix-mips.s b/lib9/getcallerpc-Irix-mips.s
new file mode 100644
index 00000000..298e1da6
--- /dev/null
+++ b/lib9/getcallerpc-Irix-mips.s
@@ -0,0 +1,9 @@
+#include <sys/regdef.h>
+#include <sys/asm.h>
+
+LEAF(getcallerpc)
+ .set noreorder
+ lw v0, 28(sp)
+ j ra
+ .set reorder
+ END(getcallerpc)
diff --git a/lib9/getcallerpc-Linux-386.S b/lib9/getcallerpc-Linux-386.S
new file mode 100644
index 00000000..71b58781
--- /dev/null
+++ b/lib9/getcallerpc-Linux-386.S
@@ -0,0 +1,7 @@
+ .file "getcallerpc-Linux-386.S"
+ .text
+ .type getcallerpc,@function
+ .global getcallerpc
+getcallerpc:
+ movl 4(%ebp), %eax
+ ret
diff --git a/lib9/getcallerpc-MacOSX-power.s b/lib9/getcallerpc-MacOSX-power.s
new file mode 100644
index 00000000..d0c7c8ab
--- /dev/null
+++ b/lib9/getcallerpc-MacOSX-power.s
@@ -0,0 +1,19 @@
+/*
+ File: getcallerpc-Darwin-power.s
+
+ getcallerpc() is passed a pointer
+
+ Plan 9 asm:
+
+ TEXT getcallerpc(SB), $-4
+ MOVW 0(R1), R3
+ RETURN
+
+ */
+
+#import <architecture/ppc/asm_help.h>
+
+.text
+LABEL(_getcallerpc)
+ mflr r3
+ blr
diff --git a/lib9/getcallerpc-Solaris-386.s b/lib9/getcallerpc-Solaris-386.s
new file mode 100644
index 00000000..5d4c7053
--- /dev/null
+++ b/lib9/getcallerpc-Solaris-386.s
@@ -0,0 +1,8 @@
+ .align 4
+ .globl getcallerpc
+getcallerpc:
+ movl 4(%ebp),%eax
+ ret
+ .align 4
+ .type getcallerpc,@function
+ .size getcallerpc,.-getcallerpc
diff --git a/lib9/getcallerpc-Solaris-sparc.s b/lib9/getcallerpc-Solaris-sparc.s
new file mode 100644
index 00000000..79e18fa0
--- /dev/null
+++ b/lib9/getcallerpc-Solaris-sparc.s
@@ -0,0 +1,11 @@
+ .section ".text", #alloc, #execinstr
+ .align 8
+ .skip 16
+ .global getcallerpc
+ .type getcallerpc, 2
+getcallerpc: ! ignore argument
+ retl
+ add %i7,0,%o0
+
+ .size getcallerpc,(.-getcallerpc)
+
diff --git a/lib9/getcallerpc-Unixware-386.s b/lib9/getcallerpc-Unixware-386.s
new file mode 100644
index 00000000..5d4c7053
--- /dev/null
+++ b/lib9/getcallerpc-Unixware-386.s
@@ -0,0 +1,8 @@
+ .align 4
+ .globl getcallerpc
+getcallerpc:
+ movl 4(%ebp),%eax
+ ret
+ .align 4
+ .type getcallerpc,@function
+ .size getcallerpc,.-getcallerpc
diff --git a/lib9/getfields.c b/lib9/getfields.c
new file mode 100644
index 00000000..44d7417c
--- /dev/null
+++ b/lib9/getfields.c
@@ -0,0 +1,35 @@
+#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/lib9/getuser-Nt.c b/lib9/getuser-Nt.c
new file mode 100644
index 00000000..3d478f8a
--- /dev/null
+++ b/lib9/getuser-Nt.c
@@ -0,0 +1,9 @@
+#include "lib9.h"
+
+char*
+getuser(void)
+{
+ static char *user = 0;
+ user = "unknown";
+ return user;
+}
diff --git a/lib9/getuser-posix.c b/lib9/getuser-posix.c
new file mode 100644
index 00000000..018ee33c
--- /dev/null
+++ b/lib9/getuser-posix.c
@@ -0,0 +1,22 @@
+#include "lib9.h"
+#include <pwd.h>
+
+char*
+getuser(void)
+{
+ struct passwd *p;
+
+ static char *user = 0;
+
+ if (!user) {
+ p = getpwuid(getuid());
+ if (p && p->pw_name) {
+ user = malloc(strlen(p->pw_name)+1);
+ if (user)
+ strcpy(user, p->pw_name);
+ }
+ }
+ if(!user)
+ user = "unknown";
+ return user;
+}
diff --git a/lib9/getwd-Nt.c b/lib9/getwd-Nt.c
new file mode 100644
index 00000000..dcda8361
--- /dev/null
+++ b/lib9/getwd-Nt.c
@@ -0,0 +1,24 @@
+#include "lib9.h"
+#include <windows.h>
+
+
+char*
+getwd(char *buf, int size)
+{
+ int n;
+ char *p;
+
+ buf[0] = 0;
+
+ n = GetCurrentDirectory(size, buf);
+ if(n == 0) {
+ /*winerror();*/
+ return 0;
+ }
+
+ for(p=buf; *p; p++)
+ if(*p == '\\')
+ *p = '/';
+
+ return buf;
+}
diff --git a/lib9/getwd-posix.c b/lib9/getwd-posix.c
new file mode 100644
index 00000000..8554a47e
--- /dev/null
+++ b/lib9/getwd-posix.c
@@ -0,0 +1,9 @@
+#include "lib9.h"
+#undef getwd
+#include <unistd.h>
+
+char *
+infgetwd(char *buf, int size)
+{
+ return getcwd(buf, size);
+}
diff --git a/lib9/lock-Hp-s800.s b/lib9/lock-Hp-s800.s
new file mode 100644
index 00000000..26b6ca47
--- /dev/null
+++ b/lib9/lock-Hp-s800.s
@@ -0,0 +1,38 @@
+;
+; /*
+; * To get lock routine, compile this into a .s, then SUBSTITUTE
+; * a LOAD AND CLEAR WORD instruction for the load and store of
+; * l->key.
+; *
+; */
+; typedef struct Lock {
+; int key;
+; } Lock;
+;
+; int
+; mutexlock(Lock *l)
+; {
+; int key;
+;
+; key = l->key;
+; l->key = 0;
+; return key != 0;
+; }
+
+ .LEVEL 1.1
+
+ .SPACE $TEXT$,SORT=8
+ .SUBSPA $CODE$,QUAD=0,ALIGN=8,ACCESS=0x2c,CODE_ONLY,SORT=24
+mutexlock
+ .PROC
+ .CALLINFO FRAME=0,ARGS_SAVED
+ .ENTRY
+; SUBSTITUTED LDW 0(%r26),%r31
+; SUBSTITUTED STWS %r0,0(%r26)
+ LDCWS 0(%r26),%r31 ; SUBSTITUTED
+ COMICLR,= 0,%r31,%r28
+ LDI 1,%r28
+ .EXIT
+ BV,N %r0(%r2)
+ .PROCEND
+ .end
diff --git a/lib9/lock-Irix-mips.s b/lib9/lock-Irix-mips.s
new file mode 100644
index 00000000..40606e93
--- /dev/null
+++ b/lib9/lock-Irix-mips.s
@@ -0,0 +1,19 @@
+#include <sys/regdef.h>
+#include <sys/asm.h>
+
+/*
+ * lock from r4000 book
+ */
+LEAF(canlock)
+ .set noreorder
+1:
+ ll t0,0(a0) /* a0 is argument */
+ or t1, t0, 1
+ sc t1,0(a0)
+ beq t1,zero,1b
+ nop
+ j $31 /* lock held */
+ xor v0, t0, 1
+
+ .set reorder
+ END(canlock)
diff --git a/lib9/lock-MacOSX-power.s b/lib9/lock-MacOSX-power.s
new file mode 100644
index 00000000..d5e15d4d
--- /dev/null
+++ b/lib9/lock-MacOSX-power.s
@@ -0,0 +1,16 @@
+#include <architecture/ppc/asm_help.h>
+
+LEAF(__tas)
+ sync
+ mr r4,r3
+ addi r5,0,0x1
+1:
+ lwarx r3,0,r4
+ cmpwi r3,0x0
+ bne- 2f
+ stwcx. r5,0,r4
+ bne- 1b /* Lost reservation, try again */
+2:
+ sync
+ blr
+END(__tas)
diff --git a/lib9/lock-Nt-386.c b/lib9/lock-Nt-386.c
new file mode 100644
index 00000000..641907ef
--- /dev/null
+++ b/lib9/lock-Nt-386.c
@@ -0,0 +1,15 @@
+#include "lib9.h"
+
+ulong
+_tas(ulong *la)
+{
+ ulong v;
+
+ _asm {
+ mov eax, la
+ mov ebx, 1
+ xchg ebx, [eax]
+ mov v, ebx
+ }
+ return v;
+}
diff --git a/lib9/lock-Solaris-386.s b/lib9/lock-Solaris-386.s
new file mode 100644
index 00000000..d3f5a581
--- /dev/null
+++ b/lib9/lock-Solaris-386.s
@@ -0,0 +1,56 @@
+ .section .bss
+ .align 4
+.L4_.bss:
+ .align 4
+Solaris_Asm_IntP: / Offset 0
+ .type Solaris_Asm_IntP,@object
+ .size Solaris_Asm_IntP,4
+ .set .,.+4
+Solaris_Asm_VoidP: / Offset 4
+ .type Solaris_Asm_VoidP,@object
+ .size Solaris_Asm_VoidP,4
+ .set .,.+4
+ .section .data
+ .align 4
+.L2_.data:
+ .align 4
+.L01: .string "canlock corrupted 0x%lux\n\000"
+ .set .,.+0x2
+ .section .text
+ .align 4
+.L1_.text:
+
+ .align 4
+ .align 4
+ .globl canlock
+canlock:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %esi
+ movl 8(%ebp),%eax
+ movl %eax,Solaris_Asm_IntP
+ movl $1, %ecx
+ xchg (%eax), %ecx
+ andl %ecx,%ecx
+ jne .L1
+ movl $1,%esi
+ jmp .L2
+ .align 4
+.L3:
+ subl %esi,%esi
+ jmp .L2
+ .align 4
+.L1:
+ cmpl $1,%ecx
+ je .L3
+ pushl %ecx
+ pushl $.L01
+ call print
+ addl $8,%esp
+.L2:
+ movl %esi,%eax
+ popl %esi
+ leave
+ ret
+ .type canlock,@function
+ .size canlock,.-canlock
diff --git a/lib9/lock-Solaris-sparc.s b/lib9/lock-Solaris-sparc.s
new file mode 100644
index 00000000..d0f9399f
--- /dev/null
+++ b/lib9/lock-Solaris-sparc.s
@@ -0,0 +1,20 @@
+
+ .section ".text", #alloc, #execinstr
+ .align 8
+ .skip 16
+ .global canlock
+ .type canlock,2
+
+canlock:
+ or %g0,1,%o1
+ swap [%o0],%o1 ! o0 points to lock; key is first word
+ cmp %o1,1
+ bne .gotit
+ nop
+ retl ! key was 1
+ or %g0,0,%o0
+.gotit:
+ retl ! key was not 1
+ or %g0,1,%o0
+
+ .size canlock,(.-canlock)
diff --git a/lib9/lock-Unixware-386.s b/lib9/lock-Unixware-386.s
new file mode 100644
index 00000000..efa33d02
--- /dev/null
+++ b/lib9/lock-Unixware-386.s
@@ -0,0 +1,56 @@
+ .section .bss
+ .align 4
+.L4_.bss:
+ .align 4
+Unixware_Asm_IntP: / Offset 0
+ .type Unixware_Asm_IntP,@object
+ .size Unixware_Asm_IntP,4
+ .set .,.+4
+Unixware_Asm_VoidP: / Offset 4
+ .type Unixware_Asm_VoidP,@object
+ .size Unixware_Asm_VoidP,4
+ .set .,.+4
+ .section .data
+ .align 4
+.L2_.data:
+ .align 4
+.L01: .string "canlock corrupted 0x%lux\n\000"
+ .set .,.+0x2
+ .section .text
+ .align 4
+.L1_.text:
+
+ .align 4
+ .align 4
+ .globl canlock
+canlock:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %esi
+ movl 8(%ebp),%eax
+ movl %eax,Unixware_Asm_IntP
+ movl $1, %ecx
+ xchg (%eax), %ecx
+ andl %ecx,%ecx
+ jne .L1
+ movl $1,%esi
+ jmp .L2
+ .align 4
+.L3:
+ subl %esi,%esi
+ jmp .L2
+ .align 4
+.L1:
+ cmpl $1,%ecx
+ je .L3
+ pushl %ecx
+ pushl $.L01
+ call print
+ addl $8,%esp
+.L2:
+ movl %esi,%eax
+ popl %esi
+ leave
+ ret
+ .type canlock,@function
+ .size canlock,.-canlock
diff --git a/lib9/lock.c b/lib9/lock.c
new file mode 100644
index 00000000..48b5d8c2
--- /dev/null
+++ b/lib9/lock.c
@@ -0,0 +1,141 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+void
+lock(Lock *l)
+{
+ int i;
+
+ if(_tas(&l->val) == 0)
+ return;
+ for(i=0; i<100; i++){
+ if(_tas(&l->val) == 0)
+ return;
+ osyield();
+ }
+ for(i=1;; i++){
+ if(_tas(&l->val) == 0)
+ return;
+ osmillisleep(i*10);
+ if(i > 100){
+ osyield();
+ i = 1;
+ }
+ }
+}
+
+int
+canlock(Lock *l)
+{
+ return _tas(&l->val) == 0;
+}
+
+void
+unlock(Lock *l)
+{
+ l->val = 0;
+}
+
+void
+qlock(QLock *q)
+{
+ Proc *p;
+
+ lock(&q->use);
+ if(!q->locked) {
+ q->locked = 1;
+ unlock(&q->use);
+ return;
+ }
+ p = q->tail;
+ if(p == 0)
+ q->head = up;
+ else
+ p->qnext = up;
+ q->tail = up;
+ up->qnext = 0;
+ unlock(&q->use);
+ osblock();
+}
+
+int
+canqlock(QLock *q)
+{
+ if(!canlock(&q->use))
+ return 0;
+ if(q->locked){
+ unlock(&q->use);
+ return 0;
+ }
+ q->locked = 1;
+ unlock(&q->use);
+ return 1;
+}
+
+void
+qunlock(QLock *q)
+{
+ Proc *p;
+
+ lock(&q->use);
+ p = q->head;
+ if(p) {
+ q->head = p->qnext;
+ if(q->head == 0)
+ q->tail = 0;
+ unlock(&q->use);
+ osready(p);
+ return;
+ }
+ q->locked = 0;
+ unlock(&q->use);
+}
+
+void
+rlock(RWlock *l)
+{
+ qlock(&l->x); /* wait here for writers and exclusion */
+ lock(&l->l);
+ l->readers++;
+ canqlock(&l->k); /* block writers if we are the first reader */
+ unlock(&l->l);
+ qunlock(&l->x);
+}
+
+/* same as rlock but punts if there are any writers waiting */
+int
+canrlock(RWlock *l)
+{
+ if (!canqlock(&l->x))
+ return 0;
+ lock(&l->l);
+ l->readers++;
+ canqlock(&l->k); /* block writers if we are the first reader */
+ unlock(&l->l);
+ qunlock(&l->x);
+ return 1;
+}
+
+void
+runlock(RWlock *l)
+{
+ lock(&l->l);
+ if(--l->readers == 0) /* last reader out allows writers */
+ qunlock(&l->k);
+ unlock(&l->l);
+}
+
+void
+wlock(RWlock *l)
+{
+ qlock(&l->x); /* wait here for writers and exclusion */
+ qlock(&l->k); /* wait here for last reader */
+}
+
+void
+wunlock(RWlock *l)
+{
+ qunlock(&l->k);
+ qunlock(&l->x);
+}
diff --git a/lib9/mkfile b/lib9/mkfile
new file mode 100644
index 00000000..32e1aa56
--- /dev/null
+++ b/lib9/mkfile
@@ -0,0 +1,85 @@
+<../mkconfig
+
+LIB=lib9.a
+
+#
+# files used by all models
+#
+COMMONFILES=\
+ convD2M.$O\
+ convM2D.$O\
+ convM2S.$O\
+ convS2M.$O\
+ fcallfmt.$O\
+ qsort.$O\
+ runestrlen.$O\
+ strtoll.$O\
+ rune.$O\
+#
+# files used by most models. these are added to TARGFILES in some
+# model-specific mkfiles included below
+#
+IMPORTFILES=\
+ argv0.$O\
+ charstod.$O\
+ cistrcmp.$O\
+ cistrncmp.$O\
+ cistrstr.$O\
+ cleanname.$O\
+ create.$O\
+ dirwstat.$O\
+ dofmt.$O\
+ dorfmt.$O\
+ errfmt.$O\
+ exits.$O\
+ fmt.$O\
+ fmtfd.$O\
+ fmtlock.$O\
+ fmtprint.$O\
+ fmtquote.$O\
+ fmtrune.$O\
+ fmtstr.$O\
+ fmtvprint.$O\
+ fprint.$O\
+ getfields.$O\
+ nulldir.$O\
+ pow10.$O\
+ print.$O\
+ readn.$O\
+ rerrstr.$O\
+ seek.$O\
+ seprint.$O\
+ smprint.$O\
+ snprint.$O\
+ sprint.$O\
+ strdup.$O\
+ strecpy.$O\
+ sysfatal.$O\
+ tokenize.$O\
+ u16.$O\
+ u32.$O\
+ u64.$O\
+ utflen.$O\
+ utfnlen.$O\
+ utfrrune.$O\
+ utfrune.$O\
+ utfecpy.$O\
+ vfprint.$O\
+ vseprint.$O\
+ vsmprint.$O\
+ vsnprint.$O\
+
+<mkfile-$TARGMODEL
+
+OFILES=$COMMONFILES $TARGFILES
+
+HFILES=$ROOT/$SYSTARG/$OBJTYPE/include/lib9.h\
+ fmtdef.h\
+
+<$ROOT/mkfiles/mksyslib-$SHELLTYPE
+
+convD2M.$O: $ROOT/include/fcall.h
+convM2D.$O: $ROOT/include/fcall.h
+convM2S.$O: $ROOT/include/fcall.h
+convS2M.$O: $ROOT/include/fcall.h
+fcallfmt.$O: $ROOT/include/fcall.h
diff --git a/lib9/mkfile-Nt b/lib9/mkfile-Nt
new file mode 100644
index 00000000..28e714ba
--- /dev/null
+++ b/lib9/mkfile-Nt
@@ -0,0 +1,8 @@
+TARGFILES=\
+ $IMPORTFILES\
+ dirstat-Nt.$O\
+ errstr-Nt.$O\
+ getuser-Nt.$O\
+ getwd-Nt.$O\
+ setbinmode-Nt.$O\
+ lock-Nt-386.$O\
diff --git a/lib9/mkfile-Plan9 b/lib9/mkfile-Plan9
new file mode 100644
index 00000000..081a2152
--- /dev/null
+++ b/lib9/mkfile-Plan9
@@ -0,0 +1,2 @@
+TARGFILES=errstr-Plan9.$O\
+
diff --git a/lib9/mkfile-Posix b/lib9/mkfile-Posix
new file mode 100644
index 00000000..085c665b
--- /dev/null
+++ b/lib9/mkfile-Posix
@@ -0,0 +1,8 @@
+TARGFILES=\
+ $IMPORTFILES\
+ dirstat-posix.$O\
+ errstr-posix.$O\
+ getuser-posix.$O\
+ getcallerpc-$SYSTARG-$OBJTYPE.$O\
+ getwd-posix.$O\
+ sbrk-posix.$O\
diff --git a/lib9/nulldir.c b/lib9/nulldir.c
new file mode 100644
index 00000000..26ca3b22
--- /dev/null
+++ b/lib9/nulldir.c
@@ -0,0 +1,8 @@
+#include "lib9.h"
+
+void
+nulldir(Dir *d)
+{
+ memset(d, ~0, sizeof(Dir));
+ d->name = d->uid = d->gid = d->muid = "";
+}
diff --git a/lib9/pow10.c b/lib9/pow10.c
new file mode 100644
index 00000000..84971a27
--- /dev/null
+++ b/lib9/pow10.c
@@ -0,0 +1,44 @@
+#ifdef LINUX_386
+#define _MATH_H
+#endif
+
+#include "lib9.h"
+
+/*
+ * this table might overflow 127-bit exponent representations.
+ * in that case, truncate it after 1.0e38.
+ * it is important to get all one can from this
+ * routine since it is used in atof to scale numbers.
+ * the presumption is that C converts fp numbers better
+ * than multipication of lower powers of 10.
+ */
+
+static
+double tab[] =
+{
+ 1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9,
+ 1.0e10,1.0e11,1.0e12,1.0e13,1.0e14,1.0e15,1.0e16,1.0e17,1.0e18,1.0e19,
+ 1.0e20,1.0e21,1.0e22,1.0e23,1.0e24,1.0e25,1.0e26,1.0e27,1.0e28,1.0e29,
+ 1.0e30,1.0e31,1.0e32,1.0e33,1.0e34,1.0e35,1.0e36,1.0e37,1.0e38,1.0e39,
+ 1.0e40,1.0e41,1.0e42,1.0e43,1.0e44,1.0e45,1.0e46,1.0e47,1.0e48,1.0e49,
+ 1.0e50,1.0e51,1.0e52,1.0e53,1.0e54,1.0e55,1.0e56,1.0e57,1.0e58,1.0e59,
+ 1.0e60,1.0e61,1.0e62,1.0e63,1.0e64,1.0e65,1.0e66,1.0e67,1.0e68,1.0e69,
+};
+
+double
+pow10(int n)
+{
+ int m;
+
+ if(n < 0) {
+ n = -n;
+ if(n < sizeof(tab)/sizeof(tab[0]))
+ return 1/tab[n];
+ m = n/2;
+ return pow10(-m) * pow10(m-n);
+ }
+ if(n < sizeof(tab)/sizeof(tab[0]))
+ return tab[n];
+ m = n/2;
+ return pow10(m) * pow10(n-m);
+}
diff --git a/lib9/print.c b/lib9/print.c
new file mode 100644
index 00000000..65feb976
--- /dev/null
+++ b/lib9/print.c
@@ -0,0 +1,26 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "lib9.h"
+
+int
+print(char *fmt, ...)
+{
+ int n;
+ va_list args;
+
+ va_start(args, fmt);
+ n = vfprint(1, fmt, args);
+ va_end(args);
+ return n;
+}
diff --git a/lib9/qsort.c b/lib9/qsort.c
new file mode 100644
index 00000000..3388891c
--- /dev/null
+++ b/lib9/qsort.c
@@ -0,0 +1,122 @@
+/*
+ * qsort -- simple quicksort
+ */
+
+typedef
+struct
+{
+ int (*cmp)(void*, void*);
+ void (*swap)(char*, char*, long);
+ long es;
+} Sort;
+
+static void
+swapb(char *i, char *j, long es)
+{
+ char c;
+
+ do {
+ c = *i;
+ *i++ = *j;
+ *j++ = c;
+ es--;
+ } while(es != 0);
+
+}
+
+static void
+swapi(char *ii, char *ij, long es)
+{
+ long *i, *j, c;
+
+ i = (long*)ii;
+ j = (long*)ij;
+ do {
+ c = *i;
+ *i++ = *j;
+ *j++ = c;
+ es -= sizeof(long);
+ } while(es != 0);
+}
+
+static char*
+pivot(char *a, long n, Sort *p)
+{
+ long j;
+ char *pi, *pj, *pk;
+
+ j = n/6 * p->es;
+ pi = a + j; /* 1/6 */
+ j += j;
+ pj = pi + j; /* 1/2 */
+ pk = pj + j; /* 5/6 */
+ if(p->cmp(pi, pj) < 0) {
+ if(p->cmp(pi, pk) < 0) {
+ if(p->cmp(pj, pk) < 0)
+ return pj;
+ return pk;
+ }
+ return pi;
+ }
+ if(p->cmp(pj, pk) < 0) {
+ if(p->cmp(pi, pk) < 0)
+ return pi;
+ return pk;
+ }
+ return pj;
+}
+
+static void
+qsorts(char *a, long n, Sort *p)
+{
+ long j, es;
+ char *pi, *pj, *pn;
+
+ es = p->es;
+ while(n > 1) {
+ if(n > 10) {
+ pi = pivot(a, n, p);
+ } else
+ pi = a + (n>>1)*es;
+
+ p->swap(a, pi, es);
+ pi = a;
+ pn = a + n*es;
+ pj = pn;
+ for(;;) {
+ do
+ pi += es;
+ while(pi < pn && p->cmp(pi, a) < 0);
+ do
+ pj -= es;
+ while(pj > a && p->cmp(pj, a) > 0);
+ if(pj < pi)
+ break;
+ p->swap(pi, pj, es);
+ }
+ p->swap(a, pj, es);
+ j = (pj - a) / es;
+
+ n = n-j-1;
+ if(j >= n) {
+ qsorts(a, j, p);
+ a += (j+1)*es;
+ } else {
+ qsorts(a + (j+1)*es, n, p);
+ n = j;
+ }
+ }
+}
+
+void
+qsort(void *va, long n, long es, int (*cmp)(void*, void*))
+{
+ Sort s;
+
+ s.cmp = cmp;
+ s.es = es;
+ s.swap = swapi;
+ if(((long)va | es) % sizeof(long))
+ s.swap = swapb;
+ qsorts((char*)va, n, &s);
+}
diff --git a/lib9/readn.c b/lib9/readn.c
new file mode 100644
index 00000000..74554264
--- /dev/null
+++ b/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/lib9/rerrstr.c b/lib9/rerrstr.c
new file mode 100644
index 00000000..ebddaf9b
--- /dev/null
+++ b/lib9/rerrstr.c
@@ -0,0 +1,12 @@
+#include "lib9.h"
+
+void
+rerrstr(char *buf, uint nbuf)
+{
+ char tmp[ERRMAX];
+
+ tmp[0] = 0;
+ errstr(tmp, sizeof tmp);
+ utfecpy(buf, buf+nbuf, tmp);
+ errstr(tmp, sizeof tmp);
+}
diff --git a/lib9/rune.c b/lib9/rune.c
new file mode 100644
index 00000000..3dcff0b4
--- /dev/null
+++ b/lib9/rune.c
@@ -0,0 +1,162 @@
+#include "lib9.h"
+
+enum
+{
+ Bit1 = 7,
+ Bitx = 6,
+ Bit2 = 5,
+ Bit3 = 4,
+ Bit4 = 3,
+
+ T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */
+ Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */
+ T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */
+ T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */
+ T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */
+
+ Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0111 1111 */
+ Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0111 1111 1111 */
+ Rune3 = (1<<(Bit3+2*Bitx))-1, /* 1111 1111 1111 1111 */
+
+ Maskx = (1<<Bitx)-1, /* 0011 1111 */
+ Testx = Maskx ^ 0xFF, /* 1100 0000 */
+
+ Bad = Runeerror
+};
+
+int
+chartorune(Rune *rune, char *str)
+{
+ int c, c1, c2;
+ long l;
+
+ /*
+ * one character sequence
+ * 00000-0007F => T1
+ */
+ c = *(uchar*)str;
+ if(c < Tx) {
+ *rune = c;
+ return 1;
+ }
+
+ /*
+ * two character sequence
+ * 0080-07FF => T2 Tx
+ */
+ c1 = *(uchar*)(str+1) ^ Tx;
+ if(c1 & Testx)
+ goto bad;
+ if(c < T3) {
+ if(c < T2)
+ goto bad;
+ l = ((c << Bitx) | c1) & Rune2;
+ if(l <= Rune1)
+ goto bad;
+ *rune = l;
+ return 2;
+ }
+
+ /*
+ * three character sequence
+ * 0800-FFFF => T3 Tx Tx
+ */
+ c2 = *(uchar*)(str+2) ^ Tx;
+ if(c2 & Testx)
+ goto bad;
+ if(c < T4) {
+ l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3;
+ if(l <= Rune2)
+ goto bad;
+ *rune = l;
+ return 3;
+ }
+
+ /*
+ * bad decoding
+ */
+bad:
+ *rune = Bad;
+ return 1;
+}
+
+int
+runetochar(char *str, Rune *rune)
+{
+ long c;
+
+ /*
+ * one character sequence
+ * 00000-0007F => 00-7F
+ */
+ c = *rune;
+ if(c <= Rune1) {
+ str[0] = c;
+ return 1;
+ }
+
+ /*
+ * two character sequence
+ * 0080-07FF => T2 Tx
+ */
+ if(c <= Rune2) {
+ str[0] = T2 | (c >> 1*Bitx);
+ str[1] = Tx | (c & Maskx);
+ return 2;
+ }
+
+ /*
+ * three character sequence
+ * 0800-FFFF => T3 Tx Tx
+ */
+ str[0] = T3 | (c >> 2*Bitx);
+ str[1] = Tx | ((c >> 1*Bitx) & Maskx);
+ str[2] = Tx | (c & Maskx);
+ return 3;
+}
+
+int
+runelen(long c)
+{
+ if(c <= Rune1)
+ return 1;
+ if(c <= Rune2)
+ return 2;
+ return 3;
+}
+
+int
+runenlen(Rune *r, int l)
+{
+ int n;
+ long c;
+
+ n = 0;
+ while(l--) {
+ c = *r++;
+ if(c <= Rune1)
+ n += 1;
+ else
+ if(c <= Rune2)
+ n += 2;
+ else
+ n += 3;
+ }
+ return n;
+}
+
+int
+fullrune(char *str, int n)
+{
+ int c;
+
+ if(n > 0) {
+ c = *(uchar*)str;
+ if(c < Tx)
+ return 1;
+ if(n > 1)
+ if(c < T3 || n > 2)
+ return 1;
+ }
+ return 0;
+}
diff --git a/lib9/runestrlen.c b/lib9/runestrlen.c
new file mode 100644
index 00000000..e1cd84ac
--- /dev/null
+++ b/lib9/runestrlen.c
@@ -0,0 +1,13 @@
+#include "lib9.h"
+
+
+long
+runestrlen(Rune *s)
+{
+ int i;
+
+ i = 0;
+ while(*s++)
+ i++;
+ return i;
+}
diff --git a/lib9/sbrk-posix.c b/lib9/sbrk-posix.c
new file mode 100644
index 00000000..c21cd2ff
--- /dev/null
+++ b/lib9/sbrk-posix.c
@@ -0,0 +1,48 @@
+#ifdef __APPLE_CC__ /* look for a better way */
+#include "lib9.h"
+#undef _POSIX_C_SOURCE
+#undef getwd
+
+#include <unistd.h>
+#include <pthread.h>
+#include <time.h>
+#include <termios.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+
+#include <sys/socket.h>
+#include <sched.h>
+#include <errno.h>
+#include <sys/ucontext.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <mach/mach_init.h>
+
+#include <mach/task.h>
+
+#include <mach/vm_map.h>
+
+
+__typeof__(sbrk(0))
+sbrk(int size)
+{
+ void *brk;
+ kern_return_t err;
+
+ err = vm_allocate( (vm_map_t) mach_task_self(),
+ (vm_address_t *)&brk,
+ size,
+ VM_FLAGS_ANYWHERE);
+ if (err != KERN_SUCCESS)
+ brk = (void*)-1;
+
+ return brk;
+}
+#else
+/* dummy function for everyone else, in case its ar complains about empty files */
+void __fakesbrk(void){}
+#endif
diff --git a/lib9/seek.c b/lib9/seek.c
new file mode 100644
index 00000000..0fb5282a
--- /dev/null
+++ b/lib9/seek.c
@@ -0,0 +1,9 @@
+#include "lib9.h"
+#include <sys/types.h>
+#include <fcntl.h>
+
+vlong
+seek(int fd, vlong where, int from)
+{
+ return lseek(fd, where, from);
+}
diff --git a/lib9/seprint.c b/lib9/seprint.c
new file mode 100644
index 00000000..7dadad62
--- /dev/null
+++ b/lib9/seprint.c
@@ -0,0 +1,26 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "lib9.h"
+
+char*
+seprint(char *buf, char *e, char *fmt, ...)
+{
+ char *p;
+ va_list args;
+
+ va_start(args, fmt);
+ p = vseprint(buf, e, fmt, args);
+ va_end(args);
+ return p;
+}
diff --git a/lib9/setbinmode-Nt.c b/lib9/setbinmode-Nt.c
new file mode 100644
index 00000000..48c5f700
--- /dev/null
+++ b/lib9/setbinmode-Nt.c
@@ -0,0 +1,9 @@
+#include "lib9.h"
+
+void
+setbinmode(void)
+{
+ _setmode(0, _O_BINARY);
+ _setmode(1, _O_BINARY);
+ _setmode(2, _O_BINARY);
+}
diff --git a/lib9/smprint.c b/lib9/smprint.c
new file mode 100644
index 00000000..ecb55492
--- /dev/null
+++ b/lib9/smprint.c
@@ -0,0 +1,26 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "lib9.h"
+
+char*
+smprint(char *fmt, ...)
+{
+ va_list args;
+ char *p;
+
+ va_start(args, fmt);
+ p = vsmprint(fmt, args);
+ va_end(args);
+ return p;
+}
diff --git a/lib9/snprint.c b/lib9/snprint.c
new file mode 100644
index 00000000..6e65c70c
--- /dev/null
+++ b/lib9/snprint.c
@@ -0,0 +1,27 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "lib9.h"
+
+int
+snprint(char *buf, int len, char *fmt, ...)
+{
+ int n;
+ va_list args;
+
+ va_start(args, fmt);
+ n = vsnprint(buf, len, fmt, args);
+ va_end(args);
+ return n;
+}
+
diff --git a/lib9/sprint.c b/lib9/sprint.c
new file mode 100644
index 00000000..86c79a32
--- /dev/null
+++ b/lib9/sprint.c
@@ -0,0 +1,35 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "lib9.h"
+
+int
+sprint(char *buf, char *fmt, ...)
+{
+ int n;
+ uint len;
+ va_list args;
+
+ len = 1<<30; /* big number, but sprint is deprecated anyway */
+ /*
+ * the stack might be near the top of memory, so
+ * we must be sure not to overflow a 32-bit pointer.
+ */
+ if(buf+len < buf)
+ len = -(uint)buf-1;
+
+ va_start(args, fmt);
+ n = vsnprint(buf, len, fmt, args);
+ va_end(args);
+ return n;
+}
diff --git a/lib9/strdup.c b/lib9/strdup.c
new file mode 100644
index 00000000..2473f8f5
--- /dev/null
+++ b/lib9/strdup.c
@@ -0,0 +1,12 @@
+#include "lib9.h"
+
+char*
+strdup(const char *s)
+{
+ char *os;
+
+ os = malloc(strlen(s) + 1);
+ if(os == 0)
+ return 0;
+ return strcpy(os, s);
+}
diff --git a/lib9/strecpy.c b/lib9/strecpy.c
new file mode 100644
index 00000000..64b23294
--- /dev/null
+++ b/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/lib9/strtoll.c b/lib9/strtoll.c
new file mode 100644
index 00000000..ff98e1c4
--- /dev/null
+++ b/lib9/strtoll.c
@@ -0,0 +1,81 @@
+#include "lib9.h"
+
+vlong
+strtoll(const char *nptr, char **endptr, int base)
+{
+ const char *p;
+ vlong n;
+ int c, v, neg, ndig;
+
+ p = nptr;
+ neg = 0;
+ n = 0;
+ ndig = 0;
+
+ /*
+ * White space
+ */
+ for(;;p++){
+ switch(*p){
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\f':
+ case '\r':
+ case '\v':
+ continue;
+ }
+ break;
+ }
+
+ /*
+ * Sign
+ */
+ if(*p=='-' || *p=='+')
+ if(*p++ == '-')
+ neg = 1;
+
+ /*
+ * Base
+ */
+ if(base==0){
+ if(*p != '0')
+ base = 10;
+ else{
+ base = 8;
+ if(p[1]=='x' || p[1]=='X'){
+ p += 2;
+ base = 16;
+ }
+ }
+ }else if(base==16 && *p=='0'){
+ if(p[1]=='x' || p[1]=='X')
+ p += 2;
+ }else if(base<0 || 36<base)
+ goto Return;
+
+ /*
+ * Non-empty sequence of digits
+ */
+ for(;; p++,ndig++){
+ c = *p;
+ v = base;
+ if('0'<=c && c<='9')
+ v = c - '0';
+ else if('a'<=c && c<='z')
+ v = c - 'a' + 10;
+ else if('A'<=c && c<='Z')
+ v = c - 'A' + 10;
+ if(v >= base)
+ break;
+ n = n*base + v;
+ }
+ Return:
+ if(ndig == 0)
+ p = nptr;
+ if(endptr)
+ *endptr = (char*) p;
+ if(neg)
+ return -n;
+ return n;
+}
diff --git a/lib9/sysfatal.c b/lib9/sysfatal.c
new file mode 100644
index 00000000..bb1f8e85
--- /dev/null
+++ b/lib9/sysfatal.c
@@ -0,0 +1,28 @@
+#include "lib9.h"
+
+
+
+static void
+_sysfatalimpl(char *fmt, va_list arg)
+{
+ char buf[1024];
+
+ vseprint(buf, buf+sizeof(buf), fmt, arg);
+ if(argv0)
+ fprint(2, "%s: %s\n", argv0, buf);
+ else
+ fprint(2, "%s\n", buf);
+ exits(buf);
+}
+
+void (*_sysfatal)(char *fmt, va_list arg) = _sysfatalimpl;
+
+void
+sysfatal(char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ (*_sysfatal)(fmt, arg);
+ va_end(arg);
+}
diff --git a/lib9/tokenize.c b/lib9/tokenize.c
new file mode 100644
index 00000000..1b222c52
--- /dev/null
+++ b/lib9/tokenize.c
@@ -0,0 +1,58 @@
+#include "lib9.h"
+
+static char qsep[] = " \t\r\n";
+
+static char*
+qtoken(char *s)
+{
+ int quoting;
+ char *t;
+
+ quoting = 0;
+ t = s; /* s is output string, t is input string */
+ while(*t!='\0' && (quoting || utfrune(qsep, *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;
+}
+
+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);
+ }
+
+ return nargs;
+}
diff --git a/lib9/u16.c b/lib9/u16.c
new file mode 100644
index 00000000..d9f41e46
--- /dev/null
+++ b/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/lib9/u32.c b/lib9/u32.c
new file mode 100644
index 00000000..1eb0c6e0
--- /dev/null
+++ b/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/lib9/u64.c b/lib9/u64.c
new file mode 100644
index 00000000..a17bdf1d
--- /dev/null
+++ b/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/lib9/utfecpy.c b/lib9/utfecpy.c
new file mode 100644
index 00000000..8ccb841c
--- /dev/null
+++ b/lib9/utfecpy.c
@@ -0,0 +1,20 @@
+#include "lib9.h"
+
+char*
+utfecpy(char *to, char *e, char *from)
+{
+ char *end;
+
+ if(to >= e)
+ return to;
+ end = memccpy(to, from, '\0', e - to);
+ if(end == nil){
+ end = e-1;
+ while(end>to && (*--end&0xC0)==0x80)
+ ;
+ *end = '\0';
+ }else{
+ end--;
+ }
+ return end;
+}
diff --git a/lib9/utflen.c b/lib9/utflen.c
new file mode 100644
index 00000000..815cc040
--- /dev/null
+++ b/lib9/utflen.c
@@ -0,0 +1,22 @@
+#include "lib9.h"
+
+int
+utflen(char *s)
+{
+ int c;
+ long n;
+ Rune rune;
+
+ n = 0;
+ for(;;) {
+ c = *(uchar*)s;
+ if(c < Runeself) {
+ if(c == 0)
+ return n;
+ s++;
+ } else
+ s += chartorune(&rune, s);
+ n++;
+ }
+ return 0;
+}
diff --git a/lib9/utfnlen.c b/lib9/utfnlen.c
new file mode 100644
index 00000000..02623f51
--- /dev/null
+++ b/lib9/utfnlen.c
@@ -0,0 +1,25 @@
+#include "lib9.h"
+
+int
+utfnlen(char *s, long m)
+{
+ int c;
+ long n;
+ Rune rune;
+ char *es;
+
+ es = s + m;
+ for(n = 0; s < es; n++) {
+ c = *(uchar*)s;
+ if(c < Runeself){
+ if(c == '\0')
+ break;
+ s++;
+ continue;
+ }
+ if(!fullrune(s, es-s))
+ break;
+ s += chartorune(&rune, s);
+ }
+ return n;
+}
diff --git a/lib9/utfrrune.c b/lib9/utfrrune.c
new file mode 100644
index 00000000..b6e8f2fc
--- /dev/null
+++ b/lib9/utfrrune.c
@@ -0,0 +1,30 @@
+#include "lib9.h"
+
+char*
+utfrrune(char *s, long c)
+{
+ long c1;
+ Rune r;
+ char *s1;
+
+ if(c < Runesync) /* not part of utf sequence */
+ return strrchr(s, c);
+
+ s1 = 0;
+ for(;;) {
+ c1 = *(uchar*)s;
+ if(c1 < Runeself) { /* one byte rune */
+ if(c1 == 0)
+ return s1;
+ if(c1 == c)
+ s1 = s;
+ s++;
+ continue;
+ }
+ c1 = chartorune(&r, s);
+ if(r == c)
+ s1 = s;
+ s += c1;
+ }
+ return 0;
+}
diff --git a/lib9/utfrune.c b/lib9/utfrune.c
new file mode 100644
index 00000000..a2737471
--- /dev/null
+++ b/lib9/utfrune.c
@@ -0,0 +1,29 @@
+#include "lib9.h"
+
+char*
+utfrune(char *s, long c)
+{
+ long c1;
+ Rune r;
+ int n;
+
+ if(c < Runesync) /* not part of utf sequence */
+ return strchr(s, c);
+
+ for(;;) {
+ c1 = *(uchar*)s;
+ if(c1 < Runeself) { /* one byte rune */
+ if(c1 == 0)
+ return 0;
+ if(c1 == c)
+ return s;
+ s++;
+ continue;
+ }
+ n = chartorune(&r, s);
+ if(r == c)
+ return s;
+ s += n;
+ }
+ return 0;
+}
diff --git a/lib9/vfprint.c b/lib9/vfprint.c
new file mode 100644
index 00000000..eeea55d1
--- /dev/null
+++ b/lib9/vfprint.c
@@ -0,0 +1,46 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "lib9.h"
+#include "fmtdef.h"
+
+/*
+ * generic routine for flushing a formatting buffer
+ * to a file descriptor
+ */
+int
+_fmtFdFlush(Fmt *f)
+{
+ int n;
+
+ n = (char*)f->to - (char*)f->start;
+ if(n && write((int)f->farg, f->start, n) != n)
+ return 0;
+ f->to = f->start;
+ return 1;
+}
+
+int
+vfprint(int fd, char *fmt, va_list args)
+{
+ Fmt f;
+ char buf[256];
+ int n;
+
+ fmtfdinit(&f, fd, buf, sizeof(buf));
+ f.args = args;
+ n = dofmt(&f, fmt);
+ if(n > 0 && _fmtFdFlush(&f) == 0)
+ return -1;
+ return n;
+}
diff --git a/lib9/vseprint.c b/lib9/vseprint.c
new file mode 100644
index 00000000..6f478960
--- /dev/null
+++ b/lib9/vseprint.c
@@ -0,0 +1,35 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "lib9.h"
+
+char*
+vseprint(char *buf, char *e, char *fmt, va_list args)
+{
+ Fmt f;
+
+ if(e <= buf)
+ return nil;
+ f.runes = 0;
+ f.start = buf;
+ f.to = buf;
+ f.stop = e - 1;
+ f.flush = nil;
+ f.farg = nil;
+ f.nfmt = 0;
+ f.args = args;
+ dofmt(&f, fmt);
+ *(char*)f.to = '\0';
+ return f.to;
+}
+
diff --git a/lib9/vsmprint.c b/lib9/vsmprint.c
new file mode 100644
index 00000000..44789be5
--- /dev/null
+++ b/lib9/vsmprint.c
@@ -0,0 +1,74 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "lib9.h"
+#include "fmtdef.h"
+
+static int
+fmtStrFlush(Fmt *f)
+{
+ char *s;
+ int n;
+
+ n = (int)f->farg;
+ n += 256;
+ f->farg = (void*)n;
+ s = f->start;
+ f->start = realloc(s, n);
+ if(f->start == nil){
+ free(s);
+ f->to = nil;
+ f->stop = nil;
+ return 0;
+ }
+ f->to = (char*)f->start + ((char*)f->to - s);
+ f->stop = (char*)f->start + n - 1;
+ return 1;
+}
+
+int
+fmtstrinit(Fmt *f)
+{
+ int n;
+
+ f->runes = 0;
+ n = 32;
+ f->start = malloc(n);
+ if(f->start == nil)
+ return -1;
+ f->to = f->start;
+ f->stop = (char*)f->start + n - 1;
+ f->flush = fmtStrFlush;
+ f->farg = (void*)n;
+ f->nfmt = 0;
+ return 0;
+}
+
+/*
+ * print into an allocated string buffer
+ */
+char*
+vsmprint(char *fmt, va_list args)
+{
+ Fmt f;
+ int n;
+
+ if(fmtstrinit(&f) < 0)
+ return nil;
+ f.args = args;
+ n = dofmt(&f, fmt);
+ if(n < 0)
+ return nil;
+ *(char*)f.to = '\0';
+ return f.start;
+}
diff --git a/lib9/vsnprint.c b/lib9/vsnprint.c
new file mode 100644
index 00000000..841d142c
--- /dev/null
+++ b/lib9/vsnprint.c
@@ -0,0 +1,34 @@
+/*
+ * The authors of this software are Rob Pike and Ken Thompson.
+ * Copyright (c) 2002 by Lucent Technologies.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ */
+#include "lib9.h"
+
+int
+vsnprint(char *buf, int len, char *fmt, va_list args)
+{
+ Fmt f;
+
+ if(len <= 0)
+ return -1;
+ f.runes = 0;
+ f.start = buf;
+ f.to = buf;
+ f.stop = buf + len - 1;
+ f.flush = nil;
+ f.farg = nil;
+ f.nfmt = 0;
+ f.args = args;
+ dofmt(&f, fmt);
+ *(char*)f.to = '\0';
+ return (char*)f.to - buf;
+}