xref: /inferno-os/libkern/dofmt.c (revision f60356712426da98d6097355b9a571cf4b13943b)
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  }
79  
80  void *
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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