fmt.c (4434B)
1 /* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ 2 #include <stdarg.h> 3 #include <string.h> 4 5 /* 6 * As of 2020, older systems like RHEL 6 and AIX still do not have C11 atomics. 7 * On those systems, make the code use volatile int accesses and hope for the best. 8 * (Most uses of fmtinstall are not actually racing with calls to print that lookup 9 * formats. The code used volatile here for years without too many problems, 10 * even though that's technically racy. A mutex is not OK, because we want to 11 * be able to call print from signal handlers.) 12 * 13 * RHEL is using an old GCC (atomics were added in GCC 4.9). 14 * AIX is using its own IBM compiler (XL C). 15 */ 16 #if __IBMC__ || !__clang__ && __GNUC__ && (__GNUC__ < 4 || (__GNUC__==4 && __GNUC_MINOR__<9)) 17 #warning not using C11 stdatomic on legacy system 18 #define _Atomic volatile 19 #define atomic_load(x) (*(x)) 20 #define atomic_store(x, y) (*(x)=(y)) 21 #define ATOMIC_VAR_INIT(x) (x) 22 #else 23 #include <stdatomic.h> 24 #endif 25 26 #include "plan9.h" 27 #include "fmt.h" 28 #include "fmtdef.h" 29 30 enum 31 { 32 Maxfmt = 128 33 }; 34 35 typedef struct Convfmt Convfmt; 36 struct Convfmt 37 { 38 int c; 39 Fmts fmt; 40 }; 41 42 static struct 43 { 44 /* 45 * lock updates to fmt by calling __fmtlock, __fmtunlock. 46 * reads can start at nfmt and work backward without 47 * further locking. later fmtinstalls take priority over earlier 48 * ones because of the backwards loop. 49 * once installed, a format is never overwritten. 50 */ 51 _Atomic int nfmt; 52 Convfmt fmt[Maxfmt]; 53 } fmtalloc = { 54 #ifdef PLAN9PORT 55 ATOMIC_VAR_INIT(27), 56 #else 57 ATOMIC_VAR_INIT(30), 58 #endif 59 { 60 {' ', __flagfmt}, 61 {'#', __flagfmt}, 62 {'%', __percentfmt}, 63 {'\'', __flagfmt}, 64 {'+', __flagfmt}, 65 {',', __flagfmt}, 66 {'-', __flagfmt}, 67 {'C', __runefmt}, /* Plan 9 addition */ 68 {'E', __efgfmt}, 69 #ifndef PLAN9PORT 70 {'F', __efgfmt}, /* ANSI only */ 71 #endif 72 {'G', __efgfmt}, 73 #ifndef PLAN9PORT 74 {'L', __flagfmt}, /* ANSI only */ 75 #endif 76 {'S', __runesfmt}, /* Plan 9 addition */ 77 {'X', __ifmt}, 78 {'b', __ifmt}, /* Plan 9 addition */ 79 {'c', __charfmt}, 80 {'d', __ifmt}, 81 {'e', __efgfmt}, 82 {'f', __efgfmt}, 83 {'g', __efgfmt}, 84 {'h', __flagfmt}, 85 #ifndef PLAN9PORT 86 {'i', __ifmt}, /* ANSI only */ 87 #endif 88 {'l', __flagfmt}, 89 {'n', __countfmt}, 90 {'o', __ifmt}, 91 {'p', __ifmt}, 92 {'r', __errfmt}, 93 {'s', __strfmt}, 94 #ifdef PLAN9PORT 95 {'u', __flagfmt}, 96 #else 97 {'u', __ifmt}, 98 #endif 99 {'x', __ifmt}, 100 } 101 }; 102 103 int (*fmtdoquote)(int); 104 105 /* 106 * __fmtlock() must be set 107 */ 108 static int 109 __fmtinstall(int c, Fmts f) 110 { 111 Convfmt *p; 112 int i; 113 114 if(c<=0 || c>=65536) 115 return -1; 116 if(!f) 117 f = __badfmt; 118 119 i = atomic_load(&fmtalloc.nfmt); 120 if(i == Maxfmt) 121 return -1; 122 p = &fmtalloc.fmt[i]; 123 p->c = c; 124 p->fmt = f; 125 atomic_store(&fmtalloc.nfmt, i+1); 126 127 return 0; 128 } 129 130 int 131 fmtinstall(int c, int (*f)(Fmt*)) 132 { 133 int ret; 134 135 __fmtlock(); 136 ret = __fmtinstall(c, f); 137 __fmtunlock(); 138 return ret; 139 } 140 141 static Fmts 142 fmtfmt(int c) 143 { 144 Convfmt *p, *ep; 145 146 ep = &fmtalloc.fmt[atomic_load(&fmtalloc.nfmt)]; 147 for(p=ep; p-- > fmtalloc.fmt; ) 148 if(p->c == c) 149 return p->fmt; 150 151 return __badfmt; 152 } 153 154 void* 155 __fmtdispatch(Fmt *f, void *fmt, int isrunes) 156 { 157 Rune rune, r; 158 int i, n; 159 160 f->flags = 0; 161 f->width = f->prec = 0; 162 163 for(;;){ 164 if(isrunes){ 165 r = *(Rune*)fmt; 166 fmt = (Rune*)fmt + 1; 167 }else{ 168 fmt = (char*)fmt + chartorune(&rune, (char*)fmt); 169 r = rune; 170 } 171 f->r = r; 172 switch(r){ 173 case '\0': 174 return nil; 175 case '.': 176 f->flags |= FmtWidth|FmtPrec; 177 continue; 178 case '0': 179 if(!(f->flags & FmtWidth)){ 180 f->flags |= FmtZero; 181 continue; 182 } 183 /* fall through */ 184 case '1': case '2': case '3': case '4': 185 case '5': case '6': case '7': case '8': case '9': 186 i = 0; 187 while(r >= '0' && r <= '9'){ 188 i = i * 10 + r - '0'; 189 if(isrunes){ 190 r = *(Rune*)fmt; 191 fmt = (Rune*)fmt + 1; 192 }else{ 193 r = *(char*)fmt; 194 fmt = (char*)fmt + 1; 195 } 196 } 197 if(isrunes) 198 fmt = (Rune*)fmt - 1; 199 else 200 fmt = (char*)fmt - 1; 201 numflag: 202 if(f->flags & FmtWidth){ 203 f->flags |= FmtPrec; 204 f->prec = i; 205 }else{ 206 f->flags |= FmtWidth; 207 f->width = i; 208 } 209 continue; 210 case '*': 211 i = va_arg(f->args, int); 212 if(i < 0){ 213 /* 214 * negative precision => 215 * ignore the precision. 216 */ 217 if(f->flags & FmtPrec){ 218 f->flags &= ~FmtPrec; 219 f->prec = 0; 220 continue; 221 } 222 i = -i; 223 f->flags |= FmtLeft; 224 } 225 goto numflag; 226 } 227 n = (*fmtfmt(r))(f); 228 if(n < 0) 229 return nil; 230 if(n == 0) 231 return fmt; 232 } 233 }