| /* | |
| * The authors of this software are Rob Pike and Ken Thompson, | |
| * with contributions from Mike Burrows and Sean Dorward. | |
| * | |
| * Copyright (c) 2002-2006 by Lucent Technologies. | |
| * Portions Copyright (c) 2004 Google Inc. | |
| * | |
| * Permission to use, copy, modify, and distribute this software for any | |
| * purpose without fee is hereby granted, provided that this entire notice | |
| * is included in all copies of any software which is or includes a copy | |
| * or modification of this software and in all copies of the supporting | |
| * documentation for such software. | |
| * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED | |
| * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES | |
| * NOR GOOGLE INC MAKE ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING | |
| * THE MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. | |
| */ | |
| #include <u.h> | |
| #include <libc.h> | |
| #include "fmtdef.h" | |
| /* format the output into f->to and return the number of characters fmted */ | |
| int | |
| dofmt(Fmt *f, char *fmt) | |
| { | |
| Rune rune, *rt, *rs; | |
| int r; | |
| char *t, *s; | |
| int n, nfmt; | |
| nfmt = f->nfmt; | |
| for(;;){ | |
| if(f->runes){ | |
| rt = (Rune*)f->to; | |
| rs = (Rune*)f->stop; | |
| while((r = *(uchar*)fmt) && r != '%'){ | |
| if(r < Runeself) | |
| fmt++; | |
| else{ | |
| fmt += chartorune(&rune, fmt); | |
| r = rune; | |
| } | |
| FMTRCHAR(f, rt, rs, r); | |
| } | |
| fmt++; | |
| f->nfmt += rt - (Rune *)f->to; | |
| f->to = rt; | |
| if(!r) | |
| return f->nfmt - nfmt; | |
| f->stop = rs; | |
| }else{ | |
| t = (char*)f->to; | |
| s = (char*)f->stop; | |
| while((r = *(uchar*)fmt) && r != '%'){ | |
| if(r < Runeself){ | |
| FMTCHAR(f, t, s, r); | |
| fmt++; | |
| }else{ | |
| n = chartorune(&rune, fmt); | |
| if(t + n > s){ | |
| t = (char*)__fmtflush(f, t, n); | |
| if(t != nil) | |
| s = (char*)f->stop; | |
| else | |
| return -1; | |
| } | |
| while(n--) | |
| *t++ = *fmt++; | |
| } | |
| } | |
| fmt++; | |
| f->nfmt += t - (char *)f->to; | |
| f->to = t; | |
| if(!r) | |
| return f->nfmt - nfmt; | |
| f->stop = s; | |
| } | |
| fmt = (char*)__fmtdispatch(f, fmt, 0); | |
| if(fmt == nil) | |
| return -1; | |
| } | |
| } | |
| void * | |
| __fmtflush(Fmt *f, void *t, int len) | |
| { | |
| if(f->runes) | |
| f->nfmt += (Rune*)t - (Rune*)f->to; | |
| else | |
| f->nfmt += (char*)t - (char *)f->to; | |
| f->to = t; | |
| if(f->flush == 0 || (*f->flush)(f) == 0 || (char*)f->to + len > (char*)f->stop){ | |
| f->stop = f->to; | |
| return nil; | |
| } | |
| return f->to; | |
| } | |
| /* | |
| * put a formatted block of memory sz bytes long of n runes into the output buffer, | |
| * left/right justified in a field of at least f->width characters (if FmtWidth is set) | |
| */ | |
| int | |
| __fmtpad(Fmt *f, int n) | |
| { | |
| char *t, *s; | |
| int i; | |
| t = (char*)f->to; | |
| s = (char*)f->stop; | |
| for(i = 0; i < n; i++) | |
| FMTCHAR(f, t, s, ' '); | |
| f->nfmt += t - (char *)f->to; | |
| f->to = t; | |
| return 0; | |
| } | |
| int | |
| __rfmtpad(Fmt *f, int n) | |
| { | |
| Rune *t, *s; | |
| int i; | |
| t = (Rune*)f->to; | |
| s = (Rune*)f->stop; | |
| for(i = 0; i < n; i++) | |
| FMTRCHAR(f, t, s, ' '); | |
| f->nfmt += t - (Rune *)f->to; | |
| f->to = t; | |
| return 0; | |
| } | |
| int | |
| __fmtcpy(Fmt *f, const void *vm, int n, int sz) | |
| { | |
| Rune *rt, *rs, r; | |
| char *t, *s, *m, *me; | |
| ulong fl; | |
| int nc, w; | |
| m = (char*)vm; | |
| me = m + sz; | |
| fl = f->flags; | |
| w = 0; | |
| if(fl & FmtWidth) | |
| w = f->width; | |
| if((fl & FmtPrec) && n > f->prec) | |
| n = f->prec; | |
| if(f->runes){ | |
| if(!(fl & FmtLeft) && __rfmtpad(f, w - n) < 0) | |
| return -1; | |
| rt = (Rune*)f->to; | |
| rs = (Rune*)f->stop; | |
| for(nc = n; nc > 0; nc--){ | |
| r = *(uchar*)m; | |
| if(r < Runeself) | |
| m++; | |
| else if((me - m) >= UTFmax || fullrune(m, me-m)) | |
| m += chartorune(&r, m); | |
| else | |
| break; | |
| FMTRCHAR(f, rt, rs, r); | |
| } | |
| f->nfmt += rt - (Rune *)f->to; | |
| f->to = rt; | |
| if(fl & FmtLeft && __rfmtpad(f, w - n) < 0) | |
| return -1; | |
| }else{ | |
| if(!(fl & FmtLeft) && __fmtpad(f, w - n) < 0) | |
| return -1; | |
| t = (char*)f->to; | |
| s = (char*)f->stop; | |
| for(nc = n; nc > 0; nc--){ | |
| r = *(uchar*)m; | |
| if(r < Runeself) | |
| m++; | |
| else if((me - m) >= UTFmax || fullrune(m, me-m)) | |
| m += chartorune(&r, m); | |
| else | |
| break; | |
| FMTRUNE(f, t, s, r); | |
| } | |
| f->nfmt += t - (char *)f->to; | |
| f->to = t; | |
| if(fl & FmtLeft && __fmtpad(f, w - n) < 0) | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| int | |
| __fmtrcpy(Fmt *f, const void *vm, int n) | |
| { | |
| Rune r, *m, *me, *rt, *rs; | |
| char *t, *s; | |
| ulong fl; | |
| int w; | |
| m = (Rune*)vm; | |
| fl = f->flags; | |
| w = 0; | |
| if(fl & FmtWidth) | |
| w = f->width; | |
| if((fl & FmtPrec) && n > f->prec) | |
| n = f->prec; | |
| if(f->runes){ | |
| if(!(fl & FmtLeft) && __rfmtpad(f, w - n) < 0) | |
| return -1; | |
| rt = (Rune*)f->to; | |
| rs = (Rune*)f->stop; | |
| for(me = m + n; m < me; m++) | |
| FMTRCHAR(f, rt, rs, *m); | |
| f->nfmt += rt - (Rune *)f->to; | |
| f->to = rt; | |
| if(fl & FmtLeft && __rfmtpad(f, w - n) < 0) | |
| return -1; | |
| }else{ | |
| if(!(fl & FmtLeft) && __fmtpad(f, w - n) < 0) | |
| return -1; | |
| t = (char*)f->to; | |
| s = (char*)f->stop; | |
| for(me = m + n; m < me; m++){ | |
| r = *m; | |
| FMTRUNE(f, t, s, r); | |
| } | |
| f->nfmt += t - (char *)f->to; | |
| f->to = t; | |
| if(fl & FmtLeft && __fmtpad(f, w - n) < 0) | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| /* fmt out one character */ | |
| int | |
| __charfmt(Fmt *f) | |
| { | |
| char x[1]; | |
| x[0] = va_arg(f->args, int); | |
| f->prec = 1; | |
| return __fmtcpy(f, (const char*)x, 1, 1); | |
| } | |
| /* fmt out one rune */ | |
| int | |
| __runefmt(Fmt *f) | |
| { | |
| Rune x[1]; | |
| x[0] = va_arg(f->args, int); | |
| return __fmtrcpy(f, (const void*)x, 1); | |
| } | |
| /* public helper routine: fmt out a null terminated string already in hand */ | |
| int | |
| fmtstrcpy(Fmt *f, char *s) | |
| { | |
| int i, j; | |
| if(!s) | |
| return __fmtcpy(f, "<nil>", 5, 5); | |
| /* if precision is specified, make sure we don't wander off the end */ | |
| if(f->flags & FmtPrec){ | |
| #ifdef PLAN9PORT | |
| Rune r; | |
| i = 0; | |
| for(j=0; j<f->prec && s[i]; j++) | |
| i += chartorune(&r, s+i); | |
| #else | |
| /* ANSI requires precision in bytes, not Runes */ | |
| for(i=0; i<f->prec; i++) | |
| if(s[i] == 0) | |
| break; | |
| j = utfnlen(s, i); /* won't print partial at end */ | |
| #endif | |
| return __fmtcpy(f, s, j, i); | |
| } | |
| return __fmtcpy(f, s, utflen(s), strlen(s)); | |
| } | |
| /* fmt out a null terminated utf string */ | |
| int | |
| __strfmt(Fmt *f) | |
| { | |
| char *s; | |
| s = va_arg(f->args, char *); | |
| return fmtstrcpy(f, s); | |
| } | |
| /* public helper routine: fmt out a null terminated rune string already in hand */ | |
| int | |
| fmtrunestrcpy(Fmt *f, Rune *s) | |
| { | |
| Rune *e; | |
| int n, p; | |
| if(!s) | |
| return __fmtcpy(f, "<nil>", 5, 5); | |
| /* if precision is specified, make sure we don't wander off the end */ | |
| if(f->flags & FmtPrec){ | |
| p = f->prec; | |
| for(n = 0; n < p; n++) | |
| if(s[n] == 0) | |
| break; | |
| }else{ | |
| for(e = s; *e; e++) | |
| ; | |
| n = e - s; | |
| } | |
| return __fmtrcpy(f, s, n); | |
| } | |
| /* fmt out a null terminated rune string */ | |
| int | |
| __runesfmt(Fmt *f) | |
| { | |
| Rune *s; | |
| s = va_arg(f->args, Rune *); | |
| return fmtrunestrcpy(f, s); | |
| } | |
| /* fmt a % */ | |
| int | |
| __percentfmt(Fmt *f) | |
| { | |
| Rune x[1]; | |
| x[0] = f->r; | |
| f->prec = 1; | |
| return __fmtrcpy(f, (const void*)x, 1); | |
| } | |
| /* fmt an integer */ | |
| int | |
| __ifmt(Fmt *f) | |
| { | |
| char buf[140], *p, *conv; | |
| /* 140: for 64 bits of binary + 3-byte sep every 4 digits */ | |
| uvlong vu; | |
| ulong u; | |
| int neg, base, i, n, fl, w, isv; | |
| int ndig, len, excess, bytelen; | |
| char *grouping; | |
| char *thousands; | |
| neg = 0; | |
| fl = f->flags; | |
| isv = 0; | |
| vu = 0; | |
| u = 0; | |
| #ifndef PLAN9PORT | |
| /* | |
| * Unsigned verbs for ANSI C | |
| */ | |
| switch(f->r){ | |
| case 'o': | |
| case 'p': | |
| case 'u': | |
| case 'x': | |
| case 'X': | |
| fl |= FmtUnsigned; | |
| fl &= ~(FmtSign|FmtSpace); | |
| break; | |
| } | |
| #endif | |
| if(f->r == 'p'){ | |
| u = (ulong)va_arg(f->args, void*); | |
| f->r = 'x'; | |
| fl |= FmtUnsigned; | |
| }else if(fl & FmtVLong){ | |
| isv = 1; | |
| if(fl & FmtUnsigned) | |
| vu = va_arg(f->args, uvlong); | |
| else | |
| vu = va_arg(f->args, vlong); | |
| }else if(fl & FmtLong){ | |
| if(fl & FmtUnsigned) | |
| u = va_arg(f->args, ulong); | |
| else | |
| u = va_arg(f->args, long); | |
| }else if(fl & FmtByte){ | |
| if(fl & FmtUnsigned) | |
| u = (uchar)va_arg(f->args, int); | |
| else | |
| u = (char)va_arg(f->args, int); | |
| }else if(fl & FmtShort){ | |
| if(fl & FmtUnsigned) | |
| u = (ushort)va_arg(f->args, int); | |
| else | |
| u = (short)va_arg(f->args, int); | |
| }else{ | |
| if(fl & FmtUnsigned) | |
| u = va_arg(f->args, uint); | |
| else | |
| u = va_arg(f->args, int); | |
| } | |
| conv = "0123456789abcdef"; | |
| grouping = "\4"; /* for hex, octal etc. (undefined by spec but nice) */ | |
| thousands = f->thousands; | |
| switch(f->r){ | |
| case 'd': | |
| case 'i': | |
| case 'u': | |
| base = 10; | |
| grouping = f->grouping; | |
| break; | |
| case 'X': | |
| conv = "0123456789ABCDEF"; | |
| /* fall through */ | |
| case 'x': | |
| base = 16; | |
| thousands = ":"; | |
| break; | |
| case 'b': | |
| base = 2; | |
| thousands = ":"; | |
| break; | |
| case 'o': | |
| base = 8; | |
| break; | |
| default: | |
| return -1; | |
| } | |
| if(!(fl & FmtUnsigned)){ | |
| if(isv && (vlong)vu < 0){ | |
| vu = -(vlong)vu; | |
| neg = 1; | |
| }else if(!isv && (long)u < 0){ | |
| u = -(long)u; | |
| neg = 1; | |
| } | |
| } | |
| p = buf + sizeof buf - 1; | |
| n = 0; /* in runes */ | |
| excess = 0; /* number of bytes > number runes */ | |
| ndig = 0; | |
| len = utflen(thousands); | |
| bytelen = strlen(thousands); | |
| if(isv){ | |
| while(vu){ | |
| i = vu % base; | |
| vu /= base; | |
| if((fl & FmtComma) && n % 4 == 3){ | |
| *p-- = ','; | |
| n++; | |
| } | |
| if((fl & FmtApost) && __needsep(&ndig, &grouping)){ | |
| n += len; | |
| excess += bytelen - len; | |
| p -= bytelen; | |
| memmove(p+1, thousands, bytelen); | |
| } | |
| *p-- = conv[i]; | |
| n++; | |
| } | |
| }else{ | |
| while(u){ | |
| i = u % base; | |
| u /= base; | |
| if((fl & FmtComma) && n % 4 == 3){ | |
| *p-- = ','; | |
| n++; | |
| } | |
| if((fl & FmtApost) && __needsep(&ndig, &grouping)){ | |
| n += len; | |
| excess += bytelen - len; | |
| p -= bytelen; | |
| memmove(p+1, thousands, bytelen); | |
| } | |
| *p-- = conv[i]; | |
| n++; | |
| } | |
| } | |
| if(n == 0){ | |
| /* | |
| * "The result of converting a zero value with | |
| * a precision of zero is no characters." - ANSI | |
| * | |
| * "For o conversion, # increases the precision, if and only if | |
| * necessary, to force the first digit of the result to be a zero | |
| * (if the value and precision are both 0, a single 0 is printed)." - ANSI | |
| */ | |
| if(!(fl & FmtPrec) || f->prec != 0 || (f->r == 'o' && (fl & FmtSharp))){ | |
| *p-- = '0'; | |
| n = 1; | |
| if(fl & FmtApost) | |
| __needsep(&ndig, &grouping); | |
| } | |
| /* | |
| * Zero values don't get 0x. | |
| */ | |
| if(f->r == 'x' || f->r == 'X') | |
| fl &= ~FmtSharp; | |
| } | |
| for(w = f->prec; n < w && p > buf+3; n++){ | |
| if((fl & FmtApost) && __needsep(&ndig, &grouping)){ | |
| n += len; | |
| excess += bytelen - len; | |
| p -= bytelen; | |
| memmove(p+1, thousands, bytelen); | |
| } | |
| *p-- = '0'; | |
| } | |
| if(neg || (fl & (FmtSign|FmtSpace))) | |
| n++; | |
| if(fl & FmtSharp){ | |
| if(base == 16) | |
| n += 2; | |
| else if(base == 8){ | |
| if(p[1] == '0') | |
| fl &= ~FmtSharp; | |
| else | |
| n++; | |
| } | |
| } | |
| if((fl & FmtZero) && !(fl & (FmtLeft|FmtPrec))){ | |
| w = 0; | |
| if(fl & FmtWidth) | |
| w = f->width; | |
| for(; n < w && p > buf+3; n++){ | |
| if((fl & FmtApost) && __needsep(&ndig, &grouping)){ | |
| n += len; | |
| excess += bytelen - len; | |
| p -= bytelen; | |
| memmove(p+1, thousands, bytelen); | |
| } | |
| *p-- = '0'; | |
| } | |
| f->flags &= ~FmtWidth; | |
| } | |
| if(fl & FmtSharp){ | |
| if(base == 16) | |
| *p-- = f->r; | |
| if(base == 16 || base == 8) | |
| *p-- = '0'; | |
| } | |
| if(neg) | |
| *p-- = '-'; | |
| else if(fl & FmtSign) | |
| *p-- = '+'; | |
| else if(fl & FmtSpace) | |
| *p-- = ' '; | |
| f->flags &= ~FmtPrec; | |
| return __fmtcpy(f, p + 1, n, n + excess); | |
| } | |
| int | |
| __countfmt(Fmt *f) | |
| { | |
| void *p; | |
| ulong fl; | |
| fl = f->flags; | |
| p = va_arg(f->args, void*); | |
| if(fl & FmtVLong){ | |
| *(vlong*)p = f->nfmt; | |
| }else if(fl & FmtLong){ | |
| *(long*)p = f->nfmt; | |
| }else if(fl & FmtByte){ | |
| *(char*)p = f->nfmt; | |
| }else if(fl & FmtShort){ | |
| *(short*)p = f->nfmt; | |
| }else{ | |
| *(int*)p = f->nfmt; | |
| } | |
| return 0; | |
| } | |
| int | |
| __flagfmt(Fmt *f) | |
| { | |
| switch(f->r){ | |
| case ',': | |
| f->flags |= FmtComma; | |
| break; | |
| case '-': | |
| f->flags |= FmtLeft; | |
| break; | |
| case '+': | |
| f->flags |= FmtSign; | |
| break; | |
| case '#': | |
| f->flags |= FmtSharp; | |
| break; | |
| case '\'': | |
| f->flags |= FmtApost; | |
| break; | |
| case ' ': | |
| f->flags |= FmtSpace; | |
| break; | |
| case 'u': | |
| f->flags |= FmtUnsigned; | |
| break; | |
| case 'h': | |
| if(f->flags & FmtShort) | |
| f->flags |= FmtByte; | |
| f->flags |= FmtShort; | |
| break; | |
| case 'L': | |
| f->flags |= FmtLDouble; | |
| break; | |
| case 'l': | |
| if(f->flags & FmtLong) | |
| f->flags |= FmtVLong; | |
| f->flags |= FmtLong; | |
| break; | |
| } | |
| return 1; | |
| } | |
| /* default error format */ | |
| int | |
| __badfmt(Fmt *f) | |
| { | |
| char x[2+UTFmax]; | |
| int n; | |
| x[0] = '%'; | |
| n = 1 + runetochar(x+1, &f->r); | |
| x[n++] = '%'; | |
| f->prec = n; | |
| __fmtcpy(f, (const void*)x, n, n); | |
| return 0; | |
| } |