diff src/print.c @ 5125:b5df3737028a ben-lisp-object

merge
author Ben Wing <ben@xemacs.org>
date Wed, 24 Feb 2010 01:58:04 -0600
parents d1247f3cc363 c2e0c3af5fe3
children a9c41067dd88
line wrap: on
line diff
--- a/src/print.c	Wed Jan 20 07:05:57 2010 -0600
+++ b/src/print.c	Wed Feb 24 01:58:04 2010 -0600
@@ -1,6 +1,6 @@
 /* Lisp object printing and output streams.
    Copyright (C) 1985, 1986, 1988, 1992-1995 Free Software Foundation, Inc.
-   Copyright (C) 1995, 1996, 2000, 2001, 2002, 2003, 2005 Ben Wing.
+   Copyright (C) 1995, 1996, 2000, 2001, 2002, 2003, 2005, 2010 Ben Wing.
 
 This file is part of XEmacs.
 
@@ -114,6 +114,9 @@
 /* Force immediate output of all printed data.  Used for debugging. */
 int print_unbuffered;
 
+/* Non-zero if in debug-printing */
+int in_debug_print;
+
 FILE *termscript;	/* Stdio stream being used for copy of all output.  */
 
 static void write_string_to_alternate_debugging_output (const Ibyte *str,
@@ -127,13 +130,15 @@
   int print_depth;
   int print_readably;
   int print_unbuffered;
+  int in_debug_print;
   int gc_currently_forbidden;
   Lisp_Object Vprint_length;
   Lisp_Object Vprint_level;
   Lisp_Object Vinhibit_quit;
 };
 
-static Lisp_Object debug_prin1_bindings;
+static int begin_inhibit_non_essential_conversion_operations (void);
+
 
 
 int stdout_needs_newline;
@@ -358,10 +363,12 @@
 void
 debug_out (const CIbyte *fmt, ...)
 {
+  int depth =  begin_inhibit_non_essential_conversion_operations ();
   va_list args;
   va_start (args, fmt);
   write_string_to_external_output_va (fmt, args, EXT_PRINT_ALL);
   va_end (args);
+  unbind_to (depth);
 }
 
 DOESNT_RETURN
@@ -651,17 +658,47 @@
 }
 
 void
-write_string (Lisp_Object stream, const Ibyte *str)
+write_istring (Lisp_Object stream, const Ibyte *str)
 {
   /* This function can GC */
   write_string_1 (stream, str, qxestrlen (str));
 }
 
 void
-write_c_string (Lisp_Object stream, const CIbyte *str)
+write_cistring (Lisp_Object stream, const CIbyte *str)
+{
+  /* This function can GC */
+  write_istring (stream, (const Ibyte *) str);
+}
+
+void
+write_ascstring (Lisp_Object stream, const Ascbyte *str)
 {
   /* This function can GC */
-  write_string_1 (stream, (const Ibyte *) str, strlen (str));
+  ASSERT_ASCTEXT_ASCII (str);
+  write_istring (stream, (const Ibyte *) str);
+}
+
+void
+write_msg_istring (Lisp_Object stream, const Ibyte *str)
+{
+  /* This function can GC */
+  write_istring (stream, IGETTEXT (str));
+}
+
+void
+write_msg_cistring (Lisp_Object stream, const CIbyte *str)
+{
+  /* This function can GC */
+  write_msg_istring (stream, (const Ibyte *) str);
+}
+
+void
+write_msg_ascstring (Lisp_Object stream, const Ascbyte *str)
+{
+  /* This function can GC */
+  ASSERT_ASCTEXT_ASCII (str);
+  write_msg_istring (stream, (const Ibyte *) str);
 }
 
 void
@@ -844,7 +881,7 @@
        (stream))
 {
   /* This function can GC */
-  write_c_string (canonicalize_printcharfun (stream), "\n");
+  write_ascstring (canonicalize_printcharfun (stream), "\n");
   return Qt;
 }
 
@@ -941,9 +978,9 @@
 
   GCPRO2 (object, stream);
   stream = print_prepare (stream, &frame);
-  write_c_string (stream, "\n");
+  write_ascstring (stream, "\n");
   print_internal (object, stream, 1);
-  write_c_string (stream, "\n");
+  write_ascstring (stream, "\n");
   print_finish (stream, frame);
   UNGCPRO;
   return object;
