1 /* 2 * The authors of this software are Rob Pike and Ken Thompson. 3 * Copyright (c) 2002 by Lucent Technologies. 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose without fee is hereby granted, provided that this entire notice 6 * is included in all copies of any software which is or includes a copy 7 * or modification of this software and in all copies of the supporting 8 * documentation for such software. 9 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED 10 * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE 11 * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY 12 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. 13 */ 14 #include <u.h> 15 #include <libc.h> 16 #include "fmtdef.h" 17 18 enum 19 { 20 Maxfmt = 64 21 }; 22 23 typedef struct Convfmt Convfmt; 24 struct Convfmt 25 { 26 int c; 27 volatile Fmts fmt; /* for spin lock in fmtfmt; avoids race due to write order */ 28 }; 29 30 struct 31 { 32 /* lock by calling __fmtlock, __fmtunlock */ 33 int nfmt; 34 Convfmt fmt[Maxfmt]; 35 } fmtalloc; 36 37 static Convfmt knownfmt[] = { 38 ' ', __flagfmt, 39 '#', __flagfmt, 40 '%', __percentfmt, 41 '+', __flagfmt, 42 ',', __flagfmt, 43 '-', __flagfmt, 44 'C', __runefmt, /* Plan 9 addition */ 45 'E', __efgfmt, 46 #ifndef PLAN9PORT 47 'F', __efgfmt, /* ANSI only */ 48 #endif 49 'G', __efgfmt, 50 #ifndef PLAN9PORT 51 'L', __flagfmt, /* ANSI only */ 52 #endif 53 'S', __runesfmt, /* Plan 9 addition */ 54 'X', __ifmt, 55 'b', __ifmt, /* Plan 9 addition */ 56 'c', __charfmt, 57 'd', __ifmt, 58 'e', __efgfmt, 59 'f', __efgfmt, 60 'g', __efgfmt, 61 'h', __flagfmt, 62 #ifndef PLAN9PORT 63 'i', __ifmt, /* ANSI only */ 64 #endif 65 'l', __flagfmt, 66 'n', __countfmt, 67 'o', __ifmt, 68 'p', __ifmt, 69 'r', __errfmt, 70 's', __strfmt, 71 #ifdef PLAN9PORT 72 'u', __flagfmt, 73 #else 74 'u', __ifmt, 75 #endif 76 'x', __ifmt, 77 0, nil, 78 }; 79 80 81 int (*fmtdoquote)(int); 82 83 /* 84 * __fmtlock() must be set 85 */ 86 static int 87 __fmtinstall(int c, Fmts f) 88 { 89 Convfmt *p, *ep; 90 91 if(c<=0 || c>=65536) 92 return -1; 93 if(!f) 94 f = __badfmt; 95 96 ep = &fmtalloc.fmt[fmtalloc.nfmt]; 97 for(p=fmtalloc.fmt; p<ep; p++) 98 if(p->c == c) 99 break; 100 101 if(p == &fmtalloc.fmt[Maxfmt]) 102 return -1; 103 104 p->fmt = f; 105 if(p == ep){ /* installing a new format character */ 106 fmtalloc.nfmt++; 107 p->c = c; 108 } 109 110 return 0; 111 } 112 113 int 114 fmtinstall(int c, int (*f)(Fmt*)) 115 { 116 int ret; 117 118 __fmtlock(); 119 ret = __fmtinstall(c, f); 120 __fmtunlock(); 121 return ret; 122 } 123 124 static Fmts 125 fmtfmt(int c) 126 { 127 Convfmt *p, *ep; 128 129 ep = &fmtalloc.fmt[fmtalloc.nfmt]; 130 for(p=fmtalloc.fmt; p<ep; p++) 131 if(p->c == c){ 132 while(p->fmt == nil) /* loop until value is updated */ 133 ; 134 return p->fmt; 135 } 136 137 /* is this a predefined format char? */ 138 __fmtlock(); 139 for(p=knownfmt; p->c; p++) 140 if(p->c == c){ 141 __fmtinstall(p->c, p->fmt); 142 __fmtunlock(); 143 return p->fmt; 144 } 145 __fmtunlock(); 146 147 return __badfmt; 148 } 149 150 void* 151 __fmtdispatch(Fmt *f, void *fmt, int isrunes) 152 { 153 Rune rune, r; 154 int i, n; 155 156 f->flags = 0; 157 f->width = f->prec = 0; 158 159 for(;;){ 160 if(isrunes){ 161 r = *(Rune*)fmt; 162 fmt = (Rune*)fmt + 1; 163 }else{ 164 fmt = (char*)fmt + chartorune(&rune, (char*)fmt); 165 r = rune; 166 } 167 f->r = r; 168 switch(r){ 169 case '\0': 170 return nil; 171 case '.': 172 f->flags |= FmtWidth|FmtPrec; 173 continue; 174 case '0': 175 if(!(f->flags & FmtWidth)){ 176 f->flags |= FmtZero; 177 continue; 178 } 179 /* fall through */ 180 case '1': case '2': case '3': case '4': 181 case '5': case '6': case '7': case '8': case '9': 182 i = 0; 183 while(r >= '0' && r <= '9'){ 184 i = i * 10 + r - '0'; 185 if(isrunes){ 186 r = *(Rune*)fmt; 187 fmt = (Rune*)fmt + 1; 188 }else{ 189 r = *(char*)fmt; 190 fmt = (char*)fmt + 1; 191 } 192 } 193 if(isrunes) 194 fmt = (Rune*)fmt - 1; 195 else 196 fmt = (char*)fmt - 1; 197 numflag: 198 if(f->flags & FmtWidth){ 199 f->flags |= FmtPrec; 200 f->prec = i; 201 }else{ 202 f->flags |= FmtWidth; 203 f->width = i; 204 } 205 continue; 206 case '*': 207 i = va_arg(f->args, int); 208 if(i < 0){ 209 /* 210 * negative precision => 211 * ignore the precision. 212 */ 213 if(f->flags & FmtPrec){ 214 f->flags &= ~FmtPrec; 215 f->prec = 0; 216 continue; 217 } 218 i = -i; 219 f->flags |= FmtLeft; 220 } 221 goto numflag; 222 } 223 n = (*fmtfmt(r))(f); 224 if(n < 0) 225 return nil; 226 if(n == 0) 227 return fmt; 228 } 229 } 230