summaryrefslogtreecommitdiff
path: root/sys/src/libstdio
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/libstdio
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/libstdio')
-rwxr-xr-xsys/src/libstdio/Stdio.h109
-rwxr-xr-xsys/src/libstdio/_IO_getc.c26
-rwxr-xr-xsys/src/libstdio/_IO_putc.c105
-rwxr-xr-xsys/src/libstdio/clearerr.c14
-rwxr-xr-xsys/src/libstdio/dtoa.c1293
-rwxr-xr-xsys/src/libstdio/fclose.c14
-rwxr-xr-xsys/src/libstdio/fdopen.c37
-rwxr-xr-xsys/src/libstdio/feof.c7
-rwxr-xr-xsys/src/libstdio/ferror.c7
-rwxr-xr-xsys/src/libstdio/fflush.c52
-rwxr-xr-xsys/src/libstdio/fgetc.c7
-rwxr-xr-xsys/src/libstdio/fgetpos.c8
-rwxr-xr-xsys/src/libstdio/fgets.c18
-rwxr-xr-xsys/src/libstdio/fileno.c10
-rwxr-xr-xsys/src/libstdio/fopen.c16
-rwxr-xr-xsys/src/libstdio/fprintf.c12
-rwxr-xr-xsys/src/libstdio/fputc.c7
-rwxr-xr-xsys/src/libstdio/fputs.c8
-rwxr-xr-xsys/src/libstdio/fread.c41
-rwxr-xr-xsys/src/libstdio/freopen.c56
-rwxr-xr-xsys/src/libstdio/fscanf.c12
-rwxr-xr-xsys/src/libstdio/fseek.c23
-rwxr-xr-xsys/src/libstdio/fseeko.c23
-rwxr-xr-xsys/src/libstdio/fsetpos.c7
-rwxr-xr-xsys/src/libstdio/ftell.c16
-rwxr-xr-xsys/src/libstdio/ftello.c16
-rwxr-xr-xsys/src/libstdio/fwrite.c51
-rwxr-xr-xsys/src/libstdio/getc.c8
-rwxr-xr-xsys/src/libstdio/gets.c17
-rwxr-xr-xsys/src/libstdio/iolib.h44
-rwxr-xr-xsys/src/libstdio/mkfile69
-rwxr-xr-xsys/src/libstdio/printf.c12
-rwxr-xr-xsys/src/libstdio/putc.c8
-rwxr-xr-xsys/src/libstdio/puts.c9
-rwxr-xr-xsys/src/libstdio/rewind.c7
-rwxr-xr-xsys/src/libstdio/scanf.c12
-rwxr-xr-xsys/src/libstdio/sclose.c34
-rwxr-xr-xsys/src/libstdio/setbuf.c17
-rwxr-xr-xsys/src/libstdio/setvbuf.c50
-rwxr-xr-xsys/src/libstdio/snprintf.c17
-rwxr-xr-xsys/src/libstdio/sopenr.c22
-rwxr-xr-xsys/src/libstdio/sopenw.c19
-rwxr-xr-xsys/src/libstdio/sprintf.c17
-rwxr-xr-xsys/src/libstdio/sscanf.c14
-rwxr-xr-xsys/src/libstdio/tmpfile.c48
-rwxr-xr-xsys/src/libstdio/tmpnam.c20
-rwxr-xr-xsys/src/libstdio/ungetc.c28
-rwxr-xr-xsys/src/libstdio/vfprintf.c558
-rwxr-xr-xsys/src/libstdio/vfscanf.c364
-rwxr-xr-xsys/src/libstdio/vprintf.c7
-rwxr-xr-xsys/src/libstdio/vsnprintf.c14
-rwxr-xr-xsys/src/libstdio/vsprintf.c14
52 files changed, 3424 insertions, 0 deletions
diff --git a/sys/src/libstdio/Stdio.h b/sys/src/libstdio/Stdio.h
new file mode 100755
index 000000000..909c90705
--- /dev/null
+++ b/sys/src/libstdio/Stdio.h
@@ -0,0 +1,109 @@
+/*
+ * pANS stdio.h
+ */
+/*
+ * According to X3J11, there is only one i/o buffer
+ * and it must not be occupied by both input and output data.
+ * If rp<wp, we must have state==RD and
+ * if wp<rp, we must have state==WR, so that getc and putc work correctly.
+ * On open, rp, wp and buf are set to 0, so first getc or putc will call _IO_getc
+ * or _IO_putc, which will allocate the buffer.
+ * If setvbuf(., ., _IONBF, .) is called, bufl is set to 0 and
+ * buf, rp and wp are pointed at unbuf.
+ * If setvbuf(., ., _IOLBF, .) is called, _IO_putc leaves wp and rp pointed at the
+ * end of the buffer so that it can be called on each putc to check whether it's got
+ * a newline. This nonsense is in order to avoid impacting performance of the other
+ * buffering modes more than necessary -- putting the test in putc adds many
+ * instructions that are wasted in non-_IOLBF mode:
+ * #define putc(c, f) (_IO_ctmp=(c),\
+ * (f)->wp>=(f)->rp || (f)->flags&LINEBUF && _IO_ctmp=='\n'\
+ * ?_IO_putc(_IO_ctmp, f)\
+ * :*(f)->wp++=_IO_ctmp)
+ *
+ */
+typedef struct{
+ int fd; /* UNIX file pointer */
+ char flags; /* bits for must free buffer on close, line-buffered */
+ char state; /* last operation was read, write, position, error, eof */
+ char *buf; /* pointer to i/o buffer */
+ char *rp; /* read pointer (or write end-of-buffer) */
+ char *wp; /* write pointer (or read end-of-buffer) */
+ char *lp; /* actual write pointer used when line-buffering */
+ long bufl; /* actual length of buffer */
+ char unbuf[1]; /* tiny buffer for unbuffered io (used for ungetc?) */
+}FILE;
+typedef long fpos_t;
+#ifndef NULL
+#define NULL 0
+#endif
+/*
+ * Third arg of setvbuf
+ */
+#define _IOFBF 1 /* block-buffered */
+#define _IOLBF 2 /* line-buffered */
+#define _IONBF 3 /* unbuffered */
+#define BUFSIZ 4096 /* size of setbuf buffer */
+#define EOF (-1) /* returned on end of file */
+#define FOPEN_MAX 128 /* max files open */
+#define FILENAME_MAX BUFSIZ /* silly filename length */
+#define L_tmpnam 20 /* sizeof "/tmp/abcdefghij9999 */
+#define SEEK_CUR 1
+#define SEEK_END 2
+#define SEEK_SET 0
+#define TMP_MAX 64 /* very hard to set correctly */
+#define stderr (&_IO_stream[2])
+#define stdin (&_IO_stream[0])
+#define stdout (&_IO_stream[1])
+#define _IO_CHMASK 0377 /* mask for 8 bit characters */
+FILE *tmpfile(void);
+char *tmpnam(char *);
+int fclose(FILE *);
+int fflush(FILE *);
+FILE *fopen(const char *, const char *);
+FILE *freopen(const char *, const char *, FILE *);
+void setbuf(FILE *, char *);
+int setvbuf(FILE *, char *, int, long);
+int fprintf(FILE *, const char *, ...);
+int fscanf(FILE *, const char *, ...);
+int printf(const char *, ...);
+int scanf(const char *, ...);
+int sprintf(char *, const char *, ...);
+int sscanf(const char *, const char *, ...);
+int vfprintf(FILE *, const char *, va_list);
+int vprintf(const char *, va_list);
+int vsprintf(char *, const char *, va_list);
+int vfscanf(FILE *, const char *, va_list);
+int fgetc(FILE *);
+char *fgets(char *, int, FILE *);
+int fputc(int, FILE *);
+int fputs(const char *, FILE *);
+int getc(FILE *);
+#define getc(f) ((f)->rp>=(f)->wp?_IO_getc(f):*(f)->rp++&_IO_CHMASK)
+int _IO_getc(FILE *f);
+int getchar(void);
+#define getchar() getc(stdin)
+char *gets(char *);
+int putc(int, FILE *);
+#define putc(c, f) ((f)->wp>=(f)->rp?_IO_putc(c, f):(*(f)->wp++=c)&_IO_CHMASK)
+int _IO_putc(int, FILE *);
+int putchar(int);
+#define putchar(c) putc(c, stdout)
+int puts(const char *);
+int ungetc(int, FILE *);
+long fread(void *, long, long, FILE *);
+long fwrite(const void *, long, long, FILE *);
+int fgetpos(FILE *, fpos_t *);
+int fseek(FILE *, long int, int);
+int fsetpos(FILE *, const fpos_t *);
+long int ftell(FILE *);
+void rewind(FILE *);
+void clearerr(FILE *);
+int feof(FILE *);
+int ferror(FILE *);
+void perror(const char *);
+extern FILE _IO_stream[FOPEN_MAX];
+FILE *sopenr(const char *);
+FILE *sopenw(void);
+char *sclose(FILE *);
+char *dtoa(double, int, int, int*, int*, char**);
+void freedtoa(char*);
diff --git a/sys/src/libstdio/_IO_getc.c b/sys/src/libstdio/_IO_getc.c
new file mode 100755
index 000000000..b6ab110c0
--- /dev/null
+++ b/sys/src/libstdio/_IO_getc.c
@@ -0,0 +1,26 @@
+/*
+ * pANS stdio -- _IO_getc
+ */
+#include "iolib.h"
+int _IO_getc(FILE *f){
+ int cnt;
+ switch(f->state){
+ default: /* CLOSED, WR, ERR, EOF */
+ return EOF;
+ case OPEN:
+ _IO_setvbuf(f);
+ case RDWR:
+ case RD:
+ if(f->flags&STRING) return EOF;
+ cnt=read(f->fd, f->buf, f->buf==f->unbuf?1:f->bufl);
+ switch(cnt){
+ case -1: f->state=ERR; return EOF;
+ case 0: f->state=END; return EOF;
+ default:
+ f->state=RD;
+ f->rp=f->buf;
+ f->wp=f->buf+cnt;
+ return (*f->rp++)&_IO_CHMASK;
+ }
+ }
+}
diff --git a/sys/src/libstdio/_IO_putc.c b/sys/src/libstdio/_IO_putc.c
new file mode 100755
index 000000000..4c3a74238
--- /dev/null
+++ b/sys/src/libstdio/_IO_putc.c
@@ -0,0 +1,105 @@
+/*
+ * pANS stdio -- _IO_putc, _IO_cleanup
+ */
+#include "iolib.h"
+void _IO_cleanup(void){
+ fflush(NULL);
+}
+/*
+ * Look this over for simplification
+ */
+int _IO_putc(int c, FILE *f){
+ int cnt;
+ static int first=1;
+ switch(f->state){
+ case RD:
+ f->state=ERR;
+ case ERR:
+ case CLOSED:
+ return EOF;
+ case OPEN:
+ _IO_setvbuf(f);
+ /* fall through */
+ case RDWR:
+ case END:
+ f->rp=f->buf+f->bufl;
+ if(f->flags&LINEBUF){
+ f->wp=f->rp;
+ f->lp=f->buf;
+ }
+ else
+ f->wp=f->buf;
+ break;
+ }
+ if(first){
+ atexit(_IO_cleanup);
+ first=0;
+ }
+ if(f->flags&STRING){
+ f->rp=f->buf+f->bufl;
+ if(f->wp==f->rp){
+ if(f->flags&BALLOC)
+ f->buf=realloc(f->buf, f->bufl+BUFSIZ);
+ else{
+ f->state=ERR;
+ return EOF;
+ }
+ if(f->buf==NULL){
+ f->state=ERR;
+ return EOF;
+ }
+ f->rp=f->buf+f->bufl;
+ f->bufl+=BUFSIZ;
+ }
+ *f->wp++=c;
+ }
+ else if(f->flags&LINEBUF){
+ if(f->lp==f->rp){
+ cnt=f->lp-f->buf;
+ if(f->flags&APPEND) seek(f->fd, 0L, 2);
+ if(cnt!=0 && write(f->fd, f->buf, cnt)!=cnt){
+ f->state=ERR;
+ return EOF;
+ }
+ f->lp=f->buf;
+ }
+ *f->lp++=c;
+ if(c=='\n'){
+ cnt=f->lp-f->buf;
+ if(f->flags&APPEND) seek(f->fd, 0L, 2);
+ if(cnt!=0 && write(f->fd, f->buf, cnt)!=cnt){
+ f->state=ERR;
+ return EOF;
+ }
+ f->lp=f->buf;
+ }
+ }
+ else if(f->buf==f->unbuf){
+ f->unbuf[0]=c;
+ if(f->flags&APPEND) seek(f->fd, 0L, 2);
+ if(write(f->fd, f->buf, 1)!=1){
+ f->state=ERR;
+ return EOF;
+ }
+ }
+ else{
+ if(f->wp==f->rp){
+ cnt=f->wp-f->buf;
+ if(f->flags&APPEND) seek(f->fd, 0L, 2);
+ if(cnt!=0 && write(f->fd, f->buf, cnt)!=cnt){
+ f->state=ERR;
+ return EOF;
+ }
+ f->wp=f->buf;
+ f->rp=f->buf+f->bufl;
+ }
+ *f->wp++=c;
+ }
+ f->state=WR;
+ /*
+ * Make sure EOF looks different from putc(-1)
+ * Should be able to cast to unsigned char, but
+ * there's a vc bug preventing that from working
+ */
+ return c&0xff;
+}
diff --git a/sys/src/libstdio/clearerr.c b/sys/src/libstdio/clearerr.c
new file mode 100755
index 000000000..904e33581
--- /dev/null
+++ b/sys/src/libstdio/clearerr.c
@@ -0,0 +1,14 @@
+/*
+ * pANS stdio -- clearerr
+ */
+#include "iolib.h"
+void clearerr(FILE *f){
+ switch(f->state){
+ case ERR:
+ f->state=f->buf?RDWR:OPEN;
+ break;
+ case END:
+ f->state=RDWR;
+ break;
+ }
+}
diff --git a/sys/src/libstdio/dtoa.c b/sys/src/libstdio/dtoa.c
new file mode 100755
index 000000000..26441ded7
--- /dev/null
+++ b/sys/src/libstdio/dtoa.c
@@ -0,0 +1,1293 @@
+/* derived from /netlib/fp/dtoa.c assuming IEEE, Standard C */
+/* kudos to dmg@bell-labs.com, gripes to ehg@bell-labs.com */
+
+/* Let x be the exact mathematical number defined by a decimal
+ * string s. Then atof(s) is the round-nearest-even IEEE
+ * floating point value.
+ * Let y be an IEEE floating point value and let s be the string
+ * printed as %.17g. Then atof(s) is exactly y.
+ */
+#include <u.h>
+#include <libc.h>
+
+static Lock _dtoalk[2];
+#define ACQUIRE_DTOA_LOCK(n) lock(&_dtoalk[n])
+#define FREE_DTOA_LOCK(n) unlock(&_dtoalk[n])
+#define PRIVATE_mem ((2000+sizeof(double)-1)/sizeof(double))
+static double private_mem[PRIVATE_mem], *pmem_next = private_mem;
+#define FLT_ROUNDS 1
+#define DBL_DIG 15
+#define DBL_MAX_10_EXP 308
+#define DBL_MAX_EXP 1024
+#define FLT_RADIX 2
+#define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff)
+#define fpword0(x) ((FPdbleword*)&x)->hi
+#define fpword1(x) ((FPdbleword*)&x)->lo
+/* Ten_pmax = floor(P*log(2)/log(5)) */
+/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */
+/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */
+/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */
+
+#define Exp_shift 20
+#define Exp_shift1 20
+#define Exp_msk1 0x100000
+#define Exp_msk11 0x100000
+#define Exp_mask 0x7ff00000
+#define P 53
+#define Bias 1023
+#define Emin (-1022)
+#define Exp_1 0x3ff00000
+#define Exp_11 0x3ff00000
+#define Ebits 11
+#define Frac_mask 0xfffff
+#define Frac_mask1 0xfffff
+#define Ten_pmax 22
+#define Bletch 0x10
+#define Bndry_mask 0xfffff
+#define Bndry_mask1 0xfffff
+#define LSB 1
+#define Sign_bit 0x80000000
+#define Log2P 1
+#define Tiny0 0
+#define Tiny1 1
+#define Quick_max 14
+#define Int_max 14
+#define Avoid_Underflow
+
+#define rounded_product(a,b) a *= b
+#define rounded_quotient(a,b) a /= b
+
+#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1))
+#define Big1 0xffffffff
+
+#define FFFFFFFF 0xffffffffUL
+
+#undef ULint
+
+#define Kmax 15
+
+struct
+Bigint {
+ struct Bigint *next;
+ int k, maxwds, sign, wds;
+ unsigned int x[1];
+};
+
+typedef struct Bigint Bigint;
+
+static Bigint *freelist[Kmax+1];
+
+static Bigint *
+Balloc(int k)
+{
+ int x;
+ Bigint * rv;
+ unsigned int len;
+
+ ACQUIRE_DTOA_LOCK(0);
+ if (rv = freelist[k]) {
+ freelist[k] = rv->next;
+ } else {
+ x = 1 << k;
+ len = (sizeof(Bigint) + (x - 1) * sizeof(unsigned int) + sizeof(double) -1)
+ / sizeof(double);
+ if (pmem_next - private_mem + len <= PRIVATE_mem) {
+ rv = (Bigint * )pmem_next;
+ pmem_next += len;
+ } else
+ rv = (Bigint * )malloc(len * sizeof(double));
+ rv->k = k;
+ rv->maxwds = x;
+ }
+ FREE_DTOA_LOCK(0);
+ rv->sign = rv->wds = 0;
+ return rv;
+}
+
+static void
+Bfree(Bigint *v)
+{
+ if (v) {
+ ACQUIRE_DTOA_LOCK(0);
+ v->next = freelist[v->k];
+ freelist[v->k] = v;
+ FREE_DTOA_LOCK(0);
+ }
+}
+
+#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \
+y->wds*sizeof(int) + 2*sizeof(int))
+
+static Bigint *
+multadd(Bigint *b, int m, int a) /* multiply by m and add a */
+{
+ int i, wds;
+ unsigned int carry, *x, y;
+ unsigned int xi, z;
+ Bigint * b1;
+
+ wds = b->wds;
+ x = b->x;
+ i = 0;
+ carry = a;
+ do {
+ xi = *x;
+ y = (xi & 0xffff) * m + carry;
+ z = (xi >> 16) * m + (y >> 16);
+ carry = z >> 16;
+ *x++ = (z << 16) + (y & 0xffff);
+ } while (++i < wds);
+ if (carry) {
+ if (wds >= b->maxwds) {
+ b1 = Balloc(b->k + 1);
+ Bcopy(b1, b);
+ Bfree(b);
+ b = b1;
+ }
+ b->x[wds++] = carry;
+ b->wds = wds;
+ }
+ return b;
+}
+
+static Bigint *
+s2b(const char *s, int nd0, int nd, unsigned int y9)
+{
+ Bigint * b;
+ int i, k;
+ int x, y;
+
+ x = (nd + 8) / 9;
+ for (k = 0, y = 1; x > y; y <<= 1, k++)
+ ;
+ b = Balloc(k);
+ b->x[0] = y9;
+ b->wds = 1;
+
+ i = 9;
+ if (9 < nd0) {
+ s += 9;
+ do
+ b = multadd(b, 10, *s++ - '0');
+ while (++i < nd0);
+ s++;
+ } else
+ s += 10;
+ for (; i < nd; i++)
+ b = multadd(b, 10, *s++ - '0');
+ return b;
+}
+
+static int
+hi0bits(register unsigned int x)
+{
+ register int k = 0;
+
+ if (!(x & 0xffff0000)) {
+ k = 16;
+ x <<= 16;
+ }
+ if (!(x & 0xff000000)) {
+ k += 8;
+ x <<= 8;
+ }
+ if (!(x & 0xf0000000)) {
+ k += 4;
+ x <<= 4;
+ }
+ if (!(x & 0xc0000000)) {
+ k += 2;
+ x <<= 2;
+ }
+ if (!(x & 0x80000000)) {
+ k++;
+ if (!(x & 0x40000000))
+ return 32;
+ }
+ return k;
+}
+
+static int
+lo0bits(unsigned int *y)
+{
+ register int k;
+ register unsigned int x = *y;
+
+ if (x & 7) {
+ if (x & 1)
+ return 0;
+ if (x & 2) {
+ *y = x >> 1;
+ return 1;
+ }
+ *y = x >> 2;
+ return 2;
+ }
+ k = 0;
+ if (!(x & 0xffff)) {
+ k = 16;
+ x >>= 16;
+ }
+ if (!(x & 0xff)) {
+ k += 8;
+ x >>= 8;
+ }
+ if (!(x & 0xf)) {
+ k += 4;
+ x >>= 4;
+ }
+ if (!(x & 0x3)) {
+ k += 2;
+ x >>= 2;
+ }
+ if (!(x & 1)) {
+ k++;
+ x >>= 1;
+ if (!x & 1)
+ return 32;
+ }
+ *y = x;
+ return k;
+}
+
+static Bigint *
+i2b(int i)
+{
+ Bigint * b;
+
+ b = Balloc(1);
+ b->x[0] = i;
+ b->wds = 1;
+ return b;
+}
+
+static Bigint *
+mult(Bigint *a, Bigint *b)
+{
+ Bigint * c;
+ int k, wa, wb, wc;
+ unsigned int * x, *xa, *xae, *xb, *xbe, *xc, *xc0;
+ unsigned int y;
+ unsigned int carry, z;
+ unsigned int z2;
+
+ if (a->wds < b->wds) {
+ c = a;
+ a = b;
+ b = c;
+ }
+ k = a->k;
+ wa = a->wds;
+ wb = b->wds;
+ wc = wa + wb;
+ if (wc > a->maxwds)
+ k++;
+ c = Balloc(k);
+ for (x = c->x, xa = x + wc; x < xa; x++)
+ *x = 0;
+ xa = a->x;
+ xae = xa + wa;
+ xb = b->x;
+ xbe = xb + wb;
+ xc0 = c->x;
+ for (; xb < xbe; xb++, xc0++) {
+ if (y = *xb & 0xffff) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ do {
+ z = (*x & 0xffff) * y + (*xc & 0xffff) + carry;
+ carry = z >> 16;
+ z2 = (*x++ >> 16) * y + (*xc >> 16) + carry;
+ carry = z2 >> 16;
+ Storeinc(xc, z2, z);
+ } while (x < xae);
+ *xc = carry;
+ }
+ if (y = *xb >> 16) {
+ x = xa;
+ xc = xc0;
+ carry = 0;
+ z2 = *xc;
+ do {
+ z = (*x & 0xffff) * y + (*xc >> 16) + carry;
+ carry = z >> 16;
+ Storeinc(xc, z, z2);
+ z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry;
+ carry = z2 >> 16;
+ } while (x < xae);
+ *xc = z2;
+ }
+ }
+ for (xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc)
+ ;
+ c->wds = wc;
+ return c;
+}
+
+static Bigint *p5s;
+
+static Bigint *
+pow5mult(Bigint *b, int k)
+{
+ Bigint * b1, *p5, *p51;
+ int i;
+ static int p05[3] = {
+ 5, 25, 125 };
+
+ if (i = k & 3)
+ b = multadd(b, p05[i-1], 0);
+
+ if (!(k >>= 2))
+ return b;
+ if (!(p5 = p5s)) {
+ /* first time */
+ ACQUIRE_DTOA_LOCK(1);
+ if (!(p5 = p5s)) {
+ p5 = p5s = i2b(625);
+ p5->next = 0;
+ }
+ FREE_DTOA_LOCK(1);
+ }
+ for (; ; ) {
+ if (k & 1) {
+ b1 = mult(b, p5);
+ Bfree(b);
+ b = b1;
+ }
+ if (!(k >>= 1))
+ break;
+ if (!(p51 = p5->next)) {
+ ACQUIRE_DTOA_LOCK(1);
+ if (!(p51 = p5->next)) {
+ p51 = p5->next = mult(p5, p5);
+ p51->next = 0;
+ }
+ FREE_DTOA_LOCK(1);
+ }
+ p5 = p51;
+ }
+ return b;
+}
+
+static Bigint *
+lshift(Bigint *b, int k)
+{
+ int i, k1, n, n1;
+ Bigint * b1;
+ unsigned int * x, *x1, *xe, z;
+
+ n = k >> 5;
+ k1 = b->k;
+ n1 = n + b->wds + 1;
+ for (i = b->maxwds; n1 > i; i <<= 1)
+ k1++;
+ b1 = Balloc(k1);
+ x1 = b1->x;
+ for (i = 0; i < n; i++)
+ *x1++ = 0;
+ x = b->x;
+ xe = x + b->wds;
+ if (k &= 0x1f) {
+ k1 = 32 - k;
+ z = 0;
+ do {
+ *x1++ = *x << k | z;
+ z = *x++ >> k1;
+ } while (x < xe);
+ if (*x1 = z)
+ ++n1;
+ } else
+ do
+ *x1++ = *x++;
+ while (x < xe);
+ b1->wds = n1 - 1;
+ Bfree(b);
+ return b1;
+}
+
+static int
+cmp(Bigint *a, Bigint *b)
+{
+ unsigned int * xa, *xa0, *xb, *xb0;
+ int i, j;
+
+ i = a->wds;
+ j = b->wds;
+ if (i -= j)
+ return i;
+ xa0 = a->x;
+ xa = xa0 + j;
+ xb0 = b->x;
+ xb = xb0 + j;
+ for (; ; ) {
+ if (*--xa != *--xb)
+ return * xa < *xb ? -1 : 1;
+ if (xa <= xa0)
+ break;
+ }
+ return 0;
+}
+
+static Bigint *
+diff(Bigint *a, Bigint *b)
+{
+ Bigint * c;
+ int i, wa, wb;
+ unsigned int * xa, *xae, *xb, *xbe, *xc;
+ unsigned int borrow, y;
+ unsigned int z;
+
+ i = cmp(a, b);
+ if (!i) {
+ c = Balloc(0);
+ c->wds = 1;
+ c->x[0] = 0;
+ return c;
+ }
+ if (i < 0) {
+ c = a;
+ a = b;
+ b = c;
+ i = 1;
+ } else
+ i = 0;
+ c = Balloc(a->k);
+ c->sign = i;
+ wa = a->wds;
+ xa = a->x;
+ xae = xa + wa;
+ wb = b->wds;
+ xb = b->x;
+ xbe = xb + wb;
+ xc = c->x;
+ borrow = 0;
+ do {
+ y = (*xa & 0xffff) - (*xb & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*xa++ >> 16) - (*xb++ >> 16) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(xc, z, y);
+ } while (xb < xbe);
+ while (xa < xae) {
+ y = (*xa & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*xa++ >> 16) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(xc, z, y);
+ }
+ while (!*--xc)
+ wa--;
+ c->wds = wa;
+ return c;
+}
+
+static double
+ulp(double x)
+{
+ register int L;
+ double a;
+
+ L = (fpword0(x) & Exp_mask) - (P - 1) * Exp_msk1;
+ fpword0(a) = L;
+ fpword1(a) = 0;
+ return a;
+}
+
+static double
+b2d(Bigint *a, int *e)
+{
+ unsigned int * xa, *xa0, w, y, z;
+ int k;
+ double d;
+#define d0 fpword0(d)
+#define d1 fpword1(d)
+
+ xa0 = a->x;
+ xa = xa0 + a->wds;
+ y = *--xa;
+ k = hi0bits(y);
+ *e = 32 - k;
+ if (k < Ebits) {
+ d0 = Exp_1 | y >> Ebits - k;
+ w = xa > xa0 ? *--xa : 0;
+ d1 = y << (32 - Ebits) + k | w >> Ebits - k;
+ goto ret_d;
+ }
+ z = xa > xa0 ? *--xa : 0;
+ if (k -= Ebits) {
+ d0 = Exp_1 | y << k | z >> 32 - k;
+ y = xa > xa0 ? *--xa : 0;
+ d1 = z << k | y >> 32 - k;
+ } else {
+ d0 = Exp_1 | y;
+ d1 = z;
+ }
+ret_d:
+#undef d0
+#undef d1
+ return d;
+}
+
+static Bigint *
+d2b(double d, int *e, int *bits)
+{
+ Bigint * b;
+ int de, i, k;
+ unsigned int * x, y, z;
+#define d0 fpword0(d)
+#define d1 fpword1(d)
+
+ b = Balloc(1);
+ x = b->x;
+
+ z = d0 & Frac_mask;
+ d0 &= 0x7fffffff; /* clear sign bit, which we ignore */
+ de = (int)(d0 >> Exp_shift);
+ z |= Exp_msk11;
+ if (y = d1) {
+ if (k = lo0bits(&y)) {
+ x[0] = y | z << 32 - k;
+ z >>= k;
+ } else
+ x[0] = y;
+ i = b->wds = (x[1] = z) ? 2 : 1;
+ } else {
+ k = lo0bits(&z);
+ x[0] = z;
+ i = b->wds = 1;
+ k += 32;
+ }
+ *e = de - Bias - (P - 1) + k;
+ *bits = P - k;
+ return b;
+}
+
+#undef d0
+#undef d1
+
+static double
+ratio(Bigint *a, Bigint *b)
+{
+ double da, db;
+ int k, ka, kb;
+
+ da = b2d(a, &ka);
+ db = b2d(b, &kb);
+ k = ka - kb + 32 * (a->wds - b->wds);
+ if (k > 0)
+ fpword0(da) += k * Exp_msk1;
+ else {
+ k = -k;
+ fpword0(db) += k * Exp_msk1;
+ }
+ return da / db;
+}
+
+static const double
+tens[] = {
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+ 1e20, 1e21, 1e22
+};
+
+static const double
+bigtens[] = {
+ 1e16, 1e32, 1e64, 1e128, 1e256 };
+
+static const double tinytens[] = {
+ 1e-16, 1e-32, 1e-64, 1e-128,
+ 9007199254740992.e-256
+};
+
+/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */
+/* flag unnecessarily. It leads to a song and dance at the end of strtod. */
+#define Scale_Bit 0x10
+#define n_bigtens 5
+
+#define NAN_WORD0 0x7ff80000
+
+#define NAN_WORD1 0
+
+static int
+match(const char **sp, char *t)
+{
+ int c, d;
+ const char * s = *sp;
+
+ while (d = *t++) {
+ if ((c = *++s) >= 'A' && c <= 'Z')
+ c += 'a' - 'A';
+ if (c != d)
+ return 0;
+ }
+ *sp = s + 1;
+ return 1;
+}
+
+static void
+gethex(double *rvp, const char **sp)
+{
+ unsigned int c, x[2];
+ const char * s;
+ int havedig, udx0, xshift;
+
+ x[0] = x[1] = 0;
+ havedig = xshift = 0;
+ udx0 = 1;
+ s = *sp;
+ while (c = *(const unsigned char * )++s) {
+ if (c >= '0' && c <= '9')
+ c -= '0';
+ else if (c >= 'a' && c <= 'f')
+ c += 10 - 'a';
+ else if (c >= 'A' && c <= 'F')
+ c += 10 - 'A';
+ else if (c <= ' ') {
+ if (udx0 && havedig) {
+ udx0 = 0;
+ xshift = 1;
+ }
+ continue;
+ } else if (/*(*/ c == ')') {
+ *sp = s + 1;
+ break;
+ } else
+ return; /* invalid form: don't change *sp */
+ havedig = 1;
+ if (xshift) {
+ xshift = 0;
+ x[0] = x[1];
+ x[1] = 0;
+ }
+ if (udx0)
+ x[0] = (x[0] << 4) | (x[1] >> 28);
+ x[1] = (x[1] << 4) | c;
+ }
+ if ((x[0] &= 0xfffff) || x[1]) {
+ fpword0(*rvp) = Exp_mask | x[0];
+ fpword1(*rvp) = x[1];
+ }
+}
+
+static int
+quorem(Bigint *b, Bigint *S)
+{
+ int n;
+ unsigned int * bx, *bxe, q, *sx, *sxe;
+ unsigned int borrow, carry, y, ys;
+ unsigned int si, z, zs;
+
+ n = S->wds;
+ if (b->wds < n)
+ return 0;
+ sx = S->x;
+ sxe = sx + --n;
+ bx = b->x;
+ bxe = bx + n;
+ q = *bxe / (*sxe + 1); /* ensure q <= true quotient */
+ if (q) {
+ borrow = 0;
+ carry = 0;
+ do {
+ si = *sx++;
+ ys = (si & 0xffff) * q + carry;
+ zs = (si >> 16) * q + (ys >> 16);
+ carry = zs >> 16;
+ y = (*bx & 0xffff) - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*bx >> 16) - (zs & 0xffff) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(bx, z, y);
+ } while (sx <= sxe);
+ if (!*bxe) {
+ bx = b->x;
+ while (--bxe > bx && !*bxe)
+ --n;
+ b->wds = n;
+ }
+ }
+ if (cmp(b, S) >= 0) {
+ q++;
+ borrow = 0;
+ carry = 0;
+ bx = b->x;
+ sx = S->x;
+ do {
+ si = *sx++;
+ ys = (si & 0xffff) + carry;
+ zs = (si >> 16) + (ys >> 16);
+ carry = zs >> 16;
+ y = (*bx & 0xffff) - (ys & 0xffff) - borrow;
+ borrow = (y & 0x10000) >> 16;
+ z = (*bx >> 16) - (zs & 0xffff) - borrow;
+ borrow = (z & 0x10000) >> 16;
+ Storeinc(bx, z, y);
+ } while (sx <= sxe);
+ bx = b->x;
+ bxe = bx + n;
+ if (!*bxe) {
+ while (--bxe > bx && !*bxe)
+ --n;
+ b->wds = n;
+ }
+ }
+ return q;
+}
+
+static char *
+rv_alloc(int i)
+{
+ int j, k, *r;
+
+ j = sizeof(unsigned int);
+ for (k = 0;
+ sizeof(Bigint) - sizeof(unsigned int) - sizeof(int) + j <= i;
+ j <<= 1)
+ k++;
+ r = (int * )Balloc(k);
+ *r = k;
+ return
+ (char *)(r + 1);
+}
+
+static char *
+nrv_alloc(char *s, char **rve, int n)
+{
+ char *rv, *t;
+
+ t = rv = rv_alloc(n);
+ while (*t = *s++)
+ t++;
+ if (rve)
+ *rve = t;
+ return rv;
+}
+
+/* freedtoa(s) must be used to free values s returned by dtoa
+ * when MULTIPLE_THREADS is #defined. It should be used in all cases,
+ * but for consistency with earlier versions of dtoa, it is optional
+ * when MULTIPLE_THREADS is not defined.
+ */
+
+void
+freedtoa(char *s)
+{
+ Bigint * b = (Bigint * )((int *)s - 1);
+ b->maxwds = 1 << (b->k = *(int * )b);
+ Bfree(b);
+}
+
+/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string.
+ *
+ * Inspired by "How to Print Floating-Point Numbers Accurately" by
+ * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101].
+ *
+ * Modifications:
+ * 1. Rather than iterating, we use a simple numeric overestimate
+ * to determine k = floor(log10(d)). We scale relevant
+ * quantities using O(log2(k)) rather than O(k) multiplications.
+ * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't
+ * try to generate digits strictly left to right. Instead, we
+ * compute with fewer bits and propagate the carry if necessary
+ * when rounding the final digit up. This is often faster.
+ * 3. Under the assumption that input will be rounded nearest,
+ * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22.
+ * That is, we allow equality in stopping tests when the
+ * round-nearest rule will give the same floating-point value
+ * as would satisfaction of the stopping test with strict
+ * inequality.
+ * 4. We remove common factors of powers of 2 from relevant
+ * quantities.
+ * 5. When converting floating-point integers less than 1e16,
+ * we use floating-point arithmetic rather than resorting
+ * to multiple-precision integers.
+ * 6. When asked to produce fewer than 15 digits, we first try
+ * to get by with floating-point arithmetic; we resort to
+ * multiple-precision integer arithmetic only if we cannot
+ * guarantee that the floating-point calculation has given
+ * the correctly rounded result. For k requested digits and
+ * "uniformly" distributed input, the probability is
+ * something like 10^(k-15) that we must resort to the int
+ * calculation.
+ */
+
+char *
+dtoa(double d, int mode, int ndigits, int *decpt, int *sign, char **rve)
+{
+ /* Arguments ndigits, decpt, sign are similar to those
+ of ecvt and fcvt; trailing zeros are suppressed from
+ the returned string. If not null, *rve is set to point
+ to the end of the return value. If d is +-Infinity or NaN,
+ then *decpt is set to 9999.
+
+ mode:
+ 0 ==> shortest string that yields d when read in
+ and rounded to nearest.
+ 1 ==> like 0, but with Steele & White stopping rule;
+ e.g. with IEEE P754 arithmetic , mode 0 gives
+ 1e23 whereas mode 1 gives 9.999999999999999e22.
+ 2 ==> max(1,ndigits) significant digits. This gives a
+ return value similar to that of ecvt, except
+ that trailing zeros are suppressed.
+ 3 ==> through ndigits past the decimal point. This
+ gives a return value similar to that from fcvt,
+ except that trailing zeros are suppressed, and
+ ndigits can be negative.
+ 4-9 should give the same return values as 2-3, i.e.,
+ 4 <= mode <= 9 ==> same return as mode
+ 2 + (mode & 1). These modes are mainly for
+ debugging; often they run slower but sometimes
+ faster than modes 2-3.
+ 4,5,8,9 ==> left-to-right digit generation.
+ 6-9 ==> don't try fast floating-point estimate
+ (if applicable).
+
+ Values of mode other than 0-9 are treated as mode 0.
+
+ Sufficient space is allocated to the return value
+ to hold the suppressed trailing zeros.
+ */
+
+ int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1,
+ j, j1, k, k0, k_check, leftright, m2, m5, s2, s5,
+ spec_case, try_quick;
+ int L;
+ Bigint * b, *b1, *delta, *mlo=nil, *mhi, *S;
+ double d2, ds, eps;
+ char *s, *s0;
+
+ if (fpword0(d) & Sign_bit) {
+ /* set sign for everything, including 0's and NaNs */
+ *sign = 1;
+ fpword0(d) &= ~Sign_bit; /* clear sign bit */
+ } else
+ *sign = 0;
+
+ if ((fpword0(d) & Exp_mask) == Exp_mask) {
+ /* Infinity or NaN */
+ *decpt = 9999;
+ if (!fpword1(d) && !(fpword0(d) & 0xfffff))
+ return nrv_alloc("Infinity", rve, 8);
+ return nrv_alloc("NaN", rve, 3);
+ }
+ if (!d) {
+ *decpt = 1;
+ return nrv_alloc("0", rve, 1);
+ }
+
+ b = d2b(d, &be, &bbits);
+ i = (int)(fpword0(d) >> Exp_shift1 & (Exp_mask >> Exp_shift1));
+ d2 = d;
+ fpword0(d2) &= Frac_mask1;
+ fpword0(d2) |= Exp_11;
+
+ /* log(x) ~=~ log(1.5) + (x-1.5)/1.5
+ * log10(x) = log(x) / log(10)
+ * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10))
+ * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2)
+ *
+ * This suggests computing an approximation k to log10(d) by
+ *
+ * k = (i - Bias)*0.301029995663981
+ * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 );
+ *
+ * We want k to be too large rather than too small.
+ * The error in the first-order Taylor series approximation
+ * is in our favor, so we just round up the constant enough
+ * to compensate for any error in the multiplication of
+ * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077,
+ * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14,
+ * adding 1e-13 to the constant term more than suffices.
+ * Hence we adjust the constant term to 0.1760912590558.
+ * (We could get a more accurate k by invoking log10,
+ * but this is probably not worthwhile.)
+ */
+
+ i -= Bias;
+ ds = (d2 - 1.5) * 0.289529654602168 + 0.1760912590558 + i * 0.301029995663981;
+ k = (int)ds;
+ if (ds < 0. && ds != k)
+ k--; /* want k = floor(ds) */
+ k_check = 1;
+ if (k >= 0 && k <= Ten_pmax) {
+ if (d < tens[k])
+ k--;
+ k_check = 0;
+ }
+ j = bbits - i - 1;
+ if (j >= 0) {
+ b2 = 0;
+ s2 = j;
+ } else {
+ b2 = -j;
+ s2 = 0;
+ }
+ if (k >= 0) {
+ b5 = 0;
+ s5 = k;
+ s2 += k;
+ } else {
+ b2 -= k;
+ b5 = -k;
+ s5 = 0;
+ }
+ if (mode < 0 || mode > 9)
+ mode = 0;
+ try_quick = 1;
+ if (mode > 5) {
+ mode -= 4;
+ try_quick = 0;
+ }
+ leftright = 1;
+ switch (mode) {
+ case 0:
+ case 1:
+ ilim = ilim1 = -1;
+ i = 18;
+ ndigits = 0;
+ break;
+ case 2:
+ leftright = 0;
+ /* no break */
+ case 4:
+ if (ndigits <= 0)
+ ndigits = 1;
+ ilim = ilim1 = i = ndigits;
+ break;
+ case 3:
+ leftright = 0;
+ /* no break */
+ case 5:
+ i = ndigits + k + 1;
+ ilim = i;
+ ilim1 = i - 1;
+ if (i <= 0)
+ i = 1;
+ }
+ s = s0 = rv_alloc(i);
+
+ if (ilim >= 0 && ilim <= Quick_max && try_quick) {
+
+ /* Try to get by with floating-point arithmetic. */
+
+ i = 0;
+ d2 = d;
+ k0 = k;
+ ilim0 = ilim;
+ ieps = 2; /* conservative */
+ if (k > 0) {
+ ds = tens[k&0xf];
+ j = k >> 4;
+ if (j & Bletch) {
+ /* prevent overflows */
+ j &= Bletch - 1;
+ d /= bigtens[n_bigtens-1];
+ ieps++;
+ }
+ for (; j; j >>= 1, i++)
+ if (j & 1) {
+ ieps++;
+ ds *= bigtens[i];
+ }
+ d /= ds;
+ } else if (j1 = -k) {
+ d *= tens[j1 & 0xf];
+ for (j = j1 >> 4; j; j >>= 1, i++)
+ if (j & 1) {
+ ieps++;
+ d *= bigtens[i];
+ }
+ }
+ if (k_check && d < 1. && ilim > 0) {
+ if (ilim1 <= 0)
+ goto fast_failed;
+ ilim = ilim1;
+ k--;
+ d *= 10.;
+ ieps++;
+ }
+ eps = ieps * d + 7.;
+ fpword0(eps) -= (P - 1) * Exp_msk1;
+ if (ilim == 0) {
+ S = mhi = 0;
+ d -= 5.;
+ if (d > eps)
+ goto one_digit;
+ if (d < -eps)
+ goto no_digits;
+ goto fast_failed;
+ }
+ /* Generate ilim digits, then fix them up. */
+ eps *= tens[ilim-1];
+ for (i = 1; ; i++, d *= 10.) {
+ L = d;
+ d -= L;
+ *s++ = '0' + (int)L;
+ if (i == ilim) {
+ if (d > 0.5 + eps)
+ goto bump_up;
+ else if (d < 0.5 - eps) {
+ while (*--s == '0')
+ ;
+ s++;
+ goto ret1;
+ }
+ break;
+ }
+ }
+fast_failed:
+ s = s0;
+ d = d2;
+ k = k0;
+ ilim = ilim0;
+ }
+
+ /* Do we have a "small" integer? */
+
+ if (be >= 0 && k <= Int_max) {
+ /* Yes. */
+ ds = tens[k];
+ if (ndigits < 0 && ilim <= 0) {
+ S = mhi = 0;
+ if (ilim < 0 || d <= 5 * ds)
+ goto no_digits;
+ goto one_digit;
+ }
+ for (i = 1; ; i++) {
+ L = d / ds;
+ d -= L * ds;
+ *s++ = '0' + (int)L;
+ if (i == ilim) {
+ d += d;
+ if (d > ds || d == ds && L & 1) {
+bump_up:
+ while (*--s == '9')
+ if (s == s0) {
+ k++;
+ *s = '0';
+ break;
+ }
+ ++ * s++;
+ }
+ break;
+ }
+ if (!(d *= 10.))
+ break;
+ }
+ goto ret1;
+ }
+
+ m2 = b2;
+ m5 = b5;
+ mhi = mlo = 0;
+ if (leftright) {
+ if (mode < 2) {
+ i =
+ 1 + P - bbits;
+ } else {
+ j = ilim - 1;
+ if (m5 >= j)
+ m5 -= j;
+ else {
+ s5 += j -= m5;
+ b5 += j;
+ m5 = 0;
+ }
+ if ((i = ilim) < 0) {
+ m2 -= i;
+ i = 0;
+ }
+ }
+ b2 += i;
+ s2 += i;
+ mhi = i2b(1);
+ }
+ if (m2 > 0 && s2 > 0) {
+ i = m2 < s2 ? m2 : s2;
+ b2 -= i;
+ m2 -= i;
+ s2 -= i;
+ }
+ if (b5 > 0) {
+ if (leftright) {
+ if (m5 > 0) {
+ mhi = pow5mult(mhi, m5);
+ b1 = mult(mhi, b);
+ Bfree(b);
+ b = b1;
+ }
+ if (j = b5 - m5)
+ b = pow5mult(b, j);
+ } else
+ b = pow5mult(b, b5);
+ }
+ S = i2b(1);
+ if (s5 > 0)
+ S = pow5mult(S, s5);
+
+ /* Check for special case that d is a normalized power of 2. */
+
+ spec_case = 0;
+ if (mode < 2) {
+ if (!fpword1(d) && !(fpword0(d) & Bndry_mask)
+ ) {
+ /* The special case */
+ b2 += Log2P;
+ s2 += Log2P;
+ spec_case = 1;
+ }
+ }
+
+ /* Arrange for convenient computation of quotients:
+ * shift left if necessary so divisor has 4 leading 0 bits.
+ *
+ * Perhaps we should just compute leading 28 bits of S once
+ * and for all and pass them and a shift to quorem, so it
+ * can do shifts and ors to compute the numerator for q.
+ */
+ if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f)
+ i = 32 - i;
+ if (i > 4) {
+ i -= 4;
+ b2 += i;
+ m2 += i;
+ s2 += i;
+ } else if (i < 4) {
+ i += 28;
+ b2 += i;
+ m2 += i;
+ s2 += i;
+ }
+ if (b2 > 0)
+ b = lshift(b, b2);
+ if (s2 > 0)
+ S = lshift(S, s2);
+ if (k_check) {
+ if (cmp(b, S) < 0) {
+ k--;
+ b = multadd(b, 10, 0); /* we botched the k estimate */
+ if (leftright)
+ mhi = multadd(mhi, 10, 0);
+ ilim = ilim1;
+ }
+ }
+ if (ilim <= 0 && mode > 2) {
+ if (ilim < 0 || cmp(b, S = multadd(S, 5, 0)) <= 0) {
+ /* no digits, fcvt style */
+no_digits:
+ k = -1 - ndigits;
+ goto ret;
+ }
+one_digit:
+ *s++ = '1';
+ k++;
+ goto ret;
+ }
+ if (leftright) {
+ if (m2 > 0)
+ mhi = lshift(mhi, m2);
+
+ /* Compute mlo -- check for special case
+ * that d is a normalized power of 2.
+ */
+
+ mlo = mhi;
+ if (spec_case) {
+ mhi = Balloc(mhi->k);
+ Bcopy(mhi, mlo);
+ mhi = lshift(mhi, Log2P);
+ }
+
+ for (i = 1; ; i++) {
+ dig = quorem(b, S) + '0';
+ /* Do we yet have the shortest decimal string
+ * that will round to d?
+ */
+ j = cmp(b, mlo);
+ delta = diff(S, mhi);
+ j1 = delta->sign ? 1 : cmp(b, delta);
+ Bfree(delta);
+ if (j1 == 0 && !mode && !(fpword1(d) & 1)) {
+ if (dig == '9')
+ goto round_9_up;
+ if (j > 0)
+ dig++;
+ *s++ = dig;
+ goto ret;
+ }
+ if (j < 0 || j == 0 && !mode
+ && !(fpword1(d) & 1)
+ ) {
+ if (j1 > 0) {
+ b = lshift(b, 1);
+ j1 = cmp(b, S);
+ if ((j1 > 0 || j1 == 0 && dig & 1)
+ && dig++ == '9')
+ goto round_9_up;
+ }
+ *s++ = dig;
+ goto ret;
+ }
+ if (j1 > 0) {
+ if (dig == '9') { /* possible if i == 1 */
+round_9_up:
+ *s++ = '9';
+ goto roundoff;
+ }
+ *s++ = dig + 1;
+ goto ret;
+ }
+ *s++ = dig;
+ if (i == ilim)
+ break;
+ b = multadd(b, 10, 0);
+ if (mlo == mhi)
+ mlo = mhi = multadd(mhi, 10, 0);
+ else {
+ mlo = multadd(mlo, 10, 0);
+ mhi = multadd(mhi, 10, 0);
+ }
+ }
+ } else
+ for (i = 1; ; i++) {
+ *s++ = dig = quorem(b, S) + '0';
+ if (i >= ilim)
+ break;
+ b = multadd(b, 10, 0);
+ }
+
+ /* Round off last digit */
+
+ b = lshift(b, 1);
+ j = cmp(b, S);
+ if (j > 0 || j == 0 && dig & 1) {
+roundoff:
+ while (*--s == '9')
+ if (s == s0) {
+ k++;
+ *s++ = '1';
+ goto ret;
+ }
+ ++ * s++;
+ } else {
+ while (*--s == '0')
+ ;
+ s++;
+ }
+ret:
+ Bfree(S);
+ if (mhi) {
+ if (mlo && mlo != mhi)
+ Bfree(mlo);
+ Bfree(mhi);
+ }
+ret1:
+ Bfree(b);
+ *s = 0;
+ *decpt = k + 1;
+ if (rve)
+ *rve = s;
+ return s0;
+}
+
diff --git a/sys/src/libstdio/fclose.c b/sys/src/libstdio/fclose.c
new file mode 100755
index 000000000..9f01825e4
--- /dev/null
+++ b/sys/src/libstdio/fclose.c
@@ -0,0 +1,14 @@
+/*
+ * pANS stdio -- fclose
+ */
+#include "iolib.h"
+int fclose(FILE *f){
+ int error=0;
+ if(f->state==CLOSED) return EOF;
+ if(fflush(f)==EOF) error=EOF;
+ if(f->flags&BALLOC) free(f->buf);
+ if(!(f->flags&STRING) && close(f->fd)<0) error=EOF;
+ f->state=CLOSED;
+ f->flags=0;
+ return error;
+}
diff --git a/sys/src/libstdio/fdopen.c b/sys/src/libstdio/fdopen.c
new file mode 100755
index 000000000..c2b5034fe
--- /dev/null
+++ b/sys/src/libstdio/fdopen.c
@@ -0,0 +1,37 @@
+/*
+ * Posix stdio -- fdopen
+ */
+#include "iolib.h"
+/*
+ * Open the named file with the given mode, using the given FILE
+ * Legal modes are given below, `additional characters may follow these sequences':
+ * r rb open to read
+ * w wb open to write, truncating
+ * a ab open to write positioned at eof, creating if non-existant
+ * r+ r+b rb+ open to read and write, creating if non-existant
+ * w+ w+b wb+ open to read and write, truncating
+ * a+ a+b ab+ open to read and write, positioned at eof, creating if non-existant.
+ */
+FILE *fdopen(const int fd, const char *mode){
+ FILE *f;
+ qlock(&_stdiolk);
+ for(f=_IO_stream;f!=&_IO_stream[FOPEN_MAX];f++)
+ if(f->state==CLOSED)
+ break;
+ if(f==&_IO_stream[FOPEN_MAX]) {
+ qunlock(&_stdiolk);
+ return NULL;
+ }
+ f->fd=fd;
+ if(mode[0]=='a')
+ seek(f->fd, 0L, 2);
+ if(f->fd==-1) return NULL;
+ f->flags=0;
+ f->state=OPEN;
+ f->buf=0;
+ f->rp=0;
+ f->wp=0;
+ f->lp=0;
+ qunlock(&_stdiolk);
+ return f;
+}
diff --git a/sys/src/libstdio/feof.c b/sys/src/libstdio/feof.c
new file mode 100755
index 000000000..9c2ce9077
--- /dev/null
+++ b/sys/src/libstdio/feof.c
@@ -0,0 +1,7 @@
+/*
+ * pANS stdio -- feof
+ */
+#include "iolib.h"
+int feof(FILE *f){
+ return f->state==END;
+}
diff --git a/sys/src/libstdio/ferror.c b/sys/src/libstdio/ferror.c
new file mode 100755
index 000000000..0c705c5d8
--- /dev/null
+++ b/sys/src/libstdio/ferror.c
@@ -0,0 +1,7 @@
+/*
+ * pANS stdio -- ferror
+ */
+#include "iolib.h"
+int ferror(FILE *f){
+ return f->state==ERR;
+}
diff --git a/sys/src/libstdio/fflush.c b/sys/src/libstdio/fflush.c
new file mode 100755
index 000000000..a5a5f0b3c
--- /dev/null
+++ b/sys/src/libstdio/fflush.c
@@ -0,0 +1,52 @@
+/*
+ * pANS stdio -- fflush
+ */
+#include "iolib.h"
+/*
+ * pANS stdio -- data (put here, since loader won't load a separate file)
+ */
+FILE _IO_stream[]={
+/* fd flags state buf rp wp lp bufl unbuf */
+ 0, 0, OPEN, 0, 0, 0, 0, 0, 0,
+ 1, 0, OPEN, 0, 0, 0, 0, 0, 0,
+ 2, 0, OPEN, 0, 0, 0, 0, 0, 0,
+};
+
+int _fflush(FILE *f){
+ int error, cnt;
+
+ if(f==NULL){
+ error=0;
+ for(f=_IO_stream;f!=&_IO_stream[FOPEN_MAX];f++)
+ if(f->state==WR && _fflush(f)==EOF)
+ error=EOF;
+ return error;
+ }
+ if(f->flags&STRING) return EOF;
+ switch(f->state){
+ default: /* OPEN RDWR EOF RD */
+ return 0;
+ case CLOSED:
+ case ERR:
+ return EOF;
+ case WR:
+ cnt=(f->flags&LINEBUF?f->lp:f->wp)-f->buf;
+ if(cnt && write(f->fd, f->buf, cnt)!=cnt){
+ f->state=ERR;
+ return EOF;
+ }
+ f->rp=f->wp=f->buf;
+ f->state=RDWR;
+ return 0;
+ }
+}
+
+int fflush(FILE *f)
+{
+ int r;
+
+ qlock(&_stdiolk);
+ r = _fflush(f);
+ qunlock(&_stdiolk);
+ return r;
+}
diff --git a/sys/src/libstdio/fgetc.c b/sys/src/libstdio/fgetc.c
new file mode 100755
index 000000000..08c5ad03b
--- /dev/null
+++ b/sys/src/libstdio/fgetc.c
@@ -0,0 +1,7 @@
+/*
+ * pANS stdio -- fgetc
+ */
+#include "iolib.h"
+int fgetc(FILE *f){
+ return getc(f);
+}
diff --git a/sys/src/libstdio/fgetpos.c b/sys/src/libstdio/fgetpos.c
new file mode 100755
index 000000000..c8edc57b1
--- /dev/null
+++ b/sys/src/libstdio/fgetpos.c
@@ -0,0 +1,8 @@
+/*
+ * pANS stdio -- fgetpos
+ */
+#include "iolib.h"
+int fgetpos(FILE *f, fpos_t *pos){
+ *pos=ftell(f);
+ return *pos==-1?-1:0;
+}
diff --git a/sys/src/libstdio/fgets.c b/sys/src/libstdio/fgets.c
new file mode 100755
index 000000000..1643b5b46
--- /dev/null
+++ b/sys/src/libstdio/fgets.c
@@ -0,0 +1,18 @@
+/*
+ * pANS stdio -- fgets
+ */
+#include "iolib.h"
+char *fgets(char *as, int n, FILE *f){
+ int c;
+ char *s=as;
+ c = EOF;
+ while(n>1 && (c=getc(f))!=EOF){
+ *s++=c;
+ --n;
+ if(c=='\n') break;
+ }
+ if(c==EOF && s==as
+ || ferror(f)) return NULL;
+ if(n) *s='\0';
+ return as;
+}
diff --git a/sys/src/libstdio/fileno.c b/sys/src/libstdio/fileno.c
new file mode 100755
index 000000000..2dd6fdb85
--- /dev/null
+++ b/sys/src/libstdio/fileno.c
@@ -0,0 +1,10 @@
+/*
+ * Posix stdio -- fileno
+ */
+#include "iolib.h"
+int fileno(FILE *f){
+ if(f==NULL)
+ return -1;
+ else
+ return f->fd;
+}
diff --git a/sys/src/libstdio/fopen.c b/sys/src/libstdio/fopen.c
new file mode 100755
index 000000000..3500fe805
--- /dev/null
+++ b/sys/src/libstdio/fopen.c
@@ -0,0 +1,16 @@
+/*
+ * pANS stdio -- fopen
+ */
+#include "iolib.h"
+FILE *fopen(const char *name, const char *mode){
+ FILE *f;
+ qlock(&_stdiolk);
+ for(f=_IO_stream;f!=&_IO_stream[FOPEN_MAX];f++) {
+ if(f->state==CLOSED) {
+ qunlock(&_stdiolk);
+ return freopen(name, mode, f);
+ }
+ }
+ qunlock(&_stdiolk);
+ return NULL;
+}
diff --git a/sys/src/libstdio/fprintf.c b/sys/src/libstdio/fprintf.c
new file mode 100755
index 000000000..9f3a861cc
--- /dev/null
+++ b/sys/src/libstdio/fprintf.c
@@ -0,0 +1,12 @@
+/*
+ * pANS stdio -- fprintf
+ */
+#include "iolib.h"
+int fprintf(FILE *f, const char *fmt, ...){
+ int n;
+ va_list args;
+ va_start(args, fmt);
+ n=vfprintf(f, fmt, args);
+ va_end(args);
+ return n;
+}
diff --git a/sys/src/libstdio/fputc.c b/sys/src/libstdio/fputc.c
new file mode 100755
index 000000000..0a482e20c
--- /dev/null
+++ b/sys/src/libstdio/fputc.c
@@ -0,0 +1,7 @@
+/*
+ * pANS stdio -- fputc
+ */
+#include "iolib.h"
+int fputc(int c, FILE *f){
+ return putc(c, f); /* This can be made more fair to _IOLBF-mode streams */
+}
diff --git a/sys/src/libstdio/fputs.c b/sys/src/libstdio/fputs.c
new file mode 100755
index 000000000..d0406b0e5
--- /dev/null
+++ b/sys/src/libstdio/fputs.c
@@ -0,0 +1,8 @@
+/*
+ * pANS stdio -- fputs
+ */
+#include "iolib.h"
+int fputs(const char *s, FILE *f){
+ while(*s) putc(*s++, f);
+ return ferror(f)?EOF:0;
+}
diff --git a/sys/src/libstdio/fread.c b/sys/src/libstdio/fread.c
new file mode 100755
index 000000000..d2759638d
--- /dev/null
+++ b/sys/src/libstdio/fread.c
@@ -0,0 +1,41 @@
+/*
+ * pANS stdio -- fread
+ */
+#include "iolib.h"
+
+#define BIGN (BUFSIZ/2)
+
+long fread(void *p, long recl, long nrec, FILE *f){
+ char *s;
+ int n, d, c;
+
+ s=(char *)p;
+ n=recl*nrec;
+ while(n>0){
+ d=f->wp-f->rp;
+ if(d>0){
+ if(d>n)
+ d=n;
+ memmove(s, f->rp, d);
+ f->rp+=d;
+ }else{
+ if(n >= BIGN && f->state==RD && !(f->flags&STRING) && f->buf!=f->unbuf){
+ d=read(f->fd, s, n);
+ if(d<=0){
+ f->state=(d==0)?END:ERR;
+ goto ret;
+ }
+ }else{
+ c=_IO_getc(f);
+ if(c==EOF)
+ goto ret;
+ *s=c;
+ d=1;
+ }
+ }
+ s+=d;
+ n-=d;
+ }
+ ret:
+ return (s-(char *)p)/(recl?recl:1);
+}
diff --git a/sys/src/libstdio/freopen.c b/sys/src/libstdio/freopen.c
new file mode 100755
index 000000000..a5ac25993
--- /dev/null
+++ b/sys/src/libstdio/freopen.c
@@ -0,0 +1,56 @@
+/*
+ * pANS stdio -- freopen
+ */
+#include "iolib.h"
+/*
+ * Open the named file with the given mode, using the given FILE
+ * Legal modes are given below, `additional characters may follow these sequences':
+ * r rb open to read
+ * w wb open to write, truncating
+ * a ab open to write positioned at eof, creating if non-existant
+ * r+ r+b rb+ open to read and write, creating if non-existant
+ * w+ w+b wb+ open to read and write, truncating
+ * a+ a+b ab+ open to read and write, positioned at eof, creating if non-existant.
+ */
+FILE *freopen(const char *name, const char *mode, FILE *f){
+ int m;
+
+ if(f->state!=CLOSED){
+ fclose(f);
+/* premature; fall through and see what happens */
+/* f->state=OPEN; */
+ }
+
+ m = *mode++;
+ if(m == 0)
+ return NULL;
+ if(*mode == 'b')
+ mode++;
+ switch(m){
+ default:
+ return NULL;
+ case 'r':
+ f->fd=open(name, (*mode == '+'? ORDWR: OREAD));
+ break;
+ case 'w':
+ f->fd=create(name, (*mode == '+'? ORDWR: OWRITE), 0666);
+ break;
+ case 'a':
+ m = (*mode == '+'? ORDWR: OWRITE);
+ f->fd=open(name, m);
+ if(f->fd<0)
+ f->fd=create(name, m, 0666);
+ seek(f->fd, 0LL, 2);
+ break;
+ }
+
+ if(f->fd==-1)
+ return NULL;
+ f->flags=(mode[0]=='a')? APPEND : 0;
+ f->state=OPEN;
+ f->buf=0;
+ f->rp=0;
+ f->wp=0;
+ f->lp=0;
+ return f;
+}
diff --git a/sys/src/libstdio/fscanf.c b/sys/src/libstdio/fscanf.c
new file mode 100755
index 000000000..3d9d874e8
--- /dev/null
+++ b/sys/src/libstdio/fscanf.c
@@ -0,0 +1,12 @@
+/*
+ * pANS stdio -- fscanf
+ */
+#include "iolib.h"
+int fscanf(FILE *f, const char *fmt, ...){
+ int n;
+ va_list args;
+ va_start(args, fmt);
+ n=vfscanf(f, fmt, args);
+ va_end(args);
+ return n;
+}
diff --git a/sys/src/libstdio/fseek.c b/sys/src/libstdio/fseek.c
new file mode 100755
index 000000000..8a7a2a5fa
--- /dev/null
+++ b/sys/src/libstdio/fseek.c
@@ -0,0 +1,23 @@
+/*
+ * pANS stdio -- fseek
+ */
+#include "iolib.h"
+int fseek(FILE *f, long offs, int type){
+ switch(f->state){
+ case ERR:
+ case CLOSED:
+ return -1;
+ case WR:
+ fflush(f);
+ break;
+ case RD:
+ if(type==1 && f->buf!=f->unbuf)
+ offs-=f->wp-f->rp;
+ break;
+ }
+ if(f->flags&STRING || seek(f->fd, offs, type)==-1) return -1;
+ if(f->state==RD) f->rp=f->wp=f->buf;
+ if(f->state!=OPEN)
+ f->state=RDWR;
+ return 0;
+}
diff --git a/sys/src/libstdio/fseeko.c b/sys/src/libstdio/fseeko.c
new file mode 100755
index 000000000..48b627bc8
--- /dev/null
+++ b/sys/src/libstdio/fseeko.c
@@ -0,0 +1,23 @@
+/*
+ * pANS stdio -- fseeko
+ */
+#include "iolib.h"
+int fseeko(FILE *f, long long offs, int type){
+ switch(f->state){
+ case ERR:
+ case CLOSED:
+ return -1;
+ case WR:
+ fflush(f);
+ break;
+ case RD:
+ if(type==1 && f->buf!=f->unbuf)
+ offs-=f->wp-f->rp;
+ break;
+ }
+ if(f->flags&STRING || seek(f->fd, offs, type)==-1) return -1;
+ if(f->state==RD) f->rp=f->wp=f->buf;
+ if(f->state!=OPEN)
+ f->state=RDWR;
+ return 0;
+}
diff --git a/sys/src/libstdio/fsetpos.c b/sys/src/libstdio/fsetpos.c
new file mode 100755
index 000000000..540e83a7e
--- /dev/null
+++ b/sys/src/libstdio/fsetpos.c
@@ -0,0 +1,7 @@
+/*
+ * pANS stdio -- fsetpos
+ */
+#include "iolib.h"
+int fsetpos(FILE *f, const fpos_t *pos){
+ return fseek(f, *pos, SEEK_SET);
+}
diff --git a/sys/src/libstdio/ftell.c b/sys/src/libstdio/ftell.c
new file mode 100755
index 000000000..d5acbb373
--- /dev/null
+++ b/sys/src/libstdio/ftell.c
@@ -0,0 +1,16 @@
+/*
+ * pANS stdio -- ftell
+ */
+#include "iolib.h"
+long ftell(FILE *f){
+ long seekp=seek(f->fd, 0L, 1);
+ if(seekp<0) return -1; /* enter error state? */
+ switch(f->state){
+ default:
+ return seekp;
+ case RD:
+ return seekp-(f->wp-f->rp);
+ case WR:
+ return (f->flags&LINEBUF?f->lp:f->wp)-f->buf+seekp;
+ }
+}
diff --git a/sys/src/libstdio/ftello.c b/sys/src/libstdio/ftello.c
new file mode 100755
index 000000000..2480856b9
--- /dev/null
+++ b/sys/src/libstdio/ftello.c
@@ -0,0 +1,16 @@
+/*
+ * pANS stdio -- ftello
+ */
+#include "iolib.h"
+long long ftello(FILE *f){
+ long long seekp=seek(f->fd, 0L, 1);
+ if(seekp<0) return -1; /* enter error state? */
+ switch(f->state){
+ default:
+ return seekp;
+ case RD:
+ return seekp-(f->wp-f->rp);
+ case WR:
+ return (f->flags&LINEBUF?f->lp:f->wp)-f->buf+seekp;
+ }
+}
diff --git a/sys/src/libstdio/fwrite.c b/sys/src/libstdio/fwrite.c
new file mode 100755
index 000000000..d77679b22
--- /dev/null
+++ b/sys/src/libstdio/fwrite.c
@@ -0,0 +1,51 @@
+/*
+ * pANS stdio -- fwrite
+ */
+#include "iolib.h"
+
+#define BIGN (BUFSIZ/2)
+
+long fwrite(const void *p, long recl, long nrec, FILE *f){
+ char *s;
+ int n, d;
+
+ s=(char *)p;
+ n=recl*nrec;
+ while(n>0){
+ d=f->rp-f->wp;
+ if(d>0){
+ if(d>n)
+ d=n;
+ memmove(f->wp, s, d);
+ f->wp+=d;
+ }else{
+ if(n>=BIGN && f->state==WR && !(f->flags&(STRING|LINEBUF)) && f->buf!=f->unbuf){
+ d=f->wp-f->buf;
+ if(d>0){
+ if(f->flags&APPEND)
+ seek(f->fd, 0L, 2);
+ if(write(f->fd, f->buf, d)!=d){
+ f->state=ERR;
+ goto ret;
+ }
+ f->wp=f->rp=f->buf;
+ }
+ if(f->flags&APPEND)
+ seek(f->fd, 0L, 2);
+ d=write(f->fd, s, n);
+ if(d<=0){
+ f->state=ERR;
+ goto ret;
+ }
+ }else{
+ if(_IO_putc(*s, f)==EOF)
+ goto ret;
+ d=1;
+ }
+ }
+ s+=d;
+ n-=d;
+ }
+ ret:
+ return (s-(char *)p)/(recl?recl:1);
+}
diff --git a/sys/src/libstdio/getc.c b/sys/src/libstdio/getc.c
new file mode 100755
index 000000000..4096972bf
--- /dev/null
+++ b/sys/src/libstdio/getc.c
@@ -0,0 +1,8 @@
+/*
+ * pANS stdio -- getc
+ */
+#include "iolib.h"
+#undef getc
+int getc(FILE *f){
+ return fgetc(f);
+}
diff --git a/sys/src/libstdio/gets.c b/sys/src/libstdio/gets.c
new file mode 100755
index 000000000..909c21485
--- /dev/null
+++ b/sys/src/libstdio/gets.c
@@ -0,0 +1,17 @@
+/*
+ * pANS stdio -- gets
+ */
+#include "iolib.h"
+char *gets(char *as){
+#ifdef secure
+ stdin->flags|=ERR;
+ return NULL;
+#else
+ char *s=as;
+ int c;
+ while((c=getchar())!='\n' && c!=EOF) *s++=c;
+ if(c!=EOF || s!=as) *s='\0';
+ else return NULL;
+ return as;
+#endif
+}
diff --git a/sys/src/libstdio/iolib.h b/sys/src/libstdio/iolib.h
new file mode 100755
index 000000000..8748b5518
--- /dev/null
+++ b/sys/src/libstdio/iolib.h
@@ -0,0 +1,44 @@
+/*
+ * pANS stdio -- definitions
+ * The following names are defined in the pANS:
+ * FILE fpos_t _IOFBF _IOLBF _IONBF
+ * BUFSIZ EOF FOPEN_MAX FILENAME_MAX L_tmpnam
+ * SEEK_CUR SEEK_END SEEK_SET TMP_MAX stderr
+ * stdin stdout remove rename tmpfile
+ * tmpnam fclose fflush fopen freopen
+ * setbuf setvbuf fprintf fscanf printf
+ * scanf sprintf sscanf vfprintf vprintf
+ * vsprintf fgetc fgets fputc fputs
+ * getc getchar gets putc putchar
+ * puts ungetc fread fwrite fgetpos
+ * fseek fsetpos ftell rewind clearerr
+ * feof ferror perror
+ *
+ * (But plan9 version omits remove and rename, because they are in libc)
+ */
+#include <u.h>
+#include <libc.h>
+#undef END
+#include "Stdio.h"
+/*
+ * Flag bits
+ */
+#define BALLOC 1 /* did stdio malloc fd->buf? */
+#define LINEBUF 2 /* is stream line buffered? */
+#define STRING 4 /* output to string, instead of file */
+#define APPEND 8 /* append mode output */
+/*
+ * States
+ */
+#define CLOSED 0 /* file not open */
+#define OPEN 1 /* file open, but no I/O buffer allocated yet */
+#define RDWR 2 /* open, buffer allocated, ok to read or write */
+#define RD 3 /* open, buffer allocated, ok to read but not write */
+#define WR 4 /* open, buffer allocated, ok to write but not read */
+#define ERR 5 /* open, but an uncleared error occurred */
+#define END 6 /* open, but at eof */
+
+int _IO_setvbuf(FILE *);
+
+/* half hearted attempt to make multi threaded */
+extern QLock _stdiolk;
diff --git a/sys/src/libstdio/mkfile b/sys/src/libstdio/mkfile
new file mode 100755
index 000000000..a3c0dbd6a
--- /dev/null
+++ b/sys/src/libstdio/mkfile
@@ -0,0 +1,69 @@
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libstdio.a
+OFILES=\
+ _IO_getc.$O\
+ _IO_putc.$O\
+ clearerr.$O\
+ dtoa.$O\
+ fclose.$O\
+ fdopen.$O\
+ feof.$O\
+ ferror.$O\
+ fflush.$O\
+ fgetc.$O\
+ fgetpos.$O\
+ fgets.$O\
+ fileno.$O\
+ fopen.$O\
+ fprintf.$O\
+ fputc.$O\
+ fputs.$O\
+ fread.$O\
+ freopen.$O\
+ fscanf.$O\
+ fseek.$O\
+ fseeko.$O\
+ fsetpos.$O\
+ ftell.$O\
+ ftello.$O\
+ fwrite.$O\
+ getc.$O\
+ gets.$O\
+ printf.$O\
+ putc.$O\
+ puts.$O\
+ rewind.$O\
+ scanf.$O\
+ sclose.$O\
+ setbuf.$O\
+ setvbuf.$O\
+ snprintf.$O\
+ sopenr.$O\
+ sopenw.$O\
+ sprintf.$O\
+ sscanf.$O\
+ tmpfile.$O\
+ tmpnam.$O\
+ ungetc.$O\
+ vfprintf.$O\
+ vfscanf.$O\
+ vprintf.$O\
+ vsnprintf.$O\
+ vsprintf.$O\
+
+HFILES=/sys/include/stdio.h\
+ iolib.h\
+
+UPDATE=\
+ mkfile\
+ $HFILES\
+ ${OFILES:%.$O=%.c}\
+ ${LIB:/$objtype/%=/386/%}\
+
+</sys/src/cmd/mksyslib
+# this code really can't handle any flow-analysis warnings
+CFLAGS=
+
+test:V: $OFILES
+ ar vu libstdio.a $OFILES
diff --git a/sys/src/libstdio/printf.c b/sys/src/libstdio/printf.c
new file mode 100755
index 000000000..8880b9d8d
--- /dev/null
+++ b/sys/src/libstdio/printf.c
@@ -0,0 +1,12 @@
+/*
+ * pANS stdio -- printf
+ */
+#include "iolib.h"
+int printf(const char *fmt, ...){
+ int n;
+ va_list args;
+ va_start(args, fmt);
+ n=vfprintf(stdout, fmt, args);
+ va_end(args);
+ return n;
+}
diff --git a/sys/src/libstdio/putc.c b/sys/src/libstdio/putc.c
new file mode 100755
index 000000000..9cffeeb01
--- /dev/null
+++ b/sys/src/libstdio/putc.c
@@ -0,0 +1,8 @@
+/*
+ * pANS stdio -- putc
+ */
+#include "iolib.h"
+#undef putc
+int putc(int c, FILE *f){
+ return fputc(c, f);
+}
diff --git a/sys/src/libstdio/puts.c b/sys/src/libstdio/puts.c
new file mode 100755
index 000000000..b25c4718d
--- /dev/null
+++ b/sys/src/libstdio/puts.c
@@ -0,0 +1,9 @@
+/*
+ * pANS stdio -- puts
+ */
+#include "iolib.h"
+int puts(const char *s){
+ fputs(s, stdout);
+ putchar('\n');
+ return ferror(stdin)?EOF:0;
+}
diff --git a/sys/src/libstdio/rewind.c b/sys/src/libstdio/rewind.c
new file mode 100755
index 000000000..0cfdb957c
--- /dev/null
+++ b/sys/src/libstdio/rewind.c
@@ -0,0 +1,7 @@
+/*
+ * pANS stdio -- rewind
+ */
+#include "iolib.h"
+void rewind(FILE *f){
+ fseek(f, 0L, SEEK_SET);
+}
diff --git a/sys/src/libstdio/scanf.c b/sys/src/libstdio/scanf.c
new file mode 100755
index 000000000..30a631cc9
--- /dev/null
+++ b/sys/src/libstdio/scanf.c
@@ -0,0 +1,12 @@
+/*
+ * pANS stdio -- scanf
+ */
+#include "iolib.h"
+int scanf(const char *fmt, ...){
+ int n;
+ va_list args;
+ va_start(args, fmt);
+ n=vfscanf(stdin, fmt, args);
+ va_end(args);
+ return n;
+}
diff --git a/sys/src/libstdio/sclose.c b/sys/src/libstdio/sclose.c
new file mode 100755
index 000000000..940b80227
--- /dev/null
+++ b/sys/src/libstdio/sclose.c
@@ -0,0 +1,34 @@
+/*
+ * pANS stdio -- sclose
+ */
+#include "iolib.h"
+char *sclose(FILE *f){
+ switch(f->state){
+ default: /* ERR CLOSED */
+ if(f->buf && f->flags&BALLOC)
+ free(f->buf);
+ f->flags=0;
+ return NULL;
+ case OPEN:
+ f->buf=malloc(1);
+ f->buf[0]='\0';
+ break;
+ case RD:
+ case END:
+ f->flags=0;
+ break;
+ case RDWR:
+ case WR:
+ if(f->wp==f->rp){
+ if(f->flags&BALLOC)
+ f->buf=realloc(f->buf, f->bufl+1);
+ if(f->buf==NULL) return NULL;
+ }
+ *f->wp='\0';
+ f->flags=0;
+ break;
+ }
+ f->state=CLOSED;
+ f->flags=0;
+ return f->buf;
+}
diff --git a/sys/src/libstdio/setbuf.c b/sys/src/libstdio/setbuf.c
new file mode 100755
index 000000000..31c5d3e1e
--- /dev/null
+++ b/sys/src/libstdio/setbuf.c
@@ -0,0 +1,17 @@
+/*
+ * pANS stdio -- setbuf
+ */
+#include "iolib.h"
+void setbuf(FILE *f, char *buf){
+ if(f->state==OPEN){
+ if(buf)
+ f->bufl=BUFSIZ;
+ else{
+ buf=f->unbuf;
+ f->bufl=0;
+ }
+ f->rp=f->wp=f->lp=f->buf=buf;
+ f->state=RDWR;
+ }
+ /* else error, but there's no way to report it */
+}
diff --git a/sys/src/libstdio/setvbuf.c b/sys/src/libstdio/setvbuf.c
new file mode 100755
index 000000000..34c3bf7c8
--- /dev/null
+++ b/sys/src/libstdio/setvbuf.c
@@ -0,0 +1,50 @@
+/*
+ * pANS stdio -- setvbuf
+ */
+#include "iolib.h"
+int setvbuf(FILE *f, char *buf, int mode, long size){
+ if(f->state!=OPEN){
+ f->state=ERR;
+ return -1;
+ }
+ f->state=RDWR;
+ switch(mode){
+ case _IOLBF:
+ f->flags|=LINEBUF;
+ case _IOFBF:
+ if(buf==0){
+ buf=malloc(size);
+ if(buf==0){
+ f->state=ERR;
+ return -1;
+ }
+ f->flags|=BALLOC;
+ }
+ f->bufl=size;
+ break;
+ case _IONBF:
+ buf=f->unbuf;
+ f->bufl=0;
+ break;
+ }
+ f->rp=f->wp=f->lp=f->buf=buf;
+ f->state=RDWR;
+ return 0;
+}
+int _IO_setvbuf(FILE *f){
+ static int isatty(int);
+ if(f==stderr || (f==stdout && isatty(1)))
+ return setvbuf(f, (char *)0, _IOLBF, BUFSIZ);
+ else return setvbuf(f, (char *)0, _IOFBF, BUFSIZ);
+}
+static int
+isatty(int fd)
+{
+ char buf[64];
+
+ if(fd2path(fd, buf, sizeof buf) != 0)
+ return 0;
+
+ /* might be /mnt/term/dev/cons */
+ return strlen(buf) >= 9 && strcmp(buf+strlen(buf)-9, "/dev/cons") == 0;
+}
diff --git a/sys/src/libstdio/snprintf.c b/sys/src/libstdio/snprintf.c
new file mode 100755
index 000000000..e73556359
--- /dev/null
+++ b/sys/src/libstdio/snprintf.c
@@ -0,0 +1,17 @@
+/*
+ * pANS stdio -- sprintf
+ */
+#include "iolib.h"
+int snprintf(char *buf, int nbuf, const char *fmt, ...){
+ int n;
+ va_list args;
+ FILE *f=sopenw();
+ if(f==NULL)
+ return 0;
+ setvbuf(f, buf, _IOFBF, nbuf);
+ va_start(args, fmt);
+ n=vfprintf(f, fmt, args);
+ va_end(args);
+ sclose(f);
+ return n;
+}
diff --git a/sys/src/libstdio/sopenr.c b/sys/src/libstdio/sopenr.c
new file mode 100755
index 000000000..bbd377602
--- /dev/null
+++ b/sys/src/libstdio/sopenr.c
@@ -0,0 +1,22 @@
+/*
+ * pANS stdio -- sopenr
+ */
+#include "iolib.h"
+
+FILE *sopenr(const char *s){
+ FILE *f;
+ qlock(&_stdiolk);
+ for(f=_IO_stream;f!=&_IO_stream[FOPEN_MAX];f++) if(f->state==CLOSED) break;
+ if(f==&_IO_stream[FOPEN_MAX]) {
+ qunlock(&_stdiolk);
+ return NULL;
+ }
+ f->buf=f->rp=(char *)s; /* what an annoyance const is */
+ f->bufl=strlen(s);
+ f->wp=f->buf+f->bufl;
+ f->state=RD;
+ f->flags=STRING;
+ f->fd=-1;
+ qunlock(&_stdiolk);
+ return f;
+}
diff --git a/sys/src/libstdio/sopenw.c b/sys/src/libstdio/sopenw.c
new file mode 100755
index 000000000..cd4836b1d
--- /dev/null
+++ b/sys/src/libstdio/sopenw.c
@@ -0,0 +1,19 @@
+/*
+ * pANS stdio -- sopenw
+ */
+#include "iolib.h"
+FILE *sopenw(void){
+ FILE *f;
+ qlock(&_stdiolk);
+ for(f=_IO_stream;f!=&_IO_stream[FOPEN_MAX];f++) if(f->state==CLOSED) break;
+ if(f==&_IO_stream[FOPEN_MAX]) {
+ qunlock(&_stdiolk);
+ return NULL;
+ }
+ f->buf=f->rp=f->wp=0;
+ f->state=OPEN;
+ f->flags=STRING;
+ f->fd=-1;
+ qunlock(&_stdiolk);
+ return f;
+}
diff --git a/sys/src/libstdio/sprintf.c b/sys/src/libstdio/sprintf.c
new file mode 100755
index 000000000..42aea8c76
--- /dev/null
+++ b/sys/src/libstdio/sprintf.c
@@ -0,0 +1,17 @@
+/*
+ * pANS stdio -- sprintf
+ */
+#include "iolib.h"
+int sprintf(char *buf, const char *fmt, ...){
+ int n;
+ va_list args;
+ FILE *f=sopenw();
+ if(f==NULL)
+ return 0;
+ setvbuf(f, buf, _IOFBF, 100000);
+ va_start(args, fmt);
+ n=vfprintf(f, fmt, args);
+ va_end(args);
+ sclose(f);
+ return n;
+}
diff --git a/sys/src/libstdio/sscanf.c b/sys/src/libstdio/sscanf.c
new file mode 100755
index 000000000..15e5cacc7
--- /dev/null
+++ b/sys/src/libstdio/sscanf.c
@@ -0,0 +1,14 @@
+/*
+ * pANS stdio -- sscanf
+ */
+#include "iolib.h"
+int sscanf(const char *s, const char *fmt, ...){
+ int n;
+ FILE *f=sopenr(s);
+ va_list args;
+ va_start(args, fmt);
+ n=vfscanf(f, fmt, args);
+ va_end(args);
+ sclose(f);
+ return n;
+}
diff --git a/sys/src/libstdio/tmpfile.c b/sys/src/libstdio/tmpfile.c
new file mode 100755
index 000000000..0728c3652
--- /dev/null
+++ b/sys/src/libstdio/tmpfile.c
@@ -0,0 +1,48 @@
+/*
+ * pANS stdio -- tmpfile
+ *
+ * Bug: contains a critical section. Two executions by the same
+ * user could interleave as follows, both yielding the same file:
+ * access fails
+ * access fails
+ * fopen succeeds
+ * fopen succeeds
+ * unlink succeeds
+ * unlink fails
+ * As I read the pANS, this can't reasonably use tmpnam to generate
+ * the name, so that code is duplicated.
+ */
+#include "iolib.h"
+
+static char tmpsmade[FOPEN_MAX][L_tmpnam+1];
+static int ntmps = 0;
+
+static void rmtmps(void);
+
+FILE *tmpfile(void){
+ FILE *f;
+ static char name[]="/tmp/tf0000000000000";
+ char *p;
+ while(access(name, 0)==0){
+ p=name+7;
+ while(*p=='9') *p++='0';
+ if(*p=='\0') return NULL;
+ ++*p;
+ }
+ f=fopen(name, "wb+");
+ if(f && ntmps<FOPEN_MAX){
+ if(ntmps==0)
+ atexit(rmtmps);
+ strcpy(tmpsmade[ntmps++], name);
+ }
+ return f;
+}
+
+static void
+rmtmps(void)
+{
+ int i;
+
+ for(i=0; i<ntmps; i++)
+ remove(tmpsmade[i]);
+}
diff --git a/sys/src/libstdio/tmpnam.c b/sys/src/libstdio/tmpnam.c
new file mode 100755
index 000000000..666e4dad9
--- /dev/null
+++ b/sys/src/libstdio/tmpnam.c
@@ -0,0 +1,20 @@
+/*
+ * pANS stdio -- tmpnam
+ */
+#include "iolib.h"
+
+char *tmpnam(char *s){
+ static char name[]="/tmp/tn000000000000";
+ char *p;
+ do{
+ p=name+7;
+ while(*p=='9') *p++='0';
+ if(*p=='\0') return NULL;
+ ++*p;
+ }while(access(name, 0)==0);
+ if(s){
+ strcpy(s, name);
+ return s;
+ }
+ return name;
+}
diff --git a/sys/src/libstdio/ungetc.c b/sys/src/libstdio/ungetc.c
new file mode 100755
index 000000000..4c12c5869
--- /dev/null
+++ b/sys/src/libstdio/ungetc.c
@@ -0,0 +1,28 @@
+/*
+ * pANS stdio -- ungetc
+ */
+#include "iolib.h"
+int ungetc(int c, FILE *f){
+ if(c==EOF) return EOF;
+ switch(f->state){
+ default: /* WR */
+ f->state=ERR;
+ return EOF;
+ case CLOSED:
+ case ERR:
+ return EOF;
+ case OPEN:
+ _IO_setvbuf(f);
+ case RDWR:
+ case END:
+ f->rp=f->wp=f->buf+(f->bufl==0?1:f->bufl);
+ f->state=RD;
+ case RD:
+ if(f->rp==f->buf) return EOF;
+ if(f->flags&STRING)
+ f->rp--;
+ else
+ *--f->rp=c;
+ return (char)c;
+ }
+}
diff --git a/sys/src/libstdio/vfprintf.c b/sys/src/libstdio/vfprintf.c
new file mode 100755
index 000000000..4600caff6
--- /dev/null
+++ b/sys/src/libstdio/vfprintf.c
@@ -0,0 +1,558 @@
+/*
+ * pANS stdio -- vfprintf
+ */
+#include "iolib.h"
+/*
+ * Leading flags
+ */
+#define SPACE 1 /* ' ' prepend space if no sign printed */
+#define ALT 2 /* '#' use alternate conversion */
+#define SIGN 4 /* '+' prepend sign, even if positive */
+#define LEFT 8 /* '-' left-justify */
+#define ZPAD 16 /* '0' zero-pad */
+/*
+ * Trailing flags
+ */
+#define SHORT 32 /* 'h' convert a short integer */
+#define LONG 64 /* 'l' convert a long integer */
+#define LDBL 128 /* 'L' convert a long double */
+#define PTR 256 /* convert a void * (%p) */
+
+static int lflag[] = { /* leading flags */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^@ ^A ^B ^C ^D ^E ^F ^G */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^H ^I ^J ^K ^L ^M ^N ^O */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^P ^Q ^R ^S ^T ^U ^V ^W */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
+SPACE, 0, 0, ALT, 0, 0, 0, 0, /* sp ! " # $ % & ' */
+0, 0, 0, SIGN, 0, LEFT, 0, 0, /* ( ) * + , - . / */
+ZPAD, 0, 0, 0, 0, 0, 0, 0, /* 0 1 2 3 4 5 6 7 */
+0, 0, 0, 0, 0, 0, 0, 0, /* 8 9 : ; < = > ? */
+0, 0, 0, 0, 0, 0, 0, 0, /* @ A B C D E F G */
+0, 0, 0, 0, 0, 0, 0, 0, /* H I J K L M N O */
+0, 0, 0, 0, 0, 0, 0, 0, /* P Q R S T U V W */
+0, 0, 0, 0, 0, 0, 0, 0, /* X Y Z [ \ ] ^ _ */
+0, 0, 0, 0, 0, 0, 0, 0, /* ` a b c d e f g */
+0, 0, 0, 0, 0, 0, 0, 0, /* h i j k l m n o */
+0, 0, 0, 0, 0, 0, 0, 0, /* p q r s t u v w */
+0, 0, 0, 0, 0, 0, 0, 0, /* x y z { | } ~ ^? */
+
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static int tflag[] = { /* trailing flags */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^@ ^A ^B ^C ^D ^E ^F ^G */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^H ^I ^J ^K ^L ^M ^N ^O */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^P ^Q ^R ^S ^T ^U ^V ^W */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
+0, 0, 0, 0, 0, 0, 0, 0, /* sp ! " # $ % & ' */
+0, 0, 0, 0, 0, 0, 0, 0, /* ( ) * + , - . / */
+0, 0, 0, 0, 0, 0, 0, 0, /* 0 1 2 3 4 5 6 7 */
+0, 0, 0, 0, 0, 0, 0, 0, /* 8 9 : ; < = > ? */
+0, 0, 0, 0, 0, 0, 0, 0, /* @ A B C D E F G */
+0, 0, 0, 0, LDBL, 0, 0, 0, /* H I J K L M N O */
+0, 0, 0, 0, 0, 0, 0, 0, /* P Q R S T U V W */
+0, 0, 0, 0, 0, 0, 0, 0, /* X Y Z [ \ ] ^ _ */
+0, 0, 0, 0, 0, 0, 0, 0, /* ` a b c d e f g */
+SHORT, 0, 0, 0, LONG, 0, 0, 0, /* h i j k l m n o */
+0, 0, 0, 0, 0, 0, 0, 0, /* p q r s t u v w */
+0, 0, 0, 0, 0, 0, 0, 0, /* x y z { | } ~ ^? */
+
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static int ocvt_E(FILE *, va_list *, int, int, int);
+static int ocvt_G(FILE *, va_list *, int, int, int);
+static int ocvt_X(FILE *, va_list *, int, int, int);
+static int ocvt_c(FILE *, va_list *, int, int, int);
+static int ocvt_d(FILE *, va_list *, int, int, int);
+static int ocvt_e(FILE *, va_list *, int, int, int);
+static int ocvt_f(FILE *, va_list *, int, int, int);
+static int ocvt_g(FILE *, va_list *, int, int, int);
+static int ocvt_n(FILE *, va_list *, int, int, int);
+static int ocvt_o(FILE *, va_list *, int, int, int);
+static int ocvt_p(FILE *, va_list *, int, int, int);
+static int ocvt_s(FILE *, va_list *, int, int, int);
+static int ocvt_u(FILE *, va_list *, int, int, int);
+static int ocvt_x(FILE *, va_list *, int, int, int);
+
+static int(*ocvt[])(FILE *, va_list *, int, int, int) = {
+0, 0, 0, 0, 0, 0, 0, 0, /* ^@ ^A ^B ^C ^D ^E ^F ^G */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^H ^I ^J ^K ^L ^M ^N ^O */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^P ^Q ^R ^S ^T ^U ^V ^W */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
+0, 0, 0, 0, 0, 0, 0, 0, /* sp ! " # $ % & ' */
+0, 0, 0, 0, 0, 0, 0, 0, /* ( ) * + , - . / */
+0, 0, 0, 0, 0, 0, 0, 0, /* 0 1 2 3 4 5 6 7 */
+0, 0, 0, 0, 0, 0, 0, 0, /* 8 9 : ; < = > ? */
+0, 0, 0, 0, 0, ocvt_E, 0, ocvt_G, /* @ A B C D E F G */
+0, 0, 0, 0, 0, 0, 0, 0, /* H I J K L M N O */
+0, 0, 0, 0, 0, 0, 0, 0, /* P Q R S T U V W */
+ocvt_X, 0, 0, 0, 0, 0, 0, 0, /* X Y Z [ \ ] ^ _ */
+0, 0, 0, ocvt_c, ocvt_d, ocvt_e, ocvt_f, ocvt_g, /* ` a b c d e f g */
+0, ocvt_d, 0, 0, 0, 0, ocvt_n, ocvt_o, /* h i j k l m n o */
+ocvt_p, 0, 0, ocvt_s, 0, ocvt_u, 0, 0, /* p q r s t u v w */
+ocvt_x, 0, 0, 0, 0, 0, 0, 0, /* x y z { | } ~ ^? */
+
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static int nprint;
+
+QLock _stdiolk;
+
+int
+vfprintf(FILE *f, const char *s, va_list args)
+{
+ int flags, width, precision;
+
+ qlock(&_stdiolk);
+
+ nprint = 0;
+ while(*s){
+ if(*s != '%'){
+ putc(*s++, f);
+ nprint++;
+ continue;
+ }
+ s++;
+ flags = 0;
+ while(lflag[*s&_IO_CHMASK]) flags |= lflag[*s++&_IO_CHMASK];
+ if(*s == '*'){
+ width = va_arg(args, int);
+ s++;
+ if(width<0){
+ flags |= LEFT;
+ width = -width;
+ }
+ }
+ else{
+ width = 0;
+ while('0'<=*s && *s<='9') width = width*10 + *s++ - '0';
+ }
+ if(*s == '.'){
+ s++;
+ if(*s == '*'){
+ precision = va_arg(args, int);
+ s++;
+ }
+ else{
+ precision = 0;
+ while('0'<=*s && *s<='9') precision = precision*10 + *s++ - '0';
+ }
+ }
+ else
+ precision = -1;
+ while(tflag[*s&_IO_CHMASK]) flags |= tflag[*s++&_IO_CHMASK];
+ if(ocvt[*s]) nprint += (*ocvt[*s++])(f, &args, flags, width, precision);
+ else if(*s){
+ putc(*s++, f);
+ nprint++;
+ }
+ }
+
+ qunlock(&_stdiolk);
+
+ if(ferror(f)){
+ if((f->flags&STRING) && f->wp==f->rp && f->wp>f->buf){
+ *(f->wp-1) = '\0';
+ return nprint;
+ }
+ return -1;
+ }
+ return nprint;
+}
+
+static int
+ocvt_c(FILE *f, va_list *args, int flags, int width, int precision)
+{
+#pragma ref precision
+ int i;
+
+ if(!(flags&LEFT)) for(i=1; i<width; i++) putc(' ', f);
+ putc((unsigned char)va_arg(*args, int), f);
+ if(flags&LEFT) for(i=1; i<width; i++) putc(' ', f);
+ return width<1 ? 1 : width;
+}
+
+static int
+ocvt_s(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ int i, n = 0;
+ char *s;
+
+ s = va_arg(*args, char *);
+ if(!(flags&LEFT)){
+ if(precision >= 0)
+ for(i=0; i!=precision && s[i]; i++);
+ else
+ for(i=0; s[i]; i++);
+ for(; i<width; i++){
+ putc(' ', f);
+ n++;
+ }
+ }
+ if(precision >= 0){
+ for(i=0; i!=precision && *s; i++){
+ putc(*s++, f);
+ n++;
+ }
+ } else{
+ for(i=0;*s;i++){
+ putc(*s++, f);
+ n++;
+ }
+ }
+ if(flags&LEFT){
+ for(; i<width; i++){
+ putc(' ', f);
+ n++;
+ }
+ }
+ return n;
+}
+
+static int
+ocvt_n(FILE *f, va_list *args, int flags, int width, int precision)
+{
+#pragma ref f
+#pragma ref width
+#pragma ref precision
+ if(flags&SHORT)
+ *va_arg(*args, short *) = nprint;
+ else if(flags&LONG)
+ *va_arg(*args, long *) = nprint;
+ else
+ *va_arg(*args, int *) = nprint;
+ return 0;
+}
+
+/*
+ * Generic fixed-point conversion
+ * f is the output FILE *;
+ * args is the va_list * from which to get the number;
+ * flags, width and precision are the results of printf-cracking;
+ * radix is the number base to print in;
+ * alphabet is the set of digits to use;
+ * prefix is the prefix to print before non-zero numbers when
+ * using ``alternate form.''
+ */
+static int
+ocvt_fixed(FILE *f, va_list *args, int flags, int width, int precision,
+ int radix, int sgned, char alphabet[], char *prefix)
+{
+ char digits[128]; /* no reasonable machine will ever overflow this */
+ char *sign;
+ char *dp;
+ long snum;
+ unsigned long num;
+ int nout, npad, nlzero;
+
+ if(sgned){
+ if(flags&PTR) snum = (long)va_arg(*args, void *);
+ else if(flags&SHORT) snum = va_arg(*args, short);
+ else if(flags&LONG) snum = va_arg(*args, long);
+ else snum = va_arg(*args, int);
+ if(snum < 0){
+ sign = "-";
+ num = -snum;
+ } else{
+ if(flags&SIGN) sign = "+";
+ else if(flags&SPACE) sign = " ";
+ else sign = "";
+ num = snum;
+ }
+ } else {
+ sign = "";
+ if(flags&PTR) num = (long)va_arg(*args, void *);
+ else if(flags&SHORT) num = va_arg(*args, unsigned short);
+ else if(flags&LONG) num = va_arg(*args, unsigned long);
+ else num = va_arg(*args, unsigned int);
+ }
+ if(num == 0) prefix = "";
+ dp = digits;
+ do{
+ *dp++ = alphabet[num%radix];
+ num /= radix;
+ }while(num);
+ if(precision==0 && dp-digits==1 && dp[-1]=='0')
+ dp--;
+ nlzero = precision-(dp-digits);
+ if(nlzero < 0) nlzero = 0;
+ if(flags&ALT){
+ if(radix == 8) if(dp[-1]=='0' || nlzero) prefix = "";
+ }
+ else prefix = "";
+ nout = dp-digits+nlzero+strlen(prefix)+strlen(sign);
+ npad = width-nout;
+ if(npad < 0) npad = 0;
+ nout += npad;
+ if(!(flags&LEFT)){
+ if(flags&ZPAD && precision <= 0){
+ fputs(sign, f);
+ fputs(prefix, f);
+ while(npad){
+ putc('0', f);
+ --npad;
+ }
+ } else{
+ while(npad){
+ putc(' ', f);
+ --npad;
+ }
+ fputs(sign, f);
+ fputs(prefix, f);
+ }
+ while(nlzero){
+ putc('0', f);
+ --nlzero;
+ }
+ while(dp!=digits) putc(*--dp, f);
+ }
+ else{
+ fputs(sign, f);
+ fputs(prefix, f);
+ while(nlzero){
+ putc('0', f);
+ --nlzero;
+ }
+ while(dp != digits) putc(*--dp, f);
+ while(npad){
+ putc(' ', f);
+ --npad;
+ }
+ }
+ return nout;
+}
+
+static int
+ocvt_X(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ return ocvt_fixed(f, args, flags, width, precision, 16, 0, "0123456789ABCDEF", "0X");
+}
+
+static int
+ocvt_d(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ return ocvt_fixed(f, args, flags, width, precision, 10, 1, "0123456789", "");
+}
+
+static int
+ocvt_o(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ return ocvt_fixed(f, args, flags, width, precision, 8, 0, "01234567", "0");
+}
+
+static int
+ocvt_p(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ return ocvt_fixed(f, args, flags|PTR|ALT, width, precision, 16, 0,
+ "0123456789ABCDEF", "0X");
+}
+
+static int
+ocvt_u(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ return ocvt_fixed(f, args, flags, width, precision, 10, 0, "0123456789", "");
+}
+
+static int
+ocvt_x(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ return ocvt_fixed(f, args, flags, width, precision, 16, 0, "0123456789abcdef", "0x");
+}
+
+static int ocvt_flt(FILE *, va_list *, int, int, int, char);
+
+static int
+ocvt_E(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ return ocvt_flt(f, args, flags, width, precision, 'E');
+}
+
+static int
+ocvt_G(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ return ocvt_flt(f, args, flags, width, precision, 'G');
+}
+
+static int
+ocvt_e(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ return ocvt_flt(f, args, flags, width, precision, 'e');
+}
+
+static int
+ocvt_f(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ return ocvt_flt(f, args, flags, width, precision, 'f');
+}
+
+static int
+ocvt_g(FILE *f, va_list *args, int flags, int width, int precision)
+{
+ return ocvt_flt(f, args, flags, width, precision, 'g');
+}
+
+static int
+ocvt_flt(FILE *f, va_list *args, int flags, int width, int precision, char afmt)
+{
+ int echr;
+ char *digits, *edigits;
+ int exponent;
+ char fmt;
+ int sign;
+ int ndig;
+ int nout, i;
+ char ebuf[20]; /* no sensible machine will overflow this */
+ char *eptr;
+ double d;
+
+ echr = 'e';
+ fmt = afmt;
+ d = va_arg(*args, double);
+ if(precision < 0) precision = 6;
+ switch(fmt){
+ case 'f':
+ digits = dtoa(d, 3, precision, &exponent, &sign, &edigits);
+ break;
+ case 'E':
+ echr = 'E';
+ fmt = 'e';
+ /* fall through */
+ case 'e':
+ digits = dtoa(d, 2, 1+precision, &exponent, &sign, &edigits);
+ break;
+ case 'G':
+ echr = 'E';
+ /* fall through */
+ case 'g':
+ if (precision > 0)
+ digits = dtoa(d, 2, precision, &exponent, &sign, &edigits);
+ else {
+ digits = dtoa(d, 0, precision, &exponent, &sign, &edigits);
+ precision = edigits - digits;
+ if (exponent > precision && exponent <= precision + 4)
+ precision = exponent;
+ }
+ if(exponent >= -3 && exponent <= precision){
+ fmt = 'f';
+ precision -= exponent;
+ }else{
+ fmt = 'e';
+ --precision;
+ }
+ break;
+ }
+ if (exponent == 9999) {
+ /* Infinity or Nan */
+ precision = 0;
+ exponent = edigits - digits;
+ fmt = 'f';
+ }
+ ndig = edigits-digits;
+ if((afmt=='g' || afmt=='G') && !(flags&ALT)){ /* knock off trailing zeros */
+ if(fmt == 'f'){
+ if(precision+exponent > ndig) {
+ precision = ndig - exponent;
+ if(precision < 0)
+ precision = 0;
+ }
+ }
+ else{
+ if(precision > ndig-1) precision = ndig-1;
+ }
+ }
+ nout = precision; /* digits after decimal point */
+ if(precision!=0 || flags&ALT) nout++; /* decimal point */
+ if(fmt=='f' && exponent>0) nout += exponent; /* digits before decimal point */
+ else nout++; /* there's always at least one */
+ if(sign || flags&(SPACE|SIGN)) nout++; /* sign */
+ if(fmt != 'f'){ /* exponent */
+ eptr = ebuf;
+ for(i=exponent<=0?1-exponent:exponent-1; i; i/=10)
+ *eptr++ = '0' + i%10;
+ while(eptr<ebuf+2) *eptr++ = '0';
+ nout += eptr-ebuf+2; /* e+99 */
+ }
+ if(!(flags&ZPAD) && !(flags&LEFT))
+ while(nout < width){
+ putc(' ', f);
+ nout++;
+ }
+ if(sign) putc('-', f);
+ else if(flags&SIGN) putc('+', f);
+ else if(flags&SPACE) putc(' ', f);
+ if(flags&ZPAD)
+ while(nout < width){
+ putc('0', f);
+ nout++;
+ }
+ if(fmt == 'f'){
+ for(i=0; i<exponent; i++) putc(i<ndig?digits[i]:'0', f);
+ if(i == 0) putc('0', f);
+ if(precision>0 || flags&ALT) putc('.', f);
+ for(i=0; i!=precision; i++)
+ putc(0<=i+exponent && i+exponent<ndig?digits[i+exponent]:'0', f);
+ }
+ else{
+ putc(digits[0], f);
+ if(precision>0 || flags&ALT) putc('.', f);
+ for(i=0; i!=precision; i++) putc(i<ndig-1?digits[i+1]:'0', f);
+ }
+ if(fmt != 'f'){
+ putc(echr, f);
+ putc(exponent<=0?'-':'+', f);
+ while(eptr>ebuf) putc(*--eptr, f);
+ }
+ while(nout < width){
+ putc(' ', f);
+ nout++;
+ }
+ freedtoa(digits);
+ return nout;
+}
diff --git a/sys/src/libstdio/vfscanf.c b/sys/src/libstdio/vfscanf.c
new file mode 100755
index 000000000..d17dcf2c4
--- /dev/null
+++ b/sys/src/libstdio/vfscanf.c
@@ -0,0 +1,364 @@
+/*
+ * pANS stdio -- vfscanf
+ */
+#include "iolib.h"
+#include <ctype.h>
+static int icvt_f(FILE *f, va_list *args, int store, int width, int type);
+static int icvt_x(FILE *f, va_list *args, int store, int width, int type);
+static int icvt_sq(FILE *f, va_list *args, int store, int width, int type);
+static int icvt_c(FILE *f, va_list *args, int store, int width, int type);
+static int icvt_d(FILE *f, va_list *args, int store, int width, int type);
+static int icvt_i(FILE *f, va_list *args, int store, int width, int type);
+static int icvt_n(FILE *f, va_list *args, int store, int width, int type);
+static int icvt_o(FILE *f, va_list *args, int store, int width, int type);
+static int icvt_p(FILE *f, va_list *args, int store, int width, int type);
+static int icvt_s(FILE *f, va_list *args, int store, int width, int type);
+static int icvt_u(FILE *f, va_list *args, int store, int width, int type);
+static int (*icvt[])(FILE *, va_list *, int, int, int)={
+0, 0, 0, 0, 0, 0, 0, 0, /* ^@ ^A ^B ^C ^D ^E ^F ^G */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^H ^I ^J ^K ^L ^M ^N ^O */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^P ^Q ^R ^S ^T ^U ^V ^W */
+0, 0, 0, 0, 0, 0, 0, 0, /* ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
+0, 0, 0, 0, 0, 0, 0, 0, /* sp ! " # $ % & ' */
+0, 0, 0, 0, 0, 0, 0, 0, /* ( ) * + , - . / */
+0, 0, 0, 0, 0, 0, 0, 0, /* 0 1 2 3 4 5 6 7 */
+0, 0, 0, 0, 0, 0, 0, 0, /* 8 9 : ; < = > ? */
+0, 0, 0, 0, 0, icvt_f, 0, icvt_f, /* @ A B C D E F G */
+0, 0, 0, 0, 0, 0, 0, 0, /* H I J K L M N O */
+0, 0, 0, 0, 0, 0, 0, 0, /* P Q R S T U V W */
+icvt_x, 0, 0, icvt_sq,0, 0, 0, 0, /* X Y Z [ \ ] ^ _ */
+0, 0, 0, icvt_c, icvt_d, icvt_f, icvt_f, icvt_f, /* ` a b c d e f g */
+0, icvt_i, 0, 0, 0, 0, icvt_n, icvt_o, /* h i j k l m n o */
+icvt_p, 0, 0, icvt_s, 0, icvt_u, 0, 0, /* p q r s t u v w */
+icvt_x, 0, 0, 0, 0, 0, 0, 0, /* x y z { | } ~ ^? */
+
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+
+};
+#define ngetc(f) (nread++, getc(f))
+#define nungetc(c, f) (--nread, ungetc((c), f))
+#define wgetc(c, f, out) if(width--==0) goto out; (c)=ngetc(f)
+#define wungetc(c, f) (++width, nungetc(c, f))
+static int nread, ncvt;
+static const char *fmtp;
+
+int vfscanf(FILE *f, const char *s, va_list args){
+ int c, width, type, store;
+ nread=0;
+ ncvt=0;
+ fmtp=s;
+ for(;*fmtp;fmtp++) switch(*fmtp){
+ default:
+ if(isspace(*fmtp)){
+ do
+ c=ngetc(f);
+ while(isspace(c));
+ if(c==EOF) return ncvt?ncvt:EOF;
+ nungetc(c, f);
+ break;
+ }
+ NonSpecial:
+ c=ngetc(f);
+ if(c==EOF) return ncvt?ncvt:EOF;
+ if(c!=*fmtp){
+ nungetc(c, f);
+ return ncvt;
+ }
+ break;
+ case '%':
+ fmtp++;
+ if(*fmtp!='*') store=1;
+ else{
+ store=0;
+ fmtp++;
+ }
+ if('0'<=*fmtp && *fmtp<='9'){
+ width=0;
+ while('0'<=*fmtp && *fmtp<='9') width=width*10 + *fmtp++ - '0';
+ }
+ else
+ width=-1;
+ type=*fmtp=='h' || *fmtp=='l' || *fmtp=='L'?*fmtp++:'n';
+ if(!icvt[*fmtp]) goto NonSpecial;
+ if(!(*icvt[*fmtp])(f, &args, store, width, type))
+ return ncvt?ncvt:EOF;
+ if(*fmtp=='\0') break;
+ if(store) ncvt++;
+ }
+ return ncvt;
+}
+static int icvt_n(FILE *f, va_list *args, int store, int width, int type){
+#pragma ref f
+#pragma ref width
+ if(store){
+ --ncvt; /* this assignment doesn't count! */
+ switch(type){
+ case 'h': *va_arg(*args, short *)=nread; break;
+ case 'n': *va_arg(*args, int *)=nread; break;
+ case 'l':
+ case 'L': *va_arg(*args, long *)=nread; break;
+ }
+ }
+ return 1;
+}
+#define SIGNED 1
+#define UNSIGNED 2
+#define POINTER 3
+/*
+ * Generic fixed-point conversion
+ * f is the input FILE *;
+ * args is the va_list * into which to store the number;
+ * store is a flag to enable storing;
+ * width is the maximum field width;
+ * type is 'h' 'l' or 'L', the scanf type modifier;
+ * unsgned is SIGNED, UNSIGNED or POINTER, giving part of the type to store in;
+ * base is the number base -- if 0, C number syntax is used.
+ */
+static int icvt_fixed(FILE *f, va_list *args,
+ int store, int width, int type, int unsgned, int base){
+ unsigned long int num=0;
+ int sign=1, ndig=0, dig;
+ int c;
+ do
+ c=ngetc(f);
+ while(isspace(c));
+ if(width--==0){
+ nungetc(c, f);
+ goto Done;
+ }
+ if(c=='+'){
+ wgetc(c, f, Done);
+ }
+ else if(c=='-'){
+ sign=-1;
+ wgetc(c, f, Done);
+ }
+ switch(base){
+ case 0:
+ if(c=='0'){
+ wgetc(c, f, Done);
+ if(c=='x' || c=='X'){
+ wgetc(c, f, Done);
+ base=16;
+ }
+ else{
+ ndig=1;
+ base=8;
+ }
+ }
+ else
+ base=10;
+ break;
+ case 16:
+ if(c=='0'){
+ wgetc(c, f, Done);
+ if(c=='x' || c=='X'){
+ wgetc(c, f, Done);
+ }
+ else ndig=1;
+ }
+ break;
+ }
+ while('0'<=c && c<='9' || 'a'<=c && c<='f' || 'A'<=c && c<='F'){
+ dig='0'<=c && c<='9'?c-'0':'a'<=c && c<='f'?c-'a'+10:c-'A'+10;
+ if(dig>=base) break;
+ ndig++;
+ num=num*base+dig;
+ wgetc(c, f, Done);
+ }
+ nungetc(c, f);
+Done:
+ if(ndig==0) return 0;
+ if(store){
+ switch(unsgned){
+ case SIGNED:
+ switch(type){
+ case 'h': *va_arg(*args, short *)=num*sign; break;
+ case 'n': *va_arg(*args, int *)=num*sign; break;
+ case 'l':
+ case 'L': *va_arg(*args, long *)=num*sign; break;
+ }
+ break;
+ case UNSIGNED:
+ switch(type){
+ case 'h': *va_arg(*args, unsigned short *)=num*sign; break;
+ case 'n': *va_arg(*args, unsigned int *)=num*sign; break;
+ case 'l':
+ case 'L': *va_arg(*args, unsigned long *)=num*sign; break;
+ }
+ break;
+ case POINTER:
+ *va_arg(*args, void **)=(void *)(num*sign); break;
+ }
+ }
+ return 1;
+}
+static int icvt_d(FILE *f, va_list *args, int store, int width, int type){
+ return icvt_fixed(f, args, store, width, type, SIGNED, 10);
+}
+static int icvt_x(FILE *f, va_list *args, int store, int width, int type){
+ return icvt_fixed(f, args, store, width, type, UNSIGNED, 16);
+}
+static int icvt_o(FILE *f, va_list *args, int store, int width, int type){
+ return icvt_fixed(f, args, store, width, type, UNSIGNED, 8);
+}
+static int icvt_i(FILE *f, va_list *args, int store, int width, int type){
+ return icvt_fixed(f, args, store, width, type, SIGNED, 0);
+}
+static int icvt_u(FILE *f, va_list *args, int store, int width, int type){
+ return icvt_fixed(f, args, store, width, type, UNSIGNED, 10);
+}
+static int icvt_p(FILE *f, va_list *args, int store, int width, int type){
+ return icvt_fixed(f, args, store, width, type, POINTER, 16);
+}
+#define NBUF 509
+static int icvt_f(FILE *f, va_list *args, int store, int width, int type){
+ char buf[NBUF+1];
+ char *s=buf;
+ int c, ndig=0, ndpt=0, nexp=1;
+ if(width<0 || NBUF<width) width=NBUF; /* bug -- no limit specified in ansi */
+ do
+ c=ngetc(f);
+ while(isspace(c));
+ if(width--==0){
+ nungetc(c, f);
+ goto Done;
+ }
+ if(c=='+' || c=='-'){
+ *s++=c;
+ wgetc(c, f, Done);
+ }
+ while('0'<=c && c<='9' || ndpt==0 && c=='.'){
+ if(c=='.') ndpt++;
+ else ndig++;
+ *s++=c;
+ wgetc(c, f, Done);
+ }
+ if(c=='e' || c=='E'){
+ *s++=c;
+ nexp=0;
+ wgetc(c, f, Done);
+ if(c=='+' || c=='-'){
+ *s++=c;
+ wgetc(c, f, Done);
+ }
+ while('0'<=c && c<='9'){
+ *s++=c;
+ nexp++;
+ wgetc(c, f, Done);
+ }
+ }
+ nungetc(c, f);
+Done:
+ if(ndig==0 || nexp==0) return 0;
+ *s='\0';
+ if(store) switch(type){
+ case 'h':
+ case 'n': *va_arg(*args, float *)=atof(buf); break;
+ case 'L': /* bug -- should store in a long double */
+ case 'l': *va_arg(*args, double *)=atof(buf); break;
+ }
+ return 1;
+}
+static int icvt_s(FILE *f, va_list *args, int store, int width, int type){
+#pragma ref type
+ int c, nn;
+ register char *s;
+ if(store) s=va_arg(*args, char *);
+ do
+ c=ngetc(f);
+ while(isspace(c));
+ if(width--==0){
+ nungetc(c, f);
+ goto Done;
+ }
+ nn=0;
+ while(!isspace(c)){
+ if(c==EOF){
+ nread--;
+ if(nn==0) return 0;
+ else goto Done;
+ }
+ nn++;
+ if(store) *s++=c;
+ wgetc(c, f, Done);
+ }
+ nungetc(c, f);
+Done:
+ if(store) *s='\0';
+ return 1;
+}
+static int icvt_c(FILE *f, va_list *args, int store, int width, int type){
+#pragma ref type
+ int c;
+ register char *s;
+ if(store) s=va_arg(*args, char *);
+ if(width<0) width=1;
+ for(;;){
+ wgetc(c, f, Done);
+ if(c==EOF) return 0;
+ if(store) *s++=c;
+ }
+Done:
+ return 1;
+}
+static int match(int c, const char *pat){
+ int ok=1;
+ if(*pat=='^'){
+ ok=!ok;
+ pat++;
+ }
+ while(pat!=fmtp){
+ if(pat+2<fmtp && pat[1]=='-'){
+ if(pat[0]<=c && c<=pat[2]
+ || pat[2]<=c && c<=pat[0])
+ return ok;
+ pat+=2;
+ }
+ else if(c==*pat) return ok;
+ pat++;
+ }
+ return !ok;
+}
+static int icvt_sq(FILE *f, va_list *args, int store, int width, int type){
+#pragma ref type
+ int c, nn;
+ register char *s;
+ register const char *pat;
+ pat=++fmtp;
+ if(*fmtp=='^') fmtp++;
+ if(*fmtp!='\0') fmtp++;
+ while(*fmtp!='\0' && *fmtp!=']') fmtp++;
+ if(store) s=va_arg(*args, char *);
+ nn=0;
+ for(;;){
+ wgetc(c, f, Done);
+ if(c==EOF){
+ nread--;
+ if(nn==0) return 0;
+ else goto Done;
+ }
+ if(!match(c, pat)) break;
+ if(store) *s++=c;
+ nn++;
+ }
+ nungetc(c, f);
+Done:
+ if(store) *s='\0';
+ return 1;
+}
diff --git a/sys/src/libstdio/vprintf.c b/sys/src/libstdio/vprintf.c
new file mode 100755
index 000000000..6576219eb
--- /dev/null
+++ b/sys/src/libstdio/vprintf.c
@@ -0,0 +1,7 @@
+/*
+ * pANS stdio -- vprintf
+ */
+#include "iolib.h"
+int vprintf(const char *fmt, va_list args){
+ return vfprintf(stdout, fmt, args);
+}
diff --git a/sys/src/libstdio/vsnprintf.c b/sys/src/libstdio/vsnprintf.c
new file mode 100755
index 000000000..16af2af98
--- /dev/null
+++ b/sys/src/libstdio/vsnprintf.c
@@ -0,0 +1,14 @@
+/*
+ * pANS stdio -- vsnprintf
+ */
+#include "iolib.h"
+int vsnprintf(char *buf, int nbuf, const char *fmt, va_list args){
+ int n;
+ FILE *f=sopenw();
+ if(f==NULL)
+ return 0;
+ setvbuf(f, buf, _IOFBF, nbuf);
+ n=vfprintf(f, fmt, args);
+ sclose(f);
+ return n;
+}
diff --git a/sys/src/libstdio/vsprintf.c b/sys/src/libstdio/vsprintf.c
new file mode 100755
index 000000000..63fb3e736
--- /dev/null
+++ b/sys/src/libstdio/vsprintf.c
@@ -0,0 +1,14 @@
+/*
+ * pANS stdio -- vsprintf
+ */
+#include "iolib.h"
+int vsprintf(char *buf, const char *fmt, va_list args){
+ int n;
+ FILE *f=sopenw();
+ if(f==NULL)
+ return 0;
+ setvbuf(f, buf, _IOFBF, 100000);
+ n=vfprintf(f, fmt, args);
+ sclose(f);
+ return n;
+}