import gdb-1999-12-06 snapshot
[deliverable/binutils-gdb.git] / gdb / utils.c
index 314a8310b45737ef28ed49c146be87cee7708a30..cbaf0cc9dfd3e251095130e0f2146e0bde267263 100644 (file)
@@ -21,9 +21,7 @@
 #include "defs.h"
 #include <ctype.h>
 #include "gdb_string.h"
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
+#include "event-top.h"
 
 #ifdef HAVE_CURSES_H
 #include <curses.h>
 
 #include <readline/readline.h>
 
+#undef XMALLOC
+#define XMALLOC(TYPE) ((TYPE*) xmalloc (sizeof (TYPE)))
+
 /* readline defines this.  */
 #undef savestring
 
 void (*error_begin_hook) PARAMS ((void));
 
+/* Holds the last error message issued by gdb */
+
+static GDB_FILE *gdb_lasterr;
+
 /* Prototypes for local functions */
 
 static void vfprintf_maybe_filtered PARAMS ((GDB_FILE *, const char *,
@@ -74,12 +79,6 @@ set_width_command PARAMS ((char *, int, struct cmd_list_element *));
 static void
 set_width PARAMS ((void));
 
-/* If this definition isn't overridden by the header files, assume
-   that isatty and fileno exist on this system.  */
-#ifndef ISATTY
-#define ISATTY(FP)     (isatty (fileno (FP)))
-#endif
-
 #ifndef GDB_FILE_ISATTY
 #define GDB_FILE_ISATTY(GDB_FILE_PTR)   (gdb_file_isatty(GDB_FILE_PTR))
 #endif
@@ -91,12 +90,15 @@ static struct cleanup *cleanup_chain;       /* cleaned up after a failed command */
 static struct cleanup *final_cleanup_chain;    /* cleaned up when gdb exits */
 static struct cleanup *run_cleanup_chain;      /* cleaned up on each 'run' */
 static struct cleanup *exec_cleanup_chain;     /* cleaned up on each execution command */
+/* cleaned up on each error from within an execution command */
+static struct cleanup *exec_error_cleanup_chain; 
 
 /* Pointer to what is left to do for an execution command after the
    target stops. Used only in asynchronous mode, by targets that
    support async execution.  The finish and until commands use it. So
    does the target extended-remote command. */
 struct continuation *cmd_continuation;
+struct continuation *intermediate_continuation;
 
 /* Nonzero if we have job control. */
 
@@ -188,6 +190,14 @@ make_exec_cleanup (function, arg)
   return make_my_cleanup (&exec_cleanup_chain, function, arg);
 }
 
+struct cleanup *
+make_exec_error_cleanup (function, arg)
+     void (*function) PARAMS ((PTR));
+     PTR arg;
+{
+  return make_my_cleanup (&exec_error_cleanup_chain, function, arg);
+}
+
 static void
 do_freeargv (arg)
      void *arg;
@@ -202,6 +212,18 @@ make_cleanup_freeargv (arg)
   return make_my_cleanup (&cleanup_chain, do_freeargv, arg);
 }
 
+static void
+do_gdb_file_delete (void *arg)
+{
+  gdb_file_delete (arg);
+}
+
+struct cleanup *
+make_cleanup_gdb_file_delete (struct gdb_file *arg)
+{
+  return make_my_cleanup (&cleanup_chain, do_gdb_file_delete, arg);
+}
+
 struct cleanup *
 make_my_cleanup (pmy_chain, function, arg)
      struct cleanup **pmy_chain;
@@ -251,6 +273,13 @@ do_exec_cleanups (old_chain)
   do_my_cleanups (&exec_cleanup_chain, old_chain);
 }
 
+void
+do_exec_error_cleanups (old_chain)
+     register struct cleanup *old_chain;
+{
+  do_my_cleanups (&exec_error_cleanup_chain, old_chain);
+}
+
 void
 do_my_cleanups (pmy_chain, old_chain)
      register struct cleanup **pmy_chain;
@@ -282,6 +311,13 @@ discard_final_cleanups (old_chain)
   discard_my_cleanups (&final_cleanup_chain, old_chain);
 }
 
+void
+discard_exec_error_cleanups (old_chain)
+     register struct cleanup *old_chain;
+{
+  discard_my_cleanups (&exec_error_cleanup_chain, old_chain);
+}
+
 void
 discard_my_cleanups (pmy_chain, old_chain)
      register struct cleanup **pmy_chain;
@@ -371,7 +407,7 @@ null_cleanup (arg)
 }
 
 /* Add a continuation to the continuation list, the gloabl list
-   cmd_continuation. */
+   cmd_continuation. The new continuation will be added at the front.*/
 void
 add_continuation (continuation_hook, arg_list)
      void (*continuation_hook) PARAMS ((struct continuation_arg *));
@@ -387,20 +423,113 @@ add_continuation (continuation_hook, arg_list)
 }
 
 /* Walk down the cmd_continuation list, and execute all the
-   continuations. */
+   continuations. There is a problem though. In some cases new
+   continuations may be added while we are in the middle of this
+   loop. If this happens they will be added in the front, and done
+   before we have a chance of exhausting those that were already
+   there. We need to then save the beginning of the list in a pointer
+   and do the continuations from there on, instead of using the
+   global beginning of list as our iteration pointer.*/
 void
 do_all_continuations ()
