/* Demangler for IA64 / g++ standard C++ ABI.
- Copyright (C) 2000 Free Software Foundation, Inc.
+ Copyright (C) 2000 CodeSourcery LLC.
Written by Alex Samuel <samuel@codesourcery.com>.
- This file is part of GNU CC.
-
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
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 (functionally similar to c++filt, but includes this
- demangler only). */
+ executable. */
#ifdef HAVE_CONFIG_H
#include "config.h"
(((CHAR) >= 'a' && (CHAR) <= 'z') \
|| ((CHAR) >= 'A' && (CHAR) <= 'Z'))
-/* The prefix prepended by GCC to an identifier represnting the
- anonymous namespace. */
-#define ANONYMOUS_NAMESPACE_PREFIX "_GLOBAL_"
-
/* 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.
/* 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
#define STATUS_INTERNAL_ERROR "Internal error."
/* This status code indicates a failure in malloc or realloc. */
-static const char *const status_allocation_failed = "Allocation failed.";
+static const char* const status_allocation_failed = "Allocation failed.";
#define STATUS_ALLOCATION_FAILED status_allocation_failed
/* Non-zero if STATUS indicates that no error has occurred. */
PARAMS ((int));
static void string_list_delete
PARAMS ((string_list_t));
-static status_t result_add_separated_char
- PARAMS ((demangling_t, int));
+static status_t result_close_template_list
+ PARAMS ((demangling_t));
static status_t result_push
PARAMS ((demangling_t));
static string_list_t result_pop
static int substitution_start
PARAMS ((demangling_t));
static status_t substitution_add
- PARAMS ((demangling_t, int, int));
+ PARAMS ((demangling_t, int, int, int));
static dyn_string_t substitution_get
PARAMS ((demangling_t, int, int *));
#ifdef CP_DEMANGLE_DEBUG
(dyn_string_append_char (&(DM)->result->string, (CHAR)) \
? STATUS_OK : STATUS_ALLOCATION_FAILED)
-/* Inserts a dyn_string_t to the demangled result at position POS. */
-#define result_insert_string(DM, POS, STRING) \
- (dyn_string_insert (&(DM)->result->string, (POS), (STRING)) \
- ? STATUS_OK : STATUS_ALLOCATION_FAILED)
-
-/* Inserts NUL-terminated string CSTR to the demangled result at
- position POS. */
-#define result_insert(DM, POS, CSTR) \
- (dyn_string_insert_cstr (&(DM)->result->string, (POS), (CSTR)) \
- ? STATUS_OK : STATUS_ALLOCATION_FAILED)
-
-/* Inserts character CHAR to the demangled result at position POS. */
-#define result_insert_char(DM, POS, CHAR) \
- (dyn_string_insert_char (&(DM)->result->string, (POS), (CHAR)) \
- ? STATUS_OK : STATUS_ALLOCATION_FAILED)
-
/* The length of the current demangled result. */
#define result_length(DM) \
dyn_string_length (&(DM)->result->string)
(dyn_string_append_space (&(DM)->result->string) \
? STATUS_OK : STATUS_ALLOCATION_FAILED)
-/* Appends a (less-than, greater-than) character to the result in DM
- to (open, close) a template argument or parameter list. Appends a
- space first if necessary to prevent spurious elision of angle
- brackets with the previous character. */
-#define result_open_template_list(DM) result_add_separated_char(DM, '<')
-#define result_close_template_list(DM) result_add_separated_char(DM, '>')
-
/* Appends a base 10 representation of VALUE to DS. STATUS_OK on
success. On failure, deletes DS and returns an error code. */
}
}
-/* Appends CHARACTER to the demangled result. If the current trailing
- character of the result is CHARACTER, a space is inserted first. */
+/* 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 status_t
-result_add_separated_char (dm, character)
+result_close_template_list (dm)
demangling_t dm;
- int character;
{
dyn_string_t s = &dm->result->string;
/* Add a space if the last character is already a closing angle
bracket, so that a nested template arg list doesn't look like
it's closed with a right-shift operator. */
- if (dyn_string_last_char (s) == character)
+ if (dyn_string_last_char (s) == '>')
{
if (!dyn_string_append_char (s, ' '))
return STATUS_ALLOCATION_FAILED;
}
/* Add closing angle brackets. */
- if (!dyn_string_append_char (s, character))
+ if (!dyn_string_append_char (s, '>'))
return STATUS_ALLOCATION_FAILED;
return STATUS_OK;
/* 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. */
+ non-zero, this potential substitution is a template-id.
+
+ If TEMPLATE_PARM_NUMBER is not NOT_TEMPLATE_PARM, the substitution
+ is for that particular <template-param>, and is distinct from other
+ otherwise-identical types and other <template-param>s with
+ different indices. */
static status_t
-substitution_add (dm, start_position, template_p)
+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);
return STATUS_ALLOCATION_FAILED;
}
+ /* 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 STATUS_OK;
+ }
+
/* If there's no room for the new entry, grow the array. */
if (dm->substitutions_allocated == dm->num_substitutions)
{
size_t new_array_size;
- if (dm->substitutions_allocated > 0)
- dm->substitutions_allocated *= 2;
- else
- dm->substitutions_allocated = 2;
+ dm->substitutions_allocated *= 2;
new_array_size =
sizeof (struct substitution_def) * dm->substitutions_allocated;
}
/* Add the substitution to the array. */
- i = dm->num_substitutions++;
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);
static status_t demangle_prefix
PARAMS ((demangling_t, int *));
static status_t demangle_unqualified_name
- PARAMS ((demangling_t, int *));
+ PARAMS ((demangling_t));
static status_t demangle_source_name
PARAMS ((demangling_t));
static status_t demangle_number
PARAMS ((demangling_t, int, dyn_string_t));
static status_t demangle_operator_name
PARAMS ((demangling_t, int, int *));
-static status_t demangle_nv_offset
- PARAMS ((demangling_t));
-static status_t demangle_v_offset
- PARAMS ((demangling_t));
-static status_t demangle_call_offset
- PARAMS ((demangling_t));
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, int *, int));
+ PARAMS ((demangling_t));
static status_t demangle_type
PARAMS ((demangling_t));
static status_t demangle_CV_qualifiers
static status_t demangle_builtin_type
PARAMS ((demangling_t));
static status_t demangle_function_type
- PARAMS ((demangling_t, int *));
+ PARAMS ((demangling_t, int));
static status_t demangle_bare_function_type
- PARAMS ((demangling_t, int *));
+ 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));
+ PARAMS ((demangling_t, int *));
static status_t demangle_template_args
PARAMS ((demangling_t));
static status_t demangle_literal
static status_t demangle_expr_primary
PARAMS ((demangling_t));
static status_t demangle_substitution
- PARAMS ((demangling_t, int *));
+ PARAMS ((demangling_t, int *, int *));
static status_t demangle_local_name
PARAMS ((demangling_t));
static status_t demangle_discriminator
PARAMS ((demangling_t, int));
static status_t cp_demangle
PARAMS ((const char *, dyn_string_t));
-#ifdef IN_LIBGCC2
static status_t cp_demangle_type
PARAMS ((const char*, dyn_string_t));
-#endif
/* 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 NULL
+#define BFT_NO_RETURN_TYPE -1
/* Check that the next character is C. If so, consume it. If not,
return an error. */
demangle_encoding (dm)
demangling_t dm;
{
- int encode_return_type;
+ int template_p;
int start_position;
template_arg_list_t old_arg_list = current_template_arg_list (dm);
char peek = peek_char (dm);
else
{
/* Now demangle the name. */
- RETURN_IF_ERROR (demangle_name (dm, &encode_return_type));
+ 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 (encode_return_type)
+ 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));
+ (demangle_bare_function_type (dm, start_position));
else
/* Non-template functions don't have their return type
encoded. */
::= <substitution> */
static status_t
-demangle_name (dm, encode_return_type)
+demangle_name (dm, template_p)
demangling_t dm;
- int *encode_return_type;
+ int *template_p;
{
+ int special_std_substitution;
int start = substitution_start (dm);
- char peek = peek_char (dm);
- int is_std_substitution = 0;
-
- /* Generally, the return type is encoded if the function is a
- template-id, and suppressed otherwise. There are a few cases,
- though, in which the return type is not encoded even for a
- templated function. In these cases, this flag is set. */
- int suppress_return_type = 0;
DEMANGLE_TRACE ("name", dm);
- switch (peek)
+ switch (peek_char (dm))
{
case 'N':
/* This is a <nested-name>. */
- RETURN_IF_ERROR (demangle_nested_name (dm, encode_return_type));
+ RETURN_IF_ERROR (demangle_nested_name (dm, template_p));
break;
case 'Z':
RETURN_IF_ERROR (demangle_local_name (dm));
- *encode_return_type = 0;
break;
case 'S':
(void) next_char (dm);
(void) next_char (dm);
RETURN_IF_ERROR (result_append (dm, "std::"));
- RETURN_IF_ERROR
- (demangle_unqualified_name (dm, &suppress_return_type));
- is_std_substitution = 1;
+ RETURN_IF_ERROR (demangle_unqualified_name (dm));
}
else
- RETURN_IF_ERROR (demangle_substitution (dm, encode_return_type));
+ {
+ 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 <nested-name> or one of the unscoped names
+ following. */
+ RETURN_IF_ERROR (result_append (dm, "::"));
+ RETURN_IF_ERROR (demangle_name (dm, template_p));
+ }
+ }
/* Check if a template argument list immediately follows.
If so, then we just demangled an <unqualified-template-name>. */
if (peek_char (dm) == 'I')
{
- /* A template name of the form std::<unqualified-name> is a
- substitution candidate. */
- if (is_std_substitution)
- RETURN_IF_ERROR (substitution_add (dm, start, 0));
- /* Demangle the <template-args> here. */
+ RETURN_IF_ERROR (substitution_add (dm, start, 0,
+ NOT_TEMPLATE_PARM));
RETURN_IF_ERROR (demangle_template_args (dm));
- *encode_return_type = !suppress_return_type;
}
- else
- *encode_return_type = 0;
-
break;
default:
/* This is an <unscoped-name> or <unscoped-template-name>. */
- RETURN_IF_ERROR (demangle_unqualified_name (dm, &suppress_return_type));
+ RETURN_IF_ERROR (demangle_unqualified_name (dm));
/* If the <unqualified-name> is followed by template args, this
is an <unscoped-template-name>. */
if (peek_char (dm) == 'I')
{
/* Add a substitution for the unqualified template name. */
- RETURN_IF_ERROR (substitution_add (dm, start, 0));
+ RETURN_IF_ERROR (substitution_add (dm, start, 0,
+ NOT_TEMPLATE_PARM));
RETURN_IF_ERROR (demangle_template_args (dm));
- *encode_return_type = !suppress_return_type;
+ *template_p = 1;
}
else
- *encode_return_type = 0;
+ *template_p = 0;
break;
}
/* Demangles and emits a <nested-name>.
- <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqulified-name> E */
+ <nested-name> ::= N [<CV-qualifiers>] <prefix> <component> E */
static status_t
-demangle_nested_name (dm, encode_return_type)
+demangle_nested_name (dm, template_p)
demangling_t dm;
- int *encode_return_type;
+ int *template_p;
{
char peek;
RETURN_IF_ERROR (result_append_space (dm));
}
- RETURN_IF_ERROR (demangle_prefix (dm, encode_return_type));
- /* No need to demangle the final <unqualified-name>; demangle_prefix
- will handle it. */
+ RETURN_IF_ERROR (demangle_prefix (dm, template_p));
+ /* No need to demangle the final <component>; demangle_prefix will
+ handle it. */
RETURN_IF_ERROR (demangle_char (dm, 'E'));
return STATUS_OK;
/* Demangles and emits a <prefix>.
- <prefix> ::= <prefix> <unqualified-name>
+ <prefix> ::= <prefix> <component>
::= <template-prefix> <template-args>
::= # empty
::= <substitution>
<template-prefix> ::= <prefix>
- ::= <substitution> */
+ ::= <substitution>
+
+ <component> ::= <unqualified-name>
+ ::= <local-name> */
static status_t
-demangle_prefix (dm, encode_return_type)
+demangle_prefix (dm, template_p)
demangling_t dm;
- int *encode_return_type;
+ int *template_p;
{
int start = substitution_start (dm);
int nested = 0;
- /* ENCODE_RETURN_TYPE is updated as we decend the nesting chain.
- After <template-args>, it is set to non-zero; after everything
- else it is set to zero. */
-
- /* Generally, the return type is encoded if the function is a
- template-id, and suppressed otherwise. There are a few cases,
- though, in which the return type is not encoded even for a
- templated function. In these cases, this flag is set. */
- int suppress_return_type = 0;
+ /* TEMPLATE_P is updated as we decend the nesting chain. After
+ <template-args>, 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 <compound-name>.";
peek = peek_char (dm);
- /* We'll initialize suppress_return_type to false, and set it to true
- if we end up demangling a constructor name. However, make
- sure we're not actually about to demangle template arguments
- -- if so, this is the <template-args> following a
- <template-prefix>, so we'll want the previous flag value
- around. */
- if (peek != 'I')
- suppress_return_type = 0;
-
if (IS_DIGIT ((unsigned char) peek)
|| (peek >= 'a' && peek <= 'z')
|| peek == 'C' || peek == 'D'
if (peek == 'S')
/* The substitution determines whether this is a
- template-id. */
- RETURN_IF_ERROR (demangle_substitution (dm, encode_return_type));
+ template-id. */
+ RETURN_IF_ERROR (demangle_substitution (dm, template_p,
+ &unused));
else
{
- /* It's just a name. */
- RETURN_IF_ERROR
- (demangle_unqualified_name (dm, &suppress_return_type));
- *encode_return_type = 0;
+ 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. */
+ RETURN_IF_ERROR (substitution_add (dm, start, 0, NOT_TEMPLATE_PARM));
RETURN_IF_ERROR (demangle_template_args (dm));
-
- /* Now we want to indicate to the caller that we've
- demangled template arguments, thus the prefix was a
- <template-prefix>. That's so that the caller knows to
- demangle the function's return type, if this turns out to
- be a function name. But, if it's a member template
- constructor or a templated conversion operator, report it
- as untemplated. Those never get encoded return types. */
- *encode_return_type = !suppress_return_type;
+ *template_p = 1;
}
else if (peek == 'E')
/* All done. */
else
return "Unexpected character in <compound-name>.";
- if (peek != 'S'
- && peek_char (dm) != 'E')
- /* Add a new substitution for the prefix thus far. */
- RETURN_IF_ERROR (substitution_add (dm, start, *encode_return_type));
+ /* Add a new substitution for the prefix thus far. */
+ RETURN_IF_ERROR (substitution_add (dm, start, *template_p,
+ NOT_TEMPLATE_PARM));
}
}
-/* Demangles and emits an <unqualified-name>. If this
- <unqualified-name> is for a special function type that should never
- have its return type encoded (particularly, a constructor or
- conversion operator), *SUPPRESS_RETURN_TYPE is set to 1; otherwise,
- it is set to zero.
+/* Demangles and emits an <unqualified-name>. If the
+ <unqualified-name> 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.
<unqualified-name> ::= <operator-name>
::= <special-name>
::= <source-name> */
static status_t
-demangle_unqualified_name (dm, suppress_return_type)
+demangle_unqualified_name (dm)
demangling_t dm;
- int *suppress_return_type;
{
char peek = peek_char (dm);
DEMANGLE_TRACE ("unqualified-name", dm);
- /* By default, don't force suppression of the return type (though
- non-template functions still don't get a return type encoded). */
- *suppress_return_type = 0;
-
if (IS_DIGIT ((unsigned char) peek))
RETURN_IF_ERROR (demangle_source_name (dm));
else if (peek >= 'a' && peek <= 'z')
{
int num_args;
-
- /* Conversion operators never have a return type encoded. */
- if (peek == 'c' && peek_char_next (dm) == 'v')
- *suppress_return_type = 1;
-
RETURN_IF_ERROR (demangle_operator_name (dm, 0, &num_args));
}
else if (peek == 'C' || peek == 'D')
- {
- /* Constructors never have a return type encoded. */
- if (peek == 'C')
- *suppress_return_type = 1;
-
- RETURN_IF_ERROR (demangle_ctor_dtor_name (dm));
- }
+ RETURN_IF_ERROR (demangle_ctor_dtor_name (dm));
else
return "Unexpected character in <unqualified-name>.";
return STATUS_ALLOCATION_FAILED;
}
- /* GCC encodes anonymous namespaces using a `_GLOBAL_[_.$]N.'
- followed by the source file name and some random characters.
- Unless we're in strict mode, decipher these names appropriately. */
- if (!flag_strict)
- {
- char *name = dyn_string_buf (identifier);
- int prefix_length = strlen (ANONYMOUS_NAMESPACE_PREFIX);
-
- /* Compare the first, fixed part. */
- if (strncmp (name, ANONYMOUS_NAMESPACE_PREFIX, prefix_length) == 0)
- {
- name += prefix_length;
- /* The next character might be a period, an underscore, or
- dollar sign, depending on the target architecture's
- assembler's capabilities. After that comes an `N'. */
- if ((*name == '.' || *name == '_' || *name == '$')
- && *(name + 1) == 'N')
- /* This looks like the anonymous namespace identifier.
- Replace it with something comprehensible. */
- dyn_string_copy_cstr (identifier, "(anonymous namespace)");
- }
- }
-
return STATUS_OK;
}
::= qu # ?
::= sz # sizeof
::= cv <type> # cast
- ::= v [0-9] <source-name> # vendor extended operator */
+ ::= vx <source-name> # vendor extended operator */
static status_t
demangle_operator_name (dm, short_name, num_args)
{ "pm", "->*" , 2 },
{ "pp", "++" , 1 },
{ "ps", "+" , 1 },
- { "pt", "->" , 2 },
{ "qu", "?" , 3 },
{ "rM", "%=" , 2 },
{ "rS", ">>=" , 2 },
DEMANGLE_TRACE ("operator-name", dm);
- /* Is this a vendor-extended operator? */
- if (c0 == 'v' && IS_DIGIT (c1))
+ /* Is this a vendor extended operator? */
+ if (c0 == 'v' && c1 == 'x')
{
- RETURN_IF_ERROR (result_append (dm, "operator "));
+ RETURN_IF_ERROR (result_append (dm, "operator"));
RETURN_IF_ERROR (demangle_source_name (dm));
*num_args = 0;
return STATUS_OK;
}
}
-/* Demangles and omits an <nv-offset>.
-
- <nv-offset> ::= <offset number> # non-virtual base override */
-
-static status_t
-demangle_nv_offset (dm)
- demangling_t dm;
-{
- dyn_string_t number;
- status_t status = STATUS_OK;
-
- DEMANGLE_TRACE ("h-offset", dm);
-
- /* Demangle the offset. */
- number = dyn_string_new (4);
- if (number == NULL)
- return STATUS_ALLOCATION_FAILED;
- demangle_number_literally (dm, number, 10, 1);
-
- /* Don't display the offset unless in verbose mode. */
- if (flag_verbose)
- {
- status = result_append (dm, " [nv:");
- if (STATUS_NO_ERROR (status))
- status = result_append_string (dm, number);
- if (STATUS_NO_ERROR (status))
- status = result_append_char (dm, ']');
- }
-
- /* Clean up. */
- dyn_string_delete (number);
- RETURN_IF_ERROR (status);
- return STATUS_OK;
-}
-
-/* Demangles and emits a <v-offset>.
-
- <v-offset> ::= <offset number> _ <virtual offset number>
- # virtual base override, with vcall offset */
-
-static status_t
-demangle_v_offset (dm)
- demangling_t dm;
-{
- dyn_string_t number;
- status_t status = STATUS_OK;
-
- DEMANGLE_TRACE ("v-offset", dm);
-
- /* Demangle the offset. */
- number = dyn_string_new (4);
- if (number == NULL)
- return STATUS_ALLOCATION_FAILED;
- demangle_number_literally (dm, number, 10, 1);
-
- /* Don't display the offset unless in verbose mode. */
- if (flag_verbose)
- {
- status = result_append (dm, " [v:");
- if (STATUS_NO_ERROR (status))
- status = result_append_string (dm, number);
- if (STATUS_NO_ERROR (status))
- result_append_char (dm, ',');
- }
- dyn_string_delete (number);
- RETURN_IF_ERROR (status);
-
- /* Demangle the separator. */
- RETURN_IF_ERROR (demangle_char (dm, '_'));
-
- /* Demangle the vcall offset. */
- number = dyn_string_new (4);
- if (number == NULL)
- return STATUS_ALLOCATION_FAILED;
- demangle_number_literally (dm, number, 10, 1);
-
- /* Don't display the vcall offset unless in verbose mode. */
- if (flag_verbose)
- {
- status = result_append_string (dm, number);
- if (STATUS_NO_ERROR (status))
- status = result_append_char (dm, ']');
- }
- dyn_string_delete (number);
- RETURN_IF_ERROR (status);
-
- return STATUS_OK;
-}
-
-/* Demangles and emits a <call-offset>.
-
- <call-offset> ::= h <nv-offset> _
- ::= v <v-offset> _ */
-
-static status_t
-demangle_call_offset (dm)
- demangling_t dm;
-{
- DEMANGLE_TRACE ("call-offset", dm);
-
- switch (peek_char (dm))
- {
- case 'h':
- advance_char (dm);
- /* Demangle the offset. */
- RETURN_IF_ERROR (demangle_nv_offset (dm));
- /* Demangle the separator. */
- RETURN_IF_ERROR (demangle_char (dm, '_'));
- break;
-
- case 'v':
- advance_char (dm);
- /* Demangle the offset. */
- RETURN_IF_ERROR (demangle_v_offset (dm));
- /* Demangle the separator. */
- RETURN_IF_ERROR (demangle_char (dm, '_'));
- break;
-
- default:
- return "Unrecognized <call-offset>.";
- }
-
- return STATUS_OK;
-}
-
/* Demangles and emits a <special-name>.
<special-name> ::= GV <object name> # Guard variable
+ ::= Th[n] <offset number> _ <base name> <base encoding>
+ # non-virtual base override thunk
+ ::= Tv[n] <offset number> _ <vcall offset number>
+ _ <base encoding>
+ # virtual base override thunk
::= TV <type> # virtual table
::= TT <type> # VTT
::= TI <type> # typeinfo structure
::= TS <type> # typeinfo name
- Other relevant productions include thunks:
-
- <special-name> ::= T <call-offset> <base encoding>
- # base is the nominal target function of thunk
-
- <special-name> ::= Tc <call-offset> <call-offset> <base encoding>
- # base is the nominal target function of thunk
- # first call-offset is 'this' adjustment
- # second call-offset is result adjustment
-
- where
-
- <call-offset> ::= h <nv-offset> _
- ::= v <v-offset> _
-
Also demangles the special g++ manglings,
- <special-name> ::= TC <type> <offset number> _ <base type>
+ <special-name> ::= CT <type> <offset number> _ <base type>
# construction vtable
::= TF <type> # typeinfo function (old ABI only)
::= TJ <type> # java Class structure */
/* Non-virtual thunk. */
advance_char (dm);
RETURN_IF_ERROR (result_append (dm, "non-virtual thunk"));
- RETURN_IF_ERROR (demangle_nv_offset (dm));
+ /* Demangle and emit the offset. */
+ number = dyn_string_new (4);
+ if (number == NULL)
+ return STATUS_ALLOCATION_FAILED;
+ demangle_number_literally (dm, number, 10, 1);
+ /* Don't display the offset unless in verbose mode. */
+ if (flag_verbose)
+ {
+ status = result_append_char (dm, ' ');
+ if (STATUS_NO_ERROR (status))
+ status = result_append_string (dm, number);
+ }
+ dyn_string_delete (number);
+ RETURN_IF_ERROR (status);
/* Demangle the separator. */
RETURN_IF_ERROR (demangle_char (dm, '_'));
/* Demangle and emit the target name and function type. */
case 'v':
/* Virtual thunk. */
advance_char (dm);
- RETURN_IF_ERROR (result_append (dm, "virtual thunk"));
- RETURN_IF_ERROR (demangle_v_offset (dm));
+ RETURN_IF_ERROR (result_append (dm, "virtual thunk "));
+ /* Demangle and emit the offset. */
+ number = dyn_string_new (4);
+ if (number == NULL)
+ return STATUS_ALLOCATION_FAILED;
+ demangle_number_literally (dm, number, 10, 1);
+ /* Don't display the offset unless in verbose mode. */
+ if (flag_verbose)
+ {
+ status = result_append_string (dm, number);
+ if (STATUS_NO_ERROR (status))
+ result_append_char (dm, ' ');
+ }
+ dyn_string_delete (number);
+ RETURN_IF_ERROR (status);
+ /* Demangle the separator. */
+ RETURN_IF_ERROR (demangle_char (dm, '_'));
+ /* Demangle and emit the vcall offset. */
+ number = dyn_string_new (4);
+ if (number == NULL)
+ return STATUS_ALLOCATION_FAILED;
+ demangle_number_literally (dm, number, 10, 1);
+ /* Don't display the vcall offset unless in verbose mode. */
+ if (flag_verbose)
+ {
+ status = result_append_string (dm, number);
+ if (STATUS_NO_ERROR (status))
+ status = result_append_char (dm, ' ');
+ }
+ dyn_string_delete (number);
+ RETURN_IF_ERROR (status);
/* Demangle the separator. */
RETURN_IF_ERROR (demangle_char (dm, '_'));
/* Demangle and emit the target function. */
- RETURN_IF_ERROR (result_append (dm, " to "));
- RETURN_IF_ERROR (demangle_encoding (dm));
- break;
-
- case 'c':
- /* Covariant return thunk. */
- advance_char (dm);
- RETURN_IF_ERROR (result_append (dm, "covariant return thunk"));
- RETURN_IF_ERROR (demangle_call_offset (dm));
- RETURN_IF_ERROR (demangle_call_offset (dm));
- /* Demangle and emit the target function. */
- RETURN_IF_ERROR (result_append (dm, " to "));
+ RETURN_IF_ERROR (result_append (dm, "to "));
RETURN_IF_ERROR (demangle_encoding (dm));
break;
::= 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 */
{
"in-charge",
"not-in-charge",
- "allocating"
+ "in-charge allocating",
+ "not-in-charge allocating"
};
static const char *const dtor_flavors[] =
{
{
/* A constructor name. Consume the C. */
advance_char (dm);
- if (peek_char (dm) < '1' || peek_char (dm) > '3')
+ if (peek_char (dm) < '1' || peek_char (dm) > '4')
return "Unrecognized constructor.";
RETURN_IF_ERROR (result_append_string (dm, dm->last_source_name));
/* Print the flavor of the constructor if in verbose mode. */
a pointer to data or pointer to function to construct the right
output syntax. C++'s pointer syntax is hairy.
- This function adds substitution candidates for every nested
- pointer/reference type it processes, including the outermost, final
- type, assuming the substitution starts at SUBSTITUTION_START in the
- demangling result. For example, if this function demangles
- `PP3Foo', it will add a substitution for `Foo', `Foo*', and
- `Foo**', in that order.
-
- *INSERT_POS is a quantity used internally, when this function calls
- itself recursively, to figure out where to insert pointer
- punctuation on the way up. On entry to this function, INSERT_POS
- should point to a temporary value, but that value need not be
- initialized.
-
<type> ::= P <type>
::= R <type>
::= <pointer-to-member-type>
<pointer-to-member-type> ::= M </class/ type> </member/ type> */
static status_t
-demangle_type_ptr (dm, insert_pos, substitution_start)
+demangle_type_ptr (dm)
demangling_t dm;
- int *insert_pos;
- int substitution_start;
{
char next;
status_t status;
- int is_substitution_candidate = 1;
+
+ /* Collect pointer symbols into this string. */
+ dyn_string_t symbols = dyn_string_new (10);
DEMANGLE_TRACE ("type*", dm);
+ if (symbols == NULL)
+ return STATUS_ALLOCATION_FAILED;
+
/* Scan forward, collecting pointers and references into symbols,
until we hit something else. Then emit the type. */
- next = peek_char (dm);
- if (next == 'P')
- {
- /* A pointer. Snarf the `P'. */
- advance_char (dm);
- /* Demangle the underlying type. */
- RETURN_IF_ERROR (demangle_type_ptr (dm, insert_pos,
- substitution_start));
- /* Insert an asterisk where we're told to; it doesn't
- necessarily go at the end. */
- RETURN_IF_ERROR (result_insert_char (dm, *insert_pos, '*'));
- /* The next (outermost) pointer or reference character should go
- after this one. */
- ++(*insert_pos);
- }
- else if (next == 'R')
- {
- /* A reference. Snarf the `R'. */
- advance_char (dm);
- /* Demangle the underlying type. */
- RETURN_IF_ERROR (demangle_type_ptr (dm, insert_pos,
- substitution_start));
- /* Insert an ampersand where we're told to; it doesn't
- necessarily go at the end. */
- RETURN_IF_ERROR (result_insert_char (dm, *insert_pos, '&'));
- /* The next (outermost) pointer or reference character should go
- after this one. */
- ++(*insert_pos);
- }
- else if (next == 'M')
+ while (1)
{
- /* A 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. */
- RETURN_IF_ERROR (result_push (dm));
- RETURN_IF_ERROR (demangle_type (dm));
- class_type = (dyn_string_t) result_pop (dm);
-
- if (peek_char (dm) == 'F')
- /* A pointer-to-member function. We want output along the
- lines of `void (C::*) (int, int)'. Demangle the function
- type, which would in this case give `void () (int, int)'
- and set *insert_pos to the spot between the first
- parentheses. */
- status = demangle_type_ptr (dm, insert_pos, substitution_start);
- else
- {
- /* A pointer-to-member variable. Demangle the type of the
- pointed-to member. */
+ next = peek_char (dm);
+ if (next == 'P')
+ {
+ if (!dyn_string_append_char (symbols, '*'))
+ return STATUS_ALLOCATION_FAILED;
+ advance_char (dm);
+ }
+ else if (next == 'R')
+ {
+ if (!dyn_string_append_char (symbols, '&'))
+ return STATUS_ALLOCATION_FAILED;
+ 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. */
+ RETURN_IF_ERROR (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 -- */
+ if (!dyn_string_prepend_cstr (symbols, "::*"))
+ return STATUS_ALLOCATION_FAILED;
+ if (!dyn_string_prepend (symbols, class_type))
+ return STATUS_ALLOCATION_FAILED;
+ 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. */
if (STATUS_NO_ERROR (status))
status = result_append_space (dm);
- /* The pointer-to-member notation (e.g. `C::*') follows the
- member's type. */
- *insert_pos = result_length (dm);
+ /* Add the pointer-to-member syntax, and other pointer and
+ reference symbols. */
+ if (STATUS_NO_ERROR (status))
+ status = 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);
+ status = result_append_char (dm, '(');
+ if (STATUS_NO_ERROR (status))
+ status = result_append_string (dm, symbols);
+ if (STATUS_NO_ERROR (status))
+ status = result_append_char (dm, ')');
+ dyn_string_delete (symbols);
+ RETURN_IF_ERROR (status);
- /* Build the pointer-to-member notation. */
- if (STATUS_NO_ERROR (status))
- status = result_insert (dm, *insert_pos, "::*");
- if (STATUS_NO_ERROR (status))
- status = result_insert_string (dm, *insert_pos, class_type);
- /* There may be additional levels of (pointer or reference)
- indirection in this type. If so, the `*' and `&' should be
- added after the pointer-to-member notation (e.g. `C::*&' for
- a reference to a pointer-to-member of class C). */
- *insert_pos += dyn_string_length (class_type) + 3;
+ RETURN_IF_ERROR (demangle_function_type (dm, position));
+ return STATUS_OK;
+ }
+ else
+ {
+ /* No more pointe or reference tokens. Finish up. */
+ status = demangle_type (dm);
- /* Clean up. */
- dyn_string_delete (class_type);
+ if (STATUS_NO_ERROR (status))
+ status = result_append_string (dm, symbols);
+ dyn_string_delete (symbols);
+ RETURN_IF_ERROR (status);
- RETURN_IF_ERROR (status);
- }
- else if (next == 'F')
- {
- /* Ooh, tricky, a pointer-to-function. When we demangle the
- function type, the return type should go at the very
- beginning. */
- *insert_pos = result_length (dm);
- /* The parentheses indicate this is a function pointer or
- reference type. */
- RETURN_IF_ERROR (result_append (dm, "()"));
- /* Now demangle the function type. The return type will be
- inserted before the `()', and the argument list will go after
- it. */
- RETURN_IF_ERROR (demangle_function_type (dm, insert_pos));
- /* We should now have something along the lines of
- `void () (int, int)'. The pointer or reference characters
- have to inside the first set of parentheses. *insert_pos has
- already been updated to point past the end of the return
- type. Move it one character over so it points inside the
- `()'. */
- ++(*insert_pos);
- }
- else
- {
- /* No more pointer or reference tokens; this is therefore a
- pointer to data. Finish up by demangling the underlying
- type. */
- RETURN_IF_ERROR (demangle_type (dm));
- /* The pointer or reference characters follow the underlying
- type, as in `int*&'. */
- *insert_pos = result_length (dm);
- /* Because of the production <type> ::= <substitution>,
- demangle_type will already have added the underlying type as
- a substitution candidate. Don't do it again. */
- is_substitution_candidate = 0;
+ RETURN_IF_ERROR (status);
+ return STATUS_OK;
+ }
}
-
- if (is_substitution_candidate)
- RETURN_IF_ERROR (substitution_add (dm, substitution_start, 0));
-
- return STATUS_OK;
}
/* Demangles and emits a <type>.
::= <array-type>
::= <pointer-to-member-type>
::= <template-param>
- ::= <template-template-param> <template-args>
::= <CV-qualifiers> <type>
::= P <type> # pointer-to
::= R <type> # reference-to
int start = substitution_start (dm);
char peek = peek_char (dm);
char peek_next;
- int encode_return_type = 0;
+ 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 insert_pos;
-
- /* A <type> can be a <substitution>; therefore, this <type> is a
- substitution candidate unless a special condition holds (see
- below). */
- int is_substitution_candidate = 1;
+ int template_parm = NOT_TEMPLATE_PARM;
DEMANGLE_TRACE ("type", dm);
/* A <class-enum-type> can start with a digit (a <source-name>), an
N (a <nested-name>), or a Z (a <local-name>). */
if (IS_DIGIT ((unsigned char) peek) || peek == 'N' || peek == 'Z')
- RETURN_IF_ERROR (demangle_class_enum_type (dm, &encode_return_type));
- /* Lower-case letters begin <builtin-type>s, except for `r', which
- denotes restrict. */
- else if (peek >= 'a' && peek <= 'z' && peek != 'r')
+ RETURN_IF_ERROR (demangle_class_enum_type (dm, &template_p));
+ else if (peek >= 'a' && peek <= 'z')
{
RETURN_IF_ERROR (demangle_builtin_type (dm));
- /* Built-in types are not substitution candidates. */
- is_substitution_candidate = 0;
+ is_builtin_type = 1;
}
else
switch (peek)
case 'r':
case 'V':
case 'K':
- /* CV-qualifiers (including restrict). We have to demangle
- them off to the side, since C++ syntax puts them in a funny
- place for qualified pointer and reference types. */
{
status_t status;
dyn_string_t cv_qualifiers = dyn_string_new (24);
break;
case 'T':
- /* It's either a <template-param> or a
- <template-template-param>. In either case, demangle the
- `T' token first. */
- RETURN_IF_ERROR (demangle_template_param (dm));
-
- /* Check for a template argument list; if one is found, it's a
- <template-template-param> ::= <template-param>
- ::= <substitution> */
- if (peek_char (dm) == 'I')
- {
- /* Add a substitution candidate. The template parameter
- `T' token is a substitution candidate by itself,
- without the template argument list. */
- RETURN_IF_ERROR (substitution_add (dm, start, encode_return_type));
-
- /* Now demangle the template argument list. */
- RETURN_IF_ERROR (demangle_template_args (dm));
- /* The entire type, including the template template
- parameter and its argument list, will be added as a
- substitution candidate below. */
- }
-
+ RETURN_IF_ERROR (demangle_template_param (dm, &template_parm));
break;
case 'S':
or underscore. */
peek_next = peek_char_next (dm);
if (IS_DIGIT (peek_next) || peek_next == '_')
- {
- RETURN_IF_ERROR (demangle_substitution (dm, &encode_return_type));
-
- /* The substituted name may have been a template name.
- Check if template arguments follow, and if so, demangle
- them. */
- if (peek_char (dm) == 'I')
- RETURN_IF_ERROR (demangle_template_args (dm));
- else
- /* A substitution token is not itself a substitution
- candidate. (However, if the substituted template is
- instantiated, the resulting type is.) */
- is_substitution_candidate = 0;
- }
+ RETURN_IF_ERROR (demangle_substitution (dm, &template_p,
+ &special_std_substitution));
else
- {
- /* Now some trickiness. We have a special substitution
- here. Often, the special substitution provides the
- name of a template that's subsequently instantiated,
- for instance `SaIcE' => std::allocator<char>. In these
- cases we need to add a substitution candidate for the
- entire <class-enum-type> and thus don't want to clear
- the is_substitution_candidate flag.
-
- However, it's possible that what we have here is a
- substitution token representing an entire type, such as
- `Ss' => std::string. In this case, we mustn't add a
- new substitution candidate for this substitution token.
- To detect this case, remember where the start of the
- substitution token is. */
- const char *next = dm->next;
- /* Now demangle the <class-enum-type>. */
- RETURN_IF_ERROR
- (demangle_class_enum_type (dm, &encode_return_type));
- /* If all that was just demangled is the two-character
- special substitution token, supress the addition of a
- new candidate for it. */
- if (dm->next == next + 2)
- is_substitution_candidate = 0;
- }
-
+ demangle_class_enum_type (dm, &template_p);
break;
case 'P':
case 'R':
case 'M':
- RETURN_IF_ERROR (demangle_type_ptr (dm, &insert_pos, start));
- /* demangle_type_ptr adds all applicable substitution
- candidates. */
- is_substitution_candidate = 0;
+ RETURN_IF_ERROR (demangle_type_ptr (dm));
break;
case 'C':
break;
case 'U':
- /* Vendor-extended type qualifier. */
+ /* Vendor extended type qualifier. */
advance_char (dm);
RETURN_IF_ERROR (demangle_source_name (dm));
RETURN_IF_ERROR (result_append_char (dm, ' '));
return "Unexpected character in <type>.";
}
- if (is_substitution_candidate)
+ /* Unqualified builin types are not substitution candidates. */
+ if (!is_builtin_type)
/* Add a new substitution for the type. If this type was a
<template-param>, pass its index since from the point of
- substitutions; a <template-param> token is a substitution
+ substitutions, a <template-param> token is a substitution
candidate distinct from the type that is substituted for it. */
- RETURN_IF_ERROR (substitution_add (dm, start, encode_return_type));
+ RETURN_IF_ERROR (substitution_add (dm, start, template_p, template_parm));
/* Pop off template argument lists added during mangling of this
type. */
}
}
-/* Demangles and emits a <function-type>. *FUNCTION_NAME_POS is the
+/* Demangles and emits a <function-type> 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;
- *FUNCTION_NAME_POS is updated to position past the end of the
- function's return type.
+ identifier, at which the function's return type will be inserted.
<function-type> ::= F [Y] <bare-function-type> E */
static status_t
demangle_function_type (dm, function_name_pos)
demangling_t dm;
- int *function_name_pos;
+ int function_name_pos;
{
DEMANGLE_TRACE ("function-type", dm);
RETURN_IF_ERROR (demangle_char (dm, 'F'));
static status_t
demangle_bare_function_type (dm, return_type_pos)
demangling_t dm;
- int *return_type_pos;
+ int return_type_pos;
{
/* Sequence is the index of the current function parameter, counting
from zero. The value -1 denotes the return type. */
/* Add a space to the end of the type. Insert the return
type where we've been asked to. */
- if (!dyn_string_append_space (return_type))
+ if (!dyn_string_append_space (return_type)
+ || !dyn_string_insert (result_string (dm), return_type_pos,
+ return_type))
status = STATUS_ALLOCATION_FAILED;
- if (STATUS_NO_ERROR (status))
- {
- if (!dyn_string_insert (result_string (dm), *return_type_pos,
- return_type))
- status = STATUS_ALLOCATION_FAILED;
- else
- *return_type_pos += dyn_string_length (return_type);
- }
dyn_string_delete (return_type);
RETURN_IF_ERROR (status);
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);
- else
{
- /* Separate parameter types by commas. */
- if (sequence > 0)
- RETURN_IF_ERROR (result_append (dm, ", "));
- /* Demangle the type. */
- RETURN_IF_ERROR (demangle_type (dm));
+ /* Consume the v. */
+ advance_char (dm);
+ continue;
}
+ /* Separate parameter types by commas. */
+ if (sequence > 0)
+ RETURN_IF_ERROR (result_append (dm, ", "));
+ /* Demangle the type. */
+ RETURN_IF_ERROR (demangle_type (dm));
}
++sequence;
}
RETURN_IF_ERROR (result_append_char (dm, ')'));
- /* We should have demangled at least one parameter type (which would
- be void, for a function that takes no parameters), plus the
- return type, if we were supposed to demangle that. */
- if (sequence == -1)
- return "Missing function return type.";
- else if (sequence == 0)
- return "Missing function parameter.";
-
return STATUS_OK;
}
-/* Demangles and emits a <class-enum-type>. *ENCODE_RETURN_TYPE is set to
+/* Demangles and emits a <class-enum-type>. *TEMPLATE_P is set to
non-zero if the type is a template-id, zero otherwise.
<class-enum-type> ::= <name> */
static status_t
-demangle_class_enum_type (dm, encode_return_type)
+demangle_class_enum_type (dm, template_p)
demangling_t dm;
- int *encode_return_type;
+ int *template_p;
{
DEMANGLE_TRACE ("class-enum-type", dm);
- RETURN_IF_ERROR (demangle_name (dm, encode_return_type));
+ RETURN_IF_ERROR (demangle_name (dm, template_p));
return STATUS_OK;
}
/* Demangles and emits an <array-type>.
- <array-type> ::= A [<dimension number>] _ <element type>
- ::= A <dimension expression> _ <element type> */
+ <array-type> ::= A [<dimension number>] _ <element type> */
static status_t
demangle_array_type (dm)
demangling_t dm;
{
- status_t status = STATUS_OK;
- dyn_string_t array_size = NULL;
- char peek;
+ status_t status;
+ dyn_string_t array_size = dyn_string_new (10);
+
+ if (array_size == NULL)
+ return STATUS_ALLOCATION_FAILED;
- RETURN_IF_ERROR (demangle_char (dm, 'A'));
+ status = demangle_char (dm, 'A');
/* Demangle the array size into array_size. */
- peek = peek_char (dm);
- if (peek == '_')
- /* Array bound is omitted. This is a C99-style VLA. */
- ;
- else if (IS_DIGIT (peek_char (dm)))
- {
- /* It looks like a constant array bound. */
- array_size = dyn_string_new (10);
- if (array_size == NULL)
- return STATUS_ALLOCATION_FAILED;
- status = demangle_number_literally (dm, array_size, 10, 0);
- }
- else
- {
- /* Anything is must be an expression for a nont-constant array
- bound. This happens if the array type occurs in a template
- and the array bound references a template parameter. */
- RETURN_IF_ERROR (result_push (dm));
- RETURN_IF_ERROR (demangle_expression (dm));
- array_size = (dyn_string_t) result_pop (dm);
- }
- /* array_size may have been allocated by now, so we can't use
- RETURN_IF_ERROR until it's been deallocated. */
+ if (STATUS_NO_ERROR (status))
+ status = demangle_number_literally (dm, array_size, 10, 0);
/* Demangle the base type of the array. */
if (STATUS_NO_ERROR (status))
/* Emit the array dimension syntax. */
if (STATUS_NO_ERROR (status))
status = result_append_char (dm, '[');
- if (STATUS_NO_ERROR (status) && array_size != NULL)
+ if (STATUS_NO_ERROR (status))
status = result_append_string (dm, array_size);
if (STATUS_NO_ERROR (status))
status = result_append_char (dm, ']');
- if (array_size != NULL)
- dyn_string_delete (array_size);
+ dyn_string_delete (array_size);
RETURN_IF_ERROR (status);
return STATUS_OK;
}
-/* Demangles and emits a <template-param>.
+/* Demangles and emits a <template-param>. The zero-indexed position
+ in the parameter list is placed in *TEMPLATE_PARM_NUMBER.
<template-param> ::= T_ # first template parameter
::= T <parameter-2 number> _ */
static status_t
-demangle_template_param (dm)
+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);
return "Template parameter number out of bounds.";
RETURN_IF_ERROR (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;
}
return STATUS_ALLOCATION_FAILED;
RETURN_IF_ERROR (demangle_char (dm, 'I'));
- RETURN_IF_ERROR (result_open_template_list (dm));
+ RETURN_IF_ERROR (result_append_char (dm, '<'));
do
{
string_list_t arg;
/* Expression. */
advance_char (dm);
RETURN_IF_ERROR (demangle_expression (dm));
- RETURN_IF_ERROR (demangle_char (dm, 'E'));
break;
default:
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));
+ RETURN_IF_ERROR (demangle_template_param (dm, &unused));
else if (peek == 'L')
{
/* Consume the `L'. */
}
/* Demangles and emits a <substitution>. Sets *TEMPLATE_P to non-zero
- if the substitution is the name of a template, zero otherwise.
+ 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.
<substitution> ::= S <seq-id> _
::= S_
::= So # ::std::basic_ostream<char,
std::char_traits<char> >
::= Sd # ::std::basic_iostream<char,
- std::char_traits<char> >
+ std::char_traits<char> >
*/
static status_t
-demangle_substitution (dm, template_p)
+demangle_substitution (dm, template_p, special_std_substitution)
demangling_t dm;
int *template_p;
+ int *special_std_substitution;
{
int seq_id;
int peek;
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. */
{
case 't':
RETURN_IF_ERROR (result_append (dm, "std"));
+ *special_std_substitution = 1;
break;
case 'a':
RETURN_IF_ERROR (demangle_char (dm, 'Z'));
RETURN_IF_ERROR (demangle_encoding (dm));
RETURN_IF_ERROR (demangle_char (dm, 'E'));
- RETURN_IF_ERROR (result_append (dm, "::"));
+ RETURN_IF_ERROR (result_append (dm, "'s "));
if (peek_char (dm) == 's')
{
else
{
int unused;
+ RETURN_IF_ERROR (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));
int suppress_first;
{
/* Output for <discriminator>s to the demangled name is completely
- suppressed if not in verbose mode. */
+ supressed if not in verbose mode. */
if (peek_char (dm) == '_')
{
dyn_string_t. On success, returns STATUS_OK. On failiure, returns
an error message, and the contents of RESULT are unchanged. */
-#ifdef IN_LIBGCC2
static status_t
cp_demangle_type (type_name, result)
const char* type_name;
return status;
}
+
+#ifdef IN_LIBGCC2
+
extern char *__cxa_demangle PARAMS ((const char *, char *, size_t *, int *));
/* ABI-mandated entry point in the C++ runtime library for performing
/* Non-zero if CHAR is a character than can occur in a mangled name. */
#define is_mangled_char(CHAR) \
- (IS_ALPHA (CHAR) || IS_DIGIT (CHAR) \
- || (CHAR) == '_' || (CHAR) == '.' || (CHAR) == '$')
+ (IS_ALPHA (CHAR) || IS_DIGIT (CHAR) || (CHAR) == '_')
/* The name of this program, as invoked. */
const char* program_name;
int exit_value;
{
fprintf (fp, "Usage: %s [options] [names ...]\n", program_name);
- fprintf (fp, "Options:\n");
+ 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");
tk_real
} type_kind_t;
+struct demangler_engine libiberty_demanglers[] =
+{
+ {
+ AUTO_DEMANGLING_STYLE_STRING,
+ auto_demangling,
+ "Automatic selection based on executable"
+ }
+ ,
+ {
+ GNU_DEMANGLING_STYLE_STRING,
+ gnu_demangling,
+ "GNU (g++) style demangling"
+ }
+ ,
+ {
+ LUCID_DEMANGLING_STYLE_STRING,
+ lucid_demangling,
+ "Lucid (lcc) style demangling"
+ }
+ ,
+ {
+ ARM_DEMANGLING_STYLE_STRING,
+ arm_demangling,
+ "ARM style demangling"
+ }
+ ,
+ {
+ HP_DEMANGLING_STYLE_STRING,
+ hp_demangling,
+ "HP (aCC) style demangling"
+ }
+ ,
+ {
+ EDG_DEMANGLING_STYLE_STRING,
+ edg_demangling,
+ "EDG style demangling"
+ }
+ ,
+ {
+ GNU_NEW_ABI_DEMANGLING_STYLE_STRING,
+ gnu_new_abi_demangling,
+ "GNU (g++) new-ABI-style demangling"
+ }
+ ,
+ {
+ NULL, unknown_demangling, NULL
+ }
+};
+
#define STRING_EMPTY(str) ((str) -> b == (str) -> p)
#define PREPEND_BLANK(str) {if (!STRING_EMPTY(str)) \
string_prepend(str, " ");}
/* Prototypes for local functions */
+static void
+delete_work_stuff PARAMS ((struct work_stuff *));
+
+static void
+delete_non_B_K_work_stuff PARAMS ((struct work_stuff *));
+
static char *
mop_up PARAMS ((struct work_stuff *, string *, int));
static void
squangle_mop_up PARAMS ((struct work_stuff *));
+static void
+work_stuff_copy_to_from PARAMS ((struct work_stuff *, struct work_stuff *));
+
#if 0
static int
demangle_method_args PARAMS ((struct work_stuff *, const char **, string *));
demangle_function_name PARAMS ((struct work_stuff *, const char **, string *,
const char *));
+static int
+iterate_demangle_function PARAMS ((struct work_stuff *,
+ const char **, string *, const char *));
+
static void
remember_type PARAMS ((struct work_stuff *, const char *, int));
}
}
else if (opname[0] == '_' && opname[1] == '_'
- && opname[2] >= 'a' && opname[2] <= 'z'
- && opname[3] >= 'a' && opname[3] <= 'z')
+ && islower((unsigned char)opname[2])
+ && islower((unsigned char)opname[3]))
{
if (opname[4] == '\0')
{
return ret;
}
+
/* Takes operator name as e.g. "++" and returns mangled
operator name (e.g. "postincrement_expr"), or NULL if not found.
return (0);
}
+/* Add a routine to set the demangling style to be sure it is valid and
+ allow for any demangler initialization that maybe necessary. */
+
+enum demangling_styles
+cplus_demangle_set_style (style)
+ enum demangling_styles style;
+{
+ struct demangler_engine *demangler = libiberty_demanglers;
+
+ for (; demangler->demangling_style != unknown_demangling; ++demangler)
+ if (style == demangler->demangling_style)
+ {
+ current_demangling_style = style;
+ return current_demangling_style;
+ }
+
+ return unknown_demangling;
+}
+
+/* Do string name to style translation */
+
+enum demangling_styles
+cplus_demangle_name_to_style (name)
+ const char *name;
+{
+ struct demangler_engine *demangler = libiberty_demanglers;
+
+ for (; demangler->demangling_style != unknown_demangling; ++demangler)
+ if (strcmp (name, demangler->demangling_style_name) == 0)
+ return demangler->demangling_style;
+
+ return unknown_demangling;
+}
+
/* char *cplus_demangle (const char *mangled, int options)
If MANGLED is a mangled function name produced by GNU C++, then
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);
}
}
-/* Clear out any mangled storage */
-static char *
-mop_up (work, declp, success)
- struct work_stuff *work;
- string *declp;
- int success;
+/* Copy the work state and storage. */
+
+static void
+work_stuff_copy_to_from (to, from)
+ struct work_stuff *to;
+ struct work_stuff *from;
{
- char *demangled = NULL;
+ int i;
+
+ delete_work_stuff (to);
+
+ /* Shallow-copy scalars. */
+ memcpy (to, from, sizeof (*to));
+
+ /* Deep-copy dynamic storage. */
+ if (from->typevec_size)
+ to->typevec
+ = (char **) xmalloc (from->typevec_size * sizeof (to->typevec[0]));
+
+ for (i = 0; i < from->ntypes; i++)
+ {
+ int len = strlen (from->typevec[i]) + 1;
+
+ to->typevec[i] = xmalloc (len);
+ memcpy (to->typevec[i], from->typevec[i], len);
+ }
+
+ if (from->ksize)
+ to->ktypevec
+ = (char **) xmalloc (from->ksize * sizeof (to->ktypevec[0]));
+
+ for (i = 0; i < from->numk; i++)
+ {
+ int len = strlen (from->ktypevec[i]) + 1;
+
+ to->ktypevec[i] = xmalloc (len);
+ memcpy (to->ktypevec[i], from->ktypevec[i], len);
+ }
+
+ if (from->bsize)
+ to->btypevec
+ = (char **) xmalloc (from->bsize * sizeof (to->btypevec[0]));
+
+ for (i = 0; i < from->numb; i++)
+ {
+ int len = strlen (from->btypevec[i]) + 1;
+
+ to->btypevec[i] = xmalloc (len);
+ memcpy (to->btypevec[i], from->btypevec[i], len);
+ }
+
+ if (from->ntmpl_args)
+ to->tmpl_argvec
+ = xmalloc (from->ntmpl_args * sizeof (to->tmpl_argvec[0]));
+ for (i = 0; i < from->ntmpl_args; i++)
+ {
+ int len = strlen (from->tmpl_argvec[i]) + 1;
+
+ to->tmpl_argvec[i] = xmalloc (len);
+ memcpy (to->tmpl_argvec[i], from->tmpl_argvec[i], len);
+ }
+
+ if (from->previous_argument)
+ {
+ to->previous_argument = (string*) xmalloc (sizeof (string));
+ string_init (to->previous_argument);
+ string_appends (to->previous_argument, from->previous_argument);
+ }
+}
+
+
+/* Delete dynamic stuff in work_stuff that is not to be re-used. */
+
+static void
+delete_non_B_K_work_stuff (work)
+ struct work_stuff *work;
+{
/* Discard the remembered types, if any. */
forget_types (work);
free ((char*) work->previous_argument);
work->previous_argument = NULL;
}
+}
+
+
+/* Delete all dynamic storage in work_stuff. */
+static void
+delete_work_stuff (work)
+ struct work_stuff *work;
+{
+ delete_non_B_K_work_stuff (work);
+ squangle_mop_up (work);
+}
+
+
+/* Clear out any mangled storage */
+
+static char *
+mop_up (work, declp, success)
+ struct work_stuff *work;
+ string *declp;
+ int success;
+{
+ char *demangled = NULL;
+
+ delete_non_B_K_work_stuff (work);
/* If demangling was successful, ensure that the demangled string is null
terminated and return it. Otherwise, free the demangling decl. */
break;
case '_':
- if (GNU_DEMANGLING && expect_return_type)
+ if ((AUTO_DEMANGLING || GNU_DEMANGLING) && expect_return_type)
{
/* Read the return type. */
string return_type;
break;
case 'H':
- if (GNU_DEMANGLING)
+ if (AUTO_DEMANGLING || GNU_DEMANGLING)
{
/* A G++ template function. Read the template arguments. */
success = demangle_template (work, mangled, declp, 0, 0,
{
int value;
+ /* By default, we let the number decide whether we shall consume an
+ underscore. */
+ int consume_following_underscore = 0;
+ int leave_following_underscore = 0;
+
success = 0;
/* Negative numbers are indicated with a leading `m'. */
string_appendn (s, "-", 1);
(*mangled)++;
}
+ else if (mangled[0][0] == '_' && mangled[0][1] == 'm')
+ {
+ /* Since consume_count_with_underscores does not handle the
+ `m'-prefix we must do it here, using consume_count and
+ adjusting underscores: we have to consume the underscore
+ matching the prepended one. */
+ consume_following_underscore = 1;
+ string_appendn (s, "-", 1);
+ (*mangled) += 2;
+ }
+ else if (**mangled == '_')
+ {
+ /* Do not consume a following underscore;
+ consume_following_underscore will consume what should be
+ consumed. */
+ leave_following_underscore = 1;
+ }
+
+ /* We must call consume_count if we expect to remove a trailing
+ underscore, since consume_count_with_underscores expects
+ the leading underscore (that we consumed) if it is to handle
+ multi-digit numbers. */
+ if (consume_following_underscore)
+ value = consume_count (mangled);
+ else
+ value = consume_count_with_underscores (mangled);
- /* Read the rest of the number. */
- value = consume_count_with_underscores (mangled);
if (value != -1)
{
char buf[INTBUF_SIZE];
sprintf (buf, "%d", value);
string_append (s, buf);
- /* If the next character is an underscore, skip it. */
- if (**mangled == '_')
+ /* Numbers not otherwise delimited, might have an underscore
+ appended as a delimeter, which we should skip.
+
+ ??? This used to always remove a following underscore, which
+ is wrong. If other (arbitrary) cases are followed by an
+ underscore, we need to do something more radical. */
+
+ if ((value > 9 || consume_following_underscore)
+ && ! leave_following_underscore
+ && **mangled == '_')
(*mangled)++;
/* All is well. */
template parameters (e.g. S) is placed in TRAWNAME if TRAWNAME is
non-NULL. If IS_TYPE is nonzero, this template is a type template,
not a function template. If both IS_TYPE and REMEMBER are nonzero,
- the tmeplate is remembered in the list of back-referenceable
+ the template is remembered in the list of back-referenceable
types. */
static int
return (success);
}
+
+/* Called when there's a "__" in the mangled name, with `scan' pointing to
+ the rightmost guess.
+
+ Find the correct "__"-sequence where the function name ends and the
+ signature starts, which is ambiguous with GNU mangling.
+ Call demangle_signature here, so we can make sure we found the right
+ one; *mangled will be consumed so caller will not make further calls to
+ demangle_signature. */
+
+static int
+iterate_demangle_function (work, mangled, declp, scan)
+ struct work_stuff *work;
+ const char **mangled;
+ string *declp;
+ const char *scan;
+{
+ const char *mangle_init = *mangled;
+ int success = 0;
+ string decl_init;
+ struct work_stuff work_init;
+
+ if (*(scan + 2) == '\0')
+ return 0;
+
+ /* Do not iterate for some demangling modes, or if there's only one
+ "__"-sequence. This is the normal case. */
+ if (ARM_DEMANGLING || LUCID_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING
+ || mystrstr (scan + 2, "__") == NULL)
+ {
+ demangle_function_name (work, mangled, declp, scan);
+ return 1;
+ }
+
+ /* Save state so we can restart if the guess at the correct "__" was
+ wrong. */
+ string_init (&decl_init);
+ string_appends (&decl_init, declp);
+ memset (&work_init, 0, sizeof work_init);
+ work_stuff_copy_to_from (&work_init, work);
+
+ /* Iterate over occurrences of __, allowing names and types to have a
+ "__" sequence in them. We must start with the first (not the last)
+ occurrence, since "__" most often occur between independent mangled
+ parts, hence starting at the last occurence inside a signature
+ might get us a "successful" demangling of the signature. */
+
+ while (scan[2])
+ {
+ demangle_function_name (work, mangled, declp, scan);
+ success = demangle_signature (work, mangled, declp);
+ if (success)
+ break;
+
+ /* Reset demangle state for the next round. */
+ *mangled = mangle_init;
+ string_clear (declp);
+ string_appends (declp, &decl_init);
+ work_stuff_copy_to_from (work, &work_init);
+
+ /* Leave this underscore-sequence. */
+ scan += 2;
+
+ /* Scan for the next "__" sequence. */
+ while (*scan && (scan[0] != '_' || scan[1] != '_'))
+ scan++;
+
+ /* Move to last "__" in this sequence. */
+ while (*scan && *scan == '_')
+ scan++;
+ scan -= 2;
+ }
+
+ /* Delete saved state. */
+ delete_work_stuff (&work_init);
+ string_delete (&decl_init);
+
+ return success;
+}
+
/*
LOCAL FUNCTION
DESCRIPTION
Consume and demangle the prefix of the mangled name.
+ While processing the function name root, arrange to call
+ demangle_signature if the root is ambiguous.
DECLP points to the string buffer into which demangled output is
placed. On entry, the buffer is empty. On exit it contains
success = 0;
}
else
- {
- const char *tmp;
-
- /* Look for the LAST occurrence of __, allowing names to
- have the '__' sequence embedded in them. */
- if (!(ARM_DEMANGLING || HP_DEMANGLING))
- {
- while ((tmp = mystrstr (scan + 2, "__")) != NULL)
- scan = tmp;
- }
- if (*(scan + 2) == '\0')
- success = 0;
- else
- demangle_function_name (work, mangled, declp, scan);
- }
+ return iterate_demangle_function (work, mangled, declp, scan);
}
}
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);
+ function name. Iterate over all "__":s until the right
+ one is found. */
+ return iterate_demangle_function (work, mangled, declp, scan);
}
else
{
success = 0;
break;
}
+
+ if (n > 10 && strncmp (*mangled, "_GLOBAL_", 8) == 0
+ && (*mangled)[9] == 'N'
+ && (*mangled)[8] == (*mangled)[10]
+ && strchr (cplus_markers, (*mangled)[8]))
+ {
+ /* A member of the anonymous namespace. There's information
+ about what identifier or filename it was keyed to, but
+ it's just there to make the mangled name unique; we just
+ step over it. */
+ string_append (declp, "{anonymous}");
+ (*mangled) += n;
+
+ /* Now p points to the marker before the N, so we need to
+ update it to the first marker after what we consumed. */
+ p = strpbrk (*mangled, cplus_markers);
+ break;
+ }
+
string_appendn (declp, *mangled, n);
(*mangled) += n;
}
success = demangle_template (work, mangled, declp, 0, 1, 1);
break;
default:
- success = demangle_fund_type (work, mangled, declp);
+ success = do_type (work, mangled, declp);
break;
}
if (success && **mangled != '\0')
int done = 0;
int success = 1;
char buf[10];
- int dec = 0;
+ unsigned int dec = 0;
string btype;
type_kind_t tk = tk_integral;
*mangled += min (strlen (*mangled), 2);
}
sscanf (buf, "%x", &dec);
- sprintf (buf, "int%i_t", dec);
+ sprintf (buf, "int%u_t", dec);
APPEND_BLANK (result);
string_append (result, buf);
break;
}
}
else if (declp->b[0] == '_' && declp->b[1] == '_'
- && declp->b[2] >= 'a' && declp->b[2] <= 'z'
- && declp->b[3] >= 'a' && declp->b[3] <= 'z')
+ && islower((unsigned char)declp->b[2])
+ && islower((unsigned char)declp->b[3]))
{
if (declp->b[4] == '\0')
{
static void demangle_it PARAMS ((char *));
static void usage PARAMS ((FILE *, int)) ATTRIBUTE_NORETURN;
static void fatal PARAMS ((const char *)) ATTRIBUTE_NORETURN;
+static void print_demangler_list PARAMS ((FILE *));
static void
demangle_it (mangled_name)
}
}
+static void
+print_demangler_list (stream)
+ FILE *stream;
+{
+ struct demangler_engine *demangler;
+
+ fprintf (stream, "{%s", libiberty_demanglers->demangling_style_name);
+
+ for (demangler = libiberty_demanglers + 1;
+ demangler->demangling_style != unknown_demangling;
+ ++demangler)
+ fprintf (stream, ",%s", demangler->demangling_style_name);
+
+ fprintf (stream, "}");
+}
+
static void
usage (stream, status)
FILE *stream;
int status;
{
fprintf (stream, "\
-Usage: %s [-_] [-n] [-s {gnu,lucid,arm,hp,edg}] [--strip-underscores]\n\
- [--no-strip-underscores] [--format={gnu,lucid,arm,hp,edg}]\n\
- [--help] [--version] [arg...]\n",
+Usage: %s [-_] [-n] [--strip-underscores] [--no-strip-underscores] \n",
program_name);
+
+ fprintf (stream, "\
+ [-s ");
+ print_demangler_list (stream);
+ fprintf (stream, "]\n");
+
+ fprintf (stream, "\
+ [--format ");
+ print_demangler_list (stream);
+ fprintf (stream, "]\n");
+
+ fprintf (stream, "\
+ [--help] [--version] [arg...]\n");
exit (status);
}
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. */
}
+/* 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
flags |= DMGL_JAVA;
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, "arm") == 0)
- {
- current_demangling_style = arm_demangling;
- }
- else if (strcmp (optarg, "hp") == 0)
- {
- current_demangling_style = hp_demangling;
- }
- else if (strcmp (optarg, "edg") == 0)
- {
- current_demangling_style = edg_demangling;
- }
- else
- {
- fprintf (stderr, "%s: unknown demangling style `%s'\n",
- program_name, optarg);
- return (1);
- }
+ {
+ enum demangling_styles style;
+
+ style = cplus_demangle_name_to_style (optarg);
+ if (style == unknown_demangling)
+ {
+ fprintf (stderr, "%s: unknown demangling style `%s'\n",
+ program_name, optarg);
+ return (1);
+ }
+ else
+ cplus_demangle_set_style (style);
+ }
break;
}
}
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
if (c == EOF)
break;
putchar (c);
+ fflush (stdout);
}
}