@@ -1019,7 +1056,7 @@
       }
     while (!NILP (tail))
       {
-	write_c_string (stream, first ? ": " : ", ");
+	write_ascstring (stream, first ? ": " : ", ");
 	/* Most errors have an explanatory string as their first argument,
 	   and it looks better not to put the quotes around it. */
 	print_internal (Fcar (tail), stream,
@@ -1039,7 +1076,7 @@
  error_throw:
   if (NILP (method))
     {
-      write_c_string (stream, GETTEXT ("Peculiar error "));
+      write_ascstring (stream, GETTEXT ("Peculiar error "));
       print_internal (error_object, stream, 1);
       return;
     }
@@ -1323,17 +1360,17 @@
       if (max < len) last = max;
     }
 
-  write_c_string (printcharfun, start);
+  write_cistring (printcharfun, start);
   for (i = 0; i < last; i++)
     {
       Lisp_Object elt = XVECTOR_DATA (obj)[i];
-      if (i != 0) write_c_string (printcharfun, " ");
+      if (i != 0) write_ascstring (printcharfun, " ");
       print_internal (elt, printcharfun, escapeflag);
     }
   UNGCPRO;
   if (last != len)
-    write_c_string (printcharfun, " ...");
-  write_c_string (printcharfun, end);
+    write_ascstring (printcharfun, " ...");
+  write_cistring (printcharfun, end);
 }
 
 void
@@ -1354,14 +1391,14 @@
     {
       obj = XCAR (XCDR (obj));
       GCPRO2 (obj, printcharfun);
-      write_c_string (printcharfun, "\'");
+      write_ascstring (printcharfun, "\'");
       UNGCPRO;
       print_internal (obj, printcharfun, escapeflag);
       return;
     }
 
   GCPRO2 (obj, printcharfun);
-  write_c_string (printcharfun, "(");
+  write_ascstring (printcharfun, "(");
 
   {
     int len;
@@ -1374,20 +1411,20 @@
 	 obj = XCDR (obj), len++)
       {
 	if (len > 0)
-	  write_c_string (printcharfun, " ");
+	  write_ascstring (printcharfun, " ");
 	if (EQ (obj, tortoise) && len > 0)
 	  {
 	    if (print_readably)
 	      printing_unreadable_object ("circular list");
 	    else
-	      write_c_string (printcharfun, "... <circular list>");
+	      write_ascstring (printcharfun, "... <circular list>");
 	    break;
 	  }
 	if (len & 1)
 	  tortoise = XCDR (tortoise);
 	if (len > max)
 	  {
-	    write_c_string (printcharfun, "...");
+	    write_ascstring (printcharfun, "...");
 	    break;
 	  }
 	print_internal (XCAR (obj), printcharfun, escapeflag);
@@ -1395,12 +1432,12 @@
   }
   if (!LISTP (obj))
     {
-      write_c_string (printcharfun, " . ");
+      write_ascstring (printcharfun, " . ");
       print_internal (obj, printcharfun, escapeflag);
     }
   UNGCPRO;
 
-  write_c_string (printcharfun, ")");
+  write_ascstring (printcharfun, ")");
   return;
 }
 
@@ -1438,13 +1475,13 @@
       /* This deals with GC-relocation and Mule. */
       output_string (printcharfun, 0, obj, 0, bcmax);
       if (max < size)
-	write_c_string (printcharfun, " ...");
+	write_ascstring (printcharfun, " ...");
     }
   else
     {
       Bytecount i, last = 0;
 
-      write_c_string (printcharfun, "\"");
+      write_ascstring (printcharfun, "\"");
       for (i = 0; i < bcmax; i++)
 	{
 	  Ibyte ch = string_byte (obj, i);
@@ -1458,17 +1495,17 @@
 		}
 	      if (ch == '\n')
 		{
-		  write_c_string (printcharfun, "\\n");
+		  write_ascstring (printcharfun, "\\n");
 		}
 	      else
 		{
 		  Ibyte temp[2];
-		  write_c_string (printcharfun, "\\");
+		  write_ascstring (printcharfun, "\\");
 		  /* This is correct for Mule because the
 		     character is either \ or " */
 		  temp[0] = string_byte (obj, i);
 		  temp[1] = '\0';
-		  write_string (printcharfun, temp);
+		  write_istring (printcharfun, temp);
 		}
 	      last = i + 1;
 	    }
@@ -1479,27 +1516,70 @@
 			 bcmax - last);
 	}
       if (max < size)
-	write_c_string (printcharfun, " ...");
-      write_c_string (printcharfun, "\"");
+	write_ascstring (printcharfun, " ...");
+      write_ascstring (printcharfun, "\"");
     }
   UNGCPRO;
 }
 
