xref: /inferno-os/libkern/dofmt.c (revision b43c1ca5eb5fc65b93ae935a568432712797b049)
1 /*
2  * The authors of this software are Rob Pike and Ken Thompson.
3  *              Copyright (c) 2002 by Lucent Technologies.
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose without fee is hereby granted, provided that this entire notice
6  * is included in all copies of any software which is or includes a copy
7  * or modification of this software and in all copies of the supporting
8  * documentation for such software.
9  * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
10  * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
11  * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
12  * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
13  */
14 #include "lib9.h"
15 #include "fmtdef.h"
16 
17 /* format the output into f->to and return the number of characters fmted  */
18 int
19 dofmt(Fmt *f, char *fmt)
20 {
21 	Rune rune, *rt, *rs;
22 	int r;
23 	char *t, *s;
24 	int n, nfmt;
25 
26 	nfmt = f->nfmt;
27 	for(;;){
28 		if(f->runes){
29 			rt = f->to;
30 			rs = f->stop;
31 			while((r = *(uchar*)fmt) && r != '%'){
32 				if(r < Runeself)
33 					fmt++;
34 				else{
35 					fmt += chartorune(&rune, fmt);
36 					r = rune;
37 				}
38 				FMTRCHAR(f, rt, rs, r);
39 			}
40 			fmt++;
41 			f->nfmt += rt - (Rune *)f->to;
42 			f->to = rt;
43 			if(!r)
44 				return f->nfmt - nfmt;
45 			f->stop = rs;
46 		}else{
47 			t = f->to;
48 			s = f->stop;
49 			while((r = *(uchar*)fmt) && r != '%'){
50 				if(r < Runeself){
51 					FMTCHAR(f, t, s, r);
52 					fmt++;
53 				}else{
54 					n = chartorune(&rune, fmt);
55 					if(t + n > s){
56 						t = _fmtflush(f, t, n);
57 						if(t != nil)
58 							s = f->stop;
59 						else
60 							return -1;
61 					}
62 					while(n--)
63 						*t++ = *fmt++;
64 				}
65 			}
66 			fmt++;
67 			f->nfmt += t - (char *)f->to;
68 			f->to = t;
69 			if(!r)
70 				return f->nfmt - nfmt;
71 			f->stop = s;
72 		}
73 
74 		fmt = _fmtdispatch(f, fmt, 0);
75 		if(fmt == nil)
76 			return -1;
77 	}
78 	return 0;	/* not reached */
79 }
80 
81 void *
82 _fmtflush(Fmt *f, void *t, int len)
83 {
84 	if(f->runes)
85 		f->nfmt += (Rune*)t - (Rune*)f->to;
86 	else
87 		f->nfmt += (char*)t - (char *)f->to;
88 	f->to = t;
89 	if(f->flush == 0 || (*f->flush)(f) == 0 || (char*)f->to + len > (char*)f->stop){
90 		f->stop = f->to;
91 		return nil;
92 	}
93 	return f->to;
94 }
95 
96 /*
97  * put a formatted block of memory sz bytes long of n runes into the output buffer,
98  * left/right justified in a field of at least f->width charactes
99  */
100 int
101 _fmtpad(Fmt *f, int n)
102 {
103 	char *t, *s;
104 	int i;
105 
106 	t = f->to;
107 	s = f->stop;
108 	for(i = 0; i < n; i++)
109 		FMTCHAR(f, t, s, ' ');
110 	f->nfmt += t - (char *)f->to;
111 	f->to = t;
112 	return 0;
113 }
114 
115 int
116 _rfmtpad(Fmt *f, int n)
117 {
118 	Rune *t, *s;
119 	int i;
120 
121 	t = f->to;
122 	s = f->stop;
123 	for(i = 0; i < n; i++)
124 		FMTRCHAR(f, t, s, ' ');
125 	f->nfmt += t - (Rune *)f->to;
126 	f->to = t;
127 	return 0;
128 }
129 
130 int
131 _fmtcpy(Fmt *f, void *vm, int n, int sz)
132 {
133 	Rune *rt, *rs, r;
134 	char *t, *s, *m, *me;
135 	ulong fl;
136 	int nc, w;
137 
138 	m = vm;
139 	me = m + sz;
140 	w = f->width;
141 	fl = f->flags;
142 	if((fl & FmtPrec) && n > f->prec)
143 		n = f->prec;
144 	if(f->runes){
145 		if(!(fl & FmtLeft) && _rfmtpad(f, w - n) < 0)
146 			return -1;
147 		rt = f->to;
148 		rs = f->stop;
149 		for(nc = n; nc > 0; nc--){
150 			r = *(uchar*)m;
151 			if(r < Runeself)
152 				m++;
153 			else if((me - m) >= UTFmax || fullrune(m, me-m))
154 				m += chartorune(&r, m);
155 			else
156 				break;
157 			FMTRCHAR(f, rt, rs, r);
158 		}
159 		f->nfmt += rt - (Rune *)f->to;
160 		f->to = rt;
161 		if(m < me)
162 			return -1;
163 		if(fl & FmtLeft && _rfmtpad(f, w - n) < 0)
164 			return -1;
165 	}else{
166 		if(!(fl & FmtLeft) && _fmtpad(f, w - n) < 0)
167 			return -1;
168 		t = f->to;
169 		s = f->stop;
170 		for(nc = n; nc > 0; nc--){
171 			r = *(uchar*)m;
172 			if(r < Runeself)
173 				m++;
174 			else if((me - m) >= UTFmax || fullrune(m, me-m))
175 				m += chartorune(&r, m);
176 			else
177 				break;
178 			FMTRUNE(f, t, s, r);
179 		}
180 		f->nfmt += t - (char *)f->to;
181 		f->to = t;
182 		if(fl & FmtLeft && _fmtpad(f, w - n) < 0)
183 			return -1;
184 	}
185 	return 0;
186 }
187 
188 int
189 _fmtrcpy(Fmt *f, void *vm, int n)
190 {
191 	Rune r, *m, *me, *rt, *rs;
192 	char *t, *s;
193 	ulong fl;
194 	int w;
195 
196 	m = vm;
197 	w = f->width;
198 	fl = f->flags;
199 	if((fl & FmtPrec) && n > f->prec)
200 		n = f->prec;
201 	if(f->runes){
202 		if(!(fl & FmtLeft) && _rfmtpad(f, w - n) < 0)
203 			return -1;
204 		rt = f->to;
205 		rs = f->stop;
206 		for(me = m + n; m < me; m++)
207 			FMTRCHAR(f, rt, rs, *m);
208 		f->nfmt += rt - (Rune *)f->to;
209 		f->to = rt;
210 		if(fl & FmtLeft && _rfmtpad(f, w - n) < 0)
211 			return -1;
212 	}else{
213 		if(!(fl & FmtLeft) && _fmtpad(f, w - n) < 0)
214 			return -1;
215 		t = f->to;
216 		s = f->stop;
217 		for(me = m + n; m < me; m++){
218 			r = *m;
219 			FMTRUNE(f, t, s, r);
220 		}
221 		f->nfmt += t - (char *)f->to;
222 		f->to = t;
223 		if(fl & FmtLeft && _fmtpad(f, w - n) < 0)
224 			return -1;
225 	}
226 	return 0;
227 }
228 
229 /* fmt out one character */
230 int
231 _charfmt(Fmt *f)
232 {
233 	char x[1];
234 
235 	x[0] = va_arg(f->args, int);
236 	f->prec = 1;
237 	return _fmtcpy(f, x, 1, 1);
238 }
239 
240 /* fmt out one rune */
241 int
242 _runefmt(Fmt *f)
243 {
244 	Rune x[1];
245 
246 	x[0] = va_arg(f->args, int);
247 	return _fmtrcpy(f, x, 1);
248 }
249 
250 /* public helper routine: fmt out a null terminated string already in hand */
251 int
252 fmtstrcpy(Fmt *f, char *s)
253 {
254 	int p, i;
255 	if(!s)
256 		return _fmtcpy(f, "<nil>", 5, 5);
257 	/* if precision is specified, make sure we don't wander off the end */
258 	if(f->flags & FmtPrec){
259 		p = f->prec;
260 		for(i = 0; i < p; i++)
261 			if(s[i] == 0)
262 				break;
263 		return _fmtcpy(f, s, utfnlen(s, i), i);	/* BUG?: won't print a partial rune at end */
264 	}
265 
266 	return _fmtcpy(f, s, utflen(s), strlen(s));
267 }
268 
269 /* fmt out a null terminated utf string */
270 int
271 _strfmt(Fmt *f)
272 {
273 	char *s;
274 
275 	s = va_arg(f->args, char *);
276 	return fmtstrcpy(f, s);
277 }
278 
279 /* public helper routine: fmt out a null terminated rune string already in hand */
280 int
281 fmtrunestrcpy(Fmt *f, Rune *s)
282 {
283 	Rune *e;
284 	int n, p;
285 
286 	if(!s)
287 		return _fmtcpy(f, "<nil>", 5, 5);
288 	/* if precision is specified, make sure we don't wander off the end */
289 	if(f->flags & FmtPrec){
290 		p = f->prec;
291 		for(n = 0; n < p; n++)
292 			if(s[n] == 0)
293 				break;
294 	}else{
295 		for(e = s; *e; e++)
296 			;
297 		n = e - s;
298 	}
299 	return _fmtrcpy(f, s, n);
300 }
301 
302 /* fmt out a null terminated rune string */
303 int
304 _runesfmt(Fmt *f)
305 {
306 	Rune *s;
307 
308 	s = va_arg(f->args, Rune *);
309 	return fmtrunestrcpy(f, s);
310 }
311 
312 /* fmt a % */
313 int
314 _percentfmt(Fmt *f)
315 {
316 	Rune x[1];
317 
318 	x[0] = f->r;
319 	f->prec = 1;
320 	return _fmtrcpy(f, x, 1);
321 }
322 
323 /* fmt an integer */
324 int
325 _ifmt(Fmt *f)
326 {
327 	char buf[70], *p, *conv;
328 	uvlong vu;
329 	ulong u;
330 	int neg, base, i, n, fl, w, isv;
331 
332 	neg = 0;
333 	fl = f->flags;
334 	isv = 0;
335 	vu = 0;
336 	u = 0;
337 	if(f->r == 'p'){
338 		u = (ulong)va_arg(f->args, void*);
339 		f->r = 'x';
340 		fl |= FmtUnsigned;
341 	}else if(fl & FmtVLong){
342 		isv = 1;
343 		if(fl & FmtUnsigned)
344 			vu = va_arg(f->args, uvlong);
345 		else
346 			vu = va_arg(f->args, vlong);
347 	}else if(fl & FmtLong){
348 		if(fl & FmtUnsigned)
349 			u = va_arg(f->args, ulong);
350 		else
351 			u = va_arg(f->args, long);
352 	}else if(fl & FmtByte){
353 		if(fl & FmtUnsigned)
354 			u = (uchar)va_arg(f->args, int);
355 		else
356 			u = (char)va_arg(f->args, int);
357 	}else if(fl & FmtShort){
358 		if(fl & FmtUnsigned)
359 			u = (ushort)va_arg(f->args, int);
360 		else
361 			u = (short)va_arg(f->args, int);
362 	}else{
363 		if(fl & FmtUnsigned)
364 			u = va_arg(f->args, uint);
365 		else
366 			u = va_arg(f->args, int);
367 	}
368 	conv = "0123456789abcdef";
369 	switch(f->r){
370 	case 'd':
371 		base = 10;
372 		break;
373 	case 'x':
374 		base = 16;
375 		break;
376 	case 'X':
377 		base = 16;
378 		conv = "0123456789ABCDEF";
379 		break;
380 	case 'b':
381 		base = 2;
382 		break;
383 	case 'o':
384 		base = 8;
385 		break;
386 	default:
387 		return -1;
388 	}
389 	if(!(fl & FmtUnsigned)){
390 		if(isv && (vlong)vu < 0){
391 			vu = -(vlong)vu;
392 			neg = 1;
393 		}else if(!isv && (long)u < 0){
394 			u = -(long)u;
395 			neg = 1;
396 		}
397 	}
398 	p = buf + sizeof buf - 1;
399 	n = 0;
400 	if(isv){
401 		while(vu){
402 			i = vu % base;
403 			vu /= base;
404 			if((fl & FmtComma) && n % 4 == 3){
405 				*p-- = ',';
406 				n++;
407 			}
408 			*p-- = conv[i];
409 			n++;
410 		}
411 	}else{
412 		while(u){
413 			i = u % base;
414 			u /= base;
415 			if((fl & FmtComma) && n % 4 == 3){
416 				*p-- = ',';
417 				n++;
418 			}
419 			*p-- = conv[i];
420 			n++;
421 		}
422 	}
423 	if(n == 0){
424 		*p-- = '0';
425 		n = 1;
426 	}
427 	for(w = f->prec; n < w && p > buf+3; n++)
428 		*p-- = '0';
429 	if(neg || (fl & (FmtSign|FmtSpace)))
430 		n++;
431 	if(fl & FmtSharp){
432 		if(base == 16)
433 			n += 2;
434 		else if(base == 8){
435 			if(p[1] == '0')
436 				fl &= ~FmtSharp;
437 			else
438 				n++;
439 		}
440 	}
441 	if((fl & FmtZero) && !(fl & FmtLeft)){
442 		for(w = f->width; n < w && p > buf+3; n++)
443 			*p-- = '0';
444 		f->width = 0;
445 	}
446 	if(fl & FmtSharp){
447 		if(base == 16)
448 			*p-- = f->r;
449 		if(base == 16 || base == 8)
450 			*p-- = '0';
451 	}
452 	if(neg)
453 		*p-- = '-';
454 	else if(fl & FmtSign)
455 		*p-- = '+';
456 	else if(fl & FmtSpace)
457 		*p-- = ' ';
458 	f->flags &= ~FmtPrec;
459 	return _fmtcpy(f, p + 1, n, n);
460 }
461 
462 int
463 _countfmt(Fmt *f)
464 {
465 	void *p;
466 	ulong fl;
467 
468 	fl = f->flags;
469 	p = va_arg(f->args, void*);
470 	if(fl & FmtVLong){
471 		*(vlong*)p = f->nfmt;
472 	}else if(fl & FmtLong){
473 		*(long*)p = f->nfmt;
474 	}else if(fl & FmtByte){
475 		*(char*)p = f->nfmt;
476 	}else if(fl & FmtShort){
477 		*(short*)p = f->nfmt;
478 	}else{
479 		*(int*)p = f->nfmt;
480 	}
481 	return 0;
482 }
483 
484 int
485 _flagfmt(Fmt *f)
486 {
487 	switch(f->r){
488 	case ',':
489 		f->flags |= FmtComma;
490 		break;
491 	case '-':
492 		f->flags |= FmtLeft;
493 		break;
494 	case '+':
495 		f->flags |= FmtSign;
496 		break;
497 	case '#':
498 		f->flags |= FmtSharp;
499 		break;
500 	case ' ':
501 		f->flags |= FmtSpace;
502 		break;
503 	case 'u':
504 		f->flags |= FmtUnsigned;
505 		break;
506 	case 'h':
507 		if(f->flags & FmtShort)
508 			f->flags |= FmtByte;
509 		f->flags |= FmtShort;
510 		break;
511 	case 'l':
512 		if(f->flags & FmtLong)
513 			f->flags |= FmtVLong;
514 		f->flags |= FmtLong;
515 		break;
516 	}
517 	return 1;
518 }
519 
520 /* default error format */
521 int
522 _badfmt(Fmt *f)
523 {
524 	char x[3];
525 
526 	x[0] = '%';
527 	x[1] = f->r;
528 	x[2] = '%';
529 	f->prec = 3;
530 	_fmtcpy(f, x, 3, 3);
531 	return 0;
532 }
533