Don't create empty literal pieces
[deliverable/binutils-gdb.git] / gdb / gdbsupport / format.c
CommitLineData
d3ce09f5
SS
1/* Parse a printf-style format string.
2
42a4f53d 3 Copyright (C) 1986-2019 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
8e481c3b 23format_pieces::format_pieces (const char **arg)
d3ce09f5 24{
bbc13ae3
KS
25 const char *s;
26 char *f, *string;
27 const char *prev_start;
28 const char *percent_loc;
d3ce09f5 29 char *sub_start, *current_substring;
d3ce09f5
SS
30 enum argclass this_argclass;
31
32 s = *arg;
33
34 /* Parse the format-control string and copy it into the string STRING,
35 processing some kinds of escape sequence. */
36
37 f = string = (char *) alloca (strlen (s) + 1);
38
39 while (*s != '"' && *s != '\0')
40 {
41 int c = *s++;
42 switch (c)
43 {
44 case '\0':
45 continue;
46
47 case '\\':
48 switch (c = *s++)
49 {
50 case '\\':
51 *f++ = '\\';
52 break;
53 case 'a':
54 *f++ = '\a';
55 break;
56 case 'b':
57 *f++ = '\b';
58 break;
b17992c1
SM
59 case 'e':
60 *f++ = '\e';
61 break;
d3ce09f5
SS
62 case 'f':
63 *f++ = '\f';
64 break;
65 case 'n':
66 *f++ = '\n';
67 break;
68 case 'r':
69 *f++ = '\r';
70 break;
71 case 't':
72 *f++ = '\t';
73 break;
74 case 'v':
75 *f++ = '\v';
76 break;
77 case '"':
78 *f++ = '"';
79 break;
80 default:
81 /* ??? TODO: handle other escape sequences. */
82 error (_("Unrecognized escape character \\%c in format string."),
83 c);
84 }
85 break;
86
87 default:
88 *f++ = c;
89 }
90 }
91
92 /* Terminate our escape-processed copy. */
93 *f++ = '\0';
94
95 /* Whether the format string ended with double-quote or zero, we're
96 done with it; it's up to callers to complain about syntax. */
97 *arg = s;
98
99 /* Need extra space for the '\0's. Doubling the size is sufficient. */
100
224c3ddb 101 current_substring = (char *) xmalloc (strlen (string) * 2 + 1000);
8e481c3b 102 m_storage.reset (current_substring);
d3ce09f5
SS
103
104 /* Now scan the string for %-specs and see what kinds of args they want.
105 argclass classifies the %-specs so we can give printf-type functions
106 something of the right size. */
107
108 f = string;
109 prev_start = string;
110 while (*f)
111 if (*f++ == '%')
112 {
113 int seen_hash = 0, seen_zero = 0, lcount = 0, seen_prec = 0;
114 int seen_space = 0, seen_plus = 0;
115 int seen_big_l = 0, seen_h = 0, seen_big_h = 0;
116 int seen_big_d = 0, seen_double_big_d = 0;
117 int bad = 0;
118
119 /* Skip over "%%", it will become part of a literal piece. */
120 if (*f == '%')
121 {
122 f++;
123 continue;
124 }
125
126 sub_start = current_substring;
127
128 strncpy (current_substring, prev_start, f - 1 - prev_start);
129 current_substring += f - 1 - prev_start;
130 *current_substring++ = '\0';
131
0dfe5bfb
TT
132 if (*sub_start != '\0')
133 m_pieces.emplace_back (sub_start, literal_piece);
d3ce09f5
SS
134
135 percent_loc = f - 1;
136
137 /* Check the validity of the format specifier, and work
138 out what argument it expects. We only accept C89
139 format strings, with the exception of long long (which
140 we autoconf for). */
141
142 /* The first part of a format specifier is a set of flag
143 characters. */
5ea5559b 144 while (*f != '\0' && strchr ("0-+ #", *f))
d3ce09f5
SS
145 {
146 if (*f == '#')
147 seen_hash = 1;
148 else if (*f == '0')
149 seen_zero = 1;
150 else if (*f == ' ')
151 seen_space = 1;
152 else if (*f == '+')
153 seen_plus = 1;
154 f++;
155 }
156
157 /* The next part of a format specifier is a width. */
5ea5559b 158 while (*f != '\0' && strchr ("0123456789", *f))
d3ce09f5
SS
159 f++;
160
161 /* The next part of a format specifier is a precision. */
162 if (*f == '.')
163 {
164 seen_prec = 1;
165 f++;
5ea5559b 166 while (*f != '\0' && strchr ("0123456789", *f))
d3ce09f5
SS
167 f++;
168 }
169
170 /* The next part of a format specifier is a length modifier. */
171 if (*f == 'h')
172 {
173 seen_h = 1;
174 f++;
175 }
176 else if (*f == 'l')
177 {
178 f++;
179 lcount++;
180 if (*f == 'l')
181 {
182 f++;
183 lcount++;
184 }
185 }
186 else if (*f == 'L')
187 {
188 seen_big_l = 1;
189 f++;
190 }
191 /* Decimal32 modifier. */
192 else if (*f == 'H')
193 {
194 seen_big_h = 1;
195 f++;
196 }
197 /* Decimal64 and Decimal128 modifiers. */
198 else if (*f == 'D')
199 {
200 f++;
201
202 /* Check for a Decimal128. */
203 if (*f == 'D')
204 {
205 f++;
206 seen_double_big_d = 1;
207 }
208 else
209 seen_big_d = 1;
210 }
211
212 switch (*f)
213 {
214 case 'u':
215 if (seen_hash)
216 bad = 1;
217 /* FALLTHROUGH */
218
219 case 'o':
220 case 'x':
221 case 'X':
222 if (seen_space || seen_plus)
223 bad = 1;
224 /* FALLTHROUGH */
225
226 case 'd':
227 case 'i':
228 if (lcount == 0)
229 this_argclass = int_arg;
230 else if (lcount == 1)
231 this_argclass = long_arg;
232 else
233 this_argclass = long_long_arg;
4ff3ce77
DE
234
235 if (seen_big_l)
236 bad = 1;
237 break;
d3ce09f5
SS
238
239 case 'c':
240 this_argclass = lcount == 0 ? int_arg : wide_char_arg;
241 if (lcount > 1 || seen_h || seen_big_l)
242 bad = 1;
243 if (seen_prec || seen_zero || seen_space || seen_plus)
244 bad = 1;
245 break;
246
247 case 'p':
248 this_argclass = ptr_arg;
249 if (lcount || seen_h || seen_big_l)
250 bad = 1;
5c30d39a
AB
251 if (seen_prec)
252 bad = 1;
253 if (seen_hash || seen_zero || seen_space || seen_plus)
d3ce09f5
SS
254 bad = 1;
255 break;
256
257 case 's':
258 this_argclass = lcount == 0 ? string_arg : wide_string_arg;
259 if (lcount > 1 || seen_h || seen_big_l)
260 bad = 1;
261 if (seen_zero || seen_space || seen_plus)
262 bad = 1;
263 break;
264
265 case 'e':
266 case 'f':
267 case 'g':
268 case 'E':
269 case 'G':
16e812b2
UW
270 if (seen_double_big_d)
271 this_argclass = dec128float_arg;
272 else if (seen_big_d)
273 this_argclass = dec64float_arg;
274 else if (seen_big_h)
275 this_argclass = dec32float_arg;
d3ce09f5
SS
276 else if (seen_big_l)
277 this_argclass = long_double_arg;
278 else
279 this_argclass = double_arg;
280
4ff3ce77
DE
281 if (lcount || seen_h)
282 bad = 1;
283 break;
d3ce09f5
SS
284
285 case '*':
286 error (_("`*' not supported for precision or width in printf"));
287
288 case 'n':
289 error (_("Format specifier `n' not supported in printf"));
290
291 case '\0':
292 error (_("Incomplete format specifier at end of format string"));
293
294 default:
295 error (_("Unrecognized format specifier '%c' in printf"), *f);
296 }
297
298 if (bad)
299 error (_("Inappropriate modifiers to "
300 "format specifier '%c' in printf"),
301 *f);
302
303 f++;
304
305 sub_start = current_substring;
306
307 if (lcount > 1 && USE_PRINTF_I64)
308 {
309 /* Windows' printf does support long long, but not the usual way.
310 Convert %lld to %I64d. */
311 int length_before_ll = f - percent_loc - 1 - lcount;
312
313 strncpy (current_substring, percent_loc, length_before_ll);
314 strcpy (current_substring + length_before_ll, "I64");
315 current_substring[length_before_ll + 3] =
316 percent_loc[length_before_ll + lcount];
317 current_substring += length_before_ll + 4;
318 }
319 else if (this_argclass == wide_string_arg
320 || this_argclass == wide_char_arg)
321 {
322 /* Convert %ls or %lc to %s. */
323 int length_before_ls = f - percent_loc - 2;
324
325 strncpy (current_substring, percent_loc, length_before_ls);
326 strcpy (current_substring + length_before_ls, "s");
327 current_substring += length_before_ls + 2;
328 }
329 else
330 {
331 strncpy (current_substring, percent_loc, f - percent_loc);
332 current_substring += f - percent_loc;
333 }
334
335 *current_substring++ = '\0';
336
337 prev_start = f;
338
8e481c3b 339 m_pieces.emplace_back (sub_start, this_argclass);
d3ce09f5
SS
340 }
341
342 /* Record the remainder of the string. */
343
0dfe5bfb
TT
344 if (f > prev_start)
345 {
346 sub_start = current_substring;
d3ce09f5 347
0dfe5bfb
TT
348 strncpy (current_substring, prev_start, f - prev_start);
349 current_substring += f - prev_start;
350 *current_substring++ = '\0';
d3ce09f5 351
0dfe5bfb
TT
352 m_pieces.emplace_back (sub_start, literal_piece);
353 }
d3ce09f5 354}
This page took 0.495183 seconds and 4 git commands to generate.