| /* |
| * 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" |
| |
| enum |
| { |
| Maxfmt = 64 |
| }; |
| |
| typedef struct Convfmt Convfmt; |
| struct Convfmt |
| { |
| int c; |
| volatile Fmts fmt; /* for spin lock in fmtfmt; avoids race due to write order */ |
| }; |
| |
| static struct |
| { |
| /* lock by calling __fmtlock, __fmtunlock */ |
| int nfmt; |
| Convfmt fmt[Maxfmt]; |
| } fmtalloc; |
| |
| static Convfmt knownfmt[] = { |
| ' ', __flagfmt, |
| '#', __flagfmt, |
| '%', __percentfmt, |
| '\'', __flagfmt, |
| '+', __flagfmt, |
| ',', __flagfmt, |
| '-', __flagfmt, |
| 'C', __runefmt, /* Plan 9 addition */ |
| 'E', __efgfmt, |
| #ifndef PLAN9PORT |
| 'F', __efgfmt, /* ANSI only */ |
| #endif |
| 'G', __efgfmt, |
| #ifndef PLAN9PORT |
| 'L', __flagfmt, /* ANSI only */ |
| #endif |
| 'S', __runesfmt, /* Plan 9 addition */ |
| 'X', __ifmt, |
| 'b', __ifmt, /* Plan 9 addition */ |
| 'c', __charfmt, |
| 'd', __ifmt, |
| 'e', __efgfmt, |
| 'f', __efgfmt, |
| 'g', __efgfmt, |
| 'h', __flagfmt, |
| #ifndef PLAN9PORT |
| 'i', __ifmt, /* ANSI only */ |
| #endif |
| 'l', __flagfmt, |
| 'n', __countfmt, |
| 'o', __ifmt, |
| 'p', __ifmt, |
| 'r', __errfmt, |
| 's', __strfmt, |
| #ifdef PLAN9PORT |
| 'u', __flagfmt, |
| #else |
| 'u', __ifmt, |
| #endif |
| 'x', __ifmt, |
| 0, nil, |
| }; |
| |
| |
| int (*fmtdoquote)(int); |
| |
| /* |
| * __fmtlock() must be set |
| */ |
| static int |
| __fmtinstall(int c, Fmts f) |
| { |
| Convfmt *p, *ep; |
| |
| if(c<=0 || c>=65536) |
| return -1; |
| if(!f) |
| f = __badfmt; |
| |
| ep = &fmtalloc.fmt[fmtalloc.nfmt]; |
| for(p=fmtalloc.fmt; p<ep; p++) |
| if(p->c == c) |
| break; |
| |
| if(p == &fmtalloc.fmt[Maxfmt]) |
| return -1; |
| |
| p->fmt = f; |
| if(p == ep){ /* installing a new format character */ |
| fmtalloc.nfmt++; |
| p->c = c; |
| } |
| |
| return 0; |
| } |
| |
| int |
| fmtinstall(int c, int (*f)(Fmt*)) |
| { |
| int ret; |
| |
| __fmtlock(); |
| ret = __fmtinstall(c, f); |
| __fmtunlock(); |
| return ret; |
| } |
| |
| static Fmts |
| fmtfmt(int c) |
| { |
| Convfmt *p, *ep; |
| |
| ep = &fmtalloc.fmt[fmtalloc.nfmt]; |
| for(p=fmtalloc.fmt; p<ep; p++) |
| if(p->c == c){ |
| while(p->fmt == nil) /* loop until value is updated */ |
| ; |
| return p->fmt; |
| } |
| |
| /* is this a predefined format char? */ |
| __fmtlock(); |
| for(p=knownfmt; p->c; p++) |
| if(p->c == c){ |
| __fmtinstall(p->c, p->fmt); |
| __fmtunlock(); |
| return p->fmt; |
| } |
| __fmtunlock(); |
| |
| return __badfmt; |
| } |
| |
| void* |
| __fmtdispatch(Fmt *f, void *fmt, int isrunes) |
| { |
| Rune rune, r; |
| int i, n; |
| |
| f->flags = 0; |
| f->width = f->prec = 0; |
| |
| for(;;){ |
| if(isrunes){ |
| r = *(Rune*)fmt; |
| fmt = (Rune*)fmt + 1; |
| }else{ |
| fmt = (char*)fmt + chartorune(&rune, (char*)fmt); |
| r = rune; |
| } |
| f->r = r; |
| switch(r){ |
| case '\0': |
| return nil; |
| case '.': |
| f->flags |= FmtWidth|FmtPrec; |
| continue; |
| case '0': |
| if(!(f->flags & FmtWidth)){ |
| f->flags |= FmtZero; |
| continue; |
| } |
| /* fall through */ |
| case '1': case '2': case '3': case '4': |
| case '5': case '6': case '7': case '8': case '9': |
| i = 0; |
| while(r >= '0' && r <= '9'){ |
| i = i * 10 + (int)r - '0'; |
| if(isrunes){ |
| r = *(Rune*)fmt; |
| fmt = (Rune*)fmt + 1; |
| }else{ |
| r = (Rune)*(char*)fmt; |
| fmt = (char*)fmt + 1; |
| } |
| } |
| if(isrunes) |
| fmt = (Rune*)fmt - 1; |
| else |
| fmt = (char*)fmt - 1; |
| numflag: |
| if(f->flags & FmtWidth){ |
| f->flags |= FmtPrec; |
| f->prec = i; |
| }else{ |
| f->flags |= FmtWidth; |
| f->width = i; |
| } |
| continue; |
| case '*': |
| i = va_arg(f->args, int); |
| if(i < 0){ |
| /* |
| * negative precision => |
| * ignore the precision. |
| */ |
| if(f->flags & FmtPrec){ |
| f->flags &= ~(ulong)FmtPrec; |
| f->prec = 0; |
| continue; |
| } |
| i = -i; |
| f->flags |= FmtLeft; |
| } |
| goto numflag; |
| } |
| n = (*fmtfmt((int)r))(f); |
| if(n < 0) |
| return nil; |
| if(n == 0) |
| return fmt; |
| } |
| } |