xref: /plan9-contrib/sys/src/libc/fmt/fmt.c (revision 56fcb4db5c720201392d2b20e8adbaa6752c58a7)
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 {
13*56fcb4dbSDavid du Colombier 	Rune	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(Rune c,Fmts f)60*56fcb4dbSDavid du Colombier _fmtinstall(Rune 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(Rune c)98*56fcb4dbSDavid du Colombier fmtfmt(Rune c)
993ff48bf5SDavid du Colombier {
1003ff48bf5SDavid du Colombier 	Convfmt *p, *ep;
1013ff48bf5SDavid du Colombier 
102*56fcb4dbSDavid du Colombier 	if (c == '\0')
103*56fcb4dbSDavid du Colombier 		return _badfmt;
104*56fcb4dbSDavid du Colombier 
1053ff48bf5SDavid du Colombier 	ep = &fmtalloc.fmt[fmtalloc.nfmt];
1063ff48bf5SDavid du Colombier 	for(p=fmtalloc.fmt; p<ep; p++)
1073ff48bf5SDavid du Colombier 		if(p->c == c){
1083ff48bf5SDavid du Colombier 			while(p->fmt == nil)	/* loop until value is updated */
1093ff48bf5SDavid du Colombier 				;
1103ff48bf5SDavid du Colombier 			return p->fmt;
1113ff48bf5SDavid du Colombier 		}
1123ff48bf5SDavid du Colombier 
1133ff48bf5SDavid du Colombier 	/* is this a predefined format char? */
1143ff48bf5SDavid du Colombier 	_fmtlock();
1153ff48bf5SDavid du Colombier 	for(p=knownfmt; p->c; p++)
1163ff48bf5SDavid du Colombier 		if(p->c == c){
1173ff48bf5SDavid du Colombier 			_fmtinstall(p->c, p->fmt);
1183ff48bf5SDavid du Colombier 			_fmtunlock();
1193ff48bf5SDavid du Colombier 			return p->fmt;
1203ff48bf5SDavid du Colombier 		}
1213ff48bf5SDavid du Colombier 	_fmtunlock();
1223ff48bf5SDavid du Colombier 
1233ff48bf5SDavid du Colombier 	return _badfmt;
1243ff48bf5SDavid du Colombier }
1253ff48bf5SDavid du Colombier 
1269a747e4fSDavid du Colombier void*
_fmtdispatch(Fmt * f,void * fmt,int isrunes)1279a747e4fSDavid du Colombier _fmtdispatch(Fmt *f, void *fmt, int isrunes)
1289a747e4fSDavid du Colombier {
1299a747e4fSDavid du Colombier 	Rune rune, r;
13053ff6c4dSDavid du Colombier 	int i, n, w, p;
13153ff6c4dSDavid du Colombier 	ulong fl;
13253ff6c4dSDavid du Colombier 	void *ret;
13353ff6c4dSDavid du Colombier 
13453ff6c4dSDavid du Colombier 	w = f->width;
13553ff6c4dSDavid du Colombier 	p = f->prec;
13653ff6c4dSDavid du Colombier 	fl = f->flags;
1379a747e4fSDavid du Colombier 
1389a747e4fSDavid du Colombier 	f->flags = 0;
1399a747e4fSDavid du Colombier 	f->width = f->prec = 0;
1409a747e4fSDavid du Colombier 
1419a747e4fSDavid du Colombier 	for(;;){
1429a747e4fSDavid du Colombier 		if(isrunes){
1439a747e4fSDavid du Colombier 			r = *(Rune*)fmt;
1449a747e4fSDavid du Colombier 			fmt = (Rune*)fmt + 1;
1459a747e4fSDavid du Colombier 		}else{
1469a747e4fSDavid du Colombier 			fmt = (char*)fmt + chartorune(&rune, fmt);
1479a747e4fSDavid du Colombier 			r = rune;
1489a747e4fSDavid du Colombier 		}
1499a747e4fSDavid du Colombier 		f->r = r;
1509a747e4fSDavid du Colombier 		switch(r){
1519a747e4fSDavid du Colombier 		case '\0':
15253ff6c4dSDavid du Colombier 			ret = nil;
15353ff6c4dSDavid du Colombier 			goto end;
1549a747e4fSDavid du Colombier 		case '.':
1559a747e4fSDavid du Colombier 			f->flags |= FmtWidth|FmtPrec;
1569a747e4fSDavid du Colombier 			continue;
1579a747e4fSDavid du Colombier 		case '0':
1589a747e4fSDavid du Colombier 			if(!(f->flags & FmtWidth)){
1599a747e4fSDavid du Colombier 				f->flags |= FmtZero;
1609a747e4fSDavid du Colombier 				continue;
1619a747e4fSDavid du Colombier 			}
1629a747e4fSDavid du Colombier 			/* fall through */
1639a747e4fSDavid du Colombier 		case '1': case '2': case '3': case '4':
1649a747e4fSDavid du Colombier 		case '5': case '6': case '7': case '8': case '9':
1659a747e4fSDavid du Colombier 			i = 0;
1669a747e4fSDavid du Colombier 			while(r >= '0' && r <= '9'){
1679a747e4fSDavid du Colombier 				i = i * 10 + r - '0';
1689a747e4fSDavid du Colombier 				if(isrunes){
1699a747e4fSDavid du Colombier 					r = *(Rune*)fmt;
1709a747e4fSDavid du Colombier 					fmt = (Rune*)fmt + 1;
1719a747e4fSDavid du Colombier 				}else{
1729a747e4fSDavid du Colombier 					r = *(char*)fmt;
1739a747e4fSDavid du Colombier 					fmt = (char*)fmt + 1;
1749a747e4fSDavid du Colombier 				}
1759a747e4fSDavid du Colombier 			}
1769a747e4fSDavid du Colombier 			if(isrunes)
1779a747e4fSDavid du Colombier 				fmt = (Rune*)fmt - 1;
1789a747e4fSDavid du Colombier 			else
1799a747e4fSDavid du Colombier 				fmt = (char*)fmt - 1;
1809a747e4fSDavid du Colombier 		numflag:
1819a747e4fSDavid du Colombier 			if(f->flags & FmtWidth){
1829a747e4fSDavid du Colombier 				f->flags |= FmtPrec;
1839a747e4fSDavid du Colombier 				f->prec = i;
1849a747e4fSDavid du Colombier 			}else{
1859a747e4fSDavid du Colombier 				f->flags |= FmtWidth;
1869a747e4fSDavid du Colombier 				f->width = i;
1879a747e4fSDavid du Colombier 			}
1889a747e4fSDavid du Colombier 			continue;
1899a747e4fSDavid du Colombier 		case '*':
1909a747e4fSDavid du Colombier 			i = va_arg(f->args, int);
1919a747e4fSDavid du Colombier 			if(i < 0){
192a0ef1dc2SDavid du Colombier 				/*
193a0ef1dc2SDavid du Colombier 				 * negative precision =>
194a0ef1dc2SDavid du Colombier 				 * ignore the precision.
195a0ef1dc2SDavid du Colombier 				 */
196a0ef1dc2SDavid du Colombier 				if(f->flags & FmtPrec){
197a0ef1dc2SDavid du Colombier 					f->flags &= ~FmtPrec;
198a0ef1dc2SDavid du Colombier 					f->prec = 0;
199a0ef1dc2SDavid du Colombier 					continue;
200a0ef1dc2SDavid du Colombier 				}
2019a747e4fSDavid du Colombier 				i = -i;
2029a747e4fSDavid du Colombier 				f->flags |= FmtLeft;
2039a747e4fSDavid du Colombier 			}
2049a747e4fSDavid du Colombier 			goto numflag;
2059a747e4fSDavid du Colombier 		}
2069a747e4fSDavid du Colombier 		n = (*fmtfmt(r))(f);
20753ff6c4dSDavid du Colombier 		if(n < 0){
20853ff6c4dSDavid du Colombier 			ret = nil;
20953ff6c4dSDavid du Colombier 			break;
2109a747e4fSDavid du Colombier 		}
21153ff6c4dSDavid du Colombier 		if(n == 0){
21253ff6c4dSDavid du Colombier 			ret = fmt;
21353ff6c4dSDavid du Colombier 			break;
21453ff6c4dSDavid du Colombier 		}
21553ff6c4dSDavid du Colombier 	}
21653ff6c4dSDavid du Colombier end:
21753ff6c4dSDavid du Colombier 	f->width = w;
21853ff6c4dSDavid du Colombier 	f->prec = p;
21953ff6c4dSDavid du Colombier 	f->flags = fl;
22053ff6c4dSDavid du Colombier 	return ret;
2219a747e4fSDavid du Colombier }
222