+DOESNT_RETURN
+printing_unreadable_object (const Ascbyte *fmt, ...)
+{
+  Lisp_Object obj;
+  va_list args;
+
+  va_start (args, fmt);
+  obj = emacs_vsprintf_string (GETTEXT (fmt), args);
+  va_end (args);
+
+  /* Fsignal GC-protects its args */
+  signal_error (Qprinting_unreadable_object, 0, obj);
+}
+
+DOESNT_RETURN
+printing_unreadable_lcrecord (Lisp_Object obj, const Ibyte *name)
+{
+  LISP_OBJECT_HEADER *header = (LISP_OBJECT_HEADER *) XPNTR (obj);
+
+#ifndef NEW_GC
+  /* This must be a real lcrecord */
+  assert (!LHEADER_IMPLEMENTATION (&header->lheader)->basic_p);
+#endif
+
+  if (name)
+    printing_unreadable_object
+      ("#<%s %s 0x%x>",
+#ifdef NEW_GC
+       LHEADER_IMPLEMENTATION (header)->name,
+#else /* not NEW_GC */
+       LHEADER_IMPLEMENTATION (&header->lheader)->name,
+#endif /* not NEW_GC */
+       name,
+       header->uid);
+  else
+    printing_unreadable_object
+      ("#<%s 0x%x>",
+#ifdef NEW_GC
+       LHEADER_IMPLEMENTATION (header)->name,
+#else /* not NEW_GC */
+       LHEADER_IMPLEMENTATION (&header->lheader)->name,
+#endif /* not NEW_GC */
+       header->uid);
+}
+
 void
 external_object_printer (Lisp_Object obj, Lisp_Object printcharfun,
 			 int UNUSED (escapeflag))
 {
   LISP_OBJECT_HEADER *header = (LISP_OBJECT_HEADER *) XPNTR (obj);
 
+#ifndef NEW_GC
+  /* This must be a real lcrecord */
+  assert (!LHEADER_IMPLEMENTATION (&header->lheader)->basic_p);
+#endif
+
   if (print_readably)
-    printing_unreadable_object
-      ("#<%s 0x%x>",
-#ifdef NEW_GC
-       LHEADER_IMPLEMENTATION (header)->name,
-#else /* not NEW_GC */
-       LHEADER_IMPLEMENTATION (&header->lheader)->name,
-#endif /* not NEW_GC */
-       header->uid);
+    printing_unreadable_lcrecord (obj, 0);
 
   write_fmt_string (printcharfun, "#<%s 0x%x>",
 #ifdef NEW_GC
@@ -1520,6 +1600,9 @@
        XRECORD_LHEADER_IMPLEMENTATION (obj)->name,
        (unsigned long) XPNTR (obj));
 
+  /* Internal objects shouldn't normally escape to the Lisp level;
+     that's why we say "XEmacs bug?".  This can happen, however, when
+     printing backtraces. */
   write_fmt_string (printcharfun,
 		    "#<INTERNAL OBJECT (XEmacs bug?) (%s) 0x%lx>",
 		    XRECORD_LHEADER_IMPLEMENTATION (obj)->name,
@@ -1530,25 +1613,31 @@
 {
   BADNESS_INTEGER_OBJECT,
   BADNESS_POINTER_OBJECT,
+  BADNESS_POINTER_OBJECT_WITH_DATA,
   BADNESS_NO_TYPE
 };
 
 static void
 printing_major_badness (Lisp_Object printcharfun,
 			const Ascbyte *badness_string, int type, void *val,
-			enum printing_badness badness)
+			void *val2, enum printing_badness badness)
 {
   Ibyte buf[666];
 
   switch (badness)
     {
     case BADNESS_INTEGER_OBJECT:
-      qxesprintf (buf, "%s %d object %ld", badness_string, type,
+      qxesprintf (buf, "%s type %d object %ld", badness_string, type,
 		  (EMACS_INT) val);
       break;
 
     case BADNESS_POINTER_OBJECT:
-      qxesprintf (buf, "%s %d object %p", badness_string, type, val);
+      qxesprintf (buf, "%s type %d object %p", badness_string, type, val);
+      break;
+
+    case BADNESS_POINTER_OBJECT_WITH_DATA:
+      qxesprintf (buf, "%s type %d object %p data %p", badness_string, type,
+		  val, val2);
       break;
 
     case BADNESS_NO_TYPE:
@@ -1564,12 +1653,14 @@
       ABORT ();
 #else  /* not ERROR_CHECK_TYPES */
       if (print_readably)
-	signal_ferror (Qinternal_error, "printing %s", buf);
+	signal_ferror (Qinternal_error, "SERIOUS XEMACS BUG: printing %s; "
+		       "save your buffers immediately and please report "
+		       "this bug", buf);
 #endif /* not ERROR_CHECK_TYPES */
     }
   write_fmt_string (printcharfun,
-		    "#<EMACS BUG: %s Save your buffers immediately and "
-		    "please report this bug>", buf);
+		    "#<SERIOUS XEMACS BUG: %s Save your buffers immediately "
+		    "and please report this bug>", buf);
 }
 
 void
@@ -1589,6 +1680,13 @@
   /* Just to be safe ... */
   GCPRO2 (obj, printcharfun);
 
+  /* WARNING WARNING WARNING!!!  Don't put anything here that might
+     dereference memory.  Instead, put it down inside of
+     the case Lisp_Type_Record, after the appropriate checks to make sure
+     we're not dereferencing bad memory.  The idea is that, ideally,
+     calling debug_print() should *NEVER* make the program crash, even when
+     something very bad has happened. --ben */
+
 #ifdef I18N3
   /* #### Both input and output streams should have a flag associated
      with them indicating whether output to that stream, or strings
@@ -1601,23 +1699,6 @@
      output. */
 #endif
 
-  /* Detect circularities and truncate them.
-     No need to offer any alternative--this is better than an error.  */
-  if (CONSP (obj) || VECTORP (obj) || COMPILED_FUNCTIONP (obj))
-    {
-      int i;
-      for (i = 0; i < print_depth; i++)
-	if (EQ (obj, being_printed[i]))
-	  {
-	    char buf[DECIMAL_PRINT_SIZE (long) + 1];
-	    *buf = '#';
-	    long_to_string (buf + 1, i);
-	    write_c_string (printcharfun, buf);
-	    UNGCPRO;
-	    return;
-	  }
-    }
-
   being_printed[print_depth] = obj;
 
   /* Avoid calling internal_bind_int, which conses, when called from
@@ -1627,7 +1708,8 @@
       specdepth = internal_bind_int (&print_depth, print_depth + 1);
 
       if (print_depth > PRINT_CIRCLE)
-	signal_error (Qstack_overflow, "Apparently circular structure being printed", Qunbound);
+	signal_error (Qstack_overflow,
+		      "Apparently circular structure being printed", Qunbound);
     }
 
   switch (XTYPE (obj))
@@ -1635,9 +1717,9 @@
     case Lisp_Type_Int_Even:
     case Lisp_Type_Int_Odd:
       {
-	char buf[DECIMAL_PRINT_SIZE (EMACS_INT)];
+	Ascbyte buf[DECIMAL_PRINT_SIZE (EMACS_INT)];
 	long_to_string (buf, XINT (obj));
-	write_c_string (printcharfun, buf);
+	write_ascstring (printcharfun, buf);
 	break;
       }
 
@@ -1710,69 +1792,100 @@
       {
 	struct lrecord_header *lheader = XRECORD_LHEADER (obj);
 
-	/* Try to check for various sorts of bogus pointers if we're in a
-	   situation where it may be likely -- i.e. called from
-	   debug_print() or we're already crashing.  In such cases,
-	   (further) crashing is counterproductive. */
+	/* Try to check for various sorts of bogus pointers or bad memory
+	   if we're in a situation where it may be likely -- i.e. called
+	   from debug_print() or we're already crashing.  In such cases,
+	   (further) crashing is counterproductive.
+
+	   We don't normally do these because they may be expensive or
+	   weird (e.g. under Unix we typically have to set a SIGSEGV
+	   handler and try to trigger a seg fault). */
 
+	if (!lheader)
+	  {
+	    /* i.e. EQ Qnull_pointer */
+	    printing_major_badness (printcharfun, "NULL POINTER LRECORD", 0,
+				    0, 0, BADNESS_NO_TYPE);
+	    break;
+	  }
+
+	/* First check to see if the lrecord header itself is garbage. */
 	if (inhibit_non_essential_conversion_operations &&
 	    !debug_can_access_memory (lheader, sizeof (*lheader)))
+	  {
+	    printing_major_badness (printcharfun,
+				    "BAD MEMORY in LRECORD HEADER", 0,
+				    lheader, 0, BADNESS_NO_TYPE);
+	      break;
+	  }
+
+	/* Check to see if the lrecord type is garbage. */
+#ifndef NEW_GC
+	if (lheader->type == lrecord_type_free)
+	  {
+	    printing_major_badness (printcharfun, "FREED LRECORD", 0,
+				    lheader, 0, BADNESS_NO_TYPE);
+	    break;
+	  }
+	if (lheader->type == lrecord_type_undefined)
+	  {
+	    printing_major_badness (printcharfun, "LRECORD_TYPE_UNDEFINED", 0,
+				    lheader, 0, BADNESS_NO_TYPE);
+	    break;
+	  }
+#endif /* not NEW_GC */
+	if ((int) (lheader->type) >= lrecord_type_count)
+	  {
+	    printing_major_badness (printcharfun, "ILLEGAL LRECORD TYPE",
+				    (int) (lheader->type),
+				    lheader, 0, BADNESS_POINTER_OBJECT);
+	    break;
+	  }
+
+	/* Check to see if the lrecord implementation is missing or garbage. */
+	{
+	  const struct lrecord_implementation *imp =
+	    LHEADER_IMPLEMENTATION (lheader);
+
+	  if (!imp)
 	    {
-	      write_fmt_string (printcharfun, "#<EMACS BUG: BAD MEMORY %p>",
-				lheader);
+	      printing_major_badness
+		(printcharfun, "NO IMPLEMENTATION FOR LRECORD TYPE",
+		 (int) (lheader->type),
+		 lheader, 0, BADNESS_POINTER_OBJECT);
 	      break;
 	    }
 
-	if (CONSP (obj) || VECTORP (obj))
-	  {
-	    /* If deeper than spec'd depth, print placeholder.  */
-	    if (INTP (Vprint_level)
-		&& print_depth > XINT (Vprint_level))
-	      {
-		write_c_string (printcharfun, "...");
-		break;
-	      }
-	  }
+	  if (inhibit_non_essential_conversion_operations)
+	    {
+	      if (!debug_can_access_memory (imp, sizeof (*imp)))
+		{
+		  printing_major_badness
+		    (printcharfun, "BAD MEMORY IN LRECORD IMPLEMENTATION",
+		     (int) (lheader->type),
+		     lheader, 0, BADNESS_POINTER_OBJECT);
+		}
+	    }
+	}
 
-#ifndef NEW_GC
-	if (lheader->type == lrecord_type_free)
-	  {
-	    printing_major_badness (printcharfun, "freed lrecord", 0,
-				    lheader, BADNESS_NO_TYPE);
-	    break;
-	  }
-	else if (lheader->type == lrecord_type_undefined)
-	  {
-	    printing_major_badness (printcharfun, "lrecord_type_undefined", 0,
-				    lheader, BADNESS_NO_TYPE);
-	    break;
-	  }
-#endif /* not NEW_GC */
-	else if ((int) (lheader->type) >= lrecord_type_count)
-	  {
-	    printing_major_badness (printcharfun, "illegal lrecord type",
-				    (int) (lheader->type),
-				    lheader, BADNESS_POINTER_OBJECT);
-	    break;
-	  }
-
-	/* Further checks for bad memory in critical situations.  We don't
-	   normally do these because they may be expensive or weird
-	   (e.g. under Unix we typically have to set a SIGSEGV handler and
-	   try to trigger a seg fault). */
+	/* Check to see if any of the memory of the lrecord is inaccessible.
+	   Note that we already checked above to see if the first part of
+	   the lrecord (the header) is inaccessible, which will catch most
+	   cases of a totally bad pointer.  */
 
 	if (inhibit_non_essential_conversion_operations)
 	  {
 	    if (!debug_can_access_memory
 		(lheader, detagged_lisp_object_size (lheader)))
 	      {
-		write_fmt_string (printcharfun,
-				  "#<EMACS BUG: type %s BAD MEMORY %p>",
-				  LHEADER_IMPLEMENTATION (lheader)->name,
-				  lheader);
+		printing_major_badness (printcharfun,
+					"BAD MEMORY IN LRECORD",
+					(int) (lheader->type),
+					lheader, 0, BADNESS_POINTER_OBJECT);
 		break;
 	      }
 
+	    /* For strings, also check the data of the string itself. */
 	    if (STRINGP (obj))
 	      {
 #ifdef NEW_GC
@@ -1789,16 +1902,45 @@
 		Lisp_String *l = (Lisp_String *) lheader;
 		if (!debug_can_access_memory (l->data_, l->size_))
 		  {
-		    write_fmt_string
-		      (printcharfun,
-		       "#<EMACS BUG: %p (BAD STRING DATA %p)>",
-		       lheader, l->data_);
+		    printing_major_badness (printcharfun,
+		       "BAD STRING DATA", (int) (lheader->type),
+					    lheader, l->data_,
+					    BADNESS_POINTER_OBJECT_WITH_DATA);
 		    break;
 		  }
 #endif /* not NEW_GC */
 	      }
 	  }
 
+	/* Detect circularities and truncate them.
+	   No need to offer any alternative--this is better than an error.  */
+	if (CONSP (obj) || VECTORP (obj) || COMPILED_FUNCTIONP (obj))
+	  {
+	    int i;
+	    for (i = 0; i < print_depth - 1; i++)
+	      if (EQ (obj, being_printed[i]))
+		{
+		  Ascbyte buf[DECIMAL_PRINT_SIZE (long) + 1];
+		  *buf = '#';
+		  long_to_string (buf + 1, i);
+		  write_ascstring (printcharfun, buf);
+		  break;
+		}
+	    if (i < print_depth - 1) /* Did we print something? */
+	      break;
+	  }
+
+	if (CONSP (obj) || VECTORP (obj))
+	  {
+	    /* If deeper than spec'd depth, print placeholder.  */
+	    if (INTP (Vprint_level)
+		&& print_depth > XINT (Vprint_level))
+	      {
+		write_ascstring (printcharfun, "...");
+		break;
+	      }
+	  }
+
 	/* Either use a custom-written printer, or use
 	   internal_object_printer or external_object_printer, depending on
 	   whether the object is internal (not visible at Lisp level) or
@@ -1812,8 +1954,9 @@
     default:
       {
 	/* We're in trouble if this happens! */
-	printing_major_badness (printcharfun, "illegal data type", XTYPE (obj),
-				LISP_TO_VOID (obj), BADNESS_INTEGER_OBJECT);
+	printing_major_badness (printcharfun, "ILLEGAL LISP OBJECT TAG TYPE",
+				XTYPE (obj), STORE_LISP_IN_VOID (obj), 0,
+				BADNESS_INTEGER_OBJECT);
 	break;
       }
     }
