]> git.lizzy.rs Git - plan9front.git/blob - sys/src/libstdio/vfprintf.c
fix typo
[plan9front.git] / sys / src / libstdio / vfprintf.c
1 /*
2  * pANS stdio -- vfprintf
3  */
4 #include "iolib.h"
5 /*
6  * Leading flags
7  */
8 #define SPACE   1               /* ' ' prepend space if no sign printed */
9 #define ALT     2               /* '#' use alternate conversion */
10 #define SIGN    4               /* '+' prepend sign, even if positive */
11 #define LEFT    8               /* '-' left-justify */
12 #define ZPAD    16              /* '0' zero-pad */
13 /*
14  * Trailing flags
15  */
16 #define SHORT   32              /* 'h' convert a short integer */
17 #define LONG    64              /* 'l' convert a long integer */
18 #define LDBL    128             /* 'L' convert a long double */
19 #define PTR     256             /*     convert a void * (%p) */
20
21 static int lflag[] = {  /* leading flags */
22 0,      0,      0,      0,      0,      0,      0,      0,      /* ^@ ^A ^B ^C ^D ^E ^F ^G */
23 0,      0,      0,      0,      0,      0,      0,      0,      /* ^H ^I ^J ^K ^L ^M ^N ^O */
24 0,      0,      0,      0,      0,      0,      0,      0,      /* ^P ^Q ^R ^S ^T ^U ^V ^W */
25 0,      0,      0,      0,      0,      0,      0,      0,      /* ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
26 SPACE,  0,      0,      ALT,    0,      0,      0,      0,      /* sp  !  "  #  $  %  &  ' */
27 0,      0,      0,      SIGN,   0,      LEFT,   0,      0,      /*  (  )  *  +  ,  -  .  / */
28 ZPAD,   0,      0,      0,      0,      0,      0,      0,      /*  0  1  2  3  4  5  6  7 */
29 0,      0,      0,      0,      0,      0,      0,      0,      /*  8  9  :  ;  <  =  >  ? */
30 0,      0,      0,      0,      0,      0,      0,      0,      /*  @  A  B  C  D  E  F  G */
31 0,      0,      0,      0,      0,      0,      0,      0,      /*  H  I  J  K  L  M  N  O */
32 0,      0,      0,      0,      0,      0,      0,      0,      /*  P  Q  R  S  T  U  V  W */
33 0,      0,      0,      0,      0,      0,      0,      0,      /*  X  Y  Z  [  \  ]  ^  _ */
34 0,      0,      0,      0,      0,      0,      0,      0,      /*  `  a  b  c  d  e  f  g */
35 0,      0,      0,      0,      0,      0,      0,      0,      /*  h  i  j  k  l  m  n  o */
36 0,      0,      0,      0,      0,      0,      0,      0,      /*  p  q  r  s  t  u  v  w */
37 0,      0,      0,      0,      0,      0,      0,      0,      /*  x  y  z  {  |  }  ~ ^? */
38
39 0,      0,      0,      0,      0,      0,      0,      0,
40 0,      0,      0,      0,      0,      0,      0,      0,
41 0,      0,      0,      0,      0,      0,      0,      0,
42 0,      0,      0,      0,      0,      0,      0,      0,
43 0,      0,      0,      0,      0,      0,      0,      0,
44 0,      0,      0,      0,      0,      0,      0,      0,
45 0,      0,      0,      0,      0,      0,      0,      0,
46 0,      0,      0,      0,      0,      0,      0,      0,
47 0,      0,      0,      0,      0,      0,      0,      0,
48 0,      0,      0,      0,      0,      0,      0,      0,
49 0,      0,      0,      0,      0,      0,      0,      0,
50 0,      0,      0,      0,      0,      0,      0,      0,
51 0,      0,      0,      0,      0,      0,      0,      0,
52 0,      0,      0,      0,      0,      0,      0,      0,
53 0,      0,      0,      0,      0,      0,      0,      0,
54 0,      0,      0,      0,      0,      0,      0,      0,
55 };
56
57 static int tflag[] = {  /* trailing flags */
58 0,      0,      0,      0,      0,      0,      0,      0,      /* ^@ ^A ^B ^C ^D ^E ^F ^G */
59 0,      0,      0,      0,      0,      0,      0,      0,      /* ^H ^I ^J ^K ^L ^M ^N ^O */
60 0,      0,      0,      0,      0,      0,      0,      0,      /* ^P ^Q ^R ^S ^T ^U ^V ^W */
61 0,      0,      0,      0,      0,      0,      0,      0,      /* ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
62 0,      0,      0,      0,      0,      0,      0,      0,      /* sp  !  "  #  $  %  &  ' */
63 0,      0,      0,      0,      0,      0,      0,      0,      /*  (  )  *  +  ,  -  .  / */
64 0,      0,      0,      0,      0,      0,      0,      0,      /*  0  1  2  3  4  5  6  7 */
65 0,      0,      0,      0,      0,      0,      0,      0,      /*  8  9  :  ;  <  =  >  ? */
66 0,      0,      0,      0,      0,      0,      0,      0,      /*  @  A  B  C  D  E  F  G */
67 0,      0,      0,      0,      LDBL,   0,      0,      0,      /*  H  I  J  K  L  M  N  O */
68 0,      0,      0,      0,      0,      0,      0,      0,      /*  P  Q  R  S  T  U  V  W */
69 0,      0,      0,      0,      0,      0,      0,      0,      /*  X  Y  Z  [  \  ]  ^  _ */
70 0,      0,      0,      0,      0,      0,      0,      0,      /*  `  a  b  c  d  e  f  g */
71 SHORT,  0,      0,      0,      LONG,   0,      0,      0,      /*  h  i  j  k  l  m  n  o */
72 0,      0,      0,      0,      0,      0,      0,      0,      /*  p  q  r  s  t  u  v  w */
73 0,      0,      0,      0,      0,      0,      0,      0,      /*  x  y  z  {  |  }  ~ ^? */
74
75 0,      0,      0,      0,      0,      0,      0,      0,
76 0,      0,      0,      0,      0,      0,      0,      0,
77 0,      0,      0,      0,      0,      0,      0,      0,
78 0,      0,      0,      0,      0,      0,      0,      0,
79 0,      0,      0,      0,      0,      0,      0,      0,
80 0,      0,      0,      0,      0,      0,      0,      0,
81 0,      0,      0,      0,      0,      0,      0,      0,
82 0,      0,      0,      0,      0,      0,      0,      0,
83 0,      0,      0,      0,      0,      0,      0,      0,
84 0,      0,      0,      0,      0,      0,      0,      0,
85 0,      0,      0,      0,      0,      0,      0,      0,
86 0,      0,      0,      0,      0,      0,      0,      0,
87 0,      0,      0,      0,      0,      0,      0,      0,
88 0,      0,      0,      0,      0,      0,      0,      0,
89 0,      0,      0,      0,      0,      0,      0,      0,
90 0,      0,      0,      0,      0,      0,      0,      0,
91 };
92
93 static int ocvt_E(FILE *, va_list *, int, int, int);
94 static int ocvt_G(FILE *, va_list *, int, int, int);
95 static int ocvt_X(FILE *, va_list *, int, int, int);
96 static int ocvt_c(FILE *, va_list *, int, int, int);
97 static int ocvt_d(FILE *, va_list *, int, int, int);
98 static int ocvt_e(FILE *, va_list *, int, int, int);
99 static int ocvt_f(FILE *, va_list *, int, int, int);
100 static int ocvt_g(FILE *, va_list *, int, int, int);
101 static int ocvt_n(FILE *, va_list *, int, int, int);
102 static int ocvt_o(FILE *, va_list *, int, int, int);
103 static int ocvt_p(FILE *, va_list *, int, int, int);
104 static int ocvt_s(FILE *, va_list *, int, int, int);
105 static int ocvt_u(FILE *, va_list *, int, int, int);
106 static int ocvt_x(FILE *, va_list *, int, int, int);
107
108 static int(*ocvt[])(FILE *, va_list *, int, int, int) = {
109 0,      0,      0,      0,      0,      0,      0,      0,      /* ^@ ^A ^B ^C ^D ^E ^F ^G */
110 0,      0,      0,      0,      0,      0,      0,      0,      /* ^H ^I ^J ^K ^L ^M ^N ^O */
111 0,      0,      0,      0,      0,      0,      0,      0,      /* ^P ^Q ^R ^S ^T ^U ^V ^W */
112 0,      0,      0,      0,      0,      0,      0,      0,      /* ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
113 0,      0,      0,      0,      0,      0,      0,      0,      /* sp  !  "  #  $  %  &  ' */
114 0,      0,      0,      0,      0,      0,      0,      0,      /*  (  )  *  +  ,  -  .  / */
115 0,      0,      0,      0,      0,      0,      0,      0,      /*  0  1  2  3  4  5  6  7 */
116 0,      0,      0,      0,      0,      0,      0,      0,      /*  8  9  :  ;  <  =  >  ? */
117 0,      0,      0,      0,      0,      ocvt_E, 0,      ocvt_G, /*  @  A  B  C  D  E  F  G */
118 0,      0,      0,      0,      0,      0,      0,      0,      /*  H  I  J  K  L  M  N  O */
119 0,      0,      0,      0,      0,      0,      0,      0,      /*  P  Q  R  S  T  U  V  W */
120 ocvt_X, 0,      0,      0,      0,      0,      0,      0,      /*  X  Y  Z  [  \  ]  ^  _ */
121 0,      0,      0,      ocvt_c, ocvt_d, ocvt_e, ocvt_f, ocvt_g, /*  `  a  b  c  d  e  f  g */
122 0,      ocvt_d, 0,      0,      0,      0,      ocvt_n, ocvt_o, /*  h  i  j  k  l  m  n  o */
123 ocvt_p, 0,      0,      ocvt_s, 0,      ocvt_u, 0,      0,      /*  p  q  r  s  t  u  v  w */
124 ocvt_x, 0,      0,      0,      0,      0,      0,      0,      /*  x  y  z  {  |  }  ~ ^? */
125
126 0,      0,      0,      0,      0,      0,      0,      0,
127 0,      0,      0,      0,      0,      0,      0,      0,
128 0,      0,      0,      0,      0,      0,      0,      0,
129 0,      0,      0,      0,      0,      0,      0,      0,
130 0,      0,      0,      0,      0,      0,      0,      0,
131 0,      0,      0,      0,      0,      0,      0,      0,
132 0,      0,      0,      0,      0,      0,      0,      0,
133 0,      0,      0,      0,      0,      0,      0,      0,
134 0,      0,      0,      0,      0,      0,      0,      0,
135 0,      0,      0,      0,      0,      0,      0,      0,
136 0,      0,      0,      0,      0,      0,      0,      0,
137 0,      0,      0,      0,      0,      0,      0,      0,
138 0,      0,      0,      0,      0,      0,      0,      0,
139 0,      0,      0,      0,      0,      0,      0,      0,
140 0,      0,      0,      0,      0,      0,      0,      0,
141 0,      0,      0,      0,      0,      0,      0,      0,
142 };
143
144 static int nprint;
145
146 QLock _stdiolk;
147
148 int
149 vfprintf(FILE *f, const char *s, va_list args)
150 {
151         int flags, width, precision;
152
153         qlock(&_stdiolk);
154
155         nprint = 0;
156         while(*s){
157                 if(*s != '%'){
158                         putc(*s++, f);
159                         nprint++;
160                         continue;
161                 }
162                 s++;
163                 flags = 0;
164                 while(lflag[*s&_IO_CHMASK]) flags |= lflag[*s++&_IO_CHMASK];
165                 if(*s == '*'){
166                         width = va_arg(args, int);
167                         s++;
168                         if(width<0){
169                                 flags |= LEFT;
170                                 width = -width;
171                         }
172                 }
173                 else{
174                         width = 0;
175                         while('0'<=*s && *s<='9') width = width*10 + *s++ - '0';
176                 }
177                 if(*s == '.'){
178                         s++;
179                         if(*s == '*'){
180                                 precision = va_arg(args, int);
181                                 s++;
182                         }
183                         else{
184                                 precision = 0;
185                                 while('0'<=*s && *s<='9') precision = precision*10 + *s++ - '0';
186                         }
187                 }
188                 else
189                         precision = -1;
190                 while(tflag[*s&_IO_CHMASK]) flags |= tflag[*s++&_IO_CHMASK];
191                 if(ocvt[*s]) nprint += (*ocvt[*s++])(f, &args, flags, width, precision);
192                 else if(*s){
193                         putc(*s++, f);
194                         nprint++;
195                 }
196         }
197
198         qunlock(&_stdiolk);
199
200         if(ferror(f)){
201                 if((f->flags&STRING) && f->wp==f->rp && f->wp>f->buf){
202                         *(f->wp-1) = '\0';
203                         return nprint;
204                 }
205                 return -1;
206         }
207         return nprint;
208 }
209
210 static int
211 ocvt_c(FILE *f, va_list *args, int flags, int width, int precision)
212 {
213 #pragma ref precision
214         int i;
215
216         if(!(flags&LEFT)) for(i=1; i<width; i++) putc(' ', f);
217         putc((unsigned char)va_arg(*args, int), f);
218         if(flags&LEFT) for(i=1; i<width; i++) putc(' ', f);
219         return width<1 ? 1 : width;
220 }
221
222 static int
223 ocvt_s(FILE *f, va_list *args, int flags, int width, int precision)
224 {
225         int i, n = 0;
226         char *s;
227
228         s = va_arg(*args, char *);
229         if(!(flags&LEFT)){
230                 if(precision >= 0)
231                         for(i=0; i!=precision && s[i]; i++);
232                 else
233                         for(i=0; s[i]; i++);
234                 for(; i<width; i++){
235                         putc(' ', f);
236                         n++;
237                 }
238         }
239         if(precision >= 0){
240                 for(i=0; i!=precision && *s; i++){
241                         putc(*s++, f);
242                         n++;
243                 }
244         } else{
245                 for(i=0;*s;i++){
246                         putc(*s++, f);
247                         n++;
248                 }
249         }
250         if(flags&LEFT){
251                 for(; i<width; i++){
252                         putc(' ', f);
253                         n++;
254                 }
255         }
256         return n;
257 }
258
259 static int
260 ocvt_n(FILE *f, va_list *args, int flags, int width, int precision)
261 {
262 #pragma ref f
263 #pragma ref width
264 #pragma ref precision
265         if(flags&SHORT)
266                 *va_arg(*args, short *) = nprint;
267         else if(flags&LONG)
268                 *va_arg(*args, long *) = nprint;
269         else
270                 *va_arg(*args, int *) = nprint;
271         return 0;
272 }
273
274 /*
275  * Generic fixed-point conversion
276  *      f is the output FILE *;
277  *      args is the va_list * from which to get the number;
278  *      flags, width and precision are the results of printf-cracking;
279  *      radix is the number base to print in;
280  *      alphabet is the set of digits to use;
281  *      prefix is the prefix to print before non-zero numbers when
282  *      using ``alternate form.''
283  */
284 static int
285 ocvt_fixed(FILE *f, va_list *args, int flags, int width, int precision,
286         int radix, int sgned, char alphabet[], char *prefix)
287 {
288         char digits[128];       /* no reasonable machine will ever overflow this */
289         char *sign;
290         char *dp;
291         long snum;
292         unsigned long num;
293         int nout, npad, nlzero;
294
295         if(sgned){
296                 if(flags&PTR) snum = (long)va_arg(*args, void *);
297                 else if(flags&SHORT) snum = va_arg(*args, short);
298                 else if(flags&LONG) snum = va_arg(*args, long);
299                 else snum = va_arg(*args, int);
300                 if(snum < 0){
301                         sign = "-";
302                         num = -snum;
303                 } else{
304                         if(flags&SIGN) sign = "+";
305                         else if(flags&SPACE) sign = " ";
306                         else sign = "";
307                         num = snum;
308                 }
309         } else {
310                 sign = "";
311                 if(flags&PTR) num = (long)va_arg(*args, void *);
312                 else if(flags&SHORT) num = va_arg(*args, unsigned short);
313                 else if(flags&LONG) num = va_arg(*args, unsigned long);
314                 else num = va_arg(*args, unsigned int);
315         }
316         if(num == 0) prefix = "";
317         dp = digits;
318         do{
319                 *dp++ = alphabet[num%radix];
320                 num /= radix;
321         }while(num);
322         if(precision==0 && dp-digits==1 && dp[-1]=='0')
323                 dp--;
324         nlzero = precision-(dp-digits);
325         if(nlzero < 0) nlzero = 0;
326         if(flags&ALT){
327                 if(radix == 8) if(dp[-1]=='0' || nlzero) prefix = "";
328         }
329         else prefix = "";
330         nout = dp-digits+nlzero+strlen(prefix)+strlen(sign);
331         npad = width-nout;
332         if(npad < 0) npad = 0;
333         nout += npad;
334         if(!(flags&LEFT)){
335                 if(flags&ZPAD && precision <= 0){
336                         fputs(sign, f);
337                         fputs(prefix, f);
338                         while(npad){
339                                 putc('0', f);
340                                 --npad;
341                         }
342                 } else{
343                         while(npad){
344                                 putc(' ', f);
345                                 --npad;
346                         }
347                         fputs(sign, f);
348                         fputs(prefix, f);
349                 }
350                 while(nlzero){
351                         putc('0', f);
352                         --nlzero;
353                 }
354                 while(dp!=digits) putc(*--dp, f);
355         }
356         else{
357                 fputs(sign, f);
358                 fputs(prefix, f);
359                 while(nlzero){
360                         putc('0', f);
361                         --nlzero;
362                 }
363                 while(dp != digits) putc(*--dp, f);
364                 while(npad){
365                         putc(' ', f);
366                         --npad;
367                 }
368         }
369         return nout;    
370 }
371
372 static int
373 ocvt_X(FILE *f, va_list *args, int flags, int width, int precision)
374 {
375         return ocvt_fixed(f, args, flags, width, precision, 16, 0, "0123456789ABCDEF", "0X");
376 }
377
378 static int
379 ocvt_d(FILE *f, va_list *args, int flags, int width, int precision)
380 {
381         return ocvt_fixed(f, args, flags, width, precision, 10, 1, "0123456789", "");
382 }
383
384 static int
385 ocvt_o(FILE *f, va_list *args, int flags, int width, int precision)
386 {
387         return ocvt_fixed(f, args, flags, width, precision, 8, 0, "01234567", "0");
388 }
389
390 static int
391 ocvt_p(FILE *f, va_list *args, int flags, int width, int precision)
392 {
393         return ocvt_fixed(f, args, flags|PTR|ALT, width, precision, 16, 0,
394                 "0123456789ABCDEF", "0X");
395 }
396
397 static int
398 ocvt_u(FILE *f, va_list *args, int flags, int width, int precision)
399 {
400         return ocvt_fixed(f, args, flags, width, precision, 10, 0, "0123456789", "");
401 }
402
403 static int
404 ocvt_x(FILE *f, va_list *args, int flags, int width, int precision)
405 {
406         return ocvt_fixed(f, args, flags, width, precision, 16, 0, "0123456789abcdef", "0x");
407 }
408
409 static int ocvt_flt(FILE *, va_list *, int, int, int, char);
410
411 static int
412 ocvt_E(FILE *f, va_list *args, int flags, int width, int precision)
413 {
414         return ocvt_flt(f, args, flags, width, precision, 'E');
415 }
416
417 static int
418 ocvt_G(FILE *f, va_list *args, int flags, int width, int precision)
419 {
420         return ocvt_flt(f, args, flags, width, precision, 'G');
421 }
422
423 static int
424 ocvt_e(FILE *f, va_list *args, int flags, int width, int precision)
425 {
426         return ocvt_flt(f, args, flags, width, precision, 'e');
427 }
428
429 static int
430 ocvt_f(FILE *f, va_list *args, int flags, int width, int precision)
431 {
432         return ocvt_flt(f, args, flags, width, precision, 'f');
433 }
434
435 static int
436 ocvt_g(FILE *f, va_list *args, int flags, int width, int precision)
437 {
438         return ocvt_flt(f, args, flags, width, precision, 'g');
439 }
440
441 static int
442 ocvt_flt(FILE *f, va_list *args, int flags, int width, int precision, char afmt)
443 {
444         int echr;
445         char *digits, *edigits;
446         int exponent;
447         char fmt;
448         int sign;
449         int ndig;
450         int nout, i;
451         char ebuf[20];  /* no sensible machine will overflow this */
452         char *eptr;
453         double d;
454
455         echr = 'e';
456         fmt = afmt;
457         d = va_arg(*args, double);
458         if(precision < 0) precision = 6;
459         switch(fmt){
460         case 'f':
461                 digits = dtoa(d, 3, precision, &exponent, &sign, &edigits);
462                 break;
463         case 'E':
464                 echr = 'E';
465                 fmt = 'e';
466                 /* fall through */
467         case 'e':
468                 digits = dtoa(d, 2, 1+precision, &exponent, &sign, &edigits);
469                 break;
470         case 'G':
471                 echr = 'E';
472                 /* fall through */
473         case 'g':
474                 if (precision > 0)
475                         digits = dtoa(d, 2, precision, &exponent, &sign, &edigits);
476                 else {
477                         digits = dtoa(d, 0, precision, &exponent, &sign, &edigits);
478                         precision = edigits - digits;
479                         if (exponent > precision && exponent <= precision + 4)
480                                 precision = exponent;
481                         }
482                 if(exponent >= -3 && exponent <= precision){
483                         fmt = 'f';
484                         precision -= exponent;
485                 }else{
486                         fmt = 'e';
487                         --precision;
488                 }
489                 break;
490         }
491         if (exponent == 9999) {
492                 /* Infinity or Nan */
493                 precision = 0;
494                 exponent = edigits - digits;
495                 fmt = 'f';
496         }
497         ndig = edigits-digits;
498         if((afmt=='g' || afmt=='G') && !(flags&ALT)){   /* knock off trailing zeros */
499                 if(fmt == 'f'){
500                         if(precision+exponent > ndig) {
501                                 precision = ndig - exponent;
502                                 if(precision < 0)
503                                         precision = 0;
504                         }
505                 }
506                 else{
507                         if(precision > ndig-1) precision = ndig-1;
508                 }
509         }
510         nout = precision;                               /* digits after decimal point */
511         if(precision!=0 || flags&ALT) nout++;           /* decimal point */
512         if(fmt=='f' && exponent>0) nout += exponent;    /* digits before decimal point */
513         else nout++;                                    /* there's always at least one */
514         if(sign || flags&(SPACE|SIGN)) nout++;          /* sign */
515         if(fmt != 'f'){                                 /* exponent */
516                 eptr = ebuf;
517                 for(i=exponent<=0?1-exponent:exponent-1; i; i/=10)
518                         *eptr++ = '0' + i%10;
519                 while(eptr<ebuf+2) *eptr++ = '0';
520                 nout += eptr-ebuf+2;                    /* e+99 */
521         }
522         if(!(flags&ZPAD) && !(flags&LEFT))
523                 while(nout < width){
524                         putc(' ', f);
525                         nout++;
526                 }
527         if(sign) putc('-', f);
528         else if(flags&SIGN) putc('+', f);
529         else if(flags&SPACE) putc(' ', f);
530         if(flags&ZPAD)
531                 while(nout < width){
532                         putc('0', f);
533                         nout++;
534                 }
535         if(fmt == 'f'){
536                 for(i=0; i<exponent; i++) putc(i<ndig?digits[i]:'0', f);
537                 if(i == 0) putc('0', f);
538                 if(precision>0 || flags&ALT) putc('.', f);
539                 for(i=0; i!=precision; i++)
540                         putc(0<=i+exponent && i+exponent<ndig?digits[i+exponent]:'0', f);
541         }
542         else{
543                 putc(digits[0], f);
544                 if(precision>0 || flags&ALT) putc('.', f);
545                 for(i=0; i!=precision; i++) putc(i<ndig-1?digits[i+1]:'0', f);
546         }
547         if(fmt != 'f'){
548                 putc(echr, f);
549                 putc(exponent<=0?'-':'+', f);
550                 while(eptr>ebuf) putc(*--eptr, f);
551         }
552         while(nout < width){
553                 putc(' ', f);
554                 nout++;
555         }
556         freedtoa(digits);
557         return nout;
558 }