• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*	$OpenBSD: vfwprintf.c,v 1.15 2015/12/28 22:08:18 mmcc Exp $ */
2 /*-
3  * Copyright (c) 1990 The Regents of the University of California.
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * Chris Torek.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #define CHAR_TYPE wchar_t
35 #define FUNCTION_NAME __vfwprintf
36 #define CHAR_TYPE_STRLEN wcslen
37 #define CHAR_TYPE_STRNLEN wcsnlen
38 #define CHAR_TYPE_INF L"INF"
39 #define CHAR_TYPE_inf L"inf"
40 #define CHAR_TYPE_NAN L"NAN"
41 #define CHAR_TYPE_nan L"nan"
42 #define CHAR_TYPE_ORIENTATION 1
43 #include "printf_common.h"
44 
FUNCTION_NAME(FILE * fp,const CHAR_TYPE * fmt0,va_list ap)45 int FUNCTION_NAME(FILE* fp, const CHAR_TYPE* fmt0, va_list ap) {
46   int caller_errno = errno;
47   int n, n2;
48   CHAR_TYPE* cp;   /* handy char pointer (short term usage) */
49   CHAR_TYPE sign;  /* sign prefix (' ', '+', '-', or \0) */
50   int flags;     /* flags as above */
51   int ret;       /* return value accumulator */
52   int width;     /* width from format (%8d), or 0 */
53   int prec;      /* precision from format; <0 for N/A */
54   /*
55    * We can decompose the printed representation of floating
56    * point numbers into several parts, some of which may be empty:
57    *
58    * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ
59    *    A       B     ---C---      D       E   F
60    *
61    * A:	'sign' holds this value if present; '\0' otherwise
62    * B:	ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal
63    * C:	cp points to the string MMMNNN.  Leading and trailing
64    *	zeros are not in the string and must be added.
65    * D:	expchar holds this character; '\0' if no exponent, e.g. %f
66    * F:	at least two digits for decimal, at least one digit for hex
67    */
68   char* decimal_point = nullptr;
69   int signflag; /* true if float is negative */
70   union {       /* floating point arguments %[aAeEfFgG] */
71     double dbl;
72     long double ldbl;
73   } fparg;
74   int expt;                      /* integer value of exponent */
75   char expchar;                  /* exponent character: [eEpP\0] */
76   char* dtoaend;                 /* pointer to end of converted digits */
77   int expsize;                   /* character count for expstr */
78   int lead;                      /* sig figs before decimal or group sep */
79   int ndig;                      /* actual number of digits returned by dtoa */
80   CHAR_TYPE expstr[MAXEXPDIG + 2]; /* buffer for exponent string: e+ZZZ */
81   char* dtoaresult = nullptr;
82 
83   uintmax_t _umax;             /* integer arguments %[diouxX] */
84   enum { OCT, DEC, HEX } base; /* base for %[diouxX] conversion */
85   int dprec;                   /* a copy of prec if %[diouxX], 0 otherwise */
86   int realsz;                  /* field size expanded by dprec */
87   int size;                    /* size of converted field or string */
88   const char* xdigs;           /* digits for %[xX] conversion */
89 #define NIOV 8
90   struct __suio uio;       /* output information: summary */
91   struct __siov iov[NIOV]; /* ... and individual io vectors */
92   struct __siov* iovp; /* for PRINT macro */
93   CHAR_TYPE buf[BUF];            /* buffer with space for digits of uintmax_t */
94   CHAR_TYPE ox[2];               /* space for 0x; ox[1] is either x, X, or \0 */
95   union arg* argtable;         /* args, built due to positional arg */
96   union arg statargtable[STATIC_ARG_TBL_SIZE];
97   size_t argtablesiz;
98   int nextarg;      /* 1-based argument index */
99   va_list orgap;    /* original argument pointer */
100   CHAR_TYPE* convbuf; /* buffer for wide/multibyte conversion */
101 
102   /*
103    * Choose PADSIZE to trade efficiency vs. size.  If larger printf
104    * fields occur frequently, increase PADSIZE and make the initialisers
105    * below longer.
106    */
107 #define PADSIZE 16 /* pad chunk size */
108   static CHAR_TYPE blanks[PADSIZE] = {
109     ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '
110   };
111   static CHAR_TYPE zeroes[PADSIZE] = {
112     '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'
113   };
114 
115   static const char xdigs_lower[] = "0123456789abcdef";
116   static const char xdigs_upper[] = "0123456789ABCDEF";
117 
118 #define PRINT(ptr, len)                                   \
119   do {                                                    \
120     for (int n3 = 0; n3 < (len); n3++) {                      \
121       if ((helpers::xfputwc((ptr)[n3], fp)) == WEOF) goto error; \
122     }                                                     \
123   } while (0)
124 
125   _SET_ORIENTATION(fp, CHAR_TYPE_ORIENTATION);
126 
127   // Writing "" to a read only file returns EOF, not 0.
128   if (cantwrite(fp)) {
129     errno = EBADF;
130     return EOF;
131   }
132 
133   // Optimize writes to stderr and other unbuffered files).
134   if ((fp->_flags & (__SNBF | __SWR | __SRW)) == (__SNBF | __SWR) && fp->_file >= 0) {
135     return (__sbprintf(fp, fmt0, ap));
136   }
137 
138   CHAR_TYPE* fmt = const_cast<CHAR_TYPE*>(fmt0);
139   argtable = nullptr;
140   nextarg = 1;
141   va_copy(orgap, ap);
142   uio.uio_iov = iovp = iov;
143   uio.uio_resid = 0;
144   uio.uio_iovcnt = 0;
145   ret = 0;
146   convbuf = nullptr;
147 
148   /*
149    * Scan the format for conversions (`%' character).
150    */
151   for (;;) {
152     int ch;
153     for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) continue;
154     if (fmt != cp) {
155       ptrdiff_t m = fmt - cp;
156       if (m < 0 || m > INT_MAX - ret) goto overflow;
157       PRINT(cp, m);
158       ret += m;
159     }
160     if (ch == '\0') goto done;
161     fmt++; /* skip over '%' */
162 
163     flags = 0;
164     dprec = 0;
165     width = 0;
166     prec = -1;
167     sign = '\0';
168     ox[1] = '\0';
169 
170   rflag:
171     ch = *fmt++;
172   reswitch:
173     switch (ch) {
174       case ' ':
175         /*
176          * ``If the space and + flags both appear, the space
177          * flag will be ignored.''
178          *	-- ANSI X3J11
179          */
180         if (!sign) sign = ' ';
181         goto rflag;
182       case '#':
183         flags |= ALT;
184         goto rflag;
185       case '\'':
186         /* grouping not implemented */
187         goto rflag;
188       case '*':
189         /*
190          * ``A negative field width argument is taken as a
191          * - flag followed by a positive field width.''
192          *	-- ANSI X3J11
193          * They don't exclude field widths read from args.
194          */
195         GETASTER(width);
196         if (width >= 0) goto rflag;
197         if (width == INT_MIN) goto overflow;
198         width = -width;
199         __BIONIC_FALLTHROUGH;
200       case '-':
201         flags |= LADJUST;
202         goto rflag;
203       case '+':
204         sign = '+';
205         goto rflag;
206       case '.':
207         if ((ch = *fmt++) == '*') {
208           GETASTER(n);
209           prec = n < 0 ? -1 : n;
210           goto rflag;
211         }
212         n = 0;
213         while (is_digit(ch)) {
214           APPEND_DIGIT(n, ch);
215           ch = *fmt++;
216         }
217         if (ch == '$') {
218           nextarg = n;
219           if (argtable == nullptr) {
220             argtable = statargtable;
221             if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) {
222               ret = -1;
223               goto error;
224             }
225           }
226           goto rflag;
227         }
228         prec = n;
229         goto reswitch;
230       case '0':
231         /*
232          * ``Note that 0 is taken as a flag, not as the
233          * beginning of a field width.''
234          *	-- ANSI X3J11
235          */
236         flags |= ZEROPAD;
237         goto rflag;
238       case '1':
239       case '2':
240       case '3':
241       case '4':
242       case '5':
243       case '6':
244       case '7':
245       case '8':
246       case '9':
247         n = 0;
248         do {
249           APPEND_DIGIT(n, ch);
250           ch = *fmt++;
251         } while (is_digit(ch));
252         if (ch == '$') {
253           nextarg = n;
254           if (argtable == nullptr) {
255             argtable = statargtable;
256             if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) {
257               ret = -1;
258               goto error;
259             }
260           }
261           goto rflag;
262         }
263         width = n;
264         goto reswitch;
265       case 'L':
266         flags |= LONGDBL;
267         goto rflag;
268       case 'h':
269         if (*fmt == 'h') {
270           fmt++;
271           flags |= CHARINT;
272         } else {
273           flags |= SHORTINT;
274         }
275         goto rflag;
276       case 'j':
277         flags |= MAXINT;
278         goto rflag;
279       case 'l':
280         if (*fmt == 'l') {
281           fmt++;
282           flags |= LLONGINT;
283         } else {
284           flags |= LONGINT;
285         }
286         goto rflag;
287       case 'q':
288         flags |= LLONGINT;
289         goto rflag;
290       case 't':
291         flags |= PTRINT;
292         goto rflag;
293       case 'z':
294         flags |= SIZEINT;
295         goto rflag;
296       case 'C':
297         flags |= LONGINT;
298         __BIONIC_FALLTHROUGH;
299       case 'c':
300         if (flags & LONGINT)
301           *(cp = buf) = (wchar_t)GETARG(wint_t);
302         else
303           *(cp = buf) = (wchar_t)btowc(GETARG(int));
304         size = 1;
305         sign = '\0';
306         break;
307       case 'D':
308         flags |= LONGINT;
309         __BIONIC_FALLTHROUGH;
310       case 'd':
311       case 'i':
312         _umax = SARG();
313         if ((intmax_t)_umax < 0) {
314           _umax = -_umax;
315           sign = '-';
316         }
317         base = DEC;
318         goto number;
319       case 'a':
320       case 'A':
321         if (ch == 'a') {
322           ox[1] = 'x';
323           xdigs = xdigs_lower;
324           expchar = 'p';
325         } else {
326           ox[1] = 'X';
327           xdigs = xdigs_upper;
328           expchar = 'P';
329         }
330         if (prec >= 0) prec++;
331         if (dtoaresult) __freedtoa(dtoaresult);
332         if (flags & LONGDBL) {
333           fparg.ldbl = GETARG(long double);
334           dtoaresult = __hldtoa(fparg.ldbl, xdigs, prec, &expt, &signflag, &dtoaend);
335           if (dtoaresult == nullptr) {
336             errno = ENOMEM;
337             goto error;
338           }
339         } else {
340           fparg.dbl = GETARG(double);
341           dtoaresult = __hdtoa(fparg.dbl, xdigs, prec, &expt, &signflag, &dtoaend);
342           if (dtoaresult == nullptr) {
343             errno = ENOMEM;
344             goto error;
345           }
346         }
347         if (prec < 0) prec = dtoaend - dtoaresult;
348         if (expt == INT_MAX) ox[1] = '\0';
349         free(convbuf);
350         cp = convbuf = helpers::mbsconv(dtoaresult, -1);
351         if (cp == nullptr) goto error;
352         ndig = dtoaend - dtoaresult;
353         goto fp_common;
354       case 'e':
355       case 'E':
356         expchar = ch;
357         if (prec < 0) /* account for digit before decpt */
358           prec = DEFPREC + 1;
359         else
360           prec++;
361         goto fp_begin;
362       case 'f':
363       case 'F':
364         expchar = '\0';
365         goto fp_begin;
366       case 'g':
367       case 'G':
368         expchar = ch - ('g' - 'e');
369         if (prec == 0) prec = 1;
370       fp_begin:
371         if (prec < 0) prec = DEFPREC;
372         if (dtoaresult) __freedtoa(dtoaresult);
373         if (flags & LONGDBL) {
374           fparg.ldbl = GETARG(long double);
375           dtoaresult = __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend);
376           if (dtoaresult == nullptr) {
377             errno = ENOMEM;
378             goto error;
379           }
380         } else {
381           fparg.dbl = GETARG(double);
382           dtoaresult = __dtoa(fparg.dbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend);
383           if (dtoaresult == nullptr) {
384             errno = ENOMEM;
385             goto error;
386           }
387           if (expt == 9999) expt = INT_MAX;
388         }
389         free(convbuf);
390         cp = convbuf = helpers::mbsconv(dtoaresult, -1);
391         if (cp == nullptr) goto error;
392         ndig = dtoaend - dtoaresult;
393       fp_common:
394         if (signflag) sign = '-';
395         if (expt == INT_MAX) { /* inf or nan */
396           if (*cp == 'N') {
397             cp = const_cast<CHAR_TYPE*>((ch >= 'a') ? CHAR_TYPE_nan : CHAR_TYPE_NAN);
398           } else {
399             cp = const_cast<CHAR_TYPE*>((ch >= 'a') ? CHAR_TYPE_inf : CHAR_TYPE_INF);
400           }
401           size = 3;
402           flags &= ~ZEROPAD;
403           break;
404         }
405         flags |= FPT;
406         if (ch == 'g' || ch == 'G') {
407           if (expt > -4 && expt <= prec) {
408             /* Make %[gG] smell like %[fF] */
409             expchar = '\0';
410             if (flags & ALT)
411               prec -= expt;
412             else
413               prec = ndig - expt;
414             if (prec < 0) prec = 0;
415           } else {
416             /*
417              * Make %[gG] smell like %[eE], but
418              * trim trailing zeroes if no # flag.
419              */
420             if (!(flags & ALT)) prec = ndig;
421           }
422         }
423         if (expchar) {
424           expsize = exponent(expstr, expt - 1, expchar);
425           size = expsize + prec;
426           if (prec > 1 || flags & ALT) ++size;
427         } else {
428           /* space for digits before decimal point */
429           if (expt > 0)
430             size = expt;
431           else /* "0" */
432             size = 1;
433           /* space for decimal pt and following digits */
434           if (prec || flags & ALT) size += prec + 1;
435           lead = expt;
436         }
437         break;
438       case 'n':
439         __fortify_fatal("%%n not allowed on Android");
440       case 'm':
441         free(convbuf);
442         convbuf = helpers::mbsconv(strerror_r(caller_errno,
443                                               reinterpret_cast<char*>(buf), sizeof(buf)), prec);
444         if (convbuf == nullptr) {
445             fp->_flags |= __SERR;
446             goto error;
447         } else {
448             cp = convbuf;
449         }
450         goto string;
451       case 'O':
452         flags |= LONGINT;
453         __BIONIC_FALLTHROUGH;
454       case 'o':
455         _umax = UARG();
456         base = OCT;
457         goto nosign;
458       case 'p':
459         /*
460          * ``The argument shall be a pointer to void.  The
461          * value of the pointer is converted to a sequence
462          * of printable characters, in an implementation-
463          * defined manner.''
464          *	-- ANSI X3J11
465          */
466         _umax = (u_long)GETARG(void*);
467         base = HEX;
468         xdigs = xdigs_lower;
469         ox[1] = 'x';
470         goto nosign;
471       case 'S':
472         flags |= LONGINT;
473         __BIONIC_FALLTHROUGH;
474       case 's':
475         if (flags & LONGINT) {
476           if ((cp = GETARG(wchar_t*)) == nullptr) cp = const_cast<wchar_t*>(L"(null)");
477         } else {
478           char* mbsarg;
479           if ((mbsarg = GETARG(char*)) == nullptr) mbsarg = const_cast<char*>("(null)");
480           free(convbuf);
481           convbuf = helpers::mbsconv(mbsarg, prec);
482           if (convbuf == nullptr) {
483             fp->_flags |= __SERR;
484             goto error;
485           } else {
486             cp = convbuf;
487           }
488         }
489   string:
490         if (prec >= 0) {
491           size = CHAR_TYPE_STRNLEN(cp, prec);
492         } else {
493           size_t len;
494 
495           if ((len = CHAR_TYPE_STRLEN(cp)) > INT_MAX) goto overflow;
496           size = (int)len;
497         }
498         sign = '\0';
499         break;
500       case 'U':
501         flags |= LONGINT;
502         __BIONIC_FALLTHROUGH;
503       case 'u':
504         _umax = UARG();
505         base = DEC;
506         goto nosign;
507       case 'X':
508         xdigs = xdigs_upper;
509         goto hex;
510       case 'x':
511         xdigs = xdigs_lower;
512       hex:
513         _umax = UARG();
514         base = HEX;
515         /* leading 0x/X only if non-zero */
516         if (flags & ALT && _umax != 0) ox[1] = ch;
517 
518         /* unsigned conversions */
519       nosign:
520         sign = '\0';
521         /*
522          * ``... diouXx conversions ... if a precision is
523          * specified, the 0 flag will be ignored.''
524          *	-- ANSI X3J11
525          */
526       number:
527         if ((dprec = prec) >= 0) flags &= ~ZEROPAD;
528 
529         /*
530          * ``The result of converting a zero value with an
531          * explicit precision of zero is no characters.''
532          *	-- ANSI X3J11
533          */
534         cp = buf + BUF;
535         if (_umax != 0 || prec != 0) {
536           /*
537            * Unsigned mod is hard, and unsigned mod
538            * by a constant is easier than that by
539            * a variable; hence this switch.
540            */
541           switch (base) {
542             case OCT:
543               do {
544                 *--cp = to_char(_umax & 7);
545                 _umax >>= 3;
546               } while (_umax);
547               /* handle octal leading 0 */
548               if (flags & ALT && *cp != '0') *--cp = '0';
549               break;
550 
551             case DEC:
552               /* many numbers are 1 digit */
553               while (_umax >= 10) {
554                 *--cp = to_char(_umax % 10);
555                 _umax /= 10;
556               }
557               *--cp = to_char(_umax);
558               break;
559 
560             case HEX:
561               do {
562                 *--cp = xdigs[_umax & 15];
563                 _umax >>= 4;
564               } while (_umax);
565               break;
566 
567             default:
568               abort();
569           }
570         }
571         size = buf + BUF - cp;
572         if (size > BUF) abort(); /* should never happen */
573         break;
574       default: /* "%?" prints ?, unless ? is NUL */
575         if (ch == '\0') goto done;
576         /* pretend it was %c with argument ch */
577         cp = buf;
578         *cp = ch;
579         size = 1;
580         sign = '\0';
581         break;
582     }
583 
584     /*
585      * All reasonable formats wind up here.  At this point, `cp'
586      * points to a string which (if not flags&LADJUST) should be
587      * padded out to `width' places.  If flags&ZEROPAD, it should
588      * first be prefixed by any sign or other prefix; otherwise,
589      * it should be blank padded before the prefix is emitted.
590      * After any left-hand padding and prefixing, emit zeroes
591      * required by a decimal %[diouxX] precision, then print the
592      * string proper, then emit zeroes required by any leftover
593      * floating precision; finally, if LADJUST, pad with blanks.
594      *
595      * Compute actual size, so we know how much to pad.
596      * size excludes decimal prec; realsz includes it.
597      */
598     realsz = dprec > size ? dprec : size;
599     if (sign) realsz++;
600     if (ox[1]) realsz += 2;
601 
602     /* right-adjusting blank padding */
603     if ((flags & (LADJUST | ZEROPAD)) == 0) PAD(width - realsz, blanks);
604 
605     /* prefix */
606     if (sign) PRINT(&sign, 1);
607     if (ox[1]) { /* ox[1] is either x, X, or \0 */
608       ox[0] = '0';
609       PRINT(ox, 2);
610     }
611 
612     /* right-adjusting zero padding */
613     if ((flags & (LADJUST | ZEROPAD)) == ZEROPAD) PAD(width - realsz, zeroes);
614 
615     /* leading zeroes from decimal precision */
616     PAD(dprec - size, zeroes);
617 
618     /* the string or number proper */
619     if ((flags & FPT) == 0) {
620       PRINT(cp, size);
621     } else { /* glue together f_p fragments */
622       if (decimal_point == nullptr) decimal_point = nl_langinfo(RADIXCHAR);
623       if (!expchar) { /* %[fF] or sufficiently short %[gG] */
624         if (expt <= 0) {
625           PRINT(zeroes, 1);
626           if (prec || flags & ALT) PRINT(decimal_point, 1);
627           PAD(-expt, zeroes);
628           /* already handled initial 0's */
629           prec += expt;
630         } else {
631           PRINTANDPAD(cp, convbuf + ndig, lead, zeroes);
632           cp += lead;
633           if (prec || flags & ALT) PRINT(decimal_point, 1);
634         }
635         PRINTANDPAD(cp, convbuf + ndig, prec, zeroes);
636       } else { /* %[eE] or sufficiently long %[gG] */
637         if (prec > 1 || flags & ALT) {
638           buf[0] = *cp++;
639           buf[1] = *decimal_point;
640           PRINT(buf, 2);
641           PRINT(cp, ndig - 1);
642           PAD(prec - ndig, zeroes);
643         } else { /* XeYYY */
644           PRINT(cp, 1);
645         }
646         PRINT(expstr, expsize);
647       }
648     }
649     /* left-adjusting padding (always blank) */
650     if (flags & LADJUST) PAD(width - realsz, blanks);
651 
652     /* finally, adjust ret */
653     if (width < realsz) width = realsz;
654     if (width > INT_MAX - ret) goto overflow;
655     ret += width;
656   }
657 done:
658 error:
659   va_end(orgap);
660   if (__sferror(fp)) ret = -1;
661   goto finish;
662 
663 overflow:
664   errno = ENOMEM;
665   ret = -1;
666 
667 finish:
668   free(convbuf);
669   if (dtoaresult) __freedtoa(dtoaresult);
670   if (argtable != nullptr && argtable != statargtable) {
671     munmap(argtable, argtablesiz);
672     argtable = nullptr;
673   }
674   return (ret);
675 }
676