• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*	$OpenBSD: vfprintf.c,v 1.71 2016/01/04 15:47:47 schwarze 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 char
35 #define FUNCTION_NAME __vfprintf
36 #define CHAR_TYPE_STRLEN strlen
37 #define CHAR_TYPE_STRNLEN strnlen
38 #define CHAR_TYPE_INF "INF"
39 #define CHAR_TYPE_inf "inf"
40 #define CHAR_TYPE_NAN "NAN"
41 #define CHAR_TYPE_nan "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     iovp->iov_base = (ptr);               \
121     iovp->iov_len = (len);                \
122     uio.uio_resid += (len);               \
123     iovp++;                               \
124     if (++uio.uio_iovcnt >= NIOV) {       \
125       if (helpers::sprint(fp, &uio)) goto error; \
126       iovp = iov;                         \
127     }                                     \
128   } while (0)
129 #define FLUSH()                                          \
130   do {                                                   \
131     if (uio.uio_resid && helpers::sprint(fp, &uio)) goto error; \
132     uio.uio_iovcnt = 0;                                  \
133     iovp = iov;                                          \
134   } while (0)
135 
136   _SET_ORIENTATION(fp, CHAR_TYPE_ORIENTATION);
137 
138   // Writing "" to a read only file returns EOF, not 0.
139   if (cantwrite(fp)) {
140     errno = EBADF;
141     return EOF;
142   }
143 
144   // Optimize writes to stderr and other unbuffered files).
145   if ((fp->_flags & (__SNBF | __SWR | __SRW)) == (__SNBF | __SWR) && fp->_file >= 0) {
146     return (__sbprintf(fp, fmt0, ap));
147   }
148 
149   CHAR_TYPE* fmt = const_cast<CHAR_TYPE*>(fmt0);
150   argtable = nullptr;
151   nextarg = 1;
152   va_copy(orgap, ap);
153   uio.uio_iov = iovp = iov;
154   uio.uio_resid = 0;
155   uio.uio_iovcnt = 0;
156   ret = 0;
157   convbuf = nullptr;
158 
159   /*
160    * Scan the format for conversions (`%' character).
161    */
162   for (;;) {
163     int ch;
164     for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) continue;
165     if (fmt != cp) {
166       ptrdiff_t m = fmt - cp;
167       if (m < 0 || m > INT_MAX - ret) goto overflow;
168       PRINT(cp, m);
169       ret += m;
170     }
171     if (ch == '\0') goto done;
172     fmt++; /* skip over '%' */
173 
174     flags = 0;
175     dprec = 0;
176     width = 0;
177     prec = -1;
178     sign = '\0';
179     ox[1] = '\0';
180 
181   rflag:
182     ch = *fmt++;
183   reswitch:
184     switch (ch) {
185       case ' ':
186         /*
187          * ``If the space and + flags both appear, the space
188          * flag will be ignored.''
189          *	-- ANSI X3J11
190          */
191         if (!sign) sign = ' ';
192         goto rflag;
193       case '#':
194         flags |= ALT;
195         goto rflag;
196       case '\'':
197         /* grouping not implemented */
198         goto rflag;
199       case '*':
200         /*
201          * ``A negative field width argument is taken as a
202          * - flag followed by a positive field width.''
203          *	-- ANSI X3J11
204          * They don't exclude field widths read from args.
205          */
206         GETASTER(width);
207         if (width >= 0) goto rflag;
208         if (width == INT_MIN) goto overflow;
209         width = -width;
210         __BIONIC_FALLTHROUGH;
211       case '-':
212         flags |= LADJUST;
213         goto rflag;
214       case '+':
215         sign = '+';
216         goto rflag;
217       case '.':
218         if ((ch = *fmt++) == '*') {
219           GETASTER(n);
220           prec = n < 0 ? -1 : n;
221           goto rflag;
222         }
223         n = 0;
224         while (is_digit(ch)) {
225           APPEND_DIGIT(n, ch);
226           ch = *fmt++;
227         }
228         if (ch == '$') {
229           nextarg = n;
230           if (argtable == nullptr) {
231             argtable = statargtable;
232             if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) {
233               ret = -1;
234               goto error;
235             }
236           }
237           goto rflag;
238         }
239         prec = n;
240         goto reswitch;
241       case '0':
242         /*
243          * ``Note that 0 is taken as a flag, not as the
244          * beginning of a field width.''
245          *	-- ANSI X3J11
246          */
247         flags |= ZEROPAD;
248         goto rflag;
249       case '1':
250       case '2':
251       case '3':
252       case '4':
253       case '5':
254       case '6':
255       case '7':
256       case '8':
257       case '9':
258         n = 0;
259         do {
260           APPEND_DIGIT(n, ch);
261           ch = *fmt++;
262         } while (is_digit(ch));
263         if (ch == '$') {
264           nextarg = n;
265           if (argtable == nullptr) {
266             argtable = statargtable;
267             if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) {
268               ret = -1;
269               goto error;
270             }
271           }
272           goto rflag;
273         }
274         width = n;
275         goto reswitch;
276       case 'L':
277         flags |= LONGDBL;
278         goto rflag;
279       case 'h':
280         if (*fmt == 'h') {
281           fmt++;
282           flags |= CHARINT;
283         } else {
284           flags |= SHORTINT;
285         }
286         goto rflag;
287       case 'j':
288         flags |= MAXINT;
289         goto rflag;
290       case 'l':
291         if (*fmt == 'l') {
292           fmt++;
293           flags |= LLONGINT;
294         } else {
295           flags |= LONGINT;
296         }
297         goto rflag;
298       case 'q':
299         flags |= LLONGINT;
300         goto rflag;
301       case 't':
302         flags |= PTRINT;
303         goto rflag;
304       case 'z':
305         flags |= SIZEINT;
306         goto rflag;
307       case 'C':
308         flags |= LONGINT;
309         __BIONIC_FALLTHROUGH;
310       case 'c':
311         if (flags & LONGINT) {
312           mbstate_t mbs;
313           size_t mbseqlen;
314 
315           memset(&mbs, 0, sizeof(mbs));
316           mbseqlen = wcrtomb(buf, (wchar_t)GETARG(wint_t), &mbs);
317           if (mbseqlen == (size_t)-1) {
318             ret = -1;
319             goto error;
320           }
321           cp = buf;
322           size = (int)mbseqlen;
323         } else {
324           *(cp = buf) = GETARG(int);
325           size = 1;
326         }
327         sign = '\0';
328         break;
329       case 'D':
330         flags |= LONGINT;
331         __BIONIC_FALLTHROUGH;
332       case 'd':
333       case 'i':
334         _umax = SARG();
335         if ((intmax_t)_umax < 0) {
336           _umax = -_umax;
337           sign = '-';
338         }
339         base = DEC;
340         goto number;
341       case 'a':
342       case 'A':
343         if (ch == 'a') {
344           ox[1] = 'x';
345           xdigs = xdigs_lower;
346           expchar = 'p';
347         } else {
348           ox[1] = 'X';
349           xdigs = xdigs_upper;
350           expchar = 'P';
351         }
352         if (prec >= 0) prec++;
353         if (dtoaresult) __freedtoa(dtoaresult);
354         if (flags & LONGDBL) {
355           fparg.ldbl = GETARG(long double);
356           dtoaresult = cp = __hldtoa(fparg.ldbl, xdigs, prec, &expt, &signflag, &dtoaend);
357           if (dtoaresult == nullptr) {
358             errno = ENOMEM;
359             goto error;
360           }
361         } else {
362           fparg.dbl = GETARG(double);
363           dtoaresult = cp = __hdtoa(fparg.dbl, xdigs, prec, &expt, &signflag, &dtoaend);
364           if (dtoaresult == nullptr) {
365             errno = ENOMEM;
366             goto error;
367           }
368         }
369         if (prec < 0) prec = dtoaend - dtoaresult;
370         if (expt == INT_MAX) ox[1] = '\0';
371         goto fp_common;
372       case 'e':
373       case 'E':
374         expchar = ch;
375         if (prec < 0) /* account for digit before decpt */
376           prec = DEFPREC + 1;
377         else
378           prec++;
379         goto fp_begin;
380       case 'f':
381       case 'F':
382         expchar = '\0';
383         goto fp_begin;
384       case 'g':
385       case 'G':
386         expchar = ch - ('g' - 'e');
387         if (prec == 0) prec = 1;
388       fp_begin:
389         if (prec < 0) prec = DEFPREC;
390         if (dtoaresult) __freedtoa(dtoaresult);
391         if (flags & LONGDBL) {
392           fparg.ldbl = GETARG(long double);
393           dtoaresult = cp = __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend);
394           if (dtoaresult == nullptr) {
395             errno = ENOMEM;
396             goto error;
397           }
398         } else {
399           fparg.dbl = GETARG(double);
400           dtoaresult = cp = __dtoa(fparg.dbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend);
401           if (dtoaresult == nullptr) {
402             errno = ENOMEM;
403             goto error;
404           }
405           if (expt == 9999) expt = INT_MAX;
406         }
407       fp_common:
408         if (signflag) sign = '-';
409         if (expt == INT_MAX) { /* inf or nan */
410           if (*cp == 'N') {
411             cp = const_cast<CHAR_TYPE*>((ch >= 'a') ? CHAR_TYPE_nan : CHAR_TYPE_NAN);
412           } else {
413             cp = const_cast<CHAR_TYPE*>((ch >= 'a') ? CHAR_TYPE_inf : CHAR_TYPE_INF);
414           }
415           size = 3;
416           flags &= ~ZEROPAD;
417           break;
418         }
419         flags |= FPT;
420         ndig = dtoaend - cp;
421         if (ch == 'g' || ch == 'G') {
422           if (expt > -4 && expt <= prec) {
423             /* Make %[gG] smell like %[fF] */
424             expchar = '\0';
425             if (flags & ALT)
426               prec -= expt;
427             else
428               prec = ndig - expt;
429             if (prec < 0) prec = 0;
430           } else {
431             /*
432              * Make %[gG] smell like %[eE], but
433              * trim trailing zeroes if no # flag.
434              */
435             if (!(flags & ALT)) prec = ndig;
436           }
437         }
438         if (expchar) {
439           expsize = exponent(expstr, expt - 1, expchar);
440           size = expsize + prec;
441           if (prec > 1 || flags & ALT) ++size;
442         } else {
443           /* space for digits before decimal point */
444           if (expt > 0)
445             size = expt;
446           else /* "0" */
447             size = 1;
448           /* space for decimal pt and following digits */
449           if (prec || flags & ALT) size += prec + 1;
450           lead = expt;
451         }
452         break;
453       case 'n':
454         __fortify_fatal("%%n not allowed on Android");
455       case 'm':
456         cp = strerror_r(caller_errno, buf, sizeof(buf));
457         goto string;
458       case 'O':
459         flags |= LONGINT;
460         __BIONIC_FALLTHROUGH;
461       case 'o':
462         _umax = UARG();
463         base = OCT;
464         goto nosign;
465       case 'p':
466         /*
467          * ``The argument shall be a pointer to void.  The
468          * value of the pointer is converted to a sequence
469          * of printable characters, in an implementation-
470          * defined manner.''
471          *	-- ANSI X3J11
472          */
473         _umax = (u_long)GETARG(void*);
474         base = HEX;
475         xdigs = xdigs_lower;
476         ox[1] = 'x';
477         goto nosign;
478       case 'S':
479         flags |= LONGINT;
480         __BIONIC_FALLTHROUGH;
481       case 's':
482         if (flags & LONGINT) {
483           wchar_t* wcp;
484 
485           free(convbuf);
486           convbuf = nullptr;
487           if ((wcp = GETARG(wchar_t*)) == nullptr) {
488             cp = const_cast<char*>("(null)");
489           } else {
490             convbuf = helpers::wcsconv(wcp, prec);
491             if (convbuf == nullptr) {
492               ret = -1;
493               goto error;
494             }
495             cp = convbuf;
496           }
497         } else if ((cp = GETARG(char*)) == nullptr) {
498           cp = const_cast<char*>("(null)");
499         }
500   string:
501         if (prec >= 0) {
502           size = CHAR_TYPE_STRNLEN(cp, prec);
503         } else {
504           size_t len;
505 
506           if ((len = CHAR_TYPE_STRLEN(cp)) > INT_MAX) goto overflow;
507           size = (int)len;
508         }
509         sign = '\0';
510         break;
511       case 'U':
512         flags |= LONGINT;
513         __BIONIC_FALLTHROUGH;
514       case 'u':
515         _umax = UARG();
516         base = DEC;
517         goto nosign;
518       case 'X':
519         xdigs = xdigs_upper;
520         goto hex;
521       case 'x':
522         xdigs = xdigs_lower;
523       hex:
524         _umax = UARG();
525         base = HEX;
526         /* leading 0x/X only if non-zero */
527         if (flags & ALT && _umax != 0) ox[1] = ch;
528 
529         /* unsigned conversions */
530       nosign:
531         sign = '\0';
532         /*
533          * ``... diouXx conversions ... if a precision is
534          * specified, the 0 flag will be ignored.''
535          *	-- ANSI X3J11
536          */
537       number:
538         if ((dprec = prec) >= 0) flags &= ~ZEROPAD;
539 
540         /*
541          * ``The result of converting a zero value with an
542          * explicit precision of zero is no characters.''
543          *	-- ANSI X3J11
544          */
545         cp = buf + BUF;
546         if (_umax != 0 || prec != 0) {
547           /*
548            * Unsigned mod is hard, and unsigned mod
549            * by a constant is easier than that by
550            * a variable; hence this switch.
551            */
552           switch (base) {
553             case OCT:
554               do {
555                 *--cp = to_char(_umax & 7);
556                 _umax >>= 3;
557               } while (_umax);
558               /* handle octal leading 0 */
559               if (flags & ALT && *cp != '0') *--cp = '0';
560               break;
561 
562             case DEC:
563               /* many numbers are 1 digit */
564               while (_umax >= 10) {
565                 *--cp = to_char(_umax % 10);
566                 _umax /= 10;
567               }
568               *--cp = to_char(_umax);
569               break;
570 
571             case HEX:
572               do {
573                 *--cp = xdigs[_umax & 15];
574                 _umax >>= 4;
575               } while (_umax);
576               break;
577 
578             default:
579               abort();
580           }
581         }
582         size = buf + BUF - cp;
583         if (size > BUF) abort(); /* should never happen */
584         break;
585       default: /* "%?" prints ?, unless ? is NUL */
586         if (ch == '\0') goto done;
587         /* pretend it was %c with argument ch */
588         cp = buf;
589         *cp = ch;
590         size = 1;
591         sign = '\0';
592         break;
593     }
594 
595     /*
596      * All reasonable formats wind up here.  At this point, `cp'
597      * points to a string which (if not flags&LADJUST) should be
598      * padded out to `width' places.  If flags&ZEROPAD, it should
599      * first be prefixed by any sign or other prefix; otherwise,
600      * it should be blank padded before the prefix is emitted.
601      * After any left-hand padding and prefixing, emit zeroes
602      * required by a decimal %[diouxX] precision, then print the
603      * string proper, then emit zeroes required by any leftover
604      * floating precision; finally, if LADJUST, pad with blanks.
605      *
606      * Compute actual size, so we know how much to pad.
607      * size excludes decimal prec; realsz includes it.
608      */
609     realsz = dprec > size ? dprec : size;
610     if (sign) realsz++;
611     if (ox[1]) realsz += 2;
612 
613     /* right-adjusting blank padding */
614     if ((flags & (LADJUST | ZEROPAD)) == 0) PAD(width - realsz, blanks);
615 
616     /* prefix */
617     if (sign) PRINT(&sign, 1);
618     if (ox[1]) { /* ox[1] is either x, X, or \0 */
619       ox[0] = '0';
620       PRINT(ox, 2);
621     }
622 
623     /* right-adjusting zero padding */
624     if ((flags & (LADJUST | ZEROPAD)) == ZEROPAD) PAD(width - realsz, zeroes);
625 
626     /* leading zeroes from decimal precision */
627     PAD(dprec - size, zeroes);
628 
629     /* the string or number proper */
630     if ((flags & FPT) == 0) {
631       PRINT(cp, size);
632     } else { /* glue together f_p fragments */
633       if (decimal_point == nullptr) decimal_point = nl_langinfo(RADIXCHAR);
634       if (!expchar) { /* %[fF] or sufficiently short %[gG] */
635         if (expt <= 0) {
636           PRINT(zeroes, 1);
637           if (prec || flags & ALT) PRINT(decimal_point, 1);
638           PAD(-expt, zeroes);
639           /* already handled initial 0's */
640           prec += expt;
641         } else {
642           PRINTANDPAD(cp, dtoaend, lead, zeroes);
643           cp += lead;
644           if (prec || flags & ALT) PRINT(decimal_point, 1);
645         }
646         PRINTANDPAD(cp, dtoaend, prec, zeroes);
647       } else { /* %[eE] or sufficiently long %[gG] */
648         if (prec > 1 || flags & ALT) {
649           buf[0] = *cp++;
650           buf[1] = *decimal_point;
651           PRINT(buf, 2);
652           PRINT(cp, ndig - 1);
653           PAD(prec - ndig, zeroes);
654         } else { /* XeYYY */
655           PRINT(cp, 1);
656         }
657         PRINT(expstr, expsize);
658       }
659     }
660     /* left-adjusting padding (always blank) */
661     if (flags & LADJUST) PAD(width - realsz, blanks);
662 
663     /* finally, adjust ret */
664     if (width < realsz) width = realsz;
665     if (width > INT_MAX - ret) goto overflow;
666     ret += width;
667 
668     FLUSH(); /* copy out the I/O vectors */
669   }
670 done:
671   FLUSH();
672 error:
673   va_end(orgap);
674   if (__sferror(fp)) ret = -1;
675   goto finish;
676 
677 overflow:
678   errno = ENOMEM;
679   ret = -1;
680 
681 finish:
682   free(convbuf);
683   if (dtoaresult) __freedtoa(dtoaresult);
684   if (argtable != nullptr && argtable != statargtable) {
685     munmap(argtable, argtablesiz);
686     argtable = nullptr;
687   }
688   return (ret);
689 }
690