From c65794b50b1bc729e7a4e940b76a973afa3030b9 Mon Sep 17 00:00:00 2001 From: Nicholas Noll Date: Thu, 11 Nov 2021 14:49:35 -0800 Subject: feat: libfmt prototype added from plan9 --- include/libfmt.h | 50 ++- sys/libfmt/buffer.c | 60 +++ sys/libfmt/do.c | 627 ++++++++++++++++++++++++++-- sys/libfmt/esprint.c | 14 + sys/libfmt/float.c | 1079 +++++++++++++++++++++++++++++++++++++++++++++++++ sys/libfmt/fprint.c | 14 + sys/libfmt/internal.h | 2 + sys/libfmt/locale.c | 16 + sys/libfmt/nsprint.c | 14 + sys/libfmt/open.c | 34 ++ sys/libfmt/print.c | 13 + sys/libfmt/rules.mk | 36 ++ sys/libfmt/sprint.c | 19 + sys/libfmt/test.c | 73 ++++ sys/libfmt/vesprint.c | 26 ++ sys/libfmt/vfprint.c | 19 + sys/libfmt/vnsprint.c | 26 ++ sys/libfmt/vprint.c | 19 + sys/libfmt/vwrite.c | 26 ++ sys/libfmt/write.c | 22 + sys/rules.mk | 3 + 21 files changed, 2131 insertions(+), 61 deletions(-) create mode 100644 sys/libfmt/buffer.c create mode 100644 sys/libfmt/esprint.c create mode 100644 sys/libfmt/float.c create mode 100644 sys/libfmt/fprint.c create mode 100644 sys/libfmt/locale.c create mode 100644 sys/libfmt/nsprint.c create mode 100644 sys/libfmt/open.c create mode 100644 sys/libfmt/rules.mk create mode 100644 sys/libfmt/sprint.c create mode 100644 sys/libfmt/test.c create mode 100644 sys/libfmt/vesprint.c create mode 100644 sys/libfmt/vfprint.c create mode 100644 sys/libfmt/vnsprint.c create mode 100644 sys/libfmt/vprint.c create mode 100644 sys/libfmt/vwrite.c create mode 100644 sys/libfmt/write.c diff --git a/include/libfmt.h b/include/libfmt.h index 17effc6..7ba40a0 100644 --- a/include/libfmt.h +++ b/include/libfmt.h @@ -9,13 +9,21 @@ struct fmt·State char *cur; char *end; } buffer; - int (*flush)(fmt·State *); int n; + va_list args; rune verb; ulong flag; int width; int prec; + char *thousands, *groups, *decimal; + + void *file; + int (*flush)(fmt·State *); + struct { + void *heap; + mem·Reallocator mem; + }; }; #define iota(x) (1 << (x)) @@ -41,22 +49,24 @@ enum #undef iota /* normal printing interface */ -int fmt·print(char *fmt, ...); -int fmt·fprint(int fd, char *fmt, ...); - -int fmt·sprint(char *buf, char *fmt, ...); -int fmt·nsprint(int len, char *buf, char *fmt, ...); -int fmt·esprint(char *buf, char *end, char *fmt, ...); - -int fmt·vprint(char *fmt, va_list args); -int fmt·vfprint(int fd, char *fmt, va_list args); -int fmt·vwrite(char *buf, char *fmt, va_list args); -int fmt·vnwrite(int len, char *buf, char *fmt, va_list args); -int fmt·vewrite(char *buf, char *end, char *fmt, va_list args); - -/* low-level interface: custom printing verbs */ -int fmt·do(fmt·State *, char *fmt); -int fmt·put(fmt·State *, char *fmt, ...); -int fmt·vput(fmt·State *, char *fmt, va_list args); -int fmt·install(fmt·State *, int (*put)(fmt·State*)); -int fmt·flush(fmt·State *); +int fmt·print(char *fmt, ...); +int fmt·fprint(int fd, char *fmt, ...); + +int fmt·sprint(char *buf, char *fmt, ...); +int fmt·nsprint(int len, char *buf, char *fmt, ...); +char *fmt·esprint(char *buf, char *end, char *fmt, ...); + +int fmt·vprint(char *fmt, va_list args); +int fmt·vfprint(int fd, char *fmt, va_list args); +int fmt·vnsprint(int len, char *buf, char *fmt, va_list args); +char *fmt·vesprint(char *buf, char *end, char *fmt, va_list args); + +/* low-level interface: used for custom printing verbs */ +int fmt·open(int fd, int len, char *buf, fmt·State *); // creates a buffer on a file +int fmt·make(mem·Reallocator mem, void *heap, fmt·State *); // creates an in-memory buffer +void fmt·free(fmt·State *); // releases an in-memory buffer + +int fmt·do(fmt·State *, char *fmt); +int fmt·write(fmt·State *, char *fmt, ...); +int fmt·vwrite(fmt·State *, char *fmt, va_list args); +int fmt·install(int c, int (*format)(fmt·State *)); diff --git a/sys/libfmt/buffer.c b/sys/libfmt/buffer.c new file mode 100644 index 0000000..0099e72 --- /dev/null +++ b/sys/libfmt/buffer.c @@ -0,0 +1,60 @@ +#include "internal.h" + +static int +flush(fmt·State *io) +{ + int n; + char *s; + + void *heap = io->heap; + mem·Reallocator mem = io->mem; + + if(!io->buffer.beg) + return 0; + + n = 2*(uintptr)io->file; + s = io->buffer.beg; + + io->buffer.beg = mem.realloc(heap, io->buffer.beg, n, 1); + if(!io->buffer.beg){ + io->file = io->buffer.cur = io->buffer.end = nil; + mem.free(heap, s); + return 0; + } + io->file = (void*)(uintptr)n; + io->buffer.cur = io->buffer.beg + (io->buffer.cur - s); + io->buffer.end = io->buffer.beg + n - 1; + + return 1; +} + +int +fmt·make(mem·Reallocator mem, void *heap, fmt·State *io) +{ + int n; + + memset(io, 0, sizeof(*io)); + + n = 32; + io->buffer.beg = io->buffer.cur = mem.alloc(heap, n, 1); + if(!io->buffer.beg) + return -1; + io->buffer.end = io->buffer.beg + n - 1; + + io->flush = flush; + io->file = (void*)(uintptr)n; + io->n = 0; + + fmt·setlocale(io, nil, nil, nil); + return 0; +} + +void +fmt·free(fmt·State *io) +{ + void *heap = io->heap; + mem·Reallocator mem = io->mem; + + mem.free(heap, io->buffer.beg); + io->buffer.beg = io->buffer.cur = io->buffer.end = nil; +} diff --git a/sys/libfmt/do.c b/sys/libfmt/do.c index 3b8ff5a..0a930b0 100644 --- a/sys/libfmt/do.c +++ b/sys/libfmt/do.c @@ -6,45 +6,68 @@ #define atomic·load atomic_load #define atomic·store atomic_store +// ----------------------------------------------------------------------- +// globals + +/* built in verbs */ +static int fmtflag(fmt·State *); +static int fmtpercent(fmt·State *); +static int fmtrune(fmt·State *); +static int fmtfloat(fmt·State *); +static int fmtutf8(fmt·State *); +static int fmtint(fmt·State *); +static int fmtchar(fmt·State *); +static int fmtcount(fmt·State *); +static int fmtstring(fmt·State *); +static int fmterror(fmt·State *); + +static int badfmt(fmt·State *); + static struct { atomic int len; Verb verb[MaxFmt]; } formatter = { - ATOMIC_VAR_INIT(27), + ATOMIC_VAR_INIT(30), { - {' ', ·fmtflag}, - {'#', ·fmtflag}, - {'%', ·fmtpercent}, - {'\'',·fmtflag}, - {'+', ·fmtflag}, - {',', ·fmtflag}, - {'-', ·fmtflag}, - {'C', ·fmtrune}, - {'E', ·fmtfloat}, - {'G', ·fmtfloat}, - {'S', ·fmtutf8}, - {'X', ·fmtint}, - {'b', ·fmtint}, - {'c', ·fmtchar}, - {'d', ·fmtint}, - {'e', ·fmtfloat}, - {'f', ·fmtfloat}, - {'g', ·fmtfloat}, - {'h', ·fmtflag}, - {'l', ·fmtflag}, - {'n', ·fmtcount}, - {'o', ·fmtint}, - {'p', ·fmtint}, - {'r', ·fmterr}, - {'s', ·fmtstr}, - {'u', ·fmtflag}, - {'u', ·fmtint}, - {'x', ·fmtint}, + {' ', fmtflag}, + {'#', fmtflag}, + {'%', fmtpercent}, + {'\'',fmtflag}, + {'+', fmtflag}, + {',', fmtflag}, + {'-', fmtflag}, + {'C', fmtrune}, + {'E', fmtfloat}, + {'F', fmtfloat}, + {'G', fmtfloat}, + {'L', fmtflag}, + {'S', fmtutf8}, + {'X', fmtint}, + {'b', fmtint}, + {'c', fmtchar}, + {'d', fmtint}, + {'e', fmtfloat}, + {'f', fmtfloat}, + {'g', fmtfloat}, + {'h', fmtflag}, + {'i', fmtint}, + {'l', fmtflag}, + {'n', fmtcount}, + {'o', fmtint}, + {'p', fmtint}, + {'r', fmterror}, + {'s', fmtstring}, + {'U', fmtflag}, + {'u', fmtint}, + {'x', fmtint}, } }; +// ----------------------------------------------------------------------- +// internal functions + static Formatter format(int c) { @@ -55,7 +78,7 @@ format(int c) return v->fmt; } - return ·fmterr; + return badfmt; } static char * @@ -81,6 +104,7 @@ dispatch(fmt·State *io, char *fmt) */ for(;;){ fmt += utf8·decode(fmt, &r); + io->verb = r; switch(r){ case 0: @@ -115,7 +139,7 @@ dispatch(fmt·State *io, char *fmt) i = va_arg(io->args, int); if(i < 0){ if(io->flag&fmt·Prec){ - io->flag ^= fmt·Prec; + io->flag = ~fmt·Prec; io->prec = 0; } i = -i; @@ -123,6 +147,7 @@ dispatch(fmt·State *io, char *fmt) } goto number; } + n = format(r)(io); if(n < 0) return nil; @@ -143,6 +168,500 @@ flush(fmt·State *io, char *b, int len) return io->buffer.cur; } +static int +pad(fmt·State *io, int n) +{ + int i; + char *b=io->buffer.cur, *e=io->buffer.end; + + for(i=0; i=e){ + if(!(b=flush(io, b, 1))) + return -1; + e = io->buffer.end; + } + *b++ = ' '; + } + + io->n += b - io->buffer.cur; + io->buffer.cur = b; + return 0; +} + +static int +copy(fmt·State *io, char *m, int sz, int n) +{ + ulong f; + rune r; + int nc, w, nb; + char *b, *e, *me; + + w = 0; + f = io->flag; + me = m + sz; + + if(f&fmt·Width) + w = io->width; + if(f&fmt·Prec && n > io->prec) + n = io->prec; + if(!(f&fmt·Left) && pad(io, w-n)<0) + return -1; + + b = io->buffer.cur; + e = io->buffer.end; + + for(nc=n; nc>0; nc--){ + r = *(uchar *)m; + if(utf8·onebyte(r)){ + nb=1; + m++; + }else if((me-m) >= UTFmax || utf8·canfit(m, me-m)){ + nb=utf8·decode(m, &r); + m+=n; + }else + break; + + if(b+n>e){ + if(!(b=flush(io, b, nb))) + return -1; + e = io->buffer.end; + } + b += utf8·encode(&r, b); + } + + io->n += b - io->buffer.cur; + io->buffer.cur = b; + if(f&fmt·Left && pad(io, w-n)<0) + return -1; + + return 0; +} + +static int +copyrune(fmt·State *io, rune *m, int n) +{ + ulong f; + rune r, *me; + int w, nb; + char *b, *e; + + w = 0; + f = io->flag; + + if(f&fmt·Width) + w = io->width; + if(f&fmt·Prec && n > io->prec) + n = io->prec; + + if(!(f&fmt·Left) && pad(io, w-n)<0) + return -1; + + b = io->buffer.cur; + e = io->buffer.end; + + for(me=m+n; m < me; m++){ + r = *m; + nb = utf8·runelen(r); + if(b + nb > e){ + if(!(b=flush(io, b, nb))) + return -1; + e = io->buffer.end; + } + b += utf8·encode(&r, b); + } + + io->n += b - io->buffer.cur; + io->buffer.cur = b; + if(f&fmt·Left && pad(io, w-n)<0) + return -1; + + return 0; +} + +static int +copystring(fmt·State *io, char *s) +{ + rune r; + int i,j; + + if(!s) + return copy(io, "", 5, 5); + + if(io->flag&fmt·Prec){ + i = 0; + for(j=0; j < io->prec && s[i]; j++) + i += utf8·decode(s+i, &r); + + return copy(io, s, i, j); + } + return copy(io, s, strlen(s), utf8·len(s)); +} + +static int +copyutf8(fmt·State *io, rune *s) +{ + rune *e; + int n,p; + + if(!s) + return copy(io, "", 5, 5); + + if(io->flag & fmt·Prec){ + p = io->prec; + for(n=0; n group){ + if((*groups)[1] != 0) + (*groups)++; + *digits = 1; + return 1; + } + return 0; +} + +// ----------------------------------------------------------------------- +// formatters + +static int +fmtchar(fmt·State *io) +{ + char x[1]; + x[0] = va_arg(io->args, int); + io->prec = 1; + + return copy(io, x, 1, 1); +} + +static int +fmtstring(fmt·State *io) +{ + char *s; + s = va_arg(io->args, char *); + return copystring(io, s); +} + +static int +fmterror(fmt·State *io) +{ + char *s; + s = strerror(errno); + return copystring(io, s); +} + +static int +fmtrune(fmt·State *io) +{ + rune x[1]; + + x[0] = va_arg(io->args, int); + return copyrune(io, x, 1); +} + +static int +fmtutf8(fmt·State *io) +{ + rune *s; + + s = va_arg(io->args, rune *); + return copyutf8(io, s); +} + +static int +fmtpercent(fmt·State *io) +{ + rune x[1]; + + x[0] = io->verb; + io->prec = 1; + return copyrune(io, x, 1); +} + +static int +fmtint(fmt·State *io) +{ + union{ + ulong u; + uvlong v; + } val; + int neg, base, i, n, f, w, isv; + int digits, bytes, runes, excess; + char *groups, *thousands; + char *p, *conv, buf[140]; + + f = io->flag; + neg = 0; + isv = 0; + val.u = 0; + + switch(io->verb){ + case 'o': case 'p': case 'u': case 'x': case 'X': + f |= fmt·Unsigned; + f &= ~(fmt·Sign|fmt·Space); + } + + /* set flags */ + if(io->verb=='p'){ + val.u = (ulong)va_arg(io->args, void*); + io->verb = 'x'; + f |= fmt·Unsigned; + }else if(f&fmt·Vlong){ + isv=1; + if(f&fmt·Unsigned) + val.v = va_arg(io->args, uvlong); + else + val.v = va_arg(io->args, vlong); + }else if(f&fmt·Long){ + if(f&fmt·Unsigned) + val.u = va_arg(io->args, ulong); + else + val.u = va_arg(io->args, long); + }else if(f&fmt·Byte){ + if(f&fmt·Unsigned) + val.u = (uchar)va_arg(io->args, int); + else + val.u = (char)va_arg(io->args, int); + }else if(f&fmt·Short){ + if(f&fmt·Unsigned) + val.u = (ushort)va_arg(io->args, int); + else + val.u = (short)va_arg(io->args, int); + }else{ + if(f&fmt·Unsigned) + val.u = va_arg(io->args, uint); + else + val.u = va_arg(io->args, int); + } + + conv = "0123456789abcdef"; + groups = "\4"; + thousands = io->thousands; + /* get base */ + switch(io->verb){ + case 'd': case 'i': case 'u': + base = 10; + groups = io->groups; + break; + case 'X': + conv = "0123456789ABCDEF"; + /*fallthrough*/ + case 'x': + base = 16; + thousands = ":"; + break; + case 'b': + base = 2; + thousands = ":"; + break; + case 'o': + base = 8; + break; + default: + return -1; + } + + /* check for negativity */ + if(!(f&fmt·Unsigned)){ + if(isv && (vlong)val.v < 0){ + val.v = -(vlong)val.v; + neg = 1; + }else if(!isv && (long)val.u < 0){ + val.u = -(long)val.u; + neg = 1; + } + } + + p = buf + sizeof(buf) - 1; + n = 0; + digits = 0; + excess = 0; + runes = utf8·len(thousands); + bytes = strlen(thousands); + +#define PARSE(VALUE) \ + while((VALUE)){ \ + i = (VALUE) % base; \ + (VALUE) /= base; \ + if((f&fmt·Comma) && n%4 == 3){ \ + *p-- = ','; \ + n++; \ + } \ + if((f&fmt·Apost) && needseperate(&digits, &groups)){ \ + n += runes; \ + excess += bytes - runes; \ + p -= bytes; \ + memmove(p+1, thousands, bytes); \ + } \ + *p-- = conv[i]; \ + n++; \ + } + if(isv) + PARSE(val.v) + else + PARSE(val.u) +#undef PARSE + + if(!n){ + if(!(f&fmt·Prec) || io->prec != 0 || (io->verb == 'o' && (f&fmt·Sharp))){ + *p-- = '0'; + n = 1; + if(f&fmt·Apost) + needseperate(&digits,&groups); + } + + if(io->verb == 'x' || io->verb == 'X') + f &= ~fmt·Sharp; + } + + for(w = io->prec; n < w && p > buf+3; n++){ + if((f&fmt·Apost) && needseperate(&digits, &groups)){ + n += runes; + excess += bytes - runes; + p -= bytes; + memmove(p+1, thousands, bytes); + } + *p-- = '0'; + } + + if(neg || (f&(fmt·Sign|fmt·Space))) + n++; + + if(f&fmt·Sharp){ + if(base==16) + n += 2; + else if(base == 8){ + if(p[1] == '0') + f &= ~fmt·Sharp; + else + n++; + } + } + + if(f&fmt·Zero && !(f & (fmt·Left|fmt·Prec))){ + w = 0; + if(f & fmt·Width) + w = io->width; + for(; n < w && p > buf+3; n++){ + if((f & fmt·Apost) && needseperate(&digits, &groups)){ + n += runes; + excess += bytes - runes; + p -= bytes; + memmove(p+1, thousands, bytes); + } + *p-- = '0'; + } + io->flag &= ~fmt·Width; + } + + if(f&fmt·Sharp){ + if(base==16) + *p-- = io->verb; + if(base==16 || base == 8) + *p-- = '0'; + } + + if(neg) + *p-- = '-'; + else if(f & fmt·Sign) + *p-- = '+'; + else if (f & fmt·Space) + *p-- = ' '; + + io->flag &= ~fmt·Prec; + return copy(io, p+1, n+excess, n); +} + +static int +fmtcount(fmt·State *io) +{ + void *p; + ulong f; + + f = io->flag; + p = va_arg(io->args, void*); + + if(f&fmt·Vlong) + *(vlong*)p = io->n; + else if(f&fmt·Long) + *(long*)p = io->n; + else if(f&fmt·Byte) + *(char*)p = io->n; + else if(f&fmt·Short) + *(short*)p = io->n; + else + *(int*)p = io->n; + + return 0; +} + +static int +fmtflag(fmt·State *io) +{ + switch(io->verb){ + case ',': io->flag |= fmt·Comma; break; + case '-': io->flag |= fmt·Left; break; + case '+': io->flag |= fmt·Sign; break; + case '#': io->flag |= fmt·Sharp; break; + case '\'': io->flag |= fmt·Apost; break; + case ' ': io->flag |= fmt·Space; break; + case 'u': io->flag |= fmt·Unsigned; break; + case 'L': io->flag |= fmt·Ldouble; break; + case 'h': + if(io->flag&fmt·Short) + io->flag |= fmt·Byte; + io->flag |= fmt·Short; + break; + case 'l': + if(io->flag&fmt·Long) + io->flag |= fmt·Vlong; + io->flag |= fmt·Long; + break; + } + return 1; +} + +static int +badfmt(fmt·State *io) +{ + int n; + char x[UTFmax+2]; + + x[0] = '%'; + n = 1 + utf8·encode(&io->verb, x+1); + x[n++] = '%'; + io->prec = n; + copy(io, x, n, n); + + return 0; +} + +#include "float.c" + +// ----------------------------------------------------------------------- +// exports + int fmt·do(fmt·State *io, char *fmt) { @@ -153,11 +672,10 @@ fmt·do(fmt·State *io, char *fmt) for(;;){ b = io->buffer.cur; e = io->buffer.end; - while((c = *(uchar *)fmt) && c != '%'){ if(utf8·onebyte(c)){ - if(b + 1 > e){ - if(!(b=flush(io, b, sizeof(*b)))) + if(b >= e){ + if(!(b=flush(io, b, 1))) return -1; e = io->buffer.end; } @@ -165,22 +683,49 @@ fmt·do(fmt·State *io, char *fmt) }else{ n = utf8·decode(fmt, &r); if(b + n > e){ - if(!(b=flush(io, b, sizeof(*b)))) + if(!(b=flush(io, b, n))) return -1; e = io->buffer.end; } while(n--) *b++ = *fmt++; } - fmt++; - io->n += b - io->buffer.cur; - io->buffer.cur = b; - if(c=='\0') /* we hit our nul terminator */ - return io->n = n; - io->buffer.end = e; } + fmt++; + io->n += b - io->buffer.cur; + io->buffer.cur = b; + if(!c) /* we hit our nul terminator */ + return io->n - n; + io->buffer.end = e; if(!(fmt=dispatch(io, fmt))) return -1; } } + +int +fmt·install(int verb, Formatter func) +{ + Verb *v; + int i, ret; + +lock: + if(verb <= 0 || verb >= 65536){ + ret = -1; + goto unlock; + } + if(!func) + func = badfmt; + + if((i = atomic·load(&formatter.len))==MaxFmt) + return -1; + + v = &formatter.verb[i]; + v->c = verb; + v->fmt = func; + + atomic·store(&formatter.len, i+1); + ret = 0; +unlock: + return ret; +} diff --git a/sys/libfmt/esprint.c b/sys/libfmt/esprint.c new file mode 100644 index 0000000..6d97340 --- /dev/null +++ b/sys/libfmt/esprint.c @@ -0,0 +1,14 @@ +#include "internal.h" + +char * +fmt·esprint(char *buf, char *end, char *fmt, ...) +{ + char *p; + va_list args; + + va_start(args, fmt); + p = fmt·vesprint(buf, end, fmt, args); + va_end(args); + + return p; +} diff --git a/sys/libfmt/float.c b/sys/libfmt/float.c new file mode 100644 index 0000000..f3b9d56 --- /dev/null +++ b/sys/libfmt/float.c @@ -0,0 +1,1079 @@ +#include "internal.h" + +#define FDIGIT 30 +#define FDEFLT 6 +#define NSIGNIF 17 + +static uvlong uvnan = ((uvlong)0x7FF00000<<32)|0x00000001; +static uvlong uvinf = ((uvlong)0x7FF00000<<32)|0x00000000; +static uvlong uvneginf = ((uvlong)0xFFF00000<<32)|0x00000000; + +static char *special[] = { "NaN", "NaN", "+Inf", "+Inf", "-Inf", "-Inf" }; + +static int +isNaN(double val) +{ + union{ + uvlong i; + double f; + }x; + + x.f = val; + return (x.i&uvinf) == uvinf && (x.i&~uvneginf) != 0; +} + +static double +NaN(void) +{ + union{ + uvlong i; + double f; + }x; + x.i = uvnan; + return x.f; +} + +static int +isInf(double val, int sign) +{ + union{ + uvlong i; + double f; + }x; + + x.f = val; + if(sign == 0) + return x.i == uvinf || x.i == uvneginf; + else if(sign == 1) + return x.i == uvinf; + else + return x.i == uvneginf; +} + +static double pows10[] = +{ + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, + 1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39, + 1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49, + 1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59, + 1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69, + 1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79, + 1e80, 1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88, 1e89, + 1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97, 1e98, 1e99, + 1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109, + 1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119, + 1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129, + 1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139, + 1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149, + 1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159, +}; + +static double +fpow10(int n) +{ + double d; + int neg; + + neg = 0; + if(n < 0){ + neg = 1; + n = -n; + } + + if(n NSIGNIF) + return 0; + + for(b = a+n-1; b >= a; b--){ + c = *b + 1; + if(c <= '9'){ + *b = c; + return 0; + } + *b = '0'; + } + /* + * need to overflow adding digit. + * shift number down and insert 1 at beginning. + * decimal is known to be 0s or we wouldn't + * have gotten this far. (e.g., 99999+1 => 00000) + */ + a[0] = '1'; + return 1; +} + +static int +sub1(char *a, int n) +{ + int c; + char *b; + + if(n < 0 || n > NSIGNIF) + return 0; + for(b = a+n-1; b >= a; b--){ + c = *b - 1; + if(c >= '0'){ + if(c == '0' && b == a){ + /* + * just zeroed the top digit; shift everyone up. + * decimal is known to be 9s or we wouldn't + * have gotten this far. (e.g., 10000-1 => 09999) + */ + *b = '9'; + return 1; + } + *b = c; + return 0; + } + *b = '9'; + } + /* + * can't get here. the number a is always normalized + * so that it has a nonzero first digit. + */ + abort(); +} + +// ----------------------------------------------------------------------- +// strtod + +#define Nbits 28 +#define Nmant 53 +#define Prec ((Nmant+Nbits+1)/Nbits) + +#define Sigbit (1<<(Prec*Nbits-Nmant)) /* first significant bit of Prec-th word */ +#define Ndig 1500 +#define One (ulong)(1<>1) +#define Maxe 310 + +#define Fsign (1<<0) /* found - */ +#define Fesign (1<<1) /* found e- */ +#define Fdpoint (1<<2) /* found . */ + +#define S0 0 /* _ _S0 +S1 #S2 .S3 */ +#define S1 1 /* _+ #S2 .S3 */ +#define S2 2 /* _+# #S2 .S4 eS5 */ +#define S3 3 /* _+. #S4 */ +#define S4 4 /* _+#.# #S4 eS5 */ +#define S5 5 /* _+#.#e +S6 #S7 */ +#define S6 6 /* _+#.#e+ #S7 */ +#define S7 7 /* _+#.#e+# #S7 */ + +typedef struct Tab Tab; +struct Tab +{ + int bp; + int siz; + char *cmp; +}; + +static ulong +umuldiv(ulong a, ulong b, ulong c) +{ + double d; + + d = ((double)a * (double)b) / (double)c; + if(d >= 4294967295.) + d = 4294967295.; + return (ulong)d; +} + +static void +frnorm(ulong *f) +{ + int i, c; + + c = 0; + for(i=Prec-1; i>0; i--) { + f[i] += c; + c = f[i] >> Nbits; + f[i] &= One-1; + } + f[0] += c; +} + +static int +fpcmp(char *a, ulong* f) +{ + ulong tf[Prec]; + int i, d, c; + + for(i=0; i> Nbits) + '0'; + tf[0] &= One-1; + + /* compare next digit */ + c = *a; + if(c == 0) { + if('0' < d) + return -1; + if(tf[0] != 0) + goto cont; + for(i=1; i d) + return +1; + if(c < d) + return -1; + a++; + cont:; +} +} + +static void +divby(char *a, int *na, int b) +{ + int n, c; + char *p; + + p = a; + n = 0; + while(n>>b == 0){ + c = *a++; + if(c == 0) { + while(n) { + c = n*10; + if(c>>b) + break; + n = c; + } + goto xx; + } + n = n*10 + c-'0'; + (*na)--; + } + for(;;){ + c = n>>b; + n -= c<>b; + n -= c<= (int)(arrlen(tab1))) + d = (int)(arrlen(tab1))-1; + t = tab1 + d; + b = t->bp; + if(memcmp(a, t->cmp, t->siz) > 0) + d--; + *dp -= d; + *bp += b; + divby(a, na, b); +} + +static void +mulby(char *a, char *p, char *q, int b) +{ + int n, c; + + n = 0; + *p = 0; + for(;;) { + q--; + if(q < a) + break; + c = *q - '0'; + c = (c<= (int)(arrlen(tab2))) + d = (int)(arrlen(tab2))-1; + t = tab2 + d; + b = t->bp; + if(memcmp(a, t->cmp, t->siz) < 0) + d--; + p = a + *na; + *bp -= b; + *dp += d; + *na += d; + mulby(a, p+d, p, b); +} + +static int +cmp(char *a, char *b) +{ + int c1, c2; + + while((c1 = *b++) != '\0') { + c2 = *a++; + if(isupper(c2)) + c2 = tolower(c2); + if(c1 != c2) + return 1; + } + return 0; +} + +double +fmtstrtod(char *as, char **aas) +{ + int na, ex, dp, bp, c, i, flag, state; + ulong low[Prec], hig[Prec], mid[Prec]; + double d; + char *s, a[Ndig]; + + flag = 0; /* Fsign, Fesign, Fdpoint */ + na = 0; /* number of digits of a[] */ + dp = 0; /* na of decimal point */ + ex = 0; /* exonent */ + + state = S0; + for(s=as;;s++){ + c = *s; + if('0' <= c && c <= '9'){ + switch(state){ + case S0: case S1: case S2: + state = S2; + break; + case S3: case S4: + state = S4; + break; + case S5: case S6: case S7: + state = S7; + ex = ex*10 + (c-'0'); + continue; + } + + if(na == 0 && c == '0'){ + dp--; + continue; + } + if(na < Ndig-50) + a[na++] = c; + continue; + } + switch(c){ + case '\t': case '\n': case '\v': case '\f': case '\r': case ' ': + if(state == S0) + continue; + break; + case '-': + if(state == S0) + flag |= Fsign; + else + flag |= Fesign; + case '+': + if(state == S0) + state = S1; + else + if(state == S5) + state = S6; + else + break; /* syntax */ + continue; + case '.': + flag |= Fdpoint; + dp = na; + if(state == S0 || state == S1){ + state = S3; + continue; + } + if(state == S2){ + state = S4; + continue; + } + break; + case 'e': case 'E': + if(state == S2 || state == S4){ + state = S5; + continue; + } + break; + } + break; + } + + /* clean up return char-pointer */ + switch(state) { + case S0: + if(cmp(s, "nan") == 0){ + if(aas != nil) + *aas = s+3; + goto retnan; + } + case S1: + if(cmp(s, "infinity") == 0){ + if(aas != nil) + *aas = s+8; + goto retinf; + } + if(cmp(s, "inf") == 0){ + if(aas != nil) + *aas = s+3; + goto retinf; + } + case S3: + if(aas != nil) + *aas = as; + goto ret0; /* no digits found */ + case S6: + s--; /* back over +- */ + case S5: + s--; /* back over e */ + break; + } + if(aas != nil) + *aas = s; + + if(flag & Fdpoint) + while(na > 0 && a[na-1] == '0') + na--; + if(na == 0) + goto ret0; /* zero */ + a[na] = 0; + if(!(flag & Fdpoint)) + dp = na; + if(flag & Fesign) + ex = -ex; + dp += ex; + if(dp < -Maxe){ + errno = ERANGE; + goto ret0; /* underflow by exp */ + } else + if(dp > +Maxe) + goto retinf; /* overflow by exp */ + + /* + * normalize the decimal ascii number + * to range .[5-9][0-9]* e0 + */ + bp = 0; /* binary exponent */ + while(dp > 0) + divascii(a, &na, &dp, &bp); + while(dp < 0 || a[0] < '5') + mulascii(a, &na, &dp, &bp); + + /* close approx by naive conversion */ + mid[0] = 0; + mid[1] = 1; + for(i=0; (c=a[i]) != '\0'; i++) { + mid[0] = mid[0]*10 + (c-'0'); + mid[1] = mid[1]*10; + if(i >= 8) + break; + } + low[0] = umuldiv(mid[0], One, mid[1]); + hig[0] = umuldiv(mid[0]+1, One, mid[1]); + for(i=1; i>= 1; + } + frnorm(mid); + + /* compare */ + c = fpcmp(a, mid); + if(c > 0) { + c = 1; + for(i=0; i= Sigbit/2) { + mid[Prec-1] += Sigbit; + frnorm(mid); + } + goto out; + +ret0: + return 0; + +retnan: + return NaN(); + +retinf: + /* Unix strtod requires these. Plan 9 would return Inf(0) or Inf(-1). */ + errno = ERANGE; + if(flag & Fsign) + return -HUGE_VAL; + return HUGE_VAL; + +out: + d = 0; + for(i=0; i 0) + *p++ = se[--i]; + + *p++ = '\0'; +} + +/* + * compute decimal integer m, exp such that: + * f = m*10^exp + * m is as short as possible with losing exactness + * assumes special cases (NaN, +Inf, -Inf) have been handled. + */ +static void +dtoa(double f, char *s, int *exp, int *neg, int *len) +{ + int c, d, e2, e, ee, i, ndigit, oerrno; + char buf[NSIGNIF+10]; + double g; + + oerrno = errno; + + *neg = 0; + if(f < 0){ + f = -f; + *neg = 1; + } + + if(f == 0){ + *exp = 0; + s[0] = '0'; + s[1] = 0; + *len = 1; + return; + } + + frexp(f, &e2); + e = (int)(e2 * .301029995664); + g = f * fpow10(-e); + while(g < 1) { + e--; + g = f * fpow10(-e); + } + while(g >= 10){ + e++; + g = f * fpow10(-e); + } + + /* convert nsignif digits as a first approximation */ + for(i=0; i g) { + if(add1(s, NSIGNIF)){ + /* gained a digit */ + e--; + fmtexp(s+NSIGNIF, e, 0); + } + continue; + } + if(f < g){ + if(sub1(s, NSIGNIF)){ + /* lost a digit */ + e++; + fmtexp(s+NSIGNIF, e, 0); + } + continue; + } + break; + } + + /* + * bump last few digits down to 0 as we can. + */ + for(i=NSIGNIF-1; i>=NSIGNIF-3; i--){ + c = s[i]; + if(c != '0'){ + s[i] = '0'; + g=fmtstrtod(s, nil); + if(g != f){ + s[i] = c; + break; + } + } + } + + /* + * remove trailing zeros. + */ + ndigit = NSIGNIF; + while(ndigit > 1 && s[ndigit-1] == '0'){ + e++; + --ndigit; + } + s[ndigit] = 0; + *exp = e; + *len = ndigit; + + errno = oerrno; +} + + +static int +fmtfloat(fmt·State *io) +{ + char buf[NSIGNIF+10], *dot, *digits, *p, *end, suf[10], *cur; + double val; + int c, verb, ndot, e, exp, f, ndigits, neg, newndigits; + int npad, pt, prec, realverb, sign, nsuf, ucase, n, z1, z2; + + if(io->flag&fmt·Long) + val = va_arg(io->args, long double); + else + val = va_arg(io->args, double); + + /* extract formatting flags */ + f = io->flag; + io->flag = 0; + prec = FDEFLT; + if(f & fmt·Prec) + prec = io->prec; + + verb = io->verb; + ucase = 0; + switch(verb) { + case 'A': + case 'E': + case 'F': + case 'G': + verb += 'a'-'A'; + ucase = 1; + break; + } + + /* pick off special numbers. */ + if(isNaN(val)) { + end = special[0+ucase]; + special: + io->flag = f & (fmt·Width|fmt·Left); + return copy(io, end, strlen(end), strlen(end)); + } + if(isInf(val, 1)) { + end = special[2+ucase]; + goto special; + } + if(isInf(val, -1)) { + end = special[4+ucase]; + goto special; + } + + /* get exact representation. */ + digits = buf; + dtoa(val, digits, &exp, &neg, &ndigits); + + /* get locale's decimal point. */ + dot = io->decimal; + if(dot == nil) + dot = "."; + ndot = utf8·len(dot); + + /* + * now the formatting fun begins. + * compute parameters for actual fmt: + * + * pad: number of spaces to insert before/after field. + * z1: number of zeros to insert before digits + * z2: number of zeros to insert after digits + * point: number of digits to print before decimal point + * ndigits: number of digits to use from digits[] + * suf: trailing suffix, like "e-5" + */ + realverb = verb; + switch(verb){ + case 'g': + /* convert to at most prec significant digits. (prec=0 means 1) */ + if(prec == 0) + prec = 1; + if(ndigits > prec) { + if(digits[prec] >= '5' && add1(digits, prec)) + exp++; + exp += ndigits-prec; + ndigits = prec; + } + + /* + * extra rules for %g (implemented below): + * trailing zeros removed after decimal unless FmtSharp. + * decimal point only if digit follows. + */ + + /* fall through to %e */ + default: + case 'e': + /* one significant digit before decimal, no leading zeros. */ + pt = 1; + z1 = 0; + + /* + * decimal point is after ndigits digits right now. + * slide to be after first. + */ + e = exp + (ndigits-1); + + /* if this is %g, check exponent and convert prec */ + if(realverb == 'g') { + if(-4 <= e && e < prec) + goto casef; + prec--; /* one digit before decimal; rest after */ + } + + /* compute trailing zero padding or truncate digits. */ + if(1+prec >= ndigits) + z2 = 1+prec - ndigits; + else { + /* truncate digits */ + assert(realverb != 'g'); + newndigits = 1+prec; + if(digits[newndigits] >= '5' && add1(digits, newndigits)) { + /* had 999e4, now have 100e5 */ + e++; + } + ndigits = newndigits; + z2 = 0; + } + fmtexp(suf, e, ucase); + nsuf = strlen(suf); + break; + + casef: + case 'f': + /* determine where digits go with respect to decimal point */ + if(ndigits+exp > 0) { + pt = ndigits+exp; + z1 = 0; + } else { + pt = 1; + z1 = 1 + -(ndigits+exp); + } + + /* + * %g specifies prec = number of significant digits + * convert to number of digits after decimal point + */ + if(realverb == 'g') + prec += z1 - pt; + + /* compute trailing zero padding or truncate digits. */ + if(pt+prec >= z1+ndigits) + z2 = pt+prec - (z1+ndigits); + else{ + /* truncate digits */ + assert(realverb != 'g'); + newndigits = pt+prec - z1; + if(newndigits < 0){ + z1 += newndigits; + newndigits = 0; + }else if(newndigits == 0){ + /* perhaps round up */ + if(digits[0] >= '5'){ + digits[0] = '1'; + newndigits = 1; + goto newdigit; + } + }else if(digits[newndigits] >= '5' && add1(digits, newndigits)){ + /* digits was 999, is now 100; make it 1000 */ + digits[newndigits++] = '0'; + newdigit: + /* account for new digit */ + if(z1) /* 0.099 => 0.100 or 0.99 => 1.00*/ + z1--; + else /* 9.99 => 10.00 */ + pt++; + } + z2 = 0; + ndigits = newndigits; + } + nsuf = 0; + break; + } + + /* + * if %g is given without FmtSharp, remove trailing zeros. + * must do after truncation, so that e.g. print %.3g 1.001 + * produces 1, not 1.00. sorry, but them's the rules. + */ + if(realverb == 'g' && !(f & fmt·Sharp)) { + if(z1+ndigits+z2 >= pt) { + if(z1+ndigits < pt) + z2 = pt - (z1+ndigits); + else{ + z2 = 0; + while(z1+ndigits > pt && digits[ndigits-1] == '0') + ndigits--; + } + } + } + + /* + * compute width of all digits and decimal point and suffix if any + */ + n = z1+ndigits+z2; + if(n > pt) + n += ndot; + else if(n == pt){ + if(f & fmt·Sharp) + n += ndot; + else + pt++; /* do not print any decimal point */ + } + n += nsuf; + + /* + * determine sign + */ + sign = 0; + if(neg) + sign = '-'; + else if(f & fmt·Sign) + sign = '+'; + else if(f & fmt·Space) + sign = ' '; + if(sign) + n++; + + /* compute padding */ + npad = 0; + if((f & fmt·Width) && io->width > n) + npad = io->width - n; + if(npad && !(f & fmt·Left) && (f & fmt·Zero)){ + z1 += npad; + pt += npad; + npad = 0; + } + + /* format the actual field. too bad about doing this twice. */ + if(npad && !(f & fmt·Left) && pad(io, npad < 0)) + return -1; + + cur = io->buffer.cur; + end = io->buffer.end; + + if(sign){ + if(cur+1 > end){ + if(!(cur=flush(io,cur,1))) + return -1; + end = io->buffer.end; + } + *cur++ = sign; + } + + while(z1>0 || ndigits>0 || z2>0){ + if(z1 > 0){ + z1--; + c = '0'; + }else if(ndigits > 0){ + ndigits--; + c = *digits++; + }else{ + z2--; + c = '0'; + } + + if(cur+1 > end){ + if(!(cur=flush(io,cur,1))) + return -1; + end = io->buffer.end; + } + *cur++ = c; + + if(--pt == 0) + for(p=dot; *p; p++){ + if(cur+1 > end){ + if(!(cur=flush(io,cur,1))) + return -1; + end = io->buffer.end; + } + *cur++ = *p; + } + } + io->n += cur - (char*)io->buffer.cur; + io->buffer.cur = cur; + if(nsuf && copy(io, suf, nsuf, nsuf) < 0) + return -1; + if(npad && (f & fmt·Left) && pad(io, npad < 0)) + return -1; + + return 0; +} diff --git a/sys/libfmt/fprint.c b/sys/libfmt/fprint.c new file mode 100644 index 0000000..26343f7 --- /dev/null +++ b/sys/libfmt/fprint.c @@ -0,0 +1,14 @@ +#include "internal.h" + +int +fprint(int fd, char *fmt, ...) +{ + int n; + va_list args; + + va_start(args, fmt); + n = fmt·vfprint(fd, fmt, args); + va_end(args); + + return n; +} diff --git a/sys/libfmt/internal.h b/sys/libfmt/internal.h index 56e64c8..725cfff 100644 --- a/sys/libfmt/internal.h +++ b/sys/libfmt/internal.h @@ -13,3 +13,5 @@ struct Verb int c; Formatter fmt; }; + +void fmt·setlocale(fmt·State *io, char *decimal, char *thousands, char *groups); diff --git a/sys/libfmt/locale.c b/sys/libfmt/locale.c new file mode 100644 index 0000000..437c61e --- /dev/null +++ b/sys/libfmt/locale.c @@ -0,0 +1,16 @@ +#include "internal.h" + +void +fmt·setlocale(fmt·State *io, char *decimal, char *thousands, char *groups) +{ + if(decimal == nil || decimal[0] == '\0') + decimal = "."; + if(thousands == nil) + thousands = ","; + if(groups == nil) + groups = "\3"; + + io->groups = groups; + io->decimal = decimal; + io->thousands = thousands; +} diff --git a/sys/libfmt/nsprint.c b/sys/libfmt/nsprint.c new file mode 100644 index 0000000..90489e0 --- /dev/null +++ b/sys/libfmt/nsprint.c @@ -0,0 +1,14 @@ +#include "internal.h" + +int +fmt·nsprint(int len, char *buf, char *fmt, ...) +{ + int n; + va_list args; + + va_start(args, fmt); + n = fmt·vnsprint(len, buf, fmt, args); + va_end(args); + + return n; +} diff --git a/sys/libfmt/open.c b/sys/libfmt/open.c new file mode 100644 index 0000000..8aadef5 --- /dev/null +++ b/sys/libfmt/open.c @@ -0,0 +1,34 @@ +#include "internal.h" + +static int +flush(fmt·State *io) +{ + int n, fd; + + fd = (uintptr)io->file; + n = io->buffer.cur - io->buffer.beg; + if(n && write(fd, io->buffer.beg, n) != n) + return -1; + + io->buffer.cur = io->buffer.beg; + return io->n; +} + +int +fmt·open(int fd, int len, char *buf, fmt·State *io) +{ + io->buffer.beg = buf; + io->buffer.cur = buf; + io->buffer.end = buf+len; + io->flush = flush; + io->file = (void*)(uintptr)fd; + io->flag = 0; + io->n = 0; + /* no heap needed */ + io->heap = nil; + io->mem = (mem·Reallocator){ 0 }; + + fmt·setlocale(io, nil, nil, nil); + + return 0; +} diff --git a/sys/libfmt/print.c b/sys/libfmt/print.c index e69de29..20b8e00 100644 --- a/sys/libfmt/print.c +++ b/sys/libfmt/print.c @@ -0,0 +1,13 @@ +#include "internal.h" + +int +fmt·print(char *fmt, ...) +{ + int n; + va_list args; + + va_start(args, fmt); + n = fmt·vfprint(1, fmt, args); + va_end(args); + return n; +} diff --git a/sys/libfmt/rules.mk b/sys/libfmt/rules.mk new file mode 100644 index 0000000..2b1b431 --- /dev/null +++ b/sys/libfmt/rules.mk @@ -0,0 +1,36 @@ +include share/push.mk + +# Iterate through subdirectory tree + +# Local sources +SRCS_$(d) :=\ + $(d)/buffer.c\ + $(d)/do.c\ + $(d)/esprint.c\ + $(d)/fprint.c\ + $(d)/locale.c\ + $(d)/nsprint.c\ + $(d)/open.c\ + $(d)/print.c\ + $(d)/sprint.c\ + $(d)/vesprint.c\ + $(d)/vfprint.c\ + $(d)/vnsprint.c\ + $(d)/vprint.c\ + $(d)/vwrite.c\ + $(d)/write.c + +LIBS_$(d) := $(d)/libfmt.a + +TSTS_$(d) := \ + $(d)/test.c + +include share/paths.mk + +$(LIBS_$(d)): $(OBJS_$(d)) + $(ARCHIVE) + +$(UNTS_$(d)): $(TOBJS_$(d)) $(LIBS_$(d)) $(OBJ_DIR)/sys/libutf/libutf.a $(OBJ_DIR)/sys/base/base.a + $(COMPLINK) + +include share/pop.mk diff --git a/sys/libfmt/sprint.c b/sys/libfmt/sprint.c new file mode 100644 index 0000000..f1be6dd --- /dev/null +++ b/sys/libfmt/sprint.c @@ -0,0 +1,19 @@ +#include "internal.h" + +int +fmt·sprint(char *buf, char *fmt, ...) +{ + int n; + uint len; + va_list args; + + len = 1 << 30; + if(buf+len < buf) + len = -(uintptr)buf-1; + + va_start(args, fmt); + n = fmt·vnsprint(len, buf, fmt, args); + va_end(args); + + return n; +} diff --git a/sys/libfmt/test.c b/sys/libfmt/test.c new file mode 100644 index 0000000..abe1d64 --- /dev/null +++ b/sys/libfmt/test.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include + +typedef +struct Complex +{ + double r, i; +} Complex; + +int +Xfmt(fmt·State *io) +{ + Complex c; + c = va_arg(io->args, Complex); + + return fmt·write(io, "(real=%g,imag=%g)", c.r, c.i); +} + +int +main(int argc, char *argv[]) +{ + fmt·print("basic tests\n"); + fmt·print("\tx: %x\n", 0x87654321); + fmt·print("\tu: %u\n", 0x87654321); + fmt·print("\td: %d\n", 0x87654321); + fmt·print("\ts: %s\n", "hi there"); + fmt·print("\tc: %c\n", '!'); + fmt·print("\tg: %g %g %g\n", 3.14159, 3.14159e10, 3.14159e-10); + fmt·print("\te: %e %e %e\n", 3.14159, 3.14159e10, 3.14159e-10); + fmt·print("\tf: %f %f %f\n", 3.14159, 3.14159e10, 3.14159e-10); + fmt·print("\tsmiley: %C\n", (rune)0x263a); + fmt·print("\t%g %.18g\n", 2e25, 2e25); + fmt·print("\t%2.18g\n", 1.0); + fmt·print("\t%2.18f\n", 1.0); + fmt·print("\t%f\n", 3.1415927/4); + fmt·print("\t%d\n", 23); + fmt·print("\t%i\n", 23); + fmt·print("\t%0.10d\n", 12345); + + fmt·print("%%4%%d tests\n"); + fmt·print("\t%3$d %4$06d %2$d %1$d\n", 444, 333, 111, 222); + fmt·print("\t%3$d %4$06d %2$d %1$d\n", 444, 333, 111, 222); + fmt·print("\t%3$d %4$*5$06d %2$d %1$d\n", 444, 333, 111, 222, 20); + fmt·print("\t%3$hd %4$*5$06d %2$d %1$d\n", 444, 333, (short)111, 222, 20); + fmt·print("\t%3$lld %4$*5$06d %2$d %1$d\n", 444, 333, 111LL, 222, 20); + + /* test %'d formats */ + fmt·print("%%'%%d tests\n"); + fmt·print("\t%'d %'d %'d\n", 1, 2222, 33333333); + fmt·print("\t%'019d\n", 0); + fmt·print("\t%08d %08d %08d\n", 1, 2222, 33333333); + fmt·print("\t%'08d %'08d %'08d\n", 1, 2222, 33333333); + fmt·print("\t%'x %'X %'b\n", 0x11111111, 0xabcd1234, 12345); + fmt·print("\t%'lld %'lld %'lld\n", 1LL, 222222222LL, 3333333333333LL); + fmt·print("\t%019lld %019lld %019lld\n", 1LL, 222222222LL, 3333333333333LL); + fmt·print("\t%'019lld %'019lld %'019lld\n", 1LL, 222222222LL, 3333333333333LL); + fmt·print("\t%'020lld %'020lld %'020lld\n", 1LL, 222222222LL, 3333333333333LL); + fmt·print("\t%'llx %'llX %'llb\n", 0x111111111111LL, 0xabcd12345678LL, 112342345LL); + + /* test precision */ + fmt·print("precision tests\n"); + fmt·print("%020.10d\n", 100); + + /* test install */ + fmt·install('X', Xfmt); + Complex c = { 1.5, -2.3 }; + fmt·print("x = %X\n", c); + + return 0; + +} diff --git a/sys/libfmt/vesprint.c b/sys/libfmt/vesprint.c new file mode 100644 index 0000000..18f4dd2 --- /dev/null +++ b/sys/libfmt/vesprint.c @@ -0,0 +1,26 @@ +#include "internal.h" + +char* +fmt·vesprint(char *buf, char *end, char *fmt, va_list args) +{ + fmt·State io; + + if(end <= buf) + return nil; + + io.n = 0; + io.buffer.beg = io.buffer.cur = buf; + io.buffer.end = end-1; + io.flush = nil; + io.file = nil; + + va_copy(io.args, args); + + fmt·setlocale(&io, nil, nil, nil); + fmt·do(&io, fmt); + + va_end(io.args); + + *(io.buffer.cur) = 0; + return io.buffer.cur; +} diff --git a/sys/libfmt/vfprint.c b/sys/libfmt/vfprint.c new file mode 100644 index 0000000..4306ea7 --- /dev/null +++ b/sys/libfmt/vfprint.c @@ -0,0 +1,19 @@ +#include "internal.h" + +int +fmt·vfprint(int fd, char *fmt, va_list args) +{ + int n; + fmt·State io; + char buf[256]; + + fmt·open(fd, sizeof(buf), buf, &io); + + va_copy(io.args, args); + n = fmt·do(&io, fmt); + va_end(io.args); + + if(n > 0 && io.flush(&io) < 0) + return -1; + return n; +} diff --git a/sys/libfmt/vnsprint.c b/sys/libfmt/vnsprint.c new file mode 100644 index 0000000..7ded908 --- /dev/null +++ b/sys/libfmt/vnsprint.c @@ -0,0 +1,26 @@ +#include "internal.h" + +int +fmt·vnsprint(int len, char *buf, char *fmt, va_list args) +{ + fmt·State io; + + if(len <= 0) + return -1; + + io.n = 0; + io.buffer.beg = io.buffer.cur = buf; + io.buffer.end = buf+len-1; + io.flush = nil; + io.file = nil; + + va_copy(io.args, args); + + fmt·setlocale(&io, nil, nil, nil); + fmt·do(&io, fmt); + + va_end(io.args); + + *(io.buffer.cur) = 0; + return io.buffer.cur - io.buffer.beg; +} diff --git a/sys/libfmt/vprint.c b/sys/libfmt/vprint.c new file mode 100644 index 0000000..bb3076b --- /dev/null +++ b/sys/libfmt/vprint.c @@ -0,0 +1,19 @@ +#include "internal.h" + +int +fmt·vprint(char *fmt, va_list args) +{ + fmt·State io; + int n; + char buf[256]; + + fmt·open(1, sizeof(buf), buf, &io); + + va_copy(io.args, args); + n = fmt·do(&io, fmt); + va_end(io.args); + + if(n > 0 && io.flush(&io) < 0) + return -1; + return n; +} diff --git a/sys/libfmt/vwrite.c b/sys/libfmt/vwrite.c new file mode 100644 index 0000000..cacdef2 --- /dev/null +++ b/sys/libfmt/vwrite.c @@ -0,0 +1,26 @@ +#include "internal.h" + +int +fmt·vwrite(fmt·State *io, char *fmt, va_list args) +{ + int n; + va_list tmp; + + io->flag = io->width = io->prec = 0; + + va_copy(tmp, io->args); + va_end(io->args); + + va_copy(io->args,args); + n = fmt·do(io, fmt); + va_end(io->args); + + va_copy(io->args, tmp); + va_end(tmp); + + io->flag = io->width = io->prec = 0; + + if(n >= 0) + return 0; + return n; +} diff --git a/sys/libfmt/write.c b/sys/libfmt/write.c new file mode 100644 index 0000000..9a77223 --- /dev/null +++ b/sys/libfmt/write.c @@ -0,0 +1,22 @@ +#include "internal.h" + +int +fmt·write(fmt·State *io, char *fmt, ...) +{ + int n; + va_list args; + + io->flag = io->width = io->prec = 0; + + va_copy(args, io->args); + va_end(io->args); + + va_start(io->args, fmt); + n = fmt·do(io, fmt); + va_end(io->args); + + io->flag = io->width = io->prec = 0; + if(n >= 0) + return 0; + return n; +} diff --git a/sys/rules.mk b/sys/rules.mk index cefa4a9..6d1dfa5 100644 --- a/sys/rules.mk +++ b/sys/rules.mk @@ -11,6 +11,9 @@ include $(DIR)/rules.mk DIR := $(d)/libutf include $(DIR)/rules.mk +DIR := $(d)/libfmt +include $(DIR)/rules.mk + DIR := $(d)/libmath include $(DIR)/rules.mk -- cgit v1.2.1