xref: /plan9/sys/src/cmd/unix/drawterm/libc/fmt.c (revision 0d601874851962e88c6c60fdd2e637bba04e13c2)
1*0d601874SDavid du Colombier /*
2*0d601874SDavid du Colombier  * The authors of this software are Rob Pike and Ken Thompson.
3*0d601874SDavid du Colombier  *              Copyright (c) 2002 by Lucent Technologies.
4*0d601874SDavid du Colombier  * Permission to use, copy, modify, and distribute this software for any
5*0d601874SDavid du Colombier  * purpose without fee is hereby granted, provided that this entire notice
6*0d601874SDavid du Colombier  * is included in all copies of any software which is or includes a copy
7*0d601874SDavid du Colombier  * or modification of this software and in all copies of the supporting
8*0d601874SDavid du Colombier  * documentation for such software.
9*0d601874SDavid du Colombier  * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
10*0d601874SDavid du Colombier  * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE
11*0d601874SDavid du Colombier  * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
12*0d601874SDavid du Colombier  * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
13*0d601874SDavid du Colombier  */
148ccd4a63SDavid du Colombier #include <u.h>
158ccd4a63SDavid du Colombier #include <libc.h>
168ccd4a63SDavid du Colombier #include "fmtdef.h"
178ccd4a63SDavid du Colombier 
188ccd4a63SDavid du Colombier enum
198ccd4a63SDavid du Colombier {
208ccd4a63SDavid du Colombier 	Maxfmt = 64
218ccd4a63SDavid du Colombier };
228ccd4a63SDavid du Colombier 
238ccd4a63SDavid du Colombier typedef struct Convfmt Convfmt;
248ccd4a63SDavid du Colombier struct Convfmt
258ccd4a63SDavid du Colombier {
268ccd4a63SDavid du Colombier 	int	c;
278ccd4a63SDavid du Colombier 	volatile	Fmts	fmt;	/* for spin lock in fmtfmt; avoids race due to write order */
288ccd4a63SDavid du Colombier };
298ccd4a63SDavid du Colombier 
308ccd4a63SDavid du Colombier struct
318ccd4a63SDavid du Colombier {
32*0d601874SDavid du Colombier 	/* lock by calling __fmtlock, __fmtunlock */
338ccd4a63SDavid du Colombier 	int	nfmt;
348ccd4a63SDavid du Colombier 	Convfmt	fmt[Maxfmt];
358ccd4a63SDavid du Colombier } fmtalloc;
368ccd4a63SDavid du Colombier 
378ccd4a63SDavid du Colombier static Convfmt knownfmt[] = {
38*0d601874SDavid du Colombier 	' ',	__flagfmt,
39*0d601874SDavid du Colombier 	'#',	__flagfmt,
40*0d601874SDavid du Colombier 	'%',	__percentfmt,
41*0d601874SDavid du Colombier 	'+',	__flagfmt,
42*0d601874SDavid du Colombier 	',',	__flagfmt,
43*0d601874SDavid du Colombier 	'-',	__flagfmt,
44*0d601874SDavid du Colombier 	'C',	__runefmt,	/* Plan 9 addition */
45*0d601874SDavid du Colombier 	'E',	__efgfmt,
46*0d601874SDavid du Colombier #ifndef PLAN9PORT
47*0d601874SDavid du Colombier 	'F',	__efgfmt,	/* ANSI only */
48*0d601874SDavid du Colombier #endif
49*0d601874SDavid du Colombier 	'G',	__efgfmt,
50*0d601874SDavid du Colombier #ifndef PLAN9PORT
51*0d601874SDavid du Colombier 	'L',	__flagfmt,	/* ANSI only */
52*0d601874SDavid du Colombier #endif
53*0d601874SDavid du Colombier 	'S',	__runesfmt,	/* Plan 9 addition */
54*0d601874SDavid du Colombier 	'X',	__ifmt,
55*0d601874SDavid du Colombier 	'b',	__ifmt,		/* Plan 9 addition */
56*0d601874SDavid du Colombier 	'c',	__charfmt,
57*0d601874SDavid du Colombier 	'd',	__ifmt,
58*0d601874SDavid du Colombier 	'e',	__efgfmt,
59*0d601874SDavid du Colombier 	'f',	__efgfmt,
60*0d601874SDavid du Colombier 	'g',	__efgfmt,
61*0d601874SDavid du Colombier 	'h',	__flagfmt,
62*0d601874SDavid du Colombier #ifndef PLAN9PORT
63*0d601874SDavid du Colombier 	'i',	__ifmt,		/* ANSI only */
64*0d601874SDavid du Colombier #endif
65*0d601874SDavid du Colombier 	'l',	__flagfmt,
66*0d601874SDavid du Colombier 	'n',	__countfmt,
67*0d601874SDavid du Colombier 	'o',	__ifmt,
68*0d601874SDavid du Colombier 	'p',	__ifmt,
69*0d601874SDavid du Colombier 	'r',	__errfmt,
70*0d601874SDavid du Colombier 	's',	__strfmt,
71*0d601874SDavid du Colombier #ifdef PLAN9PORT
72*0d601874SDavid du Colombier 	'u',	__flagfmt,
73*0d601874SDavid du Colombier #else
74*0d601874SDavid du Colombier 	'u',	__ifmt,
75*0d601874SDavid du Colombier #endif
76*0d601874SDavid du Colombier 	'x',	__ifmt,
778ccd4a63SDavid du Colombier 	0,	nil,
788ccd4a63SDavid du Colombier };
798ccd4a63SDavid du Colombier 
80*0d601874SDavid du Colombier 
81*0d601874SDavid du Colombier int	(*fmtdoquote)(int);
828ccd4a63SDavid du Colombier 
838ccd4a63SDavid du Colombier /*
84*0d601874SDavid du Colombier  * __fmtlock() must be set
858ccd4a63SDavid du Colombier  */
868ccd4a63SDavid du Colombier static int
87*0d601874SDavid du Colombier __fmtinstall(int c, Fmts f)
888ccd4a63SDavid du Colombier {
898ccd4a63SDavid du Colombier 	Convfmt *p, *ep;
908ccd4a63SDavid du Colombier 
918ccd4a63SDavid du Colombier 	if(c<=0 || c>=65536)
928ccd4a63SDavid du Colombier 		return -1;
938ccd4a63SDavid du Colombier 	if(!f)
94*0d601874SDavid du Colombier 		f = __badfmt;
958ccd4a63SDavid du Colombier 
968ccd4a63SDavid du Colombier 	ep = &fmtalloc.fmt[fmtalloc.nfmt];
978ccd4a63SDavid du Colombier 	for(p=fmtalloc.fmt; p<ep; p++)
988ccd4a63SDavid du Colombier 		if(p->c == c)
998ccd4a63SDavid du Colombier 			break;
1008ccd4a63SDavid du Colombier 
1018ccd4a63SDavid du Colombier 	if(p == &fmtalloc.fmt[Maxfmt])
1028ccd4a63SDavid du Colombier 		return -1;
1038ccd4a63SDavid du Colombier 
1048ccd4a63SDavid du Colombier 	p->fmt = f;
1058ccd4a63SDavid du Colombier 	if(p == ep){	/* installing a new format character */
1068ccd4a63SDavid du Colombier 		fmtalloc.nfmt++;
1078ccd4a63SDavid du Colombier 		p->c = c;
1088ccd4a63SDavid du Colombier 	}
1098ccd4a63SDavid du Colombier 
1108ccd4a63SDavid du Colombier 	return 0;
1118ccd4a63SDavid du Colombier }
1128ccd4a63SDavid du Colombier 
1138ccd4a63SDavid du Colombier int
114*0d601874SDavid du Colombier fmtinstall(int c, int (*f)(Fmt*))
1158ccd4a63SDavid du Colombier {
1168ccd4a63SDavid du Colombier 	int ret;
1178ccd4a63SDavid du Colombier 
118*0d601874SDavid du Colombier 	__fmtlock();
119*0d601874SDavid du Colombier 	ret = __fmtinstall(c, f);
120*0d601874SDavid du Colombier 	__fmtunlock();
1218ccd4a63SDavid du Colombier 	return ret;
1228ccd4a63SDavid du Colombier }
1238ccd4a63SDavid du Colombier 
1248ccd4a63SDavid du Colombier static Fmts
1258ccd4a63SDavid du Colombier fmtfmt(int c)
1268ccd4a63SDavid du Colombier {
1278ccd4a63SDavid du Colombier 	Convfmt *p, *ep;
1288ccd4a63SDavid du Colombier 
1298ccd4a63SDavid du Colombier 	ep = &fmtalloc.fmt[fmtalloc.nfmt];
1308ccd4a63SDavid du Colombier 	for(p=fmtalloc.fmt; p<ep; p++)
1318ccd4a63SDavid du Colombier 		if(p->c == c){
1328ccd4a63SDavid du Colombier 			while(p->fmt == nil)	/* loop until value is updated */
1338ccd4a63SDavid du Colombier 				;
1348ccd4a63SDavid du Colombier 			return p->fmt;
1358ccd4a63SDavid du Colombier 		}
1368ccd4a63SDavid du Colombier 
1378ccd4a63SDavid du Colombier 	/* is this a predefined format char? */
138*0d601874SDavid du Colombier 	__fmtlock();
1398ccd4a63SDavid du Colombier 	for(p=knownfmt; p->c; p++)
1408ccd4a63SDavid du Colombier 		if(p->c == c){
141*0d601874SDavid du Colombier 			__fmtinstall(p->c, p->fmt);
142*0d601874SDavid du Colombier 			__fmtunlock();
1438ccd4a63SDavid du Colombier 			return p->fmt;
1448ccd4a63SDavid du Colombier 		}
145*0d601874SDavid du Colombier 	__fmtunlock();
1468ccd4a63SDavid du Colombier 
147*0d601874SDavid du Colombier 	return __badfmt;
1488ccd4a63SDavid du Colombier }
1498ccd4a63SDavid du Colombier 
1508ccd4a63SDavid du Colombier void*
151*0d601874SDavid du Colombier __fmtdispatch(Fmt *f, void *fmt, int isrunes)
1528ccd4a63SDavid du Colombier {
1538ccd4a63SDavid du Colombier 	Rune rune, r;
1548ccd4a63SDavid du Colombier 	int i, n;
1558ccd4a63SDavid du Colombier 
1568ccd4a63SDavid du Colombier 	f->flags = 0;
1578ccd4a63SDavid du Colombier 	f->width = f->prec = 0;
1588ccd4a63SDavid du Colombier 
1598ccd4a63SDavid du Colombier 	for(;;){
1608ccd4a63SDavid du Colombier 		if(isrunes){
1618ccd4a63SDavid du Colombier 			r = *(Rune*)fmt;
1628ccd4a63SDavid du Colombier 			fmt = (Rune*)fmt + 1;
1638ccd4a63SDavid du Colombier 		}else{
164*0d601874SDavid du Colombier 			fmt = (char*)fmt + chartorune(&rune, (char*)fmt);
1658ccd4a63SDavid du Colombier 			r = rune;
1668ccd4a63SDavid du Colombier 		}
1678ccd4a63SDavid du Colombier 		f->r = r;
1688ccd4a63SDavid du Colombier 		switch(r){
1698ccd4a63SDavid du Colombier 		case '\0':
1708ccd4a63SDavid du Colombier 			return nil;
1718ccd4a63SDavid du Colombier 		case '.':
1728ccd4a63SDavid du Colombier 			f->flags |= FmtWidth|FmtPrec;
1738ccd4a63SDavid du Colombier 			continue;
1748ccd4a63SDavid du Colombier 		case '0':
1758ccd4a63SDavid du Colombier 			if(!(f->flags & FmtWidth)){
1768ccd4a63SDavid du Colombier 				f->flags |= FmtZero;
1778ccd4a63SDavid du Colombier 				continue;
1788ccd4a63SDavid du Colombier 			}
1798ccd4a63SDavid du Colombier 			/* fall through */
1808ccd4a63SDavid du Colombier 		case '1': case '2': case '3': case '4':
1818ccd4a63SDavid du Colombier 		case '5': case '6': case '7': case '8': case '9':
1828ccd4a63SDavid du Colombier 			i = 0;
1838ccd4a63SDavid du Colombier 			while(r >= '0' && r <= '9'){
1848ccd4a63SDavid du Colombier 				i = i * 10 + r - '0';
1858ccd4a63SDavid du Colombier 				if(isrunes){
1868ccd4a63SDavid du Colombier 					r = *(Rune*)fmt;
1878ccd4a63SDavid du Colombier 					fmt = (Rune*)fmt + 1;
1888ccd4a63SDavid du Colombier 				}else{
1898ccd4a63SDavid du Colombier 					r = *(char*)fmt;
1908ccd4a63SDavid du Colombier 					fmt = (char*)fmt + 1;
1918ccd4a63SDavid du Colombier 				}
1928ccd4a63SDavid du Colombier 			}
1938ccd4a63SDavid du Colombier 			if(isrunes)
1948ccd4a63SDavid du Colombier 				fmt = (Rune*)fmt - 1;
1958ccd4a63SDavid du Colombier 			else
1968ccd4a63SDavid du Colombier 				fmt = (char*)fmt - 1;
1978ccd4a63SDavid du Colombier 		numflag:
1988ccd4a63SDavid du Colombier 			if(f->flags & FmtWidth){
1998ccd4a63SDavid du Colombier 				f->flags |= FmtPrec;
2008ccd4a63SDavid du Colombier 				f->prec = i;
2018ccd4a63SDavid du Colombier 			}else{
2028ccd4a63SDavid du Colombier 				f->flags |= FmtWidth;
2038ccd4a63SDavid du Colombier 				f->width = i;
2048ccd4a63SDavid du Colombier 			}
2058ccd4a63SDavid du Colombier 			continue;
2068ccd4a63SDavid du Colombier 		case '*':
2078ccd4a63SDavid du Colombier 			i = va_arg(f->args, int);
2088ccd4a63SDavid du Colombier 			if(i < 0){
209*0d601874SDavid du Colombier 				/*
210*0d601874SDavid du Colombier 				 * negative precision =>
211*0d601874SDavid du Colombier 				 * ignore the precision.
212*0d601874SDavid du Colombier 				 */
213*0d601874SDavid du Colombier 				if(f->flags & FmtPrec){
214*0d601874SDavid du Colombier 					f->flags &= ~FmtPrec;
215*0d601874SDavid du Colombier 					f->prec = 0;
216*0d601874SDavid du Colombier 					continue;
217*0d601874SDavid du Colombier 				}
2188ccd4a63SDavid du Colombier 				i = -i;
2198ccd4a63SDavid du Colombier 				f->flags |= FmtLeft;
2208ccd4a63SDavid du Colombier 			}
2218ccd4a63SDavid du Colombier 			goto numflag;
2228ccd4a63SDavid du Colombier 		}
2238ccd4a63SDavid du Colombier 		n = (*fmtfmt(r))(f);
2248ccd4a63SDavid du Colombier 		if(n < 0)
2258ccd4a63SDavid du Colombier 			return nil;
2268ccd4a63SDavid du Colombier 		if(n == 0)
2278ccd4a63SDavid du Colombier 			return fmt;
2288ccd4a63SDavid du Colombier 	}
2298ccd4a63SDavid du Colombier }
230