@@ -1827,10 +1970,10 @@
 print_float (Lisp_Object obj, Lisp_Object printcharfun,
 	     int UNUSED (escapeflag))
 {
-  char pigbuf[350];	/* see comments in float_to_string */
+  Ascbyte pigbuf[350];	/* see comments in float_to_string */
 
   float_to_string (pigbuf, XFLOAT_DATA (obj));
-  write_c_string (printcharfun, pigbuf);
+  write_ascstring (printcharfun, pigbuf);
 }
 
 void
@@ -1866,9 +2009,9 @@
 	  Lisp_Object tem = Fassq (obj, Vprint_gensym_alist);
 	  if (CONSP (tem))
 	    {
-	      write_c_string (printcharfun, "#");
+	      write_ascstring (printcharfun, "#");
 	      print_internal (XCDR (tem), printcharfun, escapeflag);
-	      write_c_string (printcharfun, "#");
+	      write_ascstring (printcharfun, "#");
 	      UNGCPRO;
 	      return;
 	    }
@@ -1886,12 +2029,12 @@
 		tem = make_int (1);
 	      Vprint_gensym_alist = Fcons (Fcons (obj, tem), Vprint_gensym_alist);
 
-	      write_c_string (printcharfun, "#");
+	      write_ascstring (printcharfun, "#");
 	      print_internal (tem, printcharfun, escapeflag);
