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