From 168d63e571b2a83ad509b48044e6d76ebf8a9f2d Mon Sep 17 00:00:00 2001 From: samuel Date: Mon, 5 Jun 2000 02:28:41 +0000 Subject: [PATCH] In include, * dyn-string.h: Move here from gcc/dyn-string.h. Add new functions. * demangle.h (DMGL_GNU_NEW_ABI): New macro. (DMGL_STYLE_MASK): Or in DMGL_GNU_NEW_ABI. (current_demangling_style): Add gnu_new_abi_demangling. (GNU_NEW_ABI_DEMANGLING_STYLE_STRING): New macro. (GNU_NEW_ABI_DEMANGLING): Likewise. (cplus_demangle_new_abi): New declaration. In libiberty, * Makefile.in (CFILES): Add cp-demangle.c and dyn-string.c. (REQUIRED_OFILES): Add cp-demangle.o and dyn-string.o. (cp-demangle.o): New dependency. (dyn-string.o): Likewise. * dyn-string.c: Move here from gcc/dyn-string.c. Add new functions. * cplus-dem.c (libiberty_demanglers): Add initializer for new-ABI demangler. (cplus_demangle): Call cplus_demangle_new_abi if in new-ABI demangling mode. (gnu_new_abi_symbol_characters): New function. (main): Use gnu_new_abi_symbol_characters. * cp-demangle.c: New file. * cp-demangle.c: New file. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@34397 138bc75d-0d04-0410-961f-82ee72b054a4 --- include/ChangeLog | 11 + include/demangle.h | 50 +- include/dyn-string.h | 64 + libiberty/ChangeLog | 18 + libiberty/Makefile.in | 34 +- libiberty/cp-demangle.c | 2997 +++++++++++++++++++++++++++++++++++++++++++++++ libiberty/cplus-dem.c | 27 + libiberty/dyn-string.c | 331 ++++++ 8 files changed, 3497 insertions(+), 35 deletions(-) create mode 100644 include/dyn-string.h create mode 100644 libiberty/cp-demangle.c create mode 100644 libiberty/dyn-string.c diff --git a/include/ChangeLog b/include/ChangeLog index 2bd3e6574a2..c74f03e3204 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,14 @@ +2000-06-04 Alex Samuel + + * dyn-string.h: Move here from gcc/dyn-string.h. Add new functions. + + * demangle.h (DMGL_GNU_NEW_ABI): New macro. + (DMGL_STYLE_MASK): Or in DMGL_GNU_NEW_ABI. + (current_demangling_style): Add gnu_new_abi_demangling. + (GNU_NEW_ABI_DEMANGLING_STYLE_STRING): New macro. + (GNU_NEW_ABI_DEMANGLING): Likewise. + (cplus_demangle_new_abi): New declaration. + Tue May 30 16:53:34 2000 Andrew Cagney * floatformat.h (struct floatformat): Add field name. diff --git a/include/demangle.h b/include/demangle.h index dd0d9fcac03..146c895d96b 100644 --- a/include/demangle.h +++ b/include/demangle.h @@ -24,21 +24,23 @@ /* Options passed to cplus_demangle (in 2nd parameter). */ -#define DMGL_NO_OPTS 0 /* For readability... */ -#define DMGL_PARAMS (1 << 0) /* Include function args */ -#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ -#define DMGL_JAVA (1 << 2) /* Demangle as Java rather than C++. */ - -#define DMGL_AUTO (1 << 8) -#define DMGL_GNU (1 << 9) -#define DMGL_LUCID (1 << 10) -#define DMGL_ARM (1 << 11) -#define DMGL_HP (1 << 12) /* For the HP aCC compiler; same as ARM - except for template arguments, etc. */ -#define DMGL_EDG (1 << 13) +#define DMGL_NO_OPTS 0 /* For readability... */ +#define DMGL_PARAMS (1 << 0) /* Include function args */ +#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ +#define DMGL_JAVA (1 << 2) /* Demangle as Java rather than C++. */ + +#define DMGL_AUTO (1 << 8) +#define DMGL_GNU (1 << 9) +#define DMGL_LUCID (1 << 10) +#define DMGL_ARM (1 << 11) +#define DMGL_HP (1 << 12) /* For the HP aCC compiler; + same as ARM except for + template arguments, etc. */ +#define DMGL_EDG (1 << 13) +#define DMGL_GNU_NEW_ABI (1 << 14) /* If none of these are set, use 'current_demangling_style' as the default. */ -#define DMGL_STYLE_MASK (DMGL_AUTO|DMGL_GNU|DMGL_LUCID|DMGL_ARM|DMGL_HP|DMGL_EDG) +#define DMGL_STYLE_MASK (DMGL_AUTO|DMGL_GNU|DMGL_LUCID|DMGL_ARM|DMGL_HP|DMGL_EDG|DMGL_GNU_NEW_ABI) /* Enumeration of possible demangling styles. @@ -56,17 +58,19 @@ extern enum demangling_styles lucid_demangling = DMGL_LUCID, arm_demangling = DMGL_ARM, hp_demangling = DMGL_HP, - edg_demangling = DMGL_EDG + edg_demangling = DMGL_EDG, + gnu_new_abi_demangling = DMGL_GNU_NEW_ABI, } current_demangling_style; /* Define string names for the various demangling styles. */ -#define AUTO_DEMANGLING_STYLE_STRING "auto" -#define GNU_DEMANGLING_STYLE_STRING "gnu" -#define LUCID_DEMANGLING_STYLE_STRING "lucid" -#define ARM_DEMANGLING_STYLE_STRING "arm" -#define HP_DEMANGLING_STYLE_STRING "hp" -#define EDG_DEMANGLING_STYLE_STRING "edg" +#define AUTO_DEMANGLING_STYLE_STRING "auto" +#define GNU_DEMANGLING_STYLE_STRING "gnu" +#define LUCID_DEMANGLING_STYLE_STRING "lucid" +#define ARM_DEMANGLING_STYLE_STRING "arm" +#define HP_DEMANGLING_STYLE_STRING "hp" +#define EDG_DEMANGLING_STYLE_STRING "edg" +#define GNU_NEW_ABI_DEMANGLING_STYLE_STRING "gnu-new-abi" /* Some macros to test what demangling style is active. */ @@ -77,6 +81,7 @@ extern enum demangling_styles #define ARM_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_ARM) #define HP_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_HP) #define EDG_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_EDG) +#define GNU_NEW_ABI_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_GNU_NEW_ABI) /* Provide information about the available demangle styles. This code is pulled from gdb into libiberty because it is useful to binutils also. */ @@ -107,4 +112,9 @@ cplus_demangle_set_style PARAMS ((enum demangling_styles style)); extern enum demangling_styles cplus_demangle_name_to_style PARAMS ((const char *name)); + +/* New-ABI demangling entry point, defined in cp-demangle.c. */ +extern char* +cplus_demangle_new_abi PARAMS ((const char* mangled)); + #endif /* DEMANGLE_H */ diff --git a/include/dyn-string.h b/include/dyn-string.h new file mode 100644 index 00000000000..30c0624baa2 --- /dev/null +++ b/include/dyn-string.h @@ -0,0 +1,64 @@ +/* An abstract string datatype. + Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + Contributed by Mark Mitchell (mark@markmitchell.com). + +This file is part of GNU CC. + +GNU CC 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 2, or (at your option) +any later version. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + + +typedef struct dyn_string +{ + int allocated; /* The amount of space allocated for the string. */ + int length; /* The actual length of the string. */ + char *s; /* The string itself, NUL-terminated. */ +}* dyn_string_t; + +/* The length STR, in bytes, not including the terminating NUL. */ +#define dyn_string_length(STR) \ + ((STR)->length) + +/* The NTBS in which the contents of STR are stored. */ +#define dyn_string_buf(STR) \ + ((STR)->s) + +/* Compare DS1 to DS2 with strcmp. */ +#define dyn_string_compare(DS1, DS2) \ + (strcmp ((DS1)->s, (DS2)->s)) + + +extern void dyn_string_init PARAMS ((struct dyn_string *, int)); +extern dyn_string_t dyn_string_new PARAMS ((int)); +extern void dyn_string_delete PARAMS ((dyn_string_t)); +extern char *dyn_string_release PARAMS ((dyn_string_t)); +extern dyn_string_t dyn_string_resize PARAMS ((dyn_string_t, int)); +extern void dyn_string_clear PARAMS ((dyn_string_t)); +extern void dyn_string_copy PARAMS ((dyn_string_t, dyn_string_t)); +extern void dyn_string_copy_cstr PARAMS ((dyn_string_t, const char *)); +extern void dyn_string_prepend PARAMS ((dyn_string_t, dyn_string_t)); +extern void dyn_string_prepend_cstr PARAMS ((dyn_string_t, const char *)); +extern void dyn_string_insert PARAMS ((dyn_string_t, int, + dyn_string_t)); +extern void dyn_string_insert_cstr PARAMS ((dyn_string_t, int, + const char *)); +extern dyn_string_t dyn_string_append PARAMS ((dyn_string_t, dyn_string_t)); +extern dyn_string_t dyn_string_append_cstr + PARAMS ((dyn_string_t, const char *)); +extern dyn_string_t dyn_string_append_char + PARAMS ((dyn_string_t, char)); +extern void dyn_string_substring PARAMS ((dyn_string_t, + dyn_string_t, int, int)); +extern int dyn_string_eq PARAMS ((dyn_string_t, dyn_string_t)); diff --git a/libiberty/ChangeLog b/libiberty/ChangeLog index d1c1115d9b1..cbb7b4e691e 100644 --- a/libiberty/ChangeLog +++ b/libiberty/ChangeLog @@ -1,3 +1,21 @@ +2000-06-04 Alex Samuel + + * Makefile.in (CFILES): Add cp-demangle.c and dyn-string.c. + (REQUIRED_OFILES): Add cp-demangle.o and dyn-string.o. + (cp-demangle.o): New dependency. + (dyn-string.o): Likewise. + + * dyn-string.c: Move here from gcc/dyn-string.c. Add new functions. + + * cplus-dem.c (libiberty_demanglers): Add initializer for new-ABI + demangler. + (cplus_demangle): Call cplus_demangle_new_abi if in new-ABI + demangling mode. + (gnu_new_abi_symbol_characters): New function. + (main): Use gnu_new_abi_symbol_characters. * cp-demangle.c: New + file. + * cp-demangle.c: New file. + Tue May 30 16:45:25 2000 Andrew Cagney * floatformat.c: Add name to each floatformat field. diff --git a/libiberty/Makefile.in b/libiberty/Makefile.in index d4766a9fc6a..a7890001af3 100644 --- a/libiberty/Makefile.in +++ b/libiberty/Makefile.in @@ -125,23 +125,25 @@ HFILES = alloca-conf.h # (alphabetical), and add them to REQUIRED_OFILES or funcs in # configure.in. CFILES = asprintf.c alloca.c argv.c atexit.c basename.c bcmp.c bcopy.c \ - bzero.c calloc.c choose-temp.c clock.c concat.c cplus-dem.c fdmatch.c \ - fnmatch.c getcwd.c getpwd.c getopt.c getopt1.c getpagesize.c \ - getruntime.c floatformat.c hashtab.c hex.c index.c insque.c memchr.c \ - memcmp.c memcpy.c memmove.c memset.c mkstemps.c objalloc.c obstack.c \ - partition.c pexecute.c putenv.c random.c rename.c rindex.c \ - setenv.c sigsetmask.c sort.c spaces.c splay-tree.c strcasecmp.c \ - strncasecmp.c strchr.c strdup.c strerror.c strncmp.c strrchr.c \ - strsignal.c strstr.c strtod.c strtol.c strtoul.c tmpnam.c \ - vasprintf.c vfork.c vfprintf.c vprintf.c vsprintf.c waitpid.c \ - xatexit.c xexit.c xmalloc.c xmemdup.c xstrdup.c xstrerror.c + bzero.c calloc.c choose-temp.c clock.c concat.c cplus-dem.c \ + cp-demangle.c dyn-string.c fdmatch.c fnmatch.c getcwd.c \ + getpwd.c getopt.c getopt1.c getpagesize.c getruntime.c \ + floatformat.c hashtab.c hex.c index.c insque.c memchr.c memcmp.c \ + memcpy.c memmove.c memset.c mkstemps.c objalloc.c obstack.c \ + partition.c pexecute.c putenv.c random.c rename.c rindex.c setenv.c \ + sigsetmask.c sort.c spaces.c splay-tree.c strcasecmp.c strncasecmp.c \ + strchr.c strdup.c strerror.c strncmp.c strrchr.c strsignal.c strstr.c \ + strtod.c strtol.c strtoul.c tmpnam.c vasprintf.c vfork.c vfprintf.c \ + vprintf.c vsprintf.c waitpid.c xatexit.c xexit.c xmalloc.c \ + xmemdup.c xstrdup.c xstrerror.c # These are always included in the library. -REQUIRED_OFILES = argv.o choose-temp.o concat.o cplus-dem.o \ - fdmatch.o fnmatch.o getopt.o getopt1.o getpwd.o getruntime.o hashtab.o \ - hex.o floatformat.o objalloc.o obstack.o partition.o pexecute.o sort.o \ - spaces.o splay-tree.o strerror.o strsignal.o xatexit.o xexit.o xmalloc.o \ - xmemdup.o xstrdup.o xstrerror.o +REQUIRED_OFILES = argv.o choose-temp.o concat.o cplus-dem.o cp-demangle.o \ + dyn-string.o fdmatch.o fnmatch.o getopt.o getopt1.o getpwd.o \ + getruntime.o hashtab.o hex.o floatformat.o objalloc.o obstack.o \ + partition.o pexecute.o sort.o spaces.o splay-tree.o strerror.o \ + strsignal.o xatexit.o xexit.o xmalloc.o xmemdup.o xstrdup.o \ + xstrerror.o $(TARGETLIB): $(REQUIRED_OFILES) $(EXTRA_OFILES) $(LIBOBJS) $(ALLOCA) rm -f $(TARGETLIB) @@ -261,6 +263,8 @@ choose-temp.o: config.h clock.o: config.h concat.o: $(INCDIR)/libiberty.h cplus-dem.o: config.h $(INCDIR)/demangle.h +cp-demangle.o: config.h $(INCDIR)/dyn-string.h +dyn-string.o: config.h $(INCDIR)/dyn-string.h fdmatch.o: $(INCDIR)/libiberty.h fnmatch.o: config.h $(INCDIR)/fnmatch.h getcwd.o: config.h diff --git a/libiberty/cp-demangle.c b/libiberty/cp-demangle.c new file mode 100644 index 00000000000..25f88db3e16 --- /dev/null +++ b/libiberty/cp-demangle.c @@ -0,0 +1,2997 @@ +/* Demangler for IA64 / g++ standard C++ ABI. + Copyright (C) 2000 CodeSourcery LLC. + Written by Alex Samuel . + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* This file implements demangling of C++ names mangled according to + the IA64 / g++ standard C++ ABI. Use the cp_demangle function to + demangle a mangled name, or compile with the preprocessor macro + STANDALONE_DEMANGLER defined to create a demangling filter + executable. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#ifdef HAVE_STDLIB_H +#include +#endif + +#if defined(CP_DEMANGLE_DEBUG) || defined(STANDALONE_DEMANGLER) +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#include "ansidecl.h" +#include "libiberty.h" +#include "dyn-string.h" + +/* If CP_DEMANGLE_DEBUG is defined, a trace of the grammar evaluation, + and other debugging output, will be generated. */ +#ifdef CP_DEMANGLE_DEBUG +#define DEMANGLE_TRACE(PRODUCTION, DM) \ + fprintf (stderr, " -> %-24s at position %3d\n", \ + (PRODUCTION), current_position (DM)); +#else +#define DEMANGLE_TRACE(PRODUCTION, DM) +#endif + +/* If flag_verbose is zero, some simplifications will be made to the + output to make it easier to read and supress details that are + generally not of interest to the average C++ programmer. + Otherwise, the demangled representation will attempt to convey as + much information as the mangled form. */ +static int flag_verbose; + +/* If flag_strict is non-zero, demangle strictly according to the + specification -- don't demangle special g++ manglings. */ +static int flag_strict; + +/* String_list_t is an extended form of dyn_string_t which provides a link + field. A string_list_t may safely be cast to and used as a + dyn_string_t. */ + +struct string_list_def +{ + struct dyn_string string; + struct string_list_def *next; +}; + +typedef struct string_list_def *string_list_t; + +/* Data structure representing a potential substitution. */ + +struct substitution_def +{ + /* The demangled text of the substitution. */ + dyn_string_t text; + + /* The template parameter that this represents, indexed from zero. + If this is not a template paramter number, the value is + NOT_TEMPLATE_PARM. */ + int template_parm_number; + + /* Whether this substitution represents a template item. */ + int template_p : 1; +}; + +#define NOT_TEMPLATE_PARM (-1) + +/* Data structure representing a template argument list. */ + +struct template_arg_list_def +{ + /* The next (lower) template argument list in the stack of currently + active template arguments. */ + struct template_arg_list_def *next; + + /* The first element in the list of template arguments in + left-to-right order. */ + string_list_t first_argument; + + /* The last element in the arguments lists. */ + string_list_t last_argument; +}; + +typedef struct template_arg_list_def *template_arg_list_t; + +/* Data structure to maintain the state of the current demangling. */ + +struct demangling_def +{ + /* The full mangled name being mangled. */ + char *name; + + /* Pointer into name at the current position. */ + char *next; + + /* Stack for strings containing demangled result generated so far. + Text is emitted to the topmost (first) string. */ + string_list_t result; + + /* The number of presently available substitutions. */ + int num_substitutions; + + /* The allocated size of the substitutions array. */ + int substitutions_allocated; + + /* An array of available substitutions. The number of elements in + the array is given by num_substitions, and the allocated array + size in substitutions_size. + + The most recent substition is at the end, so + + - `S_' corresponds to substititutions[num_substitutions - 1] + - `S0_' corresponds to substititutions[num_substitutions - 2] + + etc. */ + struct substitution_def *substitutions; + + /* The stack of template argument lists. */ + template_arg_list_t template_arg_lists; + + /* The most recently demangled source-name. */ + dyn_string_t last_source_name; +}; + +typedef struct demangling_def *demangling_t; + +/* This type is the standard return code from most functions. Values + other than STATUS_OK contain descriptive messages. */ +typedef const char *status_t; + +/* Special values that can be used as a status_t. */ +#define STATUS_OK NULL +#define STATUS_ERROR "Error." +#define STATUS_UNIMPLEMENTED "Unimplemented." +#define STATUS_INTERNAL_ERROR "Internal error." + +static void int_to_dyn_string + PARAMS ((int, dyn_string_t)); +static string_list_t string_list_new + PARAMS ((int)); +static void string_list_delete + PARAMS ((string_list_t)); +static void result_close_template_list + PARAMS ((demangling_t)); +static void result_push + PARAMS ((demangling_t)); +static string_list_t result_pop + PARAMS ((demangling_t)); +static int substitution_start + PARAMS ((demangling_t)); +static void substitution_add + PARAMS ((demangling_t, int, int, int)); +static dyn_string_t substitution_get + PARAMS ((demangling_t, int, int *)); +#ifdef CP_DEMANGLE_DEBUG +static void substitutions_print + PARAMS ((demangling_t, FILE *)); +#endif +static template_arg_list_t template_arg_list_new + PARAMS ((void)); +static void template_arg_list_delete + PARAMS ((template_arg_list_t)); +static void template_arg_list_add_arg + PARAMS ((template_arg_list_t, string_list_t)); +static string_list_t template_arg_list_get_arg + PARAMS ((template_arg_list_t, int)); +static void push_template_arg_list + PARAMS ((demangling_t, template_arg_list_t)); +static void pop_to_template_arg_list + PARAMS ((demangling_t, template_arg_list_t)); +#ifdef CP_DEMANGLE_DEBUG +static void template_arg_list_print + PARAMS ((template_arg_list_t, FILE *)); +#endif +static template_arg_list_t current_template_arg_list + PARAMS ((demangling_t)); +static demangling_t demangling_new + PARAMS ((char *)); +static void demangling_delete + PARAMS ((demangling_t)); + +/* The last character of DS. Warning: DS is evaluated twice. */ +#define dyn_string_last_char(DS) \ + (dyn_string_buf (DS)[dyn_string_length (DS) - 1]) + +/* Append a space character (` ') to DS if it does not already end + with one. */ +#define dyn_string_append_space(DS) \ + do \ + { \ + if (dyn_string_length (DS) > 0 \ + && dyn_string_last_char (DS) != ' ') \ + dyn_string_append_char ((DS), ' '); \ + } \ + while (0) + +/* Returns the index of the current position in the mangled name. */ +#define current_position(DM) ((DM)->next - (DM)->name) + +/* Returns the character at the current position of the mangled name. */ +#define peek_char(DM) (*((DM)->next)) + +/* Returns the character one past the current position of the mangled + name. */ +#define peek_char_next(DM) \ + (peek_char (DM) == '\0' ? '\0' : (*((DM)->next + 1))) + +/* Returns the character at the current position, and advances the + current position to the next character. */ +#define next_char(DM) (*((DM)->next)++) + +/* Returns non-zero if the current position is the end of the mangled + name, i.e. one past the last character. */ +#define end_of_name_p(DM) (peek_char (DM) == '\0') + +/* Advances the current position by one character. */ +#define advance_char(DM) (++(DM)->next) + +/* Returns the string containing the current demangled result. */ +#define result_string(DM) (&(DM)->result->string) + +/* Appends a dyn_string_t to the demangled result. */ +#define result_append_string(DM, STRING) \ + dyn_string_append (&(DM)->result->string, (STRING)) + +/* Appends NUL-terminated string CSTR to the demangled result. */ +#define result_append(DM, CSTR) \ + dyn_string_append_cstr (&(DM)->result->string, (CSTR)) + +/* Appends character CHAR to the demangled result. */ +#define result_append_char(DM, CHAR) \ + dyn_string_append_char (&(DM)->result->string, (CHAR)) + +/* The length of the current demangled result. */ +#define result_length(DM) \ + dyn_string_length (&(DM)->result->string) + +/* Appends a space to the demangled result if the last character is + not a space. */ +#define result_append_space(DM) \ + dyn_string_append_space (&(DM)->result->string) + +/* Evaluate EXPR, which must produce a status_t. If the status code + indicates an error, return from the current function with that + status code. */ +#define RETURN_IF_ERROR(EXPR) \ + do \ + { \ + status_t s = EXPR; \ + if (s != STATUS_OK) \ + return s; \ + } \ + while (0) + +/* Appends a base 10 representation of VALUE to DS. */ + +static void +int_to_dyn_string (value, ds) + int value; + dyn_string_t ds; +{ + int i; + int mask = 1; + + /* Handle zero up front. */ + if (value == 0) + { + dyn_string_append_char (ds, '0'); + return; + } + + /* For negative numbers, emit a minus sign. */ + if (value < 0) + { + dyn_string_append_char (ds, '-'); + value = -value; + } + + /* Find the power of 10 of the first digit. */ + i = value; + while (i > 9) + { + mask *= 10; + i /= 10; + } + + /* Write the digits. */ + while (mask > 0) + { + int digit = value / mask; + dyn_string_append_char (ds, '0' + digit); + value -= digit * mask; + mask /= 10; + } +} + +/* Creates a new string list node. The contents of the string are + empty, but the initial buffer allocation is LENGTH. The string + list node should be deleted with string_list_delete. */ + +static string_list_t +string_list_new (length) + int length; +{ + string_list_t s = + (string_list_t) xmalloc (sizeof (struct string_list_def)); + dyn_string_init ((dyn_string_t) s, length); + return s; +} + +/* Deletes the entire string list starting at NODE. */ + +static void +string_list_delete (node) + string_list_t node; +{ + while (node != NULL) + { + string_list_t next = node->next; + free (node); + node = next; + } +} + +/* Appends a greater-than character to the demangled result. If the + last character is a greater-than character, a space is inserted + first, so that the two greater-than characters don't look like a + right shift token. */ + +static void +result_close_template_list (dm) + demangling_t dm; +{ + dyn_string_t s = &dm->result->string; + if (dyn_string_last_char (s) == '>') + dyn_string_append_char (s, ' '); + dyn_string_append_char (s, '>'); +} + +/* Allocates and pushes a new string onto the demangled results stack + for DM. Subsequent demangling with DM will emit to the new string. */ + +static void +result_push (dm) + demangling_t dm; +{ + string_list_t new_string = string_list_new (0); + new_string->next = (string_list_t) dm->result; + dm->result = new_string; +} + +/* Removes and returns the topmost element on the demangled results + stack for DM. The caller assumes ownership for the returned + string. */ + +static string_list_t +result_pop (dm) + demangling_t dm; +{ + string_list_t top = dm->result; + dm->result = top->next; + return top; +} + +/* Returns the start position of a fragment of the demangled result + that will be a substitution candidate. Should be called at the + start of productions that can add substitutions. */ + +static int +substitution_start (dm) + demangling_t dm; +{ + return result_length (dm); +} + +/* Adds the suffix of the current demangled result of DM starting at + START_POSITION as a potential substitution. If TEMPLATE_P is + non-zero, this potential substitution is a template-id. + + If TEMPLATE_PARM_NUMBER is not NOT_TEMPLATE_PARM, the substitution + is for that particular , and is distinct from other + otherwise-identical types and other s with + different indices. */ + +static void +substitution_add (dm, start_position, template_p, template_parm_number) + demangling_t dm; + int start_position; + int template_p; + int template_parm_number; +{ + dyn_string_t result = result_string (dm); + dyn_string_t substitution = dyn_string_new (0); + int i; + + dyn_string_substring (substitution, + result, start_position, result_length (dm)); + + /* Check whether SUBSTITUTION already occurs. */ + for (i = 0; i < dm->num_substitutions; ++i) + if (dyn_string_eq (dm->substitutions[i].text, substitution) + && dm->substitutions[i].template_parm_number == template_parm_number) + /* Found SUBSTITUTION already present. */ + { + /* Callers expect this function to take ownership of + SUBSTITUTION, so delete it. */ + dyn_string_delete (substitution); + return; + } + + /* If there's no room for the new entry, grow the array. */ + if (dm->substitutions_allocated == dm->num_substitutions) + { + dm->substitutions_allocated *= 2; + dm->substitutions = (struct substitution_def *) + xrealloc (dm->substitutions, + sizeof (struct substitution_def) + * dm->substitutions_allocated); + } + + /* Add the substitution to the array. */ + dm->substitutions[i].text = substitution; + dm->substitutions[i].template_p = template_p; + dm->substitutions[i].template_parm_number = template_parm_number; + ++dm->num_substitutions; + +#ifdef CP_DEMANGLE_DEBUG + substitutions_print (dm, stderr); +#endif +} + +/* Returns the Nth-most-recent substitution. Sets *TEMPLATE_P to + non-zero if the substitution is a template-id, zero otherwise. + N is numbered from zero. DM retains ownership of the returned + string. If N is negative, or equal to or greater than the current + number of substitution candidates, returns NULL. */ + +static dyn_string_t +substitution_get (dm, n, template_p) + demangling_t dm; + int n; + int *template_p; +{ + struct substitution_def *sub; + + /* Make sure N is in the valid range. */ + if (n < 0 || n >= dm->num_substitutions) + return NULL; + + sub = &(dm->substitutions[n]); + *template_p = sub->template_p; + return sub->text; +} + +#ifdef CP_DEMANGLE_DEBUG +/* Debugging routine to print the current substitutions to FP. */ + +static void +substitutions_print (dm, fp) + demangling_t dm; + FILE *fp; +{ + int seq_id; + int num = dm->num_substitutions; + + fprintf (fp, "SUBSTITUTIONS:\n"); + for (seq_id = -1; seq_id < num - 1; ++seq_id) + { + int template_p; + dyn_string_t text = substitution_get (dm, seq_id + 1, &template_p); + + if (seq_id == -1) + fprintf (fp, " S_ "); + else + fprintf (fp, " S%d_", seq_id); + fprintf (fp, " %c: %s\n", template_p ? '*' : ' ', dyn_string_buf (text)); + } +} + +#endif /* CP_DEMANGLE_DEBUG */ + +/* Creates a new template argument list. */ + +static template_arg_list_t +template_arg_list_new (void) +{ + template_arg_list_t new_list + = (template_arg_list_t) xmalloc (sizeof (struct template_arg_list_def)); + /* Initialize the new list to have no arguments. */ + new_list->first_argument = NULL; + new_list->last_argument = NULL; + /* Return the new list. */ + return new_list; +} + +/* Deletes a template argument list and the template arguments it + contains. */ + +static void +template_arg_list_delete (list) + template_arg_list_t list; +{ + /* If there are any arguments on LIST, delete them. */ + if (list->first_argument != NULL) + string_list_delete (list->first_argument); + /* Delete LIST. */ + free (list); +} + +/* Adds ARG to the template argument list ARG_LIST. */ + +static void +template_arg_list_add_arg (arg_list, arg) + template_arg_list_t arg_list; + string_list_t arg; +{ + if (arg_list->first_argument == NULL) + /* If there were no arguments before, ARG is the first one. */ + arg_list->first_argument = arg; + else + /* Make ARG the last argument on the list. */ + arg_list->last_argument->next = arg; + /* Make ARG the last on the list. */ + arg_list->last_argument = arg; + arg->next = NULL; +} + +/* Returns the template arugment at position INDEX in template + argument list ARG_LIST. */ + +static string_list_t +template_arg_list_get_arg (arg_list, index) + template_arg_list_t arg_list; + int index; +{ + string_list_t arg = arg_list->first_argument; + /* Scan down the list of arguments to find the one at position + INDEX. */ + while (index--) + { + arg = arg->next; + if (arg == NULL) + /* Ran out of arguments before INDEX hit zero. That's an + error. */ + return NULL; + } + /* Return the argument at position INDEX. */ + return arg; +} + +/* Pushes ARG_LIST onto the top of the template argument list stack. */ + +static void +push_template_arg_list (dm, arg_list) + demangling_t dm; + template_arg_list_t arg_list; +{ + arg_list->next = dm->template_arg_lists; + dm->template_arg_lists = arg_list; +#ifdef CP_DEMANGLE_DEBUG + fprintf (stderr, " ** pushing template arg list\n"); + template_arg_list_print (arg_list, stderr); +#endif +} + +/* Pops and deletes elements on the template argument list stack until + arg_list is the topmost element. If arg_list is NULL, all elements + are popped and deleted. */ + +static void +pop_to_template_arg_list (dm, arg_list) + demangling_t dm; + template_arg_list_t arg_list; +{ + while (dm->template_arg_lists != arg_list) + { + template_arg_list_t top = dm->template_arg_lists; + /* Disconnect the topmost element from the list. */ + dm->template_arg_lists = top->next; + /* Delete the popped element. */ + template_arg_list_delete (top); +#ifdef CP_DEMANGLE_DEBUG + fprintf (stderr, " ** removing template arg list\n"); +#endif + } +} + +#ifdef CP_DEMANGLE_DEBUG + +/* Prints the contents of ARG_LIST to FP. */ + +static void +template_arg_list_print (arg_list, fp) + template_arg_list_t arg_list; + FILE *fp; +{ + string_list_t arg; + int index = -1; + + fprintf (fp, "TEMPLATE ARGUMENT LIST:\n"); + for (arg = arg_list->first_argument; arg != NULL; arg = arg->next) + { + if (index == -1) + fprintf (fp, " T_ : "); + else + fprintf (fp, " T%d_ : ", index); + ++index; + fprintf (fp, "%s\n", dyn_string_buf ((dyn_string_t) arg)); + } +} + +#endif /* CP_DEMANGLE_DEBUG */ + +/* Returns the topmost element on the stack of template argument + lists. If there is no list of template arguments, returns NULL. */ + +static template_arg_list_t +current_template_arg_list (dm) + demangling_t dm; +{ + return dm->template_arg_lists; +} + +/* Allocates a demangling_t object for demangling mangled NAME. A new + result must be pushed before the returned object can be used. */ + +static demangling_t +demangling_new (name) + char *name; +{ + demangling_t dm = (demangling_t) + xmalloc (sizeof (struct demangling_def)); + + dm->name = name; + dm->next = name; + dm->result = NULL; + dm->last_source_name = dyn_string_new (0); + dm->num_substitutions = 0; + dm->substitutions_allocated = 10; + dm->substitutions = (struct substitution_def *) + xmalloc (dm->substitutions_allocated * sizeof (struct substitution_def)); + dm->template_arg_lists = NULL; + + return dm; +} + +/* Deallocates a demangling_t object and all memory associated with + it. */ + +static void +demangling_delete (dm) + demangling_t dm; +{ + int i; + template_arg_list_t arg_list = dm->template_arg_lists; + + /* Delete the stack of template argument lists. */ + while (arg_list != NULL) + { + template_arg_list_t next = arg_list->next; + template_arg_list_delete (arg_list); + arg_list = next; + } + /* Delete the list of substitutions. */ + for (i = dm->num_substitutions; --i >= 0; ) + dyn_string_delete (dm->substitutions[i].text); + free (dm->substitutions); + /* Delete the demangled result. */ + string_list_delete (dm->result); + /* Delete the stored identifier name. */ + dyn_string_delete (dm->last_source_name); + /* Delete the context object itself. */ + free (dm); +} + +/* These functions demangle an alternative of the corresponding + production in the mangling spec. The first argument of each is a + demangling context structure for the current demangling + operation. Most emit demangled text directly to the topmost result + string on the result string stack in the demangling context + structure. */ + +static status_t demangle_char + PARAMS ((demangling_t, char)); +static status_t demangle_mangled_name + PARAMS ((demangling_t)); +static status_t demangle_encoding + PARAMS ((demangling_t)); +static status_t demangle_name + PARAMS ((demangling_t, int *)); +static status_t demangle_nested_name + PARAMS ((demangling_t, int *)); +static status_t demangle_prefix + PARAMS ((demangling_t, int *)); +static status_t demangle_unqualified_name + PARAMS ((demangling_t)); +static status_t demangle_source_name + PARAMS ((demangling_t)); +static status_t demangle_number + PARAMS ((demangling_t, int *, int, int)); +static status_t demangle_number_literally + PARAMS ((demangling_t, dyn_string_t, int, int)); +static status_t demangle_identifier + PARAMS ((demangling_t, int, dyn_string_t)); +static status_t demangle_operator_name + PARAMS ((demangling_t, int, int *)); +static status_t demangle_special_name + PARAMS ((demangling_t)); +static status_t demangle_ctor_dtor_name + PARAMS ((demangling_t)); +static status_t demangle_type_ptr + PARAMS ((demangling_t)); +static status_t demangle_type + PARAMS ((demangling_t)); +static status_t demangle_CV_qualifiers + PARAMS ((demangling_t, dyn_string_t)); +static status_t demangle_builtin_type + PARAMS ((demangling_t)); +static status_t demangle_function_type + PARAMS ((demangling_t, int)); +static status_t demangle_bare_function_type + PARAMS ((demangling_t, int)); +static status_t demangle_class_enum_type + PARAMS ((demangling_t, int *)); +static status_t demangle_array_type + PARAMS ((demangling_t)); +static status_t demangle_template_param + PARAMS ((demangling_t, int *)); +static status_t demangle_template_args + PARAMS ((demangling_t)); +static status_t demangle_literal + PARAMS ((demangling_t)); +static status_t demangle_template_arg + PARAMS ((demangling_t)); +static status_t demangle_expression + PARAMS ((demangling_t));; +static status_t demangle_scope_expression + PARAMS ((demangling_t)); +static status_t demangle_expr_primary + PARAMS ((demangling_t));; +static status_t demangle_substitution + PARAMS ((demangling_t, int *, int *)); +static status_t demangle_local_name + PARAMS ((demangling_t)); +static status_t demangle_discriminator + PARAMS ((demangling_t, int)); + +/* When passed to demangle_bare_function_type, indicates that the + function's return type is not encoded before its parameter types. */ +#define BFT_NO_RETURN_TYPE -1 + +/* Check that the next character is C. If so, consume it. If not, + return an error. */ + +static status_t +demangle_char (dm, c) + demangling_t dm; + char c; +{ + static char *error_message = NULL; + + if (peek_char (dm) == c) + { + advance_char (dm); + return STATUS_OK; + } + else + { + if (error_message == NULL) + error_message = strdup ("Expected ?"); + error_message[9] = c; + return error_message; + } +} + +/* Demangles and emits a . + + ::= _Z */ + +static status_t +demangle_mangled_name (dm) + demangling_t dm; +{ + DEMANGLE_TRACE ("mangled-name", dm); + RETURN_IF_ERROR (demangle_char (dm, '_')); + RETURN_IF_ERROR (demangle_char (dm, 'Z')); + RETURN_IF_ERROR (demangle_encoding (dm)); + return STATUS_OK; +} + +/* Demangles and emits an . + + ::= + ::= + ::= */ + +static status_t +demangle_encoding (dm) + demangling_t dm; +{ + int template_p; + int special_std_substitution; + int start_position; + int start = substitution_start (dm); + template_arg_list_t old_arg_list = current_template_arg_list (dm); + char peek = peek_char (dm); + + DEMANGLE_TRACE ("encoding", dm); + + /* Remember where the name starts. If it turns out to be a template + function, we'll have to insert the return type here. */ + start_position = result_length (dm); + + if (peek == 'S') + { + RETURN_IF_ERROR (demangle_substitution (dm, &template_p, + &special_std_substitution)); + if (special_std_substitution) + { + /* This was the magic `std::' substitution. */ + result_append (dm, "::"); + RETURN_IF_ERROR (demangle_encoding (dm)); + } + } + else if (peek == 'G' || peek == 'T') + RETURN_IF_ERROR (demangle_special_name (dm)); + else + { + /* Now demangle the name. */ + RETURN_IF_ERROR (demangle_name (dm, &template_p)); + + /* If there's anything left, the name was a function name, with + maybe its return type, and its parameters types, following. */ + if (!end_of_name_p (dm) + && peek_char (dm) != 'E') + { + if (template_p) + /* Template functions have their return type encoded. The + return type should be inserted at start_position. */ + RETURN_IF_ERROR + (demangle_bare_function_type (dm, start_position)); + else + /* Non-template functions don't have their return type + encoded. */ + RETURN_IF_ERROR + (demangle_bare_function_type (dm, BFT_NO_RETURN_TYPE)); + } + + substitution_add (dm, start, template_p, NOT_TEMPLATE_PARM); + } + + /* Pop off template argument lists that were built during the + mangling of this name, to restore the old template context. */ + pop_to_template_arg_list (dm, old_arg_list); + + return STATUS_OK; +} + +/* Demangles and emits a . + + ::= + ::= + ::= + ::= + + ::= + ::= St # ::std:: + + + ::= + ::= */ + +static status_t +demangle_name (dm, template_p) + demangling_t dm; + int *template_p; +{ + int special_std_substitution; + int start = substitution_start (dm); + + DEMANGLE_TRACE ("name", dm); + + switch (peek_char (dm)) + { + case 'N': + /* This is a . */ + RETURN_IF_ERROR (demangle_nested_name (dm, template_p)); + break; + + case 'Z': + RETURN_IF_ERROR (demangle_local_name (dm)); + break; + + case 'S': + /* The `St' substitution allows a name nested in std:: to appear + without being enclosed in a nested name. + ::= St # ::std:: */ + if (peek_char_next (dm) == 't') + { + (void) next_char (dm); + (void) next_char (dm); + result_append (dm, "std::"); + RETURN_IF_ERROR (demangle_unqualified_name (dm)); + } + else + { + RETURN_IF_ERROR (demangle_substitution (dm, template_p, + &special_std_substitution)); + if (special_std_substitution) + { + /* This was the magic `std::' substitution. We can have + a or one of the unscoped names + following. */ + result_append (dm, "::"); + RETURN_IF_ERROR (demangle_name (dm, template_p)); + } + } + break; + + default: + /* This is an or . */ + RETURN_IF_ERROR (demangle_unqualified_name (dm)); + + /* If the is followed by template args, this + is an . */ + if (peek_char (dm) == 'I') + { + /* Add a substitution for the unqualified template name. */ + substitution_add (dm, start, 0, NOT_TEMPLATE_PARM); + + RETURN_IF_ERROR (demangle_template_args (dm)); + *template_p = 1; + } + else + *template_p = 0; + + break; + } + + return STATUS_OK; +} + +/* Demangles and emits a . + + ::= N [] E */ + +static status_t +demangle_nested_name (dm, template_p) + demangling_t dm; + int *template_p; +{ + char peek; + + DEMANGLE_TRACE ("nested-name", dm); + + RETURN_IF_ERROR (demangle_char (dm, 'N')); + + peek = peek_char (dm); + if (peek == 'r' || peek == 'V' || peek == 'K') + { + /* Snarf up and emit CV qualifiers. */ + dyn_string_t cv_qualifiers = dyn_string_new (24); + demangle_CV_qualifiers (dm, cv_qualifiers); + result_append_string (dm, cv_qualifiers); + dyn_string_delete (cv_qualifiers); + result_append_space (dm); + } + + RETURN_IF_ERROR (demangle_prefix (dm, template_p)); + /* No need to demangle the final ; demangle_prefix will + handle it. */ + RETURN_IF_ERROR (demangle_char (dm, 'E')); + + return STATUS_OK; +} + +/* Demangles and emits a . + + ::= + ::= + ::= # empty + ::= + + ::= + ::= + + ::= + ::= */ + +static status_t +demangle_prefix (dm, template_p) + demangling_t dm; + int *template_p; +{ + int start = substitution_start (dm); + int nested = 0; + + /* TEMPLATE_P is updated as we decend the nesting chain. After + , it is set to non-zero; after everything else it + is set to zero. */ + + DEMANGLE_TRACE ("prefix", dm); + + while (1) + { + char peek; + int unused; + + if (end_of_name_p (dm)) + return "Unexpected end of name in ."; + + peek = peek_char (dm); + + if (isdigit (peek) + || (peek >= 'a' && peek <= 'z') + || peek == 'C' || peek == 'D' + || peek == 'S') + { + /* We have another level of scope qualification. */ + if (nested) + result_append (dm, "::"); + else + nested = 1; + + if (peek == 'S') + /* The substitution determines whether this is a + template-id. */ + RETURN_IF_ERROR (demangle_substitution (dm, template_p, + &unused)); + else + { + RETURN_IF_ERROR (demangle_unqualified_name (dm)); + *template_p = 0; + } + } + else if (peek == 'Z') + RETURN_IF_ERROR (demangle_local_name (dm)); + else if (peek == 'I') + { + if (*template_p) + return STATUS_INTERNAL_ERROR; + /* The template name is a substitution candidate. */ + substitution_add (dm, start, 0, NOT_TEMPLATE_PARM); + RETURN_IF_ERROR (demangle_template_args (dm)); + *template_p = 1; + } + else if (peek == 'E') + /* All done. */ + return STATUS_OK; + else + return "Unexpected character in ."; + + /* Add a new substitution for the prefix thus far. */ + substitution_add (dm, start, *template_p, NOT_TEMPLATE_PARM); + } +} + +/* Demangles and emits an . If the + is a function and the first element in the + argument list should be taken to be its return type, + ENCODE_RETURN_TYPE is non-zero. + + ::= + ::= + ::= */ + +static status_t +demangle_unqualified_name (dm) + demangling_t dm; +{ + char peek = peek_char (dm); + + DEMANGLE_TRACE ("unqualified-name", dm); + + if (isdigit (peek)) + RETURN_IF_ERROR (demangle_source_name (dm)); + else if (peek >= 'a' && peek <= 'z') + { + int num_args; + RETURN_IF_ERROR (demangle_operator_name (dm, 0, &num_args)); + } + else if (peek == 'C' || peek == 'D') + RETURN_IF_ERROR (demangle_ctor_dtor_name (dm)); + else + return "Unexpected character in ."; + + return STATUS_OK; +} + +/* Demangles and emits . + + ::= */ + +static status_t +demangle_source_name (dm) + demangling_t dm; +{ + int length; + + DEMANGLE_TRACE ("source-name", dm); + + /* Decode the length of the identifier. */ + RETURN_IF_ERROR (demangle_number (dm, &length, 10, 0)); + if (length == 0) + return "Zero length in ."; + + /* Now the identifier itself. It's placed into last_source_name, + where it can be used to build a constructor or destructor name. */ + RETURN_IF_ERROR (demangle_identifier (dm, length, + dm->last_source_name)); + + /* Emit it. */ + result_append_string (dm, dm->last_source_name); + + return STATUS_OK; +} + +/* Demangles a number, either a or a at the + current position, consuming all consecutive digit characters. Sets + *VALUE to the resulting numberand returns STATUS_OK. The number is + interpreted as BASE, which must be either 10 or 36. If IS_SIGNED + is non-zero, negative numbers -- prefixed with `n' -- are accepted. + + ::= [n] + + ::= */ + +static status_t +demangle_number (dm, value, base, is_signed) + demangling_t dm; + int *value; + int base; + int is_signed; +{ + dyn_string_t number = dyn_string_new (10); + + DEMANGLE_TRACE ("number", dm); + + demangle_number_literally (dm, number, base, is_signed); + *value = strtol (dyn_string_buf (number), NULL, base); + dyn_string_delete (number); + + return STATUS_OK; +} + +/* Demangles a number at the current position. The digits (and minus + sign, if present) that make up the number are appended to STR. + Only base-BASE digits are accepted; BASE must be either 10 or 36. + If IS_SIGNED, negative numbers -- prefixed with `n' -- are + accepted. Does not consume a trailing underscore or other + terminating character. */ + +static status_t +demangle_number_literally (dm, str, base, is_signed) + demangling_t dm; + dyn_string_t str; + int base; + int is_signed; +{ + DEMANGLE_TRACE ("number*", dm); + + if (base != 10 && base != 36) + return STATUS_INTERNAL_ERROR; + + /* An `n' denotes a negative number. */ + if (is_signed && peek_char (dm) == 'n') + { + /* Skip past the n. */ + advance_char (dm); + /* The normal way to write a negative number is with a minus + sign. */ + dyn_string_append_char (str, '-'); + } + + /* Loop until we hit a non-digit. */ + while (1) + { + char peek = peek_char (dm); + if (isdigit (peek) + || (base == 36 && peek >= 'A' && peek <= 'Z')) + /* Accumulate digits. */ + dyn_string_append_char (str, next_char (dm)); + else + /* Not a digit? All done. */ + break; + } + + return STATUS_OK; +} + +/* Demangles an identifier at the current position of LENGTH + characters and places it in IDENTIFIER. */ + +static status_t +demangle_identifier (dm, length, identifier) + demangling_t dm; + int length; + dyn_string_t identifier; +{ + DEMANGLE_TRACE ("identifier", dm); + + dyn_string_clear (identifier); + dyn_string_resize (identifier, length); + while (length-- > 0) + { + if (end_of_name_p (dm)) + return "Unexpected end of name in ."; + dyn_string_append_char (identifier, next_char (dm)); + } + + return STATUS_OK; +} + +/* Demangles and emits an . If SHORT_NAME is non-zero, + the short form is emitted; otherwise the full source form + (`operator +' etc.) is emitted. *NUM_ARGS is set to the number of + operands that the operator takes. + + + ::= nw # new + ::= na # new[] + ::= dl # delete + ::= da # delete[] + ::= ps # + (unary) + ::= ng # - (unary) + ::= ad # & (unary) + ::= de # * (unary) + ::= co # ~ + ::= pl # + + ::= mi # - + ::= ml # * + ::= dv # / + ::= rm # % + ::= an # & + ::= or # | + ::= eo # ^ + ::= aS # = + ::= pL # += + ::= mI # -= + ::= mL # *= + ::= dV # /= + ::= rM # %= + ::= aN # &= + ::= oR # |= + ::= eO # ^= + ::= ls # << + ::= rs # >> + ::= lS # <<= + ::= rS # >>= + ::= eq # == + ::= ne # != + ::= lt # < + ::= gt # > + ::= le # <= + ::= ge # >= + ::= nt # ! + ::= aa # && + ::= oo # || + ::= pp # ++ + ::= mm # -- + ::= cm # , + ::= pm # ->* + ::= pt # -> + ::= cl # () + ::= ix # [] + ::= qu # ? + ::= sz # sizeof + ::= cv # cast + ::= vx # vendor extended operator */ + +static status_t +demangle_operator_name (dm, short_name, num_args) + demangling_t dm; + int short_name; + int *num_args; +{ + struct operator_code + { + /* The mangled code for this operator. */ + char *code; + /* The source name of this operator. */ + char *name; + /* The number of arguments this operator takes. */ + int num_args; + }; + + struct operator_code operators[] = + { + { "aN", "&=" , 2 }, + { "aS", "=" , 2 }, + { "aa", "&&" , 2 }, + { "ad", "&" , 1 }, + { "an", "&" , 2 }, + { "cl", "()" , 0 }, + { "cm", "," , 2 }, + { "co", "~" , 1 }, + { "dV", "/=" , 2 }, + { "da", "delete[]" , 1 }, + { "de", "*" , 1 }, + { "dl", "delete" , 1 }, + { "dv", "/" , 2 }, + { "eO", "^=" , 2 }, + { "eo", "^" , 2 }, + { "eq", "==" , 2 }, + { "ge", ">=" , 2 }, + { "gt", ">" , 2 }, + { "ix", "[]" , 2 }, + { "lS", "<<=" , 2 }, + { "le", "<=" , 2 }, + { "ls", "<<" , 2 }, + { "lt", "<" , 2 }, + { "mI", "-=" , 2 }, + { "mL", "*=" , 2 }, + { "mi", "-" , 2 }, + { "ml", "*" , 2 }, + { "mm", "--" , 1 }, + { "na", "new[]" , 1 }, + { "ne", "!=" , 2 }, + { "ng", "-" , 1 }, + { "nt", "!" , 1 }, + { "nw", "new" , 1 }, + { "oR", "|=" , 2 }, + { "oo", "||" , 2 }, + { "or", "|" , 2 }, + { "pL", "+=" , 2 }, + { "pl", "+" , 2 }, + { "pm", "->*" , 2 }, + { "pp", "++" , 1 }, + { "ps", "+" , 1 }, + { "qu", "?" , 3 }, + { "rM", "%=" , 2 }, + { "rS", ">>=" , 2 }, + { "rm", "%" , 2 }, + { "rs", ">>" , 2 }, + { "sz", "sizeof" , 1 } + }; + + const int num_operators = + sizeof (operators) / sizeof (struct operator_code); + + int c0 = next_char (dm); + int c1 = next_char (dm); + struct operator_code* p1 = operators; + struct operator_code* p2 = operators + num_operators; + + DEMANGLE_TRACE ("operator-name", dm); + + /* Is this a vendor extended operator? */ + if (c0 == 'v' && c1 == 'x') + { + result_append (dm, "operator"); + RETURN_IF_ERROR (demangle_source_name (dm)); + *num_args = 0; + return STATUS_OK; + } + + /* Is this a conversion operator? */ + if (c0 == 'c' && c1 == 'v') + { + result_append (dm, "operator "); + /* Demangle the converted-to type. */ + RETURN_IF_ERROR (demangle_type (dm)); + *num_args = 0; + return STATUS_OK; + } + + /* Perform a binary search for the operator code. */ + while (1) + { + struct operator_code* p = p1 + (p2 - p1) / 2; + char match0 = p->code[0]; + char match1 = p->code[1]; + + if (c0 == match0 && c1 == match1) + /* Found it. */ + { + if (!short_name) + result_append (dm, "operator"); + result_append (dm, p->name); + *num_args = p->num_args; + + return STATUS_OK; + } + + if (p == p1) + /* Couldn't find it. */ + return "Unknown code in ."; + + /* Try again. */ + if (c0 < match0 || (c0 == match0 && c1 < match1)) + p2 = p; + else + p1 = p; + } +} + +/* Demangles and emits a . + + ::= GV # Guard variable + ::= Th[n] _ + # non-virtual base override thunk + ::= Tv[n] _ + _ + # virtual base override thunk + ::= TV # virtual table + ::= TT # VTT + ::= TI # typeinfo structure + ::= TS # typeinfo name + + Also demangles the special g++ mangling, + + ::= CT _ + # construction vtable */ + +static status_t +demangle_special_name (dm) + demangling_t dm; +{ + dyn_string_t number; + int unused; + char peek = peek_char (dm); + + DEMANGLE_TRACE ("special-name", dm); + + if (peek == 'G') + { + /* A guard variable name. Consume the G. */ + advance_char (dm); + RETURN_IF_ERROR (demangle_char (dm, 'V')); + result_append (dm, "guard variable for "); + RETURN_IF_ERROR (demangle_name (dm, &unused)); + } + else if (peek == 'T') + { + /* Other C++ implementation miscellania. Consume the T. */ + advance_char (dm); + + switch (peek_char (dm)) + { + case 'V': + /* Virtual table. */ + advance_char (dm); + result_append (dm, "vtable for "); + RETURN_IF_ERROR (demangle_type (dm)); + break; + + case 'T': + /* VTT structure. */ + advance_char (dm); + result_append (dm, "VTT for "); + RETURN_IF_ERROR (demangle_type (dm)); + break; + + case 'I': + /* Typeinfo structure. */ + advance_char (dm); + result_append (dm, "typeinfo for "); + RETURN_IF_ERROR (demangle_type (dm)); + break; + + case 'S': + /* Character string containing type name, used in typeinfo. */ + advance_char (dm); + result_append (dm, "typeinfo name for "); + RETURN_IF_ERROR (demangle_type (dm)); + break; + + case 'h': + /* Non-virtual thunk. */ + advance_char (dm); + result_append (dm, "non-virtual thunk"); + /* Demangle and emit the offset. */ + number = dyn_string_new (4); + demangle_number_literally (dm, number, 10, 1); + /* Don't display the offset unless in verbose mode. */ + if (flag_verbose) + { + result_append_char (dm, ' '); + result_append_string (dm, number); + } + dyn_string_delete (number); + /* Demangle the separator. */ + RETURN_IF_ERROR (demangle_char (dm, '_')); + /* Demangle and emit the target name and function type. */ + result_append (dm, " to "); + RETURN_IF_ERROR (demangle_encoding (dm)); + break; + + case 'v': + /* Virtual thunk. */ + advance_char (dm); + result_append (dm, "virtual thunk "); + /* Demangle and emit the offset. */ + number = dyn_string_new (4); + demangle_number_literally (dm, number, 10, 1); + /* Don't display the offset unless in verbose mode. */ + if (flag_verbose) + { + result_append_string (dm, number); + result_append_char (dm, ' '); + } + dyn_string_delete (number); + /* Demangle the separator. */ + RETURN_IF_ERROR (demangle_char (dm, '_')); + /* Demangle and emit the vcall offset. */ + number = dyn_string_new (4); + demangle_number_literally (dm, number, 10, 1); + /* Don't display the vcall offset unless in verbose mode. */ + if (flag_verbose) + { + result_append_string (dm, number); + result_append_char (dm, ' '); + } + dyn_string_delete (number); + /* Demangle the separator. */ + RETURN_IF_ERROR (demangle_char (dm, '_')); + /* Demangle and emit the target function. */ + result_append (dm, "to "); + RETURN_IF_ERROR (demangle_encoding (dm)); + break; + + case 'C': + /* TC is a special g++ mangling for a construction vtable. */ + if (!flag_strict) + { + advance_char (dm); + result_append (dm, "construction vtable for "); + RETURN_IF_ERROR (demangle_type (dm)); + /* Demangle the offset. */ + number = dyn_string_new (4); + demangle_number_literally (dm, number, 10, 1); + /* Demangle the underscore separator. */ + RETURN_IF_ERROR (demangle_char (dm, '_')); + /* Demangle the base type. */ + result_append (dm, "-in-"); + RETURN_IF_ERROR (demangle_type (dm)); + /* Don't display the offset unless in verbose mode. */ + if (flag_verbose) + { + result_append_char (dm, ' '); + result_append_string (dm, number); + } + dyn_string_delete (number); + break; + } + /* If flag_strict, fall through. */ + + default: + return "Unrecognized ."; + } + } + else + return STATUS_ERROR; + + return STATUS_OK; +} + +/* Demangles and emits a . + + + ::= C1 # complete object (in-charge) ctor + ::= C2 # base object (not-in-charge) ctor + ::= C3 # complete object (in-charge) allocating ctor + ::= C4 # base object (not-in-charge) allocating ctor + ::= D0 # deleting (in-charge) dtor + ::= D1 # complete object (in-charge) dtor + ::= D2 # base object (not-in-charge) dtor */ + +static status_t +demangle_ctor_dtor_name (dm) + demangling_t dm; +{ + static const char *const ctor_flavors[] = + { + "in-charge", + "not-in-charge", + "in-charge allocating", + "not-in-charge allocating" + }; + static const char *const dtor_flavors[] = + { + "in-charge deleting", + "in-charge", + "not-in-charge" + }; + + int flavor; + char peek = peek_char (dm); + + DEMANGLE_TRACE ("ctor-dtor-name", dm); + + if (peek == 'C') + { + /* A constructor name. Consume the C. */ + advance_char (dm); + if (peek_char (dm) < '1' || peek_char (dm) > '4') + return "Unrecognized constructor."; + result_append_string (dm, dm->last_source_name); + /* Print the flavor of the constructor if in verbose mode. */ + flavor = next_char (dm) - '1'; + if (flag_verbose) + { + result_append (dm, "["); + result_append (dm, ctor_flavors[flavor]); + result_append_char (dm, ']'); + } + } + else if (peek == 'D') + { + /* A destructor name. Consume the D. */ + advance_char (dm); + if (peek_char (dm) < '0' || peek_char (dm) > '2') + return "Unrecognized destructor."; + result_append_char (dm, '~'); + result_append_string (dm, dm->last_source_name); + /* Print the flavor of the destructor if in verbose mode. */ + flavor = next_char (dm) - '0'; + if (flag_verbose) + { + result_append (dm, " ["); + result_append (dm, dtor_flavors[flavor]); + result_append_char (dm, ']'); + } + } + else + return STATUS_ERROR; + + return STATUS_OK; +} + +/* Handle pointer, reference, and pointer-to-member cases for + demangle_type. All consecutive `P's, `R's, and 'M's are joined to + build a pointer/reference type. We snarf all these, plus the + following , all at once since we need to know whether we have + a pointer to data or pointer to function to construct the right + output syntax. C++'s pointer syntax is hairy. + + ::= P + ::= R + ::= + + ::= M */ + +static status_t +demangle_type_ptr (dm) + demangling_t dm; +{ + char next; + status_t status; + + /* Collect pointer symbols into this string. */ + dyn_string_t symbols = dyn_string_new (10); + + DEMANGLE_TRACE ("type*", dm); + + /* Scan forward, collecting pointers and references into symbols, + until we hit something else. Then emit the type. */ + while (1) + { + next = peek_char (dm); + if (next == 'P') + { + dyn_string_append_char (symbols, '*'); + advance_char (dm); + } + else if (next == 'R') + { + dyn_string_append_char (symbols, '&'); + advance_char (dm); + } + else if (next == 'M') + { + /* Pointer-to-member. */ + dyn_string_t class_type; + + /* Eat the 'M'. */ + advance_char (dm); + + /* Capture the type of which this is a pointer-to-member. */ + result_push (dm); + RETURN_IF_ERROR (demangle_type (dm)); + class_type = (dyn_string_t) result_pop (dm); + + /* Build the pointer-to-member notation. It comes before + other pointer and reference qualifiers -- */ + dyn_string_prepend_cstr (symbols, "::*"); + dyn_string_prepend (symbols, class_type); + dyn_string_delete (class_type); + + if (peek_char (dm) == 'F') + continue; + + /* Demangle the type of the pointed-to member. */ + status = demangle_type (dm); + /* Make it pretty. */ + result_append_space (dm); + /* Add the pointer-to-member syntax, and other pointer and + reference symbols. */ + result_append_string (dm, symbols); + /* Clean up. */ + dyn_string_delete (symbols); + + RETURN_IF_ERROR (status); + return STATUS_OK; + } + else if (next == 'F') + { + /* Ooh, tricky, a pointer-to-function. */ + int position = result_length (dm); + result_append_char (dm, '('); + result_append_string (dm, symbols); + result_append_char (dm, ')'); + dyn_string_delete (symbols); + + RETURN_IF_ERROR (demangle_function_type (dm, position)); + return STATUS_OK; + } + else + { + /* No more pointe or reference tokens. Finish up. */ + status = demangle_type (dm); + + result_append_string (dm, symbols); + dyn_string_delete (symbols); + + RETURN_IF_ERROR (status); + return STATUS_OK; + } + } +} + +/* Demangles and emits a . + + ::= + ::= + ::= + ::= + ::= + ::= + ::= + ::= P # pointer-to + ::= R # reference-to + ::= C # complex pair (C 2000) + ::= G # imaginary (C 2000) + ::= U # vendor extended type qualifier + ::= */ + +static status_t +demangle_type (dm) + demangling_t dm; +{ + int start = substitution_start (dm); + char peek = peek_char (dm); + int template_p = 0; + int special_std_substitution; + int is_builtin_type = 0; + template_arg_list_t old_arg_list = current_template_arg_list (dm); + int template_parm = NOT_TEMPLATE_PARM; + + DEMANGLE_TRACE ("type", dm); + + /* A can start with a digit (a ), an + N (a ), or a Z (a ). */ + if (isdigit (peek) || peek == 'N' || peek == 'Z') + RETURN_IF_ERROR (demangle_class_enum_type (dm, &template_p)); + else if (peek >= 'a' && peek <= 'z') + { + RETURN_IF_ERROR (demangle_builtin_type (dm)); + is_builtin_type = 1; + } + else + switch (peek) + { + case 'r': + case 'V': + case 'K': + { + status_t status; + dyn_string_t cv_qualifiers = dyn_string_new (24); + demangle_CV_qualifiers (dm, cv_qualifiers); + + /* If the qualifiers apply to a pointer or reference, they + need to come after the whole qualified type. */ + if (peek_char (dm) == 'P' || peek_char (dm) == 'R') + { + status = demangle_type (dm); + result_append_space (dm); + result_append_string (dm, cv_qualifiers); + } + /* Otherwise, the qualifiers come first. */ + else + { + result_append_string (dm, cv_qualifiers); + result_append_space (dm); + status = demangle_type (dm); + } + + dyn_string_delete (cv_qualifiers); + RETURN_IF_ERROR (status); + } + break; + + case 'F': + return "Non-pointer or -reference function type."; + + case 'A': + RETURN_IF_ERROR (demangle_array_type (dm)); + break; + + case 'T': + RETURN_IF_ERROR (demangle_template_param (dm, &template_parm)); + break; + + case 'S': + RETURN_IF_ERROR (demangle_substitution (dm, &template_p, + &special_std_substitution)); + if (special_std_substitution) + { + /* This was the magic `std::' substitution. What follows + must be a class name in that namespace. */ + result_append (dm, "::"); + RETURN_IF_ERROR (demangle_class_enum_type (dm, &template_p)); + } + break; + + case 'P': + case 'R': + case 'M': + RETURN_IF_ERROR (demangle_type_ptr (dm)); + break; + + case 'C': + /* A C99 complex type. */ + result_append (dm, "complex "); + advance_char (dm); + RETURN_IF_ERROR (demangle_type (dm)); + break; + + case 'G': + /* A C99 imaginary type. */ + result_append (dm, "imaginary "); + advance_char (dm); + RETURN_IF_ERROR (demangle_type (dm)); + break; + + case 'U': + /* Vendor extended type qualifier. */ + advance_char (dm); + RETURN_IF_ERROR (demangle_source_name (dm)); + result_append_char (dm, ' '); + RETURN_IF_ERROR (demangle_type (dm)); + break; + + default: + return "Unexpected character in ."; + } + + /* Unqualified builin types are not substitution candidates. */ + if (!is_builtin_type) + /* Add a new substitution for the type. If this type was a + , pass its index since from the point of + substitutions, a token is a substitution + candidate distinct from the type that is substituted for it. */ + substitution_add (dm, start, template_p, template_parm); + + /* Pop off template argument lists added during mangling of this + type. */ + pop_to_template_arg_list (dm, old_arg_list); + + return STATUS_OK; +} + +/* C++ source names of builtin types, indexed by the mangled code + letter's position in the alphabet ('a' -> 0, 'b' -> 1, etc). */ +static const char *const builtin_type_names[26] = +{ + "signed char", /* a */ + "bool", /* b */ + "char", /* c */ + "double", /* d */ + "long double", /* e */ + "float", /* f */ + "__float128", /* g */ + "unsigned char", /* h */ + "int", /* i */ + "unsigned", /* j */ + NULL, /* k */ + "long", /* l */ + "unsigned long", /* m */ + "__int128", /* n */ + "unsigned __int128", /* o */ + NULL, /* p */ + NULL, /* q */ + NULL, /* r */ + "short", /* s */ + "unsigned short", /* t */ + NULL, /* u */ + "void", /* v */ + "wchar_t", /* w */ + "long long", /* x */ + "unsigned long long", /* y */ + "..." /* z */ +}; + +/* Demangles and emits a . + + ::= v # void + ::= w # wchar_t + ::= b # bool + ::= c # char + ::= a # signed char + ::= h # unsigned char + ::= s # short + ::= t # unsigned short + ::= i # int + ::= j # unsigned int + ::= l # long + ::= m # unsigned long + ::= x # long long, __int64 + ::= y # unsigned long long, __int64 + ::= n # __int128 + ::= o # unsigned __int128 + ::= f # float + ::= d # double + ::= e # long double, __float80 + ::= g # __float128 + ::= z # ellipsis + ::= u # vendor extended type */ + +static status_t +demangle_builtin_type (dm) + demangling_t dm; +{ + + char code = peek_char (dm); + + DEMANGLE_TRACE ("builtin-type", dm); + + if (code == 'u') + { + advance_char (dm); + RETURN_IF_ERROR (demangle_source_name (dm)); + return STATUS_OK; + } + else if (code >= 'a' && code <= 'z') + { + const char *type_name = builtin_type_names[code - 'a']; + if (type_name == NULL) + return "Unrecognized code."; + + result_append (dm, type_name); + advance_char (dm); + return STATUS_OK; + } + else + return "Non-alphabetic code."; +} + +/* Demangles all consecutive CV-qualifiers (const, volatile, and + restrict) at the current position. The qualifiers are appended to + QUALIFIERS. Returns STATUS_OK. */ + +static status_t +demangle_CV_qualifiers (dm, qualifiers) + demangling_t dm; + dyn_string_t qualifiers; +{ + DEMANGLE_TRACE ("CV-qualifiers", dm); + + while (1) + { + switch (peek_char (dm)) + { + case 'r': + dyn_string_append_space (qualifiers); + dyn_string_append_cstr (qualifiers, "restrict"); + break; + + case 'V': + dyn_string_append_space (qualifiers); + dyn_string_append_cstr (qualifiers, "volatile"); + break; + + case 'K': + dyn_string_append_space (qualifiers); + dyn_string_append_cstr (qualifiers, "const"); + break; + + default: + return STATUS_OK; + } + + advance_char (dm); + } +} + +/* Demangles and emits a FUNCTION_NAME_POS is the + position in the result string of the start of the function + identifier, at which the function's return type will be inserted. + + ::= F [Y] E */ + +static status_t +demangle_function_type (dm, function_name_pos) + demangling_t dm; + int function_name_pos; +{ + DEMANGLE_TRACE ("function-type", dm); + RETURN_IF_ERROR (demangle_char (dm, 'F')); + if (peek_char (dm) == 'Y') + { + /* Indicate this function has C linkage if in verbose mode. */ + if (flag_verbose) + result_append (dm, " [extern \"C\"] "); + advance_char (dm); + } + RETURN_IF_ERROR (demangle_bare_function_type (dm, function_name_pos)); + RETURN_IF_ERROR (demangle_char (dm, 'E')); + return STATUS_OK; +} + +/* Demangles and emits a . RETURN_TYPE_POS is the + position in the result string at which the function return type + should be inserted. If RETURN_TYPE_POS is BFT_NO_RETURN_TYPE, the + function's return type is assumed not to be encoded. + + ::= + */ + +static status_t +demangle_bare_function_type (dm, return_type_pos) + demangling_t dm; + int return_type_pos; +{ + /* Sequence is the index of the current function parameter, counting + from zero. The value -1 denotes the return type. */ + int sequence = + (return_type_pos == BFT_NO_RETURN_TYPE ? 0 : -1); + + DEMANGLE_TRACE ("bare-function-type", dm); + + result_append_char (dm, '('); + while (!end_of_name_p (dm) && peek_char (dm) != 'E') + { + if (sequence == -1) + /* We're decoding the function's return type. */ + { + dyn_string_t return_type; + + /* Decode the return type off to the side. */ + result_push (dm); + RETURN_IF_ERROR (demangle_type (dm)); + return_type = (dyn_string_t) result_pop (dm); + + /* Add a space to the end of the type. */ + dyn_string_append_space (return_type); + + /* Insert the return type where we've been asked to. */ + dyn_string_insert (result_string (dm), return_type_pos, + return_type); + dyn_string_delete (return_type); + } + else + { + /* Skip `void' parameter types. One should only occur as + the only type in a parameter list; in that case, we want + to print `foo ()' instead of `foo (void)'. */ + if (peek_char (dm) == 'v') + { + /* Consume the v. */ + advance_char (dm); + continue; + } + /* Separate parameter types by commas. */ + if (sequence > 0) + result_append (dm, ", "); + /* Demangle the type. */ + RETURN_IF_ERROR (demangle_type (dm)); + } + + ++sequence; + } + result_append_char (dm, ')'); + + return STATUS_OK; +} + +/* Demangles and emits a . *TEMPLATE_P is set to + non-zero if the type is a template-id, zero otherwise. + + ::= */ + +static status_t +demangle_class_enum_type (dm, template_p) + demangling_t dm; + int *template_p; +{ + DEMANGLE_TRACE ("class-enum-type", dm); + + RETURN_IF_ERROR (demangle_name (dm, template_p)); + return STATUS_OK; +} + +/* Demangles and emits an . + + ::= A [] _ */ + +static status_t +demangle_array_type (dm) + demangling_t dm; +{ + dyn_string_t array_size = dyn_string_new (10); + + RETURN_IF_ERROR (demangle_char (dm, 'A')); + + /* Demangle the array size into array_size. */ + RETURN_IF_ERROR (demangle_number_literally (dm, array_size, 10, 0)); + + /* Demangle the base type of the array. */ + RETURN_IF_ERROR (demangle_char (dm, '_')); + RETURN_IF_ERROR (demangle_type (dm)); + + /* Emit the array dimension syntax. */ + result_append_char (dm, '['); + result_append_string (dm, array_size); + result_append_char (dm, ']'); + dyn_string_delete (array_size); + + return STATUS_OK; +} + +/* Demangles and emits a . The zero-indexed position + in the parameter list is placed in *TEMPLATE_PARM_NUMBER. + + ::= T_ # first template parameter + ::= T _ */ + +static status_t +demangle_template_param (dm, template_parm_number) + demangling_t dm; + int *template_parm_number; +{ + int parm_number; + template_arg_list_t current_arg_list = current_template_arg_list (dm); + string_list_t arg; + + DEMANGLE_TRACE ("template-param", dm); + + /* Make sure there is a template argmust list in which to look up + this parameter reference. */ + if (current_arg_list == NULL) + return "Template parameter outside of template."; + + RETURN_IF_ERROR (demangle_char (dm, 'T')); + if (peek_char (dm) == '_') + parm_number = 0; + else + { + RETURN_IF_ERROR (demangle_number (dm, &parm_number, 10, 0)); + ++parm_number; + } + RETURN_IF_ERROR (demangle_char (dm, '_')); + + arg = template_arg_list_get_arg (current_arg_list, parm_number); + if (arg == NULL) + /* parm_number exceeded the number of arguments in the current + template argument list. */ + return "Template parameter number out of bounds."; + result_append_string (dm, (dyn_string_t) arg); + + if (peek_char (dm) == 'I') + RETURN_IF_ERROR (demangle_template_args (dm)); + + *template_parm_number = parm_number; + return STATUS_OK; +} + +/* Demangles and emits a . + + ::= I + E */ + +static status_t +demangle_template_args (dm) + demangling_t dm; +{ + int first = 1; + template_arg_list_t arg_list = template_arg_list_new (); + + /* Preserve the most recently demangled source name. */ + dyn_string_t old_last_source_name = dm->last_source_name; + dm->last_source_name = dyn_string_new (0); + + DEMANGLE_TRACE ("template-args", dm); + + RETURN_IF_ERROR (demangle_char (dm, 'I')); + result_append_char (dm, '<'); + do + { + string_list_t arg; + + if (first) + first = 0; + else + result_append (dm, ", "); + + /* Capture the template arg. */ + result_push (dm); + RETURN_IF_ERROR (demangle_template_arg (dm)); + arg = result_pop (dm); + + /* Emit it in the demangled name. */ + result_append_string (dm, (dyn_string_t) arg); + + /* Save it for use in expanding s. */ + template_arg_list_add_arg (arg_list, arg); + } + while (peek_char (dm) != 'E'); + /* Append the '>'. */ + result_close_template_list (dm); + + /* Consume the 'E'. */ + advance_char (dm); + + /* Restore the most recent demangled source name. */ + dyn_string_delete (dm->last_source_name); + dm->last_source_name = old_last_source_name; + + /* Push the list onto the top of the stack of template argument + lists, so that arguments from it are used from now on when + expanding s. */ + push_template_arg_list (dm, arg_list); + + return STATUS_OK; +} + +/* This function, which does not correspond to a production in the + mangling spec, handles the `literal' production for both + and . It does not expect or consume + the initial `L' or final `E'. The demangling is given by: + + ::= + + and the emitted output is `(type)number'. */ + +static status_t +demangle_literal (dm) + demangling_t dm; +{ + dyn_string_t value = dyn_string_new (0); + char peek = peek_char (dm); + + DEMANGLE_TRACE ("literal", dm); + + if (!flag_verbose && peek >= 'a' && peek <= 'z') + { + /* If not in verbose mode and this is a builtin type, see if we + can produce simpler numerical output. In particular, for + integer types shorter than `long', just write the number + without type information; for bools, write `true' or `false'. + Other refinements could be made here too. */ + + /* This constant string is used to map from codes + (26 letters of the alphabet) to codes that determine how the + value will be displayed. The codes are: + b: display as bool + i: display as int + l: display as long + A space means the value will be represented using cast + notation. */ + static const char *const code_map = "ibi iii ll ii i "; + + char code = code_map[peek - 'a']; + /* FIXME: Implement demangling of floats and doubles. */ + if (code == 'u') + return STATUS_UNIMPLEMENTED; + if (code == 'b') + { + /* It's a boolean. */ + char value; + + /* Consume the b. */ + advance_char (dm); + /* Look at the next character. It should be 0 or 1, + corresponding to false or true, respectively. */ + value = peek_char (dm); + if (value == '0') + result_append (dm, "false"); + else if (value == '1') + result_append (dm, "true"); + else + return "Unrecognized bool constant."; + /* Consume the 0 or 1. */ + advance_char (dm); + return STATUS_OK; + } + else if (code == 'i' || code == 'l') + { + /* It's an integer or long. */ + + /* Consume the type character. */ + advance_char (dm); + /* Demangle the number and write it out. */ + RETURN_IF_ERROR (demangle_number_literally (dm, value, 10, 1)); + result_append_string (dm, value); + /* For long integers, append an l. */ + if (code == 'l') + result_append_char (dm, code); + return STATUS_OK; + } + /* ...else code == ' ', so fall through to represent this + literal's type explicitly using cast syntax. */ + } + + result_append_char (dm, '('); + RETURN_IF_ERROR (demangle_type (dm)); + result_append_char (dm, ')'); + + RETURN_IF_ERROR (demangle_number_literally (dm, value, 10, 1)); + result_append_string (dm, value); + dyn_string_delete (value); + + return STATUS_OK; +} + +/* Demangles and emits a . + + ::= # type + ::= L E # literal + ::= LZ E # external name + ::= X E # expression */ + +static status_t +demangle_template_arg (dm) + demangling_t dm; +{ + DEMANGLE_TRACE ("template-arg", dm); + + switch (peek_char (dm)) + { + case 'L': + advance_char (dm); + + if (peek_char (dm) == 'Z') + { + /* External name. */ + advance_char (dm); + /* FIXME: Standard is contradictory here. */ + RETURN_IF_ERROR (demangle_encoding (dm)); + } + else + RETURN_IF_ERROR (demangle_literal (dm)); + RETURN_IF_ERROR (demangle_char (dm, 'E')); + break; + + case 'X': + /* Expression. */ + advance_char (dm); + RETURN_IF_ERROR (demangle_expression (dm)); + break; + + default: + RETURN_IF_ERROR (demangle_type (dm)); + break; + } + + return STATUS_OK; +} + +/* Demangles and emits an . + + ::= + ::= + ::= + ::= */ + +static status_t +demangle_expression (dm) + demangling_t dm; +{ + char peek = peek_char (dm); + + DEMANGLE_TRACE ("expression", dm); + + if (peek == 'L' || peek == 'T') + RETURN_IF_ERROR (demangle_expr_primary (dm)); + else if (peek == 's' && peek_char_next (dm) == 'r') + RETURN_IF_ERROR (demangle_scope_expression (dm)); + else + /* An operator expression. */ + { + int num_args; + dyn_string_t operator_name; + + /* We have an operator name. Since we want to output binary + operations in infix notation, capture the operator name + first. */ + result_push (dm); + RETURN_IF_ERROR (demangle_operator_name (dm, 1, &num_args)); + operator_name = (dyn_string_t) result_pop (dm); + + /* If it's binary, do an operand first. */ + if (num_args > 1) + { + result_append_char (dm, '('); + RETURN_IF_ERROR (demangle_expression (dm)); + result_append_char (dm, ')'); + } + + /* Now emit the operator, followed by its second (if binary) or + only (if unary) operand. */ + result_append_string (dm, operator_name); + dyn_string_delete (operator_name); + result_append_char (dm, '('); + RETURN_IF_ERROR (demangle_expression (dm)); + result_append_char (dm, ')'); + + /* The ternary operator takes a third operand. */ + if (num_args == 3) + { + result_append (dm, ":("); + RETURN_IF_ERROR (demangle_expression (dm)); + result_append_char (dm, ')'); + } + } + + return STATUS_OK; +} + +/* Demangles and emits a . + + ::= sr + ::= sr */ + +static status_t +demangle_scope_expression (dm) + demangling_t dm; +{ + RETURN_IF_ERROR (demangle_char (dm, 's')); + RETURN_IF_ERROR (demangle_char (dm, 'r')); + RETURN_IF_ERROR (demangle_type (dm)); + result_append (dm, "::"); + RETURN_IF_ERROR (demangle_encoding (dm)); + return STATUS_OK; +} + +/* Demangles and emits an . + + ::= + ::= L E # literal + ::= L E # external name */ + +static status_t +demangle_expr_primary (dm) + demangling_t dm; +{ + char peek = peek_char (dm); + int unused; + + DEMANGLE_TRACE ("expr-primary", dm); + + if (peek == 'T') + RETURN_IF_ERROR (demangle_template_param (dm, &unused)); + else if (peek == 'L') + { + /* Consume the `L'. */ + advance_char (dm); + peek = peek_char (dm); + + if (peek == '_') + RETURN_IF_ERROR (demangle_mangled_name (dm)); + else + RETURN_IF_ERROR (demangle_literal (dm)); + + RETURN_IF_ERROR (demangle_char (dm, 'E')); + } + else + return STATUS_ERROR; + + return STATUS_OK; +} + +/* Demangles and emits a . Sets *TEMPLATE_P to non-zero + if the substitution is the name of a template, zero otherwise. If + the substitution token is St, which corresponds to the `::std::' + namespace and can appear in a non-nested name, sets + *SPECIAL_STD_SUBSTITUTION to non-zero; zero otherwise. + + ::= S _ + ::= S_ + + ::= St # ::std:: + ::= Sa # ::std::allocator + ::= Sb # ::std::basic_string + ::= Ss # ::std::basic_string, + ::std::allocator > + ::= Si # ::std::basic_istream > + ::= So # ::std::basic_ostream > + ::= Sd # ::std::basic_iostream > +*/ + +static status_t +demangle_substitution (dm, template_p, special_std_substitution) + demangling_t dm; + int *template_p; + int *special_std_substitution; +{ + int seq_id; + int peek; + dyn_string_t text; + + DEMANGLE_TRACE ("substitution", dm); + + RETURN_IF_ERROR (demangle_char (dm, 'S')); + *special_std_substitution = 0; + + /* Scan the substitution sequence index. A missing number denotes + the first index. */ + peek = peek_char (dm); + if (peek == '_') + seq_id = -1; + /* If the following character is 0-9 or a capital letter, interpret + the sequence up to the next underscore as a base-36 substitution + index. */ + else if (isdigit (peek) + || (peek >= 'A' && peek <= 'Z')) + RETURN_IF_ERROR (demangle_number (dm, &seq_id, 36, 0)); + else + { + switch (peek) + { + case 't': + result_append (dm, "std"); + *special_std_substitution = 1; + break; + + case 'a': + result_append (dm, "std::allocator"); + dyn_string_copy_cstr (dm->last_source_name, "allocator"); + break; + + case 'b': + result_append (dm, "std::basic_string"); + dyn_string_copy_cstr (dm->last_source_name, "basic_string"); + break; + + case 's': + if (!flag_verbose) + { + result_append (dm, "std::string"); + dyn_string_copy_cstr (dm->last_source_name, "string"); + } + else + { + result_append (dm, "std::basic_string, std::allocator >"); + dyn_string_copy_cstr (dm->last_source_name, "basic_string"); + } + break; + + case 'i': + if (!flag_verbose) + { + result_append (dm, "std::istream"); + dyn_string_copy_cstr (dm->last_source_name, "istream"); + } + else + { + result_append (dm, "std::basic_istream >"); + dyn_string_copy_cstr (dm->last_source_name, "basic_istream"); + } + break; + + case 'o': + if (!flag_verbose) + { + result_append (dm, "std::ostream"); + dyn_string_copy_cstr (dm->last_source_name, "ostream"); + } + else + { + result_append (dm, "std::basic_ostream >"); + dyn_string_copy_cstr (dm->last_source_name, "basic_ostream"); + } + break; + + case 'd': + if (!flag_verbose) + { + result_append (dm, "std::iostream"); + dyn_string_copy_cstr (dm->last_source_name, "iostream"); + } + else + { + result_append (dm, "std::basic_iostream >"); + dyn_string_copy_cstr (dm->last_source_name, "basic_iostream"); + } + break; + + default: + return "Unrecognized ."; + } + + advance_char (dm); + return STATUS_OK; + } + + /* Look up the substitution text. Since `S_' is the most recent + substitution, `S0_' is the second-most-recent, etc., shift the + numbering by one. */ + text = substitution_get (dm, seq_id + 1, template_p); + if (text == NULL) + return "Substitution number out of range."; + + /* Emit the substitution text. */ + result_append_string (dm, text); + + RETURN_IF_ERROR (demangle_char (dm, '_')); + return STATUS_OK; +} + +/* Demangles and emits a . + + := Z E [] + := Z E s [] */ + +static status_t +demangle_local_name (dm) + demangling_t dm; +{ + DEMANGLE_TRACE ("local-name", dm); + + RETURN_IF_ERROR (demangle_char (dm, 'Z')); + RETURN_IF_ERROR (demangle_encoding (dm)); + RETURN_IF_ERROR (demangle_char (dm, 'E')); + result_append (dm, "'s "); + + if (peek_char (dm) == 's') + { + /* Local character string literal. */ + result_append (dm, "string literal"); + /* Consume the s. */ + advance_char (dm); + RETURN_IF_ERROR (demangle_discriminator (dm, 0)); + } + else + { + int unused; + result_append (dm, "local "); + /* Local name for some other entity. Demangle its name. */ + RETURN_IF_ERROR (demangle_name (dm, &unused)); + RETURN_IF_ERROR (demangle_discriminator (dm, 1)); + } + + return STATUS_OK; + } + + /* Optimonally demangles and emits a . If there is no + at the current position in the mangled string, the + descriminator is assumed to be zero. Emit the discriminator number + in parentheses, unless SUPPRESS_FIRST is non-zero and the + discriminator is zero. + + ::= _ */ + +static status_t +demangle_discriminator (dm, suppress_first) + demangling_t dm; + int suppress_first; +{ + /* Output for s to the demangled name is completely + supressed if not in verbose mode. */ + + if (peek_char (dm) == '_') + { + /* Consume the underscore. */ + advance_char (dm); + if (flag_verbose) + result_append (dm, " [#"); + /* Check if there's a number following the underscore. */ + if (isdigit (peek_char (dm))) + { + int discriminator; + /* Demangle the number. */ + RETURN_IF_ERROR (demangle_number (dm, &discriminator, 10, 0)); + if (flag_verbose) + /* Write the discriminator. The mangled number is two + less than the discriminator ordinal, counting from + zero. */ + int_to_dyn_string (discriminator + 2, + (dyn_string_t) dm->result); + } + else + { + if (flag_verbose) + /* A missing digit correspond to one. */ + result_append_char (dm, '1'); + } + if (flag_verbose) + result_append_char (dm, ']'); + } + else if (!suppress_first) + { + if (flag_verbose) + result_append (dm, " [#0]"); + } + + return STATUS_OK; +} + +/* Demangle NAME into RESULT, which must be an initialized + dyn_string_t. On success, returns STATUS_OK. On failure, returns + an error message, and the contents of RESULT are unchanged. */ + +status_t +cp_demangle (name, result) + char *name; + dyn_string_t result; +{ + status_t status; + int length = strlen (name); + + if (length > 2 && name[0] == '_' && name[1] == 'Z') + { + demangling_t dm = demangling_new (name); + + result_push (dm); + status = demangle_mangled_name (dm); + + if (status == STATUS_OK) + { + dyn_string_t demangled = (dyn_string_t) result_pop (dm); + dyn_string_copy (result, demangled); + dyn_string_delete (demangled); + } + + demangling_delete (dm); + } + else + { + /* It's evidently not a mangled C++ name. It could be the name + of something with C linkage, though, so just copy NAME into + RESULT. */ + dyn_string_copy_cstr (result, name); + status = STATUS_OK; + } + + return status; +} + +/* Variant entry point for integration with the existing cplus-dem + demangler. Attempts to demangle MANGLED. If the demangling + succeeds, returns a buffer, allocated with malloc, containing the + demangled name. The caller must deallocate the buffer using free. + If the demangling failes, returns NULL. */ + +char * +cplus_demangle_new_abi (mangled) + const char* mangled; +{ + /* Create a dyn_string to hold the demangled name. */ + dyn_string_t demangled = dyn_string_new (0); + /* Attempt the demangling. */ + status_t status = cp_demangle ((char *) mangled, demangled); + if (status == STATUS_OK) + /* Demangling succeeded. */ + { + /* Grab the demangled result from the dyn_string. It was + allocated with malloc, so we can return it directly. */ + char *return_value = dyn_string_release (demangled); + /* The dyn_string can go away. */ + dyn_string_delete (demangled); + /* Hand back the demangled name. */ + return return_value; + } + else + /* Demangling failed. */ + { + dyn_string_delete (demangled); + return NULL; + } +} + +#ifdef STANDALONE_DEMANGLER + +#include "getopt.h" + +static void print_usage + PARAMS ((FILE* fp, int exit_value)); + +/* Non-zero if CHAR is a character than can occur in a mangled name. */ +#define is_mangled_char(CHAR) (isalnum (CHAR) || (CHAR) == '_') + +/* The name of this program, as invoked. */ +const char* program_name; + +/* Prints usage summary to FP and then exits with EXIT_VALUE. */ + +static void +print_usage (fp, exit_value) + FILE* fp; + int exit_value; +{ + fprintf (fp, "Usage: %s [options] [names ...]\n", program_name); + fprintf (fp, "Options:\n", program_name); + fprintf (fp, " -h,--help Display this message.\n"); + fprintf (fp, " -s,--strict Demangle standard names only.\n"); + fprintf (fp, " -v,--verbose Produce verbose demanglings.\n"); + fprintf (fp, "If names are provided, they are demangled. Otherwise filters standard input.\n"); + + exit (exit_value); +} + +/* Option specification for getopt_long. */ +static struct option long_options[] = +{ + { "help", no_argument, NULL, 'h' }, + { "strict", no_argument, NULL, 's' }, + { "verbose", no_argument, NULL, 'v' }, + { NULL, no_argument, NULL, 0 }, +}; + +/* Main entry for a demangling filter executable. It will demangle + its command line arguments, if any. If none are provided, it will + filter stdin to stdout, replacing any recognized mangled C++ names + with their demangled equivalents. */ + +int +main (argc, argv) + int argc; + char *argv[]; +{ + status_t status; + int i; + int opt_char; + + /* Use the program name of this program, as invoked. */ + program_name = argv[0]; + + /* Parse options. */ + do + { + opt_char = getopt_long (argc, argv, "hsv", long_options, NULL); + switch (opt_char) + { + case '?': /* Unrecognized option. */ + print_usage (stderr, 1); + break; + + case 'h': + print_usage (stdout, 0); + break; + + case 's': + flag_strict = 1; + break; + + case 'v': + flag_verbose = 1; + break; + } + } + while (opt_char != -1); + + if (optind == argc) + /* No command line arguments were provided. Filter stdin. */ + { + dyn_string_t mangled = dyn_string_new (3); + dyn_string_t demangled = dyn_string_new (0); + status_t status; + + /* Read all of input. */ + while (!feof (stdin)) + { + char c = getchar (); + + /* The first character of a mangled name is an underscore. */ + if (feof (stdin)) + break; + if (c != '_') + { + /* It's not a mangled name. Print the character and go + on. */ + putchar (c); + continue; + } + c = getchar (); + + /* The second character of a mangled name is a capital `Z'. */ + if (feof (stdin)) + break; + if (c != 'Z') + { + /* It's not a mangled name. Print the previous + underscore, the `Z', and go on. */ + putchar ('_'); + putchar (c); + continue; + } + + /* Start keeping track of the candidate mangled name. */ + dyn_string_append_char (mangled, '_'); + dyn_string_append_char (mangled, 'Z'); + + /* Pile characters into mangled until we hit one that can't + occur in a mangled name. */ + c = getchar (); + while (!feof (stdin) && is_mangled_char (c)) + { + dyn_string_append_char (mangled, c); + if (feof (stdin)) + break; + c = getchar (); + } + + /* Attempt to demangle the name. */ + status = cp_demangle (dyn_string_buf (mangled), demangled); + + /* If the demangling succeeded, great! Print out the + demangled version. */ + if (status == STATUS_OK) + fputs (dyn_string_buf (demangled), stdout); + /* Otherwise, it might not have been a mangled name. Just + print out the original text. */ + else + fputs (dyn_string_buf (mangled), stdout); + + /* If we haven't hit EOF yet, we've read one character that + can't occur in a mangled name, so print it out. */ + if (!feof (stdin)) + putchar (c); + + /* Clear the candidate mangled name, to start afresh next + time we hit a `_Z'. */ + dyn_string_clear (mangled); + } + + dyn_string_delete (mangled); + dyn_string_delete (demangled); + } + else + /* Demangle command line arguments. */ + { + dyn_string_t result = dyn_string_new (0); + + /* Loop over command line arguments. */ + for (i = optind; i < argc; ++i) + { + /* Attempt to demangle. */ + status = cp_demangle (argv[i], result); + + /* If it worked, print the demangled name. */ + if (status == STATUS_OK) + printf ("%s\n", dyn_string_buf (result)); + /* If not, print the error message to stderr instead. */ + else + fprintf (stderr, "%s\n", status); + } + dyn_string_delete (result); + } + + return 0; +} + +#endif /* STANDALONE_DEMANGLER */ diff --git a/libiberty/cplus-dem.c b/libiberty/cplus-dem.c index 9ccce64e655..44d11968162 100644 --- a/libiberty/cplus-dem.c +++ b/libiberty/cplus-dem.c @@ -291,6 +291,12 @@ struct demangler_engine libiberty_demanglers[] = } , { + GNU_NEW_ABI_DEMANGLING_STYLE_STRING, + gnu_new_abi_demangling, + "GNU (g++) new-ABI-style demangling" + } + , + { NULL, unknown_demangling, NULL } }; @@ -876,6 +882,10 @@ cplus_demangle (mangled, options) if ((work -> options & DMGL_STYLE_MASK) == 0) work -> options |= (int) current_demangling_style & DMGL_STYLE_MASK; + /* The new-ABI demangling is implemented elsewhere. */ + if (GNU_NEW_ABI_DEMANGLING) + return cplus_demangle_new_abi (mangled); + ret = internal_cplus_demangle (work, mangled); squangle_mop_up (work); return (ret); @@ -4574,6 +4584,9 @@ standard_symbol_characters PARAMS ((void)); static const char * hp_symbol_characters PARAMS ((void)); +static const char * +gnu_new_abi_symbol_characters PARAMS ((void)); + /* Return the string of non-alnum characters that may occur as a valid symbol component, in the standard assembler symbol syntax. */ @@ -4622,6 +4635,17 @@ hp_symbol_characters () } +/* Return the string of non-alnum characters that may occur + as a valid symbol component in the GNU standard C++ ABI mangling + scheme. */ + +static const char * +gnu_new_abi_symbol_characters () +{ + return "_"; +} + + extern int main PARAMS ((int, char **)); int @@ -4696,6 +4720,9 @@ main (argc, argv) case hp_demangling: valid_symbols = hp_symbol_characters (); break; + case gnu_new_abi_demangling: + valid_symbols = gnu_new_abi_symbol_characters (); + break; default: /* Folks should explicitly indicate the appropriate alphabet for each demangling. Providing a default would allow the diff --git a/libiberty/dyn-string.c b/libiberty/dyn-string.c new file mode 100644 index 00000000000..f6fad618031 --- /dev/null +++ b/libiberty/dyn-string.c @@ -0,0 +1,331 @@ +/* An abstract string datatype. + Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + Contributed by Mark Mitchell (mark@markmitchell.com). + +This file is part of GNU CC. + +GNU CC 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 2, or (at your option) +any later version. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#include "libiberty.h" +#include "dyn-string.h" + +/* Performs in-place initialization of a dyn_string struct. This + function can be used with a dyn_string struct on the stack or + embedded in another object. The contents of of the string itself + are still dynamically allocated. The string initially is capable + of holding at least SPACE characeters, including the terminating + NUL. If SPACE is 0, it will silently be increated to 1. */ + +void +dyn_string_init (ds_struct_ptr, space) + struct dyn_string *ds_struct_ptr; + int space; +{ + /* We need at least one byte in which to store the terminating NUL. */ + if (space == 0) + space = 1; + + ds_struct_ptr->allocated = space; + ds_struct_ptr->s = (char *) xmalloc (space); + ds_struct_ptr->length = 0; + ds_struct_ptr->s[0] = '\0'; +} + +/* Create a new dynamic string capable of holding at least SPACE characters, + including the terminating NUL. If SPACE is 0, it will be silently + increased to 1. */ + +dyn_string_t +dyn_string_new (space) + int space; +{ + dyn_string_t result = (dyn_string_t) xmalloc (sizeof (struct dyn_string)); + dyn_string_init (result, space); + return result; +} + +/* Free the memory used by DS. */ + +void +dyn_string_delete (ds) + dyn_string_t ds; +{ + free (ds->s); + free (ds); +} + +/* Returns the contents of DS in a buffer allocated with malloc. It + is the caller's responsibility to deallocate the buffer using free. + DS is then set to the empty string. */ + +char* +dyn_string_release (ds) + dyn_string_t ds; +{ + /* Store the old buffer. */ + char* result = ds->s; + /* The buffer is no longer owned by DS. */ + ds->s = NULL; + /* Reinitialize DS to the empty string. */ + dyn_string_init (ds, 0); + /* Return the old buffer. */ + return result; +} + +/* Increase the capacity of DS so it can hold at least SPACE + characters, plus the terminating NUL. This function will not (at + present) reduce the capacity of DS. */ + +dyn_string_t +dyn_string_resize (ds, space) + dyn_string_t ds; + int space; +{ + int new_allocated = ds->allocated; + + /* Increase SPACE to hold the NUL termination. */ + ++space; + + while (space > new_allocated) + new_allocated *= 2; + + if (new_allocated != ds->allocated) + { + /* We actually need more space. */ + ds->allocated = new_allocated; + ds->s = (char *) xrealloc (ds->s, ds->allocated); + } + + return ds; +} + +/* Sets the contents of DS to the empty string. */ + +void +dyn_string_clear (ds) + dyn_string_t ds; +{ + /* A dyn_string always has room for at least the NUL terminator. */ + ds->s[0] = '\0'; + ds->length = 0; +} + +/* Makes the contents of DEST the same as the contents of SRC. DEST + and SRC must be distinct. */ + +void +dyn_string_copy (dest, src) + dyn_string_t dest; + dyn_string_t src; +{ + if (dest == src) + abort (); + + /* Make room in DEST. */ + dyn_string_resize (dest, src->length); + /* Copy DEST into SRC. */ + strcpy (dest->s, src->s); + /* Update the size of DEST. */ + dest->length = src->length; +} + +/* Copies SRC, a NUL-terminated string, into DEST. */ + +void +dyn_string_copy_cstr (dest, src) + dyn_string_t dest; + const char *src; +{ + int length = strlen (src); + /* Make room in DEST. */ + dyn_string_resize (dest, length); + /* Copy DEST into SRC. */ + strcpy (dest->s, src); + /* Update the size of DEST. */ + dest->length = length; +} + +/* Inserts SRC at the beginning of DEST. DEST is expanded as + necessary. SRC and DEST must be distinct. */ + +void +dyn_string_prepend (dest, src) + dyn_string_t dest; + dyn_string_t src; +{ + dyn_string_insert (dest, 0, src); +} + +/* Inserts SRC, a NUL-terminated string, at the beginning of DEST. + DEST is expanded as necessary. */ + +void +dyn_string_prepend_cstr (dest, src) + dyn_string_t dest; + const char *src; +{ + dyn_string_insert_cstr (dest, 0, src); +} + +/* Inserts SRC into DEST starting at position POS. DEST is expanded as + necessary. SRC and DEST must be distinct. */ + +void +dyn_string_insert (dest, pos, src) + dyn_string_t dest; + int pos; + dyn_string_t src; +{ + int i; + + if (src == dest) + abort (); + + dyn_string_resize (dest, dest->length + src->length); + /* Make room for the insertion. Be sure to copy the NUL. */ + for (i = dest->length; i >= pos; --i) + dest->s[i + src->length] = dest->s[i]; + /* Splice in the new stuff. */ + strncpy (dest->s + pos, src->s, src->length); + /* Compute the new length. */ + dest->length += src->length; +} + +/* Inserts SRC, a NUL-terminated string, into DEST starting at + position POS. DEST is expanded as necessary. */ + +void +dyn_string_insert_cstr (dest, pos, src) + dyn_string_t dest; + int pos; + const char *src; +{ + int i; + int length = strlen (src); + + dyn_string_resize (dest, dest->length + length); + /* Make room for the insertion. Be sure to copy the NUL. */ + for (i = dest->length; i >= pos; --i) + dest->s[i + length] = dest->s[i]; + /* Splice in the new stuff. */ + strncpy (dest->s + pos, src, length); + /* Compute the new length. */ + dest->length += length; +} + +/* Append S to DS, resizing DS if necessary. Returns DS. */ + +dyn_string_t +dyn_string_append (ds, s) + dyn_string_t ds; + dyn_string_t s; +{ + dyn_string_resize (ds, ds->length + s->length); + strcpy (ds->s + ds->length, s->s); + ds->length += s->length; + return ds; +} + +/* Append the NUL-terminated string S to DS, resizing DS if necessary. + Returns DS. */ + +dyn_string_t +dyn_string_append_cstr (ds, s) + dyn_string_t ds; + const char *s; +{ + int len = strlen (s); + + /* The new length is the old length plus the size of our string, plus + one for the null at the end. */ + dyn_string_resize (ds, ds->length + len); + strcpy (ds->s + ds->length, s); + ds->length += len; + + return ds; +} + +/* Appends C to the end of DS. */ + +dyn_string_t +dyn_string_append_char (ds, c) + dyn_string_t ds; + char c; +{ + /* Make room for the extra character. */ + dyn_string_resize (ds, ds->length + 1); + /* Append the character; it will overwrite the old NUL. */ + ds->s[ds->length] = c; + /* Add a new NUL at the end. */ + ds->s[ds->length + 1] = '\0'; + /* Update the length. */ + ++(ds->length); + return ds; +} + +/* Sets the contents of DEST to the substring of SRC starting at START + and ending before END. START must be less than or equal to END, + and both must be between zero and the length of SRC, inclusive. */ + +void +dyn_string_substring (dest, src, start, end) + dyn_string_t dest; + dyn_string_t src; + int start; + int end; +{ + int i; + int length = end - start; + + if (start > end || start > src->length || end > src->length) + abort (); + + /* Make room for the substring. */ + dyn_string_resize (dest, length); + /* Copy the characters in the substring, */ + for (i = length; --i >= 0; ) + dest->s[i] = src->s[start + i]; + /* NUL-terimate the result. */ + dest->s[length] = '\0'; + /* Record the length of the substring. */ + dest->length = length; +} + +/* Returns non-zero if DS1 and DS2 have the same contents. */ + +int +dyn_string_eq (ds1, ds2) + dyn_string_t ds1; + dyn_string_t ds2; +{ + /* If DS1 and DS2 have different lengths, they must not be the same. */ + if (ds1->length != ds2->length) + return 0; + else + return !strcmp (ds1->s, ds2->s); +} -- 2.11.0