-	      write_c_string (printcharfun, "=");
+	      write_ascstring (printcharfun, "=");
 	    }
 	}
-      write_c_string (printcharfun, "#:");
+      write_ascstring (printcharfun, "#:");
     }
 
   /* Does it look like an integer or a float? */
@@ -1926,7 +2069,7 @@
          from FSF.  --hniksic */
       confusing = isfloat_string ((char *) data);
     if (confusing)
-      write_c_string (printcharfun, "\\");
+      write_ascstring (printcharfun, "\\");
   }
 
   {
@@ -1951,7 +2094,7 @@
 	  case '[': case ']' : case '?' :
 	    if (i > last)
 	      output_string (printcharfun, 0, name, last, i - last);
-	    write_c_string (printcharfun, "\\");
+	    write_ascstring (printcharfun, "\\");
 	    last = i;
 	  }
       }
@@ -2010,8 +2153,8 @@
   if (alternate_do_pointer + extlen >= alternate_do_size)
     {
       alternate_do_size =
-	max(alternate_do_size * 2, alternate_do_pointer + extlen + 1);
-      XREALLOC_ARRAY (alternate_do_string, char, alternate_do_size);
+	max (alternate_do_size * 2, alternate_do_pointer + extlen + 1);
+      XREALLOC_ARRAY (alternate_do_string, CIbyte, alternate_do_size);
     }
   memcpy (alternate_do_string + alternate_do_pointer, extptr, extlen);
   alternate_do_pointer += extlen;
