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