1 /* Parse a printf-style format string.
3 Copyright (C) 1986-2023 Free Software Foundation, Inc.
5 This file is part of GDB.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 #include "common-defs.h"
23 format_pieces::format_pieces (const char **arg
, bool gdb_extensions
,
28 const char *prev_start
;
29 const char *percent_loc
;
30 char *sub_start
, *current_substring
;
31 enum argclass this_argclass
;
38 *arg
+= strlen (*arg
);
42 /* Parse the format-control string and copy it into the string STRING,
43 processing some kinds of escape sequence. */
45 char *f
= (char *) alloca (strlen (s
) + 1);
48 while (*s
!= '"' && *s
!= '\0')
90 /* ??? TODO: handle other escape sequences. */
91 error (_("Unrecognized escape character \\%c in format string."),
101 /* Terminate our escape-processed copy. */
104 /* Whether the format string ended with double-quote or zero, we're
105 done with it; it's up to callers to complain about syntax. */
109 /* Need extra space for the '\0's. Doubling the size is sufficient. */
111 current_substring
= (char *) xmalloc (strlen (string
) * 2 + 1000);
112 m_storage
.reset (current_substring
);
114 /* Now scan the string for %-specs and see what kinds of args they want.
115 argclass classifies the %-specs so we can give printf-type functions
116 something of the right size. */
118 const char *f
= string
;
123 int seen_hash
= 0, seen_zero
= 0, lcount
= 0, seen_prec
= 0;
124 int seen_space
= 0, seen_plus
= 0;
125 int seen_big_l
= 0, seen_h
= 0, seen_big_h
= 0;
126 int seen_big_d
= 0, seen_double_big_d
= 0;
130 bool seen_i64
= false;
132 /* Skip over "%%", it will become part of a literal piece. */
139 sub_start
= current_substring
;
141 strncpy (current_substring
, prev_start
, f
- 1 - prev_start
);
142 current_substring
+= f
- 1 - prev_start
;
143 *current_substring
++ = '\0';
145 if (*sub_start
!= '\0')
146 m_pieces
.emplace_back (sub_start
, literal_piece
, 0);
150 /* Check the validity of the format specifier, and work
151 out what argument it expects. We only accept C89
152 format strings, with the exception of long long (which
155 /* The first part of a format specifier is a set of flag
157 while (*f
!= '\0' && strchr ("0-+ #", *f
))
170 /* The next part of a format specifier is a width. */
171 if (gdb_extensions
&& *f
== '*')
178 while (*f
!= '\0' && strchr ("0123456789", *f
))
182 /* The next part of a format specifier is a precision. */
187 if (gdb_extensions
&& *f
== '*')
194 while (*f
!= '\0' && strchr ("0123456789", *f
))
199 /* The next part of a format specifier is a length modifier. */
220 /* Decimal32 modifier. */
225 /* Decimal64 and Decimal128 modifiers. */
228 /* Check for a Decimal128. */
232 seen_double_big_d
= 1;
238 /* For size_t or ssize_t. */
243 /* Support the Windows '%I64' extension, because an
244 earlier call to format_pieces might have converted %lld
246 if (f
[1] == '6' && f
[2] == '4')
265 if (seen_space
|| seen_plus
)
272 this_argclass
= size_t_arg
;
273 else if (lcount
== 0)
274 this_argclass
= int_arg
;
275 else if (lcount
== 1)
276 this_argclass
= long_arg
;
278 this_argclass
= long_long_arg
;
285 this_argclass
= lcount
== 0 ? int_arg
: wide_char_arg
;
286 if (lcount
> 1 || seen_h
|| seen_big_l
)
288 if (seen_prec
|| seen_zero
|| seen_space
|| seen_plus
)
293 this_argclass
= ptr_arg
;
294 if (lcount
|| seen_h
|| seen_big_l
)
298 if (seen_hash
|| seen_zero
|| seen_space
|| seen_plus
)
317 this_argclass
= lcount
== 0 ? string_arg
: wide_string_arg
;
318 if (lcount
> 1 || seen_h
|| seen_big_l
)
320 if (seen_zero
|| seen_space
|| seen_plus
)
329 if (seen_double_big_d
)
330 this_argclass
= dec128float_arg
;
332 this_argclass
= dec64float_arg
;
334 this_argclass
= dec32float_arg
;
336 this_argclass
= long_double_arg
;
338 this_argclass
= double_arg
;
340 if (lcount
|| seen_h
)
345 if (!value_extension
)
346 error (_("Unrecognized format specifier '%c' in printf"), *f
);
348 if (lcount
> 1 || seen_h
|| seen_big_h
|| seen_big_h
349 || seen_big_d
|| seen_double_big_d
|| seen_size_t
350 || seen_prec
|| seen_zero
|| seen_space
|| seen_plus
)
353 this_argclass
= value_arg
;
357 /* Move F forward to the next ']' character if such a
358 character exists, otherwise leave F unchanged. */
359 const char *tmp
= strchr (f
, ']');
366 error (_("`*' not supported for precision or width in printf"));
369 error (_("Format specifier `n' not supported in printf"));
372 error (_("Incomplete format specifier at end of format string"));
375 error (_("Unrecognized format specifier '%c' in printf"), *f
);
379 error (_("Inappropriate modifiers to "
380 "format specifier '%c' in printf"),
385 sub_start
= current_substring
;
387 if (lcount
> 1 && !seen_i64
&& USE_PRINTF_I64
)
389 /* Windows' printf does support long long, but not the usual way.
390 Convert %lld to %I64d. */
391 int length_before_ll
= f
- percent_loc
- 1 - lcount
;
393 strncpy (current_substring
, percent_loc
, length_before_ll
);
394 strcpy (current_substring
+ length_before_ll
, "I64");
395 current_substring
[length_before_ll
+ 3] =
396 percent_loc
[length_before_ll
+ lcount
];
397 current_substring
+= length_before_ll
+ 4;
399 else if (this_argclass
== wide_string_arg
400 || this_argclass
== wide_char_arg
)
402 /* Convert %ls or %lc to %s. */
403 int length_before_ls
= f
- percent_loc
- 2;
405 strncpy (current_substring
, percent_loc
, length_before_ls
);
406 strcpy (current_substring
+ length_before_ls
, "s");
407 current_substring
+= length_before_ls
+ 2;
411 strncpy (current_substring
, percent_loc
, f
- percent_loc
);
412 current_substring
+= f
- percent_loc
;
415 *current_substring
++ = '\0';
419 m_pieces
.emplace_back (sub_start
, this_argclass
, n_int_args
);
422 /* Record the remainder of the string. */
426 sub_start
= current_substring
;
428 strncpy (current_substring
, prev_start
, f
- prev_start
);
429 current_substring
+= f
- prev_start
;
430 *current_substring
++ = '\0';
432 m_pieces
.emplace_back (sub_start
, literal_piece
, 0);