X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fcplus-dem.c;h=b9c6288853f382a41befd58d0d9f7b374858d554;hb=173a4f1bdac6b08dddc43e1fdbb1e291e0f82cb7;hp=3689b237985633d4c625b058400574c00b183eee;hpb=f88e7af8313dbf9c7c8f4781c2b1ac064c56ac2e;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/cplus-dem.c b/gdb/cplus-dem.c index 3689b23798..b9c6288853 100644 --- a/gdb/cplus-dem.c +++ b/gdb/cplus-dem.c @@ -1,10 +1,11 @@ /* Demangler for GNU C++ - Copyright (C) 1989 Free Software Foundation, Inc. - written by James Clark (jjc@jclark.uucp) + Copyright 1989, 1991 Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.uucp) + Rewritten by Fred Fish (fnf@cygnus.com) for ARM and Lucid demangling 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 1, or (at your option) + the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, @@ -16,389 +17,1302 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* This is for g++ 1.36.1 (November 6 version). It will probably - require changes for any other version. +/* This is for g++ 1.95.03 (November 13 version). */ - Modified for g++ 1.36.2 (November 18 version). */ +/* This file exports two functions; cplus_mangle_opname and cplus_demangle. -/* This file exports one function + This file imports xmalloc and xrealloc, which are like malloc and + realloc except that they generate a fatal error if there is no + available memory. */ + +/* GDB-specific, FIXME. */ +#include "defs.h" +#include "demangle.h" + +#include +#include + +/* In order to allow a single demangler executable to demangle strings + using various common values of CPLUS_MARKER, as well as any specific + one set at compile time, we maintain a string containing all the + commonly used ones, and check to see if the marker we are looking for + is in that string. CPLUS_MARKER is usually '$' on systems where the + assembler can deal with that. Where the assembler can't, it's usually + '.' (but on many systems '.' is used for other things). We put the + current defined CPLUS_MARKER first (which defaults to '$'), followed + by the next most common value, followed by an explicit '$' in case + the value of CPLUS_MARKER is not '$'. + + We could avoid this if we could just get g++ to tell us what the actual + cplus marker character is as part of the debug information, perhaps by + ensuring that it is the character that terminates the gcc_compiled + marker symbol (FIXME). */ + +#if !defined (CPLUS_MARKER) +#define CPLUS_MARKER '$' +#endif + +static const char cplus_markers[] = { CPLUS_MARKER, '.', '$', '\0' }; + +#ifndef __STDC__ +#define const +#endif + +/* Stuff that is shared between sub-routines. + * Using a shared structure allows cplus_demangle to be reentrant. */ + +struct work_stuff +{ + int options; + char **typevec; + int ntypes; + int typevec_size; + int constructor; + int destructor; + int static_type; /* A static member function */ + int const_type; /* A const member function */ +}; + +#define PRINT_ANSI_QUALIFIERS (work -> options & DMGL_ANSI) +#define PRINT_ARG_TYPES (work -> options & DMGL_PARAMS) + +static const struct optable +{ + const char *in; + const char *out; + int flags; +} optable[] = { + "nw", " new", DMGL_ANSI, /* new (1.92, ansi) */ + "dl", " delete", DMGL_ANSI, /* new (1.92, ansi) */ + "new", " new", 0, /* old (1.91, and 1.x) */ + "delete", " delete", 0, /* old (1.91, and 1.x) */ + "as", "=", DMGL_ANSI, /* ansi */ + "ne", "!=", DMGL_ANSI, /* old, ansi */ + "eq", "==", DMGL_ANSI, /* old, ansi */ + "ge", ">=", DMGL_ANSI, /* old, ansi */ + "gt", ">", DMGL_ANSI, /* old, ansi */ + "le", "<=", DMGL_ANSI, /* old, ansi */ + "lt", "<", DMGL_ANSI, /* old, ansi */ + "plus", "+", 0, /* old */ + "pl", "+", DMGL_ANSI, /* ansi */ + "apl", "+=", DMGL_ANSI, /* ansi */ + "minus", "-", 0, /* old */ + "mi", "-", DMGL_ANSI, /* ansi */ + "ami", "-=", DMGL_ANSI, /* ansi */ + "mult", "*", 0, /* old */ + "ml", "*", DMGL_ANSI, /* ansi */ + "amu", "*=", DMGL_ANSI, /* ansi (ARM/Lucid) */ + "aml", "*=", DMGL_ANSI, /* ansi (GNU/g++) */ + "convert", "+", 0, /* old (unary +) */ + "negate", "-", 0, /* old (unary -) */ + "trunc_mod", "%", 0, /* old */ + "md", "%", DMGL_ANSI, /* ansi */ + "amd", "%=", DMGL_ANSI, /* ansi */ + "trunc_div", "/", 0, /* old */ + "dv", "/", DMGL_ANSI, /* ansi */ + "adv", "/=", DMGL_ANSI, /* ansi */ + "truth_andif", "&&", 0, /* old */ + "aa", "&&", DMGL_ANSI, /* ansi */ + "truth_orif", "||", 0, /* old */ + "oo", "||", DMGL_ANSI, /* ansi */ + "truth_not", "!", 0, /* old */ + "nt", "!", DMGL_ANSI, /* ansi */ + "postincrement","++", 0, /* old */ + "pp", "++", DMGL_ANSI, /* ansi */ + "postdecrement","--", 0, /* old */ + "mm", "--", DMGL_ANSI, /* ansi */ + "bit_ior", "|", 0, /* old */ + "or", "|", DMGL_ANSI, /* ansi */ + "aor", "|=", DMGL_ANSI, /* ansi */ + "bit_xor", "^", 0, /* old */ + "er", "^", DMGL_ANSI, /* ansi */ + "aer", "^=", DMGL_ANSI, /* ansi */ + "bit_and", "&", 0, /* old */ + "ad", "&", DMGL_ANSI, /* ansi */ + "aad", "&=", DMGL_ANSI, /* ansi */ + "bit_not", "~", 0, /* old */ + "co", "~", DMGL_ANSI, /* ansi */ + "call", "()", 0, /* old */ + "cl", "()", DMGL_ANSI, /* ansi */ + "alshift", "<<", 0, /* old */ + "ls", "<<", DMGL_ANSI, /* ansi */ + "als", "<<=", DMGL_ANSI, /* ansi */ + "arshift", ">>", 0, /* old */ + "rs", ">>", DMGL_ANSI, /* ansi */ + "ars", ">>=", DMGL_ANSI, /* ansi */ + "component", "->", 0, /* old */ + "pt", "->", DMGL_ANSI, /* ansi; Lucid C++ form */ + "rf", "->", DMGL_ANSI, /* ansi; ARM/GNU form */ + "indirect", "*", 0, /* old */ + "method_call", "->()", 0, /* old */ + "addr", "&", 0, /* old (unary &) */ + "array", "[]", 0, /* old */ + "vc", "[]", DMGL_ANSI, /* ansi */ + "compound", ", ", 0, /* old */ + "cm", ", ", DMGL_ANSI, /* ansi */ + "cond", "?:", 0, /* old */ + "cn", "?:", DMGL_ANSI, /* psuedo-ansi */ + "max", ">?", 0, /* old */ + "mx", ">?", DMGL_ANSI, /* psuedo-ansi */ + "min", "*", DMGL_ANSI, /* ansi */ +}; + + +typedef struct string /* Beware: these aren't required to be */ +{ /* '\0' terminated. */ + char *b; /* pointer to start of string */ + char *p; /* pointer after last character */ + char *e; /* pointer after end of allocated space */ +} string; + +#define STRING_EMPTY(str) ((str) -> b == (str) -> p) +#define PREPEND_BLANK(str) {if (!STRING_EMPTY(str)) \ + string_prepend(str, " ");} +#define APPEND_BLANK(str) {if (!STRING_EMPTY(str)) \ + string_append(str, " ");} + +#define ARM_VTABLE_STRING "__vtbl__" /* Lucid/cfront virtual table prefix */ +#define ARM_VTABLE_STRLEN 8 /* strlen (ARM_VTABLE_STRING) */ + +/* Prototypes for local functions */ + +static char * +mop_up PARAMS ((struct work_stuff *, string *, int)); + +#if 0 +static int +demangle_method_args PARAMS ((struct work_stuff *work, const char **, string *)); +#endif + +static int +demangle_template PARAMS ((struct work_stuff *work, const char **, string *)); + +static int +demangle_qualified PARAMS ((struct work_stuff *, const char **, string *, + int)); + +static int +demangle_class PARAMS ((struct work_stuff *, const char **, string *)); + +static int +demangle_fund_type PARAMS ((struct work_stuff *, const char **, string *)); + +static int +demangle_signature PARAMS ((struct work_stuff *, const char **, string *)); + +static int +demangle_prefix PARAMS ((struct work_stuff *, const char **, string *)); + +static int +gnu_special PARAMS ((struct work_stuff *, const char **, string *)); + +static int +cfront_special PARAMS ((struct work_stuff *, const char **, string *)); + +static void +string_need PARAMS ((string *, int)); + +static void +string_delete PARAMS ((string *)); + +static void +string_init PARAMS ((string *)); + +static void +string_clear PARAMS ((string *)); + +#if 0 +static int +string_empty PARAMS ((string *)); +#endif + +static void +string_append PARAMS ((string *, const char *)); + +static void +string_appends PARAMS ((string *, string *)); + +static void +string_appendn PARAMS ((string *, const char *, int)); + +static void +string_prepend PARAMS ((string *, const char *)); + +static void +string_prependn PARAMS ((string *, const char *, int)); + +static int +get_count PARAMS ((const char **, int *)); + +static int +consume_count PARAMS ((const char **)); + +static int +demangle_args PARAMS ((struct work_stuff *, const char **, string *)); + +static int +do_type PARAMS ((struct work_stuff *, const char **, string *)); + +static int +do_arg PARAMS ((struct work_stuff *, const char **, string *)); + +static void +demangle_function_name PARAMS ((struct work_stuff *, const char **, string *, + const char *)); + +static void +remember_type PARAMS ((struct work_stuff *, const char *, int)); + +static void +forget_types PARAMS ((struct work_stuff *)); + +#if 0 +static void +string_prepends PARAMS ((string *, string *)); +#endif + +/* Translate count to integer, consuming tokens in the process. + Conversion terminates on the first non-digit character. */ + +static int +consume_count (type) + const char **type; +{ + int count = 0; + + do + { + count *= 10; + count += **type - '0'; + (*type)++; + } while (isdigit (**type)); + return (count); +} + +/* Takes operator name as e.g. "++" and returns mangled + operator name (e.g. "postincrement_expr"), or NULL if not found. + + If OPTIONS & DMGL_ANSI == 1, return the ANSI name; + if OPTIONS & DMGL_ANSI == 0, return the old GNU name. */ + +char * +cplus_mangle_opname (opname, options) + char *opname; + int options; +{ + int i; + int len; + + len = strlen (opname); + for (i = 0; i < sizeof (optable) / sizeof (optable[0]); i++) + { + if (strlen (optable[i].out) == len + && (options & DMGL_ANSI) == (optable[i].flags & DMGL_ANSI) + && memcmp (optable[i].out, opname, len) == 0) + return ((char *)optable[i].in); + } + return (0); +} + +/* char *cplus_demangle (const char *name, int options) + + If NAME is a mangled function name produced by GNU C++, then + a pointer to a malloced string giving a C++ representation + of the name will be returned; otherwise NULL will be returned. + It is the caller's responsibility to free the string which + is returned. + + The OPTIONS arg may contain one or more of the following bits: + + DMGL_ANSI ANSI qualifiers such as `const' and `void' are + included. + DMGL_PARAMS Function parameters are included. + + For example, + + cplus_demangle ("foo__1Ai", DMGL_PARAMS) => "A::foo(int)" + cplus_demangle ("foo__1Ai", DMGL_PARAMS | DMGL_ANSI) => "A::foo(int)" + cplus_demangle ("foo__1Ai", 0) => "A::foo" + + cplus_demangle ("foo__1Afe", DMGL_PARAMS) => "A::foo(float,...)" + cplus_demangle ("foo__1Afe", DMGL_PARAMS | DMGL_ANSI)=> "A::foo(float,...)" + cplus_demangle ("foo__1Afe", 0) => "A::foo" + + Note that any leading underscores, or other such characters prepended by + the compilation system, are presumed to have already been stripped from + TYPE. */ + +char * +cplus_demangle (mangled, options) + const char *mangled; + int options; +{ + string decl; + int success = 0; + struct work_stuff work[1]; + char *demangled = NULL; + + if ((mangled != NULL) && (*mangled != '\0')) + { + memset ((char *) work, 0, sizeof (work)); + work -> options = options; + + string_init (&decl); + + /* First check to see if gnu style demangling is active and if the + string to be demangled contains a CPLUS_MARKER. If so, attempt to + recognize one of the gnu special forms rather than looking for a + standard prefix. In particular, don't worry about whether there + is a "__" string in the mangled string. Consider "_$_5__foo" for + example. */ + + if ((AUTO_DEMANGLING || GNU_DEMANGLING) + && (strpbrk (mangled, cplus_markers)) != NULL) + { + success = gnu_special (work, &mangled, &decl); + } + else + { + success = demangle_prefix (work, &mangled, &decl); + } + if (success && (*mangled != '\0')) + { + success = demangle_signature (work, &mangled, &decl); + } + demangled = mop_up (work, &decl, success); + } + return (demangled); +} + +static char * +mop_up (work, declp, success) + struct work_stuff *work; + string *declp; + int success; +{ + int i; + char *demangled = NULL; + + /* Discard the remembered types, if any. */ + + forget_types (work); + if (work -> typevec != NULL) + { + free ((char *) work -> typevec); + } + + /* If demangling was successful, ensure that the demangled string is null + terminated and return it. Otherwise, free the demangling decl. */ + + if (!success) + { + string_delete (declp); + } + else + { + string_appendn (declp, "", 1); + demangled = declp -> b; + } + return (demangled); +} + +/* + +LOCAL FUNCTION + + demangle_signature -- demangle the signature part of a mangled name + +SYNOPSIS + + static int + demangle_signature (struct work_stuff *work, const char **mangled, + string *declp); + +DESCRIPTION + + Consume and demangle the signature portion of the mangled name. + + DECLP is the string where demangled output is being built. At + entry it contains the demangled root name from the mangled name + prefix. I.E. either a demangled operator name or the root function + name. In some special cases, it may contain nothing. + + *MANGLED points to the current unconsumed location in the mangled + name. As tokens are consumed and demangling is performed, the + pointer is updated to continuously point at the next token to + be consumed. + + Demangling GNU style mangled names is nasty because there is no + explicit token that marks the start of the outermost function + argument list. +*/ + +static int +demangle_signature (work, mangled, declp) + struct work_stuff *work; + const char **mangled; + string *declp; +{ + int success = 1; + int func_done = 0; + int expect_func = 0; + const char *oldmangled; + + while (success && (**mangled != '\0')) + { + switch (**mangled) + { + case 'Q': + oldmangled = *mangled; + success = demangle_qualified (work, mangled, declp, 1); + if (success) + { + remember_type (work, oldmangled, *mangled - oldmangled); + } + if (AUTO_DEMANGLING || GNU_DEMANGLING) + { + expect_func = 1; + } + break; + + case 'S': + /* Static member function */ + (*mangled)++; + work -> static_type = 1; + break; + + case 'C': + /* a const member function */ + (*mangled)++; + work -> const_type = 1; + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + oldmangled = *mangled; + success = demangle_class (work, mangled, declp); + if (success) + { + remember_type (work, oldmangled, *mangled - oldmangled); + } + if (AUTO_DEMANGLING || GNU_DEMANGLING) + { + expect_func = 1; + } + break; + + case 'F': + /* Function */ + /* ARM style demangling includes a specific 'F' character after + the class name. For GNU style, it is just implied. So we can + safely just consume any 'F' at this point and be compatible + with either style. */ + + func_done = 1; + (*mangled)++; + + /* For lucid/cfront style we have to forget any types we might + have remembered up to this point, since they were not argument + types. GNU style considers all types seen as available for + back references. See comment in demangle_args() */ + + if (LUCID_DEMANGLING || CFRONT_DEMANGLING) + { + forget_types (work); + } + success = demangle_args (work, mangled, declp); + break; + + case 't': + /* Template */ + success = demangle_template (work, mangled, declp); + break; + + case '_': + /* At the outermost level, we cannot have a return type specified, + so if we run into another '_' at this point we are dealing with + a mangled name that is either bogus, or has been mangled by + some algorithm we don't know how to deal with. So just + reject the entire demangling. */ + success = 0; + break; + + default: + if (AUTO_DEMANGLING || GNU_DEMANGLING) + { + /* Assume we have stumbled onto the first outermost function + argument token, and start processing args. */ + func_done = 1; + success = demangle_args (work, mangled, declp); + } + else + { + /* Non-GNU demanglers use a specific token to mark the start + of the outermost function argument tokens. Typically 'F', + for ARM-demangling, for example. So if we find something + we are not prepared for, it must be an error. */ + success = 0; + } + break; + } + if (AUTO_DEMANGLING || GNU_DEMANGLING) + { + if (success && expect_func) + { + func_done = 1; + success = demangle_args (work, mangled, declp); + } + } + } + if (success && !func_done) + { + if (AUTO_DEMANGLING || GNU_DEMANGLING) + { + /* With GNU style demangling, bar__3foo is 'foo::bar(void)', and + bar__3fooi is 'foo::bar(int)'. We get here when we find the + first case, and need to ensure that the '(void)' gets added to + the current declp. Note that with ARM, the first case + represents the name of a static data member 'foo::bar', + which is in the current declp, so we leave it alone. */ + success = demangle_args (work, mangled, declp); + } + } + if (success && work -> static_type && PRINT_ARG_TYPES) + { + string_append (declp, " static"); + } + if (success && work -> const_type && PRINT_ARG_TYPES) + { + string_append (declp, " const"); + } + return (success); +} + +#if 0 + +static int +demangle_method_args (work, mangled, declp) + struct work_stuff *work; + const char **mangled; + string *declp; +{ + int success = 0; + + if (work -> static_type) + { + string_append (declp, *mangled + 1); + *mangled += strlen (*mangled); + success = 1; + } + else + { + success = demangle_args (work, mangled, declp); + } + return (success); +} + +#endif + +static int +demangle_template (work, mangled, declp) + struct work_stuff *work; + const char **mangled; + string *declp; +{ + int i; + string tname; + string trawname; + int is_pointer; + int is_real; + int is_integral; + int r; + int need_comma = 0; + int success = 0; + int done; + const char *old_p; + int symbol_len; + string temp; + + (*mangled)++; + string_init (&tname); + string_init (&trawname); + /* get template name */ + if (!get_count (mangled, &r)) + { + return (0); + } + string_appendn (&tname, *mangled, r); + string_appendn (&trawname, *mangled, r); + string_appendn (&trawname, "", 1); + *mangled += r; + string_append (&tname, "<"); + /* get size of template parameter list */ + if (!get_count (mangled, &r)) + { + return (0); + } + for (i = 0; i < r; i++) + { + if (need_comma) + { + string_append (&tname, ", "); + } + /* Z for type parameters */ + if (**mangled == 'Z') + { + (*mangled)++; + success = do_type (work, mangled, &temp); + string_appendn (&temp, "", 1); + if (success) + { + string_append (&tname, temp.b); + } + string_delete(&temp); + if (!success) + { + break; + } + } + else + { + /* otherwise, value parameter */ + old_p = *mangled; + is_pointer = 0; + is_real = 0; + is_integral = 0; + done = 0; + success = do_type (work, mangled, &temp); + string_appendn (&temp, "", 1); + if (success) + { + string_append (&tname, temp.b); + } + string_delete(&temp); + if (!success) + { + break; + } + string_append (&tname, "="); + while (*old_p && !done) + { + switch (*old_p) + { + case 'P': + case 'R': + done = is_pointer = 1; + break; + case 'C': /* const */ + case 'S': /* explicitly signed [char] */ + case 'U': /* unsigned */ + case 'V': /* volatile */ + case 'F': /* function */ + case 'M': /* member function */ + case 'O': /* ??? */ + old_p++; + continue; + case 'Q': /* repetition of following */ + case 'T': /* remembered type */ + abort (); + break; + case 'v': /* void */ + abort (); + break; + case 'x': /* long long */ + case 'l': /* long */ + case 'i': /* int */ + case 's': /* short */ + case 'c': /* char */ + done = is_integral = 1; + break; + case 'r': /* long double */ + case 'd': /* double */ + case 'f': /* float */ + done = is_real = 1; + break; + default: + abort (); + } + } + if (is_integral) + { + if (**mangled == 'm') + { + string_appendn (&tname, "-", 1); + (*mangled)++; + } + while (isdigit (**mangled)) + { + string_appendn (&tname, *mangled, 1); + (*mangled)++; + } + } + else if (is_real) + { + if (**mangled == 'm') + { + string_appendn (&tname, "-", 1); + (*mangled)++; + } + while (isdigit (**mangled)) + { + string_appendn (&tname, *mangled, 1); + (*mangled)++; + } + if (**mangled == '.') /* fraction */ + { + string_appendn (&tname, ".", 1); + (*mangled)++; + while (isdigit (**mangled)) + { + string_appendn (&tname, *mangled, 1); + (*mangled)++; + } + } + if (**mangled == 'e') /* exponent */ + { + string_appendn (&tname, "e", 1); + (*mangled)++; + while (isdigit (**mangled)) + { + string_appendn (&tname, *mangled, 1); + (*mangled)++; + } + } + } + else if (is_pointer) + { + if (!get_count (mangled, &symbol_len)) + { + success = 0; + break; + } + string_appendn (&tname, *mangled, symbol_len); + *mangled += symbol_len; + } + } + need_comma = 1; + } + string_append (&tname, ">::"); + if (work -> destructor) + { + string_append (&tname, "~"); + } + if (work -> constructor || work -> destructor) + { + string_append (&tname, trawname.b); + } + string_delete(&trawname); + + if (!success) + { + string_delete(&tname); + } + else + { + string_prepend (declp, tname.b); + string_delete (&tname); + + if (work -> static_type) + { + string_append (declp, *mangled + 1); + *mangled += strlen (*mangled); + success = 1; + } + else + { + success = demangle_args (work, mangled, declp); + } + } + return (success); +} + +/* - char *cplus_demangle (const char *name, int mode) - - If NAME is a mangled function name produced by GNU C++, then - a pointer to a malloced string giving a C++ representation - of the name will be returned; otherwise NULL will be returned. - It is the caller's responsibility to free the string which - is returned. +LOCAL FUNCTION - If MODE > 0, then ANSI qualifiers such as `const' and `void' are output. - Otherwise they are not. - If MODE >= 0, parameters are emitted; otherwise not. + demangle_class -- demangle a mangled class sequence - For example, - - cplus_demangle ("foo__1Ai", 0) => "A::foo(int)" - cplus_demangle ("foo__1Ai", 1) => "A::foo(int)" - cplus_demangle ("foo__1Ai", -1) => "A::foo" +SYNOPSIS - cplus_demangle ("foo__1Afe", 0) => "A::foo(float,...)" - cplus_demangle ("foo__1Afe", 1) => "A::foo(float,...)" - cplus_demangle ("foo__1Afe", -1) => "A::foo" + static int + demangle_class (struct work_stuff *work, const char **mangled, + strint *declp) - This file imports xmalloc and xrealloc, which are like malloc and - realloc except that they generate a fatal error if there is no - available memory. */ +DESCRIPTION -/* define this if names don't start with _ */ -/* #define nounderscore 1 */ + DECLP points to the buffer into which demangling is being done. -/* This is '$' on systems where the assembler can deal with that. - Where the assembler can't, it's '.' (but on many systems '.' is - used for other things). */ -#if !defined (CPLUS_MARKER) -#define CPLUS_MARKER '$' -#endif + *MANGLED points to the current token to be demangled. On input, + it points to a mangled class (I.E. "3foo", "13verylongclass", etc.) + On exit, it points to the next token after the mangled class on + success, or the first unconsumed token on failure. -#include -#include + If the CONSTRUCTOR or DESTRUCTOR flags are set in WORK, then + we are demangling a constructor or destructor. In this case + we prepend "class::class" or "class::~class" to DECLP. -#ifdef USG -#include -#include -#else -#include -#define memcpy(s1, s2, n) bcopy ((s2), (s1), (n)) -#define memcmp(s1, s2, n) bcmp ((s2), (s1), (n)) -#define strchr index -#define strrchr rindex -#endif + Otherwise, we prepend "class::" to the current DECLP. -#ifndef __STDC__ -#define const -#endif + Reset the constructor/destructor flags once they have been + "consumed". This allows demangle_class to be called later during + the same demangling, to do normal class demangling. -#ifdef __STDC__ -extern char *cplus_demangle (const char *type, int mode); -#else -extern char *cplus_demangle (); -#endif + Returns 1 if demangling is successful, 0 otherwise. -#ifdef __STDC__ -extern char *xmalloc (int); -extern char *xrealloc (char *, int); -extern void free (char *); -#else -extern char *xmalloc (); -extern char *xrealloc (); -extern void free (); -#endif +*/ -static char **typevec = 0; -static int ntypes = 0; -static int typevec_size = 0; +static int +demangle_class (work, mangled, declp) + struct work_stuff *work; + const char **mangled; + string *declp; +{ + int n; + int success = 0; -static struct { - const char *in; - const char *out; -} optable[] = { - "new", " new", - "delete", " delete", - "ne", "!=", - "eq", "==", - "ge", ">=", - "gt", ">", - "le", "<=", - "lt", "<", - "plus", "+", - "minus", "-", - "mult", "*", - "convert", "+", /* unary + */ - "negate", "-", /* unary - */ - "trunc_mod", "%", - "trunc_div", "/", - "truth_andif", "&&", - "truth_orif", "||", - "truth_not", "!", - "postincrement", "++", - "postdecrement", "--", - "bit_ior", "|", - "bit_xor", "^", - "bit_and", "&", - "bit_not", "~", - "call", "()", - "cond", "?:", - "alshift", "<<", - "arshift", ">>", - "component", "->", - "indirect", "*", - "method_call", "->()", - "addr", "&", /* unary & */ - "array", "[]", - "nop", "", /* for operator= */ -}; + n = consume_count (mangled); + if (strlen (*mangled) >= n) + { + if (work -> constructor || work -> destructor) + { + string_prependn (declp, *mangled, n); + if (work -> destructor) + { + string_prepend (declp, "~"); + } + work -> constructor = work -> destructor = 0; + } + string_prepend (declp, "::"); + string_prependn (declp, *mangled, n); + *mangled += n; + success = 1; + } + return (success); +} -/* Beware: these aren't '\0' terminated. */ +/* -typedef struct { - char *b; /* pointer to start of string */ - char *p; /* pointer after last character */ - char *e; /* pointer after end of allocated space */ -} string; +LOCAL FUNCTION -#ifdef __STDC__ -static void string_need (string *s, int n); -static void string_delete (string *s); -static void string_init (string *s); -static void string_clear (string *s); -static int string_empty (string *s); -static void string_append (string *p, const char *s); -static void string_appends (string *p, string *s); -static void string_appendn (string *p, const char *s, int n); -static void string_prepend (string *p, const char *s); -#if 0 -static void string_prepends (string *p, string *s); -#endif -static void string_prependn (string *p, const char *s, int n); -static int get_count (const char **type, int *count); -static int do_args (const char **type, string *decl, int arg_mode); -static int do_type (const char **type, string *result, int arg_mode); -static int do_arg (const char **type, string *result, int arg_mode); -static void munge_function_name (string *name, int arg_mode); -static void remember_type (const char *type, int len); -#else -static void string_need (); -static void string_delete (); -static void string_init (); -static void string_clear (); -static int string_empty (); -static void string_append (); -static void string_appends (); -static void string_appendn (); -static void string_prepend (); -#if 0 -static void string_prepends (); -#endif -static void string_prependn (); -static int get_count (); -static int do_args (); -static int do_type (); -static int do_arg (); -static int do_args (); -static void munge_function_name (); -static void remember_type (); -#endif + demangle_prefix -- consume the mangled name prefix and find signature -char * -cplus_demangle (type, arg_mode) - const char *type; - int arg_mode; +SYNOPSIS + + static int + demangle_prefix (struct work_stuff *work, const char **mangled, + string *declp); + +DESCRIPTION + + Consume and demangle the prefix of the mangled name. + + DECLP points to the string buffer into which demangled output is + placed. On entry, the buffer is empty. On exit it contains + the root function name, the demangled operator name, or in some + special cases either nothing or the completely demangled result. + + MANGLED points to the current pointer into the mangled name. As each + token of the mangled name is consumed, it is updated. Upon entry + the current mangled name pointer points to the first character of + the mangled name. Upon exit, it should point to the first character + of the signature if demangling was successful, or to the first + unconsumed character if demangling of the prefix was unsuccessful. + + Returns 1 on success, 0 otherwise. + */ + +static int +demangle_prefix (work, mangled, declp) + struct work_stuff *work; + const char **mangled; + string *declp; { - string decl; - int n; - int success = 0; - int constructor = 0; - int const_flag = 0; + int success = 1; + const char *scan; int i; - const char *p; -#ifndef LONGERNAMES - const char *premangle; -#endif -# define print_ansi_qualifiers (arg_mode > 0) -# define print_arg_types (arg_mode >= 0) + scan = strstr (*mangled, "__"); - if (type == NULL || *type == '\0') - return NULL; -#ifndef nounderscore - if (*type++ != '_') - return NULL; -#endif - p = type; - while (*p != '\0' && !(*p == '_' && p[1] == '_')) - p++; - if (*p == '\0') + if (scan != NULL) { - /* destructor */ - if (type[0] == '_' && type[1] == CPLUS_MARKER && type[2] == '_') + /* We found a sequence of two or more '_', ensure that we start at + the last pair in the sequence. */ + i = strspn (scan, "_"); + if (i > 2) { - int n = (strlen (type) - 3)*2 + 3 + 2 + 1; - char *tem = (char *) xmalloc (n); - strcpy (tem, type + 3); - strcat (tem, "::~"); - strcat (tem, type + 3); - strcat (tem, "()"); - return tem; + scan += (i - 2); } - /* static data member */ - if (*type != '_' && (p = strchr (type, CPLUS_MARKER)) != NULL) + } + + if (scan == NULL) + { + success = 0; + } + else if (work -> static_type) + { + if (!isdigit (scan[0]) && (scan[0] != 't')) { - int n = strlen (type) + 2; - char *tem = (char *) xmalloc (n); - memcpy (tem, type, p - type); - strcpy (tem + (p - type), "::"); - strcpy (tem + (p - type) + 2, p + 1); - return tem; + success = 0; } - /* virtual table "_vt$" */ - if (type[0] == '_' && type[1] == 'v' && type[2] == 't' && type[3] == CPLUS_MARKER) + } + else if ((scan == *mangled) && (isdigit (scan[2]) || (scan[2] == 'Q'))) + { + /* A GNU style constructor starts with "__" or "__Q". */ + work -> constructor = 1; + *mangled = scan + 2; + } + else if ((scan == *mangled) && !isdigit (scan[2]) && (scan[2] != 't')) + { + /* Mangled name starts with "__". Skip over any leading '_' characters, + then find the next "__" that separates the prefix from the signature. + */ + if (!(CFRONT_DEMANGLING || LUCID_DEMANGLING) + || (cfront_special (work, mangled, declp) == 0)) { - int n = strlen (type + 4) + 14 + 1; - char *tem = (char *) xmalloc (n); - strcpy (tem, type + 4); - strcat (tem, " virtual table"); - return tem; + while (*scan == '_') + { + scan++; + } + if ((scan = strstr (scan, "__")) == NULL || (*(scan + 2) == '\0')) + { + /* No separator (I.E. "__not_mangled"), or empty signature + (I.E. "__not_mangled_either__") */ + success = 0; + } + else + { + demangle_function_name (work, mangled, declp, scan); + } } - return NULL; } + else if (*(scan + 2) != '\0') + { + /* Mangled name does not start with "__" but does have one somewhere + in there with non empty stuff after it. Looks like a global + function name. */ + demangle_function_name (work, mangled, declp, scan); + } + else + { + /* Doesn't look like a mangled name */ + success = 0; + } + return (success); +} - string_init (&decl); +/* + +LOCAL FUNCTION + + gnu_special -- special handling of gnu mangled strings + +SYNOPSIS + + static int + gnu_special (struct work_stuff *work, const char **mangled, + string *declp); + + +DESCRIPTION + + Process some special GNU style mangling forms that don't fit + the normal pattern. For example: + + _$_3foo (destructor for class foo) + _vt$foo (foo virtual table) + _vt$foo$bar (foo::bar virtual table) + _3foo$varname (static data member) + */ + +static int +gnu_special (work, mangled, declp) + struct work_stuff *work; + const char **mangled; + string *declp; +{ + int n; + int success = 1; + const char *p; - if (p == type) + if ((*mangled)[0] == '_' + && strchr (cplus_markers, (*mangled)[1]) != NULL + && (*mangled)[2] == '_') { - if (!isdigit (p[2])) + /* Found a GNU style destructor, get past "__" */ + (*mangled) += 3; + work -> destructor = 1; + } + else if ((*mangled)[0] == '_' + && (*mangled)[1] == 'v' + && (*mangled)[2] == 't' + && strchr (cplus_markers, (*mangled)[3]) != NULL) + { + /* Found a GNU style virtual table, get past "_vt" + and create the decl. Note that we consume the entire mangled + input string, which means that demangle_signature has no work + to do. */ + (*mangled) += 4; + while (**mangled != '\0') { - string_delete (&decl); - return NULL; + n = strcspn (*mangled, cplus_markers); + string_appendn (declp, *mangled, n); + (*mangled) += n; + if (**mangled != '\0') + { + string_append (declp, "::"); + (*mangled)++; + } } - constructor = 1; + string_append (declp, " virtual table"); + } + else if ((*mangled)[0] == '_' + && isdigit ((*mangled)[1]) + && (p = strpbrk (*mangled, cplus_markers)) != NULL) + { + /* static data member, "_3foo$varname" for example */ + (*mangled)++; + p++; + n = consume_count (mangled); + string_appendn (declp, *mangled, n); + string_append (declp, "::"); + n = strlen (p); + string_appendn (declp, p, n); + (*mangled) = p + n; } else { - string_appendn (&decl, type, p - type); - munge_function_name (&decl, arg_mode); + success = 0; } - p += 2; + return (success); +} -#ifndef LONGERNAMES - premangle = p; -#endif - switch (*p) - { - case 'C': - /* a const member function */ - if (!isdigit (p[1])) - { - string_delete (&decl); - return NULL; - } - p += 1; - const_flag = 1; - /* fall through */ - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - n = 0; - do - { - n *= 10; - n += *p - '0'; - p += 1; - } - while (isdigit (*p)); - if (strlen (p) < n) - { - string_delete (&decl); - return NULL; - } - if (constructor) - { - string_appendn (&decl, p, n); - string_append (&decl, "::"); - string_appendn (&decl, p, n); - } - else +/* + +LOCAL FUNCTION + + cfront_special -- special handling of cfront/lucid mangled strings + +SYNOPSIS + + static int + cfront_special (struct work_stuff *work, const char **mangled, + string *declp); + + +DESCRIPTION + + Process some special cfront style mangling forms that don't fit + the normal pattern. For example: + + __vtbl__3foo (foo virtual table) + __vtbl__3foo__3bar (bar::foo virtual table) + + */ + +static int +cfront_special (work, mangled, declp) + struct work_stuff *work; + const char **mangled; + string *declp; +{ + int n; + int i; + int success = 1; + const char *p; + + if (strncmp (*mangled, ARM_VTABLE_STRING, ARM_VTABLE_STRLEN) == 0) + { + /* Found a cfront style virtual table, get past ARM_VTABLE_STRING + and create the decl. Note that we consume the entire mangled + input string, which means that demangle_signature has no work + to do. */ + (*mangled) += ARM_VTABLE_STRLEN; + while (**mangled != '\0') { - string_prepend (&decl, "::"); - string_prependn (&decl, p, n); + n = consume_count (mangled); + string_prependn (declp, *mangled, n); + (*mangled) += n; + if ((*mangled)[0] == '_' && (*mangled)[1] == '_') + { + string_prepend (declp, "::"); + (*mangled) += 2; + } } - p += n; -#ifndef LONGERNAMES - remember_type (premangle, p - premangle); -#endif - success = do_args (&p, &decl, arg_mode); - if (const_flag && print_arg_types) - string_append (&decl, " const"); - break; - case 'F': - p += 1; - success = do_args (&p, &decl, arg_mode); - break; + string_append (declp, " virtual table"); } - - for (i = 0; i < ntypes; i++) - if (typevec[i] != NULL) - free (typevec[i]); - ntypes = 0; - if (typevec != NULL) + else { - free ((char *)typevec); - typevec = NULL; - typevec_size = 0; + success = 0; } + return (success); +} - if (success) - { - string_appendn (&decl, "", 1); - return decl.b; - } - else +/* + +LOCAL FUNCTION + + demangle_qualified -- demangle 'Q' qualified name strings + +SYNOPSIS + + static int + demangle_qualified (struct work_stuff *, const char *mangled, + string *result, int isfuncname); + +DESCRIPTION + + Demangle a qualified name, such as "Q25Outer5Inner" which is + the mangled form of "Outer::Inner". The demangled output is + appended to the result string. + + If isfuncname is nonzero, then the qualified name we are building + is going to be used as a member function name, so if it is a + constructor or destructor function, append an appropriate + constructor or destructor name. I.E. for the above example, + the result for use as a constructor is "Outer::Inner::Inner" + and the result for use as a destructor is "Outer::Inner::~Inner". + +BUGS + + Numeric conversion is ASCII dependent (FIXME). + + */ + +static int +demangle_qualified (work, mangled, result, isfuncname) + struct work_stuff *work; + const char **mangled; + string *result; + int isfuncname; +{ + int qualifiers; + int namelength; + int success = 0; + + qualifiers = (*mangled)[1] - '0'; + if (qualifiers > 0 && qualifiers < 10) { - string_delete (&decl); - return NULL; + /* Assume success until we discover otherwise. Skip over the 'Q', the + qualifier count, and any '_' between the qualifier count and the + first name (cfront qualified names). */ + + success = 1; + if ((*mangled)[2] == '_') + { + (*mangled)++; + } + (*mangled) += 2; + + + /* Pick off the names and append them to the result string as they + are found, separated by '::'. */ + + while (qualifiers-- > 0) + { + namelength = consume_count (mangled); + if (strlen (*mangled) < namelength) + { + /* Simple sanity check failed */ + success = 0; + break; + } + string_appendn (result, *mangled, namelength); + if (qualifiers > 0) + { + string_appendn (result, "::", 2); + } + *mangled += namelength; + } + + /* If we are using the result as a function name, we need to append + the appropriate '::' separated constructor or destructor name. + We do this here because this is the most convenient place, where + we already have a pointer to the name and the length of the name. */ + + if (isfuncname && (work -> constructor || work -> destructor)) + { + string_appendn (result, "::", 2); + if (work -> destructor) + { + string_append (result, "~"); + } + string_appendn (result, (*mangled) - namelength, namelength); + } } + return (success); } +/* + +LOCAL FUNCTION + + get_count -- convert an ascii count to integer, consuming tokens + +SYNOPSIS + + static int + get_count (const char **type, int *count) + +DESCRIPTION + + Return 0 if no conversion is performed, 1 if a string is converted. +*/ + static int get_count (type, count) const char **type; int *count; { + const char *p; + int n; + if (!isdigit (**type)) - return 0; - *count = **type - '0'; - *type += 1; - /* see flush_repeats in cplus-method.c */ - if (isdigit (**type)) { - const char *p = *type; - int n = *count; - do - { - n *= 10; - n += *p - '0'; - p += 1; - } - while (isdigit (*p)); - if (*p == '_') + return (0); + } + else + { + *count = **type - '0'; + (*type)++; + if (isdigit (**type)) { - *type = p + 1; - *count = n; + p = *type; + n = *count; + do + { + n *= 10; + n += *p - '0'; + p++; + } + while (isdigit (*p)); + if (*p == '_') + { + *type = p + 1; + *count = n; + } } } - return 1; + return (1); } /* result will be initialised here; it will be freed on failure */ static int -do_type (type, result, arg_mode) - const char **type; +do_type (work, mangled, result) + struct work_stuff *work; + const char **mangled; string *result; - int arg_mode; { int n; int done; - int non_empty = 0; int success; string decl; const char *remembered_type; + int constp; + int volatilep; string_init (&decl); string_init (result); @@ -408,248 +1322,295 @@ do_type (type, result, arg_mode) while (success && !done) { int member; - switch (**type) + switch (**mangled) { + + /* A pointer type */ case 'P': - *type += 1; + (*mangled)++; string_prepend (&decl, "*"); break; + /* A reference type */ case 'R': - *type += 1; + (*mangled)++; string_prepend (&decl, "&"); break; + /* A back reference to a previously seen type */ case 'T': - *type += 1; - if (!get_count (type, &n) || n >= ntypes) - success = 0; + (*mangled)++; + if (!get_count (mangled, &n) || n >= work -> ntypes) + { + success = 0; + } else { - remembered_type = typevec[n]; - type = &remembered_type; + remembered_type = work -> typevec[n]; + mangled = &remembered_type; } break; + /* A function */ case 'F': - *type += 1; - if (!string_empty (&decl) && decl.b[0] == '*') + (*mangled)++; + if (!STRING_EMPTY (&decl) && decl.b[0] == '*') { string_prepend (&decl, "("); string_append (&decl, ")"); } - if (!do_args (type, &decl, arg_mode) || **type != '_') - success = 0; - else - *type += 1; + /* After picking off the function args, we expect to either find the + function return type (preceded by an '_') or the end of the + string. */ + if (!demangle_args (work, mangled, &decl) + || (**mangled != '_' && **mangled != '\0')) + { + success = 0; + } + if (success && (**mangled == '_')) + { + (*mangled)++; + } break; case 'M': case 'O': { - int constp = 0; - int volatilep = 0; + constp = 0; + volatilep = 0; - member = **type == 'M'; - *type += 1; - if (!isdigit (**type)) + member = **mangled == 'M'; + (*mangled)++; + if (!isdigit (**mangled)) { success = 0; break; } - n = 0; - do - { - n *= 10; - n += **type - '0'; - *type += 1; - } - while (isdigit (**type)); - if (strlen (*type) < n) + n = consume_count (mangled); + if (strlen (*mangled) < n) { success = 0; break; } string_append (&decl, ")"); string_prepend (&decl, "::"); - string_prependn (&decl, *type, n); + string_prependn (&decl, *mangled, n); string_prepend (&decl, "("); - *type += n; + *mangled += n; if (member) { - if (**type == 'C') + if (**mangled == 'C') { - *type += 1; + (*mangled)++; constp = 1; } - if (**type == 'V') + if (**mangled == 'V') { - *type += 1; + (*mangled)++; volatilep = 1; } - if (*(*type)++ != 'F') + if (*(*mangled)++ != 'F') { success = 0; break; } } - if ((member && !do_args (type, &decl, arg_mode)) || **type != '_') + if ((member && !demangle_args (work, mangled, &decl)) + || **mangled != '_') { success = 0; break; } - *type += 1; - if (! print_ansi_qualifiers) - break; + (*mangled)++; + if (! PRINT_ANSI_QUALIFIERS) + { + break; + } if (constp) { - if (non_empty) - string_append (&decl, " "); - else - non_empty = 1; - string_append (&decl, "const"); + APPEND_BLANK (&decl); + string_append (&decl, "const"); + } + if (volatilep) + { + APPEND_BLANK (&decl); + string_append (&decl, "volatile"); + } + break; + } + + case 'C': + if ((*mangled)[1] == 'P') + { + (*mangled)++; + if (PRINT_ANSI_QUALIFIERS) + { + if (!STRING_EMPTY (&decl)) + { + string_prepend (&decl, " "); + } + string_prepend (&decl, "const"); + } + break; + } + + /* fall through */ + default: + done = 1; + break; + } + } + + switch (**mangled) + { + /* A qualified name, such as "Outer::Inner". */ + case 'Q': + success = demangle_qualified (work, mangled, result, 0); + break; + + default: + success = demangle_fund_type (work, mangled, result); + break; + } + + if (success) + { + if (!STRING_EMPTY (&decl)) + { + string_append (result, " "); + string_appends (result, &decl); + } + } + else + { + string_delete (result); + } + string_delete (&decl); + return (success); +} + +/* Given a pointer to a type string that represents a fundamental type + argument (int, long, unsigned int, etc) in TYPE, a pointer to the + string in which the demangled output is being built in RESULT, and + the WORK structure, decode the types and add them to the result. + + For example: + + "Ci" => "const int" + "Sl" => "signed long" + "CUs" => "const unsigned short" + + */ + +static int +demangle_fund_type (work, mangled, result) + struct work_stuff *work; + const char **mangled; + string *result; +{ + int done = 0; + int success = 1; + int n; + + /* First pick off any type qualifiers. There can be more than one. */ + + while (!done) + { + switch (**mangled) + { + case 'C': + (*mangled)++; + if (PRINT_ANSI_QUALIFIERS) + { + APPEND_BLANK (result); + string_append (result, "const"); } - if (volatilep) + break; + case 'U': + (*mangled)++; + APPEND_BLANK (result); + string_append (result, "unsigned"); + break; + case 'S': /* signed char only */ + (*mangled)++; + APPEND_BLANK (result); + string_append (result, "signed"); + break; + case 'V': + (*mangled)++; + if (PRINT_ANSI_QUALIFIERS) { - if (non_empty) - string_append (&decl, " "); - else - non_empty = 1; - string_append (&decl, "volatile"); + APPEND_BLANK (result); + string_append (result, "volatile"); } break; - } - - case 'C': - if ((*type)[1] == 'P') - { - *type += 1; - if (print_ansi_qualifiers) - { - if (!string_empty (&decl)) - string_prepend (&decl, " "); - string_prepend (&decl, "const"); - } - break; - } - - /* fall through */ - default: - done = 1; - break; + default: + done = 1; + break; } } - done = 0; - non_empty = 0; - while (success && !done) - { - switch (**type) - { - case 'C': - *type += 1; - if (print_ansi_qualifiers) - { - if (non_empty) - string_append (result, " "); - else - non_empty = 1; - string_append (result, "const"); - } - break; - case 'U': - *type += 1; - if (non_empty) - string_append (result, " "); - else - non_empty = 1; - string_append (result, "unsigned"); - break; - case 'V': - *type += 1; - if (print_ansi_qualifiers) - { - if (non_empty) - string_append (result, " "); - else - non_empty = 1; - string_append (result, "volatile"); - } - break; - default: - done = 1; - break; - } - } + /* Now pick off the fundamental type. There can be only one. */ - if (success) - switch (**type) - { + switch (**mangled) + { case '\0': case '_': break; case 'v': - *type += 1; - if (non_empty) - string_append (result, " "); + (*mangled)++; + APPEND_BLANK (result); string_append (result, "void"); break; case 'x': - *type += 1; - if (non_empty) - string_append (result, " "); + (*mangled)++; + APPEND_BLANK (result); string_append (result, "long long"); break; case 'l': - *type += 1; - if (non_empty) - string_append (result, " "); + (*mangled)++; + APPEND_BLANK (result); string_append (result, "long"); break; case 'i': - *type += 1; - if (non_empty) - string_append (result, " "); + (*mangled)++; + APPEND_BLANK (result); string_append (result, "int"); break; case 's': - *type += 1; - if (non_empty) - string_append (result, " "); + (*mangled)++; + APPEND_BLANK (result); string_append (result, "short"); break; case 'c': - *type += 1; - if (non_empty) - string_append (result, " "); + (*mangled)++; + APPEND_BLANK (result); string_append (result, "char"); break; case 'r': - *type += 1; - if (non_empty) - string_append (result, " "); + (*mangled)++; + APPEND_BLANK (result); string_append (result, "long double"); break; case 'd': - *type += 1; - if (non_empty) - string_append (result, " "); + (*mangled)++; + APPEND_BLANK (result); string_append (result, "double"); break; case 'f': - *type += 1; - if (non_empty) - string_append (result, " "); + (*mangled)++; + APPEND_BLANK (result); string_append (result, "float"); break; case 'G': - *type += 1; - if (!isdigit (**type)) + (*mangled)++; + if (!isdigit (**mangled)) { success = 0; break; } /* fall through */ + /* An explicit type, such as "6mytype" or "7integer" */ case '0': case '1': case '2': @@ -660,214 +1621,391 @@ do_type (type, result, arg_mode) case '7': case '8': case '9': - n = 0; - do - { - n *= 10; - n += **type - '0'; - *type += 1; - } - while (isdigit (**type)); - if (strlen (*type) < n) + n = consume_count (mangled); + if (strlen (*mangled) < n) { success = 0; break; } - if (non_empty) - string_append (result, " "); - string_appendn (result, *type, n); - *type += n; + APPEND_BLANK (result); + string_appendn (result, *mangled, n); + *mangled += n; break; default: success = 0; break; } - if (success) - { - if (!string_empty (&decl)) - { - string_append (result, " "); - string_appends (result, &decl); - } - string_delete (&decl); - return 1; - } - else - { - string_delete (&decl); - string_delete (result); - return 0; - } + return (success); } -/* `result' will be initialised in do_type; it will be freed on failure */ +/* `result' will be initialized in do_type; it will be freed on failure */ static int -do_arg (type, result, arg_mode) - const char **type; +do_arg (work, mangled, result) + struct work_stuff *work; + const char **mangled; string *result; - int arg_mode; { - const char *start = *type; + const char *start = *mangled; - if (!do_type (type, result, arg_mode)) - return 0; - remember_type (start, *type - start); - return 1; + if (!do_type (work, mangled, result)) + { + return (0); + } + else + { + remember_type (work, start, *mangled - start); + return (1); + } } static void -remember_type (start, len) +remember_type (work, start, len) + struct work_stuff *work; const char *start; int len; { char *tem; - if (ntypes >= typevec_size) + if (work -> ntypes >= work -> typevec_size) { - if (typevec_size == 0) + if (work -> typevec_size == 0) { - typevec_size = 3; - typevec = (char **) xmalloc (sizeof (char*)*typevec_size); + work -> typevec_size = 3; + work -> typevec = + (char **) xmalloc (sizeof (char *) * work -> typevec_size); } else { - typevec_size *= 2; - typevec = (char **) xrealloc ((char *)typevec, sizeof (char*)*typevec_size); + work -> typevec_size *= 2; + work -> typevec = + (char **) xrealloc ((char *)work -> typevec, + sizeof (char *) * work -> typevec_size); } } tem = (char *) xmalloc (len + 1); memcpy (tem, start, len); tem[len] = '\0'; - typevec[ntypes++] = tem; + work -> typevec[work -> ntypes++] = tem; +} + +/* Forget the remembered types, but not the type vector itself. */ + +static void +forget_types (work) + struct work_stuff *work; +{ + int i; + + while (work -> ntypes > 0) + { + i = --(work -> ntypes); + if (work -> typevec[i] != NULL) + { + free (work -> typevec[i]); + work -> typevec[i] = NULL; + } + } } -/* `decl' must be already initialised, usually non-empty; - it won't be freed on failure */ +/* Process the argument list part of the signature, after any class spec + has been consumed, as well as the first 'F' character (if any). For + example: + + "__als__3fooRT0" => process "RT0" + "complexfunc5__FPFPc_PFl_i" => process "PFPc_PFl_i" + + DECLP must be already initialised, usually non-empty. It won't be freed + on failure. + + Note that g++ differs significantly from cfront and lucid style mangling + with regards to references to previously seen types. For example, given + the source fragment: + + class foo { + public: + foo::foo (int, foo &ia, int, foo &ib, int, foo &ic); + }; + + foo::foo (int, foo &ia, int, foo &ib, int, foo &ic) { ia = ib = ic; } + void foo (int, foo &ia, int, foo &ib, int, foo &ic) { ia = ib = ic; } + + g++ produces the names: + + __3fooiRT0iT2iT2 + foo__FiR3fooiT1iT1 + + while lcc (and presumably cfront as well) produces: + + foo__FiR3fooT1T2T1T2 + __ct__3fooFiR3fooT1T2T1T2 + + Note that g++ bases it's type numbers starting at zero and counts all + previously seen types, while lucid/cfront bases it's type numbers starting + at one and only considers types after it has seen the 'F' character + indicating the start of the function args. For lucid/cfront style, we + account for this difference by discarding any previously seen types when + we see the 'F' character, and subtracting one from the type number + reference. + + */ static int -do_args (type, decl, arg_mode) - const char **type; - string *decl; - int arg_mode; +demangle_args (work, mangled, declp) + struct work_stuff *work; + const char **mangled; + string *declp; { string arg; int need_comma = 0; + int r; + int t; + const char *tem; + char temptype; - if (print_arg_types) - string_append (decl, "("); + if (PRINT_ARG_TYPES) + { + string_append (declp, "("); + if (**mangled == '\0') + { + string_append (declp, "void"); + } + } - while (**type != '_' && **type != '\0' && **type != 'e' && **type != 'v') + while (**mangled != '_' && **mangled != '\0' && **mangled != 'e') { - if (**type == 'N') + if ((**mangled == 'N') || (**mangled == 'T')) { - int r; - int t; - *type += 1; - if (!get_count (type, &r) || !get_count (type, &t) || t >= ntypes) - return 0; + temptype = *(*mangled)++; + + if (temptype == 'N') + { + if (!get_count (mangled, &r)) + { + return (0); + } + } + else + { + r = 1; + } + if (!get_count (mangled, &t)) + { + return (0); + } + if (LUCID_DEMANGLING || CFRONT_DEMANGLING) + { + t--; + } + /* Validate the type index. Protect against illegal indices from + malformed type strings. */ + if ((t < 0) || (t >= work -> ntypes)) + { + return (0); + } while (--r >= 0) { - const char *tem = typevec[t]; - if (need_comma && print_arg_types) - string_append (decl, ", "); - if (!do_arg (&tem, &arg, arg_mode)) - return 0; - if (print_arg_types) - string_appends (decl, &arg); + tem = work -> typevec[t]; + if (need_comma && PRINT_ARG_TYPES) + { + string_append (declp, ", "); + } + if (!do_arg (work, &tem, &arg)) + { + return (0); + } + if (PRINT_ARG_TYPES) + { + string_appends (declp, &arg); + } string_delete (&arg); need_comma = 1; } } else { - if (need_comma & print_arg_types) - string_append (decl, ", "); - if (!do_arg (type, &arg, arg_mode)) - return 0; - if (print_arg_types) - string_appends (decl, &arg); + if (need_comma & PRINT_ARG_TYPES) + { + string_append (declp, ", "); + } + if (!do_arg (work, mangled, &arg)) + { + return (0); + } + if (PRINT_ARG_TYPES) + { + string_appends (declp, &arg); + } string_delete (&arg); need_comma = 1; } } - if (**type == 'v') - *type += 1; - else if (**type == 'e') + if (**mangled == 'e') { - *type += 1; - if (print_arg_types) + (*mangled)++; + if (PRINT_ARG_TYPES) { if (need_comma) - string_append (decl, ","); - string_append (decl, "..."); + { + string_append (declp, ","); + } + string_append (declp, "..."); } } - if (print_arg_types) - string_append (decl, ")"); - return 1; + if (PRINT_ARG_TYPES) + { + string_append (declp, ")"); + } + return (1); } static void -munge_function_name (name, arg_mode) - string *name; - int arg_mode; +demangle_function_name (work, mangled, declp, scan) + struct work_stuff *work; + const char **mangled; + string *declp; + const char *scan; { - if (!string_empty (name) && name->p - name->b >= 3 - && name->b[0] == 'o' && name->b[1] == 'p' && name->b[2] == CPLUS_MARKER) + int i; + int len; + string type; + const char *tem; + + string_appendn (declp, (*mangled), scan - (*mangled)); + string_need (declp, 1); + *(declp -> p) = '\0'; + + /* Consume the function name, including the "__" separating the name + from the signature. We are guaranteed that SCAN points to the + separator. */ + + (*mangled) = scan + 2; + + if (LUCID_DEMANGLING || CFRONT_DEMANGLING) + { + + /* See if we have an ARM style constructor or destructor operator. + If so, then just record it, clear the decl, and return. + We can't build the actual constructor/destructor decl until later, + when we recover the class name from the signature. */ + + if (strcmp (declp -> b, "__ct") == 0) + { + work -> constructor = 1; + string_clear (declp); + return; + } + else if (strcmp (declp -> b, "__dt") == 0) + { + work -> destructor = 1; + string_clear (declp); + return; + } + } + + if (declp->p - declp->b >= 3 + && declp->b[0] == 'o' + && declp->b[1] == 'p' + && strchr (cplus_markers, declp->b[2]) != NULL) { - int i; /* see if it's an assignment expression */ - if (name->p - name->b >= 10 /* op$assign_ */ - && memcmp (name->b + 3, "assign_", 7) == 0) + if (declp->p - declp->b >= 10 /* op$assign_ */ + && memcmp (declp->b + 3, "assign_", 7) == 0) { - for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++) + for (i = 0; i < sizeof (optable) / sizeof (optable[0]); i++) { - int len = name->p - name->b - 10; + len = declp->p - declp->b - 10; if (strlen (optable[i].in) == len - && memcmp (optable[i].in, name->b + 10, len) == 0) + && memcmp (optable[i].in, declp->b + 10, len) == 0) { - string_clear (name); - string_append (name, "operator"); - string_append (name, optable[i].out); - string_append (name, "="); - return; + string_clear (declp); + string_append (declp, "operator"); + string_append (declp, optable[i].out); + string_append (declp, "="); + break; } } } else { - for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++) + for (i = 0; i < sizeof (optable) / sizeof (optable[0]); i++) { - int len = name->p - name->b - 3; + int len = declp->p - declp->b - 3; if (strlen (optable[i].in) == len - && memcmp (optable[i].in, name->b + 3, len) == 0) + && memcmp (optable[i].in, declp->b + 3, len) == 0) { - string_clear (name); - string_append (name, "operator"); - string_append (name, optable[i].out); - return; + string_clear (declp); + string_append (declp, "operator"); + string_append (declp, optable[i].out); + break; } } } - return; } - else if (!string_empty (name) && name->p - name->b >= 5 - && memcmp (name->b, "type$", 5) == 0) + else if (declp->p - declp->b >= 5 && memcmp (declp->b, "type$", 5) == 0) { /* type conversion operator */ - string type; - const char *tem = name->b + 5; - if (do_type (&tem, &type, arg_mode)) + tem = declp->b + 5; + if (do_type (work, &tem, &type)) { - string_clear (name); - string_append (name, "operator "); - string_appends (name, &type); + string_clear (declp); + string_append (declp, "operator "); + string_appends (declp, &type); string_delete (&type); - return; + } + } + else if (declp->b[2] == 'o' && declp->b[3] == 'p') + { + /* ANSI. */ + /* type conversion operator. */ + tem = declp->b + 4; + if (do_type (work, &tem, &type)) + { + string_clear (declp); + string_append (declp, "operator "); + string_appends (declp, &type); + string_delete (&type); + } + } + else if (declp->b[0] == '_' && declp->b[1] == '_' + && declp->b[2] >= 'a' && declp->b[2] <= 'z' + && declp->b[3] >= 'a' && declp->b[3] <= 'z') + { + if (declp->b[4] == '\0') + { + /* Operator. */ + for (i = 0; i < sizeof (optable) / sizeof (optable[0]); i++) + { + if (strlen (optable[i].in) == 2 + && memcmp (optable[i].in, declp->b + 2, 2) == 0) + { + string_clear (declp); + string_append (declp, "operator"); + string_append (declp, optable[i].out); + break; + } + } + } + else + { + if (declp->b[2] == 'a' && declp->b[5] == '\0') + { + /* Assignment. */ + for (i = 0; i < sizeof (optable) / sizeof (optable[0]); i++) + { + if (strlen (optable[i].in) == 3 + && memcmp (optable[i].in, declp->b + 2, 3) == 0) + { + string_clear (declp); + string_append (declp, "operator"); + string_append (declp, optable[i].out); + break; + } + } + } } } } @@ -879,16 +2017,20 @@ string_need (s, n) string *s; int n; { + int tem; + if (s->b == NULL) { if (n < 32) - n = 32; + { + n = 32; + } s->p = s->b = (char *) xmalloc (n); s->e = s->b + n; } else if (s->e - s->p < n) { - int tem = s->p - s->b; + tem = s->p - s->b; n += tem; n *= 2; s->b = (char *) xrealloc (s->b, n); @@ -922,13 +2064,17 @@ string_clear (s) s->p = s->b; } +#if 0 + static int string_empty (s) string *s; { - return s->b == s->p; + return (s->b == s->p); } +#endif + static void string_append (p, s) string *p; @@ -948,12 +2094,14 @@ string_appends (p, s) string *p, *s; { int n; - if (s->b == s->p) - return; - n = s->p - s->b; - string_need (p, n); - memcpy (p->p, s->b, n); - p->p += n; + + if (s->b != s->p) + { + n = s->p - s->b; + string_need (p, n); + memcpy (p->p, s->b, n); + p->p += n; + } } static void @@ -962,11 +2110,12 @@ string_appendn (p, s, n) const char *s; int n; { - if (n == 0) - return; - string_need (p, n); - memcpy (p->p, s, n); - p->p += n; + if (n != 0) + { + string_need (p, n); + memcpy (p->p, s, n); + p->p += n; + } } static void @@ -974,20 +2123,24 @@ string_prepend (p, s) string *p; const char *s; { - if (s == NULL || *s == '\0') - return; - string_prependn (p, s, strlen (s)); + if (s != NULL && *s != '\0') + { + string_prependn (p, s, strlen (s)); + } } #if 0 + static void string_prepends (p, s) string *p, *s; { - if (s->b == s->p) - return; - string_prependn (p, s->b, s->p - s->b); + if (s->b != s->p) + { + string_prependn (p, s->b, s->p - s->b); + } } + #endif static void @@ -998,11 +2151,130 @@ string_prependn (p, s, n) { char *q; - if (n == 0) - return; - string_need (p, n); - for (q = p->p - 1; q >= p->b; q--) - q[n] = q[0]; - memcpy (p->b, s, n); - p->p += n; + if (n != 0) + { + string_need (p, n); + for (q = p->p - 1; q >= p->b; q--) + { + q[n] = q[0]; + } + memcpy (p->b, s, n); + p->p += n; + } +} + +/* To generate a standalone demangler program for testing purposes, just + compile and link this file with -DMAIN. When run, it demangles each + command line arg, or each stdin string, and prints the result on stdout. */ + +#ifdef MAIN + +static void +demangle_it (mangled_name) + char *mangled_name; +{ + char *result; + + result = cplus_demangle (mangled_name, DMGL_PARAMS | DMGL_ANSI); + if (result == NULL) + { + printf ("%s\n", mangled_name); + } + else + { + printf ("%s\n", result); + free (result); + } +} + +PTR +xmalloc (size) + long size; +{ + PTR newmem; + + if ((newmem = malloc ((int) size)) == NULL) + { + fprintf (stderr, "\nCan't allocate %u bytes\n", size); + exit (1); + } + return (newmem); +} + +PTR +xrealloc (oldmem, size) + PTR oldmem; + long size; +{ + PTR newmem; + + if ((newmem = realloc ((char *) oldmem, (int) size)) == NULL) + { + fprintf (stderr, "\nCan't reallocate %u bytes\n", size); + exit (1); + } + return (newmem); +} + +#include + +enum demangling_styles current_demangling_style = gnu_demangling; + +main (argc, argv) + int argc; + char **argv; +{ + char mangled_name[128]; + char *result; + int c; + extern char *optarg; + extern int optind; + + while ((c = getopt (argc, argv, "s:?")) != EOF) + { + switch (c) + { + case '?': + fprintf (stderr, "usage: demangle [-s style] [arg1 [arg2]] ...\n"); + fprintf (stderr, "style = { gnu, lucid, cfront }\n"); + fprintf (stderr, "reads args from stdin if none supplied\n"); + exit (0); + break; + case 's': + if (strcmp (optarg, "gnu") == 0) + { + current_demangling_style = gnu_demangling; + } + else if (strcmp (optarg, "lucid") == 0) + { + current_demangling_style = lucid_demangling; + } + else if (strcmp (optarg, "cfront") == 0) + { + current_demangling_style = cfront_demangling; + } + else + { + fprintf (stderr, "unknown demangling style `%s'\n", optarg); + exit (1); + } + break; + } + } + if (optind < argc) + { + for ( ; optind < argc; optind++) + { + demangle_it (argv[optind]); + } + } + else + { + while (gets (mangled_name)) + { + demangle_it (mangled_name); + } + } } + +#endif /* main */