fmt.c (4751B) download
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
30enum {
31 Maxfmt = 128
32};
33
34typedef struct Convfmt Convfmt;
35struct Convfmt {
36 int c;
37 Fmts fmt;
38};
39
40static 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
101int (*fmtdoquote)(int);
102
103/*
104 * __fmtlock() must be set
105 */
106static 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
127int fmtinstall(int c, int (*f)(Fmt*)) {
128 int ret;
129
130 __fmtlock();
131 ret = __fmtinstall(c, f);
132 __fmtunlock();
133 return ret;
134}
135
136static Fmts
137fmtfmt(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
148void* __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}