xref: /plan9/sys/src/libc/fmt/fmt.c (revision 53ff6c4dddeb19bd84c918cdd2542d7c51263329)
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