]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gdbsupport/format.cc
Update copyright year range in header of all files managed by GDB
[thirdparty/binutils-gdb.git] / gdbsupport / format.cc
CommitLineData
d3ce09f5
SS
1/* Parse a printf-style format string.
2
1d506c26 3 Copyright (C) 1986-2024 Free Software Foundation, Inc.
d3ce09f5
SS
4
5 This file is part of GDB.
6
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.
11
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.
16
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/>. */
19
727605ca 20#include "common-defs.h"
d3ce09f5
SS
21#include "format.h"
22
901682e4
AB
23format_pieces::format_pieces (const char **arg, bool gdb_extensions,
24 bool value_extension)
d3ce09f5 25{
bbc13ae3 26 const char *s;
2a3c1174 27 const char *string;
bbc13ae3
KS
28 const char *prev_start;
29 const char *percent_loc;
d3ce09f5 30 char *sub_start, *current_substring;
d3ce09f5
SS
31 enum argclass this_argclass;
32
33 s = *arg;
34
2a3c1174
PA
35 if (gdb_extensions)
36 {
37 string = *arg;
38 *arg += strlen (*arg);
39 }
40 else
41 {
42 /* Parse the format-control string and copy it into the string STRING,
43 processing some kinds of escape sequence. */
d3ce09f5 44
2a3c1174
PA
45 char *f = (char *) alloca (strlen (s) + 1);
46 string = f;
d3ce09f5 47
901682e4 48 while (*s != '"' && *s != '\0')
d3ce09f5 49 {
2a3c1174
PA
50 int c = *s++;
51 switch (c)
d3ce09f5 52 {
2a3c1174
PA
53 case '\0':
54 continue;
55
d3ce09f5 56 case '\\':
2a3c1174
PA
57 switch (c = *s++)
58 {
59 case '\\':
60 *f++ = '\\';
61 break;
62 case 'a':
63 *f++ = '\a';
64 break;
65 case 'b':
66 *f++ = '\b';
67 break;
68 case 'e':
69 *f++ = '\e';
70 break;
71 case 'f':
72 *f++ = '\f';
73 break;
74 case 'n':
75 *f++ = '\n';
76 break;
77 case 'r':
78 *f++ = '\r';
79 break;
80 case 't':
81 *f++ = '\t';
82 break;
83 case 'v':
84 *f++ = '\v';
85 break;
86 case '"':
87 *f++ = '"';
88 break;
89 default:
90 /* ??? TODO: handle other escape sequences. */
91 error (_("Unrecognized escape character \\%c in format string."),
92 c);
93 }
d3ce09f5 94 break;
2a3c1174 95
d3ce09f5 96 default:
2a3c1174 97 *f++ = c;
d3ce09f5 98 }
d3ce09f5 99 }
d3ce09f5 100
2a3c1174
PA
101 /* Terminate our escape-processed copy. */
102 *f++ = '\0';
d3ce09f5 103
2a3c1174
PA
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. */
106 *arg = s;
107 }
d3ce09f5
SS
108
109 /* Need extra space for the '\0's. Doubling the size is sufficient. */
110
224c3ddb 111 current_substring = (char *) xmalloc (strlen (string) * 2 + 1000);
8e481c3b 112 m_storage.reset (current_substring);
d3ce09f5
SS
113
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. */
117
2a3c1174 118 const char *f = string;
d3ce09f5
SS
119 prev_start = string;
120 while (*f)
121 if (*f++ == '%')
122 {
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;
e06f3d6e 127 int seen_size_t = 0;
d3ce09f5 128 int bad = 0;
2a3c1174 129 int n_int_args = 0;
6ba18521 130 bool seen_i64 = false;
d3ce09f5
SS
131
132 /* Skip over "%%", it will become part of a literal piece. */
133 if (*f == '%')
134 {
135 f++;
136 continue;
137 }
138
139 sub_start = current_substring;
140
141 strncpy (current_substring, prev_start, f - 1 - prev_start);
142 current_substring += f - 1 - prev_start;
143 *current_substring++ = '\0';
144
0dfe5bfb 145 if (*sub_start != '\0')
2a3c1174 146 m_pieces.emplace_back (sub_start, literal_piece, 0);
d3ce09f5
SS
147
148 percent_loc = f - 1;
149
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
153 we autoconf for). */
154
155 /* The first part of a format specifier is a set of flag
156 characters. */
5ea5559b 157 while (*f != '\0' && strchr ("0-+ #", *f))
d3ce09f5
SS
158 {
159 if (*f == '#')
160 seen_hash = 1;
161 else if (*f == '0')
162 seen_zero = 1;
163 else if (*f == ' ')
164 seen_space = 1;
165 else if (*f == '+')
166 seen_plus = 1;
167 f++;
168 }
169
170 /* The next part of a format specifier is a width. */
2a3c1174
PA
171 if (gdb_extensions && *f == '*')
172 {
173 ++f;
174 ++n_int_args;
175 }
176 else
177 {
178 while (*f != '\0' && strchr ("0123456789", *f))
179 f++;
180 }
d3ce09f5
SS
181
182 /* The next part of a format specifier is a precision. */
183 if (*f == '.')
184 {
185 seen_prec = 1;
186 f++;
2a3c1174
PA
187 if (gdb_extensions && *f == '*')
188 {
189 ++f;
190 ++n_int_args;
191 }
192 else
193 {
194 while (*f != '\0' && strchr ("0123456789", *f))
195 f++;
196 }
d3ce09f5
SS
197 }
198
199 /* The next part of a format specifier is a length modifier. */
6ba18521 200 switch (*f)
d3ce09f5 201 {
6ba18521 202 case 'h':
d3ce09f5
SS
203 seen_h = 1;
204 f++;
6ba18521
TT
205 break;
206 case 'l':
d3ce09f5
SS
207 f++;
208 lcount++;
209 if (*f == 'l')
210 {
211 f++;
212 lcount++;
213 }
6ba18521
TT
214 break;
215 case 'L':
d3ce09f5
SS
216 seen_big_l = 1;
217 f++;
6ba18521
TT
218 break;
219 case 'H':
220 /* Decimal32 modifier. */
d3ce09f5
SS
221 seen_big_h = 1;
222 f++;
6ba18521
TT
223 break;
224 case 'D':
225 /* Decimal64 and Decimal128 modifiers. */
d3ce09f5
SS
226 f++;
227
228 /* Check for a Decimal128. */
229 if (*f == 'D')
230 {
231 f++;
232 seen_double_big_d = 1;
233 }
234 else
235 seen_big_d = 1;
6ba18521
TT
236 break;
237 case 'z':
238 /* For size_t or ssize_t. */
e06f3d6e
AB
239 seen_size_t = 1;
240 f++;
6ba18521
TT
241 break;
242 case 'I':
243 /* Support the Windows '%I64' extension, because an
244 earlier call to format_pieces might have converted %lld
245 to %I64d. */
246 if (f[1] == '6' && f[2] == '4')
247 {
248 f += 3;
249 lcount = 2;
250 seen_i64 = true;
251 }
252 break;
253 }
d3ce09f5
SS
254
255 switch (*f)
256 {
257 case 'u':
258 if (seen_hash)
259 bad = 1;
d182e398 260 [[fallthrough]];
d3ce09f5
SS
261
262 case 'o':
263 case 'x':
264 case 'X':
265 if (seen_space || seen_plus)
266 bad = 1;
d182e398 267 [[fallthrough]];
d3ce09f5
SS
268
269 case 'd':
270 case 'i':
e06f3d6e
AB
271 if (seen_size_t)
272 this_argclass = size_t_arg;
273 else if (lcount == 0)
d3ce09f5
SS
274 this_argclass = int_arg;
275 else if (lcount == 1)
276 this_argclass = long_arg;
277 else
278 this_argclass = long_long_arg;
4ff3ce77
DE
279
280 if (seen_big_l)
281 bad = 1;
282 break;
d3ce09f5
SS
283
284 case 'c':
285 this_argclass = lcount == 0 ? int_arg : wide_char_arg;
286 if (lcount > 1 || seen_h || seen_big_l)
287 bad = 1;
288 if (seen_prec || seen_zero || seen_space || seen_plus)
289 bad = 1;
290 break;
291
292 case 'p':
293 this_argclass = ptr_arg;
294 if (lcount || seen_h || seen_big_l)
295 bad = 1;
5c30d39a
AB
296 if (seen_prec)
297 bad = 1;
298 if (seen_hash || seen_zero || seen_space || seen_plus)
d3ce09f5 299 bad = 1;
2a3c1174
PA
300
301 if (gdb_extensions)
302 {
303 switch (f[1])
304 {
305 case 's':
306 case 'F':
307 case '[':
308 case ']':
309 f++;
310 break;
311 }
312 }
313
d3ce09f5
SS
314 break;
315
316 case 's':
317 this_argclass = lcount == 0 ? string_arg : wide_string_arg;
318 if (lcount > 1 || seen_h || seen_big_l)
319 bad = 1;
320 if (seen_zero || seen_space || seen_plus)
321 bad = 1;
322 break;
323
324 case 'e':
325 case 'f':
326 case 'g':
327 case 'E':
328 case 'G':
16e812b2
UW
329 if (seen_double_big_d)
330 this_argclass = dec128float_arg;
331 else if (seen_big_d)
332 this_argclass = dec64float_arg;
333 else if (seen_big_h)
334 this_argclass = dec32float_arg;
d3ce09f5
SS
335 else if (seen_big_l)
336 this_argclass = long_double_arg;
337 else
338 this_argclass = double_arg;
339
4ff3ce77
DE
340 if (lcount || seen_h)
341 bad = 1;
342 break;
d3ce09f5 343
901682e4
AB
344 case 'V':
345 if (!value_extension)
346 error (_("Unrecognized format specifier '%c' in printf"), *f);
347
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)
351 bad = 1;
352
353 this_argclass = value_arg;
354
355 if (f[1] == '[')
356 {
357 /* Move F forward to the next ']' character if such a
358 character exists, otherwise leave F unchanged. */
359 const char *tmp = strchr (f, ']');
360 if (tmp != nullptr)
361 f = tmp;
362 }
363 break;
364
d3ce09f5
SS
365 case '*':
366 error (_("`*' not supported for precision or width in printf"));
367
368 case 'n':
369 error (_("Format specifier `n' not supported in printf"));
370
371 case '\0':
372 error (_("Incomplete format specifier at end of format string"));
373
374 default:
375 error (_("Unrecognized format specifier '%c' in printf"), *f);
376 }
377
378 if (bad)
379 error (_("Inappropriate modifiers to "
380 "format specifier '%c' in printf"),
381 *f);
382
383 f++;
384
385 sub_start = current_substring;
386
6ba18521 387 if (lcount > 1 && !seen_i64 && USE_PRINTF_I64)
d3ce09f5
SS
388 {
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;
392
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;
398 }
399 else if (this_argclass == wide_string_arg
400 || this_argclass == wide_char_arg)
401 {
402 /* Convert %ls or %lc to %s. */
403 int length_before_ls = f - percent_loc - 2;
404
405 strncpy (current_substring, percent_loc, length_before_ls);
406 strcpy (current_substring + length_before_ls, "s");
407 current_substring += length_before_ls + 2;
408 }
409 else
410 {
411 strncpy (current_substring, percent_loc, f - percent_loc);
412 current_substring += f - percent_loc;
413 }
414
415 *current_substring++ = '\0';
416
417 prev_start = f;
418
2a3c1174 419 m_pieces.emplace_back (sub_start, this_argclass, n_int_args);
d3ce09f5
SS
420 }
421
422 /* Record the remainder of the string. */
423
0dfe5bfb
TT
424 if (f > prev_start)
425 {
426 sub_start = current_substring;
d3ce09f5 427
0dfe5bfb
TT
428 strncpy (current_substring, prev_start, f - prev_start);
429 current_substring += f - prev_start;
430 *current_substring++ = '\0';
d3ce09f5 431
2a3c1174 432 m_pieces.emplace_back (sub_start, literal_piece, 0);
0dfe5bfb 433 }
d3ce09f5 434}