2408 lines
60 KiB
C
2408 lines
60 KiB
C
/*
|
|
* 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.31tsi Exp $ */
|
|
|
|
#include "lisp/write.h"
|
|
#include "lisp/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*, const 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, *stream;
|
|
|
|
stream = ARGUMENT(10);
|
|
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)
|
|
{
|
|
Atom_id name;
|
|
int i, length = 0, need_space = 0;
|
|
|
|
#define WRITE_ATOM(object) \
|
|
name = ATOMID(object); \
|
|
length += LispDoWriteAtom(stream, name->value, name->length, \
|
|
info->print_case)
|
|
#define WRITE_ATOMID(atomid) \
|
|
length += LispDoWriteAtom(stream, atomid->value, atomid->length, \
|
|
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_ATOMID(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->value, 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_ATOMID(Srest);
|
|
WRITE_SPACE();
|
|
WRITE_ATOM(alist->rest);
|
|
need_space = 1;
|
|
}
|
|
if (alist->auxs.num_symbols) {
|
|
if (need_space)
|
|
WRITE_SPACE();
|
|
WRITE_ATOMID(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_ATOMID
|
|
#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];
|
|
const char *string = NULL;
|
|
|
|
write_again:
|
|
switch (OBJECT_TYPE(object)) {
|
|
case LispNil_t:
|
|
if (object == NIL)
|
|
string = Snil->value;
|
|
else if (object == T)
|
|
string = St->value;
|
|
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, "NIL", 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, const 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, const 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->key;
|
|
|
|
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->value, id->length,
|
|
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 const char *ds[] = {
|
|
"", "one", "two", "three", "four",
|
|
"five", "six", "seven", "eight", "nine",
|
|
"ten", "eleven", "twelve", "thirteen", "fourteen",
|
|
"fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
|
|
};
|
|
static const char *dsth[] = {
|
|
"", "first", "second", "third", "fourth",
|
|
"fifth", "sixth", "seventh", "eighth", "ninth",
|
|
"tenth", "eleventh", "twelfth", "thirteenth", "fourteenth",
|
|
"fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth"
|
|
};
|
|
static const char *hs[] = {
|
|
"", "", "twenty", "thirty", "forty",
|
|
"fifty", "sixty", "seventy", "eighty", "ninety"
|
|
};
|
|
static const char *hsth[] = {
|
|
"", "", "twentieth", "thirtieth", "fortieth",
|
|
"fiftieth", "sixtieth", "seventieth", "eightieth", "ninetieth"
|
|
};
|
|
static const char *ts[] = {
|
|
"", "thousand", "million"
|
|
};
|
|
static const 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;
|
|
const 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)) {
|
|
const 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);
|
|
}
|