+{
+  struct continuation *continuation_ptr;
+  struct continuation *saved_continuation;
+
+  /* Copy the list header into another pointer, and set the global
+     list header to null, so that the global list can change as a side
+     effect of invoking the continuations and the processing of
+     the preexisting continuations will not be affected. */
+  continuation_ptr = cmd_continuation;
+  cmd_continuation = NULL;
+
+  /* Work now on the list we have set aside. */
+  while (continuation_ptr)
+     {
+       (continuation_ptr->continuation_hook) (continuation_ptr->arg_list);
+       saved_continuation = continuation_ptr;
+       continuation_ptr = continuation_ptr->next;
+       free (saved_continuation);
+     }
+}
+
+/* Walk down the cmd_continuation list, and get rid of all the
+   continuations. */
+void
+discard_all_continuations ()
 {
   struct continuation *continuation_ptr;
 
   while (cmd_continuation)
     {
-      (cmd_continuation->continuation_hook) (cmd_continuation->arg_list);
       continuation_ptr = cmd_continuation;
       cmd_continuation = continuation_ptr->next;
       free (continuation_ptr);
     }
 }
+
+/* Add a continuation to the continuation list, the gloabl list
+   intermediate_continuation. The new continuation will be added at the front.*/
+void
+add_intermediate_continuation (continuation_hook, arg_list)
+     void (*continuation_hook) PARAMS ((struct continuation_arg *));
+     struct continuation_arg *arg_list;
+{
+  struct continuation *continuation_ptr;
+
+  continuation_ptr = (struct continuation *) xmalloc (sizeof (struct continuation));
+  continuation_ptr->continuation_hook = continuation_hook;
+  continuation_ptr->arg_list = arg_list;
+  continuation_ptr->next = intermediate_continuation;
+  intermediate_continuation = continuation_ptr;
+}
+
+/* Walk down the cmd_continuation list, and execute all the
+   continuations. There is a problem though. In some cases new
+   continuations may be added while we are in the middle of this
+   loop. If this happens they will be added in the front, and done
+   before we have a chance of exhausting those that were already
+   there. We need to then save the beginning of the list in a pointer
+   and do the continuations from there on, instead of using the
+   global beginning of list as our iteration pointer.*/
+void
+do_all_intermediate_continuations ()
+{
+  struct continuation *continuation_ptr;
+  struct continuation *saved_continuation;
+
+  /* Copy the list header into another pointer, and set the global
+     list header to null, so that the global list can change as a side
+     effect of invoking the continuations and the processing of
+     the preexisting continuations will not be affected. */
+  continuation_ptr = intermediate_continuation;
+  intermediate_continuation = NULL;
+
+  /* Work now on the list we have set aside. */
+  while (continuation_ptr)
+     {
+       (continuation_ptr->continuation_hook) (continuation_ptr->arg_list);
+       saved_continuation = continuation_ptr;
+       continuation_ptr = continuation_ptr->next;
+       free (saved_continuation);
+     }
+}
+
+/* Walk down the cmd_continuation list, and get rid of all the
+   continuations. */
+void
+discard_all_intermediate_continuations ()
+{
+  struct continuation *continuation_ptr;
+
+  while (intermediate_continuation)
+    {
+      continuation_ptr = intermediate_continuation;
+      intermediate_continuation = continuation_ptr->next;
+      free (continuation_ptr);
+    }
+}
+
 \f
 
 /* Print a warning message.  Way to use this is to call warning_begin,
@@ -471,33 +600,77 @@ error_begin ()
    The first argument STRING is the error message, used as a fprintf string,
    and the remaining args are passed as arguments to it.  */
 
+NORETURN void
+verror (const char *string, va_list args)
+{
+  char *err_string;
+  struct cleanup *err_string_cleanup;
+  /* FIXME: cagney/1999-11-10: All error calls should come here.
+     Unfortunatly some code uses the sequence: error_begin(); print
+     error message; return_to_top_level.  That code should be
+     flushed. */
+  error_begin ();
+  /* NOTE: It's tempting to just do the following...
+       vfprintf_filtered (gdb_stderr, string, args);
+     and then follow with a similar looking statement to cause the message
+     to also go to gdb_lasterr.  But if we do this, we'll be traversing the
+     va_list twice which works on some platforms and fails miserably on
+     others. */
+  /* Save it as the last error */
+  gdb_file_rewind (gdb_lasterr);
+  vfprintf_filtered (gdb_lasterr, string, args);
+  /* Retrieve the last error and print it to gdb_stderr */
+  err_string = error_last_message ();
+  err_string_cleanup = make_cleanup (free, err_string);
+  fputs_filtered (err_string, gdb_stderr);
+  fprintf_filtered (gdb_stderr, "\n");
+  do_cleanups (err_string_cleanup);
+  return_to_top_level (RETURN_ERROR);
+}
+
 NORETURN void
 error (const char *string,...)
 {
   va_list args;
   va_start (args, string);
-  if (error_hook)
-    (*error_hook) ();
-  else
-    {
-      error_begin ();
-      vfprintf_filtered (gdb_stderr, string, args);
-      fprintf_filtered (gdb_stderr, "\n");
-      va_end (args);
-      return_to_top_level (RETURN_ERROR);
-    }
+  verror (string, args);
+  va_end (args);
+}
+
+NORETURN void
+error_stream (GDB_FILE *stream)
+{
+  long size;
+  char *msg = gdb_file_xstrdup (stream, &size);
+  make_cleanup (free, msg);
+  error ("%s", msg);
+}
+
+/* Get the last error message issued by gdb */
+
+char *
+error_last_message (void)
+{
+  long len;
+  return gdb_file_xstrdup (gdb_lasterr, &len);
 }
