xref: /inferno-os/lib9/fmt.c (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
1 /*
2  * The authors of this software are Rob Pike and Ken Thompson.
3  *              Copyright (c) 2002 by Lucent Technologies.
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose without fee is hereby granted, provided that this entire notice
6  * is included in all copies of any software which is or includes a copy
7  * or modification of this software and in all copies of the supporting
8  * documentation for such software.
9  * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
10  * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
11  * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
12  * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
13  */
14 #include "lib9.h"
15 #include "fmtdef.h"
16 
17 enum
18 {
19 	Maxfmt = 64
20 };
21 
22 typedef struct Convfmt Convfmt;
23 struct Convfmt
24 {
25 	int	c;
26 	volatile	Fmts	fmt;	/* for spin lock in fmtfmt; avoids race due to write order */
27 };
28 
29 struct
30 {
31 	/* lock by calling _fmtlock, _fmtunlock */
32 	int	nfmt;
33 	Convfmt	fmt[Maxfmt];
34 } fmtalloc;
35 
36 static Convfmt knownfmt[] = {
37 	' ',	_flagfmt,
38 	'#',	_flagfmt,
39 	'%',	_percentfmt,
40 	'+',	_flagfmt,
41 	',',	_flagfmt,
42 	'-',	_flagfmt,
43 	'C',	_runefmt,
44 	'S',	_runesfmt,
45 	'X',	_ifmt,
46 	'b',	_ifmt,
47 	'c',	_charfmt,
48 	'd',	_ifmt,
49 	'h',	_flagfmt,
50 	'l',	_flagfmt,
51 	'n',	_countfmt,
52 	'o',	_ifmt,
53 	'p',	_ifmt,
54 	'r',	errfmt,
55 	's',	_strfmt,
56 	'u',	_flagfmt,
57 	'x',	_ifmt,
58 	0,	nil,
59 };
60 
61 int	(*doquote)(int);
62 
63 static Fmts
64 fmtfmt(int c)
65 {
66 	Convfmt *p, *ep;
67 
68 	ep = &fmtalloc.fmt[fmtalloc.nfmt];
69 	for(p=fmtalloc.fmt; p<ep; p++)
70 		if(p->c == c)
71 			return p->fmt;
72 
73 	/* is this a predefined format char? */
74 	for(p=knownfmt; p->c; p++)
75 		if(p->c == c){
76 			/* no need to lock; fmtinstall is idempotent */
77 			fmtinstall(p->c, p->fmt);
78 			while(p->fmt == nil)	/* loop until value is updated */
79 				;
80 			return p->fmt;
81 		}
82 
83 	return _badfmt;
84 }
85 
86 int
87 fmtinstall(int c, Fmts f)
88 {
89 	Convfmt *p, *ep;
90 
91 	if(c<=0 || c>=65536)
92 		return -1;
93 	if(!f)
94 		f = _badfmt;
95 
96 	_fmtlock();
97 
98 	ep = &fmtalloc.fmt[fmtalloc.nfmt];
99 	for(p=fmtalloc.fmt; p<ep; p++)
100 		if(p->c == c)
101 			break;
102 
103 	if(p == &fmtalloc.fmt[Maxfmt]){
104 		_fmtunlock();
105 		return -1;
106 	}
107 
108 	p->fmt = f;
109 	if(p == ep){	/* installing a new format character */
110 		fmtalloc.nfmt++;
111 		p->c = c;
112 	}
113 
114 	_fmtunlock();
115 	return 0;
116 }
117 
118 void*
119 _fmtdispatch(Fmt *f, void *fmt, int isrunes)
120 {
121 	Rune rune, r;
122 	int i, n;
123 
124 	f->flags = 0;
125 	f->width = f->prec = 0;
126 
127 	for(;;){
128 		if(isrunes){
129 			r = *(Rune*)fmt;
130 			fmt = (Rune*)fmt + 1;
131 		}else{
132 			fmt = (char*)fmt + chartorune(&rune, fmt);
133 			r = rune;
134 		}
135 		f->r = r;
136 		switch(r){
137 		case '\0':
138 			return nil;
139 		case '.':
140 			f->flags |= FmtWidth|FmtPrec;
141 			continue;
142 		case '0':
143 			if(!(f->flags & FmtWidth)){
144 				f->flags |= FmtZero;
145 				continue;
146 			}
147 			/* fall through */
148 		case '1': case '2': case '3': case '4':
149 		case '5': case '6': case '7': case '8': case '9':
150 			i = 0;
151 			while(r >= '0' && r <= '9'){
152 				i = i * 10 + r - '0';
153 				if(isrunes){
154 					r = *(Rune*)fmt;
155 					fmt = (Rune*)fmt + 1;
156 				}else{
157 					r = *(char*)fmt;
158 					fmt = (char*)fmt + 1;
159 				}
160 			}
161 			if(isrunes)
162 				fmt = (Rune*)fmt - 1;
163 			else
164 				fmt = (char*)fmt - 1;
165 		numflag:
166 			if(f->flags & FmtWidth){
167 				f->flags |= FmtPrec;
168 				f->prec = i;
169 			}else{
170 				f->flags |= FmtWidth;
171 				f->width = i;
172 			}
173 			continue;
174 		case '*':
175 			i = va_arg(f->args, int);
176 			if(i < 0){
177 				i = -i;
178 				f->flags |= FmtLeft;
179 			}
180 			goto numflag;
181 		}
182 		n = (*fmtfmt(r))(f);
183 		if(n < 0)
184 			return nil;
185 		if(n == 0)
186 			return fmt;
187 	}
188 }
189