format.c revision f765521f
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/format.c,v 1.29tsi Exp $ */
31
32#include "lisp/io.h"
33#include "lisp/write.h"
34#include "lisp/format.h"
35#include <ctype.h>
36
37#define MAXFMT			8
38#define NOERROR			0
39
40/* parse error codes */
41#define PARSE_2MANYPARM		1	/* too many directive parameters */
42#define PARSE_2MANYATS		2	/* more than one @ in directive */
43#define PARSE_2MANYCOLS		3	/* more than one : in directive */
44#define PARSE_NOARGSLEFT	4	/* no arguments left to format */
45#define PARSE_BADFMTARG		5	/* argument is not an integer or char */
46#define PARSE_BADDIRECTIVE	6	/* unknown format directive */
47#define PARSE_BADINTEGER	7	/* bad integer representation */
48
49/* merge error codes */
50#define MERGE_2MANY		1	/* too many parameters to directive */
51#define MERGE_NOCHAR		2	/* parameter must be a character */
52#define MERGE_NOINT		3	/* parameter must be an integer */
53
54/* generic error codes */
55#define GENERIC_RADIX		1	/* radix not in range 2-36 */
56#define GENERIC_NEGATIVE	2	/* parameter is negative */
57#define GENERIC_BADSTRING	3	/* argument is not a string */
58#define GENERIC_BADLIST		4	/* argument is not a list */
59
60#define IF_SPECIFIED(arg)	(arg).specified ? &((arg).value) : NULL
61
62#define UPANDOUT_NORMAL		1
63#define UPANDOUT_COLLON		2
64#define UPANDOUT_HASH		4	/* only useful inside a ~{ iteration
65					 * forces loop finalization. */
66
67#define ITERATION_NORMAL	1
68#define ITERATION_LAST		2
69
70/*
71 * Types
72 */
73/* parameter to format */
74typedef struct {
75    unsigned int achar : 1;	/* value was specified as a character */
76    unsigned int specified : 1;	/* set if value was specified */
77    unsigned int offset : 30;	/* offset in format string, for error printing */
78    int value;
79} FmtArg;
80
81/* information about format parameters */
82typedef struct {
83    unsigned int atsign : 1;	/* @ specified */
84    unsigned int collon : 1;	/* : specified */
85    unsigned int command : 8;	/* the format command */
86    unsigned int count : 4;	/* number of arguments processed */
87    unsigned int offset : 10;	/* offset in format string, for error printing */
88    char *base, *format;
89    FmtArg arguments[MAXFMT];
90} FmtArgs;
91
92/* used for combining default format parameter values */
93typedef struct {
94    int achar;
95    int value;
96} FmtDef;
97
98/* number of default format parameter values and defaults */
99typedef struct {
100    int count;
101    FmtDef defaults[MAXFMT];
102} FmtDefs;
103
104/* used on recursive calls to LispFormat */
105typedef struct {
106    FmtArgs args;
107    LispObj *base_arguments;	/* pointer to first format argument */
108    int total_arguments;	/* number of objects in base_arguments */
109    char **format;		/* if need to update format string pointer */
110    LispObj **object;		/* CAR(arguments), for plural check */
111    LispObj **arguments;	/* current element of base_arguments */
112    int *num_arguments;		/* number of arguments after arguments */
113    int upandout;		/* information for recursive calls */
114    int iteration;		/* only set if in ~:{... or ~:@{ and in the
115				 * last argument list, hint for upandout */
116} FmtInfo;
117
118/*
119 * Prototypes
120 */
121static void merge_arguments(FmtArgs*, const FmtDefs*, int*);
122static char *parse_arguments(char*, FmtArgs*, int*, LispObj**, int*);
123static void merge_error(FmtArgs*, int);
124static void parse_error(FmtArgs*, int);
125static void generic_error(FmtArgs*, int);
126static void format_error(FmtArgs*, const char*);
127
128static int format_object(LispObj*, LispObj*);
129
130static void format_ascii(LispObj*, LispObj*, FmtArgs*);
131static void format_in_radix(LispObj*, LispObj*, int, FmtArgs*);
132static void format_radix_special(LispObj*, LispObj*, FmtArgs*);
133static void format_roman(LispObj*, LispObj*, FmtArgs*);
134static void format_english(LispObj*, LispObj*, FmtArgs*);
135static void format_character(LispObj*, LispObj*, FmtArgs*);
136static void format_fixed_float(LispObj*, LispObj*, FmtArgs*);
137static void format_exponential_float(LispObj*, LispObj*, FmtArgs*);
138static void format_general_float(LispObj*, LispObj*, FmtArgs*);
139static void format_dollar_float(LispObj*, LispObj*, FmtArgs*);
140static void format_tabulate(LispObj*, FmtArgs*);
141
142static void format_goto(FmtInfo*);
143static void format_indirection(LispObj*, LispObj*, FmtInfo*);
144
145static void list_formats(FmtInfo*, int, char**, char***, int*, int*, int*, int*);
146static void free_formats(char**, int);
147
148static void format_case_conversion(LispObj*, FmtInfo*);
149static void format_conditional(LispObj*, FmtInfo*);
150static void format_iterate(LispObj*, FmtInfo*);
151static void format_justify(LispObj*, FmtInfo*);
152
153static void LispFormat(LispObj*, FmtInfo*);
154
155/*
156 * Initialization
157 */
158static const FmtDefs AsciiDefs = {
159    4,
160    {
161	{0, 0},			/* mincol */
162	{0, 1},			/* colinc */
163	{0, 0},			/* minpad */
164	{1, ' '},		/* padchar */
165    },
166};
167
168static const FmtDefs IntegerDefs = {
169    4,
170    {
171	{0, 0},			/* mincol */
172	{1, ' '},		/* padchar */
173	{1, ','},		/* commachar */
174	{0, 3},			/* commainterval */
175    },
176};
177
178static const FmtDefs RadixDefs = {
179    5,
180    {
181	{0, 10},		/* radix */
182	{0, 0},			/* mincol */
183	{1, ' '},		/* padchar */
184	{1, ','},		/* commachar */
185	{0, 3},			/* commainterval */
186    },
187};
188
189static const FmtDefs NoneDefs = {
190    0,
191};
192
193static const FmtDefs FixedFloatDefs = {
194    5,
195    {
196	{0, 0},			/* w */
197	{0, 16},		/* d */
198	{0, 0},			/* k */
199	{1, '\0'},		/* overflowchar */
200	{1, ' '},		/* padchar */
201    },
202};
203
204static const FmtDefs ExponentialFloatDefs = {
205    7,
206    {
207	{0, 0},			/* w */
208	{0, 16},		/* d */
209	{0, 0},			/* e */
210	{0, 1},			/* k */
211	{1, '\0'},		/* overflowchar */
212	{1, ' '},		/* padchar */
213	{1, 'E'},		/* exponentchar */
214	/* XXX if/when more than one float format,
215	 * should default to object type */
216    },
217};
218
219static const FmtDefs DollarFloatDefs = {
220    4,
221    {
222	{0, 2},			/* d */
223	{0, 1},			/* n */
224	{0, 0},			/* w */
225	{1, ' '},		/* padchar */
226    },
227};
228
229static const FmtDefs OneDefs = {
230    1,
231    {
232	{0, 1},
233    },
234};
235
236static const FmtDefs TabulateDefs = {
237    2,
238    {
239	{0, 0},			/* colnum */
240	{0, 1},			/* colinc */
241    },
242};
243
244extern LispObj *Oprint_escape;
245
246/*
247 * Implementation
248 */
249static void
250merge_arguments(FmtArgs *arguments, const FmtDefs *defaults, int *code)
251{
252    int count;
253    const FmtDef *defaul;
254    FmtArg *argument;
255
256    defaul = &(defaults->defaults[0]);
257    argument = &(arguments->arguments[0]);
258    for (count = 0; count < defaults->count; count++, argument++, defaul++) {
259	if (count >= arguments->count)
260	    argument->specified = 0;
261	if (argument->specified) {
262	    if (argument->achar != defaul->achar) {
263		*code = defaul->achar ? MERGE_NOCHAR : MERGE_NOINT;
264		arguments->offset = argument->offset;
265		return;
266	    }
267	}
268	else {
269	    argument->specified = 0;
270	    argument->achar = defaul->achar;
271	    argument->value = defaul->value;
272	}
273    }
274
275    /* check if extra arguments were provided */
276    if (arguments->count > defaults->count)
277	*code = MERGE_2MANY;
278}
279
280/* the pointer arguments may be null, useful when just testing/parsing
281 * the directive parameters */
282static char *
283parse_arguments(char *format, FmtArgs *arguments,
284		int *num_objects, LispObj **objects, int *code)
285{
286    int test;
287    char *ptr;
288    FmtArg *argument;
289    unsigned int tmpcmd = 0;
290
291    /* initialize */
292    test = objects == NULL || code == NULL || num_objects == NULL;
293    ptr = format;
294    argument = &(arguments->arguments[0]);
295    arguments->atsign = arguments->collon = arguments->command = 0;
296
297    /* parse format parameters */
298    for (arguments->count = 0;; arguments->count++) {
299	arguments->offset = ptr - format + 1;
300	if (arguments->count >= MAXFMT) {
301	    if (!test)
302		*code = PARSE_2MANYPARM;
303	    return (ptr);
304	}
305	if (*ptr == '\'') {		/* character parameter value */
306	    ++ptr;			/* skip ' */
307	    argument->achar = argument->specified = 1;
308	    argument->value = *ptr++;
309	}
310	else if (*ptr == ',') {		/* use default parameter value */
311	    argument->achar = 0;
312	    argument->specified = 0;
313	    /* don't increment ptr, will be incremented below */
314	}
315	else if (*ptr == '#') {		/* number of arguments is value */
316	    ++ptr;			/* skip # */
317	    argument->achar = 0;
318	    argument->specified = 1;
319	    if (!test)
320		argument->value = *num_objects;
321	}
322	else if (*ptr == 'v' ||
323		 *ptr == 'V') {		/* format object argument is value */
324	    LispObj *object;
325
326	    ++ptr;			/* skip V */
327	    if (!test) {
328		if (!CONSP(*objects)) {
329		    *code = PARSE_NOARGSLEFT;
330		    return (ptr);
331		}
332		object = CAR((*objects));
333		if (FIXNUMP(object)) {
334		    argument->achar = 0;
335		    argument->specified = 1;
336		    argument->value = FIXNUM_VALUE(object);
337		}
338		else if (SCHARP(object)) {
339		    argument->achar = argument->specified = 1;
340		    argument->value = SCHAR_VALUE(object);
341		}
342		else {
343		    *code = PARSE_BADFMTARG;
344		    return (ptr);
345		}
346		*objects = CDR(*objects);
347		--*num_objects;
348	    }
349	}
350	else if (isdigit(*ptr) ||
351		*ptr == '-' || *ptr == '+') {	/* integer parameter value */
352	    int sign;
353
354	    argument->achar = 0;
355	    argument->specified = 1;
356	    if (!isdigit(*ptr)) {
357		sign = *ptr++ == '-';
358	    }
359	    else
360		sign = 0;
361	    if (!test && !isdigit(*ptr)) {
362		*code = PARSE_BADINTEGER;
363		return (ptr);
364	    }
365	    argument->value = *ptr++ - '0';
366	    while (isdigit(*ptr)) {
367		argument->value = (argument->value * 10) + (*ptr++ - '0');
368		if (argument->value > 65536) {
369		    if (!test) {
370			*code = PARSE_BADINTEGER;
371			return (ptr);
372		    }
373		}
374	    }
375	    if (sign)
376		argument->value = -argument->value;
377	}
378	else				/* no more arguments to format */
379	    break;
380
381	if (*ptr == ',')
382	    ++ptr;
383
384	/* remember offset of format parameter, for better error printing */
385	argument->offset = arguments->offset;
386	argument++;
387    }
388
389    /* check for extra flags */
390    for (;;) {
391	if (*ptr == '@') {		/* check for special parameter atsign */
392	    if (arguments->atsign) {
393		if (!test) {
394		    *code = PARSE_2MANYATS;
395		    return (ptr);
396		}
397	    }
398	    ++ptr;
399	    ++arguments->offset;
400	    arguments->atsign = 1;
401	}
402	else if (*ptr == ':') {		/* check for special parameter collon */
403	    if (arguments->collon) {
404		if (!test) {
405		    *code = PARSE_2MANYCOLS;
406		    return (ptr);
407		}
408	    }
409	    ++ptr;
410	    ++arguments->offset;
411	    arguments->collon = 1;
412	}
413	else				/* next value is format command */
414	    break;
415    }
416
417    if (!test)
418	*code = NOERROR;
419    arguments->command = *ptr++;
420    tmpcmd = arguments->command;
421    if (islower(tmpcmd))
422	arguments->command = toupper(tmpcmd);
423    ++arguments->offset;
424
425    return (ptr);
426}
427
428static void
429parse_error(FmtArgs *args, int code)
430{
431    static const char * const errors[] = {
432	NULL,
433	"too many parameters to directive",
434	"too many @ parameters",
435	"too many : parameters",
436	"no arguments left to format",
437	"argument is not a fixnum integer or a character",
438	"unknown format directive",
439	"parameter is not a fixnum integer",
440    };
441
442    format_error(args, errors[code]);
443}
444
445static void
446merge_error(FmtArgs *args, int code)
447{
448    static const char * const errors[] = {
449	NULL,
450	"too many parameters to directive",
451	"argument must be a character",
452	"argument must be a fixnum integer",
453    };
454
455    format_error(args, errors[code]);
456}
457
458static void
459generic_error(FmtArgs *args, int code)
460{
461    static const char * const errors[] = {
462	NULL,
463	"radix must be in the range 2 to 36, inclusive",
464	"parameter must be positive",
465	"argument must be a string",
466	"argument must be a list",
467    };
468
469    format_error(args, errors[code]);
470}
471
472static void
473format_error(FmtArgs *args, const char *str)
474{
475    char *message;
476    int errorlen, formatlen;
477
478    /* number of bytes of format to be printed */
479    formatlen = (args->format - args->base) + args->offset;
480
481    /* length of specific error message */
482    errorlen = strlen(str) + 1;			/* plus '\n' */
483
484    /* XXX allocate string with LispMalloc,
485     * so that it will be freed in LispTopLevel */
486    message = LispMalloc(formatlen + errorlen + 1);
487
488    sprintf(message, "%s\n", str);
489    memcpy(message + errorlen, args->base, formatlen);
490    message[errorlen + formatlen] = '\0';
491
492    LispDestroy("FORMAT: %s", message);
493}
494
495static int
496format_object(LispObj *stream, LispObj *object)
497{
498    int length;
499
500    length = LispWriteObject(stream, object);
501
502    return (length);
503}
504
505static void
506format_ascii(LispObj *stream, LispObj *object, FmtArgs *args)
507{
508    GC_ENTER();
509    LispObj *string = NIL;
510    int length = 0,
511	atsign = args->atsign,
512	collon = args->collon,
513	mincol = args->arguments[0].value,
514	colinc = args->arguments[1].value,
515	minpad = args->arguments[2].value,
516	padchar = args->arguments[3].value;
517
518    /* check/correct arguments */
519    if (mincol < 0)
520	mincol = 0;
521    if (colinc < 0)
522	colinc = 1;
523    if (minpad < 0)
524	minpad = 0;
525    /* XXX pachar can be the null character? */
526
527    if (object == NIL)
528	length = collon ? 2 : 3;	    /* () or NIL */
529
530    /* left padding */
531    if (atsign) {
532	/* if length not yet known */
533	if (object == NIL) {
534	    string = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0);
535	    GC_PROTECT(string);
536	    length = LispWriteObject(string, object);
537	}
538
539	/* output minpad characters at left */
540	if (minpad) {
541	    length += minpad;
542	    LispWriteChars(stream, padchar, minpad);
543	}
544
545	if (colinc) {
546	    /* puts colinc spaces at a time,
547	     * until at least mincol chars out */
548	    while (length < mincol) {
549		LispWriteChars(stream, padchar, colinc);
550		length += colinc;
551	    }
552	}
553    }
554
555    if (object == NIL) {
556	if (collon)
557	    LispWriteStr(stream, "()", 2);
558	else
559	    LispWriteStr(stream,  Snil->value, 3);
560    }
561    else {
562	/* if string is not NIL, atsign was specified
563	 * and object printed to string */
564	if (string == NIL)
565	    length = format_object(stream, object);
566	else {
567	    int size;
568	    const char *str = LispGetSstring(SSTREAMP(string), &size);
569
570	    LispWriteStr(stream, str, size);
571	}
572    }
573
574    /* right padding */
575    if (!atsign) {
576	/* output minpad characters at left */
577	if (minpad) {
578	    length += minpad;
579	    LispWriteChars(stream, padchar, minpad);
580	}
581	if (colinc) {
582	    /* puts colinc spaces at a time,
583	     * until at least mincol chars out */
584	    while (length < mincol) {
585		LispWriteChars(stream, padchar, colinc);
586		length += colinc;
587	    }
588	}
589    }
590
591    GC_LEAVE();
592}
593
594/* assumes radix is 0 or in range 2 - 36 */
595static void
596format_in_radix(LispObj *stream, LispObj *object, int radix, FmtArgs *args)
597{
598    if (INTEGERP(object)) {
599	int i, atsign, collon, mincol, padchar, commachar, commainterval;
600
601	i = (radix == 0);
602	atsign = args->atsign;
603	collon = args->collon;
604	if (radix == 0) {
605	    radix = args->arguments[0].value;
606	    if (radix < 2 || radix > 36) {
607		args->offset = args->arguments[0].offset;
608		generic_error(args, GENERIC_RADIX);
609	    }
610	}
611	mincol = args->arguments[i++].value;
612	padchar = args->arguments[i++].value;
613	commachar = args->arguments[i++].value;
614	commainterval = args->arguments[i++].value;
615
616	LispFormatInteger(stream, object, radix, atsign, collon,
617			  mincol, padchar, commachar, commainterval);
618    }
619    else
620	format_object(stream, object);
621}
622
623static void
624format_radix_special(LispObj *stream, LispObj *object, FmtArgs *args)
625{
626    if (FIXNUMP(object)) {
627	if (args->atsign)
628	    format_roman(stream, object, args);
629	else
630	    format_english(stream, object, args);
631    }
632    else
633	format_object(stream, object);
634}
635
636static void
637format_roman(LispObj *stream, LispObj *object, FmtArgs *args)
638{
639    long value = 0;
640    int cando, new_roman = args->collon == 0;
641
642    if (FIXNUMP(object)) {
643	value = FIXNUM_VALUE(object);
644	if (new_roman)
645	    cando = value >= 1 && value <= 3999;
646	else
647	    cando = value >= 1 && value <= 4999;
648    }
649    else
650	cando = 0;
651
652    if (cando)
653	LispFormatRomanInteger(stream, value, new_roman);
654    else
655	format_object(stream, object);
656}
657
658static void
659format_english(LispObj *stream, LispObj *object, FmtArgs *args)
660{
661    int cando;
662    long number = 0;
663
664    if (FIXNUMP(object)) {
665	number = FIXNUM_VALUE(object);
666	cando = number >= -999999999 && number <= 999999999;
667    }
668    else
669	cando = 0;
670
671    if (cando)
672	LispFormatEnglishInteger(stream, number, args->collon);
673    else
674	format_object(stream, object);
675}
676
677static void
678format_character(LispObj *stream, LispObj *object, FmtArgs *args)
679{
680    if (SCHARP(object))
681	LispFormatCharacter(stream, object, args->atsign, args->collon);
682    else
683	format_object(stream, object);
684}
685
686static void
687format_fixed_float(LispObj *stream, LispObj *object, FmtArgs *args)
688{
689    if (FLOATP(object))
690	LispFormatFixedFloat(stream, object, args->atsign,
691			     args->arguments[0].value,
692			     IF_SPECIFIED(args->arguments[1]),
693			     args->arguments[2].value,
694			     args->arguments[3].value,
695			     args->arguments[4].value);
696    else
697	format_object(stream, object);
698}
699
700static void
701format_exponential_float(LispObj *stream, LispObj *object, FmtArgs *args)
702{
703    if (FLOATP(object))
704	LispFormatExponentialFloat(stream, object, args->atsign,
705				   args->arguments[0].value,
706				   IF_SPECIFIED(args->arguments[1]),
707				   args->arguments[2].value,
708				   args->arguments[3].value,
709				   args->arguments[4].value,
710				   args->arguments[5].value,
711				   args->arguments[6].value);
712    else
713	format_object(stream, object);
714}
715
716static void
717format_general_float(LispObj *stream, LispObj *object, FmtArgs *args)
718{
719    if (FLOATP(object))
720	LispFormatGeneralFloat(stream, object, args->atsign,
721				args->arguments[0].value,
722				IF_SPECIFIED(args->arguments[1]),
723				args->arguments[2].value,
724				args->arguments[3].value,
725				args->arguments[4].value,
726				args->arguments[5].value,
727				args->arguments[6].value);
728    else
729	format_object(stream, object);
730}
731
732static void
733format_dollar_float(LispObj *stream, LispObj *object, FmtArgs *args)
734{
735    if (FLOATP(object))
736	LispFormatDollarFloat(stream, object,
737			      args->atsign, args->collon,
738			      args->arguments[0].value,
739			      args->arguments[1].value,
740			      args->arguments[2].value,
741			      args->arguments[3].value);
742    else
743	format_object(stream, object);
744}
745
746static void
747format_tabulate(LispObj *stream, FmtArgs *args)
748{
749    int atsign = args->atsign,
750	colnum = args->arguments[0].value,
751	colinc = args->arguments[1].value,
752	column;
753
754    column = LispGetColumn(stream);
755
756    if (atsign) {
757	/* relative tabulation */
758	if (colnum > 0) {
759	    LispWriteChars(stream, ' ', colnum);
760	    column += colnum;
761	}
762	/* tabulate until at a multiple of colinc */
763	if (colinc > 0)
764	    LispWriteChars(stream, ' ', colinc - (column % colinc));
765    }
766    else {
767	/* if colinc not specified, just move to given column */
768	if (colinc <= 0)
769	    LispWriteChars(stream, ' ', column - colnum);
770	else {
771	    /* always output at least colinc spaces */
772	    do {
773		LispWriteChars(stream, ' ', colinc);
774		colnum -= colinc;
775	    } while (colnum > column);
776	}
777    }
778}
779
780static void
781format_goto(FmtInfo *info)
782{
783    int count, num_arguments;
784    LispObj *object, *arguments;
785
786    /* number of arguments to ignore or goto offset */
787    count = info->args.arguments[0].value;
788    if (count < 0)
789	generic_error(&(info->args), GENERIC_NEGATIVE);
790
791    if (info->args.atsign) {
792	/* absolute goto */
793
794	/* if not specified, defaults to zero */
795	if (!(info->args.arguments[0].specified))
796	    count = 0;
797
798	/* if offset too large */
799	if (count > info->total_arguments)
800	    parse_error(&(info->args), PARSE_NOARGSLEFT);
801	else if (count != info->total_arguments - *(info->num_arguments)) {
802	    /* calculate new parameters */
803	    object = NIL;
804	    arguments = info->base_arguments;
805	    num_arguments = info->total_arguments - count;
806
807	    for (; count > 0; count--, arguments = CDR(arguments))
808		object = CAR(arguments);
809
810	    /* update format information */
811	    *(info->object) = object;
812	    *(info->arguments) = arguments;
813	    *(info->num_arguments) = num_arguments;
814	}
815    }
816    else if (count) {
817	/* relative goto, ignore or go back count arguments */
818
819	/* prepare to update parameters */
820	arguments = *(info->arguments);
821	num_arguments = *(info->num_arguments);
822
823	/* go back count arguments? */
824	if (info->args.collon)
825	    count = -count;
826
827	num_arguments -= count;
828
829	if (count > 0) {
830	    if (count > *(info->num_arguments))
831		parse_error(&(info->args), PARSE_NOARGSLEFT);
832
833	    object = *(info->object);
834	    for (; count > 0; count--, arguments = CDR(arguments))
835		object = CAR(arguments);
836	}
837	else {		/* count < 0 */
838	    if (info->total_arguments + count - *(info->num_arguments) < 0)
839		parse_error(&(info->args), PARSE_NOARGSLEFT);
840
841	    object = NIL;
842	    arguments = info->base_arguments;
843	    for (count = 0; count < info->total_arguments - num_arguments;
844		count++, arguments = CDR(arguments))
845		object = CAR(arguments);
846	}
847
848	/* update format parameters */
849	*(info->object) = object;
850	*(info->arguments) = arguments;
851	*(info->num_arguments) = num_arguments;
852    }
853}
854
855static void
856format_indirection(LispObj *stream, LispObj *format, FmtInfo *info)
857{
858    char *string;
859    LispObj *object;
860    FmtInfo indirect_info;
861
862    if (!STRINGP(format))
863	generic_error(&(info->args), GENERIC_BADSTRING);
864    string = THESTR(format);
865
866    /* most information is the same */
867    memcpy(&indirect_info, info, sizeof(FmtInfo));
868
869    /* set new format string */
870    indirect_info.args.base = indirect_info.args.format = string;
871    indirect_info.format = &string;
872
873    if (info->args.atsign) {
874	/* use current arguments */
875
876	/* do the indirect format */
877	LispFormat(stream, &indirect_info);
878    }
879    else {
880	/* next argument is the recursive call arguments */
881
882	int num_arguments;
883
884	/* it is valid to not have a list following string, as string may
885	 * not have format directives */
886	if (CONSP(*(indirect_info.arguments)))
887	    object = CAR(*(indirect_info.arguments));
888	else
889	    object = NIL;
890
891	if (!LISTP(object) || !CONSP(*(info->arguments)))
892	    generic_error(&(info->args), GENERIC_BADLIST);
893
894	/* update information now */
895	*(info->object) = object;
896	*(info->arguments) = CDR(*(info->arguments));
897	*(info->num_arguments) -= 1;
898
899	/* set arguments for recursive call */
900	indirect_info.base_arguments = object;
901	indirect_info.arguments = &object;
902	for (num_arguments = 0; CONSP(object); object = CDR(object))
903	    ++num_arguments;
904
905	/* note that indirect_info.arguments is a pointer to "object",
906	 * keep it pointing to the correct object */
907	object = indirect_info.base_arguments;
908	indirect_info.total_arguments = num_arguments;
909	indirect_info.num_arguments = &num_arguments;
910
911	/* do the indirect format */
912	LispFormat(stream, &indirect_info);
913    }
914}
915
916/* update pointers to a list of format strings:
917 *	for '(' and '{' only one list is required
918 *	for '[' and '<' more than one may be returned
919 *	has_default is only meaningful for '[' and '<'
920 *	comma_width and line_width are only meaningful to '<', and
921 *	    only valid if has_default set
922 * if the string is finished prematurely, LispDestroy is called
923 * format_ptr is updated to the correct pointer in the "main" format string
924 */
925static void
926list_formats(FmtInfo *info, int command, char **format_ptr,
927	     char ***format_list, int *format_count, int *has_default,
928	     int *comma_width, int *line_width)
929{
930    /* instead of processing the directives recursively, just separate the
931     * input formats in separate strings, then see if one of then need to
932     * be used */
933    FmtArgs args;
934    int counters[] = {  0,   0,   0,   0};
935		    /* '[', '(', '{', '<' */
936    char *format, *next_format, *start, **formats;
937    int num_formats, format_index, separator, add_format;
938
939    /* initialize */
940    formats = NULL;
941    num_formats = format_index = 0;
942    if (has_default != NULL)
943	*has_default = 0;
944    if (comma_width != NULL)
945	*comma_width = 0;
946    if (line_width != NULL)
947	*line_width = 0;
948    format = start = next_format = *format_ptr;
949    switch (command) {
950	case '[': counters[0] = 1; format_index = 0; break;
951	case '(': counters[1] = 1; format_index = 1; break;
952	case '{': counters[2] = 1; format_index = 2; break;
953	case '<': counters[3] = 1; format_index = 3; break;
954    }
955
956#define	LIST_FORMATS_ADD	1
957#define	LIST_FORMATS_DONE	2
958
959    /* fill list of format options to conditional */
960    while (*format) {
961	if (*format == '~') {
962	    separator = add_format = 0;
963	    args.format = format + 1;
964	    next_format = parse_arguments(format + 1, &args, NULL, NULL, NULL);
965	    switch (args.command) {
966 		case '[': ++counters[0];    break;
967		case ']': --counters[0];    break;
968		case '(': ++counters[1];    break;
969		case ')': --counters[1];    break;
970		case '{': ++counters[2];    break;
971		case '}': --counters[2];    break;
972		case '<': ++counters[3];    break;
973		case '>': --counters[3];    break;
974		case ';': separator = 1;    break;
975	    }
976
977	    /* check if a new format string must be added */
978	    if (separator && counters[format_index] == 1 &&
979		(command == '[' || command == '<'))
980		add_format = LIST_FORMATS_ADD;
981	    else if (counters[format_index] == 0)
982		add_format = LIST_FORMATS_DONE;
983
984	    if (add_format) {
985		int length = format - start;
986
987		formats = LispRealloc(formats,
988				      (num_formats + 1) * sizeof(char*));
989
990		formats[num_formats] = LispMalloc(length + 1);
991		strncpy(formats[num_formats], start, length);
992		formats[num_formats][length] = '\0';
993		++num_formats;
994		/* loop finished? */
995		if (add_format == LIST_FORMATS_DONE)
996		    break;
997		else if (command == '[' && has_default != NULL)
998		    /* will be set only for the last parameter, what is
999		     * expected, just don't warn about it in the incorrect
1000		     * place */
1001		    *has_default = args.collon != 0;
1002		else if (command == '<' && num_formats == 1) {
1003		    /* if the first parameter to '<', there may be overrides
1004		     * to comma-width and line-width */
1005		    if (args.collon && has_default != NULL) {
1006			*has_default = 1;
1007			if (comma_width != NULL &&
1008			    args.arguments[0].specified &&
1009			    !args.arguments[0].achar)
1010			    *comma_width = args.arguments[0].value;
1011			if (line_width != NULL &&
1012			    args.arguments[1].specified &&
1013			    !args.arguments[1].achar)
1014			    *line_width = args.arguments[1].value;
1015		    }
1016		}
1017		start = next_format;
1018	    }
1019	    format = next_format;
1020	}
1021	else
1022	    ++format;
1023    }
1024
1025    /* check if format string did not finish prematurely */
1026    if (counters[format_index] != 0) {
1027	char error_message[64];
1028
1029	sprintf(error_message, "expecting ~%c", command);
1030	format_error(&(info->args), error_message);
1031    }
1032
1033    /* update pointers */
1034    *format_list = formats;
1035    *format_count = num_formats;
1036    *format_ptr = next_format;
1037}
1038
1039static void
1040free_formats(char **formats, int num_formats)
1041{
1042    if (num_formats) {
1043	while (--num_formats >= 0)
1044	    LispFree(formats[num_formats]);
1045	LispFree(formats);
1046    }
1047}
1048
1049static void
1050format_case_conversion(LispObj *stream, FmtInfo *info)
1051{
1052    GC_ENTER();
1053    LispObj *string;
1054    FmtInfo case_info;
1055    char *str, *ptr;
1056    char *format, *next_format, **formats;
1057    int atsign, collon, num_formats, length;
1058
1059    atsign = info->args.atsign;
1060    collon = info->args.collon;
1061
1062    /* output to a string, before case conversion */
1063    string = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0);
1064    GC_PROTECT(string);
1065
1066    /* most information is the same */
1067    memcpy(&case_info, info, sizeof(FmtInfo));
1068
1069    /* list formats */
1070    next_format = *(info->format);
1071    list_formats(info, '(', &next_format, &formats, &num_formats,
1072		 NULL, NULL, NULL);
1073
1074    /* set new format string */
1075    format = formats[0];
1076    case_info.args.base = case_info.args.format = format;
1077    case_info.format = &format;
1078
1079    /* format text to string */
1080    LispFormat(string, &case_info);
1081
1082    str = ptr = LispGetSstring(SSTREAMP(string), &length);
1083
1084    /* do case conversion */
1085    if (!atsign && !collon) {
1086	/* convert all upercase to lowercase */
1087	for (; *ptr; ptr++) {
1088	    if (isupper(*ptr))
1089		*ptr = tolower(*ptr);
1090	}
1091    }
1092    else if (atsign && collon) {
1093	/* convert all lowercase to upercase */
1094	for (; *ptr; ptr++) {
1095	    if (islower(*ptr))
1096		*ptr = toupper(*ptr);
1097	}
1098    }
1099    else {
1100	int upper = 1;
1101
1102	/* skip non-alphanumeric characters */
1103	for (; *ptr; ptr++)
1104	    if (isalnum(*ptr))
1105		break;
1106
1107	/* capitalize words */
1108	for (; *ptr; ptr++) {
1109	    if (isalnum(*ptr)) {
1110		if (upper) {
1111		    if (islower(*ptr))
1112			*ptr = toupper(*ptr);
1113		    upper = 0;
1114		}
1115		else if (isupper(*ptr))
1116		    *ptr = tolower(*ptr);
1117	    }
1118	    else
1119		upper = collon;
1120		/* if collon, capitalize all words, else just first word */
1121	}
1122    }
1123
1124    /* output case converted string */
1125    LispWriteStr(stream, str, length);
1126
1127    /* temporary string stream is not necessary anymore */
1128    GC_LEAVE();
1129
1130    /* free temporary memory */
1131    free_formats(formats, num_formats);
1132
1133    /* this information always updated */
1134    *(info->format) = next_format;
1135}
1136
1137static void
1138format_conditional(LispObj *stream, FmtInfo *info)
1139{
1140    LispObj *object, *arguments;
1141    char *format, *next_format, **formats;
1142    int choice, num_formats, has_default, num_arguments;
1143
1144    /* save information that may change */
1145    object = *(info->object);
1146    arguments = *(info->arguments);
1147    num_arguments = *(info->num_arguments);
1148
1149    /* initialize */
1150    choice = -1;
1151    next_format = *(info->format);
1152
1153    /* list formats */
1154    list_formats(info, '[',
1155		 &next_format, &formats, &num_formats, &has_default, NULL, NULL);
1156
1157    /* ~:[false;true] */
1158    if (info->args.collon) {
1159	/* one argument always consumed */
1160	if (!CONSP(arguments))
1161	    parse_error(&(info->args), PARSE_NOARGSLEFT);
1162	object = CAR(arguments);
1163	arguments = CDR(arguments);
1164	--num_arguments;
1165	choice = object == NIL ? 0 : 1;
1166    }
1167    /* ~@[true] */
1168    else if (info->args.atsign) {
1169	/* argument consumed only if nil, but one must be available */
1170	if (!CONSP(arguments))
1171	    parse_error(&(info->args), PARSE_NOARGSLEFT);
1172	if (CAR(arguments) != NIL)
1173	    choice = 0;
1174	else {
1175	    object = CAR(arguments);
1176	    arguments = CDR(arguments);
1177	    --num_arguments;
1178	}
1179    }
1180    /* ~n[...~] */
1181    else if (info->args.arguments[0].specified)
1182	/* no arguments consumed */
1183	choice = info->args.arguments[0].value;
1184    /* ~[...~] */
1185    else {
1186	/* one argument consumed, it is the index in the available formats */
1187	if (!CONSP(arguments))
1188	    parse_error(&(info->args), PARSE_NOARGSLEFT);
1189	object = CAR(arguments);
1190	arguments = CDR(arguments);
1191	--num_arguments;
1192	/* no error if it isn't a number? */
1193	if (FIXNUMP(object))
1194	    choice = FIXNUM_VALUE(object);
1195    }
1196
1197    /* update anything that may have changed */
1198    *(info->object) = object;
1199    *(info->arguments) = arguments;
1200    *(info->num_arguments) = num_arguments;
1201
1202    /* if choice is out of range check if there is a default choice */
1203    if (has_default && (choice < 0 || choice >= num_formats))
1204	choice = num_formats - 1;
1205
1206    /* if one of the formats must be parsed */
1207    if (choice >= 0 && choice < num_formats) {
1208	FmtInfo conditional_info;
1209
1210	/* most information is the same */
1211	memcpy(&conditional_info, info, sizeof(FmtInfo));
1212
1213	/* set new format string */
1214	format = formats[choice];
1215	conditional_info.args.base = conditional_info.args.format = format;
1216	conditional_info.format = &format;
1217
1218	/* do the conditional format */
1219	LispFormat(stream, &conditional_info);
1220    }
1221
1222    /* free temporary memory */
1223    free_formats(formats, num_formats);
1224
1225    /* this information always updated */
1226    *(info->format) = next_format;
1227}
1228
1229static void
1230format_iterate(LispObj *stream, FmtInfo *info)
1231{
1232    FmtInfo iterate_info;
1233    LispObj *object, *arguments, *iarguments, *iobject;
1234    char *format, *next_format, *loop_format, **formats;
1235    int num_arguments, iterate, iterate_max, has_max, has_min, inum_arguments,
1236	num_formats;
1237
1238    /* save information that may change */
1239    object = *(info->object);
1240    arguments = *(info->arguments);
1241    num_arguments = *(info->num_arguments);
1242
1243    /* initialize */
1244    iterate = has_min = 0;
1245    next_format = *(info->format);
1246
1247    /* if has_max set, iterate at most iterate_max times */
1248    has_max = info->args.arguments[0].specified;
1249    iterate_max = info->args.arguments[0].value;
1250
1251    /* list formats */
1252    list_formats(info, '{', &next_format, &formats, &num_formats,
1253		 NULL, NULL, NULL);
1254    loop_format = formats[0];
1255
1256    /* most information is the same */
1257    memcpy(&iterate_info, info, sizeof(FmtInfo));
1258
1259    /* ~{...~} */
1260    if (!info->args.atsign && !info->args.collon) {
1261	/* next argument is the argument list for the iteration */
1262
1263	/* fetch argument list, must exist */
1264	if (!CONSP(arguments))
1265	    parse_error(&(info->args), PARSE_NOARGSLEFT);
1266	iarguments = object = CAR(arguments);
1267	object = CAR(arguments);
1268	arguments = CDR(arguments);
1269	--num_arguments;
1270
1271	inum_arguments = 0;
1272	if (CONSP(object)) {
1273	    /* count arguments to format */
1274	    for (iobject = object; CONSP(iobject); iobject = CDR(iobject))
1275		++inum_arguments;
1276	}
1277	else if (object != NIL)
1278	    generic_error(&(info->args), GENERIC_BADLIST);
1279
1280	iobject = NIL;
1281
1282	/* set new arguments to recursive calls */
1283	iarguments = object;
1284	iterate_info.base_arguments = iarguments;
1285	iterate_info.total_arguments = inum_arguments;
1286	iterate_info.object = &iobject;
1287	iterate_info.arguments = &iarguments;
1288	iterate_info.num_arguments = &inum_arguments;
1289
1290	/* iterate */
1291	for (;; iterate++) {
1292	    /* if maximum iterations done or all arguments consumed */
1293	    if (has_max && iterate > iterate_max)
1294		break;
1295	    else if (inum_arguments == 0 && (!has_min || iterate > 0))
1296		break;
1297
1298	    format = loop_format;
1299
1300	    /* set new format string */
1301	    iterate_info.args.base = iterate_info.args.format = format;
1302	    iterate_info.format = &format;
1303
1304	    /* information for possible ~^, in this case ~:^ is a noop */
1305	    iterate_info.iteration = ITERATION_NORMAL;
1306
1307	    /* do the format */
1308	    LispFormat(stream, &iterate_info);
1309
1310	    /* check for forced loop break */
1311	    if (iterate_info.upandout & UPANDOUT_HASH)
1312		break;
1313	}
1314    }
1315    /* ~:@{...~} */
1316    else if (info->args.atsign && info->args.collon) {
1317	/* every following argument is the argument list for the iteration */
1318
1319	/* iterate */
1320	for (;; iterate++) {
1321	    /* if maximum iterations done or all arguments consumed */
1322	    if (has_max && iterate > iterate_max)
1323		break;
1324	    else if (num_arguments == 0 && (!has_min || iterate > 0))
1325		break;
1326
1327	    /* fetch argument list, must exist */
1328	    if (!CONSP(arguments))
1329		parse_error(&(info->args), PARSE_NOARGSLEFT);
1330	    iarguments = object = CAR(arguments);
1331	    object = CAR(arguments);
1332	    arguments = CDR(arguments);
1333	    --num_arguments;
1334
1335	    inum_arguments = 0;
1336	    if (CONSP(object)) {
1337		/* count arguments to format */
1338		for (iobject = object; CONSP(iobject); iobject = CDR(iobject))
1339		    ++inum_arguments;
1340	    }
1341	    else if (object != NIL)
1342		generic_error(&(info->args), GENERIC_BADLIST);
1343
1344	    iobject = NIL;
1345
1346	    /* set new arguments to recursive calls */
1347	    iarguments = object;
1348	    iterate_info.base_arguments = iarguments;
1349	    iterate_info.total_arguments = inum_arguments;
1350	    iterate_info.object = &iobject;
1351	    iterate_info.arguments = &iarguments;
1352	    iterate_info.num_arguments = &inum_arguments;
1353
1354	    format = loop_format;
1355
1356	    /* set new format string */
1357	    iterate_info.args.base = iterate_info.args.format = format;
1358	    iterate_info.format = &format;
1359
1360	    /* information for possible ~^ */
1361	    iterate_info.iteration =
1362		num_arguments > 0 ? ITERATION_NORMAL : ITERATION_LAST;
1363
1364	    /* do the format */
1365	    LispFormat(stream, &iterate_info);
1366
1367	    /* check for forced loop break */
1368	    if (iterate_info.upandout & UPANDOUT_HASH)
1369		break;
1370	}
1371    }
1372    /* ~:{...~} */
1373    else if (info->args.collon) {
1374	/* next argument is a list of lists */
1375
1376	LispObj *sarguments, *sobject;
1377	int snum_arguments;
1378
1379	/* fetch argument list, must exist */
1380	if (!CONSP(arguments))
1381	    parse_error(&(info->args), PARSE_NOARGSLEFT);
1382	sarguments = object = CAR(arguments);
1383	object = CAR(arguments);
1384	arguments = CDR(arguments);
1385	--num_arguments;
1386
1387	snum_arguments = 0;
1388	if (CONSP(object)) {
1389	    /* count arguments to format */
1390	    for (sobject = object; CONSP(sobject); sobject = CDR(sobject))
1391		++snum_arguments;
1392	}
1393	else
1394	    generic_error(&(info->args), GENERIC_BADLIST);
1395
1396	/* iterate */
1397	for (;; iterate++) {
1398	    /* if maximum iterations done or all arguments consumed */
1399	    if (has_max && iterate > iterate_max)
1400		break;
1401	    else if (snum_arguments == 0 && (!has_min || iterate > 0))
1402		break;
1403
1404	    /* fetch argument list, must exist */
1405	    if (!CONSP(sarguments))
1406		parse_error(&(info->args), PARSE_NOARGSLEFT);
1407	    iarguments = sobject = CAR(sarguments);
1408	    sobject = CAR(sarguments);
1409	    sarguments = CDR(sarguments);
1410	    --snum_arguments;
1411
1412	    inum_arguments = 0;
1413	    if (CONSP(object)) {
1414		/* count arguments to format */
1415		for (iobject = sobject; CONSP(iobject); iobject = CDR(iobject))
1416		    ++inum_arguments;
1417	    }
1418	    else if (sobject != NIL)
1419		generic_error(&(info->args), GENERIC_BADLIST);
1420
1421	    iobject = NIL;
1422
1423	    /* set new arguments to recursive calls */
1424	    iarguments = sobject;
1425	    iterate_info.base_arguments = iarguments;
1426	    iterate_info.total_arguments = inum_arguments;
1427	    iterate_info.object = &iobject;
1428	    iterate_info.arguments = &iarguments;
1429	    iterate_info.num_arguments = &inum_arguments;
1430
1431	    format = loop_format;
1432
1433	    /* set new format string */
1434	    iterate_info.args.base = iterate_info.args.format = format;
1435	    iterate_info.format = &format;
1436
1437	    /* information for possible ~^ */
1438	    iterate_info.iteration =
1439		snum_arguments > 0 ? ITERATION_NORMAL : ITERATION_LAST;
1440
1441	    /* do the format */
1442	    LispFormat(stream, &iterate_info);
1443
1444	    /* check for forced loop break */
1445	    if (iterate_info.upandout & UPANDOUT_HASH)
1446		break;
1447	}
1448    }
1449    /* ~@{...~} */
1450    else if (info->args.atsign) {
1451	/* current argument list is used */
1452
1453	/* set new arguments to recursive calls */
1454	iterate_info.base_arguments = info->base_arguments;
1455	iterate_info.total_arguments = info->total_arguments;
1456	iterate_info.object = &object;
1457	iterate_info.arguments = &arguments;
1458	iterate_info.num_arguments = &num_arguments;
1459
1460	for (;; iterate++) {
1461	    /* if maximum iterations done or all arguments consumed */
1462	    if (has_max && iterate > iterate_max)
1463		break;
1464	    else if (num_arguments == 0 && (!has_min || iterate > 0))
1465		break;
1466
1467	    format = loop_format;
1468
1469	    /* set new format string */
1470	    iterate_info.args.base = iterate_info.args.format = format;
1471	    iterate_info.format = &format;
1472
1473	    /* information for possible ~^, in this case ~:^ is a noop */
1474	    iterate_info.iteration = ITERATION_NORMAL;
1475
1476	    /* do the format */
1477	    LispFormat(stream, &iterate_info);
1478
1479	    /* check for forced loop break */
1480	    if (iterate_info.upandout & UPANDOUT_HASH)
1481		break;
1482	}
1483    }
1484
1485    /* free temporary memory */
1486    free_formats(formats, num_formats);
1487
1488    /* update anything that may have changed */
1489    *(info->object) = object;
1490    *(info->arguments) = arguments;
1491    *(info->num_arguments) = num_arguments;
1492
1493    /* this information always updated */
1494    *(info->format) = next_format;
1495}
1496
1497static void
1498format_justify(LispObj *stream, FmtInfo *info)
1499{
1500    GC_ENTER();
1501    FmtInfo justify_info;
1502    char **formats, *format, *next_format;
1503    const char *str;
1504    LispObj *string, *strings = NIL, *cons;
1505    int atsign = info->args.atsign,
1506	collon = info->args.collon,
1507	mincol = info->args.arguments[0].value,
1508	colinc = info->args.arguments[1].value,
1509	minpad = info->args.arguments[2].value,
1510	padchar = info->args.arguments[3].value;
1511    int i, k, total_length, length, padding, num_formats, has_default,
1512	comma_width, line_width, size, extra;
1513
1514    next_format = *(info->format);
1515
1516    /* list formats */
1517    list_formats(info, '<', &next_format, &formats, &num_formats,
1518		 &has_default, &comma_width, &line_width);
1519
1520    /* initialize list of strings streams */
1521    if (num_formats) {
1522	string = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0);
1523	strings = cons = CONS(string, NIL);
1524	GC_PROTECT(strings);
1525	for (i = 1; i < num_formats; i++) {
1526	    string = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0);
1527	    RPLACD(cons, CONS(string, NIL));
1528	    cons = CDR(cons);
1529	}
1530    }
1531
1532    /* most information is the same */
1533    memcpy(&justify_info, info, sizeof(FmtInfo));
1534
1535    /* loop formating strings */
1536    for (i = 0, cons = strings; i < num_formats; i++, cons = CDR(cons)) {
1537	/* set new format string */
1538	format = formats[i];
1539	justify_info.args.base = justify_info.args.format = format;
1540	justify_info.format = &format;
1541
1542	/* format string, maybe consuming arguments */
1543	LispFormat(CAR(cons), &justify_info);
1544
1545	/* if format was aborted, it is discarded */
1546	if (justify_info.upandout)
1547	    RPLACA(cons, NIL);
1548	/* check if the entire "main" iteration must be aborted */
1549	if (justify_info.upandout & UPANDOUT_COLLON) {
1550	    for (cons = CDR(cons); i < num_formats; i++, cons = CDR(cons))
1551		RPLACA(cons, NIL);
1552	    break;
1553	}
1554    }
1555
1556    /* free temporary format strings */
1557    free_formats(formats, num_formats);
1558
1559    /* remove aborted formats */
1560	/* first remove leading discarded formats */
1561    if (CAR(strings) == NIL) {
1562	while (CAR(strings) == NIL) {
1563	    strings = CDR(strings);
1564	    --num_formats;
1565	}
1566	/* keep strings gc protected, discarding first entries */
1567	lisp__data.protect.objects[gc__protect] = strings;
1568    }
1569	/* now remove intermediary discarded formats */
1570    cons = strings;
1571    while (CONSP(cons)) {
1572	if (CONSP(CDR(cons)) && CAR(CDR(cons)) == NIL) {
1573	    RPLACD(cons, CDR(CDR(cons)));
1574	    --num_formats;
1575	}
1576	else
1577	    cons = CDR(cons);
1578    }
1579
1580    /* calculate total length required for output */
1581    if (has_default)
1582	cons = CDR(strings);	/* if has_defaults, strings is surely a list */
1583    else
1584	cons = strings;
1585    for (total_length = 0; CONSP(cons); cons = CDR(cons))
1586	total_length += SSTREAMP(CAR(cons))->length;
1587
1588    /* initialize pointer to string streams */
1589    if (has_default)
1590	cons = CDR(strings);
1591    else
1592	cons = strings;
1593
1594    /* check if padding will need to be printed */
1595    extra = 0;
1596    padding = mincol - total_length;
1597    if (padding < 0)
1598	k = padding = 0;
1599    else {
1600	int num_fields = num_formats - (has_default != 0);
1601
1602	if (num_fields > 1) {
1603	    /* check if padding is distributed in num_fields or
1604	     * num_fields - 1 steps */
1605	    if (!collon)
1606		--num_fields;
1607	}
1608
1609	if (num_fields)
1610	    k = padding / num_fields;
1611	else
1612	    k = padding;
1613
1614	if (k <= 0)
1615	    k = colinc;
1616	else if (colinc)
1617	    k = k + (k % colinc);
1618	extra = mincol - (num_fields * k + total_length);
1619	if (extra < 0)
1620	    extra = 0;
1621    }
1622    if (padding && k < minpad) {
1623	k = minpad;
1624	if (colinc)
1625	    k = k + (k % colinc);
1626    }
1627
1628    /* first check for the special case of only one string being justified */
1629    if (num_formats - has_default == 1) {
1630	if (has_default && line_width > 0 && comma_width >= 0 &&
1631	    total_length + comma_width > line_width) {
1632	    str = LispGetSstring(SSTREAMP(CAR(strings)), &size);
1633	    LispWriteStr(stream, str, size);
1634	}
1635	string = has_default ? CAR(CDR(strings)) : CAR(strings);
1636	/* check if need left padding */
1637	if (k && !atsign) {
1638	    LispWriteChars(stream, padchar, k);
1639	    k = 0;
1640	}
1641	/* check for centralizing text */
1642	else if (k && atsign && collon) {
1643	    LispWriteChars(stream, padchar, k / 2 + ((k / 2) & 1));
1644	    k -= k / 2;
1645	}
1646	str = LispGetSstring(SSTREAMP(string), &size);
1647	LispWriteStr(stream, str, size);
1648	/* if any padding remaining */
1649	if (k)
1650	    LispWriteChars(stream, padchar, k);
1651    }
1652    else {
1653	LispObj *result;
1654	int last, spaces_before, padout;
1655
1656	/* if has default, need to check output length */
1657	if (has_default && line_width > 0 && comma_width >= 0) {
1658	    result = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0);
1659	    GC_PROTECT(result);
1660	}
1661	/* else write directly to stream */
1662	else
1663	    result = stream;
1664
1665	/* loop printing justified text */
1666	    /* padout controls padding for cases where padding is
1667	     * is separated in n-1 chunks, where n is the number of
1668	     * formatted strings.
1669	     */
1670	for (i = padout = 0; CONSP(cons); i++, cons = CDR(cons), --extra) {
1671	    string = CAR(cons);
1672	    last = !CONSP(CDR(cons));
1673
1674	    spaces_before = (i != 0 || collon) && (!last || !atsign);
1675
1676	    if (!spaces_before) {
1677		/* check for special case */
1678		if (last && atsign && collon && padding > 0) {
1679		    int spaces;
1680
1681		    spaces = minpad > colinc ? minpad : colinc;
1682		    LispWriteChars(result, padchar, spaces + (extra > 0));
1683		    k -= spaces;
1684		}
1685		str = LispGetSstring(SSTREAMP(string), &size);
1686		LispWriteStr(result, str, size);
1687		padout = 0;
1688	    }
1689	    if (!padout)
1690		LispWriteChars(result, padchar, k + (extra > 0));
1691	    padout = k;
1692	    /* if not first string, or if left padding specified */
1693	    if (spaces_before) {
1694		str = LispGetSstring(SSTREAMP(string), &size);
1695		LispWriteStr(result, str, size);
1696		padout = 0;
1697	    }
1698	    padding -= k;
1699	}
1700
1701	if (has_default && line_width > 0 && comma_width >= 0) {
1702	    length = SSTREAMP(result)->length + LispGetColumn(stream);
1703
1704	    /* if current line is too large */
1705	    if (has_default && length + comma_width > line_width) {
1706		str = LispGetSstring(SSTREAMP(CAR(strings)), &size);
1707		LispWriteStr(stream, str, size);
1708	    }
1709
1710	    /* write result to stream */
1711	    str = LispGetSstring(SSTREAMP(result), &size);
1712	    LispWriteStr(stream, str, size);
1713	}
1714    }
1715
1716    /* unprotect string streams from GC */
1717    GC_LEAVE();
1718
1719    /* this information always updated */
1720    *(info->format) = next_format;
1721}
1722
1723static void
1724LispFormat(LispObj *stream, FmtInfo *info)
1725{
1726    FmtArgs *args;
1727    const FmtDefs *defs = NULL;
1728    LispObj *object, *arguments;
1729    char stk[256], *format, *next_format;
1730    int length, num_arguments, code, need_update, need_argument, hash, head;
1731
1732    /* arguments that will be updated on function exit */
1733    format = *(info->format);
1734    object = *(info->object);
1735    arguments = *(info->arguments);
1736    num_arguments = *(info->num_arguments);
1737
1738    /* initialize */
1739    length = 0;
1740    args = &(info->args);
1741    info->upandout = 0;
1742
1743    while (*format) {
1744	if (*format == '~') {
1745	    /* flush non formatted characters */
1746	    if (length) {
1747		LispWriteStr(stream, stk, length);
1748		length = 0;
1749	    }
1750
1751	    need_argument = need_update = hash = 0;
1752
1753	    /* parse parameters */
1754	    args->format = format + 1;
1755	    next_format = parse_arguments(format + 1, args, &num_arguments,
1756					  &arguments, &code);
1757	    if (code != NOERROR)
1758		parse_error(args, code);
1759
1760	    /* check parameters */
1761	    switch (args->command) {
1762		case 'A': case 'S':
1763		    defs = &AsciiDefs;
1764		    break;
1765		case 'B': case 'O': case 'D': case 'X':
1766		    defs = &IntegerDefs;
1767		    break;
1768		case 'R':
1769		    defs = &RadixDefs;
1770		    break;
1771		case 'P': case 'C':
1772		    defs = &NoneDefs;
1773		    break;
1774		case 'F':
1775		    defs = &FixedFloatDefs;
1776		    break;
1777		case 'E': case 'G':
1778		    defs = &ExponentialFloatDefs;
1779		    break;
1780		case '$':
1781		    defs = &DollarFloatDefs;
1782		    break;
1783		case '%': case '&': case '|': case '~': case '\n':
1784		    defs = &OneDefs;
1785		    break;
1786		case 'T':
1787		    defs = &TabulateDefs;
1788		    break;
1789		case '*':
1790		    defs = &OneDefs;
1791		    break;
1792		case '?': case '(':
1793		    defs = &NoneDefs;
1794		    break;
1795		case ')':
1796		    /* this is never seen, processed in format_case_conversion */
1797		    format_error(args, "no match for directive ~)");
1798		case '[':
1799		    defs = &OneDefs;
1800		    break;
1801		case ']':
1802		    /* this is never seen, processed in format_conditional */
1803		    format_error(args, "no match for directive ~]");
1804		case '{':
1805		    defs = &OneDefs;
1806		    break;
1807		case '}':
1808		    /* this is never seen, processed in format_iterate */
1809		    format_error(args, "no match for directive ~}");
1810		case '<':
1811		    defs = &AsciiDefs;
1812		    break;
1813		case '>':
1814		    /* this is never seen, processed in format_justify */
1815		    format_error(args, "no match for directive ~>");
1816		case ';':
1817		    /* this is never seen here */
1818		    format_error(args, "misplaced directive ~;");
1819		case '#':
1820		    /* special handling for ~#^ */
1821		    if (*next_format == '^') {
1822			++next_format;
1823			hash = 1;
1824			defs = &NoneDefs;
1825			args->command = '^';
1826			break;
1827		    }
1828		    parse_error(args, PARSE_BADDIRECTIVE);
1829		case '^':
1830		    defs = &NoneDefs;
1831		    break;
1832		default:
1833		    parse_error(args, PARSE_BADDIRECTIVE);
1834		    break;
1835	    }
1836	    merge_arguments(args, defs, &code);
1837	    if (code != NOERROR)
1838		merge_error(args, code);
1839
1840	    /* check if an argument is required by directive */
1841	    switch (args->command) {
1842		case 'A': case 'S':
1843		case 'B': case 'O': case 'D': case 'X': case 'R':
1844		    need_argument = 1;
1845		    break;
1846		case 'P':
1847		    /* if collon specified, plural is the last print argument */
1848		    need_argument = !args->collon;
1849		    break;
1850		case 'C':
1851		    need_argument = 1;
1852		    break;
1853		case 'F': case 'E': case 'G': case '$':
1854		    need_argument = 1;
1855		    break;
1856		case '%': case '&': case '|': case '~': case '\n':
1857		    break;
1858		case 'T':
1859		    break;
1860		case '*':			/* check arguments below */
1861		    need_update = 1;
1862		    break;
1863		case '?':
1864		    need_argument = need_update = 1;
1865		    break;
1866		case '(': case '[': case '{': case '<':
1867		    need_update = 1;
1868		    break;
1869		case '^':
1870		    break;
1871	    }
1872	    if (need_argument) {
1873		if (!CONSP(arguments))
1874		    parse_error(args, PARSE_NOARGSLEFT);
1875		object = CAR(arguments);
1876		arguments = CDR(arguments);
1877		--num_arguments;
1878	    }
1879
1880	    /* will do recursive calls that change info */
1881	    if (need_update) {
1882		*(info->format) = next_format;
1883		*(info->object) = object;
1884		*(info->arguments) = arguments;
1885		*(info->num_arguments) = num_arguments;
1886	    }
1887
1888	    /* everything seens fine, print the format directive */
1889	    switch (args->command) {
1890		case 'A':
1891		    head = lisp__data.env.length;
1892		    LispAddVar(Oprint_escape, NIL);
1893		    ++lisp__data.env.head;
1894		    format_ascii(stream, object, args);
1895		    lisp__data.env.head = lisp__data.env.length = head;
1896		    break;
1897		case 'S':
1898		    head = lisp__data.env.length;
1899		    LispAddVar(Oprint_escape, T);
1900		    ++lisp__data.env.head;
1901		    format_ascii(stream, object, args);
1902		    lisp__data.env.head = lisp__data.env.length = head;
1903		    break;
1904		case 'B':
1905		    format_in_radix(stream, object, 2, args);
1906		    break;
1907		case 'O':
1908		    format_in_radix(stream, object, 8, args);
1909		    break;
1910		case 'D':
1911		    format_in_radix(stream, object, 10, args);
1912		    break;
1913		case 'X':
1914		    format_in_radix(stream, object, 16, args);
1915		    break;
1916		case 'R':
1917		    /* if a single argument specified */
1918		    if (args->count)
1919			format_in_radix(stream, object, 0, args);
1920		    else
1921			format_radix_special(stream, object, args);
1922		    break;
1923		case 'P':
1924		    if (args->atsign) {
1925			if (FIXNUMP(object) && FIXNUM_VALUE(object) == 1)
1926			    LispWriteChar(stream, 'y');
1927			else
1928			    LispWriteStr(stream, "ies", 3);
1929		    }
1930		    else if (!FIXNUMP(object) || FIXNUM_VALUE(object) != 1)
1931			LispWriteChar(stream, 's');
1932		    break;
1933		case 'C':
1934		    format_character(stream, object, args);
1935		    break;
1936		case 'F':
1937		    format_fixed_float(stream, object, args);
1938		    break;
1939		case 'E':
1940		    format_exponential_float(stream, object, args);
1941		    break;
1942		case 'G':
1943		    format_general_float(stream, object, args);
1944		    break;
1945		case '$':
1946		    format_dollar_float(stream, object, args);
1947		    break;
1948		case '&':
1949		    if (LispGetColumn(stream) == 0)
1950			--args->arguments[0].value;
1951		case '%':
1952		    LispWriteChars(stream, '\n', args->arguments[0].value);
1953		    break;
1954		case '|':
1955		    LispWriteChars(stream, '\f', args->arguments[0].value);
1956		    break;
1957		case '~':
1958		    LispWriteChars(stream, '~', args->arguments[0].value);
1959		    break;
1960		case '\n':
1961		    if (!args->collon) {
1962			if (args->atsign)
1963			    LispWriteChar(stream, '\n');
1964			/* ignore newline and following spaces */
1965			while (*next_format && isspace(*next_format))
1966			    ++next_format;
1967		    }
1968		    break;
1969		case 'T':
1970		    format_tabulate(stream, args);
1971		    break;
1972		case '*':
1973		    format_goto(info);
1974		    break;
1975		case '?':
1976		    format_indirection(stream, object, info);
1977		    need_update = 1;
1978		    break;
1979		case '(':
1980		    format_case_conversion(stream, info);
1981		    /* next_format if far from what is set now */
1982		    next_format = *(info->format);
1983		    break;
1984		case '[':
1985		    format_conditional(stream, info);
1986		    /* next_format if far from what is set now */
1987		    next_format = *(info->format);
1988		    break;
1989		case '{':
1990		    format_iterate(stream, info);
1991		    /* next_format if far from what is set now */
1992		    next_format = *(info->format);
1993		    break;
1994		case '<':
1995		    format_justify(stream, info);
1996		    /* next_format if far from what is set now */
1997		    next_format = *(info->format);
1998		    break;
1999		case '^':
2000		    if (args->collon) {
2001			if (hash && num_arguments == 0) {
2002			    info->upandout = UPANDOUT_HASH;
2003			    goto format_up_and_out;
2004			}
2005			if (info->iteration &&
2006			    info->iteration == ITERATION_NORMAL)
2007			/* not exactly an error, but in this case,
2008			 * command is ignored */
2009			    break;
2010			info->upandout = UPANDOUT_COLLON;
2011			goto format_up_and_out;
2012		    }
2013		    else if (num_arguments == 0) {
2014			info->upandout = UPANDOUT_NORMAL;
2015			goto format_up_and_out;
2016		    }
2017		    break;
2018	    }
2019
2020	    if (need_update) {
2021		object = *(info->object);
2022		arguments = *(info->arguments);
2023		num_arguments = *(info->num_arguments);
2024	    }
2025
2026	    format = next_format;
2027	}
2028	else {
2029	    if (length >= sizeof(stk)) {
2030		LispWriteStr(stream, stk, length);
2031		length = 0;
2032	    }
2033	    stk[length++] = *format++;
2034	}
2035    }
2036
2037    /* flush any peding output */
2038    if (length)
2039	LispWriteStr(stream, stk, length);
2040
2041format_up_and_out:
2042    /* update for recursive call */
2043    *(info->format) = format;
2044    *(info->object) = object;
2045    *(info->arguments) = arguments;
2046    *(info->num_arguments) = num_arguments;
2047}
2048
2049LispObj *
2050Lisp_Format(LispBuiltin *builtin)
2051/*
2052 format destination control-string &rest arguments
2053 */
2054{
2055    GC_ENTER();
2056    FmtInfo info;
2057    LispObj *object;
2058    char *control_string;
2059    int num_arguments;
2060
2061    LispObj *stream, *format, *arguments;
2062
2063    arguments = ARGUMENT(2);
2064    format = ARGUMENT(1);
2065    stream = ARGUMENT(0);
2066
2067    /* check format and stream */
2068    CHECK_STRING(format);
2069    if (stream == NIL) {	/* return a string */
2070	stream = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0);
2071	GC_PROTECT(stream);
2072    }
2073    else if (stream == T ||	/* print directly to *standard-output* */
2074	     stream == STANDARD_OUTPUT)
2075	stream = NIL;
2076    else {
2077	CHECK_STREAM(stream);
2078	if (!stream->data.stream.writable)
2079	    LispDestroy("%s: stream %s is not writable",
2080			STRFUN(builtin), STROBJ(stream));
2081    }
2082
2083    /* count number of arguments */
2084    for (object = arguments, num_arguments = 0; CONSP(object);
2085	 object = CDR(object), num_arguments++)
2086	;
2087
2088    /* initialize plural/argument info */
2089    object = NIL;
2090
2091    /* the format string */
2092    control_string = THESTR(format);
2093
2094    /* arguments to recursive calls */
2095    info.args.base = control_string;
2096    info.base_arguments = arguments;
2097    info.total_arguments = num_arguments;
2098    info.format = &control_string;
2099    info.object = &object;
2100    info.arguments = &arguments;
2101    info.num_arguments = &num_arguments;
2102    info.iteration = 0;
2103
2104    /* format arguments */
2105    LispFormat(stream, &info);
2106
2107    /* if printing to stdout */
2108    if (stream == NIL)
2109	LispFflush(Stdout);
2110    /* else if printing to string-stream, return a string */
2111    else if (stream->data.stream.type == LispStreamString) {
2112	int length;
2113	const char *string;
2114
2115	string = LispGetSstring(SSTREAMP(stream), &length);
2116	stream = LSTRING(string, length);
2117    }
2118
2119    GC_LEAVE();
2120
2121    return (stream);
2122}
2123