1/* Argument-processing fragment for vfprintf.
2 Copyright (C) 1991-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
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.
9
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.
14
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/>. */
18
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. */
24
25{
26 /* Start real work. We know about all flags and modifiers and
27 now process the wanted format specifier. */
28LABEL (form_percent):
29 /* Write a literal "%". */
30 Xprintf_buffer_putc (buf, L_('%'));
31 break;
32
33LABEL (form_integer):
34 /* Signed decimal integer. */
35 base = 10;
36
37 if (is_longlong)
38 {
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;
42
43 goto LABEL (longlong_number);
44 }
45 else
46 {
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 ();
56
57 is_negative = signed_number < 0;
58 number.word = is_negative ? (- signed_number) : signed_number;
59
60 goto LABEL (number);
61 }
62 /* NOTREACHED */
63
64LABEL (form_unsigned):
65 /* Unsigned decimal integer. */
66 base = 10;
67 goto LABEL (unsigned_number);
68 /* NOTREACHED */
69
70LABEL (form_octal):
71 /* Unsigned octal integer. */
72 base = 8;
73 goto LABEL (unsigned_number);
74 /* NOTREACHED */
75
76LABEL (form_hexa):
77 /* Unsigned hexadecimal integer. */
78 base = 16;
79 goto LABEL (unsigned_number);
80 /* NOTREACHED */
81
82LABEL (form_binary):
83 /* Unsigned binary integer. */
84 base = 2;
85 goto LABEL (unsigned_number);
86 /* NOTREACHED */
87
88LABEL (unsigned_number): /* Unsigned number of base BASE. */
89
90 /* ISO specifies the `+' and ` ' flags only for signed
91 conversions. */
92 is_negative = 0;
93 showsign = 0;
94 space = 0;
95
96 if (is_longlong)
97 {
98 number.longlong = process_arg_unsigned_long_long_int ();
99
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_(' ');
108
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)
113 {
114 string = workend;
115 if (base == 8 && alt)
116 *--string = L_('0');
117 }
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;
123 }
124 else
125 {
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 ();
134
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_(' ');
143
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)
148 {
149 string = workend;
150 if (base == 8 && alt)
151 *--string = L_('0');
152 }
153 else
154 /* Put the number in WORK. */
155 string = _itoa_word (value: number.word, buflim: workend, base,
156 upper_case: spec == L_('X'));
157 }
158
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 (it: &iter, LC_NUMERIC, _NL_CURRENT_LOCALE,
164 digits: workend - string);
165 else if (use_outdigits && base == 10)
166 __grouping_iterator_init_none (it: &iter, digits: workend - string);
167
168 int number_length;
169#ifndef COMPILE_WPRINTF
170 if (use_outdigits && base == 10)
171 number_length = __translated_number_width (_NL_CURRENT_LOCALE,
172 first: string, last: 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
183
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);
188
189 /* At this point prec_inc is the additional bytes required for the
190 specified 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));
196
197 if (!left)
198 {
199 width -= number_length + prec_inc;
200
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;
204
205 if (octal_marker)
206 --width;
207
208 if (is_negative || showsign || space)
209 --width;
210
211 if (pad == L_(' '))
212 {
213 Xprintf_buffer_pad (buf, L_(' '), count: width);
214 width = 0;
215 }
216
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_(' '));
223
224 if (number.word != 0 && alt && (base == 16 || base == 2))
225 {
226 Xprintf_buffer_putc (buf, L_('0'));
227 Xprintf_buffer_putc (buf, ch: spec);
228 }
229
230 width += prec_inc;
231 Xprintf_buffer_pad (buf, L_('0'), count: width);
232
233 if (octal_marker)
234 Xprintf_buffer_putc (buf, L_('0'));
235
236 if (number_slow_path)
237 group_number (buf, iter: &iter, from: string, to: workend, thousands_sep,
238 i18n: use_outdigits && base == 10);
239 else
240 Xprintf_buffer_write (buf, s: string, count: workend - string);
241
242 break;
243 }
244 else
245 {
246 /* Perform left justification adjustments. */
247
248 if (is_negative)
249 {
250 Xprintf_buffer_putc (buf, L_('-'));
251 --width;
252 }
253 else if (showsign)
254 {
255 Xprintf_buffer_putc (buf, L_('+'));
256 --width;
257 }
258 else if (space)
259 {
260 Xprintf_buffer_putc (buf, L_(' '));
261 --width;
262 }
263
264 if (number.word != 0 && alt && (base == 16 || base == 2))
265 {
266 Xprintf_buffer_putc (buf, L_('0'));
267 Xprintf_buffer_putc (buf, ch: spec);
268 width -= 2;
269 }
270
271 if (octal_marker)
272 --width;
273
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;
279
280 Xprintf_buffer_pad (buf, L_('0'), count: prec_inc);
281
282 if (octal_marker)
283 Xprintf_buffer_putc (buf, L_('0'));
284
285 if (number_slow_path)
286 group_number (buf, iter: &iter, from: string, to: workend, thousands_sep,
287 i18n: use_outdigits && base == 10);
288 else
289 Xprintf_buffer_write (buf, s: string, count: workend - string);
290
291 Xprintf_buffer_pad (buf, L_(' '), count: width);
292 break;
293 }
294
295LABEL (form_pointer):
296 /* Generic pointer. */
297 {
298 const void *ptr = process_arg_pointer ();
299 if (ptr != NULL)
300 {
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);
309 }
310 else
311 {
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);
320 }
321 }
322 /* NOTREACHED */
323
324LABEL (form_number):
325 if ((mode_flags & PRINTF_FORTIFY) != 0)
326 {
327 if (! readonly_format)
328 {
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)));
334 }
335 if (readonly_format < 0)
336 __libc_fatal ("*** %n in writable segment detected ***\n");
337 }
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;
352
353LABEL (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)
361 {
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);
369 }
370 else
371 {
372 is_long = 0; /* This is no wide-char string. */
373 goto LABEL (print_string);
374 }
375
376LABEL (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_(' '), count: 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, ch: (unsigned char) /* Promoted. */
388 process_arg_int ());
389#endif
390 if (left)
391 Xprintf_buffer_pad (buf, L_(' '), count: width);
392 break;
393
394LABEL (form_string):
395 {
396 size_t len;
397
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):
407
408 if (string == NULL)
409 {
410 /* Write "(null)" if there's space. */
411 if (prec == -1 || prec >= (int) array_length (null) - 1)
412 {
413 string = (CHAR_T *) null;
414 len = array_length (null) - 1;
415 }
416 else
417 {
418 string = (CHAR_T *) L"";
419 len = 0;
420 }
421 }
422 else if (!is_long && spec != L_('S'))
423 {
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
437 }
438 else
439 {
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 (target: buf, src: (const wchar_t *) string,
449 prec, width, left);
450 /* The padding has already been written. */
451 break;
452#endif
453 }
454
455 if ((width -= len) < 0)
456 {
457 Xprintf_buffer_write (buf, s: string, count: len);
458 break;
459 }
460
461 if (!left)
462 Xprintf_buffer_pad (buf, L_(' '), count: width);
463 Xprintf_buffer_write (buf, s: string, count: len);
464 if (left)
465 Xprintf_buffer_pad (buf, L_(' '), count: width);
466 }
467 break;
468
469#ifdef COMPILE_WPRINTF
470LABEL (form_wcharacter):
471 {
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);
479 }
480 break;
481
482#else /* !COMPILE_WPRINTF */
483LABEL (form_wcharacter):
484 {
485 /* Wide character. */
486 char wcbuf[MB_LEN_MAX];
487 mbstate_t mbstate;
488 size_t len;
489
490 memset (&mbstate, '\0', sizeof (mbstate_t));
491 len = __wcrtomb (s: wcbuf, process_arg_wchar_t (), ps: &mbstate);
492 if (len == (size_t) -1)
493 {
494 /* Something went wrong during the conversion. Bail out. */
495 __printf_buffer_mark_failed (buf);
496 goto all_done;
497 }
498 width -= len;
499 if (!left)
500 Xprintf_buffer_pad (buf, L_(' '), count: width);
501 Xprintf_buffer_write (buf, s: wcbuf, count: len);
502 if (left)
503 Xprintf_buffer_pad (buf, L_(' '), count: width);
504 }
505 break;
506#endif /* !COMPILE_WPRINTF */
507}
508

source code of glibc/stdio-common/vfprintf-process-arg.c