summaryrefslogtreecommitdiff
path: root/lisp/write.c
diff options
context:
space:
mode:
authorKaleb Keithley <kaleb@freedesktop.org>2003-11-14 16:49:22 +0000
committerKaleb Keithley <kaleb@freedesktop.org>2003-11-14 16:49:22 +0000
commit0a193e032ba1ecf3f003e027e833dc9d274cb740 (patch)
treea1dcc00cb7f5d26e437e05e658c38fc323fe919d /lisp/write.c
Initial revision
Diffstat (limited to 'lisp/write.c')
-rw-r--r--lisp/write.c2411
1 files changed, 2411 insertions, 0 deletions
diff --git a/lisp/write.c b/lisp/write.c
new file mode 100644
index 0000000..4952119
--- /dev/null
+++ b/lisp/write.c
@@ -0,0 +1,2411 @@
+/*
+ * Copyright (c) 2002 by The XFree86 Project, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Except as contained in this notice, the name of the XFree86 Project shall
+ * not be used in advertising or otherwise to promote the sale, use or other
+ * dealings in this Software without prior written authorization from the
+ * XFree86 Project.
+ *
+ * Author: Paulo César Pereira de Andrade
+ */
+
+/* $XFree86: xc/programs/xedit/lisp/write.c,v 1.30 2002/12/04 18:43:19 paulo Exp $ */
+
+#include "write.h"
+#include "hash.h"
+#include <math.h>
+#include <ctype.h>
+
+#define FLOAT_PREC 17
+
+#define UPCASE 0
+#define DOWNCASE 1
+#define CAPITALIZE 2
+
+#define INCDEPTH() \
+ if (++info->depth > MAX_STACK_DEPTH / 2) \
+ LispDestroy("stack overflow")
+#define DECDEPTH() --info->depth
+
+/*
+ * Types
+ */
+typedef struct _circle_info {
+ long circle_nth; /* nth circular list */
+ LispObj *object; /* the circular object */
+} circle_info;
+
+typedef struct _write_info {
+ long depth;
+ long level; /* current level */
+ long length; /* current length */
+ long print_level; /* *print-level* when started printing */
+ long print_length; /* *print-length* when started printing */
+
+ int print_escape;
+ int print_case;
+
+ long circle_count;
+ /* used while building circle info */
+ LispObj **objects;
+ long num_objects;
+ /* the circular lists */
+ circle_info *circles;
+ long num_circles;
+} write_info;
+
+/*
+ * Prototypes
+ */
+static void check_stream(LispObj*, LispFile**, LispString**, int);
+static void parse_double(char*, int*, double, int);
+static int float_string_inc(char*, int);
+static void format_integer(char*, long, int);
+static int LispWriteCPointer(LispObj*, void*);
+static int LispWriteCString(LispObj*, char*, long, write_info*);
+static int LispDoFormatExponentialFloat(LispObj*, LispObj*,
+ int, int, int*, int, int,
+ int, int, int, int);
+
+static int LispWriteInteger(LispObj*, LispObj*);
+static int LispWriteCharacter(LispObj*, LispObj*, write_info*);
+static int LispWriteString(LispObj*, LispObj*, write_info*);
+static int LispWriteFloat(LispObj*, LispObj*);
+static int LispWriteAtom(LispObj*, LispObj*, write_info*);
+static int LispDoWriteAtom(LispObj*, char*, int, int);
+static int LispWriteList(LispObj*, LispObj*, write_info*, int);
+static int LispWriteArray(LispObj*, LispObj*, write_info*);
+static int LispWriteStruct(LispObj*, LispObj*, write_info*);
+static int LispDoWriteObject(LispObj*, LispObj*, write_info*, int);
+static void LispBuildCircle(LispObj*, write_info*);
+static void LispDoBuildCircle(LispObj*, write_info*);
+static long LispCheckCircle(LispObj*, write_info*);
+static int LispPrintCircle(LispObj*, LispObj*, long, int*, write_info*);
+static int LispWriteAlist(LispObj*, LispArgList*, write_info*);
+
+/*
+ * Initialization
+ */
+LispObj *Oprint_level, *Oprint_length, *Oprint_circle,
+ *Oprint_escape, *Oprint_case;
+LispObj *Kupcase, *Kdowncase, *Kcapitalize;
+
+/*
+ * Implementation
+ */
+void
+LispWriteInit(void)
+{
+ Oprint_level = STATIC_ATOM("*PRINT-LEVEL*");
+ LispProclaimSpecial(Oprint_level, NIL, NIL);
+ LispExportSymbol(Oprint_level);
+
+ Oprint_length = STATIC_ATOM("*PRINT-LENGTH*");
+ LispProclaimSpecial(Oprint_length, NIL, NIL);
+ LispExportSymbol(Oprint_length);
+
+ Oprint_circle = STATIC_ATOM("*PRINT-CIRCLE*");
+ LispProclaimSpecial(Oprint_circle, T, NIL);
+ LispExportSymbol(Oprint_circle);
+
+ Oprint_escape = STATIC_ATOM("*PRINT-ESCAPE*");
+ LispProclaimSpecial(Oprint_escape, T, NIL);
+ LispExportSymbol(Oprint_escape);
+
+ Kupcase = KEYWORD("UPCASE");
+ Kdowncase = KEYWORD("DOWNCASE");
+ Kcapitalize = KEYWORD("CAPITALIZE");
+ Oprint_case = STATIC_ATOM("*PRINT-CASE*");
+ LispProclaimSpecial(Oprint_case, Kupcase, NIL);
+ LispExportSymbol(Oprint_case);
+}
+
+LispObj *
+Lisp_FreshLine(LispBuiltin *builtin)
+/*
+ fresh-line &optional output-stream
+ */
+{
+ LispObj *output_stream;
+
+ output_stream = ARGUMENT(0);
+
+ if (output_stream == UNSPEC)
+ output_stream = NIL;
+ else if (output_stream != NIL) {
+ CHECK_STREAM(output_stream);
+ }
+ if (LispGetColumn(output_stream)) {
+ LispWriteChar(output_stream, '\n');
+ if (output_stream == NIL ||
+ (output_stream->data.stream.type == LispStreamStandard &&
+ output_stream->data.stream.source.file == Stdout))
+ LispFflush(Stdout);
+ return (T);
+ }
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_Prin1(LispBuiltin *builtin)
+/*
+ prin1 object &optional output-stream
+ */
+{
+ LispObj *object, *output_stream;
+
+ output_stream = ARGUMENT(1);
+ object = ARGUMENT(0);
+
+ if (output_stream == UNSPEC)
+ output_stream = NIL;
+ LispPrint(object, output_stream, 0);
+
+ return (object);
+}
+
+LispObj *
+Lisp_Princ(LispBuiltin *builtin)
+/*
+ princ object &optional output-stream
+ */
+{
+ int head;
+ LispObj *object, *output_stream;
+
+ output_stream = ARGUMENT(1);
+ object = ARGUMENT(0);
+
+ if (output_stream == UNSPEC)
+ output_stream = NIL;
+ head = lisp__data.env.length;
+ LispAddVar(Oprint_escape, NIL);
+ ++lisp__data.env.head;
+ LispPrint(object, output_stream, 0);
+ lisp__data.env.head = lisp__data.env.length = head;
+
+ return (object);
+}
+
+LispObj *
+Lisp_Print(LispBuiltin *builtin)
+/*
+ print object &optional output-stream
+ */
+{
+ LispObj *object, *output_stream;
+
+ output_stream = ARGUMENT(1);
+ object = ARGUMENT(0);
+
+ if (output_stream == UNSPEC)
+ output_stream = NIL;
+ LispWriteChar(output_stream, '\n');
+ LispPrint(object, output_stream, 0);
+ LispWriteChar(output_stream, ' ');
+
+ return (object);
+}
+
+LispObj *
+Lisp_Terpri(LispBuiltin *builtin)
+/*
+ terpri &optional output-stream
+ */
+{
+ LispObj *output_stream;
+
+ output_stream = ARGUMENT(0);
+
+ if (output_stream == UNSPEC)
+ output_stream = NIL;
+ else if (output_stream != NIL) {
+ CHECK_STREAM(output_stream);
+ }
+ LispWriteChar(output_stream, '\n');
+ if (output_stream == NIL ||
+ (output_stream->data.stream.type == LispStreamStandard &&
+ output_stream->data.stream.source.file == Stdout))
+ LispFflush(Stdout);
+
+ return (NIL);
+}
+
+LispObj *
+Lisp_Write(LispBuiltin *builtin)
+/*
+ write object &key case circle escape length level lines pretty readably right-margin stream
+ */
+{
+ int head = lisp__data.env.length;
+
+ LispObj *object, *ocase, *circle, *escape, *length, *level,
+ *lines, *pretty, *readably, *right_margin, *stream;
+
+ stream = ARGUMENT(10);
+ right_margin = ARGUMENT(9); /* yet unused */
+ readably = ARGUMENT(8); /* yet unused */
+ pretty = ARGUMENT(7); /* yet unused */
+ lines = ARGUMENT(6); /* yet unused */
+ level = ARGUMENT(5);
+ length = ARGUMENT(4);
+ escape = ARGUMENT(3);
+ circle = ARGUMENT(2);
+ ocase = ARGUMENT(1);
+ object = ARGUMENT(0);
+
+ if (stream == UNSPEC)
+ stream = NIL;
+ else if (stream != NIL) {
+ CHECK_STREAM(stream);
+ }
+
+ /* prepare the printer environment */
+ if (circle != UNSPEC)
+ LispAddVar(Oprint_circle, circle);
+ if (length != UNSPEC)
+ LispAddVar(Oprint_length, length);
+ if (level != UNSPEC)
+ LispAddVar(Oprint_level, level);
+ if (ocase != UNSPEC)
+ LispAddVar(Oprint_case, ocase);
+ if (escape != UNSPEC)
+ LispAddVar(Oprint_escape, escape);
+
+ lisp__data.env.head = lisp__data.env.length;
+
+ (void)LispWriteObject(stream, object);
+
+ lisp__data.env.head = lisp__data.env.length = head;
+
+ return (object);
+}
+
+LispObj *
+Lisp_WriteChar(LispBuiltin *builtin)
+/*
+ write-char character &optional output-stream
+ */
+{
+ int ch;
+
+ LispObj *character, *output_stream;
+
+ output_stream = ARGUMENT(1);
+ character = ARGUMENT(0);
+
+ if (output_stream == UNSPEC)
+ output_stream = NIL;
+ CHECK_SCHAR(character);
+ ch = SCHAR_VALUE(character);
+
+ LispWriteChar(output_stream, ch);
+
+ return (character);
+}
+
+LispObj *
+Lisp_WriteLine(LispBuiltin *builtin)
+/*
+ write-line string &optional output-stream &key start end
+ */
+{
+ return (LispWriteString_(builtin, 1));
+}
+
+LispObj *
+Lisp_WriteString(LispBuiltin *builtin)
+/*
+ write-string string &optional output-stream &key start end
+ */
+{
+ return (LispWriteString_(builtin, 0));
+}
+
+
+int
+LispWriteObject(LispObj *stream, LispObj *object)
+{
+ write_info info;
+ int bytes;
+ LispObj *level, *length, *circle, *oescape, *ocase;
+
+ /* current state */
+ info.depth = info.level = info.length = 0;
+
+ /* maximum level to descend */
+ level = LispGetVar(Oprint_level);
+ if (level && INDEXP(level))
+ info.print_level = FIXNUM_VALUE(level);
+ else
+ info.print_level = -1;
+
+ /* maximum list length */
+ length = LispGetVar(Oprint_length);
+ if (length && INDEXP(length))
+ info.print_length = FIXNUM_VALUE(length);
+ else
+ info.print_length = -1;
+
+ /* detect circular/shared objects? */
+ circle = LispGetVar(Oprint_circle);
+ info.circle_count = 0;
+ info.objects = NULL;
+ info.num_objects = 0;
+ info.circles = NULL;
+ info.num_circles = 0;
+ if (circle && circle != NIL) {
+ LispBuildCircle(object, &info);
+ /* free this data now */
+ if (info.num_objects) {
+ LispFree(info.objects);
+ info.num_objects = 0;
+ }
+ }
+
+ /* escape characters and strings? */
+ oescape = LispGetVar(Oprint_escape);
+ if (oescape != NULL)
+ info.print_escape = oescape == NIL;
+ else
+ info.print_escape = -1;
+
+ /* don't use the default case printing? */
+ ocase = LispGetVar(Oprint_case);
+ if (ocase == Kdowncase)
+ info.print_case = DOWNCASE;
+ else if (ocase == Kcapitalize)
+ info.print_case = CAPITALIZE;
+ else
+ info.print_case = UPCASE;
+
+ bytes = LispDoWriteObject(stream, object, &info, 1);
+ if (circle && circle != NIL && info.num_circles)
+ LispFree(info.circles);
+
+ return (bytes);
+}
+
+static void
+LispBuildCircle(LispObj *object, write_info *info)
+{
+ LispObj *list;
+
+ switch (OBJECT_TYPE(object)) {
+ case LispCons_t:
+ LispDoBuildCircle(object, info);
+ break;
+ case LispArray_t:
+ /* Currently arrays are implemented as lists, but only
+ * the elements could/should be circular */
+ if (LispCheckCircle(object, info) >= 0)
+ return;
+ LispDoBuildCircle(object, info);
+ for (list = object->data.array.list;
+ CONSP(list); list = CDR(list))
+ LispBuildCircle(CAR(list), info);
+ break;
+ case LispStruct_t:
+ /* Like arrays, structs are currently implemented as lists,
+ * but only the elements could/should be circular */
+ if (LispCheckCircle(object, info) >= 0)
+ return;
+ LispDoBuildCircle(object, info);
+ for (list = object->data.struc.fields;
+ CONSP(list); list = CDR(list))
+ LispBuildCircle(CAR(list), info);
+ break;
+ case LispQuote_t:
+ case LispBackquote_t:
+ case LispFunctionQuote_t:
+ LispDoBuildCircle(object, info);
+ LispBuildCircle(object->data.quote, info);
+ break;
+ case LispComma_t:
+ LispDoBuildCircle(object, info);
+ LispBuildCircle(object->data.comma.eval, info);
+ break;
+ case LispLambda_t:
+ /* Circularity in a function body should fail elsewhere... */
+ if (LispCheckCircle(object, info) >= 0)
+ return;
+ LispDoBuildCircle(object, info);
+ LispBuildCircle(object->data.lambda.code, info);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+LispDoBuildCircle(LispObj *object, write_info *info)
+{
+ long i;
+
+ if (LispCheckCircle(object, info) >= 0)
+ return;
+
+ for (i = 0; i < info->num_objects; i++)
+ if (info->objects[i] == object) {
+ /* circularity found */
+ info->circles = LispRealloc(info->circles, sizeof(circle_info) *
+ (info->num_circles + 1));
+ info->circles[info->num_circles].circle_nth = 0;
+ info->circles[info->num_circles].object = object;
+ ++info->num_circles;
+ return;
+ }
+
+ /* object pointer not yet recorded */
+ if ((i % 16) == 0)
+ info->objects = LispRealloc(info->objects, sizeof(LispObj*) *
+ (info->num_objects + 16));
+ info->objects[info->num_objects++] = object;
+
+ if (CONSP(object)) {
+ if (CONSP(CAR(object)))
+ LispDoBuildCircle(CAR(object), info);
+ else
+ LispBuildCircle(CAR(object), info);
+ if (CONSP(CDR(object)))
+ LispDoBuildCircle(CDR(object), info);
+ else
+ LispBuildCircle(CDR(object), info);
+ }
+}
+
+static long
+LispCheckCircle(LispObj *object, write_info *info)
+{
+ long i;
+
+ for (i = 0; i < info->num_circles; i++)
+ if (info->circles[i].object == object)
+ return (i);
+
+ return (-1);
+}
+
+static int
+LispPrintCircle(LispObj *stream, LispObj *object, long circle,
+ int *length, write_info *info)
+{
+ char stk[32];
+
+ if (!info->circles[circle].circle_nth) {
+ sprintf(stk, "#%ld=", ++info->circle_count);
+ *length += LispWriteStr(stream, stk, strlen(stk));
+ info->circles[circle].circle_nth = info->circle_count;
+
+ return (1);
+ }
+ sprintf(stk, "#%ld#", info->circles[circle].circle_nth);
+ *length += LispWriteStr(stream, stk, strlen(stk));
+
+ return (0);
+}
+
+static int
+LispWriteAlist(LispObj *stream, LispArgList *alist, write_info *info)
+{
+ char *name;
+ int i, length = 0, need_space = 0;
+
+#define WRITE_ATOM(object) \
+ name = ATOMID(object); \
+ length += LispDoWriteAtom(stream, name, strlen(name), \
+ info->print_case)
+#define WRITE_STRING(string) \
+ length += LispDoWriteAtom(stream, string, strlen(string), \
+ info->print_case)
+#define WRITE_OBJECT(object) \
+ length += LispDoWriteObject(stream, object, info, 1)
+#define WRITE_OPAREN() \
+ length += LispWriteChar(stream, '(')
+#define WRITE_SPACE() \
+ length += LispWriteChar(stream, ' ')
+#define WRITE_CPAREN() \
+ length += LispWriteChar(stream, ')')
+
+ WRITE_OPAREN();
+ for (i = 0; i < alist->normals.num_symbols; i++) {
+ WRITE_ATOM(alist->normals.symbols[i]);
+ if (i + 1 < alist->normals.num_symbols)
+ WRITE_SPACE();
+ else
+ need_space = 1;
+ }
+ if (alist->optionals.num_symbols) {
+ if (need_space)
+ WRITE_SPACE();
+ WRITE_STRING(Soptional);
+ WRITE_SPACE();
+ for (i = 0; i < alist->optionals.num_symbols; i++) {
+ WRITE_OPAREN();
+ WRITE_ATOM(alist->optionals.symbols[i]);
+ WRITE_SPACE();
+ WRITE_OBJECT(alist->optionals.defaults[i]);
+ if (alist->optionals.sforms[i]) {
+ WRITE_SPACE();
+ WRITE_ATOM(alist->optionals.sforms[i]);
+ }
+ WRITE_CPAREN();
+ if (i + 1 < alist->optionals.num_symbols)
+ WRITE_SPACE();
+ }
+ need_space = 1;
+ }
+ if (alist->keys.num_symbols) {
+ if (need_space)
+ WRITE_SPACE();
+ length += LispDoWriteAtom(stream, Skey, 4, info->print_case);
+ WRITE_SPACE();
+ for (i = 0; i < alist->keys.num_symbols; i++) {
+ WRITE_OPAREN();
+ if (alist->keys.keys[i]) {
+ WRITE_OPAREN();
+ WRITE_ATOM(alist->keys.keys[i]);
+ WRITE_SPACE();
+ }
+ WRITE_ATOM(alist->keys.symbols[i]);
+ if (alist->keys.keys[i])
+ WRITE_CPAREN();
+ WRITE_SPACE();
+ WRITE_OBJECT(alist->keys.defaults[i]);
+ if (alist->keys.sforms[i]) {
+ WRITE_SPACE();
+ WRITE_ATOM(alist->keys.sforms[i]);
+ }
+ WRITE_CPAREN();
+ if (i + 1 < alist->keys.num_symbols)
+ WRITE_SPACE();
+ }
+ need_space = 1;
+ }
+ if (alist->rest) {
+ if (need_space)
+ WRITE_SPACE();
+ WRITE_STRING(Srest);
+ WRITE_SPACE();
+ WRITE_ATOM(alist->rest);
+ need_space = 1;
+ }
+ if (alist->auxs.num_symbols) {
+ if (need_space)
+ WRITE_SPACE();
+ WRITE_STRING(Saux);
+ WRITE_SPACE();
+ for (i = 0; i < alist->auxs.num_symbols; i++) {
+ WRITE_OPAREN();
+ WRITE_ATOM(alist->auxs.symbols[i]);
+ WRITE_SPACE();
+ WRITE_OBJECT(alist->auxs.initials[i]);
+ WRITE_CPAREN();
+ if (i + 1 < alist->auxs.num_symbols)
+ WRITE_SPACE();
+ }
+ }
+ WRITE_CPAREN();
+
+#undef WRITE_ATOM
+#undef WRITE_STRING
+#undef WRITE_OBJECT
+#undef WRITE_OPAREN
+#undef WRITE_SPACE
+#undef WRITE_CPAREN
+
+ return (length);
+}
+
+static void
+check_stream(LispObj *stream,
+ LispFile **file, LispString **string, int check_writable)
+{
+ /* NIL is UNIX stdout, *STANDARD-OUTPUT* may not be UNIX stdout */
+ if (stream == NIL) {
+ *file = Stdout;
+ *string = NULL;
+ }
+ else {
+ if (!STREAMP(stream))
+ LispDestroy("%s is not a stream", STROBJ(stream));
+ if (check_writable && !stream->data.stream.writable)
+ LispDestroy("%s is not writable", STROBJ(stream));
+ else if (stream->data.stream.type == LispStreamString) {
+ *string = SSTREAMP(stream);
+ *file = NULL;
+ }
+ else {
+ if (stream->data.stream.type == LispStreamPipe)
+ *file = OPSTREAMP(stream);
+ else
+ *file = stream->data.stream.source.file;
+ *string = NULL;
+ }
+ }
+}
+
+/* Assumes buffer has enough storage, 64 bytes should be more than enough */
+static void
+parse_double(char *buffer, int *exponent, double value, int d)
+{
+ char stk[64], fmt[32], *ptr, *fract = NULL;
+ int positive = value >= 0.0;
+
+parse_double_again:
+ if (d >= 8) {
+ double dcheck;
+ int icheck, count;
+
+ /* this should to do the correct rounding */
+ for (count = 2; count >= 0; count--) {
+ icheck = d <= 0 ? 0 : d > FLOAT_PREC ? FLOAT_PREC - count : d - count;
+ sprintf(fmt, "%%.%de", icheck);
+ sprintf(stk, fmt, value);
+ if (count) {
+ /* if the value read back is the same formatted */
+ sscanf(stk, "%lf", &dcheck);
+ if (dcheck == value)
+ break;
+ }
+ }
+ }
+ else {
+ sprintf(fmt, "%%.%de", d <= 0 ? 0 : d > FLOAT_PREC ? FLOAT_PREC : d);
+ sprintf(stk, fmt, value);
+ }
+
+ /* this "should" never fail */
+ ptr = strchr(stk, 'e');
+ if (ptr) {
+ *ptr++ = '\0';
+ *exponent = atoi(ptr);
+ }
+ else
+ *exponent = 0;
+
+ /* find start of number representation */
+ for (ptr = stk; *ptr && !isdigit(*ptr); ptr++)
+ ;
+
+ /* check if did not trim any significant digit,
+ * this may happen because '%.e' puts only one digit before the '.' */
+ if (d > 0 && d < FLOAT_PREC && fabs(value) >= 10.0 &&
+ strlen(ptr) - 1 - !positive <= *exponent) {
+ d += *exponent - (strlen(ptr) - 1 - !positive) + 1;
+ goto parse_double_again;
+ }
+
+ /* this "should" never fail */
+ fract = strchr(ptr, '.');
+ if (fract)
+ *fract++ = '\0';
+
+ /* store number representation in buffer */
+ *buffer = positive ? '+' : '-';
+ strcpy(buffer + 1, ptr);
+ if (fract)
+ strcpy(buffer + strlen(buffer), fract);
+}
+
+static void
+format_integer(char *buffer, long value, int radix)
+{
+ if (radix == 10)
+ sprintf(buffer, "%ld", value);
+ else if (radix == 16)
+ sprintf(buffer, "%lx", value);
+ else if (radix == 8)
+ sprintf(buffer, "%lo", value);
+ else {
+ /* use bignum routine to convert number to string */
+ mpi integer;
+
+ mpi_init(&integer);
+ mpi_seti(&integer, value);
+ mpi_getstr(buffer, &integer, radix);
+ mpi_clear(&integer);
+ }
+}
+
+static int
+LispWriteCPointer(LispObj *stream, void *data)
+{
+ char stk[32];
+
+#ifdef LONG64
+ sprintf(stk, "0x%016lx", (long)data);
+#else
+ sprintf(stk, "0x%08lx", (long)data);
+#endif
+
+ return (LispWriteStr(stream, stk, strlen(stk)));
+}
+
+static int
+LispWriteCString(LispObj *stream, char *string, long length, write_info *info)
+{
+ int result;
+
+ if (!info->print_escape) {
+ char *base, *ptr, *end;
+
+ result = LispWriteChar(stream, '"');
+ for (base = ptr = string, end = string + length; ptr < end; ptr++) {
+ if (*ptr == '\\' || *ptr == '"') {
+ result += LispWriteStr(stream, base, ptr - base);
+ result += LispWriteChar(stream, '\\');
+ result += LispWriteChar(stream, *ptr);
+ base = ptr + 1;
+ }
+ }
+ result += LispWriteStr(stream, base, end - base);
+ result += LispWriteChar(stream, '"');
+ }
+ else
+ result = LispWriteStr(stream, string, length);
+
+ return (result);
+}
+
+static int
+LispWriteList(LispObj *stream, LispObj *object, write_info *info, int paren)
+{
+ int length = 0;
+ long circle = 0;
+
+ INCDEPTH();
+ if (info->print_level < 0 || info->level <= info->print_level) {
+ LispObj *car, *cdr;
+ long print_length = info->length;
+
+ if (info->circles && (circle = LispCheckCircle(object, info)) >= 0) {
+ if (!paren) {
+ length += LispWriteStr(stream, ". ", 2);
+ paren = 1;
+ }
+ if (LispPrintCircle(stream, object, circle, &length, info) == 0) {
+ DECDEPTH();
+
+ return (length);
+ }
+ }
+
+ car = CAR(object);
+ cdr = CDR(object);
+
+ if (cdr == NIL) {
+ if (paren)
+ length += LispWriteChar(stream, '(');
+ if (info->print_length < 0 || info->length < info->print_length) {
+ info->length = 0;
+ length += LispDoWriteObject(stream, car, info, 1);
+ info->length = print_length + 1;
+ }
+ else
+ length += LispWriteStr(stream, "...", 3);
+ if (paren)
+ length += LispWriteChar(stream, ')');
+ }
+ else {
+ if (paren)
+ length += LispWriteChar(stream, '(');
+ if (info->print_length < 0 || info->length < info->print_length) {
+ info->length = 0;
+ length += LispDoWriteObject(stream, car, info, 1);
+ info->length = print_length + 1;
+ if (!CONSP(cdr)) {
+ length += LispWriteStr(stream, " . ", 3);
+ info->length = 0;
+ length += LispDoWriteObject(stream, cdr, info, 0);
+ }
+ else {
+ length += LispWriteChar(stream, ' ');
+ if (info->print_length < 0 ||
+ info->length < info->print_length)
+ length += LispWriteList(stream, cdr, info, 0);
+ else
+ length += LispWriteStr(stream, "...", 3);
+ }
+ }
+ else
+ length += LispWriteStr(stream, "...", 3);
+ if (paren)
+ length += LispWriteChar(stream, ')');
+ }
+ info->length = print_length;
+ }
+ else
+ length += LispWriteChar(stream, '#');
+ DECDEPTH();
+
+ return (length);
+}
+
+static int
+LispDoWriteObject(LispObj *stream, LispObj *object, write_info *info, int paren)
+{
+ long print_level;
+ int length = 0;
+ char stk[64], *string = NULL;
+
+write_again:
+ switch (OBJECT_TYPE(object)) {
+ case LispNil_t:
+ if (object == NIL)
+ string = Snil;
+ else if (object == T)
+ string = St;
+ else if (object == DOT)
+ string = "#<DOT>";
+ else if (object == UNSPEC)
+ string = "#<UNSPEC>";
+ else if (object == UNBOUND)
+ string = "#<UNBOUND>";
+ else
+ string = "#<ERROR>";
+ length += LispDoWriteAtom(stream, string, strlen(string),
+ info->print_case);
+ break;
+ case LispOpaque_t: {
+ char *desc = LispIntToOpaqueType(object->data.opaque.type);
+
+ length += LispWriteChar(stream, '#');
+ length += LispWriteCPointer(stream, object->data.opaque.data);
+ length += LispWriteStr(stream, desc, strlen(desc));
+ } break;
+ case LispAtom_t:
+ length += LispWriteAtom(stream, object, info);
+ break;
+ case LispFunction_t:
+ if (object->data.atom->a_function) {
+ object = object->data.atom->property->fun.function;
+ goto write_lambda;
+ }
+ length += LispWriteStr(stream, "#<", 2);
+ if (object->data.atom->a_compiled)
+ LispDoWriteAtom(stream, "COMPILED", 8, info->print_case);
+ else if (object->data.atom->a_builtin)
+ LispDoWriteAtom(stream, "BUILTIN", 7, info->print_case);
+ /* XXX the function does not exist anymore */
+ /* FIXME not sure if I want this fixed... */
+ else
+ LispDoWriteAtom(stream, "UNBOUND", 7, info->print_case);
+ LispDoWriteAtom(stream, "-FUNCTION", 9, info->print_case);
+ length += LispWriteChar(stream, ' ');
+ length += LispWriteAtom(stream, object->data.atom->object, info);
+ length += LispWriteChar(stream, '>');
+ break;
+ case LispString_t:
+ length += LispWriteString(stream, object, info);
+ break;
+ case LispSChar_t:
+ length += LispWriteCharacter(stream, object, info);
+ break;
+ case LispDFloat_t:
+ length += LispWriteFloat(stream, object);
+ break;
+ case LispFixnum_t:
+ case LispInteger_t:
+ case LispBignum_t:
+ length += LispWriteInteger(stream, object);
+ break;
+ case LispRatio_t:
+ format_integer(stk, object->data.ratio.numerator, 10);
+ length += LispWriteStr(stream, stk, strlen(stk));
+ length += LispWriteChar(stream, '/');
+ format_integer(stk, object->data.ratio.denominator, 10);
+ length += LispWriteStr(stream, stk, strlen(stk));
+ break;
+ case LispBigratio_t: {
+ int sz;
+ char *ptr;
+
+ sz = mpi_getsize(mpr_num(object->data.mp.ratio), 10) + 1 +
+ mpi_getsize(mpr_den(object->data.mp.ratio), 10) + 1 +
+ (mpi_sgn(mpr_num(object->data.mp.ratio)) < 0);
+ if (sz > sizeof(stk))
+ ptr = LispMalloc(sz);
+ else
+ ptr = stk;
+ mpr_getstr(ptr, object->data.mp.ratio, 10);
+ length += LispWriteStr(stream, ptr, sz - 1);
+ if (ptr != stk)
+ LispFree(ptr);
+ } break;
+ case LispComplex_t:
+ length += LispWriteStr(stream, "#C(", 3);
+ length += LispDoWriteObject(stream,
+ object->data.complex.real, info, 0);
+ length += LispWriteChar(stream, ' ');
+ length += LispDoWriteObject(stream,
+ object->data.complex.imag, info, 0);
+ length += LispWriteChar(stream, ')');
+ break;
+ case LispCons_t:
+ print_level = info->level;
+ ++info->level;
+ length += LispWriteList(stream, object, info, paren);
+ info->level = print_level;
+ break;
+ case LispQuote_t:
+ length += LispWriteChar(stream, '\'');
+ paren = 1;
+ object = object->data.quote;
+ goto write_again;
+ case LispBackquote_t:
+ length += LispWriteChar(stream, '`');
+ paren = 1;
+ object = object->data.quote;
+ goto write_again;
+ case LispComma_t:
+ if (object->data.comma.atlist)
+ length += LispWriteStr(stream, ",@", 2);
+ else
+ length += LispWriteChar(stream, ',');
+ paren = 1;
+ object = object->data.comma.eval;
+ goto write_again;
+ break;
+ case LispFunctionQuote_t:
+ length += LispWriteStr(stream, "#'", 2);
+ paren = 1;
+ object = object->data.quote;
+ goto write_again;
+ case LispArray_t:
+ length += LispWriteArray(stream, object, info);
+ break;
+ case LispStruct_t:
+ length += LispWriteStruct(stream, object, info);
+ break;
+ case LispLambda_t:
+ write_lambda:
+ switch (object->funtype) {
+ case LispLambda:
+ string = "#<LAMBDA ";
+ break;
+ case LispFunction:
+ string = "#<FUNCTION ";
+ break;
+ case LispMacro:
+ string = "#<MACRO ";
+ break;
+ case LispSetf:
+ string = "#<SETF ";
+ break;
+ }
+ length += LispDoWriteAtom(stream, string, strlen(string),
+ info->print_case);
+ if (object->funtype != LispLambda) {
+ length += LispWriteAtom(stream, object->data.lambda.name, info);
+ length += LispWriteChar(stream, ' ');
+ length += LispWriteAlist(stream, object->data.lambda.name
+ ->data.atom->property->alist, info);
+ }
+ else {
+ length += LispDoWriteAtom(stream, Snil, 3, info->print_case);
+ length += LispWriteChar(stream, ' ');
+ length += LispWriteAlist(stream, (LispArgList*)object->
+ data.lambda.name->data.opaque.data,
+ info);
+ }
+ length += LispWriteChar(stream, ' ');
+ length += LispDoWriteObject(stream,
+ object->data.lambda.code, info, 0);
+ length += LispWriteChar(stream, '>');
+ break;
+ case LispStream_t:
+ length += LispWriteStr(stream, "#<", 2);
+ if (object->data.stream.type == LispStreamFile)
+ string = "FILE-STREAM ";
+ else if (object->data.stream.type == LispStreamString)
+ string = "STRING-STREAM ";
+ else if (object->data.stream.type == LispStreamStandard)
+ string = "STANDARD-STREAM ";
+ else if (object->data.stream.type == LispStreamPipe)
+ string = "PIPE-STREAM ";
+ length += LispDoWriteAtom(stream, string, strlen(string),
+ info->print_case);
+
+ if (!object->data.stream.readable && !object->data.stream.writable)
+ length += LispDoWriteAtom(stream, "CLOSED",
+ 6, info->print_case);
+ else {
+ if (object->data.stream.readable)
+ length += LispDoWriteAtom(stream, "READ",
+ 4, info->print_case);
+ if (object->data.stream.writable) {
+ if (object->data.stream.readable)
+ length += LispWriteChar(stream, '-');
+ length += LispDoWriteAtom(stream, "WRITE",
+ 5, info->print_case);
+ }
+ }
+ if (object->data.stream.type != LispStreamString) {
+ length += LispWriteChar(stream, ' ');
+ length += LispDoWriteObject(stream,
+ object->data.stream.pathname,
+ info, 1);
+ /* same address/size for pipes */
+ length += LispWriteChar(stream, ' ');
+ length += LispWriteCPointer(stream,
+ object->data.stream.source.file);
+ if (object->data.stream.readable &&
+ object->data.stream.type == LispStreamFile &&
+ !object->data.stream.source.file->binary) {
+ length += LispWriteStr(stream, " @", 2);
+ format_integer(stk, object->data.stream.source.file->line, 10);
+ length += LispWriteStr(stream, stk, strlen(stk));
+ }
+ }
+ length += LispWriteChar(stream, '>');
+ break;
+ case LispPathname_t:
+ length += LispWriteStr(stream, "#P", 2);
+ paren = 1;
+ object = CAR(object->data.quote);
+ goto write_again;
+ case LispPackage_t:
+ length += LispDoWriteAtom(stream, "#<PACKAGE ",
+ 10, info->print_case);
+ length += LispWriteStr(stream,
+ THESTR(object->data.package.name),
+ STRLEN(object->data.package.name));
+ length += LispWriteChar(stream, '>');
+ break;
+ case LispRegex_t:
+ length += LispDoWriteAtom(stream, "#<REGEX ",
+ 8, info->print_case);
+ length += LispDoWriteObject(stream,
+ object->data.regex.pattern, info, 1);
+ if (object->data.regex.options & RE_NOSPEC)
+ length += LispDoWriteAtom(stream, " :NOSPEC",
+ 8, info->print_case);
+ if (object->data.regex.options & RE_ICASE)
+ length += LispDoWriteAtom(stream, " :ICASE",
+ 7, info->print_case);
+ if (object->data.regex.options & RE_NOSUB)
+ length += LispDoWriteAtom(stream, " :NOSUB",
+ 7, info->print_case);
+ if (object->data.regex.options & RE_NEWLINE)
+ length += LispDoWriteAtom(stream, " :NEWLINE",
+ 9, info->print_case);
+ length += LispWriteChar(stream, '>');
+ break;
+ case LispBytecode_t:
+ length += LispDoWriteAtom(stream, "#<BYTECODE ",
+ 11, info->print_case);
+ length += LispWriteCPointer(stream,
+ object->data.bytecode.bytecode);
+ length += LispWriteChar(stream, '>');
+ break;
+ case LispHashTable_t:
+ length += LispDoWriteAtom(stream, "#<HASH-TABLE ",
+ 13, info->print_case);
+ length += LispWriteAtom(stream, object->data.hash.test, info);
+ snprintf(stk, sizeof(stk), " %g %g",
+ object->data.hash.table->rehash_size,
+ object->data.hash.table->rehash_threshold);
+ length += LispWriteStr(stream, stk, strlen(stk));
+ snprintf(stk, sizeof(stk), " %ld/%ld>",
+ object->data.hash.table->count,
+ object->data.hash.table->num_entries);
+ length += LispWriteStr(stream, stk, strlen(stk));
+ break;
+ }
+
+ return (length);
+}
+
+/* return current column number in stream */
+int
+LispGetColumn(LispObj *stream)
+{
+ LispFile *file;
+ LispString *string;
+
+ check_stream(stream, &file, &string, 0);
+ if (file != NULL)
+ return (file->column);
+ return (string->column);
+}
+
+/* write a character to stream */
+int
+LispWriteChar(LispObj *stream, int character)
+{
+ LispFile *file;
+ LispString *string;
+
+ check_stream(stream, &file, &string, 1);
+ if (file != NULL)
+ return (LispFputc(file, character));
+
+ return (LispSputc(string, character));
+}
+
+/* write a character count times to stream */
+int
+LispWriteChars(LispObj *stream, int character, int count)
+{
+ int length = 0;
+
+ if (count > 0) {
+ char stk[64];
+ LispFile *file;
+ LispString *string;
+
+ check_stream(stream, &file, &string, 1);
+ if (count >= sizeof(stk)) {
+ memset(stk, character, sizeof(stk));
+ for (; count >= sizeof(stk); count -= sizeof(stk)) {
+ if (file != NULL)
+ length += LispFwrite(file, stk, sizeof(stk));
+ else
+ length += LispSwrite(string, stk, sizeof(stk));
+ }
+ }
+ else
+ memset(stk, character, count);
+
+ if (count) {
+ if (file != NULL)
+ length += LispFwrite(file, stk, count);
+ else
+ length += LispSwrite(string, stk, count);
+ }
+ }
+
+ return (length);
+}
+
+/* write a string to stream */
+int
+LispWriteStr(LispObj *stream, char *buffer, long length)
+{
+ LispFile *file;
+ LispString *string;
+
+ check_stream(stream, &file, &string, 1);
+ if (file != NULL)
+ return (LispFwrite(file, buffer, length));
+ return (LispSwrite(string, buffer, length));
+}
+
+static int
+LispDoWriteAtom(LispObj *stream, char *string, int length, int print_case)
+{
+ int bytes = 0, cap = 0;
+ char buffer[128], *ptr;
+
+ switch (print_case) {
+ case DOWNCASE:
+ for (ptr = buffer; length > 0; length--, string++) {
+ if (isupper(*string))
+ *ptr = tolower(*string);
+ else
+ *ptr = *string;
+ ++ptr;
+ if (ptr - buffer >= sizeof(buffer)) {
+ bytes += LispWriteStr(stream, buffer, ptr - buffer);
+ ptr = buffer;
+ }
+ }
+ if (ptr > buffer)
+ bytes += LispWriteStr(stream, buffer, ptr - buffer);
+ break;
+ case CAPITALIZE:
+ for (ptr = buffer; length > 0; length--, string++) {
+ if (isalnum(*string)) {
+ if (cap && isupper(*string))
+ *ptr = tolower(*string);
+ else
+ *ptr = *string;
+ cap = 1;
+ }
+ else {
+ *ptr = *string;
+ cap = 0;
+ }
+ ++ptr;
+ if (ptr - buffer >= sizeof(buffer)) {
+ bytes += LispWriteStr(stream, buffer, ptr - buffer);
+ ptr = buffer;
+ }
+ }
+ if (ptr > buffer)
+ bytes += LispWriteStr(stream, buffer, ptr - buffer);
+ break;
+ default:
+ /* Strings are already stored upcase/quoted */
+ bytes += LispWriteStr(stream, string, length);
+ break;
+ }
+
+ return (bytes);
+}
+
+static int
+LispWriteAtom(LispObj *stream, LispObj *object, write_info *info)
+{
+ int length = 0;
+ LispAtom *atom = object->data.atom;
+ Atom_id id = atom->string;
+
+ if (atom->package != PACKAGE) {
+ if (atom->package == lisp__data.keyword)
+ length += LispWriteChar(stream, ':');
+ else if (atom->package == NULL)
+ length += LispWriteStr(stream, "#:", 2);
+ else {
+ /* Check if the symbol is visible */
+ int i, visible = 0;
+
+ if (atom->ext) {
+ for (i = lisp__data.pack->use.length - 1; i >= 0; i--) {
+ if (lisp__data.pack->use.pairs[i] == atom->package) {
+ visible = 1;
+ break;
+ }
+ }
+ }
+
+ if (!visible) {
+ /* XXX this assumes that package names are always "readable" */
+ length +=
+ LispDoWriteAtom(stream,
+ THESTR(atom->package->data.package.name),
+ STRLEN(atom->package->data.package.name),
+ info->print_case);
+ length += LispWriteChar(stream, ':');
+ if (!atom->ext)
+ length += LispWriteChar(stream, ':');
+ }
+ }
+ }
+ if (atom->unreadable)
+ length += LispWriteChar(stream, '|');
+ length += LispDoWriteAtom(stream, id, strlen(id),
+ atom->unreadable ? UPCASE : info->print_case);
+ if (atom->unreadable)
+ length += LispWriteChar(stream, '|');
+
+ return (length);
+}
+
+static int
+LispWriteInteger(LispObj *stream, LispObj *object)
+{
+ return (LispFormatInteger(stream, object, 10, 0, 0, 0, 0, 0, 0));
+}
+
+static int
+LispWriteCharacter(LispObj *stream, LispObj *object, write_info *info)
+{
+ return (LispFormatCharacter(stream, object, !info->print_escape, 0));
+}
+
+static int
+LispWriteString(LispObj *stream, LispObj *object, write_info *info)
+{
+ return (LispWriteCString(stream, THESTR(object), STRLEN(object), info));
+}
+
+static int
+LispWriteFloat(LispObj *stream, LispObj *object)
+{
+ double value = DFLOAT_VALUE(object);
+
+ if (value == 0.0 || (fabs(value) < 1.0E7 && fabs(value) > 1.0E-4))
+ return (LispFormatFixedFloat(stream, object, 0, 0, NULL, 0, 0, 0));
+
+ return (LispDoFormatExponentialFloat(stream, object, 0, 0, NULL,
+ 0, 1, 0, ' ', 'E', 0));
+}
+
+static int
+LispWriteArray(LispObj *stream, LispObj *object, write_info *info)
+{
+ int length = 0;
+ long print_level = info->level, circle;
+
+ if (info->circles && (circle = LispCheckCircle(object, info)) >= 0 &&
+ LispPrintCircle(stream, object, circle, &length, info) == 0)
+ return (length);
+
+ if (object->data.array.rank == 0) {
+ length += LispWriteStr(stream, "#0A", 3);
+ length += LispDoWriteObject(stream, object->data.array.list, info, 1);
+ return (length);
+ }
+
+ INCDEPTH();
+ ++info->level;
+ if (info->print_level < 0 || info->level <= info->print_level) {
+ if (object->data.array.rank == 1)
+ length += LispWriteStr(stream, "#(", 2);
+ else {
+ char stk[32];
+
+ format_integer(stk, object->data.array.rank, 10);
+ length += LispWriteChar(stream, '#');
+ length += LispWriteStr(stream, stk, strlen(stk));
+ length += LispWriteStr(stream, "A(", 2);
+ }
+
+ if (!object->data.array.zero) {
+ long print_length = info->length, local_length = 0;
+
+ if (object->data.array.rank == 1) {
+ LispObj *ary;
+ long count;
+
+ for (ary = object->data.array.dim, count = 1;
+ ary != NIL; ary = CDR(ary))
+ count *= FIXNUM_VALUE(CAR(ary));
+ for (ary = object->data.array.list; count > 0;
+ ary = CDR(ary), count--) {
+ if (info->print_length < 0 ||
+ ++local_length <= info->print_length) {
+ info->length = 0;
+ length += LispDoWriteObject(stream, CAR(ary), info, 1);
+ }
+ else {
+ length += LispWriteStr(stream, "...", 3);
+ break;
+ }
+ if (count - 1 > 0)
+ length += LispWriteChar(stream, ' ');
+ }
+ }
+ else {
+ LispObj *ary;
+ int i, k, rank, *dims, *loop;
+
+ rank = object->data.array.rank;
+ dims = LispMalloc(sizeof(int) * rank);
+ loop = LispCalloc(1, sizeof(int) * (rank - 1));
+
+ /* fill dim */
+ for (i = 0, ary = object->data.array.dim; ary != NIL;
+ i++, ary = CDR(ary))
+ dims[i] = FIXNUM_VALUE(CAR(ary));
+
+ i = 0;
+ ary = object->data.array.list;
+ while (loop[0] < dims[0]) {
+ if (info->print_length < 0 ||
+ local_length < info->print_length) {
+ for (; i < rank - 1; i++)
+ length += LispWriteChar(stream, '(');
+ --i;
+ for (;;) {
+ ++loop[i];
+ if (i && loop[i] >= dims[i])
+ loop[i] = 0;
+ else
+ break;
+ --i;
+ }
+ for (k = 0; k < dims[rank - 1] - 1;
+ k++, ary = CDR(ary)) {
+ if (info->print_length < 0 ||
+ k < info->print_length) {
+ ++local_length;
+ info->length = 0;
+ length += LispDoWriteObject(stream,
+ CAR(ary), info, 1);
+ length += LispWriteChar(stream, ' ');
+ }
+ }
+ if (info->print_length < 0 || k < info->print_length) {
+ ++local_length;
+ info->length = 0;
+ length += LispDoWriteObject(stream,
+ CAR(ary), info, 0);
+ }
+ else
+ length += LispWriteStr(stream, "...", 3);
+ for (k = rank - 1; k > i; k--)
+ length += LispWriteChar(stream, ')');
+ if (loop[0] < dims[0])
+ length += LispWriteChar(stream, ' ');
+ ary = CDR(ary);
+ }
+ else {
+ ++local_length;
+ length += LispWriteStr(stream, "...)", 4);
+ for (; local_length < dims[0] - 1; local_length++)
+ length += LispWriteStr(stream, " ...)", 5);
+ if (local_length <= dims[0])
+ length += LispWriteStr(stream, " ...", 4);
+ break;
+ }
+ }
+ LispFree(dims);
+ LispFree(loop);
+ }
+ info->length = print_length;
+ }
+ length += LispWriteChar(stream, ')');
+ }
+ else
+ length += LispWriteChar(stream, '#');
+ info->level = print_level;
+ DECDEPTH();
+
+ return (length);
+}
+
+static int
+LispWriteStruct(LispObj *stream, LispObj *object, write_info *info)
+{
+ int length;
+ long circle;
+ LispObj *symbol;
+ LispObj *def = object->data.struc.def;
+ LispObj *field = object->data.struc.fields;
+
+ if (info->circles && (circle = LispCheckCircle(object, info)) >= 0 &&
+ LispPrintCircle(stream, object, circle, &length, info) == 0)
+ return (length);
+
+ INCDEPTH();
+ length = LispWriteStr(stream, "#S(", 3);
+ symbol = SYMBOLP(CAR(def)) ? CAR(def) : CAAR(def);
+ length += LispWriteAtom(stream, symbol, info);
+ def = CDR(def);
+ for (; def != NIL; def = CDR(def), field = CDR(field)) {
+ length += LispWriteChar(stream, ' ');
+ symbol = SYMBOLP(CAR(def)) ? CAR(def) : CAAR(def);
+ length += LispWriteAtom(stream, symbol, info);
+ length += LispWriteChar(stream, ' ');
+ length += LispDoWriteObject(stream, CAR(field), info, 1);
+ }
+ length += LispWriteChar(stream, ')');
+ DECDEPTH();
+
+ return (length);
+}
+
+int
+LispFormatInteger(LispObj *stream, LispObj *object, int radix,
+ int atsign, int collon, int mincol,
+ int padchar, int commachar, int commainterval)
+{
+ char stk[128], *str = stk;
+ int i, length, sign, intervals;
+
+ if (LONGINTP(object))
+ format_integer(stk, LONGINT_VALUE(object), radix);
+ else {
+ if (mpi_getsize(object->data.mp.integer, radix) >= sizeof(stk))
+ str = mpi_getstr(NULL, object->data.mp.integer, radix);
+ else
+ mpi_getstr(str, object->data.mp.integer, radix);
+ }
+
+ sign = *str == '-';
+ length = strlen(str);
+
+ /* if collon, update length for the number of commachars to be printed */
+ if (collon && commainterval > 0 && commachar) {
+ intervals = length / commainterval;
+ length += intervals;
+ }
+ else
+ intervals = 0;
+
+ /* if sign must be printed, and number is positive */
+ if (atsign && !sign)
+ ++length;
+
+ /* if need padding */
+ if (padchar && mincol > length)
+ LispWriteChars(stream, padchar, mincol - length);
+
+ /* if need to print number sign */
+ if (sign || atsign)
+ LispWriteChar(stream, sign ? '-' : '+');
+
+ /* if need to print commas to separate groups of numbers */
+ if (intervals) {
+ int j;
+ char *ptr;
+
+ i = (length - atsign) - intervals;
+ j = i % commainterval;
+ /* make the loop below easier */
+ if (j == 0)
+ j = commainterval;
+ i -= j;
+ ptr = str + sign;
+ for (; j > 0; j--, ptr++)
+ LispWriteChar(stream, *ptr);
+ for (; i > 0; i -= commainterval) {
+ LispWriteChar(stream, commachar);
+ for (j = 0; j < commainterval; j++, ptr++)
+ LispWriteChar(stream, *ptr);
+ }
+ }
+ /* else, just print the string */
+ else
+ LispWriteStr(stream, str + sign, length - sign);
+
+ /* if number required more than sizeof(stk) bytes */
+ if (str != stk)
+ LispFree(str);
+
+ return (length);
+}
+
+int
+LispFormatRomanInteger(LispObj *stream, long value, int new_roman)
+{
+ char stk[32];
+ int length;
+
+ length = 0;
+ while (value > 1000) {
+ stk[length++] = 'M';
+ value -= 1000;
+ }
+ if (new_roman) {
+ if (value >= 900) {
+ strcpy(stk + length, "CM");
+ length += 2,
+ value -= 900;
+ }
+ else if (value < 500 && value >= 400) {
+ strcpy(stk + length, "CD");
+ length += 2;
+ value -= 400;
+ }
+ }
+ if (value >= 500) {
+ stk[length++] = 'D';
+ value -= 500;
+ }
+ while (value >= 100) {
+ stk[length++] = 'C';
+ value -= 100;
+ }
+ if (new_roman) {
+ if (value >= 90) {
+ strcpy(stk + length, "XC");
+ length += 2,
+ value -= 90;
+ }
+ else if (value < 50 && value >= 40) {
+ strcpy(stk + length, "XL");
+ length += 2;
+ value -= 40;
+ }
+ }
+ if (value >= 50) {
+ stk[length++] = 'L';
+ value -= 50;
+ }
+ while (value >= 10) {
+ stk[length++] = 'X';
+ value -= 10;
+ }
+ if (new_roman) {
+ if (value == 9) {
+ strcpy(stk + length, "IX");
+ length += 2,
+ value -= 9;
+ }
+ else if (value == 4) {
+ strcpy(stk + length, "IV");
+ length += 2;
+ value -= 4;
+ }
+ }
+ if (value >= 5) {
+ stk[length++] = 'V';
+ value -= 5;
+ }
+ while (value) {
+ stk[length++] = 'I';
+ --value;
+ }
+
+ stk[length] = '\0';
+
+ return (LispWriteStr(stream, stk, length));
+}
+
+int
+LispFormatEnglishInteger(LispObj *stream, long number, int ordinal)
+{
+ static char *ds[] = {
+ "", "one", "two", "three", "four",
+ "five", "six", "seven", "eight", "nine",
+ "ten", "eleven", "twelve", "thirteen", "fourteen",
+ "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
+ };
+ static char *dsth[] = {
+ "", "first", "second", "third", "fourth",
+ "fifth", "sixth", "seventh", "eighth", "ninth",
+ "tenth", "eleventh", "twelfth", "thirteenth", "fourteenth",
+ "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth"
+ };
+ static char *hs[] = {
+ "", "", "twenty", "thirty", "forty",
+ "fifty", "sixty", "seventy", "eighty", "ninety"
+ };
+ static char *hsth[] = {
+ "", "", "twentieth", "thirtieth", "fortieth",
+ "fiftieth", "sixtieth", "seventieth", "eightieth", "ninetieth"
+ };
+ static char *ts[] = {
+ "", "thousand", "million"
+ };
+ static char *tsth[] = {
+ "", "thousandth", "millionth"
+ };
+ char stk[256];
+ int length, sign;
+
+ sign = number < 0;
+ if (sign)
+ number = -number;
+ length = 0;
+
+#define SIGNLEN 6 /* strlen("minus ") */
+ if (sign) {
+ strcpy(stk, "minus ");
+ length += SIGNLEN;
+ }
+ else if (number == 0) {
+ if (ordinal) {
+ strcpy(stk, "zeroth");
+ length += 6; /* strlen("zeroth") */
+ }
+ else {
+ strcpy(stk, "zero");
+ length += 4; /* strlen("zero") */
+ }
+ }
+ for (;;) {
+ int count, temp;
+ char *t, *h, *d;
+ long value = number;
+
+ for (count = 0; value >= 1000; value /= 1000, count++)
+ ;
+
+ t = ds[value / 100];
+ if (ordinal && !count && (value % 10) == 0)
+ h = hsth[(value % 100) / 10];
+ else
+ h = hs[(value % 100) / 10];
+
+ if (ordinal && !count)
+ d = *h ? dsth[value % 10] : dsth[value % 20];
+ else
+ d = *h ? ds[value % 10] : ds[value % 20];
+
+ if (((!sign && length) || length > SIGNLEN) && (*t || *h || *d)) {
+ if (!ordinal || count || *h || *t) {
+ strcpy(stk + length, ", ");
+ length += 2;
+ }
+ else {
+ strcpy(stk + length, " ");
+ ++length;
+ }
+ }
+
+ if (*t) {
+ if (ordinal && !count && (value % 100) == 0)
+ temp = sprintf(stk + length, "%s hundredth", t);
+ else
+ temp = sprintf(stk + length, "%s hundred", t);
+ length += temp;
+ }
+
+ if (*h) {
+ if (*t) {
+ if (ordinal && !count) {
+ strcpy(stk + length, " ");
+ ++length;
+ }
+ else {
+ strcpy(stk + length, " and ");
+ length += 5; /* strlen(" and ") */
+ }
+ }
+ strcpy(stk + length, h);
+ length += strlen(h);
+ }
+
+ if (*d) {
+ if (*h) {
+ strcpy(stk + length, "-");
+ ++length;
+ }
+ else if (*t) {
+ if (ordinal && !count) {
+ strcpy(stk + length, " ");
+ ++length;
+ }
+ else {
+ strcpy(stk + length, " and ");
+ length += 5; /* strlen(" and ") */
+ }
+ }
+ strcpy(stk + length, d);
+ length += strlen(d);
+ }
+
+ if (!count)
+ break;
+ else
+ temp = count;
+
+ if (count > 1) {
+ value *= 1000;
+ while (--count)
+ value *= 1000;
+ number -= value;
+ }
+ else
+ number %= 1000;
+
+ if (ordinal && number == 0 && !*t && !*h)
+ temp = sprintf(stk + length, " %s", tsth[temp]);
+ else
+ temp = sprintf(stk + length, " %s", ts[temp]);
+ length += temp;
+
+ if (!number)
+ break;
+ }
+
+ return (LispWriteStr(stream, stk, length));
+}
+
+int
+LispFormatCharacter(LispObj *stream, LispObj *object,
+ int atsign, int collon)
+{
+ int length = 0;
+ int ch = SCHAR_VALUE(object);
+
+ if (atsign && !collon)
+ length += LispWriteStr(stream, "#\\", 2);
+ if ((atsign || collon) && (ch <= ' ' || ch == 0177)) {
+ char *name = LispChars[ch].names[0];
+
+ length += LispWriteStr(stream, name, strlen(name));
+ }
+ else
+ length += LispWriteChar(stream, ch);
+
+ return (length);
+}
+
+/* returns 1 if string size must grow, done inplace */
+static int
+float_string_inc(char *buffer, int offset)
+{
+ int i;
+
+ for (i = offset; i >= 0; i--) {
+ if (buffer[i] == '9')
+ buffer[i] = '0';
+ else if (buffer[i] != '.') {
+ ++buffer[i];
+ break;
+ }
+ }
+ if (i < 0) {
+ int length = strlen(buffer);
+
+ /* string size must change */
+ memmove(buffer + 1, buffer, length + 1);
+ buffer[0] = '1';
+
+ return (1);
+ }
+
+ return (0);
+}
+
+int
+LispFormatFixedFloat(LispObj *stream, LispObj *object,
+ int atsign, int w, int *pd, int k, int overflowchar,
+ int padchar)
+{
+ char buffer[512], stk[64];
+ int sign, exponent, length, offset, d = pd ? *pd : FLOAT_PREC, again;
+ double value = DFLOAT_VALUE(object);
+
+ if (value == 0.0) {
+ exponent = k = 0;
+ strcpy(stk, "+0");
+ }
+ else
+ /* calculate format parameters, adjusting scale factor */
+ parse_double(stk, &exponent, value, d + 1 + k);
+
+ /* make sure k won't cause overflow */
+ if (k > 128)
+ k = 128;
+ else if (k < -128)
+ k = -128;
+
+ /* make sure d won't cause overflow */
+ if (d > 128)
+ d = 128;
+ else if (d < -128)
+ d = -128;
+
+ /* adjust scale factor, exponent is used as an index in stk */
+ exponent += k + 1;
+
+ /* how many bytes in float representation */
+ length = strlen(stk) - 1;
+
+ /* need to print a sign? */
+ sign = atsign || (stk[0] == '-');
+
+ /* format number, cannot overflow, as control variables were checked */
+ offset = 0;
+ if (sign)
+ buffer[offset++] = stk[0];
+ if (exponent > 0) {
+ if (exponent > length) {
+ memcpy(buffer + offset, stk + 1, length);
+ memset(buffer + offset + length, '0', exponent - length);
+ }
+ else
+ memcpy(buffer + offset, stk + 1, exponent);
+ offset += exponent;
+ buffer[offset++] = '.';
+ if (length > exponent) {
+ memcpy(buffer + offset, stk + 1 + exponent, length - exponent);
+ offset += length - exponent;
+ }
+ else
+ buffer[offset++] = '0';
+ }
+ else {
+ buffer[offset++] = '0';
+ buffer[offset++] = '.';
+ while (exponent < 0) {
+ buffer[offset++] = '0';
+ exponent++;
+ }
+ memcpy(buffer + offset, stk + 1, length);
+ offset += length;
+ }
+ buffer[offset] = '\0';
+
+ again = 0;
+fixed_float_check_again:
+ /* make sure only d digits are printed after decimal point */
+ if (d > 0) {
+ char *dptr = strchr(buffer, '.');
+
+ length = strlen(dptr) - 1;
+ /* check if need to remove excess digits */
+ if (length > d) {
+ int digit;
+
+ offset = (dptr - buffer) + 1 + d;
+ digit = buffer[offset];
+
+ /* remove extra digits */
+ buffer[offset] = '\0';
+
+ /* check if need to round */
+ if (!again && offset > 1 && isdigit(digit) && digit >= '5' &&
+ isdigit(buffer[offset - 1]) &&
+ float_string_inc(buffer, offset - 1))
+ ++offset;
+ }
+ /* check if need to add extra zero digits to fill space */
+ else if (length < d) {
+ offset += d - length;
+ for (++length; length <= d; length++)
+ dptr[length] = '0';
+ dptr[length] = '\0';
+ }
+ }
+ else {
+ /* no digits after decimal point */
+ int digit, inc = 0;
+ char *dptr = strchr(buffer, '.') + 1;
+
+ digit = *dptr;
+ if (!again && digit >= '5' && dptr >= buffer + 2 && isdigit(dptr[-2]))
+ inc = float_string_inc(buffer, dptr - buffer - 2);
+
+ offset = (dptr - buffer) + inc;
+ buffer[offset] = '\0';
+ }
+
+ /* if d was not specified, remove any extra zeros */
+ if (pd == NULL) {
+ while (offset > 2 && buffer[offset - 2] != '.' &&
+ buffer[offset - 1] == '0')
+ --offset;
+ buffer[offset] = '\0';
+ }
+
+ if (w > 0 && offset > w) {
+ /* first check if can remove extra fractional digits */
+ if (pd == NULL) {
+ char *ptr = strchr(buffer, '.') + 1;
+
+ if (ptr - buffer < w) {
+ d = w - (ptr - buffer);
+ goto fixed_float_check_again;
+ }
+ }
+
+ /* remove leading "zero" to save space */
+ if ((!sign && buffer[0] == '0') || (sign && buffer[1] == '0')) {
+ /* ending nul also copied */
+ memmove(buffer + sign, buffer + sign + 1, offset);
+ --offset;
+ }
+ /* remove leading '+' to "save" space */
+ if (offset > w && buffer[0] == '+') {
+ /* ending nul also copied */
+ memmove(buffer, buffer + 1, offset);
+ --offset;
+ }
+ }
+
+ /* if cannot represent number in given width */
+ if (overflowchar && offset > w) {
+ again = 1;
+ goto fixed_float_overflow;
+ }
+
+ length = 0;
+ /* print padding if required */
+ if (w > offset)
+ length += LispWriteChars(stream, padchar, w - offset);
+
+ /* print float number representation */
+ return (LispWriteStr(stream, buffer, offset) + length);
+
+fixed_float_overflow:
+ return (LispWriteChars(stream, overflowchar, w));
+}
+
+int
+LispFormatExponentialFloat(LispObj *stream, LispObj *object,
+ int atsign, int w, int *pd, int e, int k,
+ int overflowchar, int padchar, int exponentchar)
+{
+ return (LispDoFormatExponentialFloat(stream, object, atsign, w,
+ pd, e, k, overflowchar, padchar,
+ exponentchar, 1));
+}
+
+int
+LispDoFormatExponentialFloat(LispObj *stream, LispObj *object,
+ int atsign, int w, int *pd, int e, int k,
+ int overflowchar, int padchar, int exponentchar,
+ int format)
+{
+ char buffer[512], stk[64];
+ int sign, exponent, length, offset, d = pd ? *pd : FLOAT_PREC;
+ double value = DFLOAT_VALUE(object);
+
+ if (value == 0.0) {
+ exponent = 0;
+ k = 1;
+ strcpy(stk, "+0");
+ }
+ else
+ /* calculate format parameters, adjusting scale factor */
+ parse_double(stk, &exponent, value, d + k - 1);
+
+ /* set e to a value that won't overflow */
+ if (e > 16)
+ e = 16;
+
+ /* set k to a value that won't overflow */
+ if (k > 128)
+ k = 128;
+ else if (k < -128)
+ k = -128;
+
+ /* set d to a value that won't overflow */
+ if (d > 128)
+ d = 128;
+ else if (d < -128)
+ d = -128;
+
+ /* how many bytes in float representation */
+ length = strlen(stk) - 1;
+
+ /* need to print a sign? */
+ sign = atsign || (stk[0] == '-');
+
+ /* adjust number of digits after decimal point */
+ if (k > 0)
+ d -= k - 1;
+
+ /* adjust exponent, based on scale factor */
+ exponent -= k - 1;
+
+ /* format number, cannot overflow, as control variables were checked */
+ offset = 0;
+ if (sign)
+ buffer[offset++] = stk[0];
+ if (k > 0) {
+ if (k > length) {
+ memcpy(buffer + offset, stk + 1, length);
+ offset += length;
+ }
+ else {
+ memcpy(buffer + offset, stk + 1, k);
+ offset += k;
+ }
+ buffer[offset++] = '.';
+ if (length > k) {
+ memcpy(buffer + offset, stk + 1 + k, length - k);
+ offset += length - k;
+ }
+ else
+ buffer[offset++] = '0';
+ }
+ else {
+ int tmp = k;
+
+ buffer[offset++] = '0';
+ buffer[offset++] = '.';
+ while (tmp < 0) {
+ buffer[offset++] = '0';
+ tmp++;
+ }
+ memcpy(buffer + offset, stk + 1, length);
+ offset += length;
+ }
+
+ /* if format, then always add a sign to exponent */
+ buffer[offset++] = exponentchar;
+ if (format || exponent < 0)
+ buffer[offset++] = exponent < 0 ? '-' : '+';
+
+ /* XXX destroy stk contents */
+ sprintf(stk, "%%0%dd", e);
+ /* format scale factor*/
+ length = sprintf(buffer + offset, stk,
+ exponent < 0 ? -exponent : exponent);
+ /* check for overflow in exponent */
+ if (length > e && overflowchar)
+ goto exponential_float_overflow;
+ offset += length;
+
+ /* make sure only d digits are printed after decimal point */
+ if (d > 0) {
+ int currd;
+ char *dptr = strchr(buffer, '.'),
+ *eptr = strchr(dptr, exponentchar);
+
+ currd = eptr - dptr - 1;
+ length = strlen(eptr);
+
+ /* check if need to remove excess digits */
+ if (currd > d) {
+ int digit, dpos;
+
+ dpos = offset = (dptr - buffer) + 1 + d;
+ digit = buffer[offset];
+
+ memmove(buffer + offset, eptr, length + 1);
+ /* also copy ending nul character */
+
+ /* adjust offset to length of total string */
+ offset += length;
+
+ /* check if need to round */
+ if (dpos > 1 && isdigit(digit) && digit >= '5' &&
+ isdigit(buffer[dpos - 1]) &&
+ float_string_inc(buffer, dpos - 1))
+ ++offset;
+ }
+ /* check if need to add extra zero digits to fill space */
+ else if (pd && currd < d) {
+ memmove(eptr + d - currd, eptr, length + 1);
+ /* also copy ending nul character */
+
+ offset += d - currd;
+ for (++currd; currd <= d; currd++)
+ dptr[currd] = '0';
+ }
+ /* check if need to remove zeros */
+ else if (pd == NULL) {
+ int zeros = 1;
+
+ while (eptr[-zeros] == '0')
+ ++zeros;
+ if (eptr[-zeros] == '.')
+ --zeros;
+ if (zeros > 1) {
+ memmove(eptr - zeros + 1, eptr, length + 1);
+ offset -= zeros - 1;
+ }
+ }
+ }
+ else {
+ /* no digits after decimal point */
+ int digit, inc = 0;
+ char *dptr = strchr(buffer, '.'),
+ *eptr = strchr(dptr, exponentchar);
+
+ digit = dptr[1];
+
+ offset = (dptr - buffer) + 1;
+ length = strlen(eptr);
+ memmove(buffer + offset, eptr, length + 1);
+ /* also copy ending nul character */
+
+ if (digit >= '5' && dptr >= buffer + 2 &&
+ isdigit(dptr[-2]))
+ inc = float_string_inc(buffer, dptr - buffer - 2);
+
+ /* adjust offset to length of total string */
+ offset += length + inc;
+ }
+
+ if (w > 0 && offset > w) {
+ /* remove leading "zero" to save space */
+ if ((!sign && buffer[0] == '0') || (sign && buffer[1] == '0')) {
+ /* ending nul also copied */
+ memmove(buffer + sign, buffer + sign + 1, offset);
+ --offset;
+ }
+ /* remove leading '+' to "save" space */
+ if (offset > w && buffer[0] == '+') {
+ /* ending nul also copied */
+ memmove(buffer, buffer + 1, offset);
+ --offset;
+ }
+ }
+
+ /* if cannot represent number in given width */
+ if (overflowchar && offset > w)
+ goto exponential_float_overflow;
+
+ length = 0;
+ /* print padding if required */
+ if (w > offset)
+ length += LispWriteChars(stream, padchar, w - offset);
+
+ /* print float number representation */
+ return (LispWriteStr(stream, buffer, offset) + length);
+
+exponential_float_overflow:
+ return (LispWriteChars(stream, overflowchar, w));
+}
+
+int
+LispFormatGeneralFloat(LispObj *stream, LispObj *object,
+ int atsign, int w, int *pd, int e, int k,
+ int overflowchar, int padchar, int exponentchar)
+{
+ char stk[64];
+ int length, exponent, n, dd, ee, ww, d = pd ? *pd : FLOAT_PREC;
+ double value = DFLOAT_VALUE(object);
+
+ if (value == 0.0) {
+ exponent = 0;
+ n = 0;
+ d = 1;
+ strcpy(stk, "+0");
+ }
+ else {
+ /* calculate format parameters, adjusting scale factor */
+ parse_double(stk, &exponent, value, d + k - 1);
+ n = exponent + 1;
+ }
+
+ /* Let ee equal e+2, or 4 if e is omitted. */
+ if (e)
+ ee = e + 2;
+ else
+ ee = 4;
+
+ /* Let ww equal w-ee, or nil if w is omitted. */
+ if (w)
+ ww = w - ee;
+ else
+ ww = 0;
+
+ dd = d - n;
+ if (d >= dd && dd >= 0) {
+ length = LispFormatFixedFloat(stream, object, atsign, ww,
+ &dd, 0, overflowchar, padchar);
+
+ /* ~ee@T */
+ length += LispWriteChars(stream, padchar, ee);
+ }
+ else
+ length = LispFormatExponentialFloat(stream, object, atsign,
+ w, pd, e, k, overflowchar,
+ padchar, exponentchar);
+
+ return (length);
+}
+
+int
+LispFormatDollarFloat(LispObj *stream, LispObj *object,
+ int atsign, int collon, int d, int n, int w, int padchar)
+{
+ char buffer[512], stk[64];
+ int sign, exponent, length, offset;
+ double value = DFLOAT_VALUE(object);
+
+ if (value == 0.0) {
+ exponent = 0;
+ strcpy(stk, "+0");
+ }
+ else
+ /* calculate format parameters, adjusting scale factor */
+ parse_double(stk, &exponent, value, d == 0 ? FLOAT_PREC : d + 1);
+
+ /* set d to a "sane" value */
+ if (d > 128)
+ d = 128;
+
+ /* set n to a "sane" value */
+ if (n > 128)
+ n = 128;
+
+ /* use exponent as index in stk */
+ ++exponent;
+
+ /* don't put sign in buffer,
+ * if collon specified, must go before padding */
+ sign = atsign || (stk[0] == '-');
+
+ offset = 0;
+
+ /* pad with zeros if required */
+ if (exponent > 0)
+ n -= exponent;
+ while (n > 0) {
+ buffer[offset++] = '0';
+ n--;
+ }
+
+ /* how many bytes in float representation */
+ length = strlen(stk) - 1;
+
+ if (exponent > 0) {
+ if (exponent > length) {
+ memcpy(buffer + offset, stk + 1, length);
+ memset(buffer + offset + length, '0', exponent - length);
+ }
+ else
+ memcpy(buffer + offset, stk + 1, exponent);
+ offset += exponent;
+ buffer[offset++] = '.';
+ if (length > exponent) {
+ memcpy(buffer + offset, stk + 1 + exponent, length - exponent);
+ offset += length - exponent;
+ }
+ else
+ buffer[offset++] = '0';
+ }
+ else {
+ if (n > 0)
+ buffer[offset++] = '0';
+ buffer[offset++] = '.';
+ while (exponent < 0) {
+ buffer[offset++] = '0';
+ exponent++;
+ }
+ memcpy(buffer + offset, stk + 1, length);
+ offset += length;
+ }
+ buffer[offset] = '\0';
+
+ /* make sure only d digits are printed after decimal point */
+ if (d > 0) {
+ char *dptr = strchr(buffer, '.');
+
+ length = strlen(dptr) - 1;
+ /* check if need to remove excess digits */
+ if (length > d) {
+ int digit;
+
+ offset = (dptr - buffer) + 1 + d;
+ digit = buffer[offset];
+
+ /* remove extra digits */
+ buffer[offset] = '\0';
+
+ /* check if need to round */
+ if (offset > 1 && isdigit(digit) && digit >= '5' &&
+ isdigit(buffer[offset - 1]) &&
+ float_string_inc(buffer, offset - 1))
+ ++offset;
+ }
+ /* check if need to add extra zero digits to fill space */
+ else if (length < d) {
+ offset += d - length;
+ for (++length; length <= d; length++)
+ dptr[length] = '0';
+ dptr[length] = '\0';
+ }
+ }
+ else {
+ /* no digits after decimal point */
+ int digit, inc = 0;
+ char *dptr = strchr(buffer, '.') + 1;
+
+ digit = *dptr;
+ if (digit >= '5' && dptr >= buffer + 2 && isdigit(dptr[-2]))
+ inc = float_string_inc(buffer, dptr - buffer - 2);
+
+ offset = (dptr - buffer) + inc;
+ buffer[offset] = '\0';
+ }
+
+ length = 0;
+ if (sign) {
+ ++offset;
+ if (atsign && collon)
+ length += LispWriteChar(stream, value >= 0.0 ? '+' : '-');
+ }
+
+ /* print padding if required */
+ if (w > offset)
+ length += LispWriteChars(stream, padchar, w - offset);
+
+ if (atsign && !collon)
+ length += LispWriteChar(stream, value >= 0.0 ? '+' : '-');
+
+ /* print float number representation */
+ return (LispWriteStr(stream, buffer, offset) + length);
+}