xref: /plan9-contrib/sys/src/libc/fmt/fmt.c (revision 56fcb4db5c720201392d2b20e8adbaa6752c58a7)
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 	Rune	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(Rune c,Fmts f)60 _fmtinstall(Rune 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(Rune c)98 fmtfmt(Rune c)
99 {
100 	Convfmt *p, *ep;
101 
102 	if (c == '\0')
103 		return _badfmt;
104 
105 	ep = &fmtalloc.fmt[fmtalloc.nfmt];
106 	for(p=fmtalloc.fmt; p<ep; p++)
107 		if(p->c == c){
108 			while(p->fmt == nil)	/* loop until value is updated */
109 				;
110 			return p->fmt;
111 		}
112 
113 	/* is this a predefined format char? */
114 	_fmtlock();
115 	for(p=knownfmt; p->c; p++)
116 		if(p->c == c){
117 			_fmtinstall(p->c, p->fmt);
118 			_fmtunlock();
119 			return p->fmt;
120 		}
121 	_fmtunlock();
122 
123 	return _badfmt;
124 }
125 
126 void*
_fmtdispatch(Fmt * f,void * fmt,int isrunes)127 _fmtdispatch(Fmt *f, void *fmt, int isrunes)
128 {
129 	Rune rune, r;
130 	int i, n, w, p;
131 	ulong fl;
132 	void *ret;
133 
134 	w = f->width;
135 	p = f->prec;
136 	fl = f->flags;
137 
138 	f->flags = 0;
139 	f->width = f->prec = 0;
140 
141 	for(;;){
142 		if(isrunes){
143 			r = *(Rune*)fmt;
144 			fmt = (Rune*)fmt + 1;
145 		}else{
146 			fmt = (char*)fmt + chartorune(&rune, fmt);
147 			r = rune;
148 		}
149 		f->r = r;
150 		switch(r){
151 		case '\0':
152 			ret = nil;
153 			goto end;
154 		case '.':
155 			f->flags |= FmtWidth|FmtPrec;
156 			continue;
157 		case '0':
158 			if(!(f->flags & FmtWidth)){
159 				f->flags |= FmtZero;
160 				continue;
161 			}
162 			/* fall through */
163 		case '1': case '2': case '3': case '4':
164 		case '5': case '6': case '7': case '8': case '9':
165 			i = 0;
166 			while(r >= '0' && r <= '9'){
167 				i = i * 10 + r - '0';
168 				if(isrunes){
169 					r = *(Rune*)fmt;
170 					fmt = (Rune*)fmt + 1;
171 				}else{
172 					r = *(char*)fmt;
173 					fmt = (char*)fmt + 1;
174 				}
175 			}
176 			if(isrunes)
177 				fmt = (Rune*)fmt - 1;
178 			else
179 				fmt = (char*)fmt - 1;
180 		numflag:
181 			if(f->flags & FmtWidth){
182 				f->flags |= FmtPrec;
183 				f->prec = i;
184 			}else{
185 				f->flags |= FmtWidth;
186 				f->width = i;
187 			}
188 			continue;
189 		case '*':
190 			i = va_arg(f->args, int);
191 			if(i < 0){
192 				/*
193 				 * negative precision =>
194 				 * ignore the precision.
195 				 */
196 				if(f->flags & FmtPrec){
197 					f->flags &= ~FmtPrec;
198 					f->prec = 0;
199 					continue;
200 				}
201 				i = -i;
202 				f->flags |= FmtLeft;
203 			}
204 			goto numflag;
205 		}
206 		n = (*fmtfmt(r))(f);
207 		if(n < 0){
208 			ret = nil;
209 			break;
210 		}
211 		if(n == 0){
212 			ret = fmt;
213 			break;
214 		}
215 	}
216 end:
217 	f->width = w;
218 	f->prec = p;
219 	f->flags = fl;
220 	return ret;
221 }
222