@@ -2128,23 +2271,44 @@
   return Qnil;
 }
 
+static Lisp_Object
+restore_inhibit_non_essential_conversion_operations (Lisp_Object obj)
+{
+  inhibit_non_essential_conversion_operations = XINT (obj);
+  return Qnil;
+}
+
+/* Bind the value of inhibit_non_essential_conversion_operations to 1
+   in a way that involves no consing. */
+static int
+begin_inhibit_non_essential_conversion_operations (void)
+{
+  int depth =
+    record_unwind_protect
+    (restore_inhibit_non_essential_conversion_operations,
+     make_int (inhibit_non_essential_conversion_operations));
+  inhibit_non_essential_conversion_operations = 1;
+  return depth;
+}
+
 static int debug_print_length   = 50;
 static int debug_print_level    = 15;
 static int debug_print_readably = -1;
 
 /* Restore values temporarily bound by debug_prin1.  We use this approach to
-   avoid consing in debug_prin1.  That is verboten, since debug_prin1 can be
+   avoid consing in debug_prin1.  That is verboten, since debug_print can be
    called by cons debugging code. */
 static Lisp_Object
-debug_prin1_exit (Lisp_Object UNUSED (ignored))
+debug_print_exit (Lisp_Object val)
 {
-  struct debug_bindings *bindings = 
-    (struct debug_bindings *) XOPAQUE (debug_prin1_bindings)->data;
+  struct debug_bindings *bindings =
+    (struct debug_bindings *) GET_VOID_FROM_LISP (val);
   inhibit_non_essential_conversion_operations =
     bindings->inhibit_non_essential_conversion_operations;
   print_depth = bindings->print_depth;
   print_readably = bindings->print_readably;
   print_unbuffered = bindings->print_unbuffered;
+  in_debug_print = bindings->in_debug_print;
   gc_currently_forbidden = bindings->gc_currently_forbidden;
   Vprint_length = bindings->Vprint_length;
   Vprint_level = bindings->Vprint_level;
@@ -2152,6 +2316,47 @@
   return Qnil;
 }
 
