xref: /plan9/sys/src/cmd/unix/drawterm/libc/fmt.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
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,	/* Plan 9 addition */
32 	'E',	__efgfmt,
33 #ifndef PLAN9PORT
34 	'F',	__efgfmt,	/* ANSI only */
35 #endif
36 	'G',	__efgfmt,
37 #ifndef PLAN9PORT
38 	'L',	__flagfmt,	/* ANSI only */
39 #endif
40 	'S',	__runesfmt,	/* Plan 9 addition */
41 	'X',	__ifmt,
42 	'b',	__ifmt,		/* Plan 9 addition */
43 	'c',	__charfmt,
44 	'd',	__ifmt,
45 	'e',	__efgfmt,
46 	'f',	__efgfmt,
47 	'g',	__efgfmt,
48 	'h',	__flagfmt,
49 #ifndef PLAN9PORT
50 	'i',	__ifmt,		/* ANSI only */
51 #endif
52 	'l',	__flagfmt,
53 	'n',	__countfmt,
54 	'o',	__ifmt,
55 	'p',	__ifmt,
56 	'r',	__errfmt,
57 	's',	__strfmt,
58 #ifdef PLAN9PORT
59 	'u',	__flagfmt,
60 #else
61 	'u',	__ifmt,
62 #endif
63 	'x',	__ifmt,
64 	0,	0,
65 };
66 
67 
68 int	(*fmtdoquote)(int);
69 
70 /*
71  * __fmtlock() must be set
72  */
73 static int
__fmtinstall(int c,Fmts f)74 __fmtinstall(int c, Fmts f)
75 {
76 	Convfmt *p, *ep;
77 
78 	if(c<=0 || c>=65536)
79 		return -1;
80 	if(!f)
81 		f = __badfmt;
82 
83 	ep = &fmtalloc.fmt[fmtalloc.nfmt];
84 	for(p=fmtalloc.fmt; p<ep; p++)
85 		if(p->c == c)
86 			break;
87 
88 	if(p == &fmtalloc.fmt[Maxfmt])
89 		return -1;
90 
91 	p->fmt = f;
92 	if(p == ep){	/* installing a new format character */
93 		fmtalloc.nfmt++;
94 		p->c = c;
95 	}
96 
97 	return 0;
98 }
99 
100 int
fmtinstall(int c,int (* f)(Fmt *))101 fmtinstall(int c, int (*f)(Fmt*))
102 {
103 	int ret;
104 
105 	__fmtlock();
106 	ret = __fmtinstall(c, f);
107 	__fmtunlock();
108 	return ret;
109 }
110 
111 static Fmts
fmtfmt(int c)112 fmtfmt(int c)
113 {
114 	Convfmt *p, *ep;
115 
116 	ep = &fmtalloc.fmt[fmtalloc.nfmt];
117 	for(p=fmtalloc.fmt; p<ep; p++)
118 		if(p->c == c){
119 			while(p->fmt == 0)	/* loop until value is updated */
120 				;
121 			return p->fmt;
122 		}
123 
124 	/* is this a predefined format char? */
125 	__fmtlock();
126 	for(p=knownfmt; p->c; p++)
127 		if(p->c == c){
128 			__fmtinstall(p->c, p->fmt);
129 			__fmtunlock();
130 			return p->fmt;
131 		}
132 	__fmtunlock();
133 
134 	return __badfmt;
135 }
136 
137 void*
__fmtdispatch(Fmt * f,void * fmt,int isrunes)138 __fmtdispatch(Fmt *f, void *fmt, int isrunes)
139 {
140 	Rune rune, r;
141 	int i, n;
142 
143 	f->flags = 0;
144 	f->width = f->prec = 0;
145 
146 	for(;;){
147 		if(isrunes){
148 			r = *(Rune*)fmt;
149 			fmt = (Rune*)fmt + 1;
150 		}else{
151 			fmt = (char*)fmt + chartorune(&rune, (char*)fmt);
152 			r = rune;
153 		}
154 		f->r = r;
155 		switch(r){
156 		case '\0':
157 			return nil;
158 		case '.':
159 			f->flags |= FmtWidth|FmtPrec;
160 			continue;
161 		case '0':
162 			if(!(f->flags & FmtWidth)){
163 				f->flags |= FmtZero;
164 				continue;
165 			}
166 			/* fall through */
167 		case '1': case '2': case '3': case '4':
168 		case '5': case '6': case '7': case '8': case '9':
169 			i = 0;
170 			while(r >= '0' && r <= '9'){
171 				i = i * 10 + r - '0';
172 				if(isrunes){
173 					r = *(Rune*)fmt;
174 					fmt = (Rune*)fmt + 1;
175 				}else{
176 					r = *(char*)fmt;
177 					fmt = (char*)fmt + 1;
178 				}
179 			}
180 			if(isrunes)
181 				fmt = (Rune*)fmt - 1;
182 			else
183 				fmt = (char*)fmt - 1;
184 		numflag:
185 			if(f->flags & FmtWidth){
186 				f->flags |= FmtPrec;
187 				f->prec = i;
188 			}else{
189 				f->flags |= FmtWidth;
190 				f->width = i;
191 			}
192 			continue;
193 		case '*':
194 			i = va_arg(f->args, int);
195 			if(i < 0){
196 				/*
197 				 * negative precision =>
198 				 * ignore the precision.
199 				 */
200 				if(f->flags & FmtPrec){
201 					f->flags &= ~FmtPrec;
202 					f->prec = 0;
203 					continue;
204 				}
205 				i = -i;
206 				f->flags |= FmtLeft;
207 			}
208 			goto numflag;
209 		}
210 		n = (*fmtfmt(r))(f);
211 		if(n < 0)
212 			return nil;
213 		if(n == 0)
214 			return fmt;
215 	}
216 }
217