|  | /* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ | 
|  | /* Copyright (c) 2004 Google Inc.; see LICENSE */ | 
|  |  | 
|  | #include <stdarg.h> | 
|  | #include <string.h> | 
|  | #include "plan9.h" | 
|  | #include "fmt.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; | 
|  | } |