+/* Save values and bind them to new values suitable for debug output.  We
+   try very hard to avoid any Lisp allocation (i.e. consing) during the
+   operation of debug printing, since we might be calling it from inside GC
+   or other sensitive places.  This means we have to be a bit careful with
+   record_unwind_protect to not create any temporary Lisp objects. */
+
+static int
+debug_print_enter (struct debug_bindings *bindings)
+{
+  /* by doing this, we trick various things that are non-essential
+     but might cause crashes into not getting executed. */
+  int specdepth;
+
+  bindings->inhibit_non_essential_conversion_operations =
+    inhibit_non_essential_conversion_operations;
+  bindings->print_depth = print_depth;
+  bindings->print_readably = print_readably;
+  bindings->print_unbuffered = print_unbuffered;
+  bindings->in_debug_print = in_debug_print;
+  bindings->gc_currently_forbidden = gc_currently_forbidden;
+  bindings->Vprint_length = Vprint_length;
+  bindings->Vprint_level = Vprint_level;
+  bindings->Vinhibit_quit = Vinhibit_quit;
+  specdepth = record_unwind_protect (debug_print_exit,
+				     STORE_VOID_IN_LISP (bindings));
+
+  inhibit_non_essential_conversion_operations = 1;
+  print_depth = 0;
+  print_readably = debug_print_readably != -1 ? debug_print_readably : 0;
+  print_unbuffered++;
+  in_debug_print = 1;
+  gc_currently_forbidden = 1;
+  if (debug_print_length > 0)
+    Vprint_length = make_int (debug_print_length);
+  if (debug_print_level > 0)
+    Vprint_level = make_int (debug_print_level);
+  Vinhibit_quit = Qt;
+
+  return specdepth;
+}
+
 /* Print an object, `prin1'-style, to various possible debugging outputs.
    Make sure it's completely unbuffered so that, in the event of a crash
    somewhere, we see as much as possible that happened before it.
@@ -2159,34 +2364,9 @@
 static void
 debug_prin1 (Lisp_Object debug_print_obj, int flags)
 {
-  /* This function can GC */
-
-  /* by doing this, we trick various things that are non-essential
-     but might cause crashes into not getting executed. */
-  int specdepth;
-  struct debug_bindings *bindings = 
-    (struct debug_bindings *) XOPAQUE (debug_prin1_bindings)->data;
-
-  bindings->inhibit_non_essential_conversion_operations =
-    inhibit_non_essential_conversion_operations;
-  bindings->print_depth = print_depth;
-  bindings->print_readably = print_readably;
-  bindings->print_unbuffered = print_unbuffered;
-  bindings->gc_currently_forbidden = gc_currently_forbidden;
-  bindings->Vprint_length = Vprint_length;
-  bindings->Vprint_level = Vprint_level;
-  bindings->Vinhibit_quit = Vinhibit_quit;
-  specdepth = record_unwind_protect (debug_prin1_exit, Qnil);
-
-  inhibit_non_essential_conversion_operations = 1;
-  print_depth = 0;
-  print_readably = debug_print_readably != -1 ? debug_print_readably : 0;
-  print_unbuffered++;
-  if (debug_print_length > 0)
-    Vprint_length = make_int (debug_print_length);
-  if (debug_print_level > 0)
-    Vprint_level = make_int (debug_print_level);
-  Vinhibit_quit = Qt;
+  /* This function cannot GC, since GC is forbidden */
+  struct debug_bindings bindings;
+  int specdepth = debug_print_enter (&bindings);
 
   if ((flags & EXT_PRINT_STDOUT) || (flags & EXT_PRINT_STDERR))
     print_internal (debug_print_obj, Qexternal_debugging_output, 1);
