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. */
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;
}
/* 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 *));
}
/* 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_continuations ()
+discard_all_intermediate_continuations ()
{
struct continuation *continuation_ptr;
- while (cmd_continuation)
+ while (intermediate_continuation)
{
- continuation_ptr = cmd_continuation;
- cmd_continuation = continuation_ptr->next;
+ continuation_ptr = intermediate_continuation;
+ intermediate_continuation = continuation_ptr->next;
free (continuation_ptr);
}
}
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");
- /* Save it as the last error as well (no newline) */
- gdb_file_rewind (gdb_lasterr);
- vfprintf_filtered (gdb_lasterr, string, args);
- va_end (args);
- return_to_top_level (RETURN_ERROR);
- }
+ verror (string, args);
+ va_end (args);
}
-/* Allows the error message to be passed on a stream buffer */
-
NORETURN void
error_stream (GDB_FILE *stream)
{
- error (gdb_file_get_strbuf (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)
{
- return (gdb_file_get_strbuf (gdb_lasterr));
+ 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 = tui_sfileopen (132);
+ 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;
/* 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
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. */
immediate_quit = 1;
}
-#else /* !defined(__GO32__) && !defined(_MSC_VER) */
+#else /* !defined(_MSC_VER) */
void
notice_quit ()
/* Done by signals */
}
-#endif /* !defined(__GO32__) && !defined(_MSC_VER) */
+#endif /* !defined(_MSC_VER) */
/* Control C comes here */
void
/* ``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;
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;
{
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);
{
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;
{
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);
}
{
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)));
}
/* A pure memory based ``struct gdb_file'' that can be used an output
- collector. It's input is available through gdb_file_put(). */
+ buffer. The buffers accumulated contents are available via
+ gdb_file_put(). */
struct mem_file
{
int *magic;
char *buffer;
int sizeof_buffer;
- int strlen_buffer;
+ int length_buffer;
};
-extern gdb_file_fputs_ftype mem_file_fputs;
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;
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_fputs (file, mem_file_fputs);
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;
}
struct mem_file *stream = gdb_file_data (file);
if (stream->magic != &mem_file_magic)
internal_error ("mem_file_rewind: bad magic number");
- if (stream->buffer != NULL)
- {
- stream->buffer[0] = '\0';
- stream->strlen_buffer = 0;
- }
+ stream->length_buffer = 0;
}
static void
-mem_file_put (struct gdb_file *file, struct gdb_file *dest)
+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->buffer != NULL)
- fputs_unfiltered (stream->buffer, dest);
+ if (stream->length_buffer > 0)
+ write (dest, stream->buffer, stream->length_buffer);
}
void
-mem_file_fputs (const char *linebuffer, struct gdb_file *file)
+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_fputs: bad magic number");
+ internal_error ("mem_file_write: bad magic number");
if (stream->buffer == NULL)
{
- stream->strlen_buffer = strlen (linebuffer);
- stream->sizeof_buffer = stream->strlen_buffer + 1;
+ stream->length_buffer = length_buffer;
+ stream->sizeof_buffer = length_buffer;
stream->buffer = xmalloc (stream->sizeof_buffer);
- strcpy (stream->buffer, linebuffer);
+ memcpy (stream->buffer, buffer, length_buffer);
}
else
{
- int len = strlen (linebuffer);
- int new_strlen = stream->strlen_buffer + len;
- int new_sizeof = new_strlen + 1;
- if (new_sizeof >= stream->sizeof_buffer)
+ int new_length = stream->length_buffer + length_buffer;
+ if (new_length >= stream->sizeof_buffer)
{
- stream->sizeof_buffer = new_sizeof;
+ stream->sizeof_buffer = new_length;
stream->buffer = xrealloc (stream->buffer, stream->sizeof_buffer);
}
- strcpy (stream->buffer + stream->strlen_buffer, linebuffer);
- stream->strlen_buffer = new_strlen;
+ 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. */
{
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))
{
}
else
/* Do not allocate the buffer now. The first time something is printed
- one will be allocated by gdb_file_adjust_strbuf() */
+ one will be allocated by tui_file_adjust_strbuf() */
tmpstream->ts_strbuf = NULL;
tmpstream->ts_buflen = n;
return 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
{
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));
}
/* All TUI I/O sent to the *_filtered and *_unfiltered functions
if (stream->ts_streamtype == astring)
{
- gdb_file_adjust_strbuf (strlen (linebuffer), stream);
+ tui_file_adjust_strbuf (strlen (linebuffer), stream);
strcat (stream->ts_strbuf, linebuffer);
}
else
/* The normal case - just do a fputs() */
if (stream->ts_streamtype == astring)
{
- gdb_file_adjust_strbuf (strlen (linebuffer), stream);
+ tui_file_adjust_strbuf (strlen (linebuffer), stream);
strcat (stream->ts_strbuf, linebuffer);
}
else
#else
if (stream->ts_streamtype == astring)
{
- gdb_file_adjust_strbuf (strlen (linebuffer), file);
+ tui_file_adjust_strbuf (strlen (linebuffer), file);
strcat (stream->ts_strbuf, linebuffer);
}
else
}
}
-/* DEPRECATED: Use tui_sfileopen() instead */
-
-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");
-
- 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;
-}
-
-void
-gdb_file_deallocate (streamptr)
- GDB_FILE **streamptr;
-{
- gdb_file_delete (*streamptr);
- *streamptr = NULL;
-}
-
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;
}
}
-void
-gdb_fclose (streamptr)
- GDB_FILE **streamptr;
-{
- gdb_file_delete (*streamptr);
- *streamptr = NULL;
-}
-
-
/* 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;
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;
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);
}
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;
}
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
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;
}
}
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
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;
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
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;
}
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;
}
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. */