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