1 /* Argument-processing fragment for vfprintf.
2    Copyright (C) 1991-2022 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   outchar (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         {
120           /* Put the number in WORK.  */
121           string = _itoa (number.longlong, workend, base,
122                           spec == L_('X'));
123           if (group && grouping)
124             string = group_number (work_buffer, string, workend,
125                                    grouping, thousands_sep);
126           if (use_outdigits && base == 10)
127             string = _i18n_number_rewrite (string, workend, workend);
128         }
129       /* Simplify further test for num != 0.  */
130       number.word = number.longlong != 0;
131     }
132   else
133     {
134       if (is_long_num)
135         number.word = process_arg_unsigned_long_int ();
136       else if (is_char)
137         number.word = (unsigned char) process_arg_unsigned_int ();
138       else if (!is_short)
139         number.word = process_arg_unsigned_int ();
140       else
141         number.word = (unsigned short int) process_arg_unsigned_int ();
142 
143       LABEL (number):
144       if (prec < 0)
145         /* Supply a default precision if none was given.  */
146         prec = 1;
147       else
148         /* We have to take care for the '0' flag.  If a precision
149            is given it must be ignored.  */
150         pad = L_(' ');
151 
152       /* If the precision is 0 and the number is 0 nothing has to
153          be written for the number, except for the 'o' format in
154          alternate form.  */
155       if (prec == 0 && number.word == 0)
156         {
157           string = workend;
158           if (base == 8 && alt)
159             *--string = L_('0');
160         }
161       else
162         {
163           /* Put the number in WORK.  */
164           string = _itoa_word (number.word, workend, base,
165                                spec == L_('X'));
166           if (group && grouping)
167             string = group_number (work_buffer, string, workend,
168                                    grouping, thousands_sep);
169           if (use_outdigits && base == 10)
170             string = _i18n_number_rewrite (string, workend, workend);
171         }
172     }
173 
174   if (prec <= workend - string && number.word != 0 && alt && base == 8)
175     /* Add octal marker.  */
176     *--string = L_('0');
177 
178   prec = MAX (0, prec - (workend - string));
179 
180   if (!left)
181     {
182       width -= workend - string + prec;
183 
184       if (number.word != 0 && alt && (base == 16 || base == 2))
185         /* Account for 0X, 0x, 0B or 0b hex or binary marker.  */
186         width -= 2;
187 
188       if (is_negative || showsign || space)
189         --width;
190 
191       if (pad == L_(' '))
192         {
193           PAD (L_(' '));
194           width = 0;
195         }
196 
197       if (is_negative)
198         outchar (L_('-'));
199       else if (showsign)
200         outchar (L_('+'));
201       else if (space)
202         outchar (L_(' '));
203 
204       if (number.word != 0 && alt && (base == 16 || base == 2))
205         {
206           outchar (L_('0'));
207           outchar (spec);
208         }
209 
210       width += prec;
211       PAD (L_('0'));
212 
213       outstring (string, workend - string);
214 
215       break;
216     }
217   else
218     {
219       if (is_negative)
220         {
221           outchar (L_('-'));
222           --width;
223         }
224       else if (showsign)
225         {
226           outchar (L_('+'));
227           --width;
228         }
229       else if (space)
230         {
231           outchar (L_(' '));
232           --width;
233         }
234 
235       if (number.word != 0 && alt && (base == 16 || base == 2))
236         {
237           outchar (L_('0'));
238           outchar (spec);
239           width -= 2;
240         }
241 
242       width -= workend - string + prec;
243 
244       if (prec > 0)
245         {
246           int temp = width;
247           width = prec;
248           PAD (L_('0'));
249           width = temp;
250         }
251 
252       outstring (string, workend - string);
253 
254       PAD (L_(' '));
255       break;
256     }
257 
LABEL(form_pointer)258 LABEL (form_pointer):
259   /* Generic pointer.  */
260   {
261     const void *ptr = process_arg_pointer ();
262     if (ptr != NULL)
263       {
264         /* If the pointer is not NULL, write it as a %#x spec.  */
265         base = 16;
266         number.word = (unsigned long int) ptr;
267         is_negative = 0;
268         alt = 1;
269         group = 0;
270         spec = L_('x');
271         goto LABEL (number);
272       }
273     else
274       {
275         /* Write "(nil)" for a nil pointer.  */
276         string = (CHAR_T *) L_("(nil)");
277         /* Make sure the full string "(nil)" is printed.  */
278         if (prec < 5)
279           prec = 5;
280         /* This is a wide string iff compiling wprintf.  */
281         is_long = sizeof (CHAR_T) > 1;
282         goto LABEL (print_string);
283       }
284   }
285   /* NOTREACHED */
286 
287 LABEL (form_number):
288   if ((mode_flags & PRINTF_FORTIFY) != 0)
289     {
290       if (! readonly_format)
291         {
292           extern int __readonly_area (const void *, size_t)
293             attribute_hidden;
294           readonly_format
295             = __readonly_area (format, ((STR_LEN (format) + 1)
296                                         * sizeof (CHAR_T)));
297         }
298       if (readonly_format < 0)
299         __libc_fatal ("*** %n in writable segment detected ***\n");
300     }
301   /* Answer the count of characters written.  */
302   void *ptrptr = process_arg_pointer ();
303   if (is_longlong)
304     *(long long int *) ptrptr = done;
305   else if (is_long_num)
306     *(long int *) ptrptr = done;
307   else if (is_char)
308     *(char *) ptrptr = done;
309   else if (!is_short)
310     *(int *) ptrptr = done;
311   else
312     *(short int *) ptrptr = done;
313   break;
314 
315 LABEL (form_strerror):
316   /* Print description of error ERRNO.  */
317   if (alt)
318     string = (CHAR_T *) __get_errname (save_errno);
319   else
320     string = (CHAR_T *) __strerror_r (save_errno, (char *) work_buffer,
321                                       WORK_BUFFER_SIZE * sizeof (CHAR_T));
322   if (string == NULL)
323     {
324       /* Print as a decimal number. */
325       base = 10;
326       is_negative = save_errno < 0;
327       number.word = save_errno;
328       if (is_negative)
329         number.word = -number.word;
330       goto LABEL (number);
331     }
332   else
333     {
334       is_long = 0;  /* This is no wide-char string.  */
335       goto LABEL (print_string);
336     }
337 
338 LABEL (form_character):
339   /* Character.  */
340   if (is_long)
341     goto LABEL (form_wcharacter);
342   --width;  /* Account for the character itself.  */
343   if (!left)
344     PAD (L_(' '));
345 #ifdef COMPILE_WPRINTF
346   outchar (__btowc ((unsigned char) process_arg_int ())); /* Promoted. */
347 #else
348   outchar ((unsigned char) process_arg_int ()); /* Promoted.  */
349 #endif
350   if (left)
351     PAD (L_(' '));
352   break;
353 
LABEL(form_string)354 LABEL (form_string):
355   {
356     size_t len;
357 
358     /* The string argument could in fact be `char *' or `wchar_t *'.
359        But this should not make a difference here.  */
360 #ifdef COMPILE_WPRINTF
361     string = (CHAR_T *) process_arg_wstring ();
362 #else
363     string = (CHAR_T *) process_arg_string ();
364 #endif
365     /* Entry point for printing other strings.  */
366     LABEL (print_string):
367 
368     if (string == NULL)
369       {
370         /* Write "(null)" if there's space.  */
371         if (prec == -1 || prec >= (int) array_length (null) - 1)
372           {
373             string = (CHAR_T *) null;
374             len = array_length (null) - 1;
375           }
376         else
377           {
378             string = (CHAR_T *) L"";
379             len = 0;
380           }
381       }
382     else if (!is_long && spec != L_('S'))
383       {
384 #ifdef COMPILE_WPRINTF
385         done = outstring_converted_wide_string
386           (s, (const char *) string, prec, width, left, done);
387         if (done < 0)
388           goto all_done;
389         /* The padding has already been written.  */
390         break;
391 #else
392         if (prec != -1)
393           /* Search for the end of the string, but don't search past
394              the length (in bytes) specified by the precision.  */
395           len = __strnlen (string, prec);
396         else
397           len = strlen (string);
398 #endif
399       }
400     else
401       {
402 #ifdef COMPILE_WPRINTF
403         if (prec != -1)
404           /* Search for the end of the string, but don't search past
405              the length specified by the precision.  */
406           len = __wcsnlen (string, prec);
407         else
408           len = __wcslen (string);
409 #else
410         done = outstring_converted_wide_string
411           (s, (const wchar_t *) string, prec, width, left, done);
412         if (done < 0)
413           goto all_done;
414         /* The padding has already been written.  */
415         break;
416 #endif
417       }
418 
419     if ((width -= len) < 0)
420       {
421         outstring (string, len);
422         break;
423       }
424 
425     if (!left)
426       PAD (L_(' '));
427     outstring (string, len);
428     if (left)
429       PAD (L_(' '));
430   }
431   break;
432 
433 #ifdef COMPILE_WPRINTF
LABEL(form_wcharacter)434 LABEL (form_wcharacter):
435   {
436     /* Wide character.  */
437     --width;
438     if (!left)
439       PAD (L' ');
440     outchar (process_arg_wchar_t ());
441     if (left)
442       PAD (L' ');
443   }
444   break;
445 
446 #else /* !COMPILE_WPRINTF */
LABEL(form_wcharacter)447 LABEL (form_wcharacter):
448   {
449     /* Wide character.  */
450     char buf[MB_LEN_MAX];
451     mbstate_t mbstate;
452     size_t len;
453 
454     memset (&mbstate, '\0', sizeof (mbstate_t));
455     len = __wcrtomb (buf, process_arg_wchar_t (), &mbstate);
456     if (len == (size_t) -1)
457       {
458         /* Something went wrong during the conversion.  Bail out.  */
459         done = -1;
460         goto all_done;
461       }
462     width -= len;
463     if (!left)
464       PAD (' ');
465     outstring (buf, len);
466     if (left)
467       PAD (' ');
468   }
469   break;
470 #endif /* !COMPILE_WPRINTF */
471 }
472