write.c revision 5dfecf96
1/*
2 * Copyright (c) 2002 by The XFree86 Project, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 *
22 * Except as contained in this notice, the name of the XFree86 Project shall
23 * not be used in advertising or otherwise to promote the sale, use or other
24 * dealings in this Software without prior written authorization from the
25 * XFree86 Project.
26 *
27 * Author: Paulo César Pereira de Andrade
28 */
29
30/* $XFree86: xc/programs/xedit/lisp/write.c,v 1.31tsi Exp $ */
31
32#include "lisp/write.h"
33#include "lisp/hash.h"
34#include <math.h>
35#include <ctype.h>
36
37#define	FLOAT_PREC	17
38
39#define UPCASE		0
40#define DOWNCASE	1
41#define CAPITALIZE	2
42
43#define INCDEPTH()							\
44    if (++info->depth > MAX_STACK_DEPTH / 2)				\
45	LispDestroy("stack overflow")
46#define DECDEPTH()	--info->depth
47
48/*
49 * Types
50 */
51typedef struct _circle_info {
52    long circle_nth;		/* nth circular list */
53    LispObj *object;		/* the circular object */
54} circle_info;
55
56typedef struct _write_info {
57    long depth;
58    long level;			/* current level */
59    long length;		/* current length */
60    long print_level;		/* *print-level* when started printing */
61    long print_length;		/* *print-length* when started printing */
62
63    int print_escape;
64    int print_case;
65
66    long circle_count;
67    /* used while building circle info */
68    LispObj **objects;
69    long num_objects;
70    /* the circular lists */
71    circle_info *circles;
72    long num_circles;
73} write_info;
74
75/*
76 * Prototypes
77 */
78static void check_stream(LispObj*, LispFile**, LispString**, int);
79static void parse_double(char*, int*, double, int);
80static int float_string_inc(char*, int);
81static void format_integer(char*, long, int);
82static int LispWriteCPointer(LispObj*, void*);
83static int LispWriteCString(LispObj*, char*, long, write_info*);
84static int LispDoFormatExponentialFloat(LispObj*, LispObj*,
85					int, int, int*, int, int,
86					int, int, int, int);
87
88static int LispWriteInteger(LispObj*, LispObj*);
89static int LispWriteCharacter(LispObj*, LispObj*, write_info*);
90static int LispWriteString(LispObj*, LispObj*, write_info*);
91static int LispWriteFloat(LispObj*, LispObj*);
92static int LispWriteAtom(LispObj*, LispObj*, write_info*);
93static int LispDoWriteAtom(LispObj*, char*, int, int);
94static int LispWriteList(LispObj*, LispObj*, write_info*, int);
95static int LispWriteArray(LispObj*, LispObj*, write_info*);
96static int LispWriteStruct(LispObj*, LispObj*, write_info*);
97static int LispDoWriteObject(LispObj*, LispObj*, write_info*, int);
98static void LispBuildCircle(LispObj*, write_info*);
99static void LispDoBuildCircle(LispObj*, write_info*);
100static long LispCheckCircle(LispObj*, write_info*);
101static int LispPrintCircle(LispObj*, LispObj*, long, int*, write_info*);
102static int LispWriteAlist(LispObj*, LispArgList*, write_info*);
103
104/*
105 * Initialization
106 */
107LispObj *Oprint_level, *Oprint_length, *Oprint_circle,
108	*Oprint_escape, *Oprint_case;
109LispObj *Kupcase, *Kdowncase, *Kcapitalize;
110
111/*
112 * Implementation
113 */
114void
115LispWriteInit(void)
116{
117    Oprint_level	= STATIC_ATOM("*PRINT-LEVEL*");
118    LispProclaimSpecial(Oprint_level, NIL, NIL);
119    LispExportSymbol(Oprint_level);
120
121    Oprint_length	= STATIC_ATOM("*PRINT-LENGTH*");
122    LispProclaimSpecial(Oprint_length, NIL, NIL);
123    LispExportSymbol(Oprint_length);
124
125    Oprint_circle	= STATIC_ATOM("*PRINT-CIRCLE*");
126    LispProclaimSpecial(Oprint_circle, T, NIL);
127    LispExportSymbol(Oprint_circle);
128
129    Oprint_escape	= STATIC_ATOM("*PRINT-ESCAPE*");
130    LispProclaimSpecial(Oprint_escape, T, NIL);
131    LispExportSymbol(Oprint_escape);
132
133    Kupcase		= KEYWORD("UPCASE");
134    Kdowncase		= KEYWORD("DOWNCASE");
135    Kcapitalize		= KEYWORD("CAPITALIZE");
136    Oprint_case		= STATIC_ATOM("*PRINT-CASE*");
137    LispProclaimSpecial(Oprint_case, Kupcase, NIL);
138    LispExportSymbol(Oprint_case);
139}
140
141LispObj *
142Lisp_FreshLine(LispBuiltin *builtin)
143/*
144 fresh-line &optional output-stream
145 */
146{
147    LispObj *output_stream;
148
149    output_stream = ARGUMENT(0);
150
151    if (output_stream == UNSPEC)
152	output_stream = NIL;
153    else if (output_stream != NIL) {
154	CHECK_STREAM(output_stream);
155    }
156    if (LispGetColumn(output_stream)) {
157	LispWriteChar(output_stream, '\n');
158	if (output_stream == NIL ||
159	    (output_stream->data.stream.type == LispStreamStandard &&
160	     output_stream->data.stream.source.file == Stdout))
161	    LispFflush(Stdout);
162	return (T);
163    }
164
165    return (NIL);
166}
167
168LispObj *
169Lisp_Prin1(LispBuiltin *builtin)
170/*
171 prin1 object &optional output-stream
172 */
173{
174    LispObj *object, *output_stream;
175
176    output_stream = ARGUMENT(1);
177    object = ARGUMENT(0);
178
179    if (output_stream == UNSPEC)
180	output_stream = NIL;
181    LispPrint(object, output_stream, 0);
182
183    return (object);
184}
185
186LispObj *
187Lisp_Princ(LispBuiltin *builtin)
188/*
189 princ object &optional output-stream
190 */
191{
192    int head;
193    LispObj *object, *output_stream;
194
195    output_stream = ARGUMENT(1);
196    object = ARGUMENT(0);
197
198    if (output_stream == UNSPEC)
199	output_stream = NIL;
200    head = lisp__data.env.length;
201    LispAddVar(Oprint_escape, NIL);
202    ++lisp__data.env.head;
203    LispPrint(object, output_stream, 0);
204    lisp__data.env.head = lisp__data.env.length = head;
205
206    return (object);
207}
208
209LispObj *
210Lisp_Print(LispBuiltin *builtin)
211/*
212 print object &optional output-stream
213 */
214{
215    LispObj *object, *output_stream;
216
217    output_stream = ARGUMENT(1);
218    object = ARGUMENT(0);
219
220    if (output_stream == UNSPEC)
221	output_stream = NIL;
222    LispWriteChar(output_stream, '\n');
223    LispPrint(object, output_stream, 0);
224    LispWriteChar(output_stream, ' ');
225
226    return (object);
227}
228
229LispObj *
230Lisp_Terpri(LispBuiltin *builtin)
231/*
232 terpri &optional output-stream
233 */
234{
235    LispObj *output_stream;
236
237    output_stream = ARGUMENT(0);
238
239    if (output_stream == UNSPEC)
240	output_stream = NIL;
241    else if (output_stream != NIL) {
242	CHECK_STREAM(output_stream);
243    }
244    LispWriteChar(output_stream, '\n');
245    if (output_stream == NIL ||
246	(output_stream->data.stream.type == LispStreamStandard &&
247	 output_stream->data.stream.source.file == Stdout))
248	LispFflush(Stdout);
249
250    return (NIL);
251}
252
253LispObj *
254Lisp_Write(LispBuiltin *builtin)
255/*
256 write object &key case circle escape length level lines pretty readably right-margin stream
257 */
258{
259    int head = lisp__data.env.length;
260
261    LispObj *object, *ocase, *circle, *escape, *length, *level, *stream;
262
263    stream = ARGUMENT(10);
264    level = ARGUMENT(5);
265    length = ARGUMENT(4);
266    escape = ARGUMENT(3);
267    circle = ARGUMENT(2);
268    ocase = ARGUMENT(1);
269    object = ARGUMENT(0);
270
271    if (stream == UNSPEC)
272	stream = NIL;
273    else if (stream != NIL) {
274	CHECK_STREAM(stream);
275    }
276
277    /* prepare the printer environment */
278    if (circle != UNSPEC)
279	LispAddVar(Oprint_circle, circle);
280    if (length != UNSPEC)
281	LispAddVar(Oprint_length, length);
282    if (level != UNSPEC)
283	LispAddVar(Oprint_level, level);
284    if (ocase != UNSPEC)
285	LispAddVar(Oprint_case, ocase);
286    if (escape != UNSPEC)
287	LispAddVar(Oprint_escape, escape);
288
289    lisp__data.env.head = lisp__data.env.length;
290
291    (void)LispWriteObject(stream, object);
292
293    lisp__data.env.head = lisp__data.env.length = head;
294
295    return (object);
296}
297
298LispObj *
299Lisp_WriteChar(LispBuiltin *builtin)
300/*
301 write-char character &optional output-stream
302 */
303{
304    int ch;
305
306    LispObj *character, *output_stream;
307
308    output_stream = ARGUMENT(1);
309    character = ARGUMENT(0);
310
311    if (output_stream == UNSPEC)
312	output_stream = NIL;
313    CHECK_SCHAR(character);
314    ch = SCHAR_VALUE(character);
315
316    LispWriteChar(output_stream, ch);
317
318    return (character);
319}
320
321LispObj *
322Lisp_WriteLine(LispBuiltin *builtin)
323/*
324 write-line string &optional output-stream &key start end
325 */
326{
327    return (LispWriteString_(builtin, 1));
328}
329
330LispObj *
331Lisp_WriteString(LispBuiltin *builtin)
332/*
333 write-string string &optional output-stream &key start end
334 */
335{
336    return (LispWriteString_(builtin, 0));
337}
338
339
340int
341LispWriteObject(LispObj *stream, LispObj *object)
342{
343    write_info info;
344    int bytes;
345    LispObj *level, *length, *circle, *oescape, *ocase;
346
347    /* current state */
348    info.depth = info.level = info.length = 0;
349
350    /* maximum level to descend */
351    level = LispGetVar(Oprint_level);
352    if (level && INDEXP(level))
353	info.print_level = FIXNUM_VALUE(level);
354    else
355	info.print_level = -1;
356
357    /* maximum list length */
358    length = LispGetVar(Oprint_length);
359    if (length && INDEXP(length))
360	info.print_length = FIXNUM_VALUE(length);
361    else
362	info.print_length = -1;
363
364    /* detect circular/shared objects? */
365    circle = LispGetVar(Oprint_circle);
366    info.circle_count = 0;
367    info.objects = NULL;
368    info.num_objects = 0;
369    info.circles = NULL;
370    info.num_circles = 0;
371    if (circle && circle != NIL) {
372	LispBuildCircle(object, &info);
373	/* free this data now */
374	if (info.num_objects) {
375	    LispFree(info.objects);
376	    info.num_objects = 0;
377	}
378    }
379
380    /* escape characters and strings? */
381    oescape = LispGetVar(Oprint_escape);
382    if (oescape != NULL)
383	info.print_escape = oescape == NIL;
384    else
385	info.print_escape = -1;
386
387    /* don't use the default case printing? */
388    ocase = LispGetVar(Oprint_case);
389    if (ocase == Kdowncase)
390	info.print_case = DOWNCASE;
391    else if (ocase == Kcapitalize)
392	info.print_case = CAPITALIZE;
393    else
394	info.print_case = UPCASE;
395
396    bytes = LispDoWriteObject(stream, object, &info, 1);
397    if (circle && circle != NIL && info.num_circles)
398	LispFree(info.circles);
399
400    return (bytes);
401}
402
403static void
404LispBuildCircle(LispObj *object, write_info *info)
405{
406    LispObj *list;
407
408    switch (OBJECT_TYPE(object)) {
409	case LispCons_t:
410	    LispDoBuildCircle(object, info);
411	    break;
412	case LispArray_t:
413	    /* Currently arrays are implemented as lists, but only
414	     * the elements could/should be circular */
415	    if (LispCheckCircle(object, info) >= 0)
416		return;
417	    LispDoBuildCircle(object, info);
418	    for (list = object->data.array.list;
419		 CONSP(list); list = CDR(list))
420		LispBuildCircle(CAR(list), info);
421	    break;
422	case LispStruct_t:
423	    /* Like arrays, structs are currently implemented as lists,
424	     * but only the elements could/should be circular */
425	    if (LispCheckCircle(object, info) >= 0)
426		return;
427	    LispDoBuildCircle(object, info);
428	    for (list = object->data.struc.fields;
429		 CONSP(list); list = CDR(list))
430		LispBuildCircle(CAR(list), info);
431	    break;
432	case LispQuote_t:
433	case LispBackquote_t:
434	case LispFunctionQuote_t:
435	    LispDoBuildCircle(object, info);
436	    LispBuildCircle(object->data.quote, info);
437	    break;
438	case LispComma_t:
439	    LispDoBuildCircle(object, info);
440	    LispBuildCircle(object->data.comma.eval, info);
441	    break;
442	case LispLambda_t:
443	    /* Circularity in a function body should fail elsewhere... */
444	    if (LispCheckCircle(object, info) >= 0)
445		return;
446	    LispDoBuildCircle(object, info);
447	    LispBuildCircle(object->data.lambda.code, info);
448	    break;
449	default:
450	    break;
451    }
452}
453
454static void
455LispDoBuildCircle(LispObj *object, write_info *info)
456{
457    long i;
458
459    if (LispCheckCircle(object, info) >= 0)
460	return;
461
462    for (i = 0; i < info->num_objects; i++)
463	if (info->objects[i] == object) {
464	    /* circularity found */
465	    info->circles = LispRealloc(info->circles, sizeof(circle_info) *
466					(info->num_circles + 1));
467	    info->circles[info->num_circles].circle_nth = 0;
468	    info->circles[info->num_circles].object = object;
469	    ++info->num_circles;
470	    return;
471	}
472
473    /* object pointer not yet recorded */
474    if ((i % 16) == 0)
475	info->objects = LispRealloc(info->objects, sizeof(LispObj*) *
476				    (info->num_objects + 16));
477    info->objects[info->num_objects++] = object;
478
479    if (CONSP(object)) {
480	if (CONSP(CAR(object)))
481	    LispDoBuildCircle(CAR(object), info);
482	else
483	    LispBuildCircle(CAR(object), info);
484	if (CONSP(CDR(object)))
485	    LispDoBuildCircle(CDR(object), info);
486	else
487	    LispBuildCircle(CDR(object), info);
488    }
489}
490
491static long
492LispCheckCircle(LispObj *object, write_info *info)
493{
494    long i;
495
496    for (i = 0; i < info->num_circles; i++)
497	if (info->circles[i].object == object)
498	    return (i);
499
500    return (-1);
501}
502
503static int
504LispPrintCircle(LispObj *stream, LispObj *object, long circle,
505		int *length, write_info *info)
506{
507    char stk[32];
508
509    if (!info->circles[circle].circle_nth) {
510	sprintf(stk, "#%ld=", ++info->circle_count);
511	*length += LispWriteStr(stream, stk, strlen(stk));
512	info->circles[circle].circle_nth = info->circle_count;
513
514	return (1);
515    }
516    sprintf(stk, "#%ld#", info->circles[circle].circle_nth);
517    *length += LispWriteStr(stream, stk, strlen(stk));
518
519    return (0);
520}
521
522static int
523LispWriteAlist(LispObj *stream, LispArgList *alist, write_info *info)
524{
525    char *name;
526    int i, length = 0, need_space = 0;
527
528#define WRITE_ATOM(object)						\
529    name = ATOMID(object);						\
530    length += LispDoWriteAtom(stream, name, strlen(name),		\
531			      info->print_case)
532#define WRITE_STRING(string)						\
533    length += LispDoWriteAtom(stream, string, strlen(string),		\
534			      info->print_case)
535#define WRITE_OBJECT(object)						\
536    length += LispDoWriteObject(stream, object, info, 1)
537#define WRITE_OPAREN()							\
538    length += LispWriteChar(stream, '(')
539#define WRITE_SPACE()							\
540    length += LispWriteChar(stream, ' ')
541#define WRITE_CPAREN()							\
542    length += LispWriteChar(stream, ')')
543
544    WRITE_OPAREN();
545    for (i = 0; i < alist->normals.num_symbols; i++) {
546	WRITE_ATOM(alist->normals.symbols[i]);
547	if (i + 1 < alist->normals.num_symbols)
548	    WRITE_SPACE();
549	else
550	    need_space = 1;
551    }
552    if (alist->optionals.num_symbols) {
553	if (need_space)
554	    WRITE_SPACE();
555	WRITE_STRING(Soptional);
556	WRITE_SPACE();
557	for (i = 0; i < alist->optionals.num_symbols; i++) {
558	    WRITE_OPAREN();
559	    WRITE_ATOM(alist->optionals.symbols[i]);
560	    WRITE_SPACE();
561	    WRITE_OBJECT(alist->optionals.defaults[i]);
562	    if (alist->optionals.sforms[i]) {
563		WRITE_SPACE();
564		WRITE_ATOM(alist->optionals.sforms[i]);
565	    }
566	    WRITE_CPAREN();
567	    if (i + 1 < alist->optionals.num_symbols)
568		WRITE_SPACE();
569	}
570	need_space = 1;
571    }
572    if (alist->keys.num_symbols) {
573	if (need_space)
574	    WRITE_SPACE();
575	length += LispDoWriteAtom(stream, Skey, 4, info->print_case);
576	WRITE_SPACE();
577	for (i = 0; i < alist->keys.num_symbols; i++) {
578	    WRITE_OPAREN();
579	    if (alist->keys.keys[i]) {
580		WRITE_OPAREN();
581		WRITE_ATOM(alist->keys.keys[i]);
582		WRITE_SPACE();
583	    }
584	    WRITE_ATOM(alist->keys.symbols[i]);
585	    if (alist->keys.keys[i])
586		WRITE_CPAREN();
587	    WRITE_SPACE();
588	    WRITE_OBJECT(alist->keys.defaults[i]);
589	    if (alist->keys.sforms[i]) {
590		WRITE_SPACE();
591		WRITE_ATOM(alist->keys.sforms[i]);
592	    }
593	    WRITE_CPAREN();
594	    if (i + 1 < alist->keys.num_symbols)
595		WRITE_SPACE();
596	}
597	need_space = 1;
598    }
599    if (alist->rest) {
600	if (need_space)
601	    WRITE_SPACE();
602	WRITE_STRING(Srest);
603	WRITE_SPACE();
604	WRITE_ATOM(alist->rest);
605	need_space = 1;
606    }
607    if (alist->auxs.num_symbols) {
608	if (need_space)
609	    WRITE_SPACE();
610	WRITE_STRING(Saux);
611	WRITE_SPACE();
612	for (i = 0; i < alist->auxs.num_symbols; i++) {
613	    WRITE_OPAREN();
614	    WRITE_ATOM(alist->auxs.symbols[i]);
615	    WRITE_SPACE();
616	    WRITE_OBJECT(alist->auxs.initials[i]);
617	    WRITE_CPAREN();
618	    if (i + 1 < alist->auxs.num_symbols)
619		WRITE_SPACE();
620	}
621    }
622    WRITE_CPAREN();
623
624#undef WRITE_ATOM
625#undef WRITE_STRING
626#undef WRITE_OBJECT
627#undef WRITE_OPAREN
628#undef WRITE_SPACE
629#undef WRITE_CPAREN
630
631    return (length);
632}
633
634static void
635check_stream(LispObj *stream,
636	     LispFile **file, LispString **string, int check_writable)
637{
638    /* NIL is UNIX stdout, *STANDARD-OUTPUT* may not be UNIX stdout */
639    if (stream == NIL) {
640	*file = Stdout;
641	*string = NULL;
642    }
643    else {
644	if (!STREAMP(stream))
645	    LispDestroy("%s is not a stream", STROBJ(stream));
646	if (check_writable && !stream->data.stream.writable)
647	    LispDestroy("%s is not writable", STROBJ(stream));
648	else if (stream->data.stream.type == LispStreamString) {
649	    *string = SSTREAMP(stream);
650	    *file = NULL;
651	}
652	else {
653	    if (stream->data.stream.type == LispStreamPipe)
654		*file = OPSTREAMP(stream);
655	    else
656		*file = stream->data.stream.source.file;
657	    *string = NULL;
658	}
659    }
660}
661
662/* Assumes buffer has enough storage, 64 bytes should be more than enough */
663static void
664parse_double(char *buffer, int *exponent, double value, int d)
665{
666    char stk[64], fmt[32], *ptr, *fract = NULL;
667    int positive = value >= 0.0;
668
669parse_double_again:
670    if (d >= 8) {
671	double dcheck;
672	int icheck, count;
673
674	/* this should to do the correct rounding */
675	for (count = 2;  count >= 0; count--) {
676	    icheck = d <= 0 ? 0 : d > FLOAT_PREC ? FLOAT_PREC - count : d - count;
677	    sprintf(fmt, "%%.%de", icheck);
678	    sprintf(stk, fmt, value);
679	    if (count) {
680		/* if the value read back is the same formatted */
681		sscanf(stk, "%lf", &dcheck);
682		if (dcheck == value)
683		    break;
684	    }
685	}
686    }
687    else {
688	sprintf(fmt, "%%.%de", d <= 0 ? 0 : d > FLOAT_PREC ? FLOAT_PREC : d);
689	sprintf(stk, fmt, value);
690    }
691
692    /* this "should" never fail */
693    ptr = strchr(stk, 'e');
694    if (ptr) {
695	*ptr++ = '\0';
696	*exponent = atoi(ptr);
697    }
698    else
699	*exponent = 0;
700
701    /* find start of number representation */
702    for (ptr = stk; *ptr && !isdigit(*ptr); ptr++)
703	;
704
705    /* check if did not trim any significant digit,
706     * this may happen because '%.e' puts only one digit before the '.' */
707    if (d > 0 && d < FLOAT_PREC && fabs(value) >= 10.0 &&
708	strlen(ptr) - 1 - !positive <= *exponent) {
709	d += *exponent - (strlen(ptr) - 1 - !positive) + 1;
710	goto parse_double_again;
711    }
712
713    /* this "should" never fail */
714    fract = strchr(ptr, '.');
715    if (fract)
716	*fract++ = '\0';
717
718    /* store number representation in buffer */
719    *buffer = positive ? '+' : '-';
720    strcpy(buffer + 1, ptr);
721    if (fract)
722	strcpy(buffer + strlen(buffer), fract);
723}
724
725static void
726format_integer(char *buffer, long value, int radix)
727{
728    if (radix == 10)
729	sprintf(buffer, "%ld", value);
730    else if (radix == 16)
731	sprintf(buffer, "%lx", value);
732    else if (radix == 8)
733	sprintf(buffer, "%lo", value);
734    else {
735	/* use bignum routine to convert number to string */
736	mpi integer;
737
738	mpi_init(&integer);
739	mpi_seti(&integer, value);
740	mpi_getstr(buffer, &integer, radix);
741	mpi_clear(&integer);
742    }
743}
744
745static int
746LispWriteCPointer(LispObj *stream, void *data)
747{
748    char stk[32];
749
750#ifdef LONG64
751    sprintf(stk, "0x%016lx", (long)data);
752#else
753    sprintf(stk, "0x%08lx", (long)data);
754#endif
755
756    return (LispWriteStr(stream, stk, strlen(stk)));
757}
758
759static int
760LispWriteCString(LispObj *stream, char *string, long length, write_info *info)
761{
762    int result;
763
764    if (!info->print_escape) {
765	char *base, *ptr, *end;
766
767	result = LispWriteChar(stream, '"');
768	for (base = ptr = string, end = string + length; ptr < end; ptr++) {
769	    if (*ptr == '\\' || *ptr == '"') {
770		result += LispWriteStr(stream, base, ptr - base);
771		result += LispWriteChar(stream, '\\');
772		result += LispWriteChar(stream, *ptr);
773		base = ptr + 1;
774	    }
775	}
776	result += LispWriteStr(stream, base, end - base);
777	result += LispWriteChar(stream, '"');
778    }
779    else
780	result = LispWriteStr(stream, string, length);
781
782    return (result);
783}
784
785static int
786LispWriteList(LispObj *stream, LispObj *object, write_info *info, int paren)
787{
788    int length = 0;
789    long circle = 0;
790
791    INCDEPTH();
792    if (info->print_level < 0 || info->level <= info->print_level) {
793	LispObj *car, *cdr;
794	long print_length = info->length;
795
796	if (info->circles && (circle = LispCheckCircle(object, info)) >= 0) {
797	    if (!paren) {
798		length += LispWriteStr(stream, ". ", 2);
799		paren = 1;
800	    }
801	    if (LispPrintCircle(stream, object, circle, &length, info) == 0) {
802		DECDEPTH();
803
804		return (length);
805	    }
806	}
807
808	car = CAR(object);
809	cdr = CDR(object);
810
811	if (cdr == NIL) {
812	    if (paren)
813		length += LispWriteChar(stream, '(');
814	    if (info->print_length < 0 || info->length < info->print_length) {
815		info->length = 0;
816		length += LispDoWriteObject(stream, car, info, 1);
817		info->length = print_length + 1;
818	    }
819	    else
820		length += LispWriteStr(stream, "...", 3);
821	    if (paren)
822		length += LispWriteChar(stream, ')');
823	}
824	else {
825	    if (paren)
826		length += LispWriteChar(stream, '(');
827	    if (info->print_length < 0 || info->length < info->print_length) {
828		info->length = 0;
829		length += LispDoWriteObject(stream, car, info, 1);
830		info->length = print_length + 1;
831		if (!CONSP(cdr)) {
832		    length += LispWriteStr(stream, " . ", 3);
833		    info->length = 0;
834		    length += LispDoWriteObject(stream, cdr, info, 0);
835		}
836		else {
837		    length += LispWriteChar(stream, ' ');
838		    if (info->print_length < 0 ||
839			info->length < info->print_length)
840			length += LispWriteList(stream, cdr, info, 0);
841		    else
842			length += LispWriteStr(stream, "...", 3);
843		}
844	    }
845	    else
846		length += LispWriteStr(stream, "...", 3);
847	    if (paren)
848		length += LispWriteChar(stream, ')');
849	}
850	info->length = print_length;
851    }
852    else
853	length += LispWriteChar(stream, '#');
854    DECDEPTH();
855
856    return (length);
857}
858
859static int
860LispDoWriteObject(LispObj *stream, LispObj *object, write_info *info, int paren)
861{
862    long print_level;
863    int length = 0;
864    char stk[64], *string = NULL;
865
866write_again:
867    switch (OBJECT_TYPE(object)) {
868	case LispNil_t:
869	    if (object == NIL)
870		string = Snil;
871	    else if (object == T)
872		string = St;
873	    else if (object == DOT)
874		string = "#<DOT>";
875	    else if (object == UNSPEC)
876		string = "#<UNSPEC>";
877	    else if (object == UNBOUND)
878		string = "#<UNBOUND>";
879	    else
880		string = "#<ERROR>";
881	    length += LispDoWriteAtom(stream, string, strlen(string),
882				      info->print_case);
883	    break;
884	case LispOpaque_t: {
885	    char *desc = LispIntToOpaqueType(object->data.opaque.type);
886
887	    length += LispWriteChar(stream, '#');
888	    length += LispWriteCPointer(stream, object->data.opaque.data);
889	    length += LispWriteStr(stream, desc, strlen(desc));
890	}   break;
891	case LispAtom_t:
892	    length += LispWriteAtom(stream, object, info);
893	    break;
894	case LispFunction_t:
895	    if (object->data.atom->a_function) {
896		object = object->data.atom->property->fun.function;
897		goto write_lambda;
898	    }
899	    length += LispWriteStr(stream, "#<", 2);
900	    if (object->data.atom->a_compiled)
901		LispDoWriteAtom(stream, "COMPILED", 8, info->print_case);
902	    else if (object->data.atom->a_builtin)
903		LispDoWriteAtom(stream, "BUILTIN", 7, info->print_case);
904	    /* XXX the function does not exist anymore */
905	    /* FIXME not sure if I want this fixed... */
906	    else
907		LispDoWriteAtom(stream, "UNBOUND", 7, info->print_case);
908	    LispDoWriteAtom(stream, "-FUNCTION", 9, info->print_case);
909	    length += LispWriteChar(stream, ' ');
910	    length += LispWriteAtom(stream, object->data.atom->object, info);
911	    length += LispWriteChar(stream, '>');
912	    break;
913	case LispString_t:
914	    length += LispWriteString(stream, object, info);
915	    break;
916	case LispSChar_t:
917	    length += LispWriteCharacter(stream, object, info);
918	    break;
919	case LispDFloat_t:
920	    length += LispWriteFloat(stream, object);
921	    break;
922	case LispFixnum_t:
923	case LispInteger_t:
924	case LispBignum_t:
925	    length += LispWriteInteger(stream, object);
926	    break;
927	case LispRatio_t:
928	    format_integer(stk, object->data.ratio.numerator, 10);
929	    length += LispWriteStr(stream, stk, strlen(stk));
930	    length += LispWriteChar(stream, '/');
931	    format_integer(stk, object->data.ratio.denominator, 10);
932	    length += LispWriteStr(stream, stk, strlen(stk));
933	    break;
934	case LispBigratio_t: {
935	    int sz;
936	    char *ptr;
937
938	    sz = mpi_getsize(mpr_num(object->data.mp.ratio), 10) + 1 +
939		 mpi_getsize(mpr_den(object->data.mp.ratio), 10) + 1 +
940		 (mpi_sgn(mpr_num(object->data.mp.ratio)) < 0);
941	    if (sz > sizeof(stk))
942		ptr = LispMalloc(sz);
943	    else
944		ptr = stk;
945	    mpr_getstr(ptr, object->data.mp.ratio, 10);
946	    length += LispWriteStr(stream, ptr, sz - 1);
947	    if (ptr != stk)
948		LispFree(ptr);
949	}   break;
950	case LispComplex_t:
951	    length += LispWriteStr(stream, "#C(", 3);
952	    length += LispDoWriteObject(stream,
953					object->data.complex.real, info, 0);
954	    length += LispWriteChar(stream, ' ');
955	    length += LispDoWriteObject(stream,
956					object->data.complex.imag, info, 0);
957	    length += LispWriteChar(stream, ')');
958	    break;
959	case LispCons_t:
960	    print_level = info->level;
961	    ++info->level;
962	    length += LispWriteList(stream, object, info, paren);
963	    info->level = print_level;
964	    break;
965	case LispQuote_t:
966	    length += LispWriteChar(stream, '\'');
967	    paren = 1;
968	    object = object->data.quote;
969	    goto write_again;
970	case LispBackquote_t:
971	    length += LispWriteChar(stream, '`');
972	    paren = 1;
973	    object = object->data.quote;
974	    goto write_again;
975	case LispComma_t:
976	    if (object->data.comma.atlist)
977		length += LispWriteStr(stream, ",@", 2);
978	    else
979		length += LispWriteChar(stream, ',');
980	    paren = 1;
981	    object = object->data.comma.eval;
982	    goto write_again;
983	    break;
984	case LispFunctionQuote_t:
985	    length += LispWriteStr(stream, "#'", 2);
986	    paren = 1;
987	    object = object->data.quote;
988	    goto write_again;
989	case LispArray_t:
990	    length += LispWriteArray(stream, object, info);
991	    break;
992	case LispStruct_t:
993	    length += LispWriteStruct(stream, object, info);
994	    break;
995	case LispLambda_t:
996	write_lambda:
997	    switch (object->funtype) {
998		case LispLambda:
999		    string = "#<LAMBDA ";
1000		    break;
1001		case LispFunction:
1002		    string = "#<FUNCTION ";
1003		    break;
1004		case LispMacro:
1005		    string = "#<MACRO ";
1006		    break;
1007		case LispSetf:
1008		    string = "#<SETF ";
1009		    break;
1010	    }
1011	    length += LispDoWriteAtom(stream, string, strlen(string),
1012				      info->print_case);
1013	    if (object->funtype != LispLambda) {
1014		length += LispWriteAtom(stream, object->data.lambda.name, info);
1015		length += LispWriteChar(stream, ' ');
1016		length += LispWriteAlist(stream, object->data.lambda.name
1017					 ->data.atom->property->alist, info);
1018	    }
1019	    else {
1020		length += LispDoWriteAtom(stream, Snil, 3, info->print_case);
1021		length += LispWriteChar(stream, ' ');
1022		length += LispWriteAlist(stream, (LispArgList*)object->
1023					 data.lambda.name->data.opaque.data,
1024					 info);
1025	    }
1026	    length += LispWriteChar(stream, ' ');
1027	    length += LispDoWriteObject(stream,
1028					object->data.lambda.code, info, 0);
1029	    length += LispWriteChar(stream, '>');
1030	    break;
1031	case LispStream_t:
1032	    length += LispWriteStr(stream, "#<", 2);
1033	    if (object->data.stream.type == LispStreamFile)
1034		string = "FILE-STREAM ";
1035	    else if (object->data.stream.type == LispStreamString)
1036		string = "STRING-STREAM ";
1037	    else if (object->data.stream.type == LispStreamStandard)
1038		string = "STANDARD-STREAM ";
1039	    else if (object->data.stream.type == LispStreamPipe)
1040		string = "PIPE-STREAM ";
1041	    length += LispDoWriteAtom(stream, string, strlen(string),
1042				      info->print_case);
1043
1044	    if (!object->data.stream.readable && !object->data.stream.writable)
1045		length += LispDoWriteAtom(stream, "CLOSED",
1046					  6, info->print_case);
1047	    else {
1048		if (object->data.stream.readable)
1049		    length += LispDoWriteAtom(stream, "READ",
1050					      4, info->print_case);
1051		if (object->data.stream.writable) {
1052		    if (object->data.stream.readable)
1053			length += LispWriteChar(stream, '-');
1054		    length += LispDoWriteAtom(stream, "WRITE",
1055					      5, info->print_case);
1056		}
1057	    }
1058	    if (object->data.stream.type != LispStreamString) {
1059		length += LispWriteChar(stream, ' ');
1060		length += LispDoWriteObject(stream,
1061					    object->data.stream.pathname,
1062					    info, 1);
1063		/* same address/size for pipes */
1064		length += LispWriteChar(stream, ' ');
1065		length += LispWriteCPointer(stream,
1066					    object->data.stream.source.file);
1067		if (object->data.stream.readable &&
1068		    object->data.stream.type == LispStreamFile &&
1069		    !object->data.stream.source.file->binary) {
1070		    length += LispWriteStr(stream, " @", 2);
1071		    format_integer(stk, object->data.stream.source.file->line, 10);
1072		    length += LispWriteStr(stream, stk, strlen(stk));
1073		}
1074	    }
1075	    length += LispWriteChar(stream, '>');
1076	    break;
1077	case LispPathname_t:
1078	    length += LispWriteStr(stream, "#P", 2);
1079	    paren = 1;
1080	    object = CAR(object->data.quote);
1081	    goto write_again;
1082	case LispPackage_t:
1083	    length += LispDoWriteAtom(stream, "#<PACKAGE ",
1084				      10, info->print_case);
1085	    length += LispWriteStr(stream,
1086				   THESTR(object->data.package.name),
1087				   STRLEN(object->data.package.name));
1088	    length += LispWriteChar(stream, '>');
1089	    break;
1090	case LispRegex_t:
1091	    length += LispDoWriteAtom(stream, "#<REGEX ",
1092				      8, info->print_case);
1093	    length += LispDoWriteObject(stream,
1094					object->data.regex.pattern, info, 1);
1095	    if (object->data.regex.options & RE_NOSPEC)
1096		length += LispDoWriteAtom(stream, " :NOSPEC",
1097					  8, info->print_case);
1098	    if (object->data.regex.options & RE_ICASE)
1099		length += LispDoWriteAtom(stream, " :ICASE",
1100					  7, info->print_case);
1101	    if (object->data.regex.options & RE_NOSUB)
1102		length += LispDoWriteAtom(stream, " :NOSUB",
1103					  7, info->print_case);
1104	    if (object->data.regex.options & RE_NEWLINE)
1105		length += LispDoWriteAtom(stream, " :NEWLINE",
1106					  9, info->print_case);
1107	    length += LispWriteChar(stream, '>');
1108	    break;
1109	case LispBytecode_t:
1110	    length += LispDoWriteAtom(stream, "#<BYTECODE ",
1111				      11, info->print_case);
1112	    length += LispWriteCPointer(stream,
1113					object->data.bytecode.bytecode);
1114	    length += LispWriteChar(stream, '>');
1115	    break;
1116	case LispHashTable_t:
1117	    length += LispDoWriteAtom(stream, "#<HASH-TABLE ",
1118				      13, info->print_case);
1119	    length += LispWriteAtom(stream, object->data.hash.test, info);
1120	    snprintf(stk, sizeof(stk), " %g %g",
1121		     object->data.hash.table->rehash_size,
1122		     object->data.hash.table->rehash_threshold);
1123	    length += LispWriteStr(stream, stk, strlen(stk));
1124	    snprintf(stk, sizeof(stk), " %ld/%ld>",
1125		     object->data.hash.table->count,
1126		     object->data.hash.table->num_entries);
1127	    length += LispWriteStr(stream, stk, strlen(stk));
1128	    break;
1129    }
1130
1131    return (length);
1132}
1133
1134/* return current column number in stream */
1135int
1136LispGetColumn(LispObj *stream)
1137{
1138    LispFile *file;
1139    LispString *string;
1140
1141    check_stream(stream, &file, &string, 0);
1142    if (file != NULL)
1143	return (file->column);
1144    return (string->column);
1145}
1146
1147/* write a character to stream */
1148int
1149LispWriteChar(LispObj *stream, int character)
1150{
1151    LispFile *file;
1152    LispString *string;
1153
1154    check_stream(stream, &file, &string, 1);
1155    if (file != NULL)
1156	return (LispFputc(file, character));
1157
1158    return (LispSputc(string, character));
1159}
1160
1161/* write a character count times to stream */
1162int
1163LispWriteChars(LispObj *stream, int character, int count)
1164{
1165    int length = 0;
1166
1167    if (count > 0) {
1168	char stk[64];
1169	LispFile *file;
1170	LispString *string;
1171
1172	check_stream(stream, &file, &string, 1);
1173	if (count >= sizeof(stk)) {
1174	    memset(stk, character, sizeof(stk));
1175	    for (; count >= sizeof(stk); count -= sizeof(stk)) {
1176		if (file != NULL)
1177		    length += LispFwrite(file, stk, sizeof(stk));
1178		else
1179		    length += LispSwrite(string, stk, sizeof(stk));
1180	    }
1181	}
1182	else
1183	    memset(stk, character, count);
1184
1185	if (count) {
1186	    if (file != NULL)
1187		length += LispFwrite(file, stk, count);
1188	    else
1189		length += LispSwrite(string, stk, count);
1190	}
1191    }
1192
1193    return (length);
1194}
1195
1196/* write a string to stream */
1197int
1198LispWriteStr(LispObj *stream, char *buffer, long length)
1199{
1200    LispFile *file;
1201    LispString *string;
1202
1203    check_stream(stream, &file, &string, 1);
1204    if (file != NULL)
1205	return (LispFwrite(file, buffer, length));
1206    return (LispSwrite(string, buffer, length));
1207}
1208
1209static int
1210LispDoWriteAtom(LispObj *stream, char *string, int length, int print_case)
1211{
1212    int bytes = 0, cap = 0;
1213    char buffer[128], *ptr;
1214
1215    switch (print_case) {
1216	case DOWNCASE:
1217	    for (ptr = buffer; length > 0; length--, string++) {
1218		if (isupper(*string))
1219		    *ptr = tolower(*string);
1220		else
1221		    *ptr = *string;
1222		++ptr;
1223		if (ptr - buffer >= sizeof(buffer)) {
1224		    bytes += LispWriteStr(stream, buffer, ptr - buffer);
1225		    ptr = buffer;
1226		}
1227	    }
1228	    if (ptr > buffer)
1229		bytes += LispWriteStr(stream, buffer, ptr - buffer);
1230	    break;
1231	case CAPITALIZE:
1232	    for (ptr = buffer; length > 0; length--, string++) {
1233		if (isalnum(*string)) {
1234		    if (cap && isupper(*string))
1235			*ptr = tolower(*string);
1236		    else
1237			*ptr = *string;
1238		    cap = 1;
1239		}
1240		else {
1241		    *ptr = *string;
1242		    cap = 0;
1243		}
1244		++ptr;
1245		if (ptr - buffer >= sizeof(buffer)) {
1246		    bytes += LispWriteStr(stream, buffer, ptr - buffer);
1247		    ptr = buffer;
1248		}
1249	    }
1250	    if (ptr > buffer)
1251		bytes += LispWriteStr(stream, buffer, ptr - buffer);
1252	    break;
1253	default:
1254	    /* Strings are already stored upcase/quoted */
1255	    bytes += LispWriteStr(stream, string, length);
1256	    break;
1257    }
1258
1259    return (bytes);
1260}
1261
1262static int
1263LispWriteAtom(LispObj *stream, LispObj *object, write_info *info)
1264{
1265    int length = 0;
1266    LispAtom *atom = object->data.atom;
1267    Atom_id id = atom->string;
1268
1269    if (atom->package != PACKAGE) {
1270	if (atom->package == lisp__data.keyword)
1271	    length += LispWriteChar(stream, ':');
1272	else if (atom->package == NULL)
1273	    length += LispWriteStr(stream, "#:", 2);
1274	else {
1275	    /* Check if the symbol is visible */
1276	    int i, visible = 0;
1277
1278	    if (atom->ext) {
1279		for (i = lisp__data.pack->use.length - 1; i >= 0; i--) {
1280		    if (lisp__data.pack->use.pairs[i] == atom->package) {
1281			visible = 1;
1282			break;
1283		    }
1284		}
1285	    }
1286
1287	    if (!visible) {
1288		/* XXX this assumes that package names are always "readable" */
1289		length +=
1290		    LispDoWriteAtom(stream,
1291				    THESTR(atom->package->data.package.name),
1292				    STRLEN(atom->package->data.package.name),
1293				    info->print_case);
1294		length += LispWriteChar(stream, ':');
1295		if (!atom->ext)
1296		    length += LispWriteChar(stream, ':');
1297	    }
1298	}
1299    }
1300    if (atom->unreadable)
1301	length += LispWriteChar(stream, '|');
1302    length += LispDoWriteAtom(stream, id, strlen(id),
1303			      atom->unreadable ? UPCASE : info->print_case);
1304    if (atom->unreadable)
1305	length += LispWriteChar(stream, '|');
1306
1307    return (length);
1308}
1309
1310static int
1311LispWriteInteger(LispObj *stream, LispObj *object)
1312{
1313    return (LispFormatInteger(stream, object, 10, 0, 0, 0, 0, 0, 0));
1314}
1315
1316static int
1317LispWriteCharacter(LispObj *stream, LispObj *object, write_info *info)
1318{
1319    return (LispFormatCharacter(stream, object, !info->print_escape, 0));
1320}
1321
1322static int
1323LispWriteString(LispObj *stream, LispObj *object, write_info *info)
1324{
1325    return (LispWriteCString(stream, THESTR(object), STRLEN(object), info));
1326}
1327
1328static int
1329LispWriteFloat(LispObj *stream, LispObj *object)
1330{
1331    double value = DFLOAT_VALUE(object);
1332
1333    if (value == 0.0 || (fabs(value) < 1.0E7 && fabs(value) > 1.0E-4))
1334	return (LispFormatFixedFloat(stream, object, 0, 0, NULL, 0, 0, 0));
1335
1336    return (LispDoFormatExponentialFloat(stream, object, 0, 0, NULL,
1337					 0, 1, 0, ' ', 'E', 0));
1338}
1339
1340static int
1341LispWriteArray(LispObj *stream, LispObj *object, write_info *info)
1342{
1343    int length = 0;
1344    long print_level = info->level, circle;
1345
1346    if (info->circles && (circle = LispCheckCircle(object, info)) >= 0 &&
1347	LispPrintCircle(stream, object, circle, &length, info) == 0)
1348	return (length);
1349
1350    if (object->data.array.rank == 0) {
1351	length += LispWriteStr(stream, "#0A", 3);
1352	length += LispDoWriteObject(stream, object->data.array.list, info, 1);
1353	return (length);
1354    }
1355
1356    INCDEPTH();
1357    ++info->level;
1358    if (info->print_level < 0 || info->level <= info->print_level) {
1359	if (object->data.array.rank == 1)
1360	    length += LispWriteStr(stream, "#(", 2);
1361	else {
1362	    char stk[32];
1363
1364	    format_integer(stk, object->data.array.rank, 10);
1365	    length += LispWriteChar(stream, '#');
1366	    length += LispWriteStr(stream, stk, strlen(stk));
1367	    length += LispWriteStr(stream, "A(", 2);
1368	}
1369
1370	if (!object->data.array.zero) {
1371	    long print_length = info->length, local_length = 0;
1372
1373	    if (object->data.array.rank == 1) {
1374		LispObj *ary;
1375		long count;
1376
1377		for (ary = object->data.array.dim, count = 1;
1378		     ary != NIL; ary = CDR(ary))
1379		    count *= FIXNUM_VALUE(CAR(ary));
1380		for (ary = object->data.array.list; count > 0;
1381		     ary = CDR(ary), count--) {
1382		    if (info->print_length < 0 ||
1383			++local_length <= info->print_length) {
1384			info->length = 0;
1385			length += LispDoWriteObject(stream, CAR(ary), info, 1);
1386		    }
1387		    else {
1388			length += LispWriteStr(stream, "...", 3);
1389			break;
1390		    }
1391		    if (count - 1 > 0)
1392			length += LispWriteChar(stream, ' ');
1393		}
1394	    }
1395	    else {
1396		LispObj *ary;
1397		int i, k, rank, *dims, *loop;
1398
1399		rank = object->data.array.rank;
1400		dims = LispMalloc(sizeof(int) * rank);
1401		loop = LispCalloc(1, sizeof(int) * (rank - 1));
1402
1403		/* fill dim */
1404		for (i = 0, ary = object->data.array.dim; ary != NIL;
1405		     i++, ary = CDR(ary))
1406		    dims[i] = FIXNUM_VALUE(CAR(ary));
1407
1408		i = 0;
1409		ary = object->data.array.list;
1410		while (loop[0] < dims[0]) {
1411		    if (info->print_length < 0 ||
1412			local_length < info->print_length) {
1413			for (; i < rank - 1; i++)
1414			    length += LispWriteChar(stream, '(');
1415			--i;
1416			for (;;) {
1417			    ++loop[i];
1418			    if (i && loop[i] >= dims[i])
1419				loop[i] = 0;
1420			    else
1421				break;
1422			    --i;
1423			}
1424			for (k = 0; k < dims[rank - 1] - 1;
1425			     k++, ary = CDR(ary)) {
1426			    if (info->print_length < 0 ||
1427				k < info->print_length) {
1428				++local_length;
1429				info->length = 0;
1430				length += LispDoWriteObject(stream,
1431							    CAR(ary), info, 1);
1432				length += LispWriteChar(stream, ' ');
1433			    }
1434			}
1435			if (info->print_length < 0 || k < info->print_length) {
1436			    ++local_length;
1437			    info->length = 0;
1438			    length += LispDoWriteObject(stream,
1439							CAR(ary), info, 0);
1440			}
1441			else
1442			    length += LispWriteStr(stream,  "...", 3);
1443			for (k = rank - 1; k > i; k--)
1444			    length += LispWriteChar(stream, ')');
1445			if (loop[0] < dims[0])
1446			    length += LispWriteChar(stream,  ' ');
1447			ary = CDR(ary);
1448		    }
1449		    else {
1450			++local_length;
1451			length += LispWriteStr(stream,	"...)", 4);
1452			for (; local_length < dims[0] - 1; local_length++)
1453			    length += LispWriteStr(stream,  " ...)", 5);
1454			if (local_length <= dims[0])
1455			    length += LispWriteStr(stream,  " ...", 4);
1456			break;
1457		    }
1458		}
1459		LispFree(dims);
1460		LispFree(loop);
1461	    }
1462	    info->length = print_length;
1463	}
1464	length += LispWriteChar(stream, ')');
1465    }
1466    else
1467	length += LispWriteChar(stream, '#');
1468    info->level = print_level;
1469    DECDEPTH();
1470
1471    return (length);
1472}
1473
1474static int
1475LispWriteStruct(LispObj *stream, LispObj *object, write_info *info)
1476{
1477    int length;
1478    long circle;
1479    LispObj *symbol;
1480    LispObj *def = object->data.struc.def;
1481    LispObj *field = object->data.struc.fields;
1482
1483    if (info->circles && (circle = LispCheckCircle(object, info)) >= 0 &&
1484	LispPrintCircle(stream, object, circle, &length, info) == 0)
1485	return (length);
1486
1487    INCDEPTH();
1488    length = LispWriteStr(stream, "#S(", 3);
1489    symbol = SYMBOLP(CAR(def)) ? CAR(def) : CAAR(def);
1490    length += LispWriteAtom(stream, symbol, info);
1491    def = CDR(def);
1492    for (; def != NIL; def = CDR(def), field = CDR(field)) {
1493	length += LispWriteChar(stream, ' ');
1494	symbol = SYMBOLP(CAR(def)) ? CAR(def) : CAAR(def);
1495	length += LispWriteAtom(stream, symbol, info);
1496	length += LispWriteChar(stream, ' ');
1497	length += LispDoWriteObject(stream, CAR(field), info, 1);
1498    }
1499    length += LispWriteChar(stream, ')');
1500    DECDEPTH();
1501
1502    return (length);
1503}
1504
1505int
1506LispFormatInteger(LispObj *stream, LispObj *object, int radix,
1507		  int atsign, int collon, int mincol,
1508		  int padchar, int commachar, int commainterval)
1509{
1510    char stk[128], *str = stk;
1511    int i, length, sign, intervals;
1512
1513    if (LONGINTP(object))
1514	format_integer(stk, LONGINT_VALUE(object), radix);
1515    else {
1516	if (mpi_getsize(object->data.mp.integer, radix) >= sizeof(stk))
1517	    str = mpi_getstr(NULL, object->data.mp.integer, radix);
1518	else
1519	    mpi_getstr(str, object->data.mp.integer, radix);
1520    }
1521
1522    sign = *str == '-';
1523    length = strlen(str);
1524
1525    /* if collon, update length for the number of commachars to be printed */
1526    if (collon && commainterval > 0 && commachar) {
1527	intervals = length / commainterval;
1528	length += intervals;
1529    }
1530    else
1531	intervals = 0;
1532
1533    /* if sign must be printed, and number is positive */
1534    if (atsign && !sign)
1535	++length;
1536
1537    /* if need padding */
1538    if (padchar && mincol > length)
1539	LispWriteChars(stream, padchar, mincol - length);
1540
1541    /* if need to print number sign */
1542    if (sign || atsign)
1543	LispWriteChar(stream, sign ? '-' : '+');
1544
1545    /* if need to print commas to separate groups of numbers */
1546    if (intervals) {
1547	int j;
1548	char *ptr;
1549
1550	i = (length - atsign) - intervals;
1551	j = i % commainterval;
1552	/* make the loop below easier */
1553	if (j == 0)
1554	    j = commainterval;
1555	i -= j;
1556	ptr = str + sign;
1557	for (; j > 0; j--, ptr++)
1558	    LispWriteChar(stream, *ptr);
1559	for (; i > 0; i -= commainterval) {
1560	    LispWriteChar(stream, commachar);
1561	    for (j = 0; j < commainterval; j++, ptr++)
1562		LispWriteChar(stream, *ptr);
1563	}
1564    }
1565    /* else, just print the string */
1566    else
1567	LispWriteStr(stream, str + sign, length - sign);
1568
1569    /* if number required more than sizeof(stk) bytes */
1570    if (str != stk)
1571	LispFree(str);
1572
1573    return (length);
1574}
1575
1576int
1577LispFormatRomanInteger(LispObj *stream, long value, int new_roman)
1578{
1579    char stk[32];
1580    int length;
1581
1582    length = 0;
1583    while (value > 1000) {
1584	stk[length++] = 'M';
1585	value -= 1000;
1586    }
1587    if (new_roman) {
1588	if (value >= 900) {
1589	    strcpy(stk + length, "CM");
1590	    length += 2,
1591	    value -= 900;
1592	}
1593	else if (value < 500 && value >= 400) {
1594	    strcpy(stk + length, "CD");
1595	    length += 2;
1596	    value -= 400;
1597	}
1598    }
1599    if (value >= 500) {
1600	stk[length++] = 'D';
1601	value -= 500;
1602    }
1603    while (value >= 100) {
1604	stk[length++] = 'C';
1605	value -= 100;
1606    }
1607    if (new_roman) {
1608	if (value >= 90) {
1609	    strcpy(stk + length, "XC");
1610	    length += 2,
1611	    value -= 90;
1612	}
1613	else if (value < 50 && value >= 40) {
1614	    strcpy(stk + length, "XL");
1615	    length += 2;
1616	    value -= 40;
1617	}
1618    }
1619    if (value >= 50) {
1620	stk[length++] = 'L';
1621	value -= 50;
1622    }
1623    while (value >= 10) {
1624	stk[length++] = 'X';
1625	value -= 10;
1626    }
1627    if (new_roman) {
1628	if (value == 9) {
1629	    strcpy(stk + length, "IX");
1630	    length += 2,
1631	    value -= 9;
1632	}
1633	else if (value == 4) {
1634	    strcpy(stk + length, "IV");
1635	    length += 2;
1636	    value -= 4;
1637	}
1638    }
1639    if (value >= 5) {
1640	stk[length++] = 'V';
1641	value -= 5;
1642    }
1643    while (value) {
1644	stk[length++] = 'I';
1645	--value;
1646    }
1647
1648    stk[length] = '\0';
1649
1650    return (LispWriteStr(stream, stk, length));
1651}
1652
1653int
1654LispFormatEnglishInteger(LispObj *stream, long number, int ordinal)
1655{
1656    static char *ds[] = {
1657	"",	      "one",	   "two",	 "three",      "four",
1658	"five",       "six",	   "seven",	 "eight",      "nine",
1659	"ten",	      "eleven",    "twelve",	 "thirteen",   "fourteen",
1660	"fifteen",    "sixteen",   "seventeen",  "eighteen",   "nineteen"
1661    };
1662    static char *dsth[] = {
1663	"",	      "first",	   "second",	  "third",	"fourth",
1664	"fifth",      "sixth",	   "seventh",	  "eighth",	"ninth",
1665	"tenth",      "eleventh",  "twelfth",	  "thirteenth", "fourteenth",
1666	 "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth"
1667    };
1668    static char *hs[] = {
1669	"",	      "",	   "twenty",	  "thirty",	"forty",
1670	"fifty",      "sixty",	   "seventy",	  "eighty",	"ninety"
1671    };
1672    static char *hsth[] = {
1673	"",	      "",	   "twentieth",   "thirtieth",	"fortieth",
1674       "fiftieth",    "sixtieth",  "seventieth",  "eightieth",	"ninetieth"
1675    };
1676    static char *ts[] = {
1677	"",	      "thousand",   "million"
1678    };
1679    static char *tsth[] = {
1680	"",	     "thousandth", "millionth"
1681    };
1682    char stk[256];
1683    int length, sign;
1684
1685    sign = number < 0;
1686    if (sign)
1687	number = -number;
1688    length = 0;
1689
1690#define SIGNLEN		6	/* strlen("minus ") */
1691    if (sign) {
1692	strcpy(stk, "minus ");
1693	length += SIGNLEN;
1694    }
1695    else if (number == 0) {
1696	if (ordinal) {
1697	    strcpy(stk, "zeroth");
1698	    length += 6;	/* strlen("zeroth") */
1699	}
1700	else {
1701	    strcpy(stk, "zero");
1702	    length += 4;	/* strlen("zero") */
1703	}
1704    }
1705    for (;;) {
1706	int count, temp;
1707	char *t, *h, *d;
1708	long value = number;
1709
1710	for (count = 0; value >= 1000; value /= 1000, count++)
1711	    ;
1712
1713	t = ds[value / 100];
1714	if (ordinal && !count && (value % 10) == 0)
1715	    h = hsth[(value % 100) / 10];
1716	else
1717	    h = hs[(value % 100) / 10];
1718
1719	if (ordinal && !count)
1720	    d = *h ? dsth[value % 10] : dsth[value % 20];
1721	else
1722	    d = *h ? ds[value % 10] : ds[value % 20];
1723
1724	if (((!sign && length) || length > SIGNLEN) && (*t || *h || *d)) {
1725	    if (!ordinal || count || *h || *t) {
1726		strcpy(stk + length, ", ");
1727		length += 2;
1728	    }
1729	    else {
1730		strcpy(stk + length, " ");
1731		++length;
1732	    }
1733	}
1734
1735	if (*t) {
1736	    if (ordinal && !count && (value % 100) == 0)
1737		temp = sprintf(stk + length, "%s hundredth", t);
1738	    else
1739		temp = sprintf(stk + length, "%s hundred", t);
1740	    length += temp;
1741	}
1742
1743	if (*h) {
1744	    if (*t) {
1745		if (ordinal && !count) {
1746		    strcpy(stk + length, " ");
1747		    ++length;
1748		}
1749		else {
1750		    strcpy(stk + length, " and ");
1751		    length += 5;	/* strlen(" and ") */
1752		}
1753	    }
1754	    strcpy(stk + length, h);
1755	    length += strlen(h);
1756	}
1757
1758	if (*d) {
1759	    if (*h) {
1760		strcpy(stk + length, "-");
1761		++length;
1762	    }
1763	    else if (*t) {
1764		if (ordinal && !count) {
1765		    strcpy(stk + length, " ");
1766		    ++length;
1767		}
1768		else {
1769		    strcpy(stk + length, " and ");
1770		    length += 5;	/* strlen(" and ") */
1771		}
1772	    }
1773	    strcpy(stk + length, d);
1774	    length += strlen(d);
1775	}
1776
1777	if (!count)
1778	    break;
1779	else
1780	    temp = count;
1781
1782	if (count > 1) {
1783	    value *= 1000;
1784	    while (--count)
1785		value *= 1000;
1786	    number -= value;
1787	}
1788	else
1789	    number %= 1000;
1790
1791	if (ordinal && number == 0 && !*t && !*h)
1792	    temp = sprintf(stk + length, " %s", tsth[temp]);
1793	else
1794	    temp = sprintf(stk + length, " %s", ts[temp]);
1795	length += temp;
1796
1797	if (!number)
1798	    break;
1799    }
1800
1801    return (LispWriteStr(stream, stk, length));
1802}
1803
1804int
1805LispFormatCharacter(LispObj *stream, LispObj *object,
1806		    int atsign, int collon)
1807{
1808    int length = 0;
1809    int ch = SCHAR_VALUE(object);
1810
1811    if (atsign && !collon)
1812	length += LispWriteStr(stream, "#\\", 2);
1813    if ((atsign || collon) && (ch <= ' ' || ch == 0177)) {
1814	char *name = LispChars[ch].names[0];
1815
1816	length += LispWriteStr(stream, name, strlen(name));
1817    }
1818    else
1819	length += LispWriteChar(stream, ch);
1820
1821    return (length);
1822}
1823
1824/* returns 1 if string size must grow, done inplace */
1825static int
1826float_string_inc(char *buffer, int offset)
1827{
1828    int i;
1829
1830    for (i = offset; i >= 0; i--) {
1831	if (buffer[i] == '9')
1832	    buffer[i] = '0';
1833	else if (buffer[i] != '.') {
1834	    ++buffer[i];
1835	    break;
1836	}
1837    }
1838    if (i < 0) {
1839	int length = strlen(buffer);
1840
1841	/* string size must change */
1842	memmove(buffer + 1, buffer, length + 1);
1843	buffer[0] = '1';
1844
1845	return (1);
1846    }
1847
1848    return (0);
1849}
1850
1851int
1852LispFormatFixedFloat(LispObj *stream, LispObj *object,
1853		     int atsign, int w, int *pd, int k, int overflowchar,
1854		     int padchar)
1855{
1856    char buffer[512], stk[64];
1857    int sign, exponent, length, offset, d = pd ? *pd : FLOAT_PREC, again;
1858    double value = DFLOAT_VALUE(object);
1859
1860    if (value == 0.0) {
1861	exponent = k = 0;
1862	strcpy(stk, "+0");
1863    }
1864    else
1865	/* calculate format parameters, adjusting scale factor */
1866	parse_double(stk, &exponent, value, d + 1 + k);
1867
1868    /* make sure k won't cause overflow */
1869    if (k > 128)
1870	k = 128;
1871    else if (k < -128)
1872	k = -128;
1873
1874    /* make sure d won't cause overflow */
1875    if (d > 128)
1876	d = 128;
1877    else if (d < -128)
1878	d = -128;
1879
1880    /* adjust scale factor, exponent is used as an index in stk */
1881    exponent += k + 1;
1882
1883    /* how many bytes in float representation */
1884    length = strlen(stk) - 1;
1885
1886    /* need to print a sign? */
1887    sign = atsign || (stk[0] == '-');
1888
1889    /* format number, cannot overflow, as control variables were checked */
1890    offset = 0;
1891    if (sign)
1892	buffer[offset++] = stk[0];
1893    if (exponent > 0) {
1894	if (exponent > length) {
1895	    memcpy(buffer + offset, stk + 1, length);
1896	    memset(buffer + offset + length, '0', exponent - length);
1897	}
1898	else
1899	    memcpy(buffer + offset, stk + 1, exponent);
1900	offset += exponent;
1901	buffer[offset++] = '.';
1902	if (length > exponent) {
1903	    memcpy(buffer + offset, stk + 1 + exponent, length - exponent);
1904	    offset += length - exponent;
1905	}
1906	else
1907	    buffer[offset++] = '0';
1908    }
1909    else {
1910	buffer[offset++] = '0';
1911	buffer[offset++] = '.';
1912	while (exponent < 0) {
1913	    buffer[offset++] = '0';
1914	    exponent++;
1915	}
1916	memcpy(buffer + offset, stk + 1, length);
1917	offset += length;
1918    }
1919    buffer[offset] = '\0';
1920
1921    again = 0;
1922fixed_float_check_again:
1923    /* make sure only d digits are printed after decimal point */
1924    if (d > 0) {
1925	char *dptr = strchr(buffer, '.');
1926
1927	length = strlen(dptr) - 1;
1928	/* check if need to remove excess digits */
1929	if (length > d) {
1930	    int digit;
1931
1932	    offset = (dptr - buffer) + 1 + d;
1933	    digit = buffer[offset];
1934
1935	    /* remove extra digits */
1936	    buffer[offset] = '\0';
1937
1938	    /* check if need to round */
1939	    if (!again && offset > 1 && isdigit(digit) && digit >= '5' &&
1940		isdigit(buffer[offset - 1]) &&
1941		float_string_inc(buffer, offset - 1))
1942		++offset;
1943	}
1944	/* check if need to add extra zero digits to fill space */
1945	else if (length < d) {
1946	    offset += d - length;
1947	    for (++length; length <= d; length++)
1948		dptr[length] = '0';
1949	    dptr[length] = '\0';
1950	}
1951    }
1952    else {
1953	/* no digits after decimal point */
1954	int digit, inc = 0;
1955	char *dptr = strchr(buffer, '.') + 1;
1956
1957	digit = *dptr;
1958	if (!again && digit >= '5' && dptr >= buffer + 2 && isdigit(dptr[-2]))
1959	    inc = float_string_inc(buffer, dptr - buffer - 2);
1960
1961	offset = (dptr - buffer) + inc;
1962	buffer[offset] = '\0';
1963    }
1964
1965    /* if d was not specified, remove any extra zeros */
1966    if (pd == NULL) {
1967	while (offset > 2 && buffer[offset - 2] != '.' &&
1968	       buffer[offset - 1] == '0')
1969	    --offset;
1970	buffer[offset] = '\0';
1971    }
1972
1973    if (w > 0 && offset > w) {
1974	/* first check if can remove extra fractional digits */
1975	if (pd == NULL) {
1976	    char *ptr = strchr(buffer, '.') + 1;
1977
1978	    if (ptr - buffer < w) {
1979		d = w - (ptr - buffer);
1980		goto fixed_float_check_again;
1981	    }
1982	}
1983
1984	/* remove leading "zero" to save space */
1985 	if ((!sign && buffer[0] == '0') || (sign && buffer[1] == '0')) {
1986	    /* ending nul also copied */
1987	    memmove(buffer + sign, buffer + sign + 1, offset);
1988	    --offset;
1989	}
1990	/* remove leading '+' to "save" space */
1991	if (offset > w && buffer[0] == '+') {
1992	    /* ending nul also copied */
1993	    memmove(buffer, buffer + 1, offset);
1994	    --offset;
1995	}
1996    }
1997
1998    /* if cannot represent number in given width */
1999    if (overflowchar && offset > w) {
2000	again = 1;
2001	goto fixed_float_overflow;
2002    }
2003
2004    length = 0;
2005    /* print padding if required */
2006    if (w > offset)
2007	length += LispWriteChars(stream, padchar, w - offset);
2008
2009    /* print float number representation */
2010    return (LispWriteStr(stream, buffer, offset) + length);
2011
2012fixed_float_overflow:
2013    return (LispWriteChars(stream, overflowchar, w));
2014}
2015
2016int
2017LispFormatExponentialFloat(LispObj *stream, LispObj *object,
2018			   int atsign, int w, int *pd, int e, int k,
2019			   int overflowchar, int padchar, int exponentchar)
2020{
2021    return (LispDoFormatExponentialFloat(stream, object, atsign, w,
2022					 pd, e, k, overflowchar, padchar,
2023					 exponentchar, 1));
2024}
2025
2026int
2027LispDoFormatExponentialFloat(LispObj *stream, LispObj *object,
2028			     int atsign, int w, int *pd, int e, int k,
2029			     int overflowchar, int padchar, int exponentchar,
2030			     int format)
2031{
2032    char buffer[512], stk[64];
2033    int sign, exponent, length, offset, d = pd ? *pd : FLOAT_PREC;
2034    double value = DFLOAT_VALUE(object);
2035
2036    if (value == 0.0) {
2037	exponent = 0;
2038	k = 1;
2039	strcpy(stk, "+0");
2040    }
2041    else
2042	/* calculate format parameters, adjusting scale factor */
2043	parse_double(stk, &exponent, value, d + k - 1);
2044
2045    /* set e to a value that won't overflow */
2046    if (e > 16)
2047	e = 16;
2048
2049    /* set k to a value that won't overflow */
2050    if (k > 128)
2051	k = 128;
2052    else if (k < -128)
2053	k = -128;
2054
2055    /* set d to a value that won't overflow */
2056    if (d > 128)
2057	d = 128;
2058    else if (d < -128)
2059	d = -128;
2060
2061    /* how many bytes in float representation */
2062    length = strlen(stk) - 1;
2063
2064    /* need to print a sign? */
2065    sign = atsign || (stk[0] == '-');
2066
2067    /* adjust number of digits after decimal point */
2068    if (k > 0)
2069	d -= k - 1;
2070
2071    /* adjust exponent, based on scale factor */
2072    exponent -= k - 1;
2073
2074    /* format number, cannot overflow, as control variables were checked */
2075    offset = 0;
2076    if (sign)
2077	buffer[offset++] = stk[0];
2078    if (k > 0) {
2079	if (k > length) {
2080	    memcpy(buffer + offset, stk + 1, length);
2081	    offset += length;
2082	}
2083	else {
2084	    memcpy(buffer + offset, stk + 1, k);
2085	    offset += k;
2086	}
2087	buffer[offset++] = '.';
2088	if (length > k) {
2089	    memcpy(buffer + offset, stk + 1 + k, length - k);
2090	    offset += length - k;
2091	}
2092 	else
2093	    buffer[offset++] = '0';
2094    }
2095    else {
2096	int tmp = k;
2097
2098	buffer[offset++] = '0';
2099	buffer[offset++] = '.';
2100	while (tmp < 0) {
2101	    buffer[offset++] = '0';
2102	    tmp++;
2103	}
2104	memcpy(buffer + offset, stk + 1, length);
2105	offset += length;
2106    }
2107
2108    /* if format, then always add a sign to exponent */
2109    buffer[offset++] = exponentchar;
2110    if (format || exponent < 0)
2111	buffer[offset++] = exponent < 0 ? '-' : '+';
2112
2113    /* XXX destroy stk contents */
2114    sprintf(stk, "%%0%dd", e);
2115    /* format scale factor*/
2116    length = sprintf(buffer + offset, stk,
2117		     exponent < 0 ? -exponent : exponent);
2118    /* check for overflow in exponent */
2119    if (length > e && overflowchar)
2120	goto exponential_float_overflow;
2121    offset += length;
2122
2123    /* make sure only d digits are printed after decimal point */
2124    if (d > 0) {
2125	int currd;
2126	char *dptr = strchr(buffer, '.'),
2127	     *eptr = strchr(dptr, exponentchar);
2128
2129	currd = eptr - dptr - 1;
2130	length = strlen(eptr);
2131
2132	/* check if need to remove excess digits */
2133	if (currd > d) {
2134	    int digit, dpos;
2135
2136	    dpos = offset = (dptr - buffer) + 1 + d;
2137	    digit = buffer[offset];
2138
2139	    memmove(buffer + offset, eptr, length + 1);
2140	    /* also copy ending nul character */
2141
2142	    /* adjust offset to length of total string */
2143	    offset += length;
2144
2145	    /* check if need to round */
2146	    if (dpos > 1 && isdigit(digit) && digit >= '5' &&
2147		isdigit(buffer[dpos - 1]) &&
2148		float_string_inc(buffer, dpos - 1))
2149		++offset;
2150	}
2151	/* check if need to add extra zero digits to fill space */
2152	else if (pd && currd < d) {
2153	    memmove(eptr + d - currd, eptr, length + 1);
2154	    /* also copy ending nul character */
2155
2156	    offset += d - currd;
2157	    for (++currd; currd <= d; currd++)
2158		dptr[currd] = '0';
2159	}
2160	/* check if need to remove zeros */
2161	else if (pd == NULL) {
2162	    int zeros = 1;
2163
2164	    while (eptr[-zeros] == '0')
2165		++zeros;
2166	    if (eptr[-zeros] == '.')
2167		--zeros;
2168	    if (zeros > 1) {
2169		memmove(eptr - zeros + 1, eptr, length + 1);
2170		offset -= zeros - 1;
2171	    }
2172	}
2173    }
2174    else {
2175	/* no digits after decimal point */
2176	int digit, inc = 0;
2177	char *dptr = strchr(buffer, '.'),
2178	     *eptr = strchr(dptr, exponentchar);
2179
2180	digit = dptr[1];
2181
2182	offset = (dptr - buffer) + 1;
2183	length = strlen(eptr);
2184	memmove(buffer + offset, eptr, length + 1);
2185	/* also copy ending nul character */
2186
2187 	if (digit >= '5' && dptr >= buffer + 2 &&
2188	    isdigit(dptr[-2]))
2189	    inc = float_string_inc(buffer, dptr - buffer - 2);
2190
2191	/* adjust offset to length of total string */
2192	offset += length + inc;
2193    }
2194
2195    if (w > 0 && offset > w) {
2196	/* remove leading "zero" to save space */
2197	if ((!sign && buffer[0] == '0') || (sign && buffer[1] == '0')) {
2198	    /* ending nul also copied */
2199	    memmove(buffer + sign, buffer + sign + 1, offset);
2200	    --offset;
2201	}
2202	/* remove leading '+' to "save" space */
2203	if (offset > w && buffer[0] == '+') {
2204	    /* ending nul also copied */
2205	    memmove(buffer, buffer + 1, offset);
2206	    --offset;
2207	}
2208    }
2209
2210    /* if cannot represent number in given width */
2211    if (overflowchar && offset > w)
2212	goto exponential_float_overflow;
2213
2214    length = 0;
2215    /* print padding if required */
2216    if (w > offset)
2217	length += LispWriteChars(stream, padchar, w - offset);
2218
2219    /* print float number representation */
2220    return (LispWriteStr(stream, buffer, offset) + length);
2221
2222exponential_float_overflow:
2223    return (LispWriteChars(stream, overflowchar, w));
2224}
2225
2226int
2227LispFormatGeneralFloat(LispObj *stream, LispObj *object,
2228		       int atsign, int w, int *pd, int e, int k,
2229		       int overflowchar, int padchar, int exponentchar)
2230{
2231    char stk[64];
2232    int length, exponent, n, dd, ee, ww, d = pd ? *pd : FLOAT_PREC;
2233    double value = DFLOAT_VALUE(object);
2234
2235    if (value == 0.0) {
2236	exponent = 0;
2237	n = 0;
2238	d = 1;
2239	strcpy(stk, "+0");
2240    }
2241    else {
2242	/* calculate format parameters, adjusting scale factor */
2243	parse_double(stk, &exponent, value, d + k - 1);
2244	n = exponent + 1;
2245    }
2246
2247    /* Let ee equal e+2, or 4 if e is omitted. */
2248    if (e)
2249	ee = e + 2;
2250    else
2251	ee = 4;
2252
2253    /* Let ww equal w-ee, or nil if w is omitted. */
2254    if (w)
2255	ww = w - ee;
2256    else
2257	ww = 0;
2258
2259    dd = d - n;
2260    if (d >= dd && dd >= 0) {
2261	length = LispFormatFixedFloat(stream, object, atsign, ww,
2262				      &dd, 0, overflowchar, padchar);
2263
2264	/* ~ee@T */
2265	length += LispWriteChars(stream, padchar, ee);
2266    }
2267    else
2268	length = LispFormatExponentialFloat(stream, object, atsign,
2269					    w, pd, e, k, overflowchar,
2270					    padchar, exponentchar);
2271
2272    return (length);
2273}
2274
2275int
2276LispFormatDollarFloat(LispObj *stream, LispObj *object,
2277		      int atsign, int collon, int d, int n, int w, int padchar)
2278{
2279    char buffer[512], stk[64];
2280    int sign, exponent, length, offset;
2281    double value = DFLOAT_VALUE(object);
2282
2283    if (value == 0.0) {
2284	exponent = 0;
2285	strcpy(stk, "+0");
2286    }
2287    else
2288	/* calculate format parameters, adjusting scale factor */
2289	parse_double(stk, &exponent, value, d == 0 ? FLOAT_PREC : d + 1);
2290
2291    /* set d to a "sane" value */
2292    if (d > 128)
2293	d = 128;
2294
2295    /* set n to a "sane" value */
2296    if (n > 128)
2297	n = 128;
2298
2299    /* use exponent as index in stk */
2300    ++exponent;
2301
2302    /* don't put sign in buffer,
2303     * if collon specified, must go before padding */
2304    sign = atsign || (stk[0] == '-');
2305
2306    offset = 0;
2307
2308    /* pad with zeros if required */
2309    if (exponent > 0)
2310	n -= exponent;
2311    while (n > 0) {
2312	buffer[offset++] = '0';
2313	n--;
2314    }
2315
2316    /* how many bytes in float representation */
2317    length = strlen(stk) - 1;
2318
2319    if (exponent > 0) {
2320	if (exponent > length) {
2321	    memcpy(buffer + offset, stk + 1, length);
2322	    memset(buffer + offset + length, '0', exponent - length);
2323	}
2324	else
2325	    memcpy(buffer + offset, stk + 1, exponent);
2326	offset += exponent;
2327	buffer[offset++] = '.';
2328	if (length > exponent) {
2329	    memcpy(buffer + offset, stk + 1 + exponent, length - exponent);
2330	    offset += length - exponent;
2331	}
2332	else
2333	    buffer[offset++] = '0';
2334    }
2335    else {
2336	if (n > 0)
2337	    buffer[offset++] = '0';
2338	buffer[offset++] = '.';
2339	while (exponent < 0) {
2340	    buffer[offset++] = '0';
2341	    exponent++;
2342	}
2343	memcpy(buffer + offset, stk + 1, length);
2344	offset += length;
2345    }
2346    buffer[offset] = '\0';
2347
2348    /* make sure only d digits are printed after decimal point */
2349    if (d > 0) {
2350	char *dptr = strchr(buffer, '.');
2351
2352	length = strlen(dptr) - 1;
2353	/* check if need to remove excess digits */
2354	if (length > d) {
2355	    int digit;
2356
2357	    offset = (dptr - buffer) + 1 + d;
2358	    digit = buffer[offset];
2359
2360	    /* remove extra digits */
2361	    buffer[offset] = '\0';
2362
2363	    /* check if need to round */
2364	    if (offset > 1 && isdigit(digit) && digit >= '5' &&
2365		isdigit(buffer[offset - 1]) &&
2366		float_string_inc(buffer, offset - 1))
2367		++offset;
2368	}
2369	/* check if need to add extra zero digits to fill space */
2370	else if (length < d) {
2371	    offset += d - length;
2372	    for (++length; length <= d; length++)
2373		dptr[length] = '0';
2374	    dptr[length] = '\0';
2375	}
2376    }
2377    else {
2378	/* no digits after decimal point */
2379	int digit, inc = 0;
2380	char *dptr = strchr(buffer, '.') + 1;
2381
2382	digit = *dptr;
2383	if (digit >= '5' && dptr >= buffer + 2 && isdigit(dptr[-2]))
2384	    inc = float_string_inc(buffer, dptr - buffer - 2);
2385
2386	offset = (dptr - buffer) + inc;
2387	buffer[offset] = '\0';
2388    }
2389
2390    length = 0;
2391    if (sign) {
2392	++offset;
2393	if (atsign && collon)
2394	    length += LispWriteChar(stream, value >= 0.0 ? '+' : '-');
2395    }
2396
2397    /* print padding if required */
2398    if (w > offset)
2399	length += LispWriteChars(stream, padchar, w - offset);
2400
2401    if (atsign && !collon)
2402	length += LispWriteChar(stream, value >= 0.0 ? '+' : '-');
2403
2404    /* print float number representation */
2405    return (LispWriteStr(stream, buffer, offset) + length);
2406}
2407