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