#include "internal.h" #include #define atomic _Atomic #define MaxFmt 128 #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(30), { {' ', 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) { 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 badfmt; } 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; } 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) { 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 >= e){ if(!(b=flush(io, b, 1))) return -1; e = io->buffer.end; } *b++ = *fmt++; }else{ n = utf8·decode(fmt, &r); if(b + n > e){ 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) /* 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; }