Remove path name from test case
[binutils-gdb.git] / gdbsupport / format.cc
1 /* Parse a printf-style format string.
2
3 Copyright (C) 1986-2023 Free Software Foundation, Inc.
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
20 #include "common-defs.h"
21 #include "format.h"
22
23 format_pieces::format_pieces (const char **arg, bool gdb_extensions,
24 bool value_extension)
25 {
26 const char *s;
27 const char *string;
28 const char *prev_start;
29 const char *percent_loc;
30 char *sub_start, *current_substring;
31 enum argclass this_argclass;
32
33 s = *arg;
34
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. */
44
45 char *f = (char *) alloca (strlen (s) + 1);
46 string = f;
47
48 while (*s != '"' && *s != '\0')
49 {
50 int c = *s++;
51 switch (c)
52 {
53 case '\0':
54 continue;
55
56 case '\\':
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 }
94 break;
95
96 default:
97 *f++ = c;
98 }
99 }
100
101 /* Terminate our escape-processed copy. */
102 *f++ = '\0';
103
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 }
108
109 /* Need extra space for the '\0's. Doubling the size is sufficient. */
110
111 current_substring = (char *) xmalloc (strlen (string) * 2 + 1000);
112 m_storage.reset (current_substring);
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
118 const char *f = string;
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;
127 int seen_size_t = 0;
128 int bad = 0;
129 int n_int_args = 0;
130 bool seen_i64 = false;
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
145 if (*sub_start != '\0')
146 m_pieces.emplace_back (sub_start, literal_piece, 0);
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. */
157 while (*f != '\0' && strchr ("0-+ #", *f))
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. */
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 }
181
182 /* The next part of a format specifier is a precision. */
183 if (*f == '.')
184 {
185 seen_prec = 1;
186 f++;
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 }
197 }
198
199 /* The next part of a format specifier is a length modifier. */
200 switch (*f)
201 {
202 case 'h':
203 seen_h = 1;
204 f++;
205 break;
206 case 'l':
207 f++;
208 lcount++;
209 if (*f == 'l')
210 {
211 f++;
212 lcount++;
213 }
214 break;
215 case 'L':
216 seen_big_l = 1;
217 f++;
218 break;
219 case 'H':
220 /* Decimal32 modifier. */
221 seen_big_h = 1;
222 f++;
223 break;
224 case 'D':
225 /* Decimal64 and Decimal128 modifiers. */
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;
236 break;
237 case 'z':
238 /* For size_t or ssize_t. */
239 seen_size_t = 1;
240 f++;
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 }
254
255 switch (*f)
256 {
257 case 'u':
258 if (seen_hash)
259 bad = 1;
260 /* FALLTHROUGH */
261
262 case 'o':
263 case 'x':
264 case 'X':
265 if (seen_space || seen_plus)
266 bad = 1;
267 /* FALLTHROUGH */
268
269 case 'd':
270 case 'i':
271 if (seen_size_t)
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;
277 else
278 this_argclass = long_long_arg;
279
280 if (seen_big_l)
281 bad = 1;
282 break;
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;
296 if (seen_prec)
297 bad = 1;
298 if (seen_hash || seen_zero || seen_space || seen_plus)
299 bad = 1;
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
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':
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;
335 else if (seen_big_l)
336 this_argclass = long_double_arg;
337 else
338 this_argclass = double_arg;
339
340 if (lcount || seen_h)
341 bad = 1;
342 break;
343
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
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
387 if (lcount > 1 && !seen_i64 && USE_PRINTF_I64)
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
419 m_pieces.emplace_back (sub_start, this_argclass, n_int_args);
420 }
421
422 /* Record the remainder of the string. */
423
424 if (f > prev_start)
425 {
426 sub_start = current_substring;
427
428 strncpy (current_substring, prev_start, f - prev_start);
429 current_substring += f - prev_start;
430 *current_substring++ = '\0';
431
432 m_pieces.emplace_back (sub_start, literal_piece, 0);
433 }
434 }