2c651946df79278afcab9d74b97677838259bdc9
[platform/upstream/glibc.git] / stdio-common / vfprintf-process-arg.c
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.
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.  */
28 LABEL (form_percent):
29   /* Write a literal "%".  */
30   Xprintf_buffer_putc (buf, L_('%'));
31   break;
32
33 LABEL (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
64 LABEL (form_unsigned):
65   /* Unsigned decimal integer.  */
66   base = 10;
67   goto LABEL (unsigned_number);
68   /* NOTREACHED */
69
70 LABEL (form_octal):
71   /* Unsigned octal integer.  */
72   base = 8;
73   goto LABEL (unsigned_number);
74   /* NOTREACHED */
75
76 LABEL (form_hexa):
77   /* Unsigned hexadecimal integer.  */
78   base = 16;
79   goto LABEL (unsigned_number);
80   /* NOTREACHED */
81
82 LABEL (form_binary):
83   /* Unsigned binary integer.  */
84   base = 2;
85   goto LABEL (unsigned_number);
86   /* NOTREACHED */
87
88 LABEL (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 (number.word, workend, base,
156                              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 (&iter, LC_NUMERIC, _NL_CURRENT_LOCALE,
164                               workend - string);
165   else if (use_outdigits && base == 10)
166     __grouping_iterator_init_none (&iter, 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                                                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
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   prec = MAX (0, prec - (workend - string));
190
191   if (!left)
192     {
193       width -= number_length + prec;
194
195       if (number.word != 0 && alt && (base == 16 || base == 2))
196         /* Account for 0X, 0x, 0B or 0b hex or binary marker.  */
197         width -= 2;
198
199       if (is_negative || showsign || space)
200         --width;
201
202       if (pad == L_(' '))
203         {
204           Xprintf_buffer_pad (buf, L_(' '), width);
205           width = 0;
206         }
207
208       if (is_negative)
209         Xprintf_buffer_putc (buf, L_('-'));
210       else if (showsign)
211         Xprintf_buffer_putc (buf, L_('+'));
212       else if (space)
213         Xprintf_buffer_putc (buf, L_(' '));
214
215       if (number.word != 0 && alt && (base == 16 || base == 2))
216         {
217           Xprintf_buffer_putc (buf, L_('0'));
218           Xprintf_buffer_putc (buf, spec);
219         }
220
221       width += prec;
222       Xprintf_buffer_pad (buf, L_('0'), width);
223
224       if (octal_marker)
225         Xprintf_buffer_putc (buf, L_('0'));
226
227       if (number_slow_path)
228         group_number (buf, &iter, string, workend, thousands_sep,
229                       use_outdigits && base == 10);
230       else
231         Xprintf_buffer_write (buf, string, workend - string);
232
233       break;
234     }
235   else
236     {
237       if (is_negative)
238         {
239           Xprintf_buffer_putc (buf, L_('-'));
240           --width;
241         }
242       else if (showsign)
243         {
244           Xprintf_buffer_putc (buf, L_('+'));
245           --width;
246         }
247       else if (space)
248         {
249           Xprintf_buffer_putc (buf, L_(' '));
250           --width;
251         }
252
253       if (number.word != 0 && alt && (base == 16 || base == 2))
254         {
255           Xprintf_buffer_putc (buf, L_('0'));
256           Xprintf_buffer_putc (buf, spec);
257           width -= 2;
258         }
259
260       width -= workend - string + prec;
261
262       Xprintf_buffer_pad (buf, L_('0'), prec);
263
264       if (octal_marker)
265         Xprintf_buffer_putc (buf, L_('0'));
266
267       if (number_slow_path)
268         group_number (buf, &iter, string, workend, thousands_sep,
269                       use_outdigits && base == 10);
270       else
271         Xprintf_buffer_write (buf, string, workend - string);
272
273       Xprintf_buffer_pad (buf, L_(' '), width);
274       break;
275     }
276
277 LABEL (form_pointer):
278   /* Generic pointer.  */
279   {
280     const void *ptr = process_arg_pointer ();
281     if (ptr != NULL)
282       {
283         /* If the pointer is not NULL, write it as a %#x spec.  */
284         base = 16;
285         number.word = (unsigned long int) ptr;
286         is_negative = 0;
287         alt = 1;
288         group = 0;
289         spec = L_('x');
290         goto LABEL (number);
291       }
292     else
293       {
294         /* Write "(nil)" for a nil pointer.  */
295         string = (CHAR_T *) L_("(nil)");
296         /* Make sure the full string "(nil)" is printed.  */
297         if (prec < 5)
298           prec = 5;
299         /* This is a wide string iff compiling wprintf.  */
300         is_long = sizeof (CHAR_T) > 1;
301         goto LABEL (print_string);
302       }
303   }
304   /* NOTREACHED */
305
306 LABEL (form_number):
307   if ((mode_flags & PRINTF_FORTIFY) != 0)
308     {
309       if (! readonly_format)
310         {
311           extern int __readonly_area (const void *, size_t)
312             attribute_hidden;
313           readonly_format
314             = __readonly_area (format, ((STR_LEN (format) + 1)
315                                         * sizeof (CHAR_T)));
316         }
317       if (readonly_format < 0)
318         __libc_fatal ("*** %n in writable segment detected ***\n");
319     }
320   /* Answer the count of characters written.  */
321   void *ptrptr = process_arg_pointer ();
322   unsigned int written = Xprintf_buffer_done (buf);
323   if (is_longlong)
324     *(long long int *) ptrptr = written;
325   else if (is_long_num)
326     *(long int *) ptrptr = written;
327   else if (is_char)
328     *(char *) ptrptr = written;
329   else if (!is_short)
330     *(int *) ptrptr = written;
331   else
332     *(short int *) ptrptr = written;
333   break;
334
335 LABEL (form_strerror):
336   /* Print description of error ERRNO.  */
337   if (alt)
338     string = (CHAR_T *) __get_errname (save_errno);
339   else
340     string = (CHAR_T *) __strerror_r (save_errno, (char *) work_buffer,
341                                       WORK_BUFFER_SIZE * sizeof (CHAR_T));
342   if (string == NULL)
343     {
344       /* Print as a decimal number. */
345       base = 10;
346       is_negative = save_errno < 0;
347       number.word = save_errno;
348       if (is_negative)
349         number.word = -number.word;
350       goto LABEL (number);
351     }
352   else
353     {
354       is_long = 0;  /* This is no wide-char string.  */
355       goto LABEL (print_string);
356     }
357
358 LABEL (form_character):
359   /* Character.  */
360   if (is_long)
361     goto LABEL (form_wcharacter);
362   --width;  /* Account for the character itself.  */
363   if (!left)
364     Xprintf_buffer_pad (buf, L_(' '), width);
365 #ifdef COMPILE_WPRINTF
366   __wprintf_buffer_putc (buf, __btowc ((unsigned char) /* Promoted. */
367                                        process_arg_int ()));
368 #else
369   __printf_buffer_putc (buf, (unsigned char) /* Promoted.  */
370                         process_arg_int ());
371 #endif
372   if (left)
373     Xprintf_buffer_pad (buf, L_(' '), width);
374   break;
375
376 LABEL (form_string):
377   {
378     size_t len;
379
380     /* The string argument could in fact be `char *' or `wchar_t *'.
381        But this should not make a difference here.  */
382 #ifdef COMPILE_WPRINTF
383     string = (CHAR_T *) process_arg_wstring ();
384 #else
385     string = (CHAR_T *) process_arg_string ();
386 #endif
387     /* Entry point for printing other strings.  */
388     LABEL (print_string):
389
390     if (string == NULL)
391       {
392         /* Write "(null)" if there's space.  */
393         if (prec == -1 || prec >= (int) array_length (null) - 1)
394           {
395             string = (CHAR_T *) null;
396             len = array_length (null) - 1;
397           }
398         else
399           {
400             string = (CHAR_T *) L"";
401             len = 0;
402           }
403       }
404     else if (!is_long && spec != L_('S'))
405       {
406 #ifdef COMPILE_WPRINTF
407         outstring_converted_wide_string (buf, (const char *) string,
408                                          prec, width, left);
409         /* The padding has already been written.  */
410         break;
411 #else
412         if (prec != -1)
413           /* Search for the end of the string, but don't search past
414              the length (in bytes) specified by the precision.  */
415           len = __strnlen (string, prec);
416         else
417           len = strlen (string);
418 #endif
419       }
420     else
421       {
422 #ifdef COMPILE_WPRINTF
423         if (prec != -1)
424           /* Search for the end of the string, but don't search past
425              the length specified by the precision.  */
426           len = __wcsnlen (string, prec);
427         else
428           len = __wcslen (string);
429 #else
430         outstring_converted_wide_string (buf, (const wchar_t *) string,
431                                          prec, width, left);
432         /* The padding has already been written.  */
433         break;
434 #endif
435       }
436
437     if ((width -= len) < 0)
438       {
439         Xprintf_buffer_write (buf, string, len);
440         break;
441       }
442
443     if (!left)
444       Xprintf_buffer_pad (buf, L_(' '), width);
445     Xprintf_buffer_write (buf, string, len);
446     if (left)
447       Xprintf_buffer_pad (buf, L_(' '), width);
448   }
449   break;
450
451 #ifdef COMPILE_WPRINTF
452 LABEL (form_wcharacter):
453   {
454     /* Wide character.  */
455     --width;
456     if (!left)
457       Xprintf_buffer_pad (buf, L_(' '), width);
458     Xprintf_buffer_putc (buf, process_arg_wchar_t ());
459     if (left)
460       Xprintf_buffer_pad (buf, L_(' '), width);
461   }
462   break;
463
464 #else /* !COMPILE_WPRINTF */
465 LABEL (form_wcharacter):
466   {
467     /* Wide character.  */
468     char wcbuf[MB_LEN_MAX];
469     mbstate_t mbstate;
470     size_t len;
471
472     memset (&mbstate, '\0', sizeof (mbstate_t));
473     len = __wcrtomb (wcbuf, process_arg_wchar_t (), &mbstate);
474     if (len == (size_t) -1)
475       {
476         /* Something went wrong during the conversion.  Bail out.  */
477         __printf_buffer_mark_failed (buf);
478         goto all_done;
479       }
480     width -= len;
481     if (!left)
482       Xprintf_buffer_pad (buf, L_(' '), width);
483     Xprintf_buffer_write (buf, wcbuf, len);
484     if (left)
485       Xprintf_buffer_pad (buf, L_(' '), width);
486   }
487   break;
488 #endif /* !COMPILE_WPRINTF */
489 }