/src/plan9port/src/lib9/fmt/fmt.c
Line | Count | Source (jump to first uncovered line) |
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 | 0 | { |
111 | 0 | Convfmt *p; |
112 | 0 | int i; |
113 | |
|
114 | 0 | if(c<=0 || c>=65536) |
115 | 0 | return -1; |
116 | 0 | if(!f) |
117 | 0 | f = __badfmt; |
118 | |
|
119 | 0 | i = atomic_load(&fmtalloc.nfmt); |
120 | 0 | if(i == Maxfmt) |
121 | 0 | return -1; |
122 | 0 | p = &fmtalloc.fmt[i]; |
123 | 0 | p->c = c; |
124 | 0 | p->fmt = f; |
125 | 0 | atomic_store(&fmtalloc.nfmt, i+1); |
126 | |
|
127 | 0 | return 0; |
128 | 0 | } |
129 | | |
130 | | int |
131 | | fmtinstall(int c, int (*f)(Fmt*)) |
132 | 0 | { |
133 | 0 | int ret; |
134 | |
|
135 | 0 | __fmtlock(); |
136 | 0 | ret = __fmtinstall(c, f); |
137 | 0 | __fmtunlock(); |
138 | 0 | return ret; |
139 | 0 | } |
140 | | |
141 | | static Fmts |
142 | | fmtfmt(int c) |
143 | 20.9k | { |
144 | 20.9k | Convfmt *p, *ep; |
145 | | |
146 | 20.9k | ep = &fmtalloc.fmt[atomic_load(&fmtalloc.nfmt)]; |
147 | 142k | for(p=ep; p-- > fmtalloc.fmt; ) |
148 | 142k | if(p->c == c) |
149 | 20.9k | return p->fmt; |
150 | | |
151 | 0 | return __badfmt; |
152 | 20.9k | } |
153 | | |
154 | | void* |
155 | | __fmtdispatch(Fmt *f, void *fmt, int isrunes) |
156 | 20.9k | { |
157 | 20.9k | Rune rune, r; |
158 | 20.9k | int i, n; |
159 | | |
160 | 20.9k | f->flags = 0; |
161 | 20.9k | f->width = f->prec = 0; |
162 | | |
163 | 25.8k | for(;;){ |
164 | 25.8k | if(isrunes){ |
165 | 0 | r = *(Rune*)fmt; |
166 | 0 | fmt = (Rune*)fmt + 1; |
167 | 25.8k | }else{ |
168 | 25.8k | fmt = (char*)fmt + chartorune(&rune, (char*)fmt); |
169 | 25.8k | r = rune; |
170 | 25.8k | } |
171 | 25.8k | f->r = r; |
172 | 25.8k | switch(r){ |
173 | 0 | case '\0': |
174 | 0 | return nil; |
175 | 2.46k | case '.': |
176 | 2.46k | f->flags |= FmtWidth|FmtPrec; |
177 | 2.46k | continue; |
178 | 0 | case '0': |
179 | 0 | if(!(f->flags & FmtWidth)){ |
180 | 0 | f->flags |= FmtZero; |
181 | 0 | continue; |
182 | 0 | } |
183 | | /* fall through */ |
184 | 2.46k | case '1': case '2': case '3': case '4': |
185 | 2.46k | case '5': case '6': case '7': case '8': case '9': |
186 | 2.46k | i = 0; |
187 | 4.92k | while(r >= '0' && r <= '9'){ |
188 | 2.46k | i = i * 10 + r - '0'; |
189 | 2.46k | if(isrunes){ |
190 | 0 | r = *(Rune*)fmt; |
191 | 0 | fmt = (Rune*)fmt + 1; |
192 | 2.46k | }else{ |
193 | 2.46k | r = *(char*)fmt; |
194 | 2.46k | fmt = (char*)fmt + 1; |
195 | 2.46k | } |
196 | 2.46k | } |
197 | 2.46k | if(isrunes) |
198 | 0 | fmt = (Rune*)fmt - 1; |
199 | 2.46k | else |
200 | 2.46k | fmt = (char*)fmt - 1; |
201 | 2.46k | numflag: |
202 | 2.46k | if(f->flags & FmtWidth){ |
203 | 2.46k | f->flags |= FmtPrec; |
204 | 2.46k | f->prec = i; |
205 | 2.46k | }else{ |
206 | 0 | f->flags |= FmtWidth; |
207 | 0 | f->width = i; |
208 | 0 | } |
209 | 2.46k | continue; |
210 | 0 | case '*': |
211 | 0 | i = va_arg(f->args, int); |
212 | 0 | if(i < 0){ |
213 | | /* |
214 | | * negative precision => |
215 | | * ignore the precision. |
216 | | */ |
217 | 0 | if(f->flags & FmtPrec){ |
218 | 0 | f->flags &= ~FmtPrec; |
219 | 0 | f->prec = 0; |
220 | 0 | continue; |
221 | 0 | } |
222 | 0 | i = -i; |
223 | 0 | f->flags |= FmtLeft; |
224 | 0 | } |
225 | 0 | goto numflag; |
226 | 25.8k | } |
227 | 20.9k | n = (*fmtfmt(r))(f); |
228 | 20.9k | if(n < 0) |
229 | 0 | return nil; |
230 | 20.9k | if(n == 0) |
231 | 20.9k | return fmt; |
232 | 20.9k | } |
233 | 20.9k | } |