From 45f9449ae0b904917110d3f6937d0266daa84769 Mon Sep 17 00:00:00 2001 From: Nicholas Date: Thu, 11 Nov 2021 08:10:09 -0800 Subject: feat: added libfmt --- sys/libfmt/do.c | 186 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 sys/libfmt/do.c (limited to 'sys/libfmt/do.c') diff --git a/sys/libfmt/do.c b/sys/libfmt/do.c new file mode 100644 index 0000000..3b8ff5a --- /dev/null +++ b/sys/libfmt/do.c @@ -0,0 +1,186 @@ +#include "internal.h" +#include + +#define atomic _Atomic +#define MaxFmt 128 +#define atomic·load atomic_load +#define atomic·store atomic_store + +static struct +{ + atomic int len; + Verb verb[MaxFmt]; +} formatter = +{ + ATOMIC_VAR_INIT(27), + { + {' ', ·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}, + } +}; + +static Formatter +format(int c) +{ + Verb *v, *e; + e = &formatter.verb[atomic·load(&formatter.len)]; + for(v=e; v > formatter.verb; --v){ + if(v->c == c) + return v->fmt; + } + + return ·fmterr; +} + +static char * +dispatch(fmt·State *io, char *fmt) +{ + rune r; + int i, n; + + io->flag = 0; + io->width = io->prec = 0; + + /* + * the form of each print verb: + * % [flags] verb + * + the verb is a single character + * + each flag is either + * - a single character + * - a decimal numeric string + * - up to 2 decimal strings can be used + * - [width|*].[prec|*] + * - if missing, set to 0 + * - if *, grab from varargs + */ + for(;;){ + fmt += utf8·decode(fmt, &r); + io->verb = r; + switch(r){ + case 0: + return nil; + case '.': + io->flag |= fmt·Width|fmt·Prec; + continue; + case '0': + if(!(io->flag & fmt·Width)){ + io->flag |= fmt·Zero; + continue; + } + /* fallthrough */ + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + i = 0; + while('0' <= r && r <= '9'){ + i = 10*i + (r-'0'); + r = *fmt++; + } + fmt--; + number: + if(io->flag & fmt·Width){ + io->flag |= fmt·Prec; + io->prec = i; + }else{ + io->flag |= fmt·Width; + io->width = i; + } + continue; + case '*': + i = va_arg(io->args, int); + if(i < 0){ + if(io->flag&fmt·Prec){ + io->flag ^= fmt·Prec; + io->prec = 0; + } + i = -i; + io->flag |= fmt·Left; + } + goto number; + } + n = format(r)(io); + if(n < 0) + return nil; + if(!n) + return fmt; + } +} + +static char * +flush(fmt·State *io, char *b, int len) +{ + io->n += b - io->buffer.cur; + io->buffer.cur = b; + if(!io->flush || !(*io->flush)(io) || io->buffer.cur + len >= io->buffer.end) { + io->buffer.end = io->buffer.cur; + return nil; + } + return io->buffer.cur; +} + +int +fmt·do(fmt·State *io, char *fmt) +{ + rune r; + int c, n; + char *b, *e; + + 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)))) + return -1; + e = io->buffer.end; + } + *b++ = *fmt++; + }else{ + n = utf8·decode(fmt, &r); + if(b + n > e){ + if(!(b=flush(io, b, sizeof(*b)))) + 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; + } + + if(!(fmt=dispatch(io, fmt))) + return -1; + } +} -- cgit v1.2.1 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 --- sys/libfmt/do.c | 627 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 586 insertions(+), 41 deletions(-) (limited to 'sys/libfmt/do.c') 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; +} -- cgit v1.2.1