fmt.c (4751B)
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 "fmt.h" 27 #include "fmtdef.h" 28 #include "plan9.h" 29 30 enum { 31 Maxfmt = 128 32 }; 33 34 typedef struct Convfmt Convfmt; 35 struct Convfmt { 36 int c; 37 Fmts fmt; 38 }; 39 40 static struct 41 { 42 /* 43 * lock updates to fmt by calling __fmtlock, __fmtunlock. 44 * reads can start at nfmt and work backward without 45 * further locking. later fmtinstalls take priority over earlier 46 * ones because of the backwards loop. 47 * once installed, a format is never overwritten. 48 */ 49 _Atomic int nfmt; 50 Convfmt fmt[Maxfmt]; 51 } fmtalloc = { 52 #ifdef PLAN9PORT 53 ATOMIC_VAR_INIT(27), 54 #else 55 ATOMIC_VAR_INIT(30), 56 #endif 57 { 58 { ' ', __flagfmt }, 59 { '#', __flagfmt }, 60 { '%', __percentfmt }, 61 { '\'', __flagfmt }, 62 { '+', __flagfmt }, 63 { ',', __flagfmt }, 64 { '-', __flagfmt }, 65 { 'C', __runefmt }, /* Plan 9 addition */ 66 { 'E', __efgfmt }, 67 #ifndef PLAN9PORT 68 { 'F', __efgfmt }, /* ANSI only */ 69 #endif 70 { 'G', __efgfmt }, 71 #ifndef PLAN9PORT 72 { 'L', __flagfmt }, /* ANSI only */ 73 #endif 74 { 'S', __runesfmt }, /* Plan 9 addition */ 75 { 'X', __ifmt }, 76 { 'b', __ifmt }, /* Plan 9 addition */ 77 { 'c', __charfmt }, 78 { 'd', __ifmt }, 79 { 'e', __efgfmt }, 80 { 'f', __efgfmt }, 81 { 'g', __efgfmt }, 82 { 'h', __flagfmt }, 83 #ifndef PLAN9PORT 84 { 'i', __ifmt }, /* ANSI only */ 85 #endif 86 { 'l', __flagfmt }, 87 { 'n', __countfmt }, 88 { 'o', __ifmt }, 89 { 'p', __ifmt }, 90 { 'r', __errfmt }, 91 { 's', __strfmt }, 92 #ifdef PLAN9PORT 93 { 'u', __flagfmt }, 94 #else 95 { 'u', __ifmt }, 96 #endif 97 { 'x', __ifmt }, 98 } 99 }; 100 101 int (*fmtdoquote)(int); 102 103 /* 104 * __fmtlock() must be set 105 */ 106 static int 107 __fmtinstall(int c, Fmts f) { 108 Convfmt* p; 109 int i; 110 111 if (c <= 0 || c >= 65536) 112 return -1; 113 if (!f) 114 f = __badfmt; 115 116 i = atomic_load(&fmtalloc.nfmt); 117 if (i == Maxfmt) 118 return -1; 119 p = &fmtalloc.fmt[i]; 120 p->c = c; 121 p->fmt = f; 122 atomic_store(&fmtalloc.nfmt, i + 1); 123 124 return 0; 125 } 126 127 int fmtinstall(int c, int (*f)(Fmt*)) { 128 int ret; 129 130 __fmtlock(); 131 ret = __fmtinstall(c, f); 132 __fmtunlock(); 133 return ret; 134 } 135 136 static Fmts 137 fmtfmt(int c) { 138 Convfmt *p, *ep; 139 140 ep = &fmtalloc.fmt[atomic_load(&fmtalloc.nfmt)]; 141 for (p = ep; p-- > fmtalloc.fmt;) 142 if (p->c == c) 143 return p->fmt; 144 145 return __badfmt; 146 } 147 148 void* __fmtdispatch(Fmt* f, void* fmt, int isrunes) { 149 Rune rune, r; 150 int i, n; 151 152 f->flags = 0; 153 f->width = f->prec = 0; 154 155 for (;;) { 156 if (isrunes) { 157 r = *(Rune*) fmt; 158 fmt = (Rune*) fmt + 1; 159 } else { 160 fmt = (char*) fmt + chartorune(&rune, (char*) fmt); 161 r = rune; 162 } 163 f->r = r; 164 switch (r) { 165 case '\0': 166 return nil; 167 case '.': 168 f->flags |= FmtWidth | FmtPrec; 169 continue; 170 case '0': 171 if (!(f->flags & FmtWidth)) { 172 f->flags |= FmtZero; 173 continue; 174 } 175 /* fall through */ 176 case '1': 177 case '2': 178 case '3': 179 case '4': 180 case '5': 181 case '6': 182 case '7': 183 case '8': 184 case '9': 185 i = 0; 186 while (r >= '0' && r <= '9') { 187 i = i * 10 + r - '0'; 188 if (isrunes) { 189 r = *(Rune*) fmt; 190 fmt = (Rune*) fmt + 1; 191 } else { 192 r = *(char*) fmt; 193 fmt = (char*) fmt + 1; 194 } 195 } 196 if (isrunes) 197 fmt = (Rune*) fmt - 1; 198 else 199 fmt = (char*) fmt - 1; 200 numflag: 201 if (f->flags & FmtWidth) { 202 f->flags |= FmtPrec; 203 f->prec = i; 204 } else { 205 f->flags |= FmtWidth; 206 f->width = i; 207 } 208 continue; 209 case '*': 210 i = va_arg(f->args, int); 211 if (i < 0) { 212 /* 213 * negative precision => 214 * ignore the precision. 215 */ 216 if (f->flags & FmtPrec) { 217 f->flags &= ~FmtPrec; 218 f->prec = 0; 219 continue; 220 } 221 i = -i; 222 f->flags |= FmtLeft; 223 } 224 goto numflag; 225 } 226 n = (*fmtfmt(r))(f); 227 if (n < 0) 228 return nil; 229 if (n == 0) 230 return fmt; 231 } 232 }