@@ -2206,7 +2386,6 @@
 void
 debug_p4 (Lisp_Object obj)
 {
-  inhibit_non_essential_conversion_operations = 1;
   if (STRINGP (obj))
     debug_out ("\"%s\"", XSTRING_DATA (obj));
   else if (CONSP (obj))
@@ -2280,42 +2459,41 @@
 				((struct old_lcrecord_header *) header)->uid));
 #endif /* not NEW_GC */
     }
-
-  inhibit_non_essential_conversion_operations = 0;
 }
 
-static void
+static int
 ext_print_begin (int dest)
 {
+  int depth = begin_inhibit_non_essential_conversion_operations ();
   if (dest & EXT_PRINT_ALTERNATE)
     alternate_do_pointer = 0;
   if (dest & (EXT_PRINT_STDERR | EXT_PRINT_STDOUT))
     stdout_clear_before_next_output = 1;
+  return depth;
 }
 
 static void
-ext_print_end (int dest)
+ext_print_end (int dest, int depth)
 {
   if (dest & (EXT_PRINT_MSWINDOWS | EXT_PRINT_STDERR | EXT_PRINT_STDOUT))
     external_out (dest & (EXT_PRINT_MSWINDOWS | EXT_PRINT_STDERR |
 			  EXT_PRINT_STDOUT), "\n");
+  unbind_to (depth);
 }
 
 static void
 external_debug_print (Lisp_Object object, int dest)
 {
-  ext_print_begin (dest);
+  int depth = ext_print_begin (dest);
   debug_prin1 (object, dest);
-  ext_print_end (dest);
+  ext_print_end (dest, depth);
 }
 
 void
 debug_p3 (Lisp_Object obj)
 {
   debug_p4 (obj);
-  inhibit_non_essential_conversion_operations = 1;
   debug_out ("\n");
-  inhibit_non_essential_conversion_operations = 0;
 }
 
 void
@@ -2347,22 +2525,9 @@
 void
 debug_backtrace (void)
 {
-  /* This function can GC */
-
-  /* by doing this, we trick various things that are non-essential
-     but might cause crashes into not getting executed. */
-  int specdepth = 
-    internal_bind_int (&inhibit_non_essential_conversion_operations, 1);
-
-  internal_bind_int (&print_depth, 0);
-  internal_bind_int (&print_readably, 0);
-  internal_bind_int (&print_unbuffered, print_unbuffered + 1);
-  if (debug_print_length > 0)
-    internal_bind_lisp_object (&Vprint_length, make_int (debug_print_length));
-  if (debug_print_level > 0)
-    internal_bind_lisp_object (&Vprint_level, make_int (debug_print_level));
-  /* #### Do we need this?  It was in the old code. */
-  internal_bind_lisp_object (&Vinhibit_quit, Vinhibit_quit);
+  /* This function cannot GC, since GC is forbidden */
+  struct debug_bindings bindings;
+  int specdepth = debug_print_enter (&bindings);
 
   Fbacktrace (Qexternal_debugging_output, Qt);
   stderr_out ("\n");
@@ -2383,6 +2548,7 @@
 {
   int first = 1;
   struct backtrace *bt = backtrace_list;
+
   debug_out ("   [");
   while (length > 0 && bt)
     {
@@ -2562,10 +2728,9 @@
 */ );
   Vprint_message_label = Qprint;
 
-  debug_prin1_bindings =
-    make_opaque (OPAQUE_UNINIT, sizeof (struct debug_bindings));
-  staticpro (&debug_prin1_bindings);
-
+  /* The exact size doesn't matter since we realloc when necessary.
+     Use CIbyte instead of Ibyte so that debuggers show the associated
+     string automatically. */
   alternate_do_size = 5000;
-  alternate_do_string = xnew_array(char, 5000);
+  alternate_do_string = xnew_array (CIbyte, 5000);
 }