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