1 /* Parse a printf-style format string.
3 Copyright (C) 1986-2022 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)
27 const char *prev_start;
28 const char *percent_loc;
29 char *sub_start, *current_substring;
30 enum argclass this_argclass;
37 *arg += strlen (*arg);
41 /* Parse the format-control string and copy it into the string STRING,
42 processing some kinds of escape sequence. */
44 char *f = (char *) alloca (strlen (s) + 1);
47 while ((gdb_extensions || *s != '"') && *s != '\0')
89 /* ??? TODO: handle other escape sequences. */
90 error (_("Unrecognized escape character \\%c in format string."),
100 /* Terminate our escape-processed copy. */
103 /* Whether the format string ended with double-quote or zero, we're
104 done with it; it's up to callers to complain about syntax. */
108 /* Need extra space for the '\0's. Doubling the size is sufficient. */
110 current_substring = (char *) xmalloc (strlen (string) * 2 + 1000);
111 m_storage.reset (current_substring);
113 /* Now scan the string for %-specs and see what kinds of args they want.
114 argclass classifies the %-specs so we can give printf-type functions
115 something of the right size. */
117 const char *f = string;
122 int seen_hash = 0, seen_zero = 0, lcount = 0, seen_prec = 0;
123 int seen_space = 0, seen_plus = 0;
124 int seen_big_l = 0, seen_h = 0, seen_big_h = 0;
125 int seen_big_d = 0, seen_double_big_d = 0;
129 bool seen_i64 = false;
131 /* Skip over "%%", it will become part of a literal piece. */
138 sub_start = current_substring;
140 strncpy (current_substring, prev_start, f - 1 - prev_start);
141 current_substring += f - 1 - prev_start;
142 *current_substring++ = '\0';
144 if (*sub_start != '\0')
145 m_pieces.emplace_back (sub_start, literal_piece, 0);
149 /* Check the validity of the format specifier, and work
150 out what argument it expects. We only accept C89
151 format strings, with the exception of long long (which
154 /* The first part of a format specifier is a set of flag
156 while (*f != '\0' && strchr ("0-+ #", *f))
169 /* The next part of a format specifier is a width. */
170 if (gdb_extensions && *f == '*')
177 while (*f != '\0' && strchr ("0123456789", *f))
181 /* The next part of a format specifier is a precision. */
186 if (gdb_extensions && *f == '*')
193 while (*f != '\0' && strchr ("0123456789", *f))
198 /* The next part of a format specifier is a length modifier. */
219 /* Decimal32 modifier. */
224 /* Decimal64 and Decimal128 modifiers. */
227 /* Check for a Decimal128. */
231 seen_double_big_d = 1;
237 /* For size_t or ssize_t. */
242 /* Support the Windows '%I64' extension, because an
243 earlier call to format_pieces might have converted %lld
245 if (f[1] == '6' && f[2] == '4')
264 if (seen_space || seen_plus)
271 this_argclass = size_t_arg;
272 else if (lcount == 0)
273 this_argclass = int_arg;
274 else if (lcount == 1)
275 this_argclass = long_arg;
277 this_argclass = long_long_arg;
284 this_argclass = lcount == 0 ? int_arg : wide_char_arg;
285 if (lcount > 1 || seen_h || seen_big_l)
287 if (seen_prec || seen_zero || seen_space || seen_plus)
292 this_argclass = ptr_arg;
293 if (lcount || seen_h || seen_big_l)
297 if (seen_hash || seen_zero || seen_space || seen_plus)
316 this_argclass = lcount == 0 ? string_arg : wide_string_arg;
317 if (lcount > 1 || seen_h || seen_big_l)
319 if (seen_zero || seen_space || seen_plus)
328 if (seen_double_big_d)
329 this_argclass = dec128float_arg;
331 this_argclass = dec64float_arg;
333 this_argclass = dec32float_arg;
335 this_argclass = long_double_arg;
337 this_argclass = double_arg;
339 if (lcount || seen_h)
344 error (_("`*' not supported for precision or width in printf"));
347 error (_("Format specifier `n' not supported in printf"));
350 error (_("Incomplete format specifier at end of format string"));
353 error (_("Unrecognized format specifier '%c' in printf"), *f);
357 error (_("Inappropriate modifiers to "
358 "format specifier '%c' in printf"),
363 sub_start = current_substring;
365 if (lcount > 1 && !seen_i64 && USE_PRINTF_I64)
367 /* Windows' printf does support long long, but not the usual way.
368 Convert %lld to %I64d. */
369 int length_before_ll = f - percent_loc - 1 - lcount;
371 strncpy (current_substring, percent_loc, length_before_ll);
372 strcpy (current_substring + length_before_ll, "I64");
373 current_substring[length_before_ll + 3] =
374 percent_loc[length_before_ll + lcount];
375 current_substring += length_before_ll + 4;
377 else if (this_argclass == wide_string_arg
378 || this_argclass == wide_char_arg)
380 /* Convert %ls or %lc to %s. */
381 int length_before_ls = f - percent_loc - 2;
383 strncpy (current_substring, percent_loc, length_before_ls);
384 strcpy (current_substring + length_before_ls, "s");
385 current_substring += length_before_ls + 2;
389 strncpy (current_substring, percent_loc, f - percent_loc);
390 current_substring += f - percent_loc;
393 *current_substring++ = '\0';
397 m_pieces.emplace_back (sub_start, this_argclass, n_int_args);
400 /* Record the remainder of the string. */
404 sub_start = current_substring;
406 strncpy (current_substring, prev_start, f - prev_start);
407 current_substring += f - prev_start;
408 *current_substring++ = '\0';
410 m_pieces.emplace_back (sub_start, literal_piece, 0);