diff options
Diffstat (limited to 'gdb/common')
-rw-r--r-- | gdb/common/ax.def | 2 | ||||
-rw-r--r-- | gdb/common/format.c | 400 | ||||
-rw-r--r-- | gdb/common/format.h | 63 |
3 files changed, 465 insertions, 0 deletions
diff --git a/gdb/common/ax.def b/gdb/common/ax.def index fdc043f..b08ede3 100644 --- a/gdb/common/ax.def +++ b/gdb/common/ax.def @@ -93,3 +93,5 @@ DEFOP (invalid2, 0, 0, 0, 0, 0x31) express the right thing. */ DEFOP (pick, 1, 0, 0, 1, 0x32) DEFOP (rot, 0, 0, 3, 3, 0x33) +/* Both the argument and consumed numbers are dynamic for this one. */ +DEFOP (printf, 0, 0, 0, 0, 0x34) diff --git a/gdb/common/format.c b/gdb/common/format.c new file mode 100644 index 0000000..9c22b6d --- /dev/null +++ b/gdb/common/format.c @@ -0,0 +1,400 @@ +/* Parse a printf-style format string. + + Copyright (C) 1986-2012 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifdef GDBSERVER +#include "server.h" +#else +#include "defs.h" +#endif + +#include <string.h> + +#include "format.h" + +struct format_piece * +parse_format_string (char **arg) +{ + char *s, *f, *string; + char *prev_start; + char *percent_loc; + char *sub_start, *current_substring; + struct format_piece *pieces; + int next_frag; + int max_pieces; + enum argclass this_argclass; + + s = *arg; + + /* Parse the format-control string and copy it into the string STRING, + processing some kinds of escape sequence. */ + + f = string = (char *) alloca (strlen (s) + 1); + + while (*s != '"' && *s != '\0') + { + int c = *s++; + switch (c) + { + case '\0': + continue; + + case '\\': + switch (c = *s++) + { + case '\\': + *f++ = '\\'; + break; + case 'a': + *f++ = '\a'; + break; + case 'b': + *f++ = '\b'; + break; + case 'f': + *f++ = '\f'; + break; + case 'n': + *f++ = '\n'; + break; + case 'r': + *f++ = '\r'; + break; + case 't': + *f++ = '\t'; + break; + case 'v': + *f++ = '\v'; + break; + case '"': + *f++ = '"'; + break; + default: + /* ??? TODO: handle other escape sequences. */ + error (_("Unrecognized escape character \\%c in format string."), + c); + } + break; + + default: + *f++ = c; + } + } + + /* Terminate our escape-processed copy. */ + *f++ = '\0'; + + /* Whether the format string ended with double-quote or zero, we're + done with it; it's up to callers to complain about syntax. */ + *arg = s; + + /* Need extra space for the '\0's. Doubling the size is sufficient. */ + + current_substring = xmalloc (strlen (string) * 2 + 1000); + + max_pieces = strlen (string) + 2; + + pieces = (struct format_piece *) + xmalloc (max_pieces * sizeof (struct format_piece)); + + next_frag = 0; + + /* Now scan the string for %-specs and see what kinds of args they want. + argclass classifies the %-specs so we can give printf-type functions + something of the right size. */ + + f = string; + prev_start = string; + while (*f) + if (*f++ == '%') + { + int seen_hash = 0, seen_zero = 0, lcount = 0, seen_prec = 0; + int seen_space = 0, seen_plus = 0; + int seen_big_l = 0, seen_h = 0, seen_big_h = 0; + int seen_big_d = 0, seen_double_big_d = 0; + int bad = 0; + + /* Skip over "%%", it will become part of a literal piece. */ + if (*f == '%') + { + f++; + continue; + } + + sub_start = current_substring; + + strncpy (current_substring, prev_start, f - 1 - prev_start); + current_substring += f - 1 - prev_start; + *current_substring++ = '\0'; + + pieces[next_frag].string = sub_start; + pieces[next_frag].argclass = literal_piece; + next_frag++; + + percent_loc = f - 1; + + /* Check the validity of the format specifier, and work + out what argument it expects. We only accept C89 + format strings, with the exception of long long (which + we autoconf for). */ + + /* The first part of a format specifier is a set of flag + characters. */ + while (strchr ("0-+ #", *f)) + { + if (*f == '#') + seen_hash = 1; + else if (*f == '0') + seen_zero = 1; + else if (*f == ' ') + seen_space = 1; + else if (*f == '+') + seen_plus = 1; + f++; + } + + /* The next part of a format specifier is a width. */ + while (strchr ("0123456789", *f)) + f++; + + /* The next part of a format specifier is a precision. */ + if (*f == '.') + { + seen_prec = 1; + f++; + while (strchr ("0123456789", *f)) + f++; + } + + /* The next part of a format specifier is a length modifier. */ + if (*f == 'h') + { + seen_h = 1; + f++; + } + else if (*f == 'l') + { + f++; + lcount++; + if (*f == 'l') + { + f++; + lcount++; + } + } + else if (*f == 'L') + { + seen_big_l = 1; + f++; + } + /* Decimal32 modifier. */ + else if (*f == 'H') + { + seen_big_h = 1; + f++; + } + /* Decimal64 and Decimal128 modifiers. */ + else if (*f == 'D') + { + f++; + + /* Check for a Decimal128. */ + if (*f == 'D') + { + f++; + seen_double_big_d = 1; + } + else + seen_big_d = 1; + } + + switch (*f) + { + case 'u': + if (seen_hash) + bad = 1; + /* FALLTHROUGH */ + + case 'o': + case 'x': + case 'X': + if (seen_space || seen_plus) + bad = 1; + /* FALLTHROUGH */ + + case 'd': + case 'i': + if (lcount == 0) + this_argclass = int_arg; + else if (lcount == 1) + this_argclass = long_arg; + else + this_argclass = long_long_arg; + + if (seen_big_l) + bad = 1; + break; + + case 'c': + this_argclass = lcount == 0 ? int_arg : wide_char_arg; + if (lcount > 1 || seen_h || seen_big_l) + bad = 1; + if (seen_prec || seen_zero || seen_space || seen_plus) + bad = 1; + break; + + case 'p': + this_argclass = ptr_arg; + if (lcount || seen_h || seen_big_l) + bad = 1; + if (seen_prec || seen_zero || seen_space || seen_plus) + bad = 1; + break; + + case 's': + this_argclass = lcount == 0 ? string_arg : wide_string_arg; + if (lcount > 1 || seen_h || seen_big_l) + bad = 1; + if (seen_zero || seen_space || seen_plus) + bad = 1; + break; + + case 'e': + case 'f': + case 'g': + case 'E': + case 'G': + if (seen_big_h || seen_big_d || seen_double_big_d) + this_argclass = decfloat_arg; + else if (seen_big_l) + this_argclass = long_double_arg; + else + this_argclass = double_arg; + + if (lcount || seen_h) + bad = 1; + break; + + case '*': + error (_("`*' not supported for precision or width in printf")); + + case 'n': + error (_("Format specifier `n' not supported in printf")); + + case '\0': + error (_("Incomplete format specifier at end of format string")); + + default: + error (_("Unrecognized format specifier '%c' in printf"), *f); + } + + if (bad) + error (_("Inappropriate modifiers to " + "format specifier '%c' in printf"), + *f); + + f++; + + sub_start = current_substring; + + if (lcount > 1 && USE_PRINTF_I64) + { + /* Windows' printf does support long long, but not the usual way. + Convert %lld to %I64d. */ + int length_before_ll = f - percent_loc - 1 - lcount; + + strncpy (current_substring, percent_loc, length_before_ll); + strcpy (current_substring + length_before_ll, "I64"); + current_substring[length_before_ll + 3] = + percent_loc[length_before_ll + lcount]; + current_substring += length_before_ll + 4; + } + else if (this_argclass == wide_string_arg + || this_argclass == wide_char_arg) + { + /* Convert %ls or %lc to %s. */ + int length_before_ls = f - percent_loc - 2; + + strncpy (current_substring, percent_loc, length_before_ls); + strcpy (current_substring + length_before_ls, "s"); + current_substring += length_before_ls + 2; + } + else + { + strncpy (current_substring, percent_loc, f - percent_loc); + current_substring += f - percent_loc; + } + + *current_substring++ = '\0'; + + prev_start = f; + + pieces[next_frag].string = sub_start; + pieces[next_frag].argclass = this_argclass; + next_frag++; + } + + /* Record the remainder of the string. */ + + sub_start = current_substring; + + strncpy (current_substring, prev_start, f - prev_start); + current_substring += f - prev_start; + *current_substring++ = '\0'; + + pieces[next_frag].string = sub_start; + pieces[next_frag].argclass = literal_piece; + next_frag++; + + /* Record an end-of-array marker. */ + + pieces[next_frag].string = NULL; + pieces[next_frag].argclass = literal_piece; + + return pieces; +} + +void +free_format_pieces (struct format_piece *pieces) +{ + if (!pieces) + return; + + /* We happen to know that all the string pieces are in the block + pointed to by the first string piece. */ + if (pieces[0].string) + xfree (pieces[0].string); + + xfree (pieces); +} + +void +free_format_pieces_cleanup (void *ptr) +{ + void **location = ptr; + + if (location == NULL) + return; + + if (*location != NULL) + { + free_format_pieces (*location); + *location = NULL; + } +} + diff --git a/gdb/common/format.h b/gdb/common/format.h new file mode 100644 index 0000000..4c6c1b9 --- /dev/null +++ b/gdb/common/format.h @@ -0,0 +1,63 @@ +/* Parse a printf-style format string. + + Copyright (C) 1986-2012 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#if defined(__MINGW32__) && !defined(PRINTF_HAS_LONG_LONG) +# define USE_PRINTF_I64 1 +# define PRINTF_HAS_LONG_LONG +#else +# define USE_PRINTF_I64 0 +#endif + +/* The argclass represents the general type of data that goes with a + format directive; int_arg for %d, long_arg for %l, and so forth. + Note that these primarily distinguish types by size and need for + special handling, so for instance %u and %x are (at present) also + classed as int_arg. */ + +enum argclass + { + literal_piece, + int_arg, long_arg, long_long_arg, ptr_arg, + string_arg, wide_string_arg, wide_char_arg, + double_arg, long_double_arg, decfloat_arg + }; + +/* A format piece is a section of the format string that may include a + single print directive somewhere in it, and the associated class + for the argument. */ + +struct format_piece +{ + char *string; + enum argclass argclass; +}; + +/* Return an array of printf fragments found at the given string, and + rewrite ARG with a pointer to the end of the format string. */ + +extern struct format_piece *parse_format_string (char **arg); + +/* Given a pointer to an array of format pieces, free any memory that + would have been allocated by parse_format_string. */ + +extern void free_format_pieces (struct format_piece *frags); + +/* Freeing, cast as a cleanup. */ + +extern void free_format_pieces_cleanup (void *); |