1 /*
2 * The authors of this software are Rob Pike and Ken Thompson,
3 * with contributions from Mike Burrows and Sean Dorward.
4 *
5 * Copyright (c) 2002-2006 by Lucent Technologies.
6 * Portions Copyright (c) 2004 Google Inc.
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose without fee is hereby granted, provided that this entire notice
10 * is included in all copies of any software which is or includes a copy
11 * or modification of this software and in all copies of the supporting
12 * documentation for such software.
13 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
14 * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES
15 * NOR GOOGLE INC MAKE ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING
16 * THE MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
17 */
18
19 #include <u.h>
20 #include <libc.h>
21 #include "fmtdef.h"
22
23 enum
24 {
25 Maxfmt = 64
26 };
27
28 typedef struct Convfmt Convfmt;
29 struct Convfmt
30 {
31 int c;
32 volatile Fmts fmt; /* for spin lock in fmtfmt; avoids race due to write order */
33 };
34
35 static struct
36 {
37 /* lock by calling __fmtlock, __fmtunlock */
38 int nfmt;
39 Convfmt fmt[Maxfmt];
40 } fmtalloc;
41
42 static Convfmt knownfmt[] = {
43 ' ', __flagfmt,
44 '#', __flagfmt,
45 '%', __percentfmt,
46 '\'', __flagfmt,
47 '+', __flagfmt,
48 ',', __flagfmt,
49 '-', __flagfmt,
50 'C', __runefmt, /* Plan 9 addition */
51 'E', __efgfmt,
52 #ifndef PLAN9PORT
53 'F', __efgfmt, /* ANSI only */
54 #endif
55 'G', __efgfmt,
56 #ifndef PLAN9PORT
57 'L', __flagfmt, /* ANSI only */
58 #endif
59 'S', __runesfmt, /* Plan 9 addition */
60 'X', __ifmt,
61 'b', __ifmt, /* Plan 9 addition */
62 'c', __charfmt,
63 'd', __ifmt,
64 'e', __efgfmt,
65 'f', __efgfmt,
66 'g', __efgfmt,
67 'h', __flagfmt,
68 #ifndef PLAN9PORT
69 'i', __ifmt, /* ANSI only */
70 #endif
71 'l', __flagfmt,
72 'n', __countfmt,
73 'o', __ifmt,
74 'p', __ifmt,
75 'r', __errfmt,
76 's', __strfmt,
77 #ifdef PLAN9PORT
78 'u', __flagfmt,
79 #else
80 'u', __ifmt,
81 #endif
82 'x', __ifmt,
83 0, nil,
84 };
85
86
87 int (*fmtdoquote)(int);
88
89 /*
90 * __fmtlock() must be set
91 */
92 static int
93 __fmtinstall(int c, Fmts f)
94 {
95 Convfmt *p, *ep;
96
97 if(c<=0 || c>=65536)
98 return -1;
99 if(!f)
100 f = __badfmt;
101
102 ep = &fmtalloc.fmt[fmtalloc.nfmt];
103 for(p=fmtalloc.fmt; p<ep; p++)
104 if(p->c == c)
105 break;
106
107 if(p == &fmtalloc.fmt[Maxfmt])
108 return -1;
109
110 p->fmt = f;
111 if(p == ep){ /* installing a new format character */
112 fmtalloc.nfmt++;
113 p->c = c;
114 }
115
116 return 0;
117 }
118
119 int
120 fmtinstall(int c, int (*f)(Fmt*))
121 {
122 int ret;
123
124 __fmtlock();
125 ret = __fmtinstall(c, f);
126 __fmtunlock();
127 return ret;
128 }
129
130 static Fmts
131 fmtfmt(int c)
132 {
133 Convfmt *p, *ep;
134
135 ep = &fmtalloc.fmt[fmtalloc.nfmt];
136 for(p=fmtalloc.fmt; p<ep; p++)
137 if(p->c == c){
138 while(p->fmt == nil) /* loop until value is updated */
139 ;
140 return p->fmt;
141 }
142
143 /* is this a predefined format char? */
144 __fmtlock();
145 for(p=knownfmt; p->c; p++)
146 if(p->c == c){
147 __fmtinstall(p->c, p->fmt);
148 __fmtunlock();
149 return p->fmt;
150 }
151 __fmtunlock();
152
153 return __badfmt;
154 }
155
156 void*
157 __fmtdispatch(Fmt *f, void *fmt, int isrunes)
158 {
159 Rune rune, r;
160 int i, n;
161
162 f->flags = 0;
163 f->width = f->prec = 0;
164
165 for(;;){
166 if(isrunes){
167 r = *(Rune*)fmt;
168 fmt = (Rune*)fmt + 1;
169 }else{
170 fmt = (char*)fmt + chartorune(&rune, (char*)fmt);
171 r = rune;
172 }
173 f->r = r;
174 switch(r){
175 case '\0':
176 return nil;
177 case '.':
178 f->flags |= FmtWidth|FmtPrec;
179 continue;
180 case '0':
181 if(!(f->flags & FmtWidth)){
182 f->flags |= FmtZero;
183 continue;
184 }
185 /* fall through */
186 case '1': case '2': case '3': case '4':
187 case '5': case '6': case '7': case '8': case '9':
188 i = 0;
189 while(r >= '0' && r <= '9'){
190 i = i * 10 + r - '0';
191 if(isrunes){
192 r = *(Rune*)fmt;
193 fmt = (Rune*)fmt + 1;
194 }else{
195 r = *(char*)fmt;
196 fmt = (char*)fmt + 1;
197 }
198 }
199 if(isrunes)
200 fmt = (Rune*)fmt - 1;
201 else
202 fmt = (char*)fmt - 1;
203 numflag:
204 if(f->flags & FmtWidth){
205 f->flags |= FmtPrec;
206 f->prec = i;
207 }else{
208 f->flags |= FmtWidth;
209 f->width = i;
210 }
211 continue;
212 case '*':
213 i = va_arg(f->args, int);
214 if(i < 0){
215 /*
216 * negative precision =>
217 * ignore the precision.
218 */
219 if(f->flags & FmtPrec){
220 f->flags &= ~FmtPrec;
221 f->prec = 0;
222 continue;
223 }
224 i = -i;
225 f->flags |= FmtLeft;
226 }
227 goto numflag;
228 }
229 n = (*fmtfmt(r))(f);
230 if(n < 0)
231 return nil;
232 if(n == 0)
233 return fmt;
234 }
235 }