Blob


1 /* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
2 #include <stdarg.h>
3 #include <string.h>
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
26 #include "plan9.h"
27 #include "fmt.h"
28 #include "fmtdef.h"
30 enum
31 {
32 Maxfmt = 128
33 };
35 typedef struct Convfmt Convfmt;
36 struct Convfmt
37 {
38 int c;
39 Fmts fmt;
40 };
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},
101 };
103 int (*fmtdoquote)(int);
105 /*
106 * __fmtlock() must be set
107 */
108 static int
109 __fmtinstall(int c, Fmts f)
111 Convfmt *p;
112 int i;
114 if(c<=0 || c>=65536)
115 return -1;
116 if(!f)
117 f = __badfmt;
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);
127 return 0;
130 int
131 fmtinstall(int c, int (*f)(Fmt*))
133 int ret;
135 __fmtlock();
136 ret = __fmtinstall(c, f);
137 __fmtunlock();
138 return ret;
141 static Fmts
142 fmtfmt(int c)
144 Convfmt *p, *ep;
146 ep = &fmtalloc.fmt[atomic_load(&fmtalloc.nfmt)];
147 for(p=ep; p-- > fmtalloc.fmt; )
148 if(p->c == c)
149 return p->fmt;
151 return __badfmt;
154 void*
155 __fmtdispatch(Fmt *f, void *fmt, int isrunes)
157 Rune rune, r;
158 int i, n;
160 f->flags = 0;
161 f->width = f->prec = 0;
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;
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;
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;
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;
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;
222 i = -i;
223 f->flags |= FmtLeft;
225 goto numflag;
227 n = (*fmtfmt(r))(f);
228 if(n < 0)
229 return nil;
230 if(n == 0)
231 return fmt;