| /* GDB/Scheme pretty-printing. | 
 |  | 
 |    Copyright (C) 2008-2024 Free Software Foundation, Inc. | 
 |  | 
 |    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/>.  */ | 
 |  | 
 | /* See README file in this directory for implementation notes, coding | 
 |    conventions, et.al.  */ | 
 |  | 
 | #include "top.h" | 
 | #include "charset.h" | 
 | #include "symtab.h" | 
 | #include "language.h" | 
 | #include "objfiles.h" | 
 | #include "value.h" | 
 | #include "valprint.h" | 
 | #include "guile-internal.h" | 
 |  | 
 | /* Return type of print_string_repr.  */ | 
 |  | 
 | enum guile_string_repr_result | 
 | { | 
 |   /* The string method returned None.  */ | 
 |   STRING_REPR_NONE, | 
 |   /* The string method had an error.  */ | 
 |   STRING_REPR_ERROR, | 
 |   /* Everything ok.  */ | 
 |   STRING_REPR_OK | 
 | }; | 
 |  | 
 | /* Display hints.  */ | 
 |  | 
 | enum display_hint | 
 | { | 
 |   /* No display hint.  */ | 
 |   HINT_NONE, | 
 |   /* The display hint has a bad value.  */ | 
 |   HINT_ERROR, | 
 |   /* Print as an array.  */ | 
 |   HINT_ARRAY, | 
 |   /* Print as a map.  */ | 
 |   HINT_MAP, | 
 |   /* Print as a string.  */ | 
 |   HINT_STRING | 
 | }; | 
 |  | 
 | /* The <gdb:pretty-printer> smob.  */ | 
 |  | 
 | struct pretty_printer_smob | 
 | { | 
 |   /* This must appear first.  */ | 
 |   gdb_smob base; | 
 |  | 
 |   /* A string representing the name of the printer.  */ | 
 |   SCM name; | 
 |  | 
 |   /* A boolean indicating whether the printer is enabled.  */ | 
 |   SCM enabled; | 
 |  | 
 |   /* A procedure called to look up the printer for the given value. | 
 |      The procedure is called as (lookup gdb:pretty-printer value). | 
 |      The result should either be a gdb:pretty-printer object that will print | 
 |      the value, or #f if the value is not recognized.  */      | 
 |   SCM lookup; | 
 |  | 
 |   /* Note: Attaching subprinters to this smob is left to Scheme.  */ | 
 | }; | 
 |  | 
 | /* The <gdb:pretty-printer-worker> smob.  */ | 
 |  | 
 | struct pretty_printer_worker_smob | 
 | { | 
 |   /* This must appear first.  */ | 
 |   gdb_smob base; | 
 |  | 
 |   /* Either #f or one of the supported display hints: map, array, string. | 
 |      If neither of those then the display hint is ignored (treated as #f).  */ | 
 |   SCM display_hint; | 
 |  | 
 |   /* A procedure called to pretty-print the value. | 
 |      (lambda (printer) ...) -> string | <gdb:lazy-string> | <gdb:value>  */ | 
 |   SCM to_string; | 
 |  | 
 |   /* A procedure called to print children of the value. | 
 |      (lambda (printer) ...) -> <gdb:iterator> | 
 |      The iterator returns a pair for each iteration: (name . value), | 
 |      where "value" can have the same types as to_string.  */ | 
 |   SCM children; | 
 | }; | 
 |  | 
 | static const char pretty_printer_smob_name[] = | 
 |   "gdb:pretty-printer"; | 
 | static const char pretty_printer_worker_smob_name[] = | 
 |   "gdb:pretty-printer-worker"; | 
 |  | 
 | /* The tag Guile knows the pretty-printer smobs by.  */ | 
 | static scm_t_bits pretty_printer_smob_tag; | 
 | static scm_t_bits pretty_printer_worker_smob_tag; | 
 |  | 
 | /* The global pretty-printer list.  */ | 
 | static SCM pretty_printer_list; | 
 |  | 
 | /* gdb:pp-type-error.  */ | 
 | static SCM pp_type_error_symbol; | 
 |  | 
 | /* Pretty-printer display hints are specified by strings.  */ | 
 | static SCM ppscm_map_string; | 
 | static SCM ppscm_array_string; | 
 | static SCM ppscm_string_string; | 
 |  | 
 | /* Administrivia for pretty-printer matcher smobs.  */ | 
 |  | 
 | /* The smob "print" function for <gdb:pretty-printer>.  */ | 
 |  | 
 | static int | 
 | ppscm_print_pretty_printer_smob (SCM self, SCM port, scm_print_state *pstate) | 
 | { | 
 |   pretty_printer_smob *pp_smob = (pretty_printer_smob *) SCM_SMOB_DATA (self); | 
 |  | 
 |   gdbscm_printf (port, "#<%s ", pretty_printer_smob_name); | 
 |   scm_write (pp_smob->name, port); | 
 |   scm_puts (gdbscm_is_true (pp_smob->enabled) ? " enabled" : " disabled", | 
 | 	    port); | 
 |   scm_puts (">", port); | 
 |  | 
 |   scm_remember_upto_here_1 (self); | 
 |  | 
 |   /* Non-zero means success.  */ | 
 |   return 1; | 
 | } | 
 |  | 
 | /* (make-pretty-printer string procedure) -> <gdb:pretty-printer> */ | 
 |  | 
 | static SCM | 
 | gdbscm_make_pretty_printer (SCM name, SCM lookup) | 
 | { | 
 |   pretty_printer_smob *pp_smob = (pretty_printer_smob *) | 
 |     scm_gc_malloc (sizeof (pretty_printer_smob), | 
 | 		   pretty_printer_smob_name); | 
 |   SCM smob; | 
 |  | 
 |   SCM_ASSERT_TYPE (scm_is_string (name), name, SCM_ARG1, FUNC_NAME, | 
 | 		   _("string")); | 
 |   SCM_ASSERT_TYPE (gdbscm_is_procedure (lookup), lookup, SCM_ARG2, FUNC_NAME, | 
 | 		   _("procedure")); | 
 |  | 
 |   pp_smob->name = name; | 
 |   pp_smob->lookup = lookup; | 
 |   pp_smob->enabled = SCM_BOOL_T; | 
 |   smob = scm_new_smob (pretty_printer_smob_tag, (scm_t_bits) pp_smob); | 
 |   gdbscm_init_gsmob (&pp_smob->base); | 
 |  | 
 |   return smob; | 
 | } | 
 |  | 
 | /* Return non-zero if SCM is a <gdb:pretty-printer> object.  */ | 
 |  | 
 | static int | 
 | ppscm_is_pretty_printer (SCM scm) | 
 | { | 
 |   return SCM_SMOB_PREDICATE (pretty_printer_smob_tag, scm); | 
 | } | 
 |  | 
 | /* (pretty-printer? object) -> boolean */ | 
 |  | 
 | static SCM | 
 | gdbscm_pretty_printer_p (SCM scm) | 
 | { | 
 |   return scm_from_bool (ppscm_is_pretty_printer (scm)); | 
 | } | 
 |  | 
 | /* Returns the <gdb:pretty-printer> object in SELF. | 
 |    Throws an exception if SELF is not a <gdb:pretty-printer> object.  */ | 
 |  | 
 | static SCM | 
 | ppscm_get_pretty_printer_arg_unsafe (SCM self, int arg_pos, | 
 | 				     const char *func_name) | 
 | { | 
 |   SCM_ASSERT_TYPE (ppscm_is_pretty_printer (self), self, arg_pos, func_name, | 
 | 		   pretty_printer_smob_name); | 
 |  | 
 |   return self; | 
 | } | 
 |  | 
 | /* Returns a pointer to the pretty-printer smob of SELF. | 
 |    Throws an exception if SELF is not a <gdb:pretty-printer> object.  */ | 
 |  | 
 | static pretty_printer_smob * | 
 | ppscm_get_pretty_printer_smob_arg_unsafe (SCM self, int arg_pos, | 
 | 					  const char *func_name) | 
 | { | 
 |   SCM pp_scm = ppscm_get_pretty_printer_arg_unsafe (self, arg_pos, func_name); | 
 |   pretty_printer_smob *pp_smob | 
 |     = (pretty_printer_smob *) SCM_SMOB_DATA (pp_scm); | 
 |  | 
 |   return pp_smob; | 
 | } | 
 |  | 
 | /* Pretty-printer methods.  */ | 
 |  | 
 | /* (pretty-printer-enabled? <gdb:pretty-printer>) -> boolean */ | 
 |  | 
 | static SCM | 
 | gdbscm_pretty_printer_enabled_p (SCM self) | 
 | { | 
 |   pretty_printer_smob *pp_smob | 
 |     = ppscm_get_pretty_printer_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME); | 
 |  | 
 |   return pp_smob->enabled; | 
 | } | 
 |  | 
 | /* (set-pretty-printer-enabled! <gdb:pretty-printer> boolean) | 
 |      -> unspecified */ | 
 |  | 
 | static SCM | 
 | gdbscm_set_pretty_printer_enabled_x (SCM self, SCM enabled) | 
 | { | 
 |   pretty_printer_smob *pp_smob | 
 |     = ppscm_get_pretty_printer_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME); | 
 |  | 
 |   pp_smob->enabled = scm_from_bool (gdbscm_is_true (enabled)); | 
 |  | 
 |   return SCM_UNSPECIFIED; | 
 | } | 
 |  | 
 | /* (pretty-printers) -> list | 
 |    Returns the list of global pretty-printers.  */ | 
 |  | 
 | static SCM | 
 | gdbscm_pretty_printers (void) | 
 | { | 
 |   return pretty_printer_list; | 
 | } | 
 |  | 
 | /* (set-pretty-printers! list) -> unspecified | 
 |    Set the global pretty-printers list.  */ | 
 |  | 
 | static SCM | 
 | gdbscm_set_pretty_printers_x (SCM printers) | 
 | { | 
 |   SCM_ASSERT_TYPE (gdbscm_is_true (scm_list_p (printers)), printers, | 
 | 		   SCM_ARG1, FUNC_NAME, _("list")); | 
 |  | 
 |   pretty_printer_list = printers; | 
 |  | 
 |   return SCM_UNSPECIFIED; | 
 | } | 
 |  | 
 | /* Administrivia for pretty-printer-worker smobs. | 
 |    These are created when a matcher recognizes a value.  */ | 
 |  | 
 | /* The smob "print" function for <gdb:pretty-printer-worker>.  */ | 
 |  | 
 | static int | 
 | ppscm_print_pretty_printer_worker_smob (SCM self, SCM port, | 
 | 					scm_print_state *pstate) | 
 | { | 
 |   pretty_printer_worker_smob *w_smob | 
 |     = (pretty_printer_worker_smob *) SCM_SMOB_DATA (self); | 
 |  | 
 |   gdbscm_printf (port, "#<%s ", pretty_printer_worker_smob_name); | 
 |   scm_write (w_smob->display_hint, port); | 
 |   scm_puts (" ", port); | 
 |   scm_write (w_smob->to_string, port); | 
 |   scm_puts (" ", port); | 
 |   scm_write (w_smob->children, port); | 
 |   scm_puts (">", port); | 
 |  | 
 |   scm_remember_upto_here_1 (self); | 
 |  | 
 |   /* Non-zero means success.  */ | 
 |   return 1; | 
 | } | 
 |  | 
 | /* (make-pretty-printer-worker string procedure procedure) | 
 |      -> <gdb:pretty-printer-worker> */ | 
 |  | 
 | static SCM | 
 | gdbscm_make_pretty_printer_worker (SCM display_hint, SCM to_string, | 
 | 				   SCM children) | 
 | { | 
 |   pretty_printer_worker_smob *w_smob = (pretty_printer_worker_smob *) | 
 |     scm_gc_malloc (sizeof (pretty_printer_worker_smob), | 
 | 		   pretty_printer_worker_smob_name); | 
 |   SCM w_scm; | 
 |  | 
 |   w_smob->display_hint = display_hint; | 
 |   w_smob->to_string = to_string; | 
 |   w_smob->children = children; | 
 |   w_scm = scm_new_smob (pretty_printer_worker_smob_tag, (scm_t_bits) w_smob); | 
 |   gdbscm_init_gsmob (&w_smob->base); | 
 |   return w_scm; | 
 | } | 
 |  | 
 | /* Return non-zero if SCM is a <gdb:pretty-printer-worker> object.  */ | 
 |  | 
 | static int | 
 | ppscm_is_pretty_printer_worker (SCM scm) | 
 | { | 
 |   return SCM_SMOB_PREDICATE (pretty_printer_worker_smob_tag, scm); | 
 | } | 
 |  | 
 | /* (pretty-printer-worker? object) -> boolean */ | 
 |  | 
 | static SCM | 
 | gdbscm_pretty_printer_worker_p (SCM scm) | 
 | { | 
 |   return scm_from_bool (ppscm_is_pretty_printer_worker (scm)); | 
 | } | 
 |  | 
 | /* Helper function to create a <gdb:exception> object indicating that the | 
 |    type of some value returned from a pretty-printer is invalid.  */ | 
 |  | 
 | static SCM | 
 | ppscm_make_pp_type_error_exception (const char *message, SCM object) | 
 | { | 
 |   std::string msg = string_printf ("%s: ~S", message); | 
 |   return gdbscm_make_error (pp_type_error_symbol, | 
 | 			    NULL /* func */, msg.c_str (), | 
 | 			    scm_list_1 (object), scm_list_1 (object)); | 
 | } | 
 |  | 
 | /* Print MESSAGE as an exception (meaning it is controlled by | 
 |    "guile print-stack"). | 
 |    Called from the printer code when the Scheme code returns an invalid type | 
 |    for something.  */ | 
 |  | 
 | static void | 
 | ppscm_print_pp_type_error (const char *message, SCM object) | 
 | { | 
 |   SCM exception = ppscm_make_pp_type_error_exception (message, object); | 
 |  | 
 |   gdbscm_print_gdb_exception (SCM_BOOL_F, exception); | 
 | } | 
 |  | 
 | /* Helper function for find_pretty_printer which iterates over a list, | 
 |    calls each function and inspects output.  This will return a | 
 |    <gdb:pretty-printer> object if one recognizes VALUE.  If no printer is | 
 |    found, it will return #f.  On error, it will return a <gdb:exception> | 
 |    object. | 
 |  | 
 |    Note: This has to be efficient and careful. | 
 |    We don't want to excessively slow down printing of values, but any kind of | 
 |    random crud can appear in the pretty-printer list, and we can't crash | 
 |    because of it.  */ | 
 |  | 
 | static SCM | 
 | ppscm_search_pp_list (SCM list, SCM value) | 
 | { | 
 |   SCM orig_list = list; | 
 |  | 
 |   if (scm_is_null (list)) | 
 |     return SCM_BOOL_F; | 
 |   if (gdbscm_is_false (scm_list_p (list))) /* scm_is_pair? */ | 
 |     { | 
 |       return ppscm_make_pp_type_error_exception | 
 | 	(_("pretty-printer list is not a list"), list); | 
 |     } | 
 |  | 
 |   for ( ; scm_is_pair (list); list = scm_cdr (list)) | 
 |     { | 
 |       SCM matcher = scm_car (list); | 
 |       SCM worker; | 
 |       pretty_printer_smob *pp_smob; | 
 |  | 
 |       if (!ppscm_is_pretty_printer (matcher)) | 
 | 	{ | 
 | 	  return ppscm_make_pp_type_error_exception | 
 | 	    (_("pretty-printer list contains non-pretty-printer object"), | 
 | 	     matcher); | 
 | 	} | 
 |  | 
 |       pp_smob = (pretty_printer_smob *) SCM_SMOB_DATA (matcher); | 
 |  | 
 |       /* Skip if disabled.  */ | 
 |       if (gdbscm_is_false (pp_smob->enabled)) | 
 | 	continue; | 
 |  | 
 |       if (!gdbscm_is_procedure (pp_smob->lookup)) | 
 | 	{ | 
 | 	  return ppscm_make_pp_type_error_exception | 
 | 	    (_("invalid lookup object in pretty-printer matcher"), | 
 | 	     pp_smob->lookup); | 
 | 	} | 
 |  | 
 |       worker = gdbscm_safe_call_2 (pp_smob->lookup, matcher, | 
 | 				   value, gdbscm_memory_error_p); | 
 |       if (!gdbscm_is_false (worker)) | 
 | 	{ | 
 | 	  if (gdbscm_is_exception (worker)) | 
 | 	    return worker; | 
 | 	  if (ppscm_is_pretty_printer_worker (worker)) | 
 | 	    return worker; | 
 | 	  return ppscm_make_pp_type_error_exception | 
 | 	    (_("invalid result from pretty-printer lookup"), worker); | 
 | 	} | 
 |     } | 
 |  | 
 |   if (!scm_is_null (list)) | 
 |     { | 
 |       return ppscm_make_pp_type_error_exception | 
 | 	(_("pretty-printer list is not a list"), orig_list); | 
 |     } | 
 |  | 
 |   return SCM_BOOL_F; | 
 | } | 
 |  | 
 | /* Subroutine of find_pretty_printer to simplify it. | 
 |    Look for a pretty-printer to print VALUE in all objfiles. | 
 |    If there's an error an exception smob is returned. | 
 |    The result is #f, if no pretty-printer was found. | 
 |    Otherwise the result is the pretty-printer smob.  */ | 
 |  | 
 | static SCM | 
 | ppscm_find_pretty_printer_from_objfiles (SCM value) | 
 | { | 
 |   for (objfile *objfile : current_program_space->objfiles ()) | 
 |     { | 
 |       objfile_smob *o_smob = ofscm_objfile_smob_from_objfile (objfile); | 
 |       SCM pp | 
 | 	= ppscm_search_pp_list (ofscm_objfile_smob_pretty_printers (o_smob), | 
 | 				value); | 
 |  | 
 |       /* Note: This will return if pp is a <gdb:exception> object, | 
 | 	 which is what we want.  */ | 
 |       if (gdbscm_is_true (pp)) | 
 | 	return pp; | 
 |     } | 
 |  | 
 |   return SCM_BOOL_F; | 
 | } | 
 |  | 
 | /* Subroutine of find_pretty_printer to simplify it. | 
 |    Look for a pretty-printer to print VALUE in the current program space. | 
 |    If there's an error an exception smob is returned. | 
 |    The result is #f, if no pretty-printer was found. | 
 |    Otherwise the result is the pretty-printer smob.  */ | 
 |  | 
 | static SCM | 
 | ppscm_find_pretty_printer_from_progspace (SCM value) | 
 | { | 
 |   pspace_smob *p_smob = psscm_pspace_smob_from_pspace (current_program_space); | 
 |   SCM pp | 
 |     = ppscm_search_pp_list (psscm_pspace_smob_pretty_printers (p_smob), value); | 
 |  | 
 |   return pp; | 
 | } | 
 |  | 
 | /* Subroutine of find_pretty_printer to simplify it. | 
 |    Look for a pretty-printer to print VALUE in the gdb module. | 
 |    If there's an error a Scheme exception is returned. | 
 |    The result is #f, if no pretty-printer was found. | 
 |    Otherwise the result is the pretty-printer smob.  */ | 
 |  | 
 | static SCM | 
 | ppscm_find_pretty_printer_from_gdb (SCM value) | 
 | { | 
 |   SCM pp = ppscm_search_pp_list (pretty_printer_list, value); | 
 |  | 
 |   return pp; | 
 | } | 
 |  | 
 | /* Find the pretty-printing constructor function for VALUE.  If no | 
 |    pretty-printer exists, return #f.  If one exists, return the | 
 |    gdb:pretty-printer smob that implements it.  On error, an exception smob | 
 |    is returned. | 
 |  | 
 |    Note: In the end it may be better to call out to Scheme once, and then | 
 |    do all of the lookup from Scheme.  TBD.  */ | 
 |  | 
 | static SCM | 
 | ppscm_find_pretty_printer (SCM value) | 
 | { | 
 |   SCM pp; | 
 |  | 
 |   /* Look at the pretty-printer list for each objfile | 
 |      in the current program-space.  */ | 
 |   pp = ppscm_find_pretty_printer_from_objfiles (value); | 
 |   /* Note: This will return if function is a <gdb:exception> object, | 
 |      which is what we want.  */ | 
 |   if (gdbscm_is_true (pp)) | 
 |     return pp; | 
 |  | 
 |   /* Look at the pretty-printer list for the current program-space.  */ | 
 |   pp = ppscm_find_pretty_printer_from_progspace (value); | 
 |   /* Note: This will return if function is a <gdb:exception> object, | 
 |      which is what we want.  */ | 
 |   if (gdbscm_is_true (pp)) | 
 |     return pp; | 
 |  | 
 |   /* Look at the pretty-printer list in the gdb module.  */ | 
 |   pp = ppscm_find_pretty_printer_from_gdb (value); | 
 |   return pp; | 
 | } | 
 |  | 
 | /* Pretty-print a single value, via the PRINTER, which must be a | 
 |    <gdb:pretty-printer-worker> object. | 
 |    The caller is responsible for ensuring PRINTER is valid. | 
 |    If the function returns a string, an SCM containing the string | 
 |    is returned.  If the function returns #f that means the pretty | 
 |    printer returned #f as a value.  Otherwise, if the function returns a | 
 |    <gdb:value> object, *OUT_VALUE is set to the value and #t is returned. | 
 |    It is an error if the printer returns #t. | 
 |    On error, an exception smob is returned.  */ | 
 |  | 
 | static SCM | 
 | ppscm_pretty_print_one_value (SCM printer, struct value **out_value, | 
 | 			      struct gdbarch *gdbarch, | 
 | 			      const struct language_defn *language) | 
 | { | 
 |   SCM result = SCM_BOOL_F; | 
 |  | 
 |   *out_value = NULL; | 
 |   try | 
 |     { | 
 |       pretty_printer_worker_smob *w_smob | 
 | 	= (pretty_printer_worker_smob *) SCM_SMOB_DATA (printer); | 
 |  | 
 |       result = gdbscm_safe_call_1 (w_smob->to_string, printer, | 
 | 				   gdbscm_memory_error_p); | 
 |       if (gdbscm_is_false (result)) | 
 | 	; /* Done.  */ | 
 |       else if (scm_is_string (result) | 
 | 	       || lsscm_is_lazy_string (result)) | 
 | 	; /* Done.  */ | 
 |       else if (vlscm_is_value (result)) | 
 | 	{ | 
 | 	  SCM except_scm; | 
 |  | 
 | 	  *out_value | 
 | 	    = vlscm_convert_value_from_scheme (FUNC_NAME, GDBSCM_ARG_NONE, | 
 | 					       result, &except_scm, | 
 | 					       gdbarch, language); | 
 | 	  if (*out_value != NULL) | 
 | 	    result = SCM_BOOL_T; | 
 | 	  else | 
 | 	    result = except_scm; | 
 | 	} | 
 |       else if (gdbscm_is_exception (result)) | 
 | 	; /* Done.  */ | 
 |       else | 
 | 	{ | 
 | 	  /* Invalid result from to-string.  */ | 
 | 	  result = ppscm_make_pp_type_error_exception | 
 | 	    (_("invalid result from pretty-printer to-string"), result); | 
 | 	} | 
 |     } | 
 |   catch (const gdb_exception_forced_quit &except) | 
 |     { | 
 |       quit_force (NULL, 0); | 
 |     } | 
 |   catch (const gdb_exception &except) | 
 |     { | 
 |     } | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | /* Return the display hint for PRINTER as a Scheme object. | 
 |    The caller is responsible for ensuring PRINTER is a | 
 |    <gdb:pretty-printer-worker> object.  */ | 
 |   | 
 | static SCM | 
 | ppscm_get_display_hint_scm (SCM printer) | 
 | { | 
 |   pretty_printer_worker_smob *w_smob | 
 |     = (pretty_printer_worker_smob *) SCM_SMOB_DATA (printer); | 
 |  | 
 |   return w_smob->display_hint; | 
 | } | 
 |  | 
 | /* Return the display hint for the pretty-printer PRINTER. | 
 |    The caller is responsible for ensuring PRINTER is a | 
 |    <gdb:pretty-printer-worker> object. | 
 |    Returns the display hint or #f if the hint is not a string.  */ | 
 |  | 
 | static enum display_hint | 
 | ppscm_get_display_hint_enum (SCM printer) | 
 | { | 
 |   SCM hint = ppscm_get_display_hint_scm (printer); | 
 |  | 
 |   if (gdbscm_is_false (hint)) | 
 |     return HINT_NONE; | 
 |   if (scm_is_string (hint)) | 
 |     { | 
 |       if (gdbscm_is_true (scm_string_equal_p (hint, ppscm_array_string))) | 
 | 	return HINT_STRING; | 
 |       if (gdbscm_is_true (scm_string_equal_p (hint, ppscm_map_string))) | 
 | 	return HINT_STRING; | 
 |       if (gdbscm_is_true (scm_string_equal_p (hint, ppscm_string_string))) | 
 | 	return HINT_STRING; | 
 |       return HINT_ERROR; | 
 |     } | 
 |   return HINT_ERROR; | 
 | } | 
 |  | 
 | /* A wrapper for gdbscm_print_gdb_exception that ignores memory errors. | 
 |    EXCEPTION is a <gdb:exception> object.  */ | 
 |  | 
 | static void | 
 | ppscm_print_exception_unless_memory_error (SCM exception, | 
 | 					   struct ui_file *stream) | 
 | { | 
 |   if (gdbscm_memory_error_p (gdbscm_exception_key (exception))) | 
 |     { | 
 |       gdb::unique_xmalloc_ptr<char> msg | 
 | 	= gdbscm_exception_message_to_string (exception); | 
 |  | 
 |       /* This "shouldn't happen", but play it safe.  */ | 
 |       if (msg == NULL || msg.get ()[0] == '\0') | 
 | 	gdb_printf (stream, _("<error reading variable>")); | 
 |       else | 
 | 	{ | 
 | 	  /* Remove the trailing newline.  We could instead call a special | 
 | 	     routine for printing memory error messages, but this is easy | 
 | 	     enough for now.  */ | 
 | 	  char *msg_text = msg.get (); | 
 | 	  size_t len = strlen (msg_text); | 
 |  | 
 | 	  if (msg_text[len - 1] == '\n') | 
 | 	    msg_text[len - 1] = '\0'; | 
 | 	  gdb_printf (stream, _("<error reading variable: %s>"), msg_text); | 
 | 	} | 
 |     } | 
 |   else | 
 |     gdbscm_print_gdb_exception (SCM_BOOL_F, exception); | 
 | } | 
 |  | 
 | /* Helper for gdbscm_apply_val_pretty_printer which calls to_string and | 
 |    formats the result.  */ | 
 |  | 
 | static enum guile_string_repr_result | 
 | ppscm_print_string_repr (SCM printer, enum display_hint hint, | 
 | 			 struct ui_file *stream, int recurse, | 
 | 			 const struct value_print_options *options, | 
 | 			 struct gdbarch *gdbarch, | 
 | 			 const struct language_defn *language) | 
 | { | 
 |   struct value *replacement = NULL; | 
 |   SCM str_scm; | 
 |   enum guile_string_repr_result result = STRING_REPR_ERROR; | 
 |  | 
 |   str_scm = ppscm_pretty_print_one_value (printer, &replacement, | 
 | 					  gdbarch, language); | 
 |   if (gdbscm_is_false (str_scm)) | 
 |     { | 
 |       result = STRING_REPR_NONE; | 
 |     } | 
 |   else if (scm_is_eq (str_scm, SCM_BOOL_T)) | 
 |     { | 
 |       struct value_print_options opts = *options; | 
 |  | 
 |       gdb_assert (replacement != NULL); | 
 |       opts.addressprint = false; | 
 |       common_val_print (replacement, stream, recurse, &opts, language); | 
 |       result = STRING_REPR_OK; | 
 |     } | 
 |   else if (scm_is_string (str_scm)) | 
 |     { | 
 |       size_t length; | 
 |       gdb::unique_xmalloc_ptr<char> string | 
 | 	= gdbscm_scm_to_string (str_scm, &length, | 
 | 				target_charset (gdbarch), 0 /*!strict*/, NULL); | 
 |  | 
 |       if (hint == HINT_STRING) | 
 | 	{ | 
 | 	  struct type *type = builtin_type (gdbarch)->builtin_char; | 
 | 	   | 
 | 	  language->printstr (stream, type, (gdb_byte *) string.get (), | 
 | 			      length, NULL, 0, options); | 
 | 	} | 
 |       else | 
 | 	{ | 
 | 	  /* Alas scm_to_stringn doesn't nul-terminate the string if we | 
 | 	     ask for the length.  */ | 
 | 	  size_t i; | 
 |  | 
 | 	  for (i = 0; i < length; ++i) | 
 | 	    { | 
 | 	      if (string.get ()[i] == '\0') | 
 | 		gdb_puts ("\\000", stream); | 
 | 	      else | 
 | 		gdb_putc (string.get ()[i], stream); | 
 | 	    } | 
 | 	} | 
 |       result = STRING_REPR_OK; | 
 |     } | 
 |   else if (lsscm_is_lazy_string (str_scm)) | 
 |     { | 
 |       struct value_print_options local_opts = *options; | 
 |  | 
 |       local_opts.addressprint = false; | 
 |       lsscm_val_print_lazy_string (str_scm, stream, &local_opts); | 
 |       result = STRING_REPR_OK; | 
 |     } | 
 |   else | 
 |     { | 
 |       gdb_assert (gdbscm_is_exception (str_scm)); | 
 |       ppscm_print_exception_unless_memory_error (str_scm, stream); | 
 |       result = STRING_REPR_ERROR; | 
 |     } | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | /* Helper for gdbscm_apply_val_pretty_printer that formats children of the | 
 |    printer, if any exist. | 
 |    The caller is responsible for ensuring PRINTER is a printer smob. | 
 |    If PRINTED_NOTHING is true, then nothing has been printed by to_string, | 
 |    and format output accordingly. */ | 
 |  | 
 | static void | 
 | ppscm_print_children (SCM printer, enum display_hint hint, | 
 | 		      struct ui_file *stream, int recurse, | 
 | 		      const struct value_print_options *options, | 
 | 		      struct gdbarch *gdbarch, | 
 | 		      const struct language_defn *language, | 
 | 		      int printed_nothing) | 
 | { | 
 |   pretty_printer_worker_smob *w_smob | 
 |     = (pretty_printer_worker_smob *) SCM_SMOB_DATA (printer); | 
 |   int is_map, is_array, done_flag, pretty; | 
 |   unsigned int i; | 
 |   SCM children; | 
 |   SCM iter = SCM_BOOL_F; /* -Wall */ | 
 |  | 
 |   if (gdbscm_is_false (w_smob->children)) | 
 |     return; | 
 |   if (!gdbscm_is_procedure (w_smob->children)) | 
 |     { | 
 |       ppscm_print_pp_type_error | 
 | 	(_("pretty-printer \"children\" object is not a procedure or #f"), | 
 | 	 w_smob->children); | 
 |       return; | 
 |     } | 
 |  | 
 |   /* If we are printing a map or an array, we want special formatting.  */ | 
 |   is_map = hint == HINT_MAP; | 
 |   is_array = hint == HINT_ARRAY; | 
 |  | 
 |   children = gdbscm_safe_call_1 (w_smob->children, printer, | 
 | 				 gdbscm_memory_error_p); | 
 |   if (gdbscm_is_exception (children)) | 
 |     { | 
 |       ppscm_print_exception_unless_memory_error (children, stream); | 
 |       goto done; | 
 |     } | 
 |   /* We combine two steps here: get children, make an iterator out of them. | 
 |      This simplifies things because there's no language means of creating | 
 |      iterators, and it's the printer object that knows how it will want its | 
 |      children iterated over.  */ | 
 |   if (!itscm_is_iterator (children)) | 
 |     { | 
 |       ppscm_print_pp_type_error | 
 | 	(_("result of pretty-printer \"children\" procedure is not" | 
 | 	   " a <gdb:iterator> object"), children); | 
 |       goto done; | 
 |     } | 
 |   iter = children; | 
 |  | 
 |   /* Use the prettyformat_arrays option if we are printing an array, | 
 |      and the pretty option otherwise.  */ | 
 |   if (is_array) | 
 |     pretty = options->prettyformat_arrays; | 
 |   else | 
 |     { | 
 |       if (options->prettyformat == Val_prettyformat) | 
 | 	pretty = 1; | 
 |       else | 
 | 	pretty = options->prettyformat_structs; | 
 |     } | 
 |  | 
 |   done_flag = 0; | 
 |   for (i = 0; i < options->print_max; ++i) | 
 |     { | 
 |       SCM scm_name, v_scm; | 
 |       SCM item = itscm_safe_call_next_x (iter, gdbscm_memory_error_p); | 
 |  | 
 |       if (gdbscm_is_exception (item)) | 
 | 	{ | 
 | 	  ppscm_print_exception_unless_memory_error (item, stream); | 
 | 	  break; | 
 | 	} | 
 |       if (itscm_is_end_of_iteration (item)) | 
 | 	{ | 
 | 	  /* Set a flag so we can know whether we printed all the | 
 | 	     available elements.  */ | 
 | 	  done_flag = 1; | 
 | 	  break; | 
 | 	} | 
 |  | 
 |       if (! scm_is_pair (item)) | 
 | 	{ | 
 | 	  ppscm_print_pp_type_error | 
 | 	    (_("result of pretty-printer children iterator is not a pair" | 
 | 	       " or (end-of-iteration)"), | 
 | 	     item); | 
 | 	  continue; | 
 | 	} | 
 |       scm_name = scm_car (item); | 
 |       v_scm = scm_cdr (item); | 
 |       if (!scm_is_string (scm_name)) | 
 | 	{ | 
 | 	  ppscm_print_pp_type_error | 
 | 	    (_("first element of pretty-printer children iterator is not" | 
 | 	       " a string"), item); | 
 | 	  continue; | 
 | 	} | 
 |       gdb::unique_xmalloc_ptr<char> name | 
 | 	= gdbscm_scm_to_c_string (scm_name); | 
 |  | 
 |       /* Print initial "=" to separate print_string_repr output and | 
 | 	 children.  For other elements, there are three cases: | 
 | 	 1. Maps.  Print a "," after each value element. | 
 | 	 2. Arrays.  Always print a ",". | 
 | 	 3. Other.  Always print a ",".  */ | 
 |       if (i == 0) | 
 | 	{ | 
 | 	  if (!printed_nothing) | 
 | 	    gdb_puts (" = ", stream); | 
 | 	} | 
 |       else if (! is_map || i % 2 == 0) | 
 | 	gdb_puts (pretty ? "," : ", ", stream); | 
 |  | 
 |       /* Skip printing children if max_depth has been reached.  This check | 
 | 	 is performed after print_string_repr and the "=" separator so that | 
 | 	 these steps are not skipped if the variable is located within the | 
 | 	 permitted depth.  */ | 
 |       if (val_print_check_max_depth (stream, recurse, options, language)) | 
 | 	goto done; | 
 |       else if (i == 0) | 
 | 	/* Print initial "{" to bookend children.  */ | 
 | 	gdb_puts ("{", stream); | 
 |  | 
 |       /* In summary mode, we just want to print "= {...}" if there is | 
 | 	 a value.  */ | 
 |       if (options->summary) | 
 | 	{ | 
 | 	  /* This increment tricks the post-loop logic to print what | 
 | 	     we want.  */ | 
 | 	  ++i; | 
 | 	  /* Likewise.  */ | 
 | 	  pretty = 0; | 
 | 	  break; | 
 | 	} | 
 |  | 
 |       if (! is_map || i % 2 == 0) | 
 | 	{ | 
 | 	  if (pretty) | 
 | 	    { | 
 | 	      gdb_puts ("\n", stream); | 
 | 	      print_spaces (2 + 2 * recurse, stream); | 
 | 	    } | 
 | 	  else | 
 | 	    stream->wrap_here (2 + 2 *recurse); | 
 | 	} | 
 |  | 
 |       if (is_map && i % 2 == 0) | 
 | 	gdb_puts ("[", stream); | 
 |       else if (is_array) | 
 | 	{ | 
 | 	  /* We print the index, not whatever the child method | 
 | 	     returned as the name.  */ | 
 | 	  if (options->print_array_indexes) | 
 | 	    gdb_printf (stream, "[%d] = ", i); | 
 | 	} | 
 |       else if (! is_map) | 
 | 	{ | 
 | 	  gdb_puts (name.get (), stream); | 
 | 	  gdb_puts (" = ", stream); | 
 | 	} | 
 |  | 
 |       if (lsscm_is_lazy_string (v_scm)) | 
 | 	{ | 
 | 	  struct value_print_options local_opts = *options; | 
 |  | 
 | 	  local_opts.addressprint = false; | 
 | 	  lsscm_val_print_lazy_string (v_scm, stream, &local_opts); | 
 | 	} | 
 |       else if (scm_is_string (v_scm)) | 
 | 	{ | 
 | 	  gdb::unique_xmalloc_ptr<char> output | 
 | 	    = gdbscm_scm_to_c_string (v_scm); | 
 | 	  gdb_puts (output.get (), stream); | 
 | 	} | 
 |       else | 
 | 	{ | 
 | 	  SCM except_scm; | 
 | 	  struct value *value | 
 | 	    = vlscm_convert_value_from_scheme (FUNC_NAME, GDBSCM_ARG_NONE, | 
 | 					       v_scm, &except_scm, | 
 | 					       gdbarch, language); | 
 |  | 
 | 	  if (value == NULL) | 
 | 	    { | 
 | 	      ppscm_print_exception_unless_memory_error (except_scm, stream); | 
 | 	      break; | 
 | 	    } | 
 | 	  else | 
 | 	    { | 
 | 	      /* When printing the key of a map we allow one additional | 
 | 		 level of depth.  This means the key will print before the | 
 | 		 value does.  */ | 
 | 	      struct value_print_options opt = *options; | 
 | 	      if (is_map && i % 2 == 0 | 
 | 		  && opt.max_depth != -1 | 
 | 		  && opt.max_depth < INT_MAX) | 
 | 		++opt.max_depth; | 
 | 	      common_val_print (value, stream, recurse + 1, &opt, language); | 
 | 	    } | 
 | 	} | 
 |  | 
 |       if (is_map && i % 2 == 0) | 
 | 	gdb_puts ("] = ", stream); | 
 |     } | 
 |  | 
 |   if (i) | 
 |     { | 
 |       if (!done_flag) | 
 | 	{ | 
 | 	  if (pretty) | 
 | 	    { | 
 | 	      gdb_puts ("\n", stream); | 
 | 	      print_spaces (2 + 2 * recurse, stream); | 
 | 	    } | 
 | 	  gdb_puts ("...", stream); | 
 | 	} | 
 |       if (pretty) | 
 | 	{ | 
 | 	  gdb_puts ("\n", stream); | 
 | 	  print_spaces (2 * recurse, stream); | 
 | 	} | 
 |       gdb_puts ("}", stream); | 
 |     } | 
 |  | 
 |  done: | 
 |   /* Play it safe, make sure ITER doesn't get GC'd.  */ | 
 |   scm_remember_upto_here_1 (iter); | 
 | } | 
 |  | 
 | /* This is the extension_language_ops.apply_val_pretty_printer "method".  */ | 
 |  | 
 | enum ext_lang_rc | 
 | gdbscm_apply_val_pretty_printer (const struct extension_language_defn *extlang, | 
 | 				 struct value *value, | 
 | 				 struct ui_file *stream, int recurse, | 
 | 				 const struct value_print_options *options, | 
 | 				 const struct language_defn *language) | 
 | { | 
 |   struct type *type = value->type (); | 
 |   struct gdbarch *gdbarch = type->arch (); | 
 |   SCM exception = SCM_BOOL_F; | 
 |   SCM printer = SCM_BOOL_F; | 
 |   SCM val_obj = SCM_BOOL_F; | 
 |   enum display_hint hint; | 
 |   enum ext_lang_rc result = EXT_LANG_RC_NOP; | 
 |   enum guile_string_repr_result print_result; | 
 |  | 
 |   if (value->lazy ()) | 
 |     value->fetch_lazy (); | 
 |  | 
 |   /* No pretty-printer support for unavailable values.  */ | 
 |   if (!value->bytes_available (0, type->length ())) | 
 |     return EXT_LANG_RC_NOP; | 
 |  | 
 |   if (!gdb_scheme_initialized) | 
 |     return EXT_LANG_RC_NOP; | 
 |  | 
 |   /* Instantiate the printer.  */ | 
 |   val_obj = vlscm_scm_from_value_no_release (value); | 
 |   if (gdbscm_is_exception (val_obj)) | 
 |     { | 
 |       exception = val_obj; | 
 |       result = EXT_LANG_RC_ERROR; | 
 |       goto done; | 
 |     } | 
 |  | 
 |   printer = ppscm_find_pretty_printer (val_obj); | 
 |  | 
 |   if (gdbscm_is_exception (printer)) | 
 |     { | 
 |       exception = printer; | 
 |       result = EXT_LANG_RC_ERROR; | 
 |       goto done; | 
 |     } | 
 |   if (gdbscm_is_false (printer)) | 
 |     { | 
 |       result = EXT_LANG_RC_NOP; | 
 |       goto done; | 
 |     } | 
 |   gdb_assert (ppscm_is_pretty_printer_worker (printer)); | 
 |  | 
 |   /* If we are printing a map, we want some special formatting.  */ | 
 |   hint = ppscm_get_display_hint_enum (printer); | 
 |   if (hint == HINT_ERROR) | 
 |     { | 
 |       /* Print the error as an exception for consistency.  */ | 
 |       SCM hint_scm = ppscm_get_display_hint_scm (printer); | 
 |  | 
 |       ppscm_print_pp_type_error ("Invalid display hint", hint_scm); | 
 |       /* Fall through.  A bad hint doesn't stop pretty-printing.  */ | 
 |       hint = HINT_NONE; | 
 |     } | 
 |  | 
 |   /* Print the section.  */ | 
 |   print_result = ppscm_print_string_repr (printer, hint, stream, recurse, | 
 | 					  options, gdbarch, language); | 
 |   if (print_result != STRING_REPR_ERROR) | 
 |     { | 
 |       ppscm_print_children (printer, hint, stream, recurse, options, | 
 | 			    gdbarch, language, | 
 | 			    print_result == STRING_REPR_NONE); | 
 |     } | 
 |  | 
 |   result = EXT_LANG_RC_OK; | 
 |  | 
 |  done: | 
 |   if (gdbscm_is_exception (exception)) | 
 |     ppscm_print_exception_unless_memory_error (exception, stream); | 
 |   return result; | 
 | } | 
 |  | 
 | /* Initialize the Scheme pretty-printer code.  */ | 
 |  | 
 | static const scheme_function pretty_printer_functions[] = | 
 | { | 
 |   { "make-pretty-printer", 2, 0, 0, | 
 |     as_a_scm_t_subr (gdbscm_make_pretty_printer), | 
 |     "\ | 
 | Create a <gdb:pretty-printer> object.\n\ | 
 | \n\ | 
 |   Arguments: name lookup\n\ | 
 |     name:   a string naming the matcher\n\ | 
 |     lookup: a procedure:\n\ | 
 |       (pretty-printer <gdb:value>) -> <gdb:pretty-printer-worker> | #f." }, | 
 |  | 
 |   { "pretty-printer?", 1, 0, 0, as_a_scm_t_subr (gdbscm_pretty_printer_p), | 
 |     "\ | 
 | Return #t if the object is a <gdb:pretty-printer> object." }, | 
 |  | 
 |   { "pretty-printer-enabled?", 1, 0, 0, | 
 |     as_a_scm_t_subr (gdbscm_pretty_printer_enabled_p), | 
 |     "\ | 
 | Return #t if the pretty-printer is enabled." }, | 
 |  | 
 |   { "set-pretty-printer-enabled!", 2, 0, 0, | 
 |     as_a_scm_t_subr (gdbscm_set_pretty_printer_enabled_x), | 
 |     "\ | 
 | Set the enabled flag of the pretty-printer.\n\ | 
 | Returns \"unspecified\"." }, | 
 |  | 
 |   { "make-pretty-printer-worker", 3, 0, 0, | 
 |     as_a_scm_t_subr (gdbscm_make_pretty_printer_worker), | 
 |     "\ | 
 | Create a <gdb:pretty-printer-worker> object.\n\ | 
 | \n\ | 
 |   Arguments: display-hint to-string children\n\ | 
 |     display-hint: either #f or one of \"array\", \"map\", or \"string\"\n\ | 
 |     to-string:    a procedure:\n\ | 
 |       (pretty-printer) -> string | #f | <gdb:value>\n\ | 
 |     children:     either #f or a procedure:\n\ | 
 |       (pretty-printer) -> <gdb:iterator>" }, | 
 |  | 
 |   { "pretty-printer-worker?", 1, 0, 0, | 
 |     as_a_scm_t_subr (gdbscm_pretty_printer_worker_p), | 
 |     "\ | 
 | Return #t if the object is a <gdb:pretty-printer-worker> object." }, | 
 |  | 
 |   { "pretty-printers", 0, 0, 0, as_a_scm_t_subr (gdbscm_pretty_printers), | 
 |     "\ | 
 | Return the list of global pretty-printers." }, | 
 |  | 
 |   { "set-pretty-printers!", 1, 0, 0, | 
 |     as_a_scm_t_subr (gdbscm_set_pretty_printers_x), | 
 |     "\ | 
 | Set the list of global pretty-printers." }, | 
 |  | 
 |   END_FUNCTIONS | 
 | }; | 
 |  | 
 | void | 
 | gdbscm_initialize_pretty_printers (void) | 
 | { | 
 |   pretty_printer_smob_tag | 
 |     = gdbscm_make_smob_type (pretty_printer_smob_name, | 
 | 			     sizeof (pretty_printer_smob)); | 
 |   scm_set_smob_print (pretty_printer_smob_tag, | 
 | 		      ppscm_print_pretty_printer_smob); | 
 |  | 
 |   pretty_printer_worker_smob_tag | 
 |     = gdbscm_make_smob_type (pretty_printer_worker_smob_name, | 
 | 			     sizeof (pretty_printer_worker_smob)); | 
 |   scm_set_smob_print (pretty_printer_worker_smob_tag, | 
 | 		      ppscm_print_pretty_printer_worker_smob); | 
 |  | 
 |   gdbscm_define_functions (pretty_printer_functions, 1); | 
 |  | 
 |   pretty_printer_list = SCM_EOL; | 
 |  | 
 |   pp_type_error_symbol = scm_from_latin1_symbol ("gdb:pp-type-error"); | 
 |  | 
 |   ppscm_map_string = scm_from_latin1_string ("map"); | 
 |   ppscm_array_string = scm_from_latin1_string ("array"); | 
 |   ppscm_string_string = scm_from_latin1_string ("string"); | 
 | } |