|  | /* Support for printing Fortran values for GDB, the GNU debugger. | 
|  |  | 
|  | Copyright (C) 1993-2022 Free Software Foundation, Inc. | 
|  |  | 
|  | Contributed by Motorola.  Adapted from the C definitions by Farooq Butt | 
|  | (fmbutt@engage.sps.mot.com), additionally worked over by Stan Shebs. | 
|  |  | 
|  | This file is part of GDB. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #include "defs.h" | 
|  | #include "annotate.h" | 
|  | #include "symtab.h" | 
|  | #include "gdbtypes.h" | 
|  | #include "expression.h" | 
|  | #include "value.h" | 
|  | #include "valprint.h" | 
|  | #include "language.h" | 
|  | #include "f-lang.h" | 
|  | #include "frame.h" | 
|  | #include "gdbcore.h" | 
|  | #include "command.h" | 
|  | #include "block.h" | 
|  | #include "dictionary.h" | 
|  | #include "cli/cli-style.h" | 
|  | #include "gdbarch.h" | 
|  | #include "f-array-walker.h" | 
|  |  | 
|  | static void f77_get_dynamic_length_of_aggregate (struct type *); | 
|  |  | 
|  | LONGEST | 
|  | f77_get_lowerbound (struct type *type) | 
|  | { | 
|  | if (type->bounds ()->low.kind () != PROP_CONST) | 
|  | error (_("Lower bound may not be '*' in F77")); | 
|  |  | 
|  | return type->bounds ()->low.const_val (); | 
|  | } | 
|  |  | 
|  | LONGEST | 
|  | f77_get_upperbound (struct type *type) | 
|  | { | 
|  | if (type->bounds ()->high.kind () != PROP_CONST) | 
|  | { | 
|  | /* We have an assumed size array on our hands.  Assume that | 
|  | upper_bound == lower_bound so that we show at least 1 element. | 
|  | If the user wants to see more elements, let him manually ask for 'em | 
|  | and we'll subscript the array and show him.  */ | 
|  |  | 
|  | return f77_get_lowerbound (type); | 
|  | } | 
|  |  | 
|  | return type->bounds ()->high.const_val (); | 
|  | } | 
|  |  | 
|  | /* Obtain F77 adjustable array dimensions.  */ | 
|  |  | 
|  | static void | 
|  | f77_get_dynamic_length_of_aggregate (struct type *type) | 
|  | { | 
|  | int upper_bound = -1; | 
|  | int lower_bound = 1; | 
|  |  | 
|  | /* Recursively go all the way down into a possibly multi-dimensional | 
|  | F77 array and get the bounds.  For simple arrays, this is pretty | 
|  | easy but when the bounds are dynamic, we must be very careful | 
|  | to add up all the lengths correctly.  Not doing this right | 
|  | will lead to horrendous-looking arrays in parameter lists. | 
|  |  | 
|  | This function also works for strings which behave very | 
|  | similarly to arrays.  */ | 
|  |  | 
|  | if (type->target_type ()->code () == TYPE_CODE_ARRAY | 
|  | || type->target_type ()->code () == TYPE_CODE_STRING) | 
|  | f77_get_dynamic_length_of_aggregate (type->target_type ()); | 
|  |  | 
|  | /* Recursion ends here, start setting up lengths.  */ | 
|  | lower_bound = f77_get_lowerbound (type); | 
|  | upper_bound = f77_get_upperbound (type); | 
|  |  | 
|  | /* Patch in a valid length value.  */ | 
|  | type->set_length ((upper_bound - lower_bound + 1) | 
|  | * check_typedef (type->target_type ())->length ()); | 
|  | } | 
|  |  | 
|  | /* Per-dimension statistics.  */ | 
|  |  | 
|  | struct dimension_stats | 
|  | { | 
|  | /* The type of the index used to address elements in the dimension.  */ | 
|  | struct type *index_type; | 
|  |  | 
|  | /* Total number of elements in the dimension, counted as we go.  */ | 
|  | int nelts; | 
|  | }; | 
|  |  | 
|  | /* A class used by FORTRAN_PRINT_ARRAY as a specialisation of the array | 
|  | walking template.  This specialisation prints Fortran arrays.  */ | 
|  |  | 
|  | class fortran_array_printer_impl : public fortran_array_walker_base_impl | 
|  | { | 
|  | public: | 
|  | /* Constructor.  TYPE is the array type being printed, ADDRESS is the | 
|  | address in target memory for the object of TYPE being printed.  VAL is | 
|  | the GDB value (of TYPE) being printed.  STREAM is where to print to, | 
|  | RECOURSE is passed through (and prevents infinite recursion), and | 
|  | OPTIONS are the printing control options.  */ | 
|  | explicit fortran_array_printer_impl (struct type *type, | 
|  | CORE_ADDR address, | 
|  | struct value *val, | 
|  | struct ui_file *stream, | 
|  | int recurse, | 
|  | const struct value_print_options *options) | 
|  | : m_elts (0), | 
|  | m_val (val), | 
|  | m_stream (stream), | 
|  | m_recurse (recurse), | 
|  | m_options (options), | 
|  | m_dimension (0), | 
|  | m_nrepeats (0), | 
|  | m_stats (0) | 
|  | { /* Nothing.  */ } | 
|  |  | 
|  | /* Called while iterating over the array bounds.  When SHOULD_CONTINUE is | 
|  | false then we must return false, as we have reached the end of the | 
|  | array bounds for this dimension.  However, we also return false if we | 
|  | have printed too many elements (after printing '...').  In all other | 
|  | cases, return true.  */ | 
|  | bool continue_walking (bool should_continue) | 
|  | { | 
|  | bool cont = should_continue && (m_elts < m_options->print_max); | 
|  | if (!cont && should_continue) | 
|  | gdb_puts ("...", m_stream); | 
|  | return cont; | 
|  | } | 
|  |  | 
|  | /* Called when we start iterating over a dimension.  If it's not the | 
|  | inner most dimension then print an opening '(' character.  */ | 
|  | void start_dimension (struct type *index_type, LONGEST nelts, bool inner_p) | 
|  | { | 
|  | size_t dim_indx = m_dimension++; | 
|  |  | 
|  | m_elt_type_prev = nullptr; | 
|  | if (m_stats.size () < m_dimension) | 
|  | { | 
|  | m_stats.resize (m_dimension); | 
|  | m_stats[dim_indx].index_type = index_type; | 
|  | m_stats[dim_indx].nelts = nelts; | 
|  | } | 
|  |  | 
|  | gdb_puts ("(", m_stream); | 
|  | } | 
|  |  | 
|  | /* Called when we finish processing a batch of items within a dimension | 
|  | of the array.  Depending on whether this is the inner most dimension | 
|  | or not we print different things, but this is all about adding | 
|  | separators between elements, and dimensions of the array.  */ | 
|  | void finish_dimension (bool inner_p, bool last_p) | 
|  | { | 
|  | gdb_puts (")", m_stream); | 
|  | if (!last_p) | 
|  | gdb_puts (" ", m_stream); | 
|  |  | 
|  | m_dimension--; | 
|  | } | 
|  |  | 
|  | /* Called when processing dimensions of the array other than the | 
|  | innermost one.  WALK_1 is the walker to normally call, ELT_TYPE is | 
|  | the type of the element being extracted, and ELT_OFF is the offset | 
|  | of the element from the start of array being walked, INDEX_TYPE | 
|  | and INDEX is the type and the value respectively of the element's | 
|  | index in the dimension currently being walked and LAST_P is true | 
|  | only when this is the last element that will be processed in this | 
|  | dimension.  */ | 
|  | void process_dimension (gdb::function_view<void (struct type *, | 
|  | int, bool)> walk_1, | 
|  | struct type *elt_type, LONGEST elt_off, | 
|  | LONGEST index, bool last_p) | 
|  | { | 
|  | size_t dim_indx = m_dimension - 1; | 
|  | struct type *elt_type_prev = m_elt_type_prev; | 
|  | LONGEST elt_off_prev = m_elt_off_prev; | 
|  | bool repeated = (m_options->repeat_count_threshold < UINT_MAX | 
|  | && elt_type_prev != nullptr | 
|  | && (m_elts + ((m_nrepeats + 1) | 
|  | * m_stats[dim_indx + 1].nelts) | 
|  | <= m_options->print_max) | 
|  | && dimension_contents_eq (m_val, elt_type, | 
|  | elt_off_prev, elt_off)); | 
|  |  | 
|  | if (repeated) | 
|  | m_nrepeats++; | 
|  | if (!repeated || last_p) | 
|  | { | 
|  | LONGEST nrepeats = m_nrepeats; | 
|  |  | 
|  | m_nrepeats = 0; | 
|  | if (nrepeats >= m_options->repeat_count_threshold) | 
|  | { | 
|  | annotate_elt_rep (nrepeats + 1); | 
|  | gdb_printf (m_stream, "%p[<repeats %s times>%p]", | 
|  | metadata_style.style ().ptr (), | 
|  | plongest (nrepeats + 1), | 
|  | nullptr); | 
|  | annotate_elt_rep_end (); | 
|  | if (!repeated) | 
|  | gdb_puts (" ", m_stream); | 
|  | m_elts += nrepeats * m_stats[dim_indx + 1].nelts; | 
|  | } | 
|  | else | 
|  | for (LONGEST i = nrepeats; i > 0; i--) | 
|  | { | 
|  | maybe_print_array_index (m_stats[dim_indx].index_type, | 
|  | index - nrepeats + repeated, | 
|  | m_stream, m_options); | 
|  | walk_1 (elt_type_prev, elt_off_prev, repeated && i == 1); | 
|  | } | 
|  |  | 
|  | if (!repeated) | 
|  | { | 
|  | /* We need to specially handle the case of hitting `print_max' | 
|  | exactly as recursing would cause lone `(...)' to be printed. | 
|  | And we need to print `...' by hand if the skipped element | 
|  | would be the last one processed, because the subsequent call | 
|  | to `continue_walking' from our caller won't do that.  */ | 
|  | if (m_elts < m_options->print_max) | 
|  | { | 
|  | maybe_print_array_index (m_stats[dim_indx].index_type, index, | 
|  | m_stream, m_options); | 
|  | walk_1 (elt_type, elt_off, last_p); | 
|  | nrepeats++; | 
|  | } | 
|  | else if (last_p) | 
|  | gdb_puts ("...", m_stream); | 
|  | } | 
|  | } | 
|  |  | 
|  | m_elt_type_prev = elt_type; | 
|  | m_elt_off_prev = elt_off; | 
|  | } | 
|  |  | 
|  | /* Called to process an element of ELT_TYPE at offset ELT_OFF from the | 
|  | start of the parent object, where INDEX is the value of the element's | 
|  | index in the dimension currently being walked and LAST_P is true only | 
|  | when this is the last element to be processed in this dimension.  */ | 
|  | void process_element (struct type *elt_type, LONGEST elt_off, | 
|  | LONGEST index, bool last_p) | 
|  | { | 
|  | size_t dim_indx = m_dimension - 1; | 
|  | struct type *elt_type_prev = m_elt_type_prev; | 
|  | LONGEST elt_off_prev = m_elt_off_prev; | 
|  | bool repeated = (m_options->repeat_count_threshold < UINT_MAX | 
|  | && elt_type_prev != nullptr | 
|  | && value_contents_eq (m_val, elt_off_prev, m_val, elt_off, | 
|  | elt_type->length ())); | 
|  |  | 
|  | if (repeated) | 
|  | m_nrepeats++; | 
|  | if (!repeated || last_p || m_elts + 1 == m_options->print_max) | 
|  | { | 
|  | LONGEST nrepeats = m_nrepeats; | 
|  | bool printed = false; | 
|  |  | 
|  | if (nrepeats != 0) | 
|  | { | 
|  | m_nrepeats = 0; | 
|  | if (nrepeats >= m_options->repeat_count_threshold) | 
|  | { | 
|  | annotate_elt_rep (nrepeats + 1); | 
|  | gdb_printf (m_stream, "%p[<repeats %s times>%p]", | 
|  | metadata_style.style ().ptr (), | 
|  | plongest (nrepeats + 1), | 
|  | nullptr); | 
|  | annotate_elt_rep_end (); | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Extract the element value from the parent value.  */ | 
|  | struct value *e_val | 
|  | = value_from_component (m_val, elt_type, elt_off_prev); | 
|  |  | 
|  | for (LONGEST i = nrepeats; i > 0; i--) | 
|  | { | 
|  | maybe_print_array_index (m_stats[dim_indx].index_type, | 
|  | index - i + 1, | 
|  | m_stream, m_options); | 
|  | common_val_print (e_val, m_stream, m_recurse, m_options, | 
|  | current_language); | 
|  | if (i > 1) | 
|  | gdb_puts (", ", m_stream); | 
|  | } | 
|  | } | 
|  | printed = true; | 
|  | } | 
|  |  | 
|  | if (!repeated) | 
|  | { | 
|  | /* Extract the element value from the parent value.  */ | 
|  | struct value *e_val | 
|  | = value_from_component (m_val, elt_type, elt_off); | 
|  |  | 
|  | if (printed) | 
|  | gdb_puts (", ", m_stream); | 
|  | maybe_print_array_index (m_stats[dim_indx].index_type, index, | 
|  | m_stream, m_options); | 
|  | common_val_print (e_val, m_stream, m_recurse, m_options, | 
|  | current_language); | 
|  | } | 
|  | if (!last_p) | 
|  | gdb_puts (", ", m_stream); | 
|  | } | 
|  |  | 
|  | m_elt_type_prev = elt_type; | 
|  | m_elt_off_prev = elt_off; | 
|  | ++m_elts; | 
|  | } | 
|  |  | 
|  | private: | 
|  | /* Called to compare two VAL elements of ELT_TYPE at offsets OFFSET1 | 
|  | and OFFSET2 each.  Handle subarrays recursively, because they may | 
|  | have been sliced and we do not want to compare any memory contents | 
|  | present between the slices requested.  */ | 
|  | bool | 
|  | dimension_contents_eq (const struct value *val, struct type *type, | 
|  | LONGEST offset1, LONGEST offset2) | 
|  | { | 
|  | if (type->code () == TYPE_CODE_ARRAY | 
|  | && type->target_type ()->code () != TYPE_CODE_CHAR) | 
|  | { | 
|  | /* Extract the range, and get lower and upper bounds.  */ | 
|  | struct type *range_type = check_typedef (type)->index_type (); | 
|  | LONGEST lowerbound, upperbound; | 
|  | if (!get_discrete_bounds (range_type, &lowerbound, &upperbound)) | 
|  | error ("failed to get range bounds"); | 
|  |  | 
|  | /* CALC is used to calculate the offsets for each element.  */ | 
|  | fortran_array_offset_calculator calc (type); | 
|  |  | 
|  | struct type *subarray_type = check_typedef (type->target_type ()); | 
|  | for (LONGEST i = lowerbound; i < upperbound + 1; i++) | 
|  | { | 
|  | /* Use the index and the stride to work out a new offset.  */ | 
|  | LONGEST index_offset = calc.index_offset (i); | 
|  |  | 
|  | if (!dimension_contents_eq (val, subarray_type, | 
|  | offset1 + index_offset, | 
|  | offset2 + index_offset)) | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  | else | 
|  | return value_contents_eq (val, offset1, val, offset2, | 
|  | type->length ()); | 
|  | } | 
|  |  | 
|  | /* The number of elements printed so far.  */ | 
|  | int m_elts; | 
|  |  | 
|  | /* The value from which we are printing elements.  */ | 
|  | struct value *m_val; | 
|  |  | 
|  | /* The stream we should print too.  */ | 
|  | struct ui_file *m_stream; | 
|  |  | 
|  | /* The recursion counter, passed through when we print each element.  */ | 
|  | int m_recurse; | 
|  |  | 
|  | /* The print control options.  Gives us the maximum number of elements to | 
|  | print, and is passed through to each element that we print.  */ | 
|  | const struct value_print_options *m_options = nullptr; | 
|  |  | 
|  | /* The number of the current dimension being handled.  */ | 
|  | LONGEST m_dimension; | 
|  |  | 
|  | /* The number of element repetitions in the current series.  */ | 
|  | LONGEST m_nrepeats; | 
|  |  | 
|  | /* The type and offset from M_VAL of the element handled in the previous | 
|  | iteration over the current dimension.  */ | 
|  | struct type *m_elt_type_prev; | 
|  | LONGEST m_elt_off_prev; | 
|  |  | 
|  | /* Per-dimension stats.  */ | 
|  | std::vector<struct dimension_stats> m_stats; | 
|  | }; | 
|  |  | 
|  | /* This function gets called to print a Fortran array.  */ | 
|  |  | 
|  | static void | 
|  | fortran_print_array (struct type *type, CORE_ADDR address, | 
|  | struct ui_file *stream, int recurse, | 
|  | const struct value *val, | 
|  | const struct value_print_options *options) | 
|  | { | 
|  | fortran_array_walker<fortran_array_printer_impl> p | 
|  | (type, address, (struct value *) val, stream, recurse, options); | 
|  | p.walk (); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Decorations for Fortran.  */ | 
|  |  | 
|  | static const struct generic_val_print_decorations f_decorations = | 
|  | { | 
|  | "(", | 
|  | ",", | 
|  | ")", | 
|  | ".TRUE.", | 
|  | ".FALSE.", | 
|  | "void", | 
|  | "{", | 
|  | "}" | 
|  | }; | 
|  |  | 
|  | /* See f-lang.h.  */ | 
|  |  | 
|  | void | 
|  | f_language::value_print_inner (struct value *val, struct ui_file *stream, | 
|  | int recurse, | 
|  | const struct value_print_options *options) const | 
|  | { | 
|  | struct type *type = check_typedef (value_type (val)); | 
|  | struct gdbarch *gdbarch = type->arch (); | 
|  | int printed_field = 0; /* Number of fields printed.  */ | 
|  | struct type *elttype; | 
|  | CORE_ADDR addr; | 
|  | int index; | 
|  | const gdb_byte *valaddr = value_contents_for_printing (val).data (); | 
|  | const CORE_ADDR address = value_address (val); | 
|  |  | 
|  | switch (type->code ()) | 
|  | { | 
|  | case TYPE_CODE_STRING: | 
|  | f77_get_dynamic_length_of_aggregate (type); | 
|  | printstr (stream, builtin_type (gdbarch)->builtin_char, valaddr, | 
|  | type->length (), NULL, 0, options); | 
|  | break; | 
|  |  | 
|  | case TYPE_CODE_ARRAY: | 
|  | if (type->target_type ()->code () != TYPE_CODE_CHAR) | 
|  | fortran_print_array (type, address, stream, recurse, val, options); | 
|  | else | 
|  | { | 
|  | struct type *ch_type = type->target_type (); | 
|  |  | 
|  | f77_get_dynamic_length_of_aggregate (type); | 
|  | printstr (stream, ch_type, valaddr, | 
|  | type->length () / ch_type->length (), NULL, 0, | 
|  | options); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case TYPE_CODE_PTR: | 
|  | if (options->format && options->format != 's') | 
|  | { | 
|  | value_print_scalar_formatted (val, options, 0, stream); | 
|  | break; | 
|  | } | 
|  | else | 
|  | { | 
|  | int want_space = 0; | 
|  |  | 
|  | addr = unpack_pointer (type, valaddr); | 
|  | elttype = check_typedef (type->target_type ()); | 
|  |  | 
|  | if (elttype->code () == TYPE_CODE_FUNC) | 
|  | { | 
|  | /* Try to print what function it points to.  */ | 
|  | print_function_pointer_address (options, gdbarch, addr, stream); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (options->symbol_print) | 
|  | want_space = print_address_demangle (options, gdbarch, addr, | 
|  | stream, demangle); | 
|  | else if (options->addressprint && options->format != 's') | 
|  | { | 
|  | gdb_puts (paddress (gdbarch, addr), stream); | 
|  | want_space = 1; | 
|  | } | 
|  |  | 
|  | /* For a pointer to char or unsigned char, also print the string | 
|  | pointed to, unless pointer is null.  */ | 
|  | if (elttype->length () == 1 | 
|  | && elttype->code () == TYPE_CODE_INT | 
|  | && (options->format == 0 || options->format == 's') | 
|  | && addr != 0) | 
|  | { | 
|  | if (want_space) | 
|  | gdb_puts (" ", stream); | 
|  | val_print_string (type->target_type (), NULL, addr, -1, | 
|  | stream, options); | 
|  | } | 
|  | return; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case TYPE_CODE_STRUCT: | 
|  | case TYPE_CODE_UNION: | 
|  | case TYPE_CODE_NAMELIST: | 
|  | /* Starting from the Fortran 90 standard, Fortran supports derived | 
|  | types.  */ | 
|  | gdb_printf (stream, "( "); | 
|  | for (index = 0; index < type->num_fields (); index++) | 
|  | { | 
|  | struct type *field_type | 
|  | = check_typedef (type->field (index).type ()); | 
|  |  | 
|  | if (field_type->code () != TYPE_CODE_FUNC) | 
|  | { | 
|  | const char *field_name = type->field (index).name (); | 
|  | struct value *field; | 
|  |  | 
|  | if (type->code () == TYPE_CODE_NAMELIST) | 
|  | { | 
|  | /* While printing namelist items, fetch the appropriate | 
|  | value field before printing its value.  */ | 
|  | struct block_symbol sym | 
|  | = lookup_symbol (field_name, get_selected_block (nullptr), | 
|  | VAR_DOMAIN, nullptr); | 
|  | if (sym.symbol == nullptr) | 
|  | error (_("failed to find symbol for name list component %s"), | 
|  | field_name); | 
|  | field = value_of_variable (sym.symbol, sym.block); | 
|  | } | 
|  | else | 
|  | field = value_field (val, index); | 
|  |  | 
|  | if (printed_field > 0) | 
|  | gdb_puts (", ", stream); | 
|  |  | 
|  | if (field_name != NULL) | 
|  | { | 
|  | fputs_styled (field_name, variable_name_style.style (), | 
|  | stream); | 
|  | gdb_puts (" = ", stream); | 
|  | } | 
|  |  | 
|  | common_val_print (field, stream, recurse + 1, | 
|  | options, current_language); | 
|  |  | 
|  | ++printed_field; | 
|  | } | 
|  | } | 
|  | gdb_printf (stream, " )"); | 
|  | break; | 
|  |  | 
|  | case TYPE_CODE_BOOL: | 
|  | if (options->format || options->output_format) | 
|  | { | 
|  | struct value_print_options opts = *options; | 
|  | opts.format = (options->format ? options->format | 
|  | : options->output_format); | 
|  | value_print_scalar_formatted (val, &opts, 0, stream); | 
|  | } | 
|  | else | 
|  | { | 
|  | LONGEST longval = value_as_long (val); | 
|  | /* The Fortran standard doesn't specify how logical types are | 
|  | represented.  Different compilers use different non zero | 
|  | values to represent logical true.  */ | 
|  | if (longval == 0) | 
|  | gdb_puts (f_decorations.false_name, stream); | 
|  | else | 
|  | gdb_puts (f_decorations.true_name, stream); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case TYPE_CODE_INT: | 
|  | case TYPE_CODE_REF: | 
|  | case TYPE_CODE_FUNC: | 
|  | case TYPE_CODE_FLAGS: | 
|  | case TYPE_CODE_FLT: | 
|  | case TYPE_CODE_VOID: | 
|  | case TYPE_CODE_ERROR: | 
|  | case TYPE_CODE_RANGE: | 
|  | case TYPE_CODE_UNDEF: | 
|  | case TYPE_CODE_COMPLEX: | 
|  | case TYPE_CODE_CHAR: | 
|  | default: | 
|  | generic_value_print (val, stream, recurse, options, &f_decorations); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | info_common_command_for_block (const struct block *block, const char *comname, | 
|  | int *any_printed) | 
|  | { | 
|  | struct block_iterator iter; | 
|  | struct symbol *sym; | 
|  | struct value_print_options opts; | 
|  |  | 
|  | get_user_print_options (&opts); | 
|  |  | 
|  | ALL_BLOCK_SYMBOLS (block, iter, sym) | 
|  | if (sym->domain () == COMMON_BLOCK_DOMAIN) | 
|  | { | 
|  | const struct common_block *common = sym->value_common_block (); | 
|  | size_t index; | 
|  |  | 
|  | gdb_assert (sym->aclass () == LOC_COMMON_BLOCK); | 
|  |  | 
|  | if (comname && (!sym->linkage_name () | 
|  | || strcmp (comname, sym->linkage_name ()) != 0)) | 
|  | continue; | 
|  |  | 
|  | if (*any_printed) | 
|  | gdb_putc ('\n'); | 
|  | else | 
|  | *any_printed = 1; | 
|  | if (sym->print_name ()) | 
|  | gdb_printf (_("Contents of F77 COMMON block '%s':\n"), | 
|  | sym->print_name ()); | 
|  | else | 
|  | gdb_printf (_("Contents of blank COMMON block:\n")); | 
|  |  | 
|  | for (index = 0; index < common->n_entries; index++) | 
|  | { | 
|  | struct value *val = NULL; | 
|  |  | 
|  | gdb_printf ("%s = ", | 
|  | common->contents[index]->print_name ()); | 
|  |  | 
|  | try | 
|  | { | 
|  | val = value_of_variable (common->contents[index], block); | 
|  | value_print (val, gdb_stdout, &opts); | 
|  | } | 
|  |  | 
|  | catch (const gdb_exception_error &except) | 
|  | { | 
|  | fprintf_styled (gdb_stdout, metadata_style.style (), | 
|  | "<error reading variable: %s>", | 
|  | except.what ()); | 
|  | } | 
|  |  | 
|  | gdb_putc ('\n'); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* This function is used to print out the values in a given COMMON | 
|  | block.  It will always use the most local common block of the | 
|  | given name.  */ | 
|  |  | 
|  | static void | 
|  | info_common_command (const char *comname, int from_tty) | 
|  | { | 
|  | frame_info_ptr fi; | 
|  | const struct block *block; | 
|  | int values_printed = 0; | 
|  |  | 
|  | /* We have been told to display the contents of F77 COMMON | 
|  | block supposedly visible in this function.  Let us | 
|  | first make sure that it is visible and if so, let | 
|  | us display its contents.  */ | 
|  |  | 
|  | fi = get_selected_frame (_("No frame selected")); | 
|  |  | 
|  | /* The following is generally ripped off from stack.c's routine | 
|  | print_frame_info().  */ | 
|  |  | 
|  | block = get_frame_block (fi, 0); | 
|  | if (block == NULL) | 
|  | { | 
|  | gdb_printf (_("No symbol table info available.\n")); | 
|  | return; | 
|  | } | 
|  |  | 
|  | while (block) | 
|  | { | 
|  | info_common_command_for_block (block, comname, &values_printed); | 
|  | /* After handling the function's top-level block, stop.  Don't | 
|  | continue to its superblock, the block of per-file symbols.  */ | 
|  | if (block->function ()) | 
|  | break; | 
|  | block = block->superblock (); | 
|  | } | 
|  |  | 
|  | if (!values_printed) | 
|  | { | 
|  | if (comname) | 
|  | gdb_printf (_("No common block '%s'.\n"), comname); | 
|  | else | 
|  | gdb_printf (_("No common blocks.\n")); | 
|  | } | 
|  | } | 
|  |  | 
|  | void _initialize_f_valprint (); | 
|  | void | 
|  | _initialize_f_valprint () | 
|  | { | 
|  | add_info ("common", info_common_command, | 
|  | _("Print out the values contained in a Fortran COMMON block.")); | 
|  | } |