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