1 /* print.c -- formatted printing routines ($Revision: 1.1.1.1 $) */
2
3 #include "es.h"
4 #include "print.h"
5
6 #define MAXCONV 256
7
8 /*
9 * conversion functions
10 * true return -> flag changes only, not a conversion
11 */
12
13 #define Flag(name, flag) \
14 static Boolean name(Format *format) { \
15 format->flags |= flag; \
16 return TRUE; \
17 }
18
Flag(uconv,FMT_unsigned)19 Flag(uconv, FMT_unsigned)
20 Flag(hconv, FMT_short)
21 Flag(longconv, FMT_long)
22 Flag(altconv, FMT_altform)
23 Flag(leftconv, FMT_leftside)
24 Flag(dotconv, FMT_f2set)
25
26 static Boolean digitconv(Format *format) {
27 int c = format->invoker;
28 if (format->flags & FMT_f2set)
29 format->f2 = 10 * format->f2 + c - '0';
30 else {
31 format->flags |= FMT_f1set;
32 format->f1 = 10 * format->f1 + c - '0';
33 }
34 return TRUE;
35 }
36
zeroconv(Format * format)37 static Boolean zeroconv(Format *format) {
38 if (format->flags & (FMT_f1set | FMT_f2set))
39 return digitconv(format);
40 format->flags |= FMT_zeropad;
41 return TRUE;
42 }
43
pad(Format * format,long len,int c)44 static void pad(Format *format, long len, int c) {
45 while (len-- > 0)
46 fmtputc(format, c);
47 }
48
sconv(Format * format)49 static Boolean sconv(Format *format) {
50 char *s = va_arg(format->args, char *);
51 if ((format->flags & FMT_f1set) == 0)
52 fmtcat(format, s);
53 else {
54 size_t len = strlen(s), width = format->f1 - len;
55 if (format->flags & FMT_leftside) {
56 fmtappend(format, s, len);
57 pad(format, width, ' ');
58 } else {
59 pad(format, width, ' ');
60 fmtappend(format, s, len);
61 }
62 }
63 return FALSE;
64 }
65
utoa(unsigned long u,char * t,unsigned int radix,char * digit)66 static char *utoa(unsigned long u, char *t, unsigned int radix, char *digit) {
67 if (u >= radix) {
68 t = utoa(u / radix, t, radix, digit);
69 u %= radix;
70 }
71 *t++ = digit[u];
72 return t;
73 }
74
intconv(Format * format,unsigned int radix,int upper,char * altform)75 static void intconv(Format *format, unsigned int radix, int upper, char *altform) {
76 static char * table[] = {
77 "0123456789abcdefghijklmnopqrstuvwxyz",
78 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
79 };
80 char padchar;
81 size_t len, pre, zeroes, padding, width;
82 long n, flags;
83 unsigned long u;
84 char number[64], prefix[20];
85
86 if (radix > 36)
87 return;
88
89 flags = format->flags;
90 if (flags & FMT_long)
91 n = va_arg(format->args, long);
92 else if (flags & FMT_short)
93 n = va_arg(format->args, short);
94 else
95 n = va_arg(format->args, int);
96
97 pre = 0;
98 if ((flags & FMT_unsigned) || n >= 0)
99 u = n;
100 else {
101 prefix[pre++] = '-';
102 u = -n;
103 }
104
105 if (flags & FMT_altform)
106 while (*altform != '\0')
107 prefix[pre++] = *altform++;
108
109 len = utoa(u, number, radix, table[upper]) - number;
110 if ((flags & FMT_f2set) && (size_t) format->f2 > len)
111 zeroes = format->f2 - len;
112 else
113 zeroes = 0;
114
115 width = pre + zeroes + len;
116 if ((flags & FMT_f1set) && (size_t) format->f1 > width) {
117 padding = format->f1 - width;
118 } else
119 padding = 0;
120
121 padchar = ' ';
122 if (padding > 0 && flags & FMT_zeropad) {
123 padchar = '0';
124 if ((flags & FMT_leftside) == 0) {
125 zeroes += padding;
126 padding = 0;
127 }
128 }
129
130 if ((flags & FMT_leftside) == 0)
131 pad(format, padding, padchar);
132 fmtappend(format, prefix, pre);
133 pad(format, zeroes, '0');
134 fmtappend(format, number, len);
135 if (flags & FMT_leftside)
136 pad(format, padding, padchar);
137 }
138
cconv(Format * format)139 static Boolean cconv(Format *format) {
140 fmtputc(format, va_arg(format->args, int));
141 return FALSE;
142 }
143
dconv(Format * format)144 static Boolean dconv(Format *format) {
145 intconv(format, 10, 0, "");
146 return FALSE;
147 }
148
oconv(Format * format)149 static Boolean oconv(Format *format) {
150 intconv(format, 8, 0, "0");
151 return FALSE;
152 }
153
xconv(Format * format)154 static Boolean xconv(Format *format) {
155 intconv(format, 16, 0, "0x");
156 return FALSE;
157 }
158
pctconv(Format * format)159 static Boolean pctconv(Format *format) {
160 fmtputc(format, '%');
161 return FALSE;
162 }
163
badconv(Format * format)164 static Boolean badconv(Format *format) {
165 panic("bad conversion character in printfmt: %%%c", format->invoker);
166 return FALSE; /* hush up gcc -Wall */
167 }
168
169
170 /*
171 * conversion table management
172 */
173
174 static Conv *fmttab;
175
inittab(void)176 static void inittab(void) {
177 int i;
178
179 fmttab = ealloc(MAXCONV * sizeof (Conv));
180 for (i = 0; i < MAXCONV; i++)
181 fmttab[i] = badconv;
182
183 fmttab['s'] = sconv;
184 fmttab['c'] = cconv;
185 fmttab['d'] = dconv;
186 fmttab['o'] = oconv;
187 fmttab['x'] = xconv;
188 fmttab['%'] = pctconv;
189
190 fmttab['u'] = uconv;
191 fmttab['h'] = hconv;
192 fmttab['l'] = longconv;
193 fmttab['#'] = altconv;
194 fmttab['-'] = leftconv;
195 fmttab['.'] = dotconv;
196
197 fmttab['0'] = zeroconv;
198 for (i = '1'; i <= '9'; i++)
199 fmttab[i] = digitconv;
200 }
201
fmtinstall(int c,Conv f)202 Conv fmtinstall(int c, Conv f) {
203 Conv oldf;
204 if (fmttab == NULL)
205 inittab();
206 c &= MAXCONV - 1;
207 oldf = fmttab[c];
208 if (f != NULL)
209 fmttab[c] = f;
210 return oldf;
211 }
212
213
214 /*
215 * functions for inserting strings in the format buffer
216 */
217
fmtappend(Format * format,const char * s,size_t len)218 extern void fmtappend(Format *format, const char *s, size_t len) {
219 while (format->buf + len > format->bufend) {
220 size_t split = format->bufend - format->buf;
221 memcpy(format->buf, s, split);
222 format->buf += split;
223 s += split;
224 len -= split;
225 (*format->grow)(format, len);
226 }
227 memcpy(format->buf, s, len);
228 format->buf += len;
229 }
230
fmtcat(Format * format,const char * s)231 extern void fmtcat(Format *format, const char *s) {
232 fmtappend(format, s, strlen(s));
233 }
234
235 /*
236 * printfmt -- the driver routine
237 */
238
printfmt(Format * format,const char * fmt)239 extern int printfmt(Format *format, const char *fmt) {
240 unsigned char *s = (unsigned char *) fmt;
241
242 if (fmttab[0] == NULL)
243 inittab();
244
245 for (;;) {
246 int c = *s++;
247 switch (c) {
248 case '%':
249 format->flags = format->f1 = format->f2 = 0;
250 do
251 format->invoker = c = *s++;
252 while ((*fmttab[c])(format));
253 break;
254 case '\0':
255 return format->buf - format->bufbegin + format->flushed;
256 default:
257 fmtputc(format, c);
258 break;
259 }
260 }
261 }
262
263
264 /*
265 * the public entry points
266 */
267
VARARGS2(Format *,format,const char *,fmt)268 extern int fmtprint VARARGS2(Format *, format, const char *, fmt) {
269 int n = -format->flushed;
270 #if NO_VA_LIST_ASSIGN
271 va_list saveargs;
272
273 memcpy(saveargs, format->args, sizeof(va_list));
274 #else
275 va_list saveargs = format->args;
276 #endif
277
278
279 VA_START(format->args, fmt);
280 n += printfmt(format, fmt);
281 va_end(format->args);
282 #if NO_VA_LIST_ASSIGN
283 memcpy(format->args, saveargs, sizeof(va_list));
284 #else
285 format->args = saveargs;
286 #endif
287
288 return n + format->flushed;
289 }
290
fprint_flush(Format * format,size_t more)291 static void fprint_flush(Format *format, size_t more) {
292 size_t n = format->buf - format->bufbegin;
293 char *buf = format->bufbegin;
294
295 format->flushed += n;
296 format->buf = format->bufbegin;
297 while (n != 0) {
298 int written = write(format->u.n, buf, n);
299 if (written == -1) {
300 if (format->u.n != 2)
301 uerror("write");
302 exit(1);
303 }
304 n -= written;
305 }
306 }
307
fdprint(Format * format,int fd,const char * fmt)308 static void fdprint(Format *format, int fd, const char *fmt) {
309 char buf[FPRINT_BUFSIZ];
310
311 format->buf = buf;
312 format->bufbegin = buf;
313 format->bufend = buf + sizeof buf;
314 format->grow = fprint_flush;
315 format->flushed = 0;
316 format->u.n = fdmap(fd);
317
318 gcdisable();
319 printfmt(format, fmt);
320 fprint_flush(format, 0);
321 gcenable();
322 }
323
VARARGS2(int,fd,const char *,fmt)324 extern int fprint VARARGS2(int, fd, const char *, fmt) {
325 Format format;
326 VA_START(format.args, fmt);
327 fdprint(&format, fd, fmt);
328 va_end(format.args);
329 return format.flushed;
330 }
331
VARARGS1(const char *,fmt)332 extern int print VARARGS1(const char *, fmt) {
333 Format format;
334 VA_START(format.args, fmt);
335 fdprint(&format, 1, fmt);
336 va_end(format.args);
337 return format.flushed;
338 }
339
VARARGS1(const char *,fmt)340 extern int eprint VARARGS1(const char *, fmt) {
341 Format format;
342 VA_START(format.args, fmt);
343 fdprint(&format, 2, fmt);
344 va_end(format.args);
345 return format.flushed;
346 }
347
VARARGS1(const char *,fmt)348 extern noreturn panic VARARGS1(const char *, fmt) {
349 Format format;
350 gcdisable();
351 VA_START(format.args, fmt);
352 eprint("es panic: ");
353 fdprint(&format, 2, fmt);
354 va_end(format.args);
355 eprint("\n");
356 exit(1);
357 }
358