xref: /plan9/sys/src/libc/fmt/fmt.c (revision 53ff6c4dddeb19bd84c918cdd2542d7c51263329)
19a747e4fSDavid du Colombier #include <u.h>
29a747e4fSDavid du Colombier #include <libc.h>
39a747e4fSDavid du Colombier #include "fmtdef.h"
49a747e4fSDavid du Colombier 
59a747e4fSDavid du Colombier enum
69a747e4fSDavid du Colombier {
79a747e4fSDavid du Colombier 	Maxfmt = 64
89a747e4fSDavid du Colombier };
99a747e4fSDavid du Colombier 
109a747e4fSDavid du Colombier typedef struct Convfmt Convfmt;
119a747e4fSDavid du Colombier struct Convfmt
129a747e4fSDavid du Colombier {
139a747e4fSDavid du Colombier 	int	c;
149a747e4fSDavid du Colombier 	volatile	Fmts	fmt;	/* for spin lock in fmtfmt; avoids race due to write order */
159a747e4fSDavid du Colombier };
169a747e4fSDavid du Colombier 
179a747e4fSDavid du Colombier struct
189a747e4fSDavid du Colombier {
199a747e4fSDavid du Colombier 	/* lock by calling _fmtlock, _fmtunlock */
209a747e4fSDavid du Colombier 	int	nfmt;
219a747e4fSDavid du Colombier 	Convfmt	fmt[Maxfmt];
229a747e4fSDavid du Colombier } fmtalloc;
239a747e4fSDavid du Colombier 
249a747e4fSDavid du Colombier static Convfmt knownfmt[] = {
259a747e4fSDavid du Colombier 	' ',	_flagfmt,
269a747e4fSDavid du Colombier 	'#',	_flagfmt,
279a747e4fSDavid du Colombier 	'%',	_percentfmt,
289a747e4fSDavid du Colombier 	'+',	_flagfmt,
299a747e4fSDavid du Colombier 	',',	_flagfmt,
309a747e4fSDavid du Colombier 	'-',	_flagfmt,
319a747e4fSDavid du Colombier 	'C',	_runefmt,
329a747e4fSDavid du Colombier 	'E',	_efgfmt,
339a747e4fSDavid du Colombier 	'G',	_efgfmt,
349a747e4fSDavid du Colombier 	'S',	_runesfmt,
359a747e4fSDavid du Colombier 	'X',	_ifmt,
369a747e4fSDavid du Colombier 	'b',	_ifmt,
379a747e4fSDavid du Colombier 	'c',	_charfmt,
389a747e4fSDavid du Colombier 	'd',	_ifmt,
399a747e4fSDavid du Colombier 	'e',	_efgfmt,
409a747e4fSDavid du Colombier 	'f',	_efgfmt,
419a747e4fSDavid du Colombier 	'g',	_efgfmt,
429a747e4fSDavid du Colombier 	'h',	_flagfmt,
439a747e4fSDavid du Colombier 	'l',	_flagfmt,
449a747e4fSDavid du Colombier 	'n',	_countfmt,
459a747e4fSDavid du Colombier 	'o',	_ifmt,
469a747e4fSDavid du Colombier 	'p',	_ifmt,
479a747e4fSDavid du Colombier 	'r',	errfmt,
489a747e4fSDavid du Colombier 	's',	_strfmt,
499a747e4fSDavid du Colombier 	'u',	_flagfmt,
509a747e4fSDavid du Colombier 	'x',	_ifmt,
519a747e4fSDavid du Colombier 	0,	nil,
529a747e4fSDavid du Colombier };
539a747e4fSDavid du Colombier 
549a747e4fSDavid du Colombier int	(*doquote)(int);
559a747e4fSDavid du Colombier 
563ff48bf5SDavid du Colombier /*
573ff48bf5SDavid du Colombier  * _fmtlock() must be set
583ff48bf5SDavid du Colombier  */
593ff48bf5SDavid du Colombier static int
_fmtinstall(int c,Fmts f)603ff48bf5SDavid du Colombier _fmtinstall(int c, Fmts f)
619a747e4fSDavid du Colombier {
629a747e4fSDavid du Colombier 	Convfmt *p, *ep;
639a747e4fSDavid du Colombier 
649a747e4fSDavid du Colombier 	if(c<=0 || c>=65536)
659a747e4fSDavid du Colombier 		return -1;
669a747e4fSDavid du Colombier 	if(!f)
679a747e4fSDavid du Colombier 		f = _badfmt;
689a747e4fSDavid du Colombier 
699a747e4fSDavid du Colombier 	ep = &fmtalloc.fmt[fmtalloc.nfmt];
709a747e4fSDavid du Colombier 	for(p=fmtalloc.fmt; p<ep; p++)
719a747e4fSDavid du Colombier 		if(p->c == c)
729a747e4fSDavid du Colombier 			break;
739a747e4fSDavid du Colombier 
743ff48bf5SDavid du Colombier 	if(p == &fmtalloc.fmt[Maxfmt])
759a747e4fSDavid du Colombier 		return -1;
769a747e4fSDavid du Colombier 
779a747e4fSDavid du Colombier 	p->fmt = f;
789a747e4fSDavid du Colombier 	if(p == ep){	/* installing a new format character */
799a747e4fSDavid du Colombier 		fmtalloc.nfmt++;
809a747e4fSDavid du Colombier 		p->c = c;
819a747e4fSDavid du Colombier 	}
829a747e4fSDavid du Colombier 
839a747e4fSDavid du Colombier 	return 0;
849a747e4fSDavid du Colombier }
859a747e4fSDavid du Colombier 
863ff48bf5SDavid du Colombier int
fmtinstall(int c,Fmts f)873ff48bf5SDavid du Colombier fmtinstall(int c, Fmts f)
883ff48bf5SDavid du Colombier {
893ff48bf5SDavid du Colombier 	int ret;
903ff48bf5SDavid du Colombier 
913ff48bf5SDavid du Colombier 	_fmtlock();
923ff48bf5SDavid du Colombier 	ret = _fmtinstall(c, f);
933ff48bf5SDavid du Colombier 	_fmtunlock();
943ff48bf5SDavid du Colombier 	return ret;
953ff48bf5SDavid du Colombier }
963ff48bf5SDavid du Colombier 
973ff48bf5SDavid du Colombier static Fmts
fmtfmt(int c)983ff48bf5SDavid du Colombier fmtfmt(int c)
993ff48bf5SDavid du Colombier {
1003ff48bf5SDavid du Colombier 	Convfmt *p, *ep;
1013ff48bf5SDavid du Colombier 
1023ff48bf5SDavid du Colombier 	ep = &fmtalloc.fmt[fmtalloc.nfmt];
1033ff48bf5SDavid du Colombier 	for(p=fmtalloc.fmt; p<ep; p++)
1043ff48bf5SDavid du Colombier 		if(p->c == c){
1053ff48bf5SDavid du Colombier 			while(p->fmt == nil)	/* loop until value is updated */
1063ff48bf5SDavid du Colombier 				;
1073ff48bf5SDavid du Colombier 			return p->fmt;
1083ff48bf5SDavid du Colombier 		}
1093ff48bf5SDavid du Colombier 
1103ff48bf5SDavid du Colombier 	/* is this a predefined format char? */
1113ff48bf5SDavid du Colombier 	_fmtlock();
1123ff48bf5SDavid du Colombier 	for(p=knownfmt; p->c; p++)
1133ff48bf5SDavid du Colombier 		if(p->c == c){
1143ff48bf5SDavid du Colombier 			_fmtinstall(p->c, p->fmt);
1153ff48bf5SDavid du Colombier 			_fmtunlock();
1163ff48bf5SDavid du Colombier 			return p->fmt;
1173ff48bf5SDavid du Colombier 		}
1183ff48bf5SDavid du Colombier 	_fmtunlock();
1193ff48bf5SDavid du Colombier 
1203ff48bf5SDavid du Colombier 	return _badfmt;
1213ff48bf5SDavid du Colombier }
1223ff48bf5SDavid du Colombier 
1239a747e4fSDavid du Colombier void*
_fmtdispatch(Fmt * f,void * fmt,int isrunes)1249a747e4fSDavid du Colombier _fmtdispatch(Fmt *f, void *fmt, int isrunes)
1259a747e4fSDavid du Colombier {
1269a747e4fSDavid du Colombier 	Rune rune, r;
127*53ff6c4dSDavid du Colombier 	int i, n, w, p;
128*53ff6c4dSDavid du Colombier 	ulong fl;
129*53ff6c4dSDavid du Colombier 	void *ret;
130*53ff6c4dSDavid du Colombier 
131*53ff6c4dSDavid du Colombier 	w = f->width;
132*53ff6c4dSDavid du Colombier 	p = f->prec;
133*53ff6c4dSDavid du Colombier 	fl = f->flags;
1349a747e4fSDavid du Colombier 
1359a747e4fSDavid du Colombier 	f->flags = 0;
1369a747e4fSDavid du Colombier 	f->width = f->prec = 0;
1379a747e4fSDavid du Colombier 
1389a747e4fSDavid du Colombier 	for(;;){
1399a747e4fSDavid du Colombier 		if(isrunes){
1409a747e4fSDavid du Colombier 			r = *(Rune*)fmt;
1419a747e4fSDavid du Colombier 			fmt = (Rune*)fmt + 1;
1429a747e4fSDavid du Colombier 		}else{
1439a747e4fSDavid du Colombier 			fmt = (char*)fmt + chartorune(&rune, fmt);
1449a747e4fSDavid du Colombier 			r = rune;
1459a747e4fSDavid du Colombier 		}
1469a747e4fSDavid du Colombier 		f->r = r;
1479a747e4fSDavid du Colombier 		switch(r){
1489a747e4fSDavid du Colombier 		case '\0':
149*53ff6c4dSDavid du Colombier 			ret = nil;
150*53ff6c4dSDavid du Colombier 			goto end;
1519a747e4fSDavid du Colombier 		case '.':
1529a747e4fSDavid du Colombier 			f->flags |= FmtWidth|FmtPrec;
1539a747e4fSDavid du Colombier 			continue;
1549a747e4fSDavid du Colombier 		case '0':
1559a747e4fSDavid du Colombier 			if(!(f->flags & FmtWidth)){
1569a747e4fSDavid du Colombier 				f->flags |= FmtZero;
1579a747e4fSDavid du Colombier 				continue;
1589a747e4fSDavid du Colombier 			}
1599a747e4fSDavid du Colombier 			/* fall through */
1609a747e4fSDavid du Colombier 		case '1': case '2': case '3': case '4':
1619a747e4fSDavid du Colombier 		case '5': case '6': case '7': case '8': case '9':
1629a747e4fSDavid du Colombier 			i = 0;
1639a747e4fSDavid du Colombier 			while(r >= '0' && r <= '9'){
1649a747e4fSDavid du Colombier 				i = i * 10 + r - '0';
1659a747e4fSDavid du Colombier 				if(isrunes){
1669a747e4fSDavid du Colombier 					r = *(Rune*)fmt;
1679a747e4fSDavid du Colombier 					fmt = (Rune*)fmt + 1;
1689a747e4fSDavid du Colombier 				}else{
1699a747e4fSDavid du Colombier 					r = *(char*)fmt;
1709a747e4fSDavid du Colombier 					fmt = (char*)fmt + 1;
1719a747e4fSDavid du Colombier 				}
1729a747e4fSDavid du Colombier 			}
1739a747e4fSDavid du Colombier 			if(isrunes)
1749a747e4fSDavid du Colombier 				fmt = (Rune*)fmt - 1;
1759a747e4fSDavid du Colombier 			else
1769a747e4fSDavid du Colombier 				fmt = (char*)fmt - 1;
1779a747e4fSDavid du Colombier 		numflag:
1789a747e4fSDavid du Colombier 			if(f->flags & FmtWidth){
1799a747e4fSDavid du Colombier 				f->flags |= FmtPrec;
1809a747e4fSDavid du Colombier 				f->prec = i;
1819a747e4fSDavid du Colombier 			}else{
1829a747e4fSDavid du Colombier 				f->flags |= FmtWidth;
1839a747e4fSDavid du Colombier 				f->width = i;
1849a747e4fSDavid du Colombier 			}
1859a747e4fSDavid du Colombier 			continue;
1869a747e4fSDavid du Colombier 		case '*':
1879a747e4fSDavid du Colombier 			i = va_arg(f->args, int);
1889a747e4fSDavid du Colombier 			if(i < 0){
189a0ef1dc2SDavid du Colombier 				/*
190a0ef1dc2SDavid du Colombier 				 * negative precision =>
191a0ef1dc2SDavid du Colombier 				 * ignore the precision.
192a0ef1dc2SDavid du Colombier 				 */
193a0ef1dc2SDavid du Colombier 				if(f->flags & FmtPrec){
194a0ef1dc2SDavid du Colombier 					f->flags &= ~FmtPrec;
195a0ef1dc2SDavid du Colombier 					f->prec = 0;
196a0ef1dc2SDavid du Colombier 					continue;
197a0ef1dc2SDavid du Colombier 				}
1989a747e4fSDavid du Colombier 				i = -i;
1999a747e4fSDavid du Colombier 				f->flags |= FmtLeft;
2009a747e4fSDavid du Colombier 			}
2019a747e4fSDavid du Colombier 			goto numflag;
2029a747e4fSDavid du Colombier 		}
2039a747e4fSDavid du Colombier 		n = (*fmtfmt(r))(f);
204*53ff6c4dSDavid du Colombier 		if(n < 0){
205*53ff6c4dSDavid du Colombier 			ret = nil;
206*53ff6c4dSDavid du Colombier 			break;
2079a747e4fSDavid du Colombier 		}
208*53ff6c4dSDavid du Colombier 		if(n == 0){
209*53ff6c4dSDavid du Colombier 			ret = fmt;
210*53ff6c4dSDavid du Colombier 			break;
211*53ff6c4dSDavid du Colombier 		}
212*53ff6c4dSDavid du Colombier 	}
213*53ff6c4dSDavid du Colombier end:
214*53ff6c4dSDavid du Colombier 	f->width = w;
215*53ff6c4dSDavid du Colombier 	f->prec = p;
216*53ff6c4dSDavid du Colombier 	f->flags = fl;
217*53ff6c4dSDavid du Colombier 	return ret;
2189a747e4fSDavid du Colombier }
219