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