2 * Copyright (c) 2002 by The XFree86 Project, Inc.
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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
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
27 * Author: Paulo César Pereira de Andrade
30 /* $XFree86: xc/programs/xedit/lisp/format.c,v 1.29tsi Exp $ */
33 #include "lisp/write.h"
34 #include "lisp/format.h"
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 */
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 */
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 */
60 #define IF_SPECIFIED(arg) (arg).specified ? &((arg).value) : NULL
62 #define UPANDOUT_NORMAL 1
63 #define UPANDOUT_COLLON 2
64 #define UPANDOUT_HASH 4 /* only useful inside a ~{ iteration
65 * forces loop finalization. */
67 #define ITERATION_NORMAL 1
68 #define ITERATION_LAST 2
73 /* parameter to format */
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 */
81 /* information about format parameters */
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 */
89 FmtArg arguments[MAXFMT];
92 /* used for combining default format parameter values */
98 /* number of default format parameter values and defaults */
101 FmtDef defaults[MAXFMT];
104 /* used on recursive calls to LispFormat */
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 */
121 static void merge_arguments(FmtArgs*, FmtDefs*, int*);
122 static char *parse_arguments(char*, FmtArgs*, int*, LispObj**, int*);
123 static void merge_error(FmtArgs*, int);
124 static void parse_error(FmtArgs*, int);
125 static void generic_error(FmtArgs*, int);
126 static void format_error(FmtArgs*, char*);
128 static int format_object(LispObj*, LispObj*);
130 static void format_ascii(LispObj*, LispObj*, FmtArgs*);
131 static void format_in_radix(LispObj*, LispObj*, int, FmtArgs*);
132 static void format_radix_special(LispObj*, LispObj*, FmtArgs*);
133 static void format_roman(LispObj*, LispObj*, FmtArgs*);
134 static void format_english(LispObj*, LispObj*, FmtArgs*);
135 static void format_character(LispObj*, LispObj*, FmtArgs*);
136 static void format_fixed_float(LispObj*, LispObj*, FmtArgs*);
137 static void format_exponential_float(LispObj*, LispObj*, FmtArgs*);
138 static void format_general_float(LispObj*, LispObj*, FmtArgs*);
139 static void format_dollar_float(LispObj*, LispObj*, FmtArgs*);
140 static void format_tabulate(LispObj*, FmtArgs*);
142 static void format_goto(FmtInfo*);
143 static void format_indirection(LispObj*, LispObj*, FmtInfo*);
145 static void list_formats(FmtInfo*, int, char**, char***, int*, int*, int*, int*);
146 static void free_formats(char**, int);
148 static void format_case_conversion(LispObj*, FmtInfo*);
149 static void format_conditional(LispObj*, FmtInfo*);
150 static void format_iterate(LispObj*, FmtInfo*);
151 static void format_justify(LispObj*, FmtInfo*);
153 static void LispFormat(LispObj*, FmtInfo*);
158 static FmtDefs AsciiDefs = {
164 {1, ' '}, /* padchar */
168 static FmtDefs IntegerDefs = {
172 {1, ' '}, /* padchar */
173 {1, ','}, /* commachar */
174 {0, 3}, /* commainterval */
178 static FmtDefs RadixDefs = {
183 {1, ' '}, /* padchar */
184 {1, ','}, /* commachar */
185 {0, 3}, /* commainterval */
189 static FmtDefs NoneDefs = {
193 static FmtDefs FixedFloatDefs = {
199 {1, '\0'}, /* overflowchar */
200 {1, ' '}, /* padchar */
204 static FmtDefs ExponentialFloatDefs = {
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 */
219 static FmtDefs DollarFloatDefs = {
225 {1, ' '}, /* padchar */
229 static FmtDefs OneDefs = {
236 static FmtDefs TabulateDefs = {
244 extern LispObj *Oprint_escape;
250 merge_arguments(FmtArgs *arguments, FmtDefs *defaults, int *code)
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;
269 argument->specified = 0;
270 argument->achar = defaul->achar;
271 argument->value = defaul->value;
275 /* check if extra arguments were provided */
276 if (arguments->count > defaults->count)
280 /* the pointer arguments may be null, useful when just testing/parsing
281 * the directive parameters */
283 parse_arguments(char *format, FmtArgs *arguments,
284 int *num_objects, LispObj **objects, int *code)
289 unsigned int tmpcmd = 0;
292 test = objects == NULL || code == NULL || num_objects == NULL;
294 argument = &(arguments->arguments[0]);
295 arguments->atsign = arguments->collon = arguments->command = 0;
297 /* parse format parameters */
298 for (arguments->count = 0;; arguments->count++) {
299 arguments->offset = ptr - format + 1;
300 if (arguments->count >= MAXFMT) {
302 *code = PARSE_2MANYPARM;
305 if (*ptr == '\'') { /* character parameter value */
307 argument->achar = argument->specified = 1;
308 argument->value = *ptr++;
310 else if (*ptr == ',') { /* use default parameter value */
312 argument->specified = 0;
313 /* don't increment ptr, will be incremented below */
315 else if (*ptr == '#') { /* number of arguments is value */
318 argument->specified = 1;
320 argument->value = *num_objects;
322 else if (*ptr == 'v' ||
323 *ptr == 'V') { /* format object argument is value */
328 if (!CONSP(*objects)) {
329 *code = PARSE_NOARGSLEFT;
332 object = CAR((*objects));
333 if (FIXNUMP(object)) {
335 argument->specified = 1;
336 argument->value = FIXNUM_VALUE(object);
338 else if (SCHARP(object)) {
339 argument->achar = argument->specified = 1;
340 argument->value = SCHAR_VALUE(object);
343 *code = PARSE_BADFMTARG;
346 *objects = CDR(*objects);
350 else if (isdigit(*ptr) ||
351 *ptr == '-' || *ptr == '+') { /* integer parameter value */
355 argument->specified = 1;
356 if (!isdigit(*ptr)) {
357 sign = *ptr++ == '-';
361 if (!test && !isdigit(*ptr)) {
362 *code = PARSE_BADINTEGER;
365 argument->value = *ptr++ - '0';
366 while (isdigit(*ptr)) {
367 argument->value = (argument->value * 10) + (*ptr++ - '0');
368 if (argument->value > 65536) {
370 *code = PARSE_BADINTEGER;
376 argument->value = -argument->value;
378 else /* no more arguments to format */
384 /* remember offset of format parameter, for better error printing */
385 argument->offset = arguments->offset;
389 /* check for extra flags */
391 if (*ptr == '@') { /* check for special parameter atsign */
392 if (arguments->atsign) {
394 *code = PARSE_2MANYATS;
400 arguments->atsign = 1;
402 else if (*ptr == ':') { /* check for special parameter collon */
403 if (arguments->collon) {
405 *code = PARSE_2MANYCOLS;
411 arguments->collon = 1;
413 else /* next value is format command */
419 arguments->command = *ptr++;
420 tmpcmd = arguments->command;
422 arguments->command = toupper(tmpcmd);
429 parse_error(FmtArgs *args, int code)
431 static char *errors[] = {
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",
442 format_error(args, errors[code]);
446 merge_error(FmtArgs *args, int code)
448 static char *errors[] = {
450 "too many parameters to directive",
451 "argument must be a character",
452 "argument must be a fixnum integer",
455 format_error(args, errors[code]);
459 generic_error(FmtArgs *args, int code)
461 static char *errors[] = {
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",
469 format_error(args, errors[code]);
473 format_error(FmtArgs *args, char *str)
476 int errorlen, formatlen;
478 /* number of bytes of format to be printed */
479 formatlen = (args->format - args->base) + args->offset;
481 /* length of specific error message */
482 errorlen = strlen(str) + 1; /* plus '\n' */
484 /* XXX allocate string with LispMalloc,
485 * so that it will be freed in LispTopLevel */
486 message = LispMalloc(formatlen + errorlen + 1);
488 sprintf(message, "%s\n", str);
489 memcpy(message + errorlen, args->base, formatlen);
490 message[errorlen + formatlen] = '\0';
492 LispDestroy("FORMAT: %s", message);
496 format_object(LispObj *stream, LispObj *object)
500 length = LispWriteObject(stream, object);
506 format_ascii(LispObj *stream, LispObj *object, FmtArgs *args)
509 LispObj *string = NIL;
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;
518 /* check/correct arguments */
525 /* XXX pachar can be the null character? */
528 length = collon ? 2 : 3; /* () or NIL */
532 /* if length not yet known */
534 string = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0);
536 length = LispWriteObject(string, object);
539 /* output minpad characters at left */
542 LispWriteChars(stream, padchar, minpad);
546 /* puts colinc spaces at a time,
547 * until at least mincol chars out */
548 while (length < mincol) {
549 LispWriteChars(stream, padchar, colinc);
557 LispWriteStr(stream, "()", 2);
559 LispWriteStr(stream, Snil->value, 3);
562 /* if string is not NIL, atsign was specified
563 * and object printed to string */
565 length = format_object(stream, object);
568 char *str = LispGetSstring(SSTREAMP(string), &size);
570 LispWriteStr(stream, str, size);
576 /* output minpad characters at left */
579 LispWriteChars(stream, padchar, minpad);
582 /* puts colinc spaces at a time,
583 * until at least mincol chars out */
584 while (length < mincol) {
585 LispWriteChars(stream, padchar, colinc);
594 /* assumes radix is 0 or in range 2 - 36 */
596 format_in_radix(LispObj *stream, LispObj *object, int radix, FmtArgs *args)
598 if (INTEGERP(object)) {
599 int i, atsign, collon, mincol, padchar, commachar, commainterval;
602 atsign = args->atsign;
603 collon = args->collon;
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);
611 mincol = args->arguments[i++].value;
612 padchar = args->arguments[i++].value;
613 commachar = args->arguments[i++].value;
614 commainterval = args->arguments[i++].value;
616 LispFormatInteger(stream, object, radix, atsign, collon,
617 mincol, padchar, commachar, commainterval);
620 format_object(stream, object);
624 format_radix_special(LispObj *stream, LispObj *object, FmtArgs *args)
626 if (FIXNUMP(object)) {
628 format_roman(stream, object, args);
630 format_english(stream, object, args);
633 format_object(stream, object);
637 format_roman(LispObj *stream, LispObj *object, FmtArgs *args)
640 int cando, new_roman = args->collon == 0;
642 if (FIXNUMP(object)) {
643 value = FIXNUM_VALUE(object);
645 cando = value >= 1 && value <= 3999;
647 cando = value >= 1 && value <= 4999;
653 LispFormatRomanInteger(stream, value, new_roman);
655 format_object(stream, object);
659 format_english(LispObj *stream, LispObj *object, FmtArgs *args)
664 if (FIXNUMP(object)) {
665 number = FIXNUM_VALUE(object);
666 cando = number >= -999999999 && number <= 999999999;
672 LispFormatEnglishInteger(stream, number, args->collon);
674 format_object(stream, object);
678 format_character(LispObj *stream, LispObj *object, FmtArgs *args)
681 LispFormatCharacter(stream, object, args->atsign, args->collon);
683 format_object(stream, object);
687 format_fixed_float(LispObj *stream, LispObj *object, FmtArgs *args)
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);
697 format_object(stream, object);
701 format_exponential_float(LispObj *stream, LispObj *object, FmtArgs *args)
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);
713 format_object(stream, object);
717 format_general_float(LispObj *stream, LispObj *object, FmtArgs *args)
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);
729 format_object(stream, object);
733 format_dollar_float(LispObj *stream, LispObj *object, FmtArgs *args)
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);
743 format_object(stream, object);
747 format_tabulate(LispObj *stream, FmtArgs *args)
749 int atsign = args->atsign,
750 colnum = args->arguments[0].value,
751 colinc = args->arguments[1].value,
754 column = LispGetColumn(stream);
757 /* relative tabulation */
759 LispWriteChars(stream, ' ', colnum);
762 /* tabulate until at a multiple of colinc */
764 LispWriteChars(stream, ' ', colinc - (column % colinc));
767 /* if colinc not specified, just move to given column */
769 LispWriteChars(stream, ' ', column - colnum);
771 /* always output at least colinc spaces */
773 LispWriteChars(stream, ' ', colinc);
775 } while (colnum > column);
781 format_goto(FmtInfo *info)
783 int count, num_arguments;
784 LispObj *object, *arguments;
786 /* number of arguments to ignore or goto offset */
787 count = info->args.arguments[0].value;
789 generic_error(&(info->args), GENERIC_NEGATIVE);
791 if (info->args.atsign) {
794 /* if not specified, defaults to zero */
795 if (!(info->args.arguments[0].specified))
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 */
804 arguments = info->base_arguments;
805 num_arguments = info->total_arguments - count;
807 for (; count > 0; count--, arguments = CDR(arguments))
808 object = CAR(arguments);
810 /* update format information */
811 *(info->object) = object;
812 *(info->arguments) = arguments;
813 *(info->num_arguments) = num_arguments;
817 /* relative goto, ignore or go back count arguments */
819 /* prepare to update parameters */
820 arguments = *(info->arguments);
821 num_arguments = *(info->num_arguments);
823 /* go back count arguments? */
824 if (info->args.collon)
827 num_arguments -= count;
830 if (count > *(info->num_arguments))
831 parse_error(&(info->args), PARSE_NOARGSLEFT);
833 object = *(info->object);
834 for (; count > 0; count--, arguments = CDR(arguments))
835 object = CAR(arguments);
837 else { /* count < 0 */
838 if (info->total_arguments + count - *(info->num_arguments) < 0)
839 parse_error(&(info->args), PARSE_NOARGSLEFT);
842 arguments = info->base_arguments;
843 for (count = 0; count < info->total_arguments - num_arguments;
844 count++, arguments = CDR(arguments))
845 object = CAR(arguments);
848 /* update format parameters */
849 *(info->object) = object;
850 *(info->arguments) = arguments;
851 *(info->num_arguments) = num_arguments;
856 format_indirection(LispObj *stream, LispObj *format, FmtInfo *info)
860 FmtInfo indirect_info;
862 if (!STRINGP(format))
863 generic_error(&(info->args), GENERIC_BADSTRING);
864 string = THESTR(format);
866 /* most information is the same */
867 memcpy(&indirect_info, info, sizeof(FmtInfo));
869 /* set new format string */
870 indirect_info.args.base = indirect_info.args.format = string;
871 indirect_info.format = &string;
873 if (info->args.atsign) {
874 /* use current arguments */
876 /* do the indirect format */
877 LispFormat(stream, &indirect_info);
880 /* next argument is the recursive call arguments */
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));
891 if (!LISTP(object) || !CONSP(*(info->arguments)))
892 generic_error(&(info->args), GENERIC_BADLIST);
894 /* update information now */
895 *(info->object) = object;
896 *(info->arguments) = CDR(*(info->arguments));
897 *(info->num_arguments) -= 1;
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))
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;
911 /* do the indirect format */
912 LispFormat(stream, &indirect_info);
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
926 list_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)
930 /* instead of processing the directives recursively, just separate the
931 * input formats in separate strings, then see if one of then need to
934 int counters[] = { 0, 0, 0, 0};
935 /* '[', '(', '{', '<' */
936 char *format, *next_format, *start, **formats;
937 int num_formats, format_index, separator, add_format;
941 num_formats = format_index = 0;
942 if (has_default != NULL)
944 if (comma_width != NULL)
946 if (line_width != NULL)
948 format = start = next_format = *format_ptr;
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;
956 #define LIST_FORMATS_ADD 1
957 #define LIST_FORMATS_DONE 2
959 /* fill list of format options to conditional */
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;
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;
985 int length = format - start;
987 formats = LispRealloc(formats,
988 (num_formats + 1) * sizeof(char*));
990 formats[num_formats] = LispMalloc(length + 1);
991 strncpy(formats[num_formats], start, length);
992 formats[num_formats][length] = '\0';
995 if (add_format == LIST_FORMATS_DONE)
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
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) {
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;
1017 start = next_format;
1019 format = next_format;
1025 /* check if format string did not finish prematurely */
1026 if (counters[format_index] != 0) {
1027 char error_message[64];
1029 sprintf(error_message, "expecting ~%c", command);
1030 format_error(&(info->args), error_message);
1033 /* update pointers */
1034 *format_list = formats;
1035 *format_count = num_formats;
1036 *format_ptr = next_format;
1040 free_formats(char **formats, int num_formats)
1043 while (--num_formats >= 0)
1044 LispFree(formats[num_formats]);
1050 format_case_conversion(LispObj *stream, FmtInfo *info)
1056 char *format, *next_format, **formats;
1057 int atsign, collon, num_formats, length;
1059 atsign = info->args.atsign;
1060 collon = info->args.collon;
1062 /* output to a string, before case conversion */
1063 string = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0);
1066 /* most information is the same */
1067 memcpy(&case_info, info, sizeof(FmtInfo));
1070 next_format = *(info->format);
1071 list_formats(info, '(', &next_format, &formats, &num_formats,
1074 /* set new format string */
1075 format = formats[0];
1076 case_info.args.base = case_info.args.format = format;
1077 case_info.format = &format;
1079 /* format text to string */
1080 LispFormat(string, &case_info);
1082 str = ptr = LispGetSstring(SSTREAMP(string), &length);
1084 /* do case conversion */
1085 if (!atsign && !collon) {
1086 /* convert all upercase to lowercase */
1087 for (; *ptr; ptr++) {
1089 *ptr = tolower(*ptr);
1092 else if (atsign && collon) {
1093 /* convert all lowercase to upercase */
1094 for (; *ptr; ptr++) {
1096 *ptr = toupper(*ptr);
1102 /* skip non-alphanumeric characters */
1107 /* capitalize words */
1108 for (; *ptr; ptr++) {
1109 if (isalnum(*ptr)) {
1112 *ptr = toupper(*ptr);
1115 else if (isupper(*ptr))
1116 *ptr = tolower(*ptr);
1120 /* if collon, capitalize all words, else just first word */
1124 /* output case converted string */
1125 LispWriteStr(stream, str, length);
1127 /* temporary string stream is not necessary anymore */
1130 /* free temporary memory */
1131 free_formats(formats, num_formats);
1133 /* this information always updated */
1134 *(info->format) = next_format;
1138 format_conditional(LispObj *stream, FmtInfo *info)
1140 LispObj *object, *arguments;
1141 char *format, *next_format, **formats;
1142 int choice, num_formats, has_default, num_arguments;
1144 /* save information that may change */
1145 object = *(info->object);
1146 arguments = *(info->arguments);
1147 num_arguments = *(info->num_arguments);
1151 next_format = *(info->format);
1154 list_formats(info, '[',
1155 &next_format, &formats, &num_formats, &has_default, NULL, NULL);
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);
1165 choice = object == NIL ? 0 : 1;
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)
1175 object = CAR(arguments);
1176 arguments = CDR(arguments);
1181 else if (info->args.arguments[0].specified)
1182 /* no arguments consumed */
1183 choice = info->args.arguments[0].value;
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);
1192 /* no error if it isn't a number? */
1193 if (FIXNUMP(object))
1194 choice = FIXNUM_VALUE(object);
1197 /* update anything that may have changed */
1198 *(info->object) = object;
1199 *(info->arguments) = arguments;
1200 *(info->num_arguments) = num_arguments;
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;
1206 /* if one of the formats must be parsed */
1207 if (choice >= 0 && choice < num_formats) {
1208 FmtInfo conditional_info;
1210 /* most information is the same */
1211 memcpy(&conditional_info, info, sizeof(FmtInfo));
1213 /* set new format string */
1214 format = formats[choice];
1215 conditional_info.args.base = conditional_info.args.format = format;
1216 conditional_info.format = &format;
1218 /* do the conditional format */
1219 LispFormat(stream, &conditional_info);
1222 /* free temporary memory */
1223 free_formats(formats, num_formats);
1225 /* this information always updated */
1226 *(info->format) = next_format;
1230 format_iterate(LispObj *stream, FmtInfo *info)
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,
1238 /* save information that may change */
1239 object = *(info->object);
1240 arguments = *(info->arguments);
1241 num_arguments = *(info->num_arguments);
1244 iterate = has_min = 0;
1245 next_format = *(info->format);
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;
1252 list_formats(info, '{', &next_format, &formats, &num_formats,
1254 loop_format = formats[0];
1256 /* most information is the same */
1257 memcpy(&iterate_info, info, sizeof(FmtInfo));
1260 if (!info->args.atsign && !info->args.collon) {
1261 /* next argument is the argument list for the iteration */
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);
1272 if (CONSP(object)) {
1273 /* count arguments to format */
1274 for (iobject = object; CONSP(iobject); iobject = CDR(iobject))
1277 else if (object != NIL)
1278 generic_error(&(info->args), GENERIC_BADLIST);
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;
1291 for (;; iterate++) {
1292 /* if maximum iterations done or all arguments consumed */
1293 if (has_max && iterate > iterate_max)
1295 else if (inum_arguments == 0 && (!has_min || iterate > 0))
1298 format = loop_format;
1300 /* set new format string */
1301 iterate_info.args.base = iterate_info.args.format = format;
1302 iterate_info.format = &format;
1304 /* information for possible ~^, in this case ~:^ is a noop */
1305 iterate_info.iteration = ITERATION_NORMAL;
1308 LispFormat(stream, &iterate_info);
1310 /* check for forced loop break */
1311 if (iterate_info.upandout & UPANDOUT_HASH)
1316 else if (info->args.atsign && info->args.collon) {
1317 /* every following argument is the argument list for the iteration */
1320 for (;; iterate++) {
1321 /* if maximum iterations done or all arguments consumed */
1322 if (has_max && iterate > iterate_max)
1324 else if (num_arguments == 0 && (!has_min || iterate > 0))
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);
1336 if (CONSP(object)) {
1337 /* count arguments to format */
1338 for (iobject = object; CONSP(iobject); iobject = CDR(iobject))
1341 else if (object != NIL)
1342 generic_error(&(info->args), GENERIC_BADLIST);
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;
1354 format = loop_format;
1356 /* set new format string */
1357 iterate_info.args.base = iterate_info.args.format = format;
1358 iterate_info.format = &format;
1360 /* information for possible ~^ */
1361 iterate_info.iteration =
1362 num_arguments > 0 ? ITERATION_NORMAL : ITERATION_LAST;
1365 LispFormat(stream, &iterate_info);
1367 /* check for forced loop break */
1368 if (iterate_info.upandout & UPANDOUT_HASH)
1373 else if (info->args.collon) {
1374 /* next argument is a list of lists */
1376 LispObj *sarguments, *sobject;
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);
1388 if (CONSP(object)) {
1389 /* count arguments to format */
1390 for (sobject = object; CONSP(sobject); sobject = CDR(sobject))
1394 generic_error(&(info->args), GENERIC_BADLIST);
1397 for (;; iterate++) {
1398 /* if maximum iterations done or all arguments consumed */
1399 if (has_max && iterate > iterate_max)
1401 else if (snum_arguments == 0 && (!has_min || iterate > 0))
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);
1413 if (CONSP(object)) {
1414 /* count arguments to format */
1415 for (iobject = sobject; CONSP(iobject); iobject = CDR(iobject))
1418 else if (sobject != NIL)
1419 generic_error(&(info->args), GENERIC_BADLIST);
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;
1431 format = loop_format;
1433 /* set new format string */
1434 iterate_info.args.base = iterate_info.args.format = format;
1435 iterate_info.format = &format;
1437 /* information for possible ~^ */
1438 iterate_info.iteration =
1439 snum_arguments > 0 ? ITERATION_NORMAL : ITERATION_LAST;
1442 LispFormat(stream, &iterate_info);
1444 /* check for forced loop break */
1445 if (iterate_info.upandout & UPANDOUT_HASH)
1450 else if (info->args.atsign) {
1451 /* current argument list is used */
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;
1460 for (;; iterate++) {
1461 /* if maximum iterations done or all arguments consumed */
1462 if (has_max && iterate > iterate_max)
1464 else if (num_arguments == 0 && (!has_min || iterate > 0))
1467 format = loop_format;
1469 /* set new format string */
1470 iterate_info.args.base = iterate_info.args.format = format;
1471 iterate_info.format = &format;
1473 /* information for possible ~^, in this case ~:^ is a noop */
1474 iterate_info.iteration = ITERATION_NORMAL;
1477 LispFormat(stream, &iterate_info);
1479 /* check for forced loop break */
1480 if (iterate_info.upandout & UPANDOUT_HASH)
1485 /* free temporary memory */
1486 free_formats(formats, num_formats);
1488 /* update anything that may have changed */
1489 *(info->object) = object;
1490 *(info->arguments) = arguments;
1491 *(info->num_arguments) = num_arguments;
1493 /* this information always updated */
1494 *(info->format) = next_format;
1498 format_justify(LispObj *stream, FmtInfo *info)
1501 FmtInfo justify_info;
1502 char **formats, *format, *next_format, *str;
1503 LispObj *string, *strings = NIL, *cons;
1504 int atsign = info->args.atsign,
1505 collon = info->args.collon,
1506 mincol = info->args.arguments[0].value,
1507 colinc = info->args.arguments[1].value,
1508 minpad = info->args.arguments[2].value,
1509 padchar = info->args.arguments[3].value;
1510 int i, k, total_length, length, padding, num_formats, has_default,
1511 comma_width, line_width, size, extra;
1513 next_format = *(info->format);
1516 list_formats(info, '<', &next_format, &formats, &num_formats,
1517 &has_default, &comma_width, &line_width);
1519 /* initialize list of strings streams */
1521 string = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0);
1522 strings = cons = CONS(string, NIL);
1523 GC_PROTECT(strings);
1524 for (i = 1; i < num_formats; i++) {
1525 string = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0);
1526 RPLACD(cons, CONS(string, NIL));
1531 /* most information is the same */
1532 memcpy(&justify_info, info, sizeof(FmtInfo));
1534 /* loop formating strings */
1535 for (i = 0, cons = strings; i < num_formats; i++, cons = CDR(cons)) {
1536 /* set new format string */
1537 format = formats[i];
1538 justify_info.args.base = justify_info.args.format = format;
1539 justify_info.format = &format;
1541 /* format string, maybe consuming arguments */
1542 LispFormat(CAR(cons), &justify_info);
1544 /* if format was aborted, it is discarded */
1545 if (justify_info.upandout)
1547 /* check if the entire "main" iteration must be aborted */
1548 if (justify_info.upandout & UPANDOUT_COLLON) {
1549 for (cons = CDR(cons); i < num_formats; i++, cons = CDR(cons))
1555 /* free temporary format strings */
1556 free_formats(formats, num_formats);
1558 /* remove aborted formats */
1559 /* first remove leading discarded formats */
1560 if (CAR(strings) == NIL) {
1561 while (CAR(strings) == NIL) {
1562 strings = CDR(strings);
1565 /* keep strings gc protected, discarding first entries */
1566 lisp__data.protect.objects[gc__protect] = strings;
1568 /* now remove intermediary discarded formats */
1570 while (CONSP(cons)) {
1571 if (CONSP(CDR(cons)) && CAR(CDR(cons)) == NIL) {
1572 RPLACD(cons, CDR(CDR(cons)));
1579 /* calculate total length required for output */
1581 cons = CDR(strings); /* if has_defaults, strings is surely a list */
1584 for (total_length = 0; CONSP(cons); cons = CDR(cons))
1585 total_length += SSTREAMP(CAR(cons))->length;
1587 /* initialize pointer to string streams */
1589 cons = CDR(strings);
1593 /* check if padding will need to be printed */
1595 padding = mincol - total_length;
1599 int num_fields = num_formats - (has_default != 0);
1601 if (num_fields > 1) {
1602 /* check if padding is distributed in num_fields or
1603 * num_fields - 1 steps */
1609 k = padding / num_fields;
1616 k = k + (k % colinc);
1617 extra = mincol - (num_fields * k + total_length);
1621 if (padding && k < minpad) {
1624 k = k + (k % colinc);
1627 /* first check for the special case of only one string being justified */
1628 if (num_formats - has_default == 1) {
1629 if (has_default && line_width > 0 && comma_width >= 0 &&
1630 total_length + comma_width > line_width) {
1631 str = LispGetSstring(SSTREAMP(CAR(strings)), &size);
1632 LispWriteStr(stream, str, size);
1634 string = has_default ? CAR(CDR(strings)) : CAR(strings);
1635 /* check if need left padding */
1637 LispWriteChars(stream, padchar, k);
1640 /* check for centralizing text */
1641 else if (k && atsign && collon) {
1642 LispWriteChars(stream, padchar, k / 2 + ((k / 2) & 1));
1645 str = LispGetSstring(SSTREAMP(string), &size);
1646 LispWriteStr(stream, str, size);
1647 /* if any padding remaining */
1649 LispWriteChars(stream, padchar, k);
1653 int last, spaces_before, padout;
1655 /* if has default, need to check output length */
1656 if (has_default && line_width > 0 && comma_width >= 0) {
1657 result = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0);
1660 /* else write directly to stream */
1664 /* loop printing justified text */
1665 /* padout controls padding for cases where padding is
1666 * is separated in n-1 chunks, where n is the number of
1667 * formatted strings.
1669 for (i = padout = 0; CONSP(cons); i++, cons = CDR(cons), --extra) {
1671 last = !CONSP(CDR(cons));
1673 spaces_before = (i != 0 || collon) && (!last || !atsign);
1675 if (!spaces_before) {
1676 /* check for special case */
1677 if (last && atsign && collon && padding > 0) {
1680 spaces = minpad > colinc ? minpad : colinc;
1681 LispWriteChars(result, padchar, spaces + (extra > 0));
1684 str = LispGetSstring(SSTREAMP(string), &size);
1685 LispWriteStr(result, str, size);
1689 LispWriteChars(result, padchar, k + (extra > 0));
1691 /* if not first string, or if left padding specified */
1692 if (spaces_before) {
1693 str = LispGetSstring(SSTREAMP(string), &size);
1694 LispWriteStr(result, str, size);
1700 if (has_default && line_width > 0 && comma_width >= 0) {
1701 length = SSTREAMP(result)->length + LispGetColumn(stream);
1703 /* if current line is too large */
1704 if (has_default && length + comma_width > line_width) {
1705 str = LispGetSstring(SSTREAMP(CAR(strings)), &size);
1706 LispWriteStr(stream, str, size);
1709 /* write result to stream */
1710 str = LispGetSstring(SSTREAMP(result), &size);
1711 LispWriteStr(stream, str, size);
1715 /* unprotect string streams from GC */
1718 /* this information always updated */
1719 *(info->format) = next_format;
1723 LispFormat(LispObj *stream, FmtInfo *info)
1726 FmtDefs *defs = NULL;
1727 LispObj *object, *arguments;
1728 char stk[256], *format, *next_format;
1729 int length, num_arguments, code, need_update, need_argument, hash, head;
1731 /* arguments that will be updated on function exit */
1732 format = *(info->format);
1733 object = *(info->object);
1734 arguments = *(info->arguments);
1735 num_arguments = *(info->num_arguments);
1739 args = &(info->args);
1743 if (*format == '~') {
1744 /* flush non formatted characters */
1746 LispWriteStr(stream, stk, length);
1750 need_argument = need_update = hash = 0;
1752 /* parse parameters */
1753 args->format = format + 1;
1754 next_format = parse_arguments(format + 1, args, &num_arguments,
1756 if (code != NOERROR)
1757 parse_error(args, code);
1759 /* check parameters */
1760 switch (args->command) {
1764 case 'B': case 'O': case 'D': case 'X':
1765 defs = &IntegerDefs;
1774 defs = &FixedFloatDefs;
1777 defs = &ExponentialFloatDefs;
1780 defs = &DollarFloatDefs;
1782 case '%': case '&': case '|': case '~': case '\n':
1786 defs = &TabulateDefs;
1795 /* this is never seen, processed in format_case_conversion */
1796 format_error(args, "no match for directive ~)");
1801 /* this is never seen, processed in format_conditional */
1802 format_error(args, "no match for directive ~]");
1807 /* this is never seen, processed in format_iterate */
1808 format_error(args, "no match for directive ~}");
1813 /* this is never seen, processed in format_justify */
1814 format_error(args, "no match for directive ~>");
1816 /* this is never seen here */
1817 format_error(args, "misplaced directive ~;");
1819 /* special handling for ~#^ */
1820 if (*next_format == '^') {
1824 args->command = '^';
1827 parse_error(args, PARSE_BADDIRECTIVE);
1832 parse_error(args, PARSE_BADDIRECTIVE);
1835 merge_arguments(args, defs, &code);
1836 if (code != NOERROR)
1837 merge_error(args, code);
1839 /* check if an argument is required by directive */
1840 switch (args->command) {
1842 case 'B': case 'O': case 'D': case 'X': case 'R':
1846 /* if collon specified, plural is the last print argument */
1847 need_argument = !args->collon;
1852 case 'F': case 'E': case 'G': case '$':
1855 case '%': case '&': case '|': case '~': case '\n':
1859 case '*': /* check arguments below */
1863 need_argument = need_update = 1;
1865 case '(': case '[': case '{': case '<':
1871 if (need_argument) {
1872 if (!CONSP(arguments))
1873 parse_error(args, PARSE_NOARGSLEFT);
1874 object = CAR(arguments);
1875 arguments = CDR(arguments);
1879 /* will do recursive calls that change info */
1881 *(info->format) = next_format;
1882 *(info->object) = object;
1883 *(info->arguments) = arguments;
1884 *(info->num_arguments) = num_arguments;
1887 /* everything seens fine, print the format directive */
1888 switch (args->command) {
1890 head = lisp__data.env.length;
1891 LispAddVar(Oprint_escape, NIL);
1892 ++lisp__data.env.head;
1893 format_ascii(stream, object, args);
1894 lisp__data.env.head = lisp__data.env.length = head;
1897 head = lisp__data.env.length;
1898 LispAddVar(Oprint_escape, T);
1899 ++lisp__data.env.head;
1900 format_ascii(stream, object, args);
1901 lisp__data.env.head = lisp__data.env.length = head;
1904 format_in_radix(stream, object, 2, args);
1907 format_in_radix(stream, object, 8, args);
1910 format_in_radix(stream, object, 10, args);
1913 format_in_radix(stream, object, 16, args);
1916 /* if a single argument specified */
1918 format_in_radix(stream, object, 0, args);
1920 format_radix_special(stream, object, args);
1924 if (FIXNUMP(object) && FIXNUM_VALUE(object) == 1)
1925 LispWriteChar(stream, 'y');
1927 LispWriteStr(stream, "ies", 3);
1929 else if (!FIXNUMP(object) || FIXNUM_VALUE(object) != 1)
1930 LispWriteChar(stream, 's');
1933 format_character(stream, object, args);
1936 format_fixed_float(stream, object, args);
1939 format_exponential_float(stream, object, args);
1942 format_general_float(stream, object, args);
1945 format_dollar_float(stream, object, args);
1948 if (LispGetColumn(stream) == 0)
1949 --args->arguments[0].value;
1951 LispWriteChars(stream, '\n', args->arguments[0].value);
1954 LispWriteChars(stream, '\f', args->arguments[0].value);
1957 LispWriteChars(stream, '~', args->arguments[0].value);
1960 if (!args->collon) {
1962 LispWriteChar(stream, '\n');
1963 /* ignore newline and following spaces */
1964 while (*next_format && isspace(*next_format))
1969 format_tabulate(stream, args);
1975 format_indirection(stream, object, info);
1979 format_case_conversion(stream, info);
1980 /* next_format if far from what is set now */
1981 next_format = *(info->format);
1984 format_conditional(stream, info);
1985 /* next_format if far from what is set now */
1986 next_format = *(info->format);
1989 format_iterate(stream, info);
1990 /* next_format if far from what is set now */
1991 next_format = *(info->format);
1994 format_justify(stream, info);
1995 /* next_format if far from what is set now */
1996 next_format = *(info->format);
2000 if (hash && num_arguments == 0) {
2001 info->upandout = UPANDOUT_HASH;
2002 goto format_up_and_out;
2004 if (info->iteration &&
2005 info->iteration == ITERATION_NORMAL)
2006 /* not exactly an error, but in this case,
2007 * command is ignored */
2009 info->upandout = UPANDOUT_COLLON;
2010 goto format_up_and_out;
2012 else if (num_arguments == 0) {
2013 info->upandout = UPANDOUT_NORMAL;
2014 goto format_up_and_out;
2020 object = *(info->object);
2021 arguments = *(info->arguments);
2022 num_arguments = *(info->num_arguments);
2025 format = next_format;
2028 if (length >= sizeof(stk)) {
2029 LispWriteStr(stream, stk, length);
2032 stk[length++] = *format++;
2036 /* flush any peding output */
2038 LispWriteStr(stream, stk, length);
2041 /* update for recursive call */
2042 *(info->format) = format;
2043 *(info->object) = object;
2044 *(info->arguments) = arguments;
2045 *(info->num_arguments) = num_arguments;
2049 Lisp_Format(LispBuiltin *builtin)
2051 format destination control-string &rest arguments
2057 char *control_string;
2060 LispObj *stream, *format, *arguments;
2062 arguments = ARGUMENT(2);
2063 format = ARGUMENT(1);
2064 stream = ARGUMENT(0);
2066 /* check format and stream */
2067 CHECK_STRING(format);
2068 if (stream == NIL) { /* return a string */
2069 stream = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0);
2072 else if (stream == T || /* print directly to *standard-output* */
2073 stream == STANDARD_OUTPUT)
2076 CHECK_STREAM(stream);
2077 if (!stream->data.stream.writable)
2078 LispDestroy("%s: stream %s is not writable",
2079 STRFUN(builtin), STROBJ(stream));
2082 /* count number of arguments */
2083 for (object = arguments, num_arguments = 0; CONSP(object);
2084 object = CDR(object), num_arguments++)
2087 /* initialize plural/argument info */
2090 /* the format string */
2091 control_string = THESTR(format);
2093 /* arguments to recursive calls */
2094 info.args.base = control_string;
2095 info.base_arguments = arguments;
2096 info.total_arguments = num_arguments;
2097 info.format = &control_string;
2098 info.object = &object;
2099 info.arguments = &arguments;
2100 info.num_arguments = &num_arguments;
2103 /* format arguments */
2104 LispFormat(stream, &info);
2106 /* if printing to stdout */
2109 /* else if printing to string-stream, return a string */
2110 else if (stream->data.stream.type == LispStreamString) {
2114 string = LispGetSstring(SSTREAMP(stream), &length);
2115 stream = LSTRING(string, length);