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