xref: /plan9-contrib/sys/src/ape/lib/fmt/fmt.c (revision 53ff6c4dddeb19bd84c918cdd2542d7c51263329)
140ef9009SDavid du Colombier /*
240ef9009SDavid du Colombier  * The authors of this software are Rob Pike and Ken Thompson.
340ef9009SDavid du Colombier  *              Copyright (c) 2002 by Lucent Technologies.
440ef9009SDavid du Colombier  * Permission to use, copy, modify, and distribute this software for any
540ef9009SDavid du Colombier  * purpose without fee is hereby granted, provided that this entire notice
640ef9009SDavid du Colombier  * is included in all copies of any software which is or includes a copy
740ef9009SDavid du Colombier  * or modification of this software and in all copies of the supporting
840ef9009SDavid du Colombier  * documentation for such software.
940ef9009SDavid du Colombier  * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
1040ef9009SDavid du Colombier  * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
1140ef9009SDavid du Colombier  * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
1240ef9009SDavid du Colombier  * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
1340ef9009SDavid du Colombier  */
1440ef9009SDavid du Colombier #include <stdarg.h>
1540ef9009SDavid du Colombier #include <string.h>
1640ef9009SDavid du Colombier #include "utf.h"
1740ef9009SDavid du Colombier #include "fmt.h"
1840ef9009SDavid du Colombier #include "fmtdef.h"
1940ef9009SDavid du Colombier 
2040ef9009SDavid du Colombier enum
2140ef9009SDavid du Colombier {
2240ef9009SDavid du Colombier 	Maxfmt = 64
2340ef9009SDavid du Colombier };
2440ef9009SDavid du Colombier 
2540ef9009SDavid du Colombier typedef struct Convfmt Convfmt;
2640ef9009SDavid du Colombier struct Convfmt
2740ef9009SDavid du Colombier {
2840ef9009SDavid du Colombier 	int	c;
2940ef9009SDavid du Colombier 	volatile	Fmts	fmt;	/* for spin lock in fmtfmt; avoids race due to write order */
3040ef9009SDavid du Colombier };
3140ef9009SDavid du Colombier 
3240ef9009SDavid du Colombier struct
3340ef9009SDavid du Colombier {
3440ef9009SDavid du Colombier 	/* lock by calling __fmtlock, __fmtunlock */
3540ef9009SDavid du Colombier 	int	nfmt;
3640ef9009SDavid du Colombier 	Convfmt	fmt[Maxfmt];
3740ef9009SDavid du Colombier } fmtalloc;
3840ef9009SDavid du Colombier 
3940ef9009SDavid du Colombier static Convfmt knownfmt[] = {
4040ef9009SDavid du Colombier 	' ',	__flagfmt,
4140ef9009SDavid du Colombier 	'#',	__flagfmt,
4240ef9009SDavid du Colombier 	'%',	__percentfmt,
4340ef9009SDavid du Colombier 	'+',	__flagfmt,
4440ef9009SDavid du Colombier 	',',	__flagfmt,
4540ef9009SDavid du Colombier 	'-',	__flagfmt,
4640ef9009SDavid du Colombier 	'C',	__runefmt,	/* Plan 9 addition */
4740ef9009SDavid du Colombier 	'E',	__efgfmt,
4840ef9009SDavid du Colombier 	'F',	__efgfmt,	/* ANSI only */
4940ef9009SDavid du Colombier 	'G',	__efgfmt,
5040ef9009SDavid du Colombier 	'L',	__flagfmt,	/* ANSI only */
5140ef9009SDavid du Colombier 	'S',	__runesfmt,	/* Plan 9 addition */
5240ef9009SDavid du Colombier 	'X',	__ifmt,
5340ef9009SDavid du Colombier 	'b',	__ifmt,		/* Plan 9 addition */
5440ef9009SDavid du Colombier 	'c',	__charfmt,
5540ef9009SDavid du Colombier 	'd',	__ifmt,
5640ef9009SDavid du Colombier 	'e',	__efgfmt,
5740ef9009SDavid du Colombier 	'f',	__efgfmt,
5840ef9009SDavid du Colombier 	'g',	__efgfmt,
5940ef9009SDavid du Colombier 	'h',	__flagfmt,
6040ef9009SDavid du Colombier 	'i',	__ifmt,		/* ANSI only */
6140ef9009SDavid du Colombier 	'l',	__flagfmt,
6240ef9009SDavid du Colombier 	'n',	__countfmt,
6340ef9009SDavid du Colombier 	'o',	__ifmt,
6440ef9009SDavid du Colombier 	'p',	__ifmt,
6540ef9009SDavid du Colombier 	'r',	__errfmt,
6640ef9009SDavid du Colombier 	's',	__strfmt,
6740ef9009SDavid du Colombier 	'u',	__flagfmt,	/* in Unix, __ifmt */
6840ef9009SDavid du Colombier 	'x',	__ifmt,
6940ef9009SDavid du Colombier 	0,	nil,
7040ef9009SDavid du Colombier };
7140ef9009SDavid du Colombier 
7240ef9009SDavid du Colombier 
7340ef9009SDavid du Colombier int	(*fmtdoquote)(int);
7440ef9009SDavid du Colombier 
7540ef9009SDavid du Colombier /*
7640ef9009SDavid du Colombier  * __fmtlock() must be set
7740ef9009SDavid du Colombier  */
7840ef9009SDavid du Colombier static int
__fmtinstall(int c,Fmts f)7940ef9009SDavid du Colombier __fmtinstall(int c, Fmts f)
8040ef9009SDavid du Colombier {
8140ef9009SDavid du Colombier 	Convfmt *p, *ep;
8240ef9009SDavid du Colombier 
8340ef9009SDavid du Colombier 	if(c<=0 || c>=65536)
8440ef9009SDavid du Colombier 		return -1;
8540ef9009SDavid du Colombier 	if(!f)
8640ef9009SDavid du Colombier 		f = __badfmt;
8740ef9009SDavid du Colombier 
8840ef9009SDavid du Colombier 	ep = &fmtalloc.fmt[fmtalloc.nfmt];
8940ef9009SDavid du Colombier 	for(p=fmtalloc.fmt; p<ep; p++)
9040ef9009SDavid du Colombier 		if(p->c == c)
9140ef9009SDavid du Colombier 			break;
9240ef9009SDavid du Colombier 
9340ef9009SDavid du Colombier 	if(p == &fmtalloc.fmt[Maxfmt])
9440ef9009SDavid du Colombier 		return -1;
9540ef9009SDavid du Colombier 
9640ef9009SDavid du Colombier 	p->fmt = f;
9740ef9009SDavid du Colombier 	if(p == ep){	/* installing a new format character */
9840ef9009SDavid du Colombier 		fmtalloc.nfmt++;
9940ef9009SDavid du Colombier 		p->c = c;
10040ef9009SDavid du Colombier 	}
10140ef9009SDavid du Colombier 
10240ef9009SDavid du Colombier 	return 0;
10340ef9009SDavid du Colombier }
10440ef9009SDavid du Colombier 
10540ef9009SDavid du Colombier int
fmtinstall(int c,Fmts f)10640ef9009SDavid du Colombier fmtinstall(int c, Fmts f)
10740ef9009SDavid du Colombier {
10840ef9009SDavid du Colombier 	int ret;
10940ef9009SDavid du Colombier 
11040ef9009SDavid du Colombier 	__fmtlock();
11140ef9009SDavid du Colombier 	ret = __fmtinstall(c, f);
11240ef9009SDavid du Colombier 	__fmtunlock();
11340ef9009SDavid du Colombier 	return ret;
11440ef9009SDavid du Colombier }
11540ef9009SDavid du Colombier 
11640ef9009SDavid du Colombier static Fmts
fmtfmt(int c)11740ef9009SDavid du Colombier fmtfmt(int c)
11840ef9009SDavid du Colombier {
11940ef9009SDavid du Colombier 	Convfmt *p, *ep;
12040ef9009SDavid du Colombier 
12140ef9009SDavid du Colombier 	ep = &fmtalloc.fmt[fmtalloc.nfmt];
12240ef9009SDavid du Colombier 	for(p=fmtalloc.fmt; p<ep; p++)
12340ef9009SDavid du Colombier 		if(p->c == c){
12440ef9009SDavid du Colombier 			while(p->fmt == nil)	/* loop until value is updated */
12540ef9009SDavid du Colombier 				;
12640ef9009SDavid du Colombier 			return p->fmt;
12740ef9009SDavid du Colombier 		}
12840ef9009SDavid du Colombier 
12940ef9009SDavid du Colombier 	/* is this a predefined format char? */
13040ef9009SDavid du Colombier 	__fmtlock();
13140ef9009SDavid du Colombier 	for(p=knownfmt; p->c; p++)
13240ef9009SDavid du Colombier 		if(p->c == c){
13340ef9009SDavid du Colombier 			__fmtinstall(p->c, p->fmt);
13440ef9009SDavid du Colombier 			__fmtunlock();
13540ef9009SDavid du Colombier 			return p->fmt;
13640ef9009SDavid du Colombier 		}
13740ef9009SDavid du Colombier 	__fmtunlock();
13840ef9009SDavid du Colombier 
13940ef9009SDavid du Colombier 	return __badfmt;
14040ef9009SDavid du Colombier }
14140ef9009SDavid du Colombier 
14240ef9009SDavid du Colombier void*
__fmtdispatch(Fmt * f,void * fmt,int isrunes)14340ef9009SDavid du Colombier __fmtdispatch(Fmt *f, void *fmt, int isrunes)
14440ef9009SDavid du Colombier {
14540ef9009SDavid du Colombier 	Rune rune, r;
146*53ff6c4dSDavid du Colombier 	int i, n, w, p;
147*53ff6c4dSDavid du Colombier 	ulong fl;
148*53ff6c4dSDavid du Colombier 	void *ret;
149*53ff6c4dSDavid du Colombier 
150*53ff6c4dSDavid du Colombier 	w = f->width;
151*53ff6c4dSDavid du Colombier 	p = f->prec;
152*53ff6c4dSDavid du Colombier 	fl = f->flags;
15340ef9009SDavid du Colombier 
15440ef9009SDavid du Colombier 	f->flags = 0;
15540ef9009SDavid du Colombier 	f->width = f->prec = 0;
15640ef9009SDavid du Colombier 
15740ef9009SDavid du Colombier 	for(;;){
15840ef9009SDavid du Colombier 		if(isrunes){
15940ef9009SDavid du Colombier 			r = *(Rune*)fmt;
16040ef9009SDavid du Colombier 			fmt = (Rune*)fmt + 1;
16140ef9009SDavid du Colombier 		}else{
16240ef9009SDavid du Colombier 			fmt = (char*)fmt + chartorune(&rune, (char*)fmt);
16340ef9009SDavid du Colombier 			r = rune;
16440ef9009SDavid du Colombier 		}
16540ef9009SDavid du Colombier 		f->r = r;
16640ef9009SDavid du Colombier 		switch(r){
16740ef9009SDavid du Colombier 		case '\0':
168*53ff6c4dSDavid du Colombier 			ret = nil;
169*53ff6c4dSDavid du Colombier 			goto end;
17040ef9009SDavid du Colombier 		case '.':
17140ef9009SDavid du Colombier 			f->flags |= FmtWidth|FmtPrec;
17240ef9009SDavid du Colombier 			continue;
17340ef9009SDavid du Colombier 		case '0':
17440ef9009SDavid du Colombier 			if(!(f->flags & FmtWidth)){
17540ef9009SDavid du Colombier 				f->flags |= FmtZero;
17640ef9009SDavid du Colombier 				continue;
17740ef9009SDavid du Colombier 			}
17840ef9009SDavid du Colombier 			/* fall through */
17940ef9009SDavid du Colombier 		case '1': case '2': case '3': case '4':
18040ef9009SDavid du Colombier 		case '5': case '6': case '7': case '8': case '9':
18140ef9009SDavid du Colombier 			i = 0;
18240ef9009SDavid du Colombier 			while(r >= '0' && r <= '9'){
18340ef9009SDavid du Colombier 				i = i * 10 + r - '0';
18440ef9009SDavid du Colombier 				if(isrunes){
18540ef9009SDavid du Colombier 					r = *(Rune*)fmt;
18640ef9009SDavid du Colombier 					fmt = (Rune*)fmt + 1;
18740ef9009SDavid du Colombier 				}else{
18840ef9009SDavid du Colombier 					r = *(char*)fmt;
18940ef9009SDavid du Colombier 					fmt = (char*)fmt + 1;
19040ef9009SDavid du Colombier 				}
19140ef9009SDavid du Colombier 			}
19240ef9009SDavid du Colombier 			if(isrunes)
19340ef9009SDavid du Colombier 				fmt = (Rune*)fmt - 1;
19440ef9009SDavid du Colombier 			else
19540ef9009SDavid du Colombier 				fmt = (char*)fmt - 1;
19640ef9009SDavid du Colombier 		numflag:
19740ef9009SDavid du Colombier 			if(f->flags & FmtWidth){
19840ef9009SDavid du Colombier 				f->flags |= FmtPrec;
19940ef9009SDavid du Colombier 				f->prec = i;
20040ef9009SDavid du Colombier 			}else{
20140ef9009SDavid du Colombier 				f->flags |= FmtWidth;
20240ef9009SDavid du Colombier 				f->width = i;
20340ef9009SDavid du Colombier 			}
20440ef9009SDavid du Colombier 			continue;
20540ef9009SDavid du Colombier 		case '*':
20640ef9009SDavid du Colombier 			i = va_arg(f->args, int);
20740ef9009SDavid du Colombier 			if(i < 0){
20840ef9009SDavid du Colombier 				/*
20940ef9009SDavid du Colombier 				 * negative precision =>
21040ef9009SDavid du Colombier 				 * ignore the precision.
21140ef9009SDavid du Colombier 				 */
21240ef9009SDavid du Colombier 				if(f->flags & FmtPrec){
21340ef9009SDavid du Colombier 					f->flags &= ~FmtPrec;
21440ef9009SDavid du Colombier 					f->prec = 0;
21540ef9009SDavid du Colombier 					continue;
21640ef9009SDavid du Colombier 				}
21740ef9009SDavid du Colombier 				i = -i;
21840ef9009SDavid du Colombier 				f->flags |= FmtLeft;
21940ef9009SDavid du Colombier 			}
22040ef9009SDavid du Colombier 			goto numflag;
22140ef9009SDavid du Colombier 		}
22240ef9009SDavid du Colombier 		n = (*fmtfmt(r))(f);
223*53ff6c4dSDavid du Colombier 		if(n < 0){
224*53ff6c4dSDavid du Colombier 			ret = nil;
225*53ff6c4dSDavid du Colombier 			break;
22640ef9009SDavid du Colombier 		}
227*53ff6c4dSDavid du Colombier 		if(n == 0){
228*53ff6c4dSDavid du Colombier 			ret = fmt;
229*53ff6c4dSDavid du Colombier 			break;
230*53ff6c4dSDavid du Colombier 		}
231*53ff6c4dSDavid du Colombier 	}
232*53ff6c4dSDavid du Colombier end:
233*53ff6c4dSDavid du Colombier 	f->width = w;
234*53ff6c4dSDavid du Colombier 	f->prec = p;
235*53ff6c4dSDavid du Colombier 	f->flags = fl;
236*53ff6c4dSDavid du Colombier 	return ret;
23740ef9009SDavid du Colombier }
238