1 #include <u.h>
2 #include <libc.h>
3 #include "fmtdef.h"
4
5 enum
6 {
7 Maxfmt = 64
8 };
9
10 typedef struct Convfmt Convfmt;
11 struct Convfmt
12 {
13 int c;
14 volatile Fmts fmt; /* for spin lock in fmtfmt; avoids race due to write order */
15 };
16
17 struct
18 {
19 /* lock by calling _fmtlock, _fmtunlock */
20 int nfmt;
21 Convfmt fmt[Maxfmt];
22 } fmtalloc;
23
24 static Convfmt knownfmt[] = {
25 ' ', _flagfmt,
26 '#', _flagfmt,
27 '%', _percentfmt,
28 '+', _flagfmt,
29 ',', _flagfmt,
30 '-', _flagfmt,
31 'C', _runefmt,
32 'E', _efgfmt,
33 'G', _efgfmt,
34 'S', _runesfmt,
35 'X', _ifmt,
36 'b', _ifmt,
37 'c', _charfmt,
38 'd', _ifmt,
39 'e', _efgfmt,
40 'f', _efgfmt,
41 'g', _efgfmt,
42 'h', _flagfmt,
43 'l', _flagfmt,
44 'n', _countfmt,
45 'o', _ifmt,
46 'p', _ifmt,
47 'r', errfmt,
48 's', _strfmt,
49 'u', _flagfmt,
50 'x', _ifmt,
51 0, nil,
52 };
53
54 int (*doquote)(int);
55
56 /*
57 * _fmtlock() must be set
58 */
59 static int
_fmtinstall(int c,Fmts f)60 _fmtinstall(int c, Fmts f)
61 {
62 Convfmt *p, *ep;
63
64 if(c<=0 || c>=65536)
65 return -1;
66 if(!f)
67 f = _badfmt;
68
69 ep = &fmtalloc.fmt[fmtalloc.nfmt];
70 for(p=fmtalloc.fmt; p<ep; p++)
71 if(p->c == c)
72 break;
73
74 if(p == &fmtalloc.fmt[Maxfmt])
75 return -1;
76
77 p->fmt = f;
78 if(p == ep){ /* installing a new format character */
79 fmtalloc.nfmt++;
80 p->c = c;
81 }
82
83 return 0;
84 }
85
86 int
fmtinstall(int c,Fmts f)87 fmtinstall(int c, Fmts f)
88 {
89 int ret;
90
91 _fmtlock();
92 ret = _fmtinstall(c, f);
93 _fmtunlock();
94 return ret;
95 }
96
97 static Fmts
fmtfmt(int c)98 fmtfmt(int c)
99 {
100 Convfmt *p, *ep;
101
102 ep = &fmtalloc.fmt[fmtalloc.nfmt];
103 for(p=fmtalloc.fmt; p<ep; p++)
104 if(p->c == c){
105 while(p->fmt == nil) /* loop until value is updated */
106 ;
107 return p->fmt;
108 }
109
110 /* is this a predefined format char? */
111 _fmtlock();
112 for(p=knownfmt; p->c; p++)
113 if(p->c == c){
114 _fmtinstall(p->c, p->fmt);
115 _fmtunlock();
116 return p->fmt;
117 }
118 _fmtunlock();
119
120 return _badfmt;
121 }
122
123 void*
_fmtdispatch(Fmt * f,void * fmt,int isrunes)124 _fmtdispatch(Fmt *f, void *fmt, int isrunes)
125 {
126 Rune rune, r;
127 int i, n, w, p;
128 ulong fl;
129 void *ret;
130
131 w = f->width;
132 p = f->prec;
133 fl = f->flags;
134
135 f->flags = 0;
136 f->width = f->prec = 0;
137
138 for(;;){
139 if(isrunes){
140 r = *(Rune*)fmt;
141 fmt = (Rune*)fmt + 1;
142 }else{
143 fmt = (char*)fmt + chartorune(&rune, fmt);
144 r = rune;
145 }
146 f->r = r;
147 switch(r){
148 case '\0':
149 ret = nil;
150 goto end;
151 case '.':
152 f->flags |= FmtWidth|FmtPrec;
153 continue;
154 case '0':
155 if(!(f->flags & FmtWidth)){
156 f->flags |= FmtZero;
157 continue;
158 }
159 /* fall through */
160 case '1': case '2': case '3': case '4':
161 case '5': case '6': case '7': case '8': case '9':
162 i = 0;
163 while(r >= '0' && r <= '9'){
164 i = i * 10 + r - '0';
165 if(isrunes){
166 r = *(Rune*)fmt;
167 fmt = (Rune*)fmt + 1;
168 }else{
169 r = *(char*)fmt;
170 fmt = (char*)fmt + 1;
171 }
172 }
173 if(isrunes)
174 fmt = (Rune*)fmt - 1;
175 else
176 fmt = (char*)fmt - 1;
177 numflag:
178 if(f->flags & FmtWidth){
179 f->flags |= FmtPrec;
180 f->prec = i;
181 }else{
182 f->flags |= FmtWidth;
183 f->width = i;
184 }
185 continue;
186 case '*':
187 i = va_arg(f->args, int);
188 if(i < 0){
189 /*
190 * negative precision =>
191 * ignore the precision.
192 */
193 if(f->flags & FmtPrec){
194 f->flags &= ~FmtPrec;
195 f->prec = 0;
196 continue;
197 }
198 i = -i;
199 f->flags |= FmtLeft;
200 }
201 goto numflag;
202 }
203 n = (*fmtfmt(r))(f);
204 if(n < 0){
205 ret = nil;
206 break;
207 }
208 if(n == 0){
209 ret = fmt;
210 break;
211 }
212 }
213 end:
214 f->width = w;
215 f->prec = p;
216 f->flags = fl;
217 return ret;
218 }
219