blob: 2f86b3c2202a42ef18b388d332d31ae0c8d6befc [file] [log] [blame]
Elliott Hughes506c6de2016-01-15 16:30:18 -08001/* $OpenBSD: vfwprintf.c,v 1.15 2015/12/28 22:08:18 mmcc Exp $ */
Elliott Hughes94336d82014-04-29 17:39:29 -07002/*-
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
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -070034#define CHAR_TYPE wchar_t
Elliott Hughes93a1f8b2017-11-07 22:52:29 -080035#define FUNCTION_NAME __vfwprintf
Elliott Hughesbc27bdc2017-11-10 15:25:49 -080036#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
Elliott Hughes26a0ebd2023-05-09 11:52:16 -070043
44#define PRINT(ptr, len) \
45 do { \
46 for (int n3 = 0; n3 < (len); n3++) { \
47 if ((helpers::xfputwc((ptr)[n3], fp)) == WEOF) goto error; \
48 } \
49 } while (0)
50
51#define FLUSH()
52
Elliott Hughes1f493172017-11-08 16:13:18 -080053#include "printf_common.h"
Elliott Hughes94336d82014-04-29 17:39:29 -070054
Elliott Hughes1f493172017-11-08 16:13:18 -080055int FUNCTION_NAME(FILE* fp, const CHAR_TYPE* fmt0, va_list ap) {
Elliott Hughes654cd832018-08-30 16:00:42 -070056 int caller_errno = errno;
Elliott Hughesb70576b2017-11-13 11:10:05 -080057 int n, n2;
Elliott Hughes93a1f8b2017-11-07 22:52:29 -080058 CHAR_TYPE* cp; /* handy char pointer (short term usage) */
59 CHAR_TYPE sign; /* sign prefix (' ', '+', '-', or \0) */
Elliott Hughesc8f2c522017-10-31 13:07:51 -070060 int flags; /* flags as above */
61 int ret; /* return value accumulator */
62 int width; /* width from format (%8d), or 0 */
63 int prec; /* precision from format; <0 for N/A */
Elliott Hughesc8f2c522017-10-31 13:07:51 -070064 /*
65 * We can decompose the printed representation of floating
66 * point numbers into several parts, some of which may be empty:
67 *
68 * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ
69 * A B ---C--- D E F
70 *
71 * A: 'sign' holds this value if present; '\0' otherwise
72 * B: ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal
73 * C: cp points to the string MMMNNN. Leading and trailing
74 * zeros are not in the string and must be added.
75 * D: expchar holds this character; '\0' if no exponent, e.g. %f
76 * F: at least two digits for decimal, at least one digit for hex
77 */
Yi Kong32bc0fc2018-08-02 17:31:13 -070078 char* decimal_point = nullptr;
Elliott Hughesc8f2c522017-10-31 13:07:51 -070079 int signflag; /* true if float is negative */
80 union { /* floating point arguments %[aAeEfFgG] */
81 double dbl;
82 long double ldbl;
83 } fparg;
84 int expt; /* integer value of exponent */
85 char expchar; /* exponent character: [eEpP\0] */
86 char* dtoaend; /* pointer to end of converted digits */
87 int expsize; /* character count for expstr */
88 int lead; /* sig figs before decimal or group sep */
89 int ndig; /* actual number of digits returned by dtoa */
Elliott Hughes93a1f8b2017-11-07 22:52:29 -080090 CHAR_TYPE expstr[MAXEXPDIG + 2]; /* buffer for exponent string: e+ZZZ */
Yi Kong32bc0fc2018-08-02 17:31:13 -070091 char* dtoaresult = nullptr;
Elliott Hughes94336d82014-04-29 17:39:29 -070092
Elliott Hughesc8f2c522017-10-31 13:07:51 -070093 uintmax_t _umax; /* integer arguments %[diouxX] */
Elliott Hughesb813a6a2022-08-01 22:18:40 +000094 enum { BIN, OCT, DEC, HEX } base; /* base for %[bBdiouxX] conversion */
95 int dprec; /* a copy of prec if %[bBdiouxX], 0 otherwise */
Elliott Hughesc8f2c522017-10-31 13:07:51 -070096 int realsz; /* field size expanded by dprec */
97 int size; /* size of converted field or string */
98 const char* xdigs; /* digits for %[xX] conversion */
Elliott Hughesb70576b2017-11-13 11:10:05 -080099#define NIOV 8
100 struct __suio uio; /* output information: summary */
101 struct __siov iov[NIOV]; /* ... and individual io vectors */
102 struct __siov* iovp; /* for PRINT macro */
Elliott Hughes93a1f8b2017-11-07 22:52:29 -0800103 CHAR_TYPE buf[BUF]; /* buffer with space for digits of uintmax_t */
104 CHAR_TYPE ox[2]; /* space for 0x; ox[1] is either x, X, or \0 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700105 union arg* argtable; /* args, built due to positional arg */
106 union arg statargtable[STATIC_ARG_TBL_SIZE];
107 size_t argtablesiz;
108 int nextarg; /* 1-based argument index */
109 va_list orgap; /* original argument pointer */
Elliott Hughes93a1f8b2017-11-07 22:52:29 -0800110 CHAR_TYPE* convbuf; /* buffer for wide/multibyte conversion */
Elliott Hughes94336d82014-04-29 17:39:29 -0700111
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700112 /*
113 * Choose PADSIZE to trade efficiency vs. size. If larger printf
114 * fields occur frequently, increase PADSIZE and make the initialisers
115 * below longer.
116 */
117#define PADSIZE 16 /* pad chunk size */
Elliott Hughes5305a4d2017-11-03 14:00:37 -0700118 static CHAR_TYPE blanks[PADSIZE] = {
119 ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '
120 };
121 static CHAR_TYPE zeroes[PADSIZE] = {
122 '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'
123 };
Elliott Hughes94336d82014-04-29 17:39:29 -0700124
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700125 static const char xdigs_lower[] = "0123456789abcdef";
126 static const char xdigs_upper[] = "0123456789ABCDEF";
Elliott Hughes94336d82014-04-29 17:39:29 -0700127
Elliott Hughesbc27bdc2017-11-10 15:25:49 -0800128 _SET_ORIENTATION(fp, CHAR_TYPE_ORIENTATION);
Elliott Hughes5305a4d2017-11-03 14:00:37 -0700129
130 // Writing "" to a read only file returns EOF, not 0.
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700131 if (cantwrite(fp)) {
132 errno = EBADF;
Elliott Hughes5305a4d2017-11-03 14:00:37 -0700133 return EOF;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700134 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700135
Elliott Hughes5305a4d2017-11-03 14:00:37 -0700136 // Optimize writes to stderr and other unbuffered files).
137 if ((fp->_flags & (__SNBF | __SWR | __SRW)) == (__SNBF | __SWR) && fp->_file >= 0) {
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700138 return (__sbprintf(fp, fmt0, ap));
Elliott Hughes5305a4d2017-11-03 14:00:37 -0700139 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700140
Elliott Hughes5305a4d2017-11-03 14:00:37 -0700141 CHAR_TYPE* fmt = const_cast<CHAR_TYPE*>(fmt0);
Yi Kong32bc0fc2018-08-02 17:31:13 -0700142 argtable = nullptr;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700143 nextarg = 1;
144 va_copy(orgap, ap);
Elliott Hughesb70576b2017-11-13 11:10:05 -0800145 uio.uio_iov = iovp = iov;
146 uio.uio_resid = 0;
147 uio.uio_iovcnt = 0;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700148 ret = 0;
Yi Kong32bc0fc2018-08-02 17:31:13 -0700149 convbuf = nullptr;
Elliott Hughes94336d82014-04-29 17:39:29 -0700150
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700151 /*
152 * Scan the format for conversions (`%' character).
153 */
154 for (;;) {
Elliott Hughesb70576b2017-11-13 11:10:05 -0800155 int ch;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700156 for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) continue;
157 if (fmt != cp) {
158 ptrdiff_t m = fmt - cp;
159 if (m < 0 || m > INT_MAX - ret) goto overflow;
160 PRINT(cp, m);
161 ret += m;
162 }
163 if (ch == '\0') goto done;
164 fmt++; /* skip over '%' */
Elliott Hughes94336d82014-04-29 17:39:29 -0700165
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700166 flags = 0;
167 dprec = 0;
168 width = 0;
169 prec = -1;
170 sign = '\0';
171 ox[1] = '\0';
Elliott Hughes94336d82014-04-29 17:39:29 -0700172
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700173 rflag:
174 ch = *fmt++;
175 reswitch:
176 switch (ch) {
177 case ' ':
178 /*
179 * ``If the space and + flags both appear, the space
180 * flag will be ignored.''
181 * -- ANSI X3J11
182 */
183 if (!sign) sign = ' ';
184 goto rflag;
185 case '#':
186 flags |= ALT;
187 goto rflag;
188 case '\'':
189 /* grouping not implemented */
190 goto rflag;
191 case '*':
192 /*
193 * ``A negative field width argument is taken as a
194 * - flag followed by a positive field width.''
195 * -- ANSI X3J11
196 * They don't exclude field widths read from args.
197 */
198 GETASTER(width);
199 if (width >= 0) goto rflag;
200 if (width == INT_MIN) goto overflow;
201 width = -width;
George Burgess IVfa5410f2018-08-13 17:44:06 -0700202 __BIONIC_FALLTHROUGH;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700203 case '-':
204 flags |= LADJUST;
205 goto rflag;
206 case '+':
207 sign = '+';
208 goto rflag;
209 case '.':
210 if ((ch = *fmt++) == '*') {
211 GETASTER(n);
212 prec = n < 0 ? -1 : n;
213 goto rflag;
214 }
215 n = 0;
216 while (is_digit(ch)) {
217 APPEND_DIGIT(n, ch);
218 ch = *fmt++;
219 }
220 if (ch == '$') {
221 nextarg = n;
Yi Kong32bc0fc2018-08-02 17:31:13 -0700222 if (argtable == nullptr) {
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700223 argtable = statargtable;
Elliott Hughes5305a4d2017-11-03 14:00:37 -0700224 if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) {
225 ret = -1;
226 goto error;
227 }
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700228 }
229 goto rflag;
230 }
231 prec = n;
232 goto reswitch;
233 case '0':
234 /*
235 * ``Note that 0 is taken as a flag, not as the
236 * beginning of a field width.''
237 * -- ANSI X3J11
238 */
239 flags |= ZEROPAD;
240 goto rflag;
241 case '1':
242 case '2':
243 case '3':
244 case '4':
245 case '5':
246 case '6':
247 case '7':
248 case '8':
249 case '9':
250 n = 0;
251 do {
252 APPEND_DIGIT(n, ch);
253 ch = *fmt++;
254 } while (is_digit(ch));
255 if (ch == '$') {
256 nextarg = n;
Yi Kong32bc0fc2018-08-02 17:31:13 -0700257 if (argtable == nullptr) {
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700258 argtable = statargtable;
Elliott Hughes5305a4d2017-11-03 14:00:37 -0700259 if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) {
260 ret = -1;
261 goto error;
262 }
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700263 }
264 goto rflag;
265 }
266 width = n;
267 goto reswitch;
268 case 'L':
269 flags |= LONGDBL;
270 goto rflag;
271 case 'h':
272 if (*fmt == 'h') {
273 fmt++;
274 flags |= CHARINT;
275 } else {
276 flags |= SHORTINT;
277 }
278 goto rflag;
279 case 'j':
280 flags |= MAXINT;
281 goto rflag;
282 case 'l':
283 if (*fmt == 'l') {
284 fmt++;
285 flags |= LLONGINT;
286 } else {
287 flags |= LONGINT;
288 }
289 goto rflag;
290 case 'q':
291 flags |= LLONGINT;
292 goto rflag;
293 case 't':
294 flags |= PTRINT;
295 goto rflag;
296 case 'z':
297 flags |= SIZEINT;
298 goto rflag;
Elliott Hughesb813a6a2022-08-01 22:18:40 +0000299 case 'B':
300 case 'b':
301 _umax = UARG();
302 base = BIN;
303 if (flags & ALT && _umax != 0) ox[1] = ch;
304 goto nosign;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700305 case 'C':
306 flags |= LONGINT;
George Burgess IVfa5410f2018-08-13 17:44:06 -0700307 __BIONIC_FALLTHROUGH;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700308 case 'c':
309 if (flags & LONGINT)
310 *(cp = buf) = (wchar_t)GETARG(wint_t);
311 else
312 *(cp = buf) = (wchar_t)btowc(GETARG(int));
313 size = 1;
314 sign = '\0';
315 break;
316 case 'D':
317 flags |= LONGINT;
George Burgess IVfa5410f2018-08-13 17:44:06 -0700318 __BIONIC_FALLTHROUGH;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700319 case 'd':
320 case 'i':
321 _umax = SARG();
322 if ((intmax_t)_umax < 0) {
323 _umax = -_umax;
324 sign = '-';
325 }
326 base = DEC;
327 goto number;
328 case 'a':
329 case 'A':
330 if (ch == 'a') {
331 ox[1] = 'x';
332 xdigs = xdigs_lower;
333 expchar = 'p';
334 } else {
335 ox[1] = 'X';
336 xdigs = xdigs_upper;
337 expchar = 'P';
338 }
339 if (prec >= 0) prec++;
340 if (dtoaresult) __freedtoa(dtoaresult);
341 if (flags & LONGDBL) {
342 fparg.ldbl = GETARG(long double);
343 dtoaresult = __hldtoa(fparg.ldbl, xdigs, prec, &expt, &signflag, &dtoaend);
Yi Kong32bc0fc2018-08-02 17:31:13 -0700344 if (dtoaresult == nullptr) {
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700345 errno = ENOMEM;
346 goto error;
347 }
348 } else {
349 fparg.dbl = GETARG(double);
350 dtoaresult = __hdtoa(fparg.dbl, xdigs, prec, &expt, &signflag, &dtoaend);
Yi Kong32bc0fc2018-08-02 17:31:13 -0700351 if (dtoaresult == nullptr) {
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700352 errno = ENOMEM;
353 goto error;
354 }
355 }
356 if (prec < 0) prec = dtoaend - dtoaresult;
357 if (expt == INT_MAX) ox[1] = '\0';
358 free(convbuf);
Elliott Hughesbc27bdc2017-11-10 15:25:49 -0800359 cp = convbuf = helpers::mbsconv(dtoaresult, -1);
Yi Kong32bc0fc2018-08-02 17:31:13 -0700360 if (cp == nullptr) goto error;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700361 ndig = dtoaend - dtoaresult;
362 goto fp_common;
363 case 'e':
364 case 'E':
365 expchar = ch;
366 if (prec < 0) /* account for digit before decpt */
367 prec = DEFPREC + 1;
368 else
369 prec++;
370 goto fp_begin;
371 case 'f':
372 case 'F':
373 expchar = '\0';
374 goto fp_begin;
375 case 'g':
376 case 'G':
377 expchar = ch - ('g' - 'e');
378 if (prec == 0) prec = 1;
379 fp_begin:
380 if (prec < 0) prec = DEFPREC;
381 if (dtoaresult) __freedtoa(dtoaresult);
382 if (flags & LONGDBL) {
383 fparg.ldbl = GETARG(long double);
384 dtoaresult = __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend);
Yi Kong32bc0fc2018-08-02 17:31:13 -0700385 if (dtoaresult == nullptr) {
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700386 errno = ENOMEM;
387 goto error;
388 }
389 } else {
390 fparg.dbl = GETARG(double);
391 dtoaresult = __dtoa(fparg.dbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend);
Yi Kong32bc0fc2018-08-02 17:31:13 -0700392 if (dtoaresult == nullptr) {
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700393 errno = ENOMEM;
394 goto error;
395 }
396 if (expt == 9999) expt = INT_MAX;
397 }
398 free(convbuf);
Elliott Hughesbc27bdc2017-11-10 15:25:49 -0800399 cp = convbuf = helpers::mbsconv(dtoaresult, -1);
Yi Kong32bc0fc2018-08-02 17:31:13 -0700400 if (cp == nullptr) goto error;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700401 ndig = dtoaend - dtoaresult;
402 fp_common:
403 if (signflag) sign = '-';
404 if (expt == INT_MAX) { /* inf or nan */
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700405 if (*cp == 'N') {
Elliott Hughesbc27bdc2017-11-10 15:25:49 -0800406 cp = const_cast<CHAR_TYPE*>((ch >= 'a') ? CHAR_TYPE_nan : CHAR_TYPE_NAN);
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700407 } else {
Elliott Hughesbc27bdc2017-11-10 15:25:49 -0800408 cp = const_cast<CHAR_TYPE*>((ch >= 'a') ? CHAR_TYPE_inf : CHAR_TYPE_INF);
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700409 }
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700410 size = 3;
411 flags &= ~ZEROPAD;
412 break;
413 }
414 flags |= FPT;
415 if (ch == 'g' || ch == 'G') {
416 if (expt > -4 && expt <= prec) {
417 /* Make %[gG] smell like %[fF] */
418 expchar = '\0';
419 if (flags & ALT)
420 prec -= expt;
421 else
422 prec = ndig - expt;
423 if (prec < 0) prec = 0;
424 } else {
425 /*
426 * Make %[gG] smell like %[eE], but
427 * trim trailing zeroes if no # flag.
428 */
429 if (!(flags & ALT)) prec = ndig;
430 }
431 }
432 if (expchar) {
433 expsize = exponent(expstr, expt - 1, expchar);
434 size = expsize + prec;
435 if (prec > 1 || flags & ALT) ++size;
436 } else {
437 /* space for digits before decimal point */
438 if (expt > 0)
439 size = expt;
440 else /* "0" */
441 size = 1;
442 /* space for decimal pt and following digits */
443 if (prec || flags & ALT) size += prec + 1;
444 lead = expt;
445 }
446 break;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700447 case 'n':
Elliott Hughes41398d02018-03-07 13:32:58 -0800448 __fortify_fatal("%%n not allowed on Android");
Elliott Hughes654cd832018-08-30 16:00:42 -0700449 case 'm':
450 free(convbuf);
Elliott Hughesf340a562018-09-06 10:42:40 -0700451 convbuf = helpers::mbsconv(strerror_r(caller_errno,
452 reinterpret_cast<char*>(buf), sizeof(buf)), prec);
Elliott Hughes654cd832018-08-30 16:00:42 -0700453 if (convbuf == nullptr) {
454 fp->_flags |= __SERR;
455 goto error;
456 } else {
457 cp = convbuf;
458 }
459 goto string;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700460 case 'O':
461 flags |= LONGINT;
George Burgess IVfa5410f2018-08-13 17:44:06 -0700462 __BIONIC_FALLTHROUGH;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700463 case 'o':
464 _umax = UARG();
465 base = OCT;
466 goto nosign;
467 case 'p':
468 /*
469 * ``The argument shall be a pointer to void. The
470 * value of the pointer is converted to a sequence
471 * of printable characters, in an implementation-
472 * defined manner.''
473 * -- ANSI X3J11
474 */
475 _umax = (u_long)GETARG(void*);
476 base = HEX;
477 xdigs = xdigs_lower;
478 ox[1] = 'x';
479 goto nosign;
480 case 'S':
481 flags |= LONGINT;
George Burgess IVfa5410f2018-08-13 17:44:06 -0700482 __BIONIC_FALLTHROUGH;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700483 case 's':
484 if (flags & LONGINT) {
Yi Kong32bc0fc2018-08-02 17:31:13 -0700485 if ((cp = GETARG(wchar_t*)) == nullptr) cp = const_cast<wchar_t*>(L"(null)");
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700486 } else {
487 char* mbsarg;
Yi Kong32bc0fc2018-08-02 17:31:13 -0700488 if ((mbsarg = GETARG(char*)) == nullptr) mbsarg = const_cast<char*>("(null)");
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700489 free(convbuf);
Elliott Hughesbc27bdc2017-11-10 15:25:49 -0800490 convbuf = helpers::mbsconv(mbsarg, prec);
Yi Kong32bc0fc2018-08-02 17:31:13 -0700491 if (convbuf == nullptr) {
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700492 fp->_flags |= __SERR;
493 goto error;
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700494 } else {
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700495 cp = convbuf;
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700496 }
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700497 }
Elliott Hughes654cd832018-08-30 16:00:42 -0700498 string:
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700499 if (prec >= 0) {
Elliott Hughesbc27bdc2017-11-10 15:25:49 -0800500 size = CHAR_TYPE_STRNLEN(cp, prec);
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700501 } else {
502 size_t len;
Elliott Hughes94336d82014-04-29 17:39:29 -0700503
Elliott Hughesbc27bdc2017-11-10 15:25:49 -0800504 if ((len = CHAR_TYPE_STRLEN(cp)) > INT_MAX) goto overflow;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700505 size = (int)len;
506 }
507 sign = '\0';
508 break;
509 case 'U':
510 flags |= LONGINT;
George Burgess IVfa5410f2018-08-13 17:44:06 -0700511 __BIONIC_FALLTHROUGH;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700512 case 'u':
513 _umax = UARG();
514 base = DEC;
515 goto nosign;
zijunzhao1fdece92023-04-25 17:37:19 +0000516 case 'w': {
zijunzhao3b846ea2023-04-11 20:53:39 +0000517 n = 0;
zijunzhao1fdece92023-04-25 17:37:19 +0000518 bool fast = false;
zijunzhao3b846ea2023-04-11 20:53:39 +0000519 ch = *fmt++;
zijunzhao1fdece92023-04-25 17:37:19 +0000520 if (ch == 'f') {
521 fast = true;
522 ch = *fmt++;
523 }
zijunzhao3b846ea2023-04-11 20:53:39 +0000524 while (is_digit(ch)) {
525 APPEND_DIGIT(n, ch);
526 ch = *fmt++;
527 }
zijunzhao1fdece92023-04-25 17:37:19 +0000528 flags |= helpers::w_to_flag(n, fast);
529 goto reswitch;
530 }
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700531 case 'X':
532 xdigs = xdigs_upper;
533 goto hex;
534 case 'x':
535 xdigs = xdigs_lower;
536 hex:
537 _umax = UARG();
538 base = HEX;
539 /* leading 0x/X only if non-zero */
540 if (flags & ALT && _umax != 0) ox[1] = ch;
Elliott Hughes94336d82014-04-29 17:39:29 -0700541
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700542 /* unsigned conversions */
543 nosign:
544 sign = '\0';
545 /*
546 * ``... diouXx conversions ... if a precision is
547 * specified, the 0 flag will be ignored.''
548 * -- ANSI X3J11
549 */
550 number:
551 if ((dprec = prec) >= 0) flags &= ~ZEROPAD;
Elliott Hughes94336d82014-04-29 17:39:29 -0700552
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700553 /*
554 * ``The result of converting a zero value with an
555 * explicit precision of zero is no characters.''
556 * -- ANSI X3J11
557 */
558 cp = buf + BUF;
559 if (_umax != 0 || prec != 0) {
560 /*
561 * Unsigned mod is hard, and unsigned mod
562 * by a constant is easier than that by
563 * a variable; hence this switch.
564 */
565 switch (base) {
Elliott Hughesb813a6a2022-08-01 22:18:40 +0000566 case BIN:
567 do {
568 *--cp = to_char(_umax & 1);
569 _umax >>= 1;
570 } while (_umax);
571 break;
572
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700573 case OCT:
574 do {
575 *--cp = to_char(_umax & 7);
576 _umax >>= 3;
577 } while (_umax);
578 /* handle octal leading 0 */
579 if (flags & ALT && *cp != '0') *--cp = '0';
580 break;
Elliott Hughes94336d82014-04-29 17:39:29 -0700581
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700582 case DEC:
583 /* many numbers are 1 digit */
584 while (_umax >= 10) {
585 *--cp = to_char(_umax % 10);
586 _umax /= 10;
587 }
588 *--cp = to_char(_umax);
589 break;
Elliott Hughes94336d82014-04-29 17:39:29 -0700590
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700591 case HEX:
592 do {
593 *--cp = xdigs[_umax & 15];
594 _umax >>= 4;
595 } while (_umax);
596 break;
Elliott Hughes94336d82014-04-29 17:39:29 -0700597
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700598 default:
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700599 abort();
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700600 }
601 }
602 size = buf + BUF - cp;
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700603 if (size > BUF) abort(); /* should never happen */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700604 break;
605 default: /* "%?" prints ?, unless ? is NUL */
606 if (ch == '\0') goto done;
607 /* pretend it was %c with argument ch */
608 cp = buf;
609 *cp = ch;
610 size = 1;
611 sign = '\0';
612 break;
613 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700614
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700615 /*
616 * All reasonable formats wind up here. At this point, `cp'
617 * points to a string which (if not flags&LADJUST) should be
618 * padded out to `width' places. If flags&ZEROPAD, it should
619 * first be prefixed by any sign or other prefix; otherwise,
620 * it should be blank padded before the prefix is emitted.
621 * After any left-hand padding and prefixing, emit zeroes
Elliott Hughesb813a6a2022-08-01 22:18:40 +0000622 * required by a decimal %[bBdiouxX] precision, then print the
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700623 * string proper, then emit zeroes required by any leftover
624 * floating precision; finally, if LADJUST, pad with blanks.
625 *
626 * Compute actual size, so we know how much to pad.
627 * size excludes decimal prec; realsz includes it.
628 */
629 realsz = dprec > size ? dprec : size;
630 if (sign) realsz++;
631 if (ox[1]) realsz += 2;
Elliott Hughes94336d82014-04-29 17:39:29 -0700632
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700633 /* right-adjusting blank padding */
634 if ((flags & (LADJUST | ZEROPAD)) == 0) PAD(width - realsz, blanks);
Elliott Hughes94336d82014-04-29 17:39:29 -0700635
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700636 /* prefix */
637 if (sign) PRINT(&sign, 1);
638 if (ox[1]) { /* ox[1] is either x, X, or \0 */
639 ox[0] = '0';
640 PRINT(ox, 2);
641 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700642
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700643 /* right-adjusting zero padding */
644 if ((flags & (LADJUST | ZEROPAD)) == ZEROPAD) PAD(width - realsz, zeroes);
Elliott Hughes94336d82014-04-29 17:39:29 -0700645
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700646 /* leading zeroes from decimal precision */
647 PAD(dprec - size, zeroes);
Elliott Hughes94336d82014-04-29 17:39:29 -0700648
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700649 /* the string or number proper */
650 if ((flags & FPT) == 0) {
651 PRINT(cp, size);
652 } else { /* glue together f_p fragments */
Yi Kong32bc0fc2018-08-02 17:31:13 -0700653 if (decimal_point == nullptr) decimal_point = nl_langinfo(RADIXCHAR);
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700654 if (!expchar) { /* %[fF] or sufficiently short %[gG] */
655 if (expt <= 0) {
656 PRINT(zeroes, 1);
657 if (prec || flags & ALT) PRINT(decimal_point, 1);
658 PAD(-expt, zeroes);
659 /* already handled initial 0's */
660 prec += expt;
661 } else {
662 PRINTANDPAD(cp, convbuf + ndig, lead, zeroes);
663 cp += lead;
664 if (prec || flags & ALT) PRINT(decimal_point, 1);
665 }
666 PRINTANDPAD(cp, convbuf + ndig, prec, zeroes);
667 } else { /* %[eE] or sufficiently long %[gG] */
668 if (prec > 1 || flags & ALT) {
669 buf[0] = *cp++;
670 buf[1] = *decimal_point;
671 PRINT(buf, 2);
672 PRINT(cp, ndig - 1);
673 PAD(prec - ndig, zeroes);
674 } else { /* XeYYY */
675 PRINT(cp, 1);
676 }
677 PRINT(expstr, expsize);
678 }
679 }
680 /* left-adjusting padding (always blank) */
681 if (flags & LADJUST) PAD(width - realsz, blanks);
Elliott Hughes94336d82014-04-29 17:39:29 -0700682
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700683 /* finally, adjust ret */
684 if (width < realsz) width = realsz;
685 if (width > INT_MAX - ret) goto overflow;
686 ret += width;
Elliott Hughes26a0ebd2023-05-09 11:52:16 -0700687
688 FLUSH(); /* copy out the I/O vectors */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700689 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700690done:
Elliott Hughes26a0ebd2023-05-09 11:52:16 -0700691 FLUSH();
Elliott Hughes94336d82014-04-29 17:39:29 -0700692error:
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700693 va_end(orgap);
694 if (__sferror(fp)) ret = -1;
695 goto finish;
Elliott Hughes94336d82014-04-29 17:39:29 -0700696
697overflow:
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700698 errno = ENOMEM;
699 ret = -1;
Elliott Hughes94336d82014-04-29 17:39:29 -0700700
701finish:
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700702 free(convbuf);
703 if (dtoaresult) __freedtoa(dtoaresult);
Yi Kong32bc0fc2018-08-02 17:31:13 -0700704 if (argtable != nullptr && argtable != statargtable) {
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700705 munmap(argtable, argtablesiz);
Yi Kong32bc0fc2018-08-02 17:31:13 -0700706 argtable = nullptr;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700707 }
708 return (ret);
Elliott Hughes94336d82014-04-29 17:39:29 -0700709}