mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-01-18 12:24:38 +08:00
d747e0af3d
* All GDB files that #include defs.h: Removed stdio.h. (defs.h): #include stdio.h. This has been tested by building GDBs for all targets hosted on Sun4. None of the build problems were related to stdio.h inclusion. (n.b. many configurations don't build for other reasons.)
1379 lines
27 KiB
C
1379 lines
27 KiB
C
/* Demangler for GNU C++
|
|
Copyright 1989, 1991 Free Software Foundation, Inc.
|
|
written by James Clark (jjc@jclark.uucp)
|
|
|
|
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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|
|
|
/* This is for g++ 1.95.03 (November 13 verison). */
|
|
|
|
/* This file exports one function
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
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"
|
|
|
|
cplus_demangle ("foo__1Afe", 0) => "A::foo(float,...)"
|
|
cplus_demangle ("foo__1Afe", 1) => "A::foo(float,...)"
|
|
cplus_demangle ("foo__1Afe", -1) => "A::foo"
|
|
|
|
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. */
|
|
|
|
/* define this if names don't start with _ */
|
|
/* #define nounderscore 1 */
|
|
|
|
/* GDB-specific, FIXME. */
|
|
#include "defs.h"
|
|
|
|
#include <ctype.h>
|
|
|
|
#ifdef USG
|
|
#include <memory.h>
|
|
#include <string.h>
|
|
#else
|
|
#include <strings.h>
|
|
#endif
|
|
|
|
/* 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
|
|
|
|
#ifndef __STDC__
|
|
#define const
|
|
#endif
|
|
|
|
#ifdef __STDC__
|
|
extern char *cplus_demangle (const char *type, int mode);
|
|
#else
|
|
extern char *cplus_demangle ();
|
|
#endif
|
|
|
|
#ifdef __STDC__
|
|
/* GDB prototypes these as void* in defs.h, so we better too, at least
|
|
as long as we're including defs.h. */
|
|
/* FIXME extern void *xmalloc (int);
|
|
extern void *xrealloc (char *, int); */
|
|
extern void free (void *);
|
|
#else
|
|
extern char *xmalloc ();
|
|
extern char *xrealloc ();
|
|
extern void free ();
|
|
#endif
|
|
|
|
static char **typevec = 0;
|
|
static int ntypes = 0;
|
|
static int typevec_size = 0;
|
|
|
|
const static struct optable {
|
|
const char *in;
|
|
const char *out;
|
|
int ansi;
|
|
} optable[] = {
|
|
"nw", " new", 1, /* new (1.92, ansi) */
|
|
"dl", " delete", 1, /* new (1.92, ansi) */
|
|
"new", " new", 0, /* old (1.91, and 1.x) */
|
|
"delete", " delete", 0, /* old (1.91, and 1.x) */
|
|
"as", "=", 1, /* ansi */
|
|
"ne", "!=", 1, /* old, ansi */
|
|
"eq", "==", 1, /* old, ansi */
|
|
"ge", ">=", 1, /* old, ansi */
|
|
"gt", ">", 1, /* old, ansi */
|
|
"le", "<=", 1, /* old, ansi */
|
|
"lt", "<", 1, /* old, ansi */
|
|
"plus", "+", 0, /* old */
|
|
"pl", "+", 1, /* ansi */
|
|
"apl", "+=", 1, /* ansi */
|
|
"minus", "-", 0, /* old */
|
|
"mi", "-", 1, /* ansi */
|
|
"ami", "-=", 1, /* ansi */
|
|
"mult", "*", 0, /* old */
|
|
"ml", "*", 1, /* ansi */
|
|
"aml", "*=", 1, /* ansi */
|
|
"convert", "+", 0, /* old (unary +) */
|
|
"negate", "-", 0, /* old (unary -) */
|
|
"trunc_mod", "%", 0, /* old */
|
|
"md", "%", 1, /* ansi */
|
|
"amd", "%=", 1, /* ansi */
|
|
"trunc_div", "/", 0, /* old */
|
|
"dv", "/", 1, /* ansi */
|
|
"adv", "/=", 1, /* ansi */
|
|
"truth_andif", "&&", 0, /* old */
|
|
"aa", "&&", 1, /* ansi */
|
|
"truth_orif", "||", 0, /* old */
|
|
"oo", "||", 1, /* ansi */
|
|
"truth_not", "!", 0, /* old */
|
|
"nt", "!", 1, /* ansi */
|
|
"postincrement", "++", 0, /* old */
|
|
"pp", "++", 1, /* ansi */
|
|
"postdecrement", "--", 0, /* old */
|
|
"mm", "--", 1, /* ansi */
|
|
"bit_ior", "|", 0, /* old */
|
|
"or", "|", 1, /* ansi */
|
|
"aor", "|=", 1, /* ansi */
|
|
"bit_xor", "^", 0, /* old */
|
|
"er", "^", 1, /* ansi */
|
|
"aer", "^=", 1, /* ansi */
|
|
"bit_and", "&", 0, /* old */
|
|
"ad", "&", 1, /* ansi */
|
|
"aad", "&=", 1, /* ansi */
|
|
"bit_not", "~", 0, /* old */
|
|
"co", "~", 1, /* ansi */
|
|
"call", "()", 0, /* old */
|
|
"cl", "()", 1, /* ansi */
|
|
"alshift", "<<", 0, /* old */
|
|
"ls", "<<", 1, /* ansi */
|
|
"als", "<<=", 1, /* ansi */
|
|
"arshift", ">>", 0, /* old */
|
|
"rs", ">>", 1, /* ansi */
|
|
"ars", ">>=", 1, /* ansi */
|
|
"component", "->", 0, /* old */
|
|
"rf", "->", 1, /* ansi */
|
|
"indirect", "*", 0, /* old */
|
|
"method_call", "->()", 0, /* old */
|
|
"addr", "&", 0, /* old (unary &) */
|
|
"array", "[]", 0, /* old */
|
|
"vc", "[]", 1, /* ansi */
|
|
"compound", ",", 0, /* old */
|
|
"cm", ",", 1, /* ansi */
|
|
"cond", "?:", 0, /* old */
|
|
"cn", "?:", 1, /* psuedo-ansi */
|
|
"max", ">?", 0, /* old */
|
|
"mx", ">?", 1, /* psuedo-ansi */
|
|
"min", "<?", 0, /* old */
|
|
"mn", "<?", 1, /* psuedo-ansi */
|
|
"nop", "", 0, /* old (for operator=) */
|
|
};
|
|
|
|
/* Beware: these aren't '\0' terminated. */
|
|
|
|
typedef struct string {
|
|
char *b; /* pointer to start of string */
|
|
char *p; /* pointer after last character */
|
|
char *e; /* pointer after end of allocated space */
|
|
} 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 *));
|
|
|
|
static int
|
|
string_empty PARAMS ((string *));
|
|
|
|
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
|
|
do_args PARAMS ((const char **, string *, int));
|
|
|
|
static int
|
|
do_type PARAMS ((const char **, string *, int));
|
|
|
|
static int
|
|
do_arg PARAMS ((const char **, string *, int));
|
|
|
|
static void
|
|
munge_function_name PARAMS ((string *, int));
|
|
|
|
static void
|
|
remember_type PARAMS ((const char *, int));
|
|
|
|
#if 0
|
|
static void
|
|
string_prepends PARAMS ((string *, string *));
|
|
#endif
|
|
|
|
|
|
/* Takes operator name as e.g. "++" and returns mangled
|
|
operator name (e.g. "postincrement_expr"), or NULL if not found.
|
|
|
|
If ARG_MODE == 1, return the ANSI name;
|
|
if ARG_MODE == 0 return the old GNU name. */
|
|
char *
|
|
cplus_mangle_opname (opname, arg_mode)
|
|
char *opname;
|
|
int arg_mode;
|
|
{
|
|
int i, len = strlen (opname);
|
|
|
|
if (arg_mode != 0 && arg_mode != 1)
|
|
error ("invalid arg_mode");
|
|
|
|
for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++)
|
|
{
|
|
if (strlen (optable[i].out) == len
|
|
&& arg_mode == optable[i].ansi
|
|
&& memcmp (optable[i].out, opname, len) == 0)
|
|
return (char *)optable[i].in;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
char *
|
|
cplus_demangle (type, arg_mode)
|
|
const char *type;
|
|
int arg_mode;
|
|
{
|
|
string decl;
|
|
int n;
|
|
int success = 0;
|
|
int constructor = 0;
|
|
int destructor = 0;
|
|
int static_type = 0;
|
|
int const_flag = 0;
|
|
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)
|
|
|
|
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')
|
|
{
|
|
/* destructor */
|
|
if (type[0] == '_' && type[1] == CPLUS_MARKER && type[2] == '_')
|
|
{
|
|
int n;
|
|
char *tem;
|
|
|
|
type += 3; /* Get past _$_ at front. */
|
|
while (isdigit (*type))
|
|
/* If there are digits at the front, it's because
|
|
of new 2.0 name mangling. Just skip them. */
|
|
type++;
|
|
|
|
n = strlen (type)*2 + 3 + 2 + 1;
|
|
tem = (char *) xmalloc (n);
|
|
strcpy (tem, type);
|
|
strcat (tem, "::~");
|
|
strcat (tem, type);
|
|
if (print_arg_types)
|
|
strcat (tem, "()");
|
|
return tem;
|
|
}
|
|
/* static data member */
|
|
if (*type != '_' && (p = strchr (type, CPLUS_MARKER)) != NULL)
|
|
{
|
|
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;
|
|
}
|
|
/* virtual table "_vt$" */
|
|
if (type[0] == '_' && type[1] == 'v' && type[2] == 't' && type[3] == CPLUS_MARKER)
|
|
{
|
|
int n = strlen (type + 4) + 14 + 1;
|
|
char *tem = (char *) xmalloc (n);
|
|
strcpy (tem, type + 4);
|
|
strcat (tem, " virtual table");
|
|
return tem;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
string_init (&decl);
|
|
|
|
if (static_type)
|
|
{
|
|
if (!isdigit (p[0]) && ('t' != p[0]))
|
|
{
|
|
string_delete (&decl);
|
|
return NULL;
|
|
}
|
|
}
|
|
else if (p == type)
|
|
{
|
|
if (!isdigit (p[2]) && ('t' != p[2]))
|
|
{
|
|
p += 1;
|
|
while (*p != '\0' && !(*p == '_' && p[1] == '_'))
|
|
p++;
|
|
string_appendn (&decl, type, p - type);
|
|
string_need (&decl, 1);
|
|
*(decl.p) = '\0';
|
|
munge_function_name (&decl, 1);
|
|
if (decl.b[0] == '_')
|
|
{
|
|
string_delete (&decl);
|
|
return NULL;
|
|
}
|
|
else
|
|
p += 2;
|
|
}
|
|
else
|
|
{
|
|
constructor = 1;
|
|
p += 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string_appendn (&decl, type, p - type);
|
|
string_need (&decl, 1);
|
|
*(decl.p) = '\0';
|
|
munge_function_name (&decl, arg_mode);
|
|
p += 2;
|
|
}
|
|
|
|
#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 || destructor)
|
|
{
|
|
string_appendn (&decl, p, n);
|
|
string_append (&decl, "::");
|
|
if (destructor)
|
|
string_append(&decl, "~");
|
|
string_appendn (&decl, p, n);
|
|
}
|
|
else
|
|
{
|
|
string_prepend (&decl, "::");
|
|
string_prependn (&decl, p, n);
|
|
}
|
|
p += n;
|
|
#ifndef LONGERNAMES
|
|
remember_type (premangle, p - premangle);
|
|
#endif
|
|
if (static_type)
|
|
{
|
|
string_append(&decl, p+1);
|
|
p += strlen(p);
|
|
success = 1;
|
|
}
|
|
else
|
|
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;
|
|
/* template additions */
|
|
case 't':
|
|
p += 1;
|
|
{
|
|
int r, i;
|
|
int non_empty = 0;
|
|
string tname;
|
|
string trawname;
|
|
|
|
string temp;
|
|
int need_comma = 0;
|
|
|
|
string_init(&tname);
|
|
string_init(&trawname);
|
|
|
|
/* get template name */
|
|
if (!get_count (&p, &r))
|
|
return 0;
|
|
string_appendn (&tname, p, r);
|
|
string_appendn (&trawname, p, r);
|
|
string_appendn (&trawname, "", 1);
|
|
p += r;
|
|
string_append (&tname, "<");
|
|
/* get size of template parameter list */
|
|
if (!get_count (&p, &r))
|
|
return 0;
|
|
for (i = 0; i < r; i++)
|
|
{
|
|
if (need_comma)
|
|
string_append (&tname, ", ");
|
|
/* Z for type parameters */
|
|
if (*p == 'Z')
|
|
{
|
|
p += 1;
|
|
|
|
success = do_type (&p, &temp, arg_mode);
|
|
string_appendn (&temp, "", 1);
|
|
if (success)
|
|
string_append (&tname, temp.b);
|
|
string_delete(&temp);
|
|
if (!success)
|
|
break;
|
|
}
|
|
/* otherwise, value parameter */
|
|
else
|
|
{
|
|
const char *old_p = p;
|
|
int is_pointer = 0;
|
|
int is_real = 0;
|
|
int is_integral = 0;
|
|
int done = 0;
|
|
|
|
success = do_type (&p, &temp, arg_mode);
|
|
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 '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 (*p == 'm')
|
|
{
|
|
string_appendn (&tname, "-", 1);
|
|
p++;
|
|
}
|
|
while (isdigit (*p))
|
|
{
|
|
string_appendn (&tname, p, 1);
|
|
p++;
|
|
}
|
|
}
|
|
else if (is_real)
|
|
{
|
|
if (*p == 'm')
|
|
{
|
|
string_appendn (&tname, "-", 1);
|
|
p++;
|
|
}
|
|
while (isdigit (*p))
|
|
{
|
|
string_appendn (&tname, p, 1);
|
|
p++;
|
|
}
|
|
if (*p == '.') /* fraction */
|
|
{
|
|
string_appendn (&tname, ".", 1);
|
|
p++;
|
|
while (isdigit (*p))
|
|
{
|
|
string_appendn (&tname, p, 1);
|
|
p++;
|
|
}
|
|
}
|
|
if (*p == 'e') /* exponent */
|
|
{
|
|
string_appendn (&tname, "e", 1);
|
|
p++;
|
|
while (isdigit (*p))
|
|
{
|
|
string_appendn (&tname, p, 1);
|
|
p++;
|
|
}
|
|
}
|
|
}
|
|
else if (is_pointer)
|
|
{
|
|
int symbol_len;
|
|
|
|
if (!get_count (&p, &symbol_len))
|
|
{
|
|
success = 0;
|
|
break;
|
|
}
|
|
string_appendn (&tname, p, symbol_len);
|
|
p += symbol_len;
|
|
}
|
|
}
|
|
need_comma = 1;
|
|
}
|
|
string_append (&tname, ">::");
|
|
if (destructor)
|
|
string_append(&tname, "~");
|
|
if (constructor || destructor) {
|
|
string_append (&tname, trawname.b);
|
|
}
|
|
string_delete(&trawname);
|
|
|
|
if (!success) {
|
|
string_delete(&tname);
|
|
return 0;
|
|
}
|
|
string_prepend (&decl, tname.b);
|
|
string_delete(&tname);
|
|
|
|
if (static_type)
|
|
{
|
|
string_append(&decl, p+1);
|
|
p += strlen(p);
|
|
success = 1;
|
|
}
|
|
else
|
|
success = do_args (&p, &decl, arg_mode);
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ntypes; i++)
|
|
if (typevec[i] != NULL)
|
|
free (typevec[i]);
|
|
ntypes = 0;
|
|
if (typevec != NULL)
|
|
{
|
|
free ((char *)typevec);
|
|
typevec = NULL;
|
|
typevec_size = 0;
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
string_appendn (&decl, "", 1);
|
|
return decl.b;
|
|
}
|
|
else
|
|
{
|
|
string_delete (&decl);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static int
|
|
get_count (type, count)
|
|
const char **type;
|
|
int *count;
|
|
{
|
|
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 == '_')
|
|
{
|
|
*type = p + 1;
|
|
*count = n;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* result will be initialised here; it will be freed on failure */
|
|
|
|
static int
|
|
do_type (type, result, arg_mode)
|
|
const char **type;
|
|
string *result;
|
|
int arg_mode;
|
|
{
|
|
int n;
|
|
int done;
|
|
int non_empty = 0;
|
|
int success;
|
|
string decl;
|
|
const char *remembered_type;
|
|
|
|
string_init (&decl);
|
|
string_init (result);
|
|
|
|
done = 0;
|
|
success = 1;
|
|
while (success && !done)
|
|
{
|
|
int member;
|
|
switch (**type)
|
|
{
|
|
case 'Q':
|
|
n = (*type)[1] - '0';
|
|
if (n < 0 || n > 9)
|
|
success = 0;
|
|
*type += 2;
|
|
while (n-- > 0)
|
|
do_type (type, result, arg_mode);
|
|
break;
|
|
|
|
case 'P':
|
|
*type += 1;
|
|
string_prepend (&decl, "*");
|
|
break;
|
|
|
|
case 'R':
|
|
*type += 1;
|
|
string_prepend (&decl, "&");
|
|
break;
|
|
|
|
case 'T':
|
|
*type += 1;
|
|
if (!get_count (type, &n) || n >= ntypes)
|
|
success = 0;
|
|
else
|
|
{
|
|
remembered_type = typevec[n];
|
|
type = &remembered_type;
|
|
}
|
|
break;
|
|
|
|
case 'F':
|
|
*type += 1;
|
|
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;
|
|
break;
|
|
|
|
case 'M':
|
|
case 'O':
|
|
{
|
|
int constp = 0;
|
|
int volatilep = 0;
|
|
|
|
member = **type == 'M';
|
|
*type += 1;
|
|
if (!isdigit (**type))
|
|
{
|
|
success = 0;
|
|
break;
|
|
}
|
|
n = 0;
|
|
do
|
|
{
|
|
n *= 10;
|
|
n += **type - '0';
|
|
*type += 1;
|
|
}
|
|
while (isdigit (**type));
|
|
if (strlen (*type) < n)
|
|
{
|
|
success = 0;
|
|
break;
|
|
}
|
|
string_append (&decl, ")");
|
|
string_prepend (&decl, "::");
|
|
string_prependn (&decl, *type, n);
|
|
string_prepend (&decl, "(");
|
|
*type += n;
|
|
if (member)
|
|
{
|
|
if (**type == 'C')
|
|
{
|
|
*type += 1;
|
|
constp = 1;
|
|
}
|
|
if (**type == 'V')
|
|
{
|
|
*type += 1;
|
|
volatilep = 1;
|
|
}
|
|
if (*(*type)++ != 'F')
|
|
{
|
|
success = 0;
|
|
break;
|
|
}
|
|
}
|
|
if ((member && !do_args (type, &decl, arg_mode)) || **type != '_')
|
|
{
|
|
success = 0;
|
|
break;
|
|
}
|
|
*type += 1;
|
|
if (! print_ansi_qualifiers)
|
|
break;
|
|
if (constp)
|
|
{
|
|
if (non_empty)
|
|
string_append (&decl, " ");
|
|
else
|
|
non_empty = 1;
|
|
string_append (&decl, "const");
|
|
}
|
|
if (volatilep)
|
|
{
|
|
if (non_empty)
|
|
string_append (&decl, " ");
|
|
else
|
|
non_empty = 1;
|
|
string_append (&decl, "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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (success)
|
|
switch (**type)
|
|
{
|
|
case '\0':
|
|
case '_':
|
|
break;
|
|
case 'v':
|
|
*type += 1;
|
|
if (non_empty)
|
|
string_append (result, " ");
|
|
string_append (result, "void");
|
|
break;
|
|
case 'x':
|
|
*type += 1;
|
|
if (non_empty)
|
|
string_append (result, " ");
|
|
string_append (result, "long long");
|
|
break;
|
|
case 'l':
|
|
*type += 1;
|
|
if (non_empty)
|
|
string_append (result, " ");
|
|
string_append (result, "long");
|
|
break;
|
|
case 'i':
|
|
*type += 1;
|
|
if (non_empty)
|
|
string_append (result, " ");
|
|
string_append (result, "int");
|
|
break;
|
|
case 's':
|
|
*type += 1;
|
|
if (non_empty)
|
|
string_append (result, " ");
|
|
string_append (result, "short");
|
|
break;
|
|
case 'c':
|
|
*type += 1;
|
|
if (non_empty)
|
|
string_append (result, " ");
|
|
string_append (result, "char");
|
|
break;
|
|
case 'r':
|
|
*type += 1;
|
|
if (non_empty)
|
|
string_append (result, " ");
|
|
string_append (result, "long double");
|
|
break;
|
|
case 'd':
|
|
*type += 1;
|
|
if (non_empty)
|
|
string_append (result, " ");
|
|
string_append (result, "double");
|
|
break;
|
|
case 'f':
|
|
*type += 1;
|
|
if (non_empty)
|
|
string_append (result, " ");
|
|
string_append (result, "float");
|
|
break;
|
|
case 'G':
|
|
*type += 1;
|
|
if (!isdigit (**type))
|
|
{
|
|
success = 0;
|
|
break;
|
|
}
|
|
/* 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 += **type - '0';
|
|
*type += 1;
|
|
}
|
|
while (isdigit (**type));
|
|
if (strlen (*type) < n)
|
|
{
|
|
success = 0;
|
|
break;
|
|
}
|
|
if (non_empty)
|
|
string_append (result, " ");
|
|
string_appendn (result, *type, n);
|
|
*type += 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;
|
|
}
|
|
}
|
|
|
|
/* `result' will be initialised in do_type; it will be freed on failure */
|
|
|
|
static int
|
|
do_arg (type, result, arg_mode)
|
|
const char **type;
|
|
string *result;
|
|
int arg_mode;
|
|
{
|
|
const char *start = *type;
|
|
|
|
if (!do_type (type, result, arg_mode))
|
|
return 0;
|
|
remember_type (start, *type - start);
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
remember_type (start, len)
|
|
const char *start;
|
|
int len;
|
|
{
|
|
char *tem;
|
|
|
|
if (ntypes >= typevec_size)
|
|
{
|
|
if (typevec_size == 0)
|
|
{
|
|
typevec_size = 3;
|
|
typevec = (char **) xmalloc (sizeof (char*)*typevec_size);
|
|
}
|
|
else
|
|
{
|
|
typevec_size *= 2;
|
|
typevec = (char **) xrealloc ((char *)typevec, sizeof (char*)*typevec_size);
|
|
}
|
|
}
|
|
tem = (char *) xmalloc (len + 1);
|
|
memcpy (tem, start, len);
|
|
tem[len] = '\0';
|
|
typevec[ntypes++] = tem;
|
|
}
|
|
|
|
/* `decl' must be already initialised, usually non-empty;
|
|
it won't be freed on failure */
|
|
|
|
static int
|
|
do_args (type, decl, arg_mode)
|
|
const char **type;
|
|
string *decl;
|
|
int arg_mode;
|
|
{
|
|
string arg;
|
|
int need_comma = 0;
|
|
|
|
if (print_arg_types)
|
|
string_append (decl, "(");
|
|
|
|
while (**type != '_' && **type != '\0' && **type != 'e' && **type != 'v')
|
|
{
|
|
if (**type == 'N')
|
|
{
|
|
int r;
|
|
int t;
|
|
*type += 1;
|
|
if (!get_count (type, &r) || !get_count (type, &t) || t >= 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);
|
|
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);
|
|
string_delete (&arg);
|
|
need_comma = 1;
|
|
}
|
|
}
|
|
|
|
if (**type == 'v')
|
|
*type += 1;
|
|
else if (**type == 'e')
|
|
{
|
|
*type += 1;
|
|
if (print_arg_types)
|
|
{
|
|
if (need_comma)
|
|
string_append (decl, ",");
|
|
string_append (decl, "...");
|
|
}
|
|
}
|
|
|
|
if (print_arg_types)
|
|
string_append (decl, ")");
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
munge_function_name (name, arg_mode)
|
|
string *name;
|
|
int arg_mode;
|
|
{
|
|
if (string_empty (name))
|
|
return;
|
|
|
|
if (name->p - name->b >= 3
|
|
&& name->b[0] == 'o' && name->b[1] == 'p' && name->b[2] == CPLUS_MARKER)
|
|
{
|
|
int i;
|
|
/* see if it's an assignment expression */
|
|
if (name->p - name->b >= 10 /* op$assign_ */
|
|
&& memcmp (name->b + 3, "assign_", 7) == 0)
|
|
{
|
|
for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++)
|
|
{
|
|
int len = name->p - name->b - 10;
|
|
if (strlen (optable[i].in) == len
|
|
&& memcmp (optable[i].in, name->b + 10, len) == 0)
|
|
{
|
|
string_clear (name);
|
|
string_append (name, "operator");
|
|
string_append (name, optable[i].out);
|
|
string_append (name, "=");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++)
|
|
{
|
|
int len = name->p - name->b - 3;
|
|
if (strlen (optable[i].in) == len
|
|
&& memcmp (optable[i].in, name->b + 3, len) == 0)
|
|
{
|
|
string_clear (name);
|
|
string_append (name, "operator");
|
|
string_append (name, optable[i].out);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
else if (name->p - name->b >= 5 && memcmp (name->b, "type$", 5) == 0)
|
|
{
|
|
/* type conversion operator */
|
|
string type;
|
|
const char *tem = name->b + 5;
|
|
if (do_type (&tem, &type, arg_mode))
|
|
{
|
|
string_clear (name);
|
|
string_append (name, "operator ");
|
|
string_appends (name, &type);
|
|
string_delete (&type);
|
|
return;
|
|
}
|
|
}
|
|
/* ANSI. */
|
|
else if (name->b[2] == 'o' && name->b[3] == 'p')
|
|
{
|
|
/* type conversion operator. */
|
|
string type;
|
|
const char *tem = name->b + 4;
|
|
if (do_type (&tem, &type, arg_mode))
|
|
{
|
|
string_clear (name);
|
|
string_append (name, "operator ");
|
|
string_appends (name, &type);
|
|
string_delete (&type);
|
|
return;
|
|
}
|
|
}
|
|
else if (name->b[0] == '_' && name->b[1] == '_'
|
|
&& name->b[2] >= 'a' && name->b[2] <= 'z'
|
|
&& name->b[3] >= 'a' && name->b[3] <= 'z')
|
|
{
|
|
int i;
|
|
|
|
if (name->b[4] == '\0')
|
|
{
|
|
/* Operator. */
|
|
for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++)
|
|
{
|
|
if (strlen (optable[i].in) == 2
|
|
&& memcmp (optable[i].in, name->b + 2, 2) == 0)
|
|
{
|
|
string_clear (name);
|
|
string_append (name, "operator");
|
|
string_append (name, optable[i].out);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (name->b[2] != 'a' || name->b[5] != '\0')
|
|
return;
|
|
/* Assignment. */
|
|
for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++)
|
|
{
|
|
if (strlen (optable[i].in) == 3
|
|
&& memcmp (optable[i].in, name->b + 2, 3) == 0)
|
|
{
|
|
string_clear (name);
|
|
string_append (name, "operator");
|
|
string_append (name, optable[i].out);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* a mini string-handling package */
|
|
|
|
static void
|
|
string_need (s, n)
|
|
string *s;
|
|
int n;
|
|
{
|
|
if (s->b == NULL)
|
|
{
|
|
if (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;
|
|
n += tem;
|
|
n *= 2;
|
|
s->b = (char *) xrealloc (s->b, n);
|
|
s->p = s->b + tem;
|
|
s->e = s->b + n;
|
|
}
|
|
}
|
|
|
|
static void
|
|
string_delete (s)
|
|
string *s;
|
|
{
|
|
if (s->b != NULL)
|
|
{
|
|
free (s->b);
|
|
s->b = s->e = s->p = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
string_init (s)
|
|
string *s;
|
|
{
|
|
s->b = s->p = s->e = NULL;
|
|
}
|
|
|
|
static void
|
|
string_clear (s)
|
|
string *s;
|
|
{
|
|
s->p = s->b;
|
|
}
|
|
|
|
static int
|
|
string_empty (s)
|
|
string *s;
|
|
{
|
|
return s->b == s->p;
|
|
}
|
|
|
|
static void
|
|
string_append (p, s)
|
|
string *p;
|
|
const char *s;
|
|
{
|
|
int n;
|
|
if (s == NULL || *s == '\0')
|
|
return;
|
|
n = strlen (s);
|
|
string_need (p, n);
|
|
memcpy (p->p, s, n);
|
|
p->p += n;
|
|
}
|
|
|
|
static void
|
|
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;
|
|
}
|
|
|
|
static void
|
|
string_appendn (p, s, n)
|
|
string *p;
|
|
const char *s;
|
|
int n;
|
|
{
|
|
if (n == 0)
|
|
return;
|
|
string_need (p, n);
|
|
memcpy (p->p, s, n);
|
|
p->p += n;
|
|
}
|
|
|
|
static void
|
|
string_prepend (p, s)
|
|
string *p;
|
|
const char *s;
|
|
{
|
|
if (s == NULL || *s == '\0')
|
|
return;
|
|
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);
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
string_prependn (p, s, n)
|
|
string *p;
|
|
const char *s;
|
|
int 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;
|
|
}
|