Blob


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
11 * ANY 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 "plan9.h"
17 #include "fmt.h"
18 #include "fmtdef.h"
20 enum
21 {
22 Maxfmt = 64
23 };
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 };
32 struct
33 {
34 /* lock by calling __fmtlock, __fmtunlock */
35 int nfmt;
36 Convfmt fmt[Maxfmt];
37 } fmtalloc;
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 #ifndef PLAN9PORT
49 'F', __efgfmt, /* ANSI only */
50 #endif
51 'G', __efgfmt,
52 #ifndef PLAN9PORT
53 'L', __flagfmt, /* ANSI only */
54 #endif
55 'S', __runesfmt, /* Plan 9 addition */
56 'X', __ifmt,
57 'b', __ifmt, /* Plan 9 addition */
58 'c', __charfmt,
59 'd', __ifmt,
60 'e', __efgfmt,
61 'f', __efgfmt,
62 'g', __efgfmt,
63 'h', __flagfmt,
64 #ifndef PLAN9PORT
65 'i', __ifmt, /* ANSI only */
66 #endif
67 'l', __flagfmt,
68 'n', __countfmt,
69 'o', __ifmt,
70 'p', __ifmt,
71 'r', __errfmt,
72 's', __strfmt,
73 #ifdef PLAN9PORT
74 'u', __flagfmt,
75 #else
76 'u', __ifmt,
77 #endif
78 'x', __ifmt,
79 0, nil,
80 };
83 int (*fmtdoquote)(int);
85 /*
86 * __fmtlock() must be set
87 */
88 static int
89 __fmtinstall(int c, Fmts f)
90 {
91 Convfmt *p, *ep;
93 if(c<=0 || c>=65536)
94 return -1;
95 if(!f)
96 f = __badfmt;
98 ep = &fmtalloc.fmt[fmtalloc.nfmt];
99 for(p=fmtalloc.fmt; p<ep; p++)
100 if(p->c == c)
101 break;
103 if(p == &fmtalloc.fmt[Maxfmt])
104 return -1;
106 p->fmt = f;
107 if(p == ep){ /* installing a new format character */
108 fmtalloc.nfmt++;
109 p->c = c;
112 return 0;
115 int
116 fmtinstall(int c, int (*f)(Fmt*))
118 int ret;
120 __fmtlock();
121 ret = __fmtinstall(c, f);
122 __fmtunlock();
123 return ret;
126 static Fmts
127 fmtfmt(int c)
129 Convfmt *p, *ep;
131 ep = &fmtalloc.fmt[fmtalloc.nfmt];
132 for(p=fmtalloc.fmt; p<ep; p++)
133 if(p->c == c){
134 while(p->fmt == nil) /* loop until value is updated */
136 return p->fmt;
139 /* is this a predefined format char? */
140 __fmtlock();
141 for(p=knownfmt; p->c; p++)
142 if(p->c == c){
143 __fmtinstall(p->c, p->fmt);
144 __fmtunlock();
145 return p->fmt;
147 __fmtunlock();
149 return __badfmt;
152 void*
153 __fmtdispatch(Fmt *f, void *fmt, int isrunes)
155 Rune rune, r;
156 int i, n;
158 f->flags = 0;
159 f->width = f->prec = 0;
161 for(;;){
162 if(isrunes){
163 r = *(Rune*)fmt;
164 fmt = (Rune*)fmt + 1;
165 }else{
166 fmt = (char*)fmt + chartorune(&rune, (char*)fmt);
167 r = rune;
169 f->r = r;
170 switch(r){
171 case '\0':
172 return nil;
173 case '.':
174 f->flags |= FmtWidth|FmtPrec;
175 continue;
176 case '0':
177 if(!(f->flags & FmtWidth)){
178 f->flags |= FmtZero;
179 continue;
181 /* fall through */
182 case '1': case '2': case '3': case '4':
183 case '5': case '6': case '7': case '8': case '9':
184 i = 0;
185 while(r >= '0' && r <= '9'){
186 i = i * 10 + r - '0';
187 if(isrunes){
188 r = *(Rune*)fmt;
189 fmt = (Rune*)fmt + 1;
190 }else{
191 r = *(char*)fmt;
192 fmt = (char*)fmt + 1;
195 if(isrunes)
196 fmt = (Rune*)fmt - 1;
197 else
198 fmt = (char*)fmt - 1;
199 numflag:
200 if(f->flags & FmtWidth){
201 f->flags |= FmtPrec;
202 f->prec = i;
203 }else{
204 f->flags |= FmtWidth;
205 f->width = i;
207 continue;
208 case '*':
209 i = va_arg(f->args, int);
210 if(i < 0){
211 /*
212 * negative precision =>
213 * ignore the precision.
214 */
215 if(f->flags & FmtPrec){
216 f->flags &= ~FmtPrec;
217 f->prec = 0;
218 continue;
220 i = -i;
221 f->flags |= FmtLeft;
223 goto numflag;
225 n = (*fmtfmt(r))(f);
226 if(n < 0)
227 return nil;
228 if(n == 0)
229 return fmt;