Account for grouping in printf width (bug 30068)
[glibc.git] / stdio-common / vfprintf-process-arg.c
blob8c0fcbcf785a947527b376d41b8cf1dc4e9ee029
1 /* Argument-processing fragment for vfprintf.
2 Copyright (C) 1991-2023 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
19 /* This file is included twice from vfprintf-internal.c, for standard
20 and GNU-style positional (%N$) arguments. Before that,
21 process_arg_int etc. macros have to be defined to extract one
22 argument of the appropriate type, in addition to the file-specific
23 macros in vfprintf-internal.c. */
26 /* Start real work. We know about all flags and modifiers and
27 now process the wanted format specifier. */
28 LABEL (form_percent):
29 /* Write a literal "%". */
30 Xprintf_buffer_putc (buf, L_('%'));
31 break;
33 LABEL (form_integer):
34 /* Signed decimal integer. */
35 base = 10;
37 if (is_longlong)
39 long long int signed_number = process_arg_long_long_int ();
40 is_negative = signed_number < 0;
41 number.longlong = is_negative ? (- signed_number) : signed_number;
43 goto LABEL (longlong_number);
45 else
47 long int signed_number;
48 if (is_long_num)
49 signed_number = process_arg_long_int ();
50 else if (is_char)
51 signed_number = (signed char) process_arg_unsigned_int ();
52 else if (!is_short)
53 signed_number = process_arg_int ();
54 else
55 signed_number = (short int) process_arg_unsigned_int ();
57 is_negative = signed_number < 0;
58 number.word = is_negative ? (- signed_number) : signed_number;
60 goto LABEL (number);
62 /* NOTREACHED */
64 LABEL (form_unsigned):
65 /* Unsigned decimal integer. */
66 base = 10;
67 goto LABEL (unsigned_number);
68 /* NOTREACHED */
70 LABEL (form_octal):
71 /* Unsigned octal integer. */
72 base = 8;
73 goto LABEL (unsigned_number);
74 /* NOTREACHED */
76 LABEL (form_hexa):
77 /* Unsigned hexadecimal integer. */
78 base = 16;
79 goto LABEL (unsigned_number);
80 /* NOTREACHED */
82 LABEL (form_binary):
83 /* Unsigned binary integer. */
84 base = 2;
85 goto LABEL (unsigned_number);
86 /* NOTREACHED */
88 LABEL (unsigned_number): /* Unsigned number of base BASE. */
90 /* ISO specifies the `+' and ` ' flags only for signed
91 conversions. */
92 is_negative = 0;
93 showsign = 0;
94 space = 0;
96 if (is_longlong)
98 number.longlong = process_arg_unsigned_long_long_int ();
100 LABEL (longlong_number):
101 if (prec < 0)
102 /* Supply a default precision if none was given. */
103 prec = 1;
104 else
105 /* We have to take care for the '0' flag. If a precision
106 is given it must be ignored. */
107 pad = L_(' ');
109 /* If the precision is 0 and the number is 0 nothing has to
110 be written for the number, except for the 'o' format in
111 alternate form. */
112 if (prec == 0 && number.longlong == 0)
114 string = workend;
115 if (base == 8 && alt)
116 *--string = L_('0');
118 else
119 /* Put the number in WORK. */
120 string = _itoa (number.longlong, workend, base, spec == L_('X'));
121 /* Simplify further test for num != 0. */
122 number.word = number.longlong != 0;
124 else
126 if (is_long_num)
127 number.word = process_arg_unsigned_long_int ();
128 else if (is_char)
129 number.word = (unsigned char) process_arg_unsigned_int ();
130 else if (!is_short)
131 number.word = process_arg_unsigned_int ();
132 else
133 number.word = (unsigned short int) process_arg_unsigned_int ();
135 LABEL (number):
136 if (prec < 0)
137 /* Supply a default precision if none was given. */
138 prec = 1;
139 else
140 /* We have to take care for the '0' flag. If a precision
141 is given it must be ignored. */
142 pad = L_(' ');
144 /* If the precision is 0 and the number is 0 nothing has to
145 be written for the number, except for the 'o' format in
146 alternate form. */
147 if (prec == 0 && number.word == 0)
149 string = workend;
150 if (base == 8 && alt)
151 *--string = L_('0');
153 else
154 /* Put the number in WORK. */
155 string = _itoa_word (number.word, workend, base,
156 spec == L_('X'));
159 /* Grouping is also used for outdigits translation. */
160 struct grouping_iterator iter;
161 bool number_slow_path = group || (use_outdigits && base == 10);
162 if (group)
163 __grouping_iterator_init (&iter, LC_NUMERIC, _NL_CURRENT_LOCALE,
164 workend - string);
165 else if (use_outdigits && base == 10)
166 __grouping_iterator_init_none (&iter, workend - string);
168 int number_length;
169 #ifndef COMPILE_WPRINTF
170 if (use_outdigits && base == 10)
171 number_length = __translated_number_width (_NL_CURRENT_LOCALE,
172 string, workend);
173 else
174 number_length = workend - string;
175 if (group)
176 number_length += iter.separators * strlen (thousands_sep);
177 #else
178 number_length = workend - string;
179 /* All wide separators have length 1. */
180 if (group && thousands_sep != L'\0')
181 number_length += iter.separators;
182 #endif
184 /* The marker comes right before the number, but is not subject
185 to grouping. */
186 bool octal_marker = (prec <= number_length && number.word != 0
187 && alt && base == 8);
189 /* At this point prec_inc is the additional bytes required for the
190 specificed precision. It is 0 if the precision would not have
191 required additional bytes i.e. the number of input digits is more
192 than the precision. It is greater than zero if the precision is
193 more than the number of digits without grouping (precision only
194 considers digits). */
195 unsigned int prec_inc = MAX (0, prec - (workend - string));
197 if (!left)
199 width -= number_length + prec_inc;
201 if (number.word != 0 && alt && (base == 16 || base == 2))
202 /* Account for 0X, 0x, 0B or 0b hex or binary marker. */
203 width -= 2;
205 if (octal_marker)
206 --width;
208 if (is_negative || showsign || space)
209 --width;
211 if (pad == L_(' '))
213 Xprintf_buffer_pad (buf, L_(' '), width);
214 width = 0;
217 if (is_negative)
218 Xprintf_buffer_putc (buf, L_('-'));
219 else if (showsign)
220 Xprintf_buffer_putc (buf, L_('+'));
221 else if (space)
222 Xprintf_buffer_putc (buf, L_(' '));
224 if (number.word != 0 && alt && (base == 16 || base == 2))
226 Xprintf_buffer_putc (buf, L_('0'));
227 Xprintf_buffer_putc (buf, spec);
230 width += prec_inc;
231 Xprintf_buffer_pad (buf, L_('0'), width);
233 if (octal_marker)
234 Xprintf_buffer_putc (buf, L_('0'));
236 if (number_slow_path)
237 group_number (buf, &iter, string, workend, thousands_sep,
238 use_outdigits && base == 10);
239 else
240 Xprintf_buffer_write (buf, string, workend - string);
242 break;
244 else
246 /* Perform left justification adjustments. */
248 if (is_negative)
250 Xprintf_buffer_putc (buf, L_('-'));
251 --width;
253 else if (showsign)
255 Xprintf_buffer_putc (buf, L_('+'));
256 --width;
258 else if (space)
260 Xprintf_buffer_putc (buf, L_(' '));
261 --width;
264 if (number.word != 0 && alt && (base == 16 || base == 2))
266 Xprintf_buffer_putc (buf, L_('0'));
267 Xprintf_buffer_putc (buf, spec);
268 width -= 2;
271 if (octal_marker)
272 --width;
274 /* Adjust the width by subtracting the number of bytes
275 required to represent the number with grouping characters
276 (NUMBER_LENGTH) and any additional bytes required for
277 precision. */
278 width -= number_length + prec_inc;
280 Xprintf_buffer_pad (buf, L_('0'), prec_inc);
282 if (octal_marker)
283 Xprintf_buffer_putc (buf, L_('0'));
285 if (number_slow_path)
286 group_number (buf, &iter, string, workend, thousands_sep,
287 use_outdigits && base == 10);
288 else
289 Xprintf_buffer_write (buf, string, workend - string);
291 Xprintf_buffer_pad (buf, L_(' '), width);
292 break;
295 LABEL (form_pointer):
296 /* Generic pointer. */
298 const void *ptr = process_arg_pointer ();
299 if (ptr != NULL)
301 /* If the pointer is not NULL, write it as a %#x spec. */
302 base = 16;
303 number.word = (unsigned long int) ptr;
304 is_negative = 0;
305 alt = 1;
306 group = 0;
307 spec = L_('x');
308 goto LABEL (number);
310 else
312 /* Write "(nil)" for a nil pointer. */
313 string = (CHAR_T *) L_("(nil)");
314 /* Make sure the full string "(nil)" is printed. */
315 if (prec < 5)
316 prec = 5;
317 /* This is a wide string iff compiling wprintf. */
318 is_long = sizeof (CHAR_T) > 1;
319 goto LABEL (print_string);
322 /* NOTREACHED */
324 LABEL (form_number):
325 if ((mode_flags & PRINTF_FORTIFY) != 0)
327 if (! readonly_format)
329 extern int __readonly_area (const void *, size_t)
330 attribute_hidden;
331 readonly_format
332 = __readonly_area (format, ((STR_LEN (format) + 1)
333 * sizeof (CHAR_T)));
335 if (readonly_format < 0)
336 __libc_fatal ("*** %n in writable segment detected ***\n");
338 /* Answer the count of characters written. */
339 void *ptrptr = process_arg_pointer ();
340 unsigned int written = Xprintf_buffer_done (buf);
341 if (is_longlong)
342 *(long long int *) ptrptr = written;
343 else if (is_long_num)
344 *(long int *) ptrptr = written;
345 else if (is_char)
346 *(char *) ptrptr = written;
347 else if (!is_short)
348 *(int *) ptrptr = written;
349 else
350 *(short int *) ptrptr = written;
351 break;
353 LABEL (form_strerror):
354 /* Print description of error ERRNO. */
355 if (alt)
356 string = (CHAR_T *) __get_errname (save_errno);
357 else
358 string = (CHAR_T *) __strerror_r (save_errno, (char *) work_buffer,
359 WORK_BUFFER_SIZE * sizeof (CHAR_T));
360 if (string == NULL)
362 /* Print as a decimal number. */
363 base = 10;
364 is_negative = save_errno < 0;
365 number.word = save_errno;
366 if (is_negative)
367 number.word = -number.word;
368 goto LABEL (number);
370 else
372 is_long = 0; /* This is no wide-char string. */
373 goto LABEL (print_string);
376 LABEL (form_character):
377 /* Character. */
378 if (is_long)
379 goto LABEL (form_wcharacter);
380 --width; /* Account for the character itself. */
381 if (!left)
382 Xprintf_buffer_pad (buf, L_(' '), width);
383 #ifdef COMPILE_WPRINTF
384 __wprintf_buffer_putc (buf, __btowc ((unsigned char) /* Promoted. */
385 process_arg_int ()));
386 #else
387 __printf_buffer_putc (buf, (unsigned char) /* Promoted. */
388 process_arg_int ());
389 #endif
390 if (left)
391 Xprintf_buffer_pad (buf, L_(' '), width);
392 break;
394 LABEL (form_string):
396 size_t len;
398 /* The string argument could in fact be `char *' or `wchar_t *'.
399 But this should not make a difference here. */
400 #ifdef COMPILE_WPRINTF
401 string = (CHAR_T *) process_arg_wstring ();
402 #else
403 string = (CHAR_T *) process_arg_string ();
404 #endif
405 /* Entry point for printing other strings. */
406 LABEL (print_string):
408 if (string == NULL)
410 /* Write "(null)" if there's space. */
411 if (prec == -1 || prec >= (int) array_length (null) - 1)
413 string = (CHAR_T *) null;
414 len = array_length (null) - 1;
416 else
418 string = (CHAR_T *) L"";
419 len = 0;
422 else if (!is_long && spec != L_('S'))
424 #ifdef COMPILE_WPRINTF
425 outstring_converted_wide_string (buf, (const char *) string,
426 prec, width, left);
427 /* The padding has already been written. */
428 break;
429 #else
430 if (prec != -1)
431 /* Search for the end of the string, but don't search past
432 the length (in bytes) specified by the precision. */
433 len = __strnlen (string, prec);
434 else
435 len = strlen (string);
436 #endif
438 else
440 #ifdef COMPILE_WPRINTF
441 if (prec != -1)
442 /* Search for the end of the string, but don't search past
443 the length specified by the precision. */
444 len = __wcsnlen (string, prec);
445 else
446 len = __wcslen (string);
447 #else
448 outstring_converted_wide_string (buf, (const wchar_t *) string,
449 prec, width, left);
450 /* The padding has already been written. */
451 break;
452 #endif
455 if ((width -= len) < 0)
457 Xprintf_buffer_write (buf, string, len);
458 break;
461 if (!left)
462 Xprintf_buffer_pad (buf, L_(' '), width);
463 Xprintf_buffer_write (buf, string, len);
464 if (left)
465 Xprintf_buffer_pad (buf, L_(' '), width);
467 break;
469 #ifdef COMPILE_WPRINTF
470 LABEL (form_wcharacter):
472 /* Wide character. */
473 --width;
474 if (!left)
475 Xprintf_buffer_pad (buf, L_(' '), width);
476 Xprintf_buffer_putc (buf, process_arg_wchar_t ());
477 if (left)
478 Xprintf_buffer_pad (buf, L_(' '), width);
480 break;
482 #else /* !COMPILE_WPRINTF */
483 LABEL (form_wcharacter):
485 /* Wide character. */
486 char wcbuf[MB_LEN_MAX];
487 mbstate_t mbstate;
488 size_t len;
490 memset (&mbstate, '\0', sizeof (mbstate_t));
491 len = __wcrtomb (wcbuf, process_arg_wchar_t (), &mbstate);
492 if (len == (size_t) -1)
494 /* Something went wrong during the conversion. Bail out. */
495 __printf_buffer_mark_failed (buf);
496 goto all_done;
498 width -= len;
499 if (!left)
500 Xprintf_buffer_pad (buf, L_(' '), width);
501 Xprintf_buffer_write (buf, wcbuf, len);
502 if (left)
503 Xprintf_buffer_pad (buf, L_(' '), width);
505 break;
506 #endif /* !COMPILE_WPRINTF */