+  
+/* This is to be called by main() at the very beginning */
 
+void
+error_init (void)
+{
+  gdb_lasterr = mem_fileopen ();
+}
 
 /* Print a message reporting an internal error. Ask the user if they
    want to continue, dump core, or just exit. */
 
 NORETURN void
-internal_error (char *string, ...)
+internal_verror (const char *fmt, va_list ap)
 {
   static char msg[] = "Internal GDB error: recursive internal error.\n";
   static int dejavu = 0;
-  va_list args;
   int continue_p;
   int dump_core_p;
 
@@ -519,9 +692,7 @@ internal_error (char *string, ...)
 
   /* Try to get the message out */
   fputs_unfiltered ("gdb-internal-error: ", gdb_stderr);
-  va_start (args, string);
-  vfprintf_unfiltered (gdb_stderr, string, args);
-  va_end (args);
+  vfprintf_unfiltered (gdb_stderr, fmt, ap);
   fputs_unfiltered ("\n", gdb_stderr);
 
   /* Default (no case) is to quit GDB.  When in batch mode this
@@ -555,6 +726,15 @@ Create a core file containing the current state of GDB? ");
   return_to_top_level (RETURN_ERROR);
 }
 
+NORETURN void
+internal_error (char *string, ...)
+{
+  va_list ap;
+  va_start (ap, string);
+  internal_verror (string, ap);
+  va_end (ap);
+}
+
 /* The strerror() function can return NULL for errno values that are
    out of range.  Provide a "safe" version that always returns a
    printable string. */
@@ -710,7 +890,7 @@ notice_quit ()
     immediate_quit = 1;
 }
 
-#else /* !defined(__GO32__) && !defined(_MSC_VER) */
+#else /* !defined(_MSC_VER) */
 
 void
 notice_quit ()
@@ -718,7 +898,7 @@ notice_quit ()
   /* Done by signals */
 }
 
-#endif /* !defined(__GO32__) && !defined(_MSC_VER) */
+#endif /* !defined(_MSC_VER) */
 
 /* Control C comes here */
 void
@@ -1004,9 +1184,7 @@ print_spaces (n, file)
 /* Print a host address.  */
 
 void
