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; 147 148 f->flags = 0; 149 f->width = f->prec = 0; 150 151 for(;;){ 152 if(isrunes){ 153 r = *(Rune*)fmt; 154 fmt = (Rune*)fmt + 1; 155 }else{ 156 fmt = (char*)fmt + chartorune(&rune, (char*)fmt); 157 r = rune; 158 } 159 f->r = r; 160 switch(r){ 161 case '\0': 162 return nil; 163 case '.': 164 f->flags |= FmtWidth|FmtPrec; 165 continue; 166 case '0': 167 if(!(f->flags & FmtWidth)){ 168 f->flags |= FmtZero; 169 continue; 170 } 171 /* fall through */ 172 case '1': case '2': case '3': case '4': 173 case '5': case '6': case '7': case '8': case '9': 174 i = 0; 175 while(r >= '0' && r <= '9'){ 176 i = i * 10 + r - '0'; 177 if(isrunes){ 178 r = *(Rune*)fmt; 179 fmt = (Rune*)fmt + 1; 180 }else{ 181 r = *(char*)fmt; 182 fmt = (char*)fmt + 1; 183 } 184 } 185 if(isrunes) 186 fmt = (Rune*)fmt - 1; 187 else 188 fmt = (char*)fmt - 1; 189 numflag: 190 if(f->flags & FmtWidth){ 191 f->flags |= FmtPrec; 192 f->prec = i; 193 }else{ 194 f->flags |= FmtWidth; 195 f->width = i; 196 } 197 continue; 198 case '*': 199 i = va_arg(f->args, int); 200 if(i < 0){ 201 /* 202 * negative precision => 203 * ignore the precision. 204 */ 205 if(f->flags & FmtPrec){ 206 f->flags &= ~FmtPrec; 207 f->prec = 0; 208 continue; 209 } 210 i = -i; 211 f->flags |= FmtLeft; 212 } 213 goto numflag; 214 } 215 n = (*fmtfmt(r))(f); 216 if(n < 0) 217 return nil; 218 if(n == 0) 219 return fmt; 220 } 221 } 222