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