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