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