xref: /plan9/sys/src/9/port/print.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 
4 enum
5 {
6 	SIZE	= 1024,
7 	IDIGIT	= 30,
8 	MAXCONV	= 40,
9 	FDIGIT	= 30,
10 	FDEFLT	= 6,
11 	NONE	= -1000,
12 	MAXFMT	= 512,
13 
14 	FPLUS	= (1<<0),
15 	FMINUS	= (1<<1),
16 	FSHARP	= (1<<2),
17 	FLONG	= (1<<3),
18 	FSHORT	= (1<<4),
19 	FUNSIGN	= (1<<5),
20 	FVLONG	= (1<<6),
21 };
22 
23 #define	PTR	sizeof(char*)
24 #define	SHORT	sizeof(int)
25 #define	INT	sizeof(int)
26 #define	LONG	sizeof(long)
27 #define	VLONG	sizeof(vlong)
28 
29 int	printcol;
30 
31 static	int	convcount;
32 static	char	fmtindex[MAXFMT];
33 
34 static	int	noconv(void*, Fconv*);
35 static	int	flags(void*, Fconv*);
36 
37 static	int	cconv(void*, Fconv*);
38 static	int	sconv(void*, Fconv*);
39 static	int	percent(void*, Fconv*);
40 
41 int	numbconv(void*, Fconv*);
42 
43 static
44 int	(*fmtconv[MAXCONV])(void*, Fconv*) =
45 {
46 	noconv
47 };
48 
49 static
50 void
51 initfmt(void)
52 {
53 	int cc;
54 
55 	cc = 0;
56 	fmtconv[cc] = noconv;
57 	cc++;
58 
59 	fmtconv[cc] = flags;
60 	fmtindex['+'] = cc;
61 	fmtindex['-'] = cc;
62 	fmtindex['#'] = cc;
63 	fmtindex['h'] = cc;
64 	fmtindex['l'] = cc;
65 	fmtindex['u'] = cc;
66 	cc++;
67 
68 	fmtconv[cc] = numbconv;
69 	fmtindex['d'] = cc;
70 	fmtindex['o'] = cc;
71 	fmtindex['x'] = cc;
72 	fmtindex['X'] = cc;
73 	cc++;
74 
75 	fmtconv[cc] = cconv;
76 	fmtindex['c'] = cc;
77 	fmtindex['C'] = cc;
78 	cc++;
79 
80 	fmtconv[cc] = sconv;
81 	fmtindex['s'] = cc;
82 	fmtindex['S'] = cc;
83 	cc++;
84 
85 	fmtconv[cc] = percent;
86 	fmtindex['%'] = cc;
87 	cc++;
88 
89 	convcount = cc;
90 }
91 
92 int
93 fmtinstall(int c, int (*f)(void*, Fconv*))
94 {
95 
96 	if(convcount == 0)
97 		initfmt();
98 	if(c < 0 || c >= MAXFMT)
99 		return 1;
100 	if(convcount >= MAXCONV)
101 		return 1;
102 	fmtconv[convcount] = f;
103 	fmtindex[c] = convcount;
104 	convcount++;
105 	return 0;
106 }
107 
108 char*
109 doprint(char *s, char *es, char *fmt, void *argp)
110 {
111 	int n, c;
112 	Rune rune;
113 	Fconv local;
114 
115 	local.out = s;
116 	local.eout = es-4;		/* room for multi-byte character and 0 */
117 
118 loop:
119 	c = *fmt & 0xff;
120 	if(c >= Runeself) {
121 		n = chartorune(&rune, fmt);
122 		fmt += n;
123 		c = rune;
124 	} else
125 		fmt++;
126 	switch(c) {
127 	case 0:
128 		*local.out = 0;
129 		return local.out;
130 
131 	default:
132 		printcol++;
133 		goto common;
134 
135 	case '\n':
136 		printcol = 0;
137 		goto common;
138 
139 	case '\t':
140 		printcol = (printcol+8) & ~7;
141 		goto common;
142 
143 	common:
144 		if(local.out < local.eout)
145 			if(c >= Runeself) {
146 				rune = c;
147 				n = runetochar(local.out, &rune);	/* BUG */
148 				local.out += n;
149 			} else
150 				*local.out++ = c;
151 		goto loop;
152 
153 	case '%':
154 		break;
155 	}
156 	local.f1 = NONE;
157 	local.f2 = NONE;
158 	local.f3 = 0;
159 
160 	/*
161 	 * read one of the following
162 	 *	1. number, => f1, f2 in order.
163 	 *	2. '*' same as number (from args)
164 	 *	3. '.' ignored (separates numbers)
165 	 *	4. flag => f3
166 	 *	5. verb and terminate
167 	 */
168 l0:
169 	c = *fmt & 0xff;
170 	if(c >= Runeself) {
171 		n = chartorune(&rune, fmt);
172 		fmt += n;
173 		c = rune;
174 	} else
175 		fmt++;
176 
177 l1:
178 	if(c == 0) {
179 		fmt--;
180 		goto loop;
181 	}
182 	if(c == '.') {
183 		if(local.f1 == NONE)
184 			local.f1 = 0;
185 		local.f2 = 0;
186 		goto l0;
187 	}
188 	if((c >= '1' && c <= '9') ||
189 	   (c == '0' && local.f1 != NONE)) {	/* '0' is a digit for f2 */
190 		n = 0;
191 		while(c >= '0' && c <= '9') {
192 			n = n*10 + c-'0';
193 			c = *fmt++;
194 		}
195 		if(local.f1 == NONE)
196 			local.f1 = n;
197 		else
198 			local.f2 = n;
199 		goto l1;
200 	}
201 	if(c == '*') {
202 		n = *(int*)argp;
203 		argp = (char*)argp + INT;
204 		if(local.f1 == NONE)
205 			local.f1 = n;
206 		else
207 			local.f2 = n;
208 		goto l0;
209 	}
210 	n = 0;
211 	if(c >= 0 && c < MAXFMT)
212 		n = fmtindex[c];
213 	local.chr = c;
214 	n = (*fmtconv[n])(argp, &local);
215 	if(n < 0) {
216 		local.f3 |= -n;
217 		goto l0;
218 	}
219 	argp = (char*)argp + n;
220 	goto loop;
221 }
222 
223 int
224 numbconv(void *o, Fconv *fp)
225 {
226 	char s[IDIGIT];
227 	int i, f, n, r, b, ucase;
228 	short h;
229 	long v;
230 	vlong vl;
231 
232 	SET(v);
233 	SET(vl);
234 
235 	ucase = 0;
236 	b = fp->chr;
237 	switch(fp->chr) {
238 	case 'u':
239 		fp->f3 |= FUNSIGN;
240 	case 'd':
241 		b = 10;
242 		break;
243 
244 	case 'o':
245 		b = 8;
246 		break;
247 
248 	case 'X':
249 		ucase = 1;
250 	case 'x':
251 		b = 16;
252 		break;
253 	}
254 
255 	f = 0;
256 	switch(fp->f3 & (FVLONG|FLONG|FSHORT|FUNSIGN)) {
257 	case FVLONG|FLONG:
258 		vl = *(vlong*)o;
259 		r = VLONG;
260 		break;
261 
262 	case FLONG:
263 		v = *(long*)o;
264 		r = LONG;
265 		break;
266 
267 	case FUNSIGN|FLONG:
268 		v = *(ulong*)o;
269 		r = LONG;
270 		break;
271 
272 	case FSHORT:
273 		h = *(int*)o;
274 		v = h;
275 		r = SHORT;
276 		break;
277 
278 	case FUNSIGN|FSHORT:
279 		h = *(int*)o;
280 		v = (ushort)h;
281 		r = SHORT;
282 		break;
283 
284 	default:
285 		v = *(int*)o;
286 		r = INT;
287 		break;
288 
289 	case FUNSIGN:
290 		v = *(unsigned*)o;
291 		r = INT;
292 		break;
293 	}
294 	if(!(fp->f3 & FUNSIGN) && v < 0) {
295 		v = -v;
296 		f = 1;
297 	}
298 	s[IDIGIT-1] = 0;
299 	for(i = IDIGIT-2;; i--) {
300 		if(fp->f3 & FVLONG)
301 			n = vl % b;
302 		else
303 			n = (ulong)v % b;
304 		n += '0';
305 		if(n > '9') {
306 			n += 'a' - ('9'+1);
307 			if(ucase)
308 				n += 'A'-'a';
309 		}
310 		s[i] = n;
311 		if(i < 2)
312 			break;
313 		if(fp->f3 & FVLONG)
314 			vl = vl / b;
315 		else
316 			v = (ulong)v / b;
317 		if(fp->f2 != NONE && i >= IDIGIT-fp->f2)
318 			continue;
319 		if(fp->f3 & FVLONG)
320 			if(vl <= 0)
321 				break;
322 		if(v <= 0)
323 			break;
324 	}
325 	if(fp->f3 & FSHARP)
326 	if(s[i] != '0') {
327 		if(b == 8)
328 			s[--i] = '0';
329 		else
330 		if(b == 16) {
331 			if(ucase)
332 				s[--i] = 'X';
333 			else
334 				s[--i] = 'x';
335 			s[--i] = '0';
336 		}
337 	}
338 	if(f)
339 		s[--i] = '-';
340 	fp->f2 = NONE;
341 	strconv(s+i, fp);
342 	return r;
343 }
344 
345 void
346 Strconv(Rune *s, Fconv *fp)
347 {
348 	int n, c, i;
349 	Rune rune;
350 
351 	if(fp->f3 & FMINUS)
352 		fp->f1 = -fp->f1;
353 	n = 0;
354 	if(fp->f1 != NONE && fp->f1 >= 0) {
355 		for(; s[n]; n++)
356 			;
357 		while(n < fp->f1) {
358 			if(fp->out < fp->eout)
359 				*fp->out++ = ' ';
360 			printcol++;
361 			n++;
362 		}
363 	}
364 	for(;;) {
365 		c = *s++;
366 		if(c == 0)
367 			break;
368 		n++;
369 		if(fp->f2 == NONE || fp->f2 > 0) {
370 			if(fp->out < fp->eout)
371 				if(c >= Runeself) {
372 					rune = c;
373 					i = runetochar(fp->out, &rune);
374 					fp->out += i;
375 				} else
376 					*fp->out++ = c;
377 			if(fp->f2 != NONE)
378 				fp->f2--;
379 			switch(c) {
380 			default:
381 				printcol++;
382 				break;
383 			case '\n':
384 				printcol = 0;
385 				break;
386 			case '\t':
387 				printcol = (printcol+8) & ~7;
388 				break;
389 			}
390 		}
391 	}
392 	if(fp->f1 != NONE && fp->f1 < 0) {
393 		fp->f1 = -fp->f1;
394 		while(n < fp->f1) {
395 			if(fp->out < fp->eout)
396 				*fp->out++ = ' ';
397 			printcol++;
398 			n++;
399 		}
400 	}
401 }
402 
403 void
404 strconv(char *s, Fconv *fp)
405 {
406 	int n, c, i;
407 	Rune rune;
408 
409 	if(fp->f3 & FMINUS)
410 		fp->f1 = -fp->f1;
411 	n = 0;
412 	if(fp->f1 != NONE && fp->f1 >= 0) {
413 		n = utflen(s);
414 		while(n < fp->f1) {
415 			if(fp->out < fp->eout)
416 				*fp->out++ = ' ';
417 			printcol++;
418 			n++;
419 		}
420 	}
421 	for(;;) {
422 		c = *s & 0xff;
423 		if(c >= Runeself) {
424 			i = chartorune(&rune, s);
425 			s += i;
426 			c = rune;
427 		} else
428 			s++;
429 		if(c == 0)
430 			break;
431 		n++;
432 		if(fp->f2 == NONE || fp->f2 > 0) {
433 			if(fp->out < fp->eout)
434 				if(c >= Runeself) {
435 					rune = c;
436 					i = runetochar(fp->out, &rune);
437 					fp->out += i;
438 				} else
439 					*fp->out++ = c;
440 			if(fp->f2 != NONE)
441 				fp->f2--;
442 			switch(c) {
443 			default:
444 				printcol++;
445 				break;
446 			case '\n':
447 				printcol = 0;
448 				break;
449 			case '\t':
450 				printcol = (printcol+8) & ~7;
451 				break;
452 			}
453 		}
454 	}
455 	if(fp->f1 != NONE && fp->f1 < 0) {
456 		fp->f1 = -fp->f1;
457 		while(n < fp->f1) {
458 			if(fp->out < fp->eout)
459 				*fp->out++ = ' ';
460 			printcol++;
461 			n++;
462 		}
463 	}
464 }
465 
466 static
467 int
468 noconv(void *o, Fconv *fp)
469 {
470 	int n;
471 	char s[10];
472 
473 	if(convcount == 0) {
474 		initfmt();
475 		n = 0;
476 		if(fp->chr >= 0 && fp->chr < MAXFMT)
477 			n = fmtindex[fp->chr];
478 		return (*fmtconv[n])(o, fp);
479 	}
480 	sprint(s, "*%c*", fp->chr);
481 	fp->f1 = 0;
482 	fp->f2 = NONE;
483 	fp->f3 = 0;
484 	strconv(s, fp);
485 	return 0;
486 }
487 
488 static
489 int
490 cconv(void *o, Fconv *fp)
491 {
492 	char s[10];
493 	Rune rune;
494 
495 	rune = *(int*)o;
496 	if(fp->chr == 'c')
497 		rune &= 0xff;
498 	s[runetochar(s, &rune)] = 0;
499 
500 	fp->f2 = NONE;
501 	strconv(s, fp);
502 	return INT;
503 }
504 
505 static
506 int
507 sconv(void *o, Fconv *fp)
508 {
509 	char *s;
510 	Rune *r;
511 
512 	if(fp->chr == 's') {
513 		s = *(char**)o;
514 		if(s == 0)
515 			s = "<null>";
516 		strconv(s, fp);
517 	} else {
518 		r = *(Rune**)o;
519 		if(r == 0)
520 			r = L"<null>";
521 		Strconv(r, fp);
522 	}
523 	return PTR;
524 }
525 
526 static
527 int
528 percent(void *o, Fconv *fp)
529 {
530 
531 	USED(o);
532 	if(fp->out < fp->eout)
533 		*fp->out++ = '%';
534 	return 0;
535 }
536 
537 static
538 int
539 flags(void *o, Fconv *fp)
540 {
541 	int f;
542 
543 	USED(o);
544 
545 	f = 0;
546 	switch(fp->chr) {
547 	case '+':
548 		f = FPLUS;
549 		break;
550 
551 	case '-':
552 		f = FMINUS;
553 		break;
554 
555 	case '#':
556 		f = FSHARP;
557 		break;
558 
559 	case 'h':
560 		f = FSHORT;
561 		break;
562 
563 	case 'l':
564 		f = FLONG;
565 		if(fp->f3 & FLONG)
566 			f = FVLONG;
567 		break;
568 
569 	case 'u':
570 		f = FUNSIGN;
571 		break;
572 	}
573 	return -f;
574 }
575