+/* ``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;
+static struct gdb_file *stdio_file_new PARAMS ((FILE * file, int close_p));
+static gdb_file_flush_ftype stdio_file_flush;
+
+static int stdio_file_magic;
+
+struct stdio_file
+ {
+ int *magic;
+ FILE *file;
+ int close_p;
+ };
+
+static struct gdb_file *
+stdio_file_new (file, close_p)
+ FILE *file;
+ int close_p;
+{
+ struct gdb_file *gdb_file = gdb_file_new ();
+ struct stdio_file *stdio = xmalloc (sizeof (struct stdio_file));
+ stdio->magic = &stdio_file_magic;
+ stdio->file = file;
+ 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;
+}
+
+static void
+stdio_file_delete (file)
+ struct gdb_file *file;
+{
+ struct stdio_file *stdio = gdb_file_data (file);
+ if (stdio->magic != &stdio_file_magic)
+ internal_error ("stdio_file_delete: bad magic number");
+ if (stdio->close_p)
+ {
+ fclose (stdio->file);
+ }
+ free (stdio);
+}
+
+static void
+stdio_file_flush (file)
+ struct gdb_file *file;
+{
+ struct stdio_file *stdio = gdb_file_data (file);
+ if (stdio->magic != &stdio_file_magic)
+ 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;
+ struct gdb_file *file;
+{
+ struct stdio_file *stdio = gdb_file_data (file);
+ if (stdio->magic != &stdio_file_magic)
+ internal_error ("stdio_file_fputs: bad magic number");
+ fputs (linebuffer, stdio->file);
+}
+
+static int
+stdio_file_isatty (file)
+ struct gdb_file *file;
+{
+ struct stdio_file *stdio = gdb_file_data (file);
+ if (stdio->magic != &stdio_file_magic)
+ internal_error ("stdio_file_isatty: bad magic number");
+ return (isatty (fileno (stdio->file)));
+}
+
+/* Like fdopen(). Create a gdb_file from a previously opened FILE. */
+
+struct gdb_file *
+stdio_fileopen (file)
+ FILE *file;
+{
+ return stdio_file_new (file, 0);
+}
+
+
+/* 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;
+static gdb_file_rewind_ftype tui_file_rewind;
+static gdb_file_put_ftype tui_file_put;
+static gdb_file_delete_ftype tui_file_delete;
+static struct gdb_file *tui_file_new PARAMS ((void));
+static int tui_file_magic;
+
+static struct gdb_file *
+tui_file_new ()
+{
+ struct tui_stream *tui = xmalloc (sizeof (struct tui_stream));
+ struct gdb_file *file = gdb_file_new ();
+ set_gdb_file_data (file, tui, tui_file_delete);
+ set_gdb_file_flush (file, tui_file_flush);
+ set_gdb_file_fputs (file, tui_file_fputs);
+ set_gdb_file_isatty (file, tui_file_isatty);
+ set_gdb_file_rewind (file, tui_file_rewind);
+ set_gdb_file_put (file, tui_file_put);
+ tui->ts_magic = &tui_file_magic;
+ return file;
+}
+
+static void
+tui_file_delete (file)
+ struct gdb_file *file;
+{
+ struct tui_stream *tmpstream = gdb_file_data (file);
+ if (tmpstream->ts_magic != &tui_file_magic)
+ internal_error ("tui_file_delete: bad magic number");
+ if ((tmpstream->ts_streamtype == astring) &&
+ (tmpstream->ts_strbuf != NULL))
+ {
+ free (tmpstream->ts_strbuf);
+ }
+ free (tmpstream);
+}
+
+struct gdb_file *
+tui_fileopen (stream)
+ FILE *stream;
+{
+ struct gdb_file *file = tui_file_new ();
+ struct tui_stream *tmpstream = gdb_file_data (file);
+ tmpstream->ts_streamtype = afile;
+ tmpstream->ts_filestream = stream;
+ tmpstream->ts_strbuf = NULL;
+ tmpstream->ts_buflen = 0;
+ 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)
+ internal_error ("tui_file_isatty: bad magic number");
+ if (stream->ts_streamtype == afile)
+ return (isatty (fileno (stream->ts_filestream)));
+ else
+ return 0;
+}
+
+static void
+tui_file_rewind (file)
+ struct gdb_file *file;
+{
+ struct tui_stream *stream = gdb_file_data (file);
+ if (stream->ts_magic != &tui_file_magic)
+ internal_error ("tui_file_rewind: bad magic number");
+ stream->ts_strbuf[0] = '\0';
+}
+
+static void
+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)
+ internal_error ("tui_file_put: bad magic number");
+ if (stream->ts_streamtype == astring)
+ write (dest, stream->ts_strbuf, strlen (stream->ts_strbuf));
+}
+
+/* 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. */
+
+/* FIXME: Should be broken up and moved to a TUI specific file. */
+
+void
+tui_file_fputs (linebuffer, file)
+ const char *linebuffer;
+ GDB_FILE *file;
+{
+ 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 *
+tui_file_get_strbuf (struct gdb_file *file)
+{
+ struct tui_stream *stream = gdb_file_data (file);
+ if (stream->ts_magic != &tui_file_magic)
+ 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
+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)
+ internal_error ("tui_file_adjust_strbuf: bad magic number");
+
+ if (stream->ts_streamtype != astring)
+ return;
+
+ if (stream->ts_strbuf)
+ {
+ /* There is already a buffer allocated */
+ non_null_chars = strlen (stream->ts_strbuf);
+
+ if (n > (stream->ts_buflen - non_null_chars - 1))
+ {
+ stream->ts_buflen = n + non_null_chars + 1;
+ stream->ts_strbuf = xrealloc (stream->ts_strbuf, stream->ts_buflen);
+ }
+ }
+ else
+ /* No buffer yet, so allocate one of the desired size */
+ stream->ts_strbuf = xmalloc ((n + 1) * sizeof (char));
+}
+