-gdb_print_address (addr, stream)
-     PTR addr;
-     GDB_FILE *stream;
+gdb_print_host_address (void *addr, struct gdb_file *stream)
 {
 
   /* We could use the %p conversion specifier to fprintf if we had any
@@ -1304,7 +1482,7 @@ fputstrn_unfiltered (str, n, quoter, stream)
 
 /* Number of lines per page or UINT_MAX if paging is disabled.  */
 static unsigned int lines_per_page;
-/* Number of chars per line or UNIT_MAX is line folding is disabled.  */
+/* Number of chars per line or UNIT_MAX if line folding is disabled.  */
 static unsigned int chars_per_line;
 /* Current count of lines printed on this page, chars on this line.  */
 static unsigned int lines_printed, chars_printed;
@@ -1480,7 +1658,7 @@ prompt_for_continue ()
        ++p;
       if (p[0] == 'q')
        {
-         if (!async_p)
+         if (!event_loop_p)
            request_quit (SIGINT);
          else
            async_request_quit (0);
@@ -1580,6 +1758,7 @@ begin_line ()
 /* ``struct gdb_file'' implementation that maps directly onto
    <stdio.h>'s FILE. */
 
+static gdb_file_write_ftype stdio_file_write;
 static gdb_file_fputs_ftype stdio_file_fputs;
 static gdb_file_isatty_ftype stdio_file_isatty;
 static gdb_file_delete_ftype stdio_file_delete;
@@ -1607,6 +1786,7 @@ stdio_file_new (file, close_p)
   stdio->close_p = close_p;
   set_gdb_file_data (gdb_file, stdio, stdio_file_delete);
   set_gdb_file_flush (gdb_file, stdio_file_flush);
+  set_gdb_file_write (gdb_file, stdio_file_write);
   set_gdb_file_fputs (gdb_file, stdio_file_fputs);
   set_gdb_file_isatty (gdb_file, stdio_file_isatty);
   return gdb_file;
@@ -1618,7 +1798,7 @@ stdio_file_delete (file)
 {
   struct stdio_file *stdio = gdb_file_data (file);
   if (stdio->magic != &stdio_file_magic)
-    error ("Internal error: bad magic number");
+    internal_error ("stdio_file_delete: bad magic number");
   if (stdio->close_p)
     {
       fclose (stdio->file);
@@ -1632,10 +1812,19 @@ stdio_file_flush (file)
 {
   struct stdio_file *stdio = gdb_file_data (file);
   if (stdio->magic != &stdio_file_magic)
-    error ("Internal error: bad magic number");
+    internal_error ("stdio_file_flush: bad magic number");
   fflush (stdio->file);
 }
 
+static void
+stdio_file_write (struct gdb_file *file, const char *buf, long length_buf)
+{
+  struct stdio_file *stdio = gdb_file_data (file);
+  if (stdio->magic != &stdio_file_magic)
+    internal_error ("stdio_file_write: bad magic number");
+  fwrite (buf, length_buf, 1, stdio->file);
+}
+
 static void
 stdio_file_fputs (linebuffer, file)
      const char *linebuffer;
@@ -1643,7 +1832,7 @@ stdio_file_fputs (linebuffer, file)
 {
   struct stdio_file *stdio = gdb_file_data (file);
   if (stdio->magic != &stdio_file_magic)
-    error ("Internal error: bad magic number");
+    internal_error ("stdio_file_fputs: bad magic number");
   fputs (linebuffer, stdio->file);
 }
 
@@ -1653,7 +1842,7 @@ stdio_file_isatty (file)
 {
   struct stdio_file *stdio = gdb_file_data (file);
   if (stdio->magic != &stdio_file_magic)
-    error ("Internal error: bad magic number");
+    internal_error ("stdio_file_isatty: bad magic number");
   return (isatty (fileno (stdio->file)));
 }
 
@@ -1667,9 +1856,127 @@ stdio_fileopen (file)
 }
 
 
+/* A pure memory based ``struct gdb_file'' that can be used an output
+   buffer. The buffers accumulated contents are available via
+   gdb_file_put(). */
+
+struct mem_file
+  {
+    int *magic;
+    char *buffer;
+    int sizeof_buffer;
+    int length_buffer;
+  };
+
+static gdb_file_rewind_ftype mem_file_rewind;
+static gdb_file_put_ftype mem_file_put;
+static gdb_file_write_ftype mem_file_write;
+static gdb_file_delete_ftype mem_file_delete;
+static struct gdb_file *mem_file_new PARAMS ((void));
+static int mem_file_magic;
+
+static struct gdb_file *
+mem_file_new (void)
+{
+  struct mem_file *stream = XMALLOC (struct mem_file);
+  struct gdb_file *file = gdb_file_new ();
+  set_gdb_file_data (file, stream, mem_file_delete);
+  set_gdb_file_rewind (file, mem_file_rewind);
+  set_gdb_file_put (file, mem_file_put);
+  set_gdb_file_write (file, mem_file_write);
+  stream->magic = &mem_file_magic;
+  stream->buffer = NULL;
+  stream->sizeof_buffer = 0;
+  stream->length_buffer = 0;
+  return file;
+}
+
+static void
+mem_file_delete (struct gdb_file *file)
+{
+  struct mem_file *stream = gdb_file_data (file);
+  if (stream->magic != &mem_file_magic)
+    internal_error ("mem_file_delete: bad magic number");
+  if (stream->buffer != NULL)
+    free (stream->buffer);
+  free (stream);
+}
+
+struct gdb_file *
+mem_fileopen (void)
+{
+  return mem_file_new ();
+}
+
+static void
+mem_file_rewind (struct gdb_file *file)
+{
+  struct mem_file *stream = gdb_file_data (file);
+  if (stream->magic != &mem_file_magic)
+    internal_error ("mem_file_rewind: bad magic number");
+  stream->length_buffer = 0;
+}
+
+static void
+mem_file_put (struct gdb_file *file,
+             gdb_file_put_method_ftype *write,
+             void *dest)
+{
+  struct mem_file *stream = gdb_file_data (file);
+  if (stream->magic != &mem_file_magic)
+    internal_error ("mem_file_put: bad magic number");
+  if (stream->length_buffer > 0)
+    write (dest, stream->buffer, stream->length_buffer);
+}
+
+void
+mem_file_write (struct gdb_file *file,
+               const char *buffer,
+               long length_buffer)
+{
+  struct mem_file *stream = gdb_file_data (file);
+  if (stream->magic != &mem_file_magic)
+    internal_error ("mem_file_write: bad magic number");
+  if (stream->buffer == NULL)
+    {
+      stream->length_buffer = length_buffer;
+      stream->sizeof_buffer = length_buffer;
+      stream->buffer = xmalloc (stream->sizeof_buffer);
+      memcpy (stream->buffer, buffer, length_buffer);
+    }
+  else
+    {
+      int new_length = stream->length_buffer + length_buffer;
+      if (new_length >= stream->sizeof_buffer)
+       {
+         stream->sizeof_buffer = new_length;
+         stream->buffer = xrealloc (stream->buffer, stream->sizeof_buffer);
+       }
+      memcpy (stream->buffer + stream->length_buffer, buffer, length_buffer);
+      stream->length_buffer = new_length;
+    }
+}
+
 /* A ``struct gdb_file'' that is compatible with all the legacy
    code. */
 
+/* new */
+enum streamtype
+{
+  afile,
+  astring
+};
+
+/* new */
+struct tui_stream
+{
+  int *ts_magic;
+  enum streamtype ts_streamtype;
+  FILE *ts_filestream;
+  char *ts_strbuf;
+  int ts_buflen;
+};
+
 static gdb_file_flush_ftype tui_file_flush;
 extern gdb_file_fputs_ftype tui_file_fputs;
 static gdb_file_isatty_ftype tui_file_isatty;
@@ -1700,7 +2007,7 @@ tui_file_delete (file)
 {
   struct tui_stream *tmpstream = gdb_file_data (file);
   if (tmpstream->ts_magic != &tui_file_magic)
-    error ("Internal error: bad magic number");
+    internal_error ("tui_file_delete: bad magic number");
   if ((tmpstream->ts_streamtype == astring) &&
       (tmpstream->ts_strbuf != NULL))
     {
@@ -1722,13 +2029,34 @@ tui_fileopen (stream)
   return file;
 }
 
+struct gdb_file *
+tui_sfileopen (n)
+     int n;
+{
+  struct gdb_file *file = tui_file_new ();
+  struct tui_stream *tmpstream = gdb_file_data (file);
+  tmpstream->ts_streamtype = astring;
+  tmpstream->ts_filestream = NULL;
+  if (n > 0)
+    {
+      tmpstream->ts_strbuf = xmalloc ((n + 1) * sizeof (char));
+      tmpstream->ts_strbuf[0] = '\0';
+    }
+  else
+    /* Do not allocate the buffer now.  The first time something is printed
+       one will be allocated by tui_file_adjust_strbuf()  */
+    tmpstream->ts_strbuf = NULL;
+  tmpstream->ts_buflen = n;
+  return file;
+}
+
 static int
 tui_file_isatty (file)
      struct gdb_file *file;
 {
   struct tui_stream *stream = gdb_file_data (file);
   if (stream->ts_magic != &tui_file_magic)
-    error ("Internal error: bad magic number");
+    internal_error ("tui_file_isatty: bad magic number");
   if (stream->ts_streamtype == afile)
     return (isatty (fileno (stream->ts_filestream)));
   else
@@ -1741,76 +2069,118 @@ tui_file_rewind (file)
 {
   struct tui_stream *stream = gdb_file_data (file);
   if (stream->ts_magic != &tui_file_magic)
-    error ("Internal error: bad magic number");
+    internal_error ("tui_file_rewind: bad magic number");
   stream->ts_strbuf[0] = '\0';
 }
 
 static void
-tui_file_put (file, dest)
-     struct gdb_file *file;
-     struct gdb_file *dest;
+tui_file_put (struct gdb_file *file,
+             gdb_file_put_method_ftype *write,
+             void *dest)
 {
   struct tui_stream *stream = gdb_file_data (file);
   if (stream->ts_magic != &tui_file_magic)
-    error ("Internal error: bad magic number");
+    internal_error ("tui_file_put: bad magic number");
   if (stream->ts_streamtype == astring)
-    {
-      fputs_unfiltered (stream->ts_strbuf, dest);
-    }
+    write (dest, stream->ts_strbuf, strlen (stream->ts_strbuf));
 }
 
-GDB_FILE *
-gdb_file_init_astring (n)
-     int n;
-{
-  struct gdb_file *file = tui_file_new ();
-  struct tui_stream *tmpstream = gdb_file_data (file);
-  if (tmpstream->ts_magic != &tui_file_magic)
-    error ("Internal error: bad magic number");
+/* All TUI I/O sent to the *_filtered and *_unfiltered functions
+   eventually ends up here.  The fputs_unfiltered_hook is primarily
+   used by GUIs to collect all output and send it to the GUI, instead
+   of the controlling terminal.  Only output to gdb_stdout and
+   gdb_stderr are sent to the hook.  Everything else is sent on to
+   fputs to allow file I/O to be handled appropriately.  */
 
-  tmpstream->ts_streamtype = astring;
-  tmpstream->ts_filestream = NULL;
-  if (n > 0)
-    {
-      tmpstream->ts_strbuf = xmalloc ((n + 1) * sizeof (char));
-      tmpstream->ts_strbuf[0] = '\0';
-    }
-  else
-    tmpstream->ts_strbuf = NULL;
-  tmpstream->ts_buflen = n;
-
-  return file;
-}
+/* FIXME: Should be broken up and moved to a TUI specific file. */
 
 void
-gdb_file_deallocate (streamptr)
-     GDB_FILE **streamptr;
+tui_file_fputs (linebuffer, file)
+     const char *linebuffer;
+     GDB_FILE *file;
 {
-  gdb_file_delete (*streamptr);
-  *streamptr = NULL;
+  struct tui_stream *stream = gdb_file_data (file);
+#if defined(TUI)
+  extern int tui_owns_terminal;
+#endif
+  /* NOTE: cagney/1999-10-13: The use of fputs_unfiltered_hook is
+     seriously discouraged.  Those wanting to hook output should
+     instead implement their own gdb_file object and install that. See
+     also tui_file_flush(). */
+  if (fputs_unfiltered_hook
+      && (file == gdb_stdout
+         || file == gdb_stderr))
+    fputs_unfiltered_hook (linebuffer, file);
+  else
+    {
+#if defined(TUI)
+      if (tui_version && tui_owns_terminal)
+       {
+         /* If we get here somehow while updating the TUI (from
+          * within a tuiDo(), then we need to temporarily 
+          * set up the terminal for GDB output. This probably just
+          * happens on error output.
+          */
+
+         if (stream->ts_streamtype == astring)
+           {
+             tui_file_adjust_strbuf (strlen (linebuffer), stream);
+             strcat (stream->ts_strbuf, linebuffer);
+           }
+         else
+           {
+             tuiTermUnsetup (0, (tui_version) ? cmdWin->detail.commandInfo.curch : 0);
+             fputs (linebuffer, stream->ts_filestream);
+             tuiTermSetup (0);
+             if (linebuffer[strlen (linebuffer) - 1] == '\n')
+               tuiClearCommandCharCount ();
+             else
+               tuiIncrCommandCharCountBy (strlen (linebuffer));
+           }
+       }
+      else
+       {
+         /* The normal case - just do a fputs() */
+         if (stream->ts_streamtype == astring)
+           {
+             tui_file_adjust_strbuf (strlen (linebuffer), stream);
+             strcat (stream->ts_strbuf, linebuffer);
+           }
+         else
+           fputs (linebuffer, stream->ts_filestream);
+       }
+
+
+#else
+      if (stream->ts_streamtype == astring)
+       {
+         tui_file_adjust_strbuf (strlen (linebuffer), file);
+         strcat (stream->ts_strbuf, linebuffer);
+       }
+      else
+       fputs (linebuffer, stream->ts_filestream);
+#endif
+    }
 }
 
 char *
-gdb_file_get_strbuf (file)
-     GDB_FILE *file;
+tui_file_get_strbuf (struct gdb_file *file)
 {
   struct tui_stream *stream = gdb_file_data (file);
   if (stream->ts_magic != &tui_file_magic)
-    error ("Internal error: bad magic number");
+    internal_error ("tui_file_get_strbuf: bad magic number");
   return (stream->ts_strbuf);
 }
 
 /* adjust the length of the buffer by the amount necessary
    to accomodate appending a string of length N to the buffer contents */
 void
-gdb_file_adjust_strbuf (n, file)
-     int n;
-     GDB_FILE *file;
+tui_file_adjust_strbuf (int n, struct gdb_file *file)
 {
   struct tui_stream *stream = gdb_file_data (file);
   int non_null_chars;
   if (stream->ts_magic != &tui_file_magic)
-    error ("Internal error: bad magic number");
+    internal_error ("tui_file_adjust_strbuf: bad magic number");
 
   if (stream->ts_streamtype != astring)
     return;
@@ -1848,30 +2218,29 @@ tui_file_flush (file)
 {
   struct tui_stream *stream = gdb_file_data (file);
   if (stream->ts_magic != &tui_file_magic)
-    error ("Internal error: bad magic number");
-  if (flush_hook
-      && (file == gdb_stdout
-         || file == gdb_stderr))
-    {
-      flush_hook (file);
-      return;
-    }
+    internal_error ("tui_file_flush: bad magic number");
 
-  fflush (stream->ts_filestream);
-}
+  /* NOTE: cagney/1999-10-12: If we've been linked with code that uses
+     fputs_unfiltered_hook then we assume that it doesn't need to know
+     about flushes.  Code that does need to know about flushes can
+     implement a proper gdb_file object. */
+  if (fputs_unfiltered_hook)
+    return;
 
-void
-gdb_fclose (streamptr)
-     GDB_FILE **streamptr;
-{
-  gdb_file_delete (*streamptr);
-  *streamptr = NULL;
+  switch (stream->ts_streamtype)
+    {
+    case astring:
+      break;
+    case afile:
+      fflush (stream->ts_filestream);
+      break;
+    }
 }
 
-
 /* Implement the ``struct gdb_file'' object. */
 
 static gdb_file_isatty_ftype null_file_isatty;
+static gdb_file_write_ftype null_file_write;
 static gdb_file_fputs_ftype null_file_fputs;
 static gdb_file_flush_ftype null_file_flush;
 static gdb_file_delete_ftype null_file_delete;
@@ -1880,7 +2249,9 @@ static gdb_file_put_ftype null_file_put;
 
 struct gdb_file
   {
+    int *magic;
     gdb_file_flush_ftype *to_flush;
+    gdb_file_write_ftype *to_write;
     gdb_file_fputs_ftype *to_fputs;
     gdb_file_delete_ftype *to_delete;
     gdb_file_isatty_ftype *to_isatty;
@@ -1888,13 +2259,16 @@ struct gdb_file
     gdb_file_put_ftype *to_put;
     void *to_data;
   };
+int gdb_file_magic;
 
 struct gdb_file *
 gdb_file_new ()
 {
   struct gdb_file *file = xmalloc (sizeof (struct gdb_file));
+  file->magic = &gdb_file_magic;
   set_gdb_file_data (file, NULL, null_file_delete);
   set_gdb_file_flush (file, null_file_flush);
+  set_gdb_file_write (file, null_file_write);
   set_gdb_file_fputs (file, null_file_fputs);
   set_gdb_file_isatty (file, null_file_isatty);
   set_gdb_file_rewind (file, null_file_rewind);
@@ -1925,9 +2299,9 @@ null_file_rewind (file)
 }
 
 static void
-null_file_put (file, src)
-     struct gdb_file *file;
-     struct gdb_file *src;
+null_file_put (struct gdb_file *file,
+              gdb_file_put_method_ftype *write,
+              void *dest)
 {
   return;
 }
@@ -1939,12 +2313,48 @@ null_file_flush (file)
   return;
 }
 
+static void
+null_file_write (struct gdb_file *file,
+                const char *buf,
+                long sizeof_buf)
+{
+  if (file->to_fputs == null_file_fputs)
+    /* Both the write and fputs methods are null. Discard the
+       request. */
+    return;
+  else
+    {
+      /* The fputs method isn't null, slowly pass the write request
+         onto that.  FYI, this isn't as bad as it may look - the
+         current (as of 1999-11-07) printf_* function calls fputc and
+         fputc does exactly the below.  By having a write function it
+         is possible to clean up that code.  */
+      int i;
+      char b[2];
+      b[1] = '\0';
+      for (i = 0; i < sizeof_buf; i++)
+       {
+         b[0] = buf[i];
+         file->to_fputs (b, file);
+       }
+      return;
+    }
+}
+
 static void
 null_file_fputs (buf, file)
      const char *buf;
      struct gdb_file *file;
 {
-  return;
+  if (file->to_write == null_file_write)
+    /* Both the write and fputs methods are null. Discard the
+       request. */
+    return;
+  else
+    {
+      /* The write method was implemented, use that. */
+      file->to_write (file, buf, strlen (buf));
+    }
 }
 
 static void
@@ -1958,6 +2368,8 @@ void *
 gdb_file_data (file)
      struct gdb_file *file;
 {
+  if (file->magic != &gdb_file_magic)
+    internal_error ("gdb_file_data: bad magic number");
   return file->to_data;
 }
 
@@ -1983,11 +2395,19 @@ gdb_file_rewind (file)
 }
 
 void
-gdb_file_put (file, dest)
-     struct gdb_file *file;
-     struct gdb_file *dest;
+gdb_file_put (struct gdb_file *file,
+             gdb_file_put_method_ftype *write,
+             void *dest)
 {
-  file->to_put (file, dest);
+  file->to_put (file, write, dest);
+}
+
+void
+gdb_file_write (struct gdb_file *file,
+               const char *buf,
+               long length_buf)
+{
+  file->to_write (file, buf, length_buf);
 }
 
 void
@@ -2030,6 +2450,13 @@ set_gdb_file_put (file, put)
   file->to_put = put;
 }
 
+void
+set_gdb_file_write (struct gdb_file *file,
+                   gdb_file_write_ftype *write)
+{
+  file->to_write = write;
+}
+
 void
 set_gdb_file_fputs (file, fputs)
      struct gdb_file *file;
@@ -2048,6 +2475,43 @@ set_gdb_file_data (file, data, delete)
   file->to_delete = delete;
 }
 
+/* gdb_file utility function for converting a ``struct gdb_file'' into
+   a memory buffer''. */
+
+struct accumulated_gdb_file
+{
+  char *buffer;
+  long length;
+};
+
+static void
+do_gdb_file_xstrdup (void *context, const char *buffer, long length)
+{
+  struct accumulated_gdb_file *acc = context;
+  if (acc->buffer == NULL)
+    acc->buffer = xmalloc (length + 1);
+  else
+    acc->buffer = xrealloc (acc->buffer, acc->length + length + 1);
+  memcpy (acc->buffer + acc->length, buffer, length);
+  acc->length += length;
+  acc->buffer[acc->length] = '\0';
+}
+
+char *
+gdb_file_xstrdup (struct gdb_file *file,
+                 long *length)
+{
+  struct accumulated_gdb_file acc;
+  acc.buffer = NULL;
+  acc.length = 0;
+  gdb_file_put (file, do_gdb_file_xstrdup, &acc);
+  if (acc.buffer == NULL)
+    acc.buffer = xstrdup ("");
+  *length = acc.length;
+  return acc.buffer;
+}
+
+
 /* Like fputs but if FILTER is true, pause after every screenful.
 
    Regardless of FILTER can wrap at points other than the final
@@ -2177,11 +2641,8 @@ int
 putchar_unfiltered (c)
      int c;
 {
-  char buf[2];
-
-  buf[0] = c;
-  buf[1] = 0;
-  fputs_unfiltered (buf, gdb_stdout);
+  char buf = c;
+  gdb_file_write (gdb_stdout, &buf, 1);
   return c;
 }
 
@@ -2190,11 +2651,8 @@ fputc_unfiltered (c, stream)
      int c;
      GDB_FILE *stream;
 {
-  char buf[2];
-
-  buf[0] = c;
-  buf[1] = 0;
-  fputs_unfiltered (buf, stream);
+  char buf = c;
+  gdb_file_write (stream, &buf, 1);
   return c;
 }
 
@@ -2832,9 +3290,12 @@ floatformat_to_doublest (fmt, from, to)
 
   special_exponent = exponent == 0 || exponent == fmt->exp_nan;
 
-/* Don't bias zero's, denorms or NaNs.  */
+/* Don't bias NaNs. Use minimum exponent for denorms. For simplicity,
+   we don't check for zero as the exponent doesn't matter. */
   if (!special_exponent)
     exponent -= fmt->exp_bias;
+  else if (exponent == 0)
+    exponent = 1 - fmt->exp_bias;
 
   /* Build the result algebraically.  Might go infinite, underflow, etc;
      who cares. */
@@ -3111,14 +3572,21 @@ get_cell ()
 
  */
 
-static int thirty_two = 32;    /* eliminate warning from compiler on 32-bit systems */
+int
+strlen_paddr (void)
+{
+  return (TARGET_PTR_BIT / 8 * 2);
+}
+
+
+/* eliminate warning from compiler on 32-bit systems */
+static int thirty_two = 32;
 
 char *
-paddr (addr)
-     t_addr addr;
+paddr (CORE_ADDR addr)
 {
   char *paddr_str = get_cell ();
-  switch (sizeof (t_addr))
+  switch (TARGET_PTR_BIT / 8)
     {
     case 8:
       sprintf (paddr_str, "%08lx%08lx",
@@ -3137,34 +3605,10 @@ paddr (addr)
 }
 
 char *
-preg (reg)
-     t_reg reg;
-{
-  char *preg_str = get_cell ();
-  switch (sizeof (t_reg))
-    {
-    case 8:
-      sprintf (preg_str, "%08lx%08lx",
-              (unsigned long) (reg >> thirty_two), (unsigned long) (reg & 0xffffffff));
-      break;
-    case 4:
-      sprintf (preg_str, "%08lx", (unsigned long) reg);
-      break;
-    case 2:
-      sprintf (preg_str, "%04x", (unsigned short) (reg & 0xffff));
-      break;
-    default:
-      sprintf (preg_str, "%lx", (unsigned long) reg);
-    }
-  return preg_str;
-}
-
-char *
-paddr_nz (addr)
-     t_addr addr;
+paddr_nz (CORE_ADDR addr)
 {
   char *paddr_str = get_cell ();
-  switch (sizeof (t_addr))
+  switch (TARGET_PTR_BIT / 8)
     {
     case 8:
       {
@@ -3188,6 +3632,81 @@ paddr_nz (addr)
   return paddr_str;
 }
 
+static void
+decimal2str (char *paddr_str, char *sign, ULONGEST addr)
+{
+  /* steal code from valprint.c:print_decimal().  Should this worry
+     about the real size of addr as the above does? */
+  unsigned long temp[3];
+  int i = 0;
+  do
+    {
+      temp[i] = addr % (1000 * 1000 * 1000);
+      addr /= (1000 * 1000 * 1000);
+      i++;
+    }
+  while (addr != 0 && i < (sizeof (temp) / sizeof (temp[0])));
+  switch (i)
+    {
+    case 1:
+      sprintf (paddr_str, "%s%lu",
+              sign, temp[0]);
+      break;
+    case 2:
+      sprintf (paddr_str, "%s%lu%09lu",
+              sign, temp[1], temp[0]);
+      break;
+    case 3:
+      sprintf (paddr_str, "%s%lu%09lu%09lu",
+              sign, temp[2], temp[1], temp[0]);
+      break;
+    default:
+      abort ();
+    }
+}
+
+char *
+paddr_u (CORE_ADDR addr)
+{
+  char *paddr_str = get_cell ();
+  decimal2str (paddr_str, "", addr);
+  return paddr_str;
+}
+
+char *
+paddr_d (LONGEST addr)
+{
+  char *paddr_str = get_cell ();
+  if (addr < 0)
+    decimal2str (paddr_str, "-", -addr);
+  else
+    decimal2str (paddr_str, "", addr);
+  return paddr_str;
+}
+
+char *
+preg (reg)
+     t_reg reg;
+{
+  char *preg_str = get_cell ();
+  switch (sizeof (t_reg))
+    {
+    case 8:
+      sprintf (preg_str, "%08lx%08lx",
+              (unsigned long) (reg >> thirty_two), (unsigned long) (reg & 0xffffffff));
+      break;
+    case 4:
+      sprintf (preg_str, "%08lx", (unsigned long) reg);
+      break;
+    case 2:
+      sprintf (preg_str, "%04x", (unsigned short) (reg & 0xffff));
+      break;
+    default:
+      sprintf (preg_str, "%lx", (unsigned long) reg);
+    }
+  return preg_str;
+}
+
 char *
 preg_nz (reg)
      t_reg reg;
This page took 0.03789 seconds and 4 git commands to generate.