15dfecf96Smrg/* 25dfecf96Smrg * Copyright (c) 2002 by The XFree86 Project, Inc. 35dfecf96Smrg * 45dfecf96Smrg * Permission is hereby granted, free of charge, to any person obtaining a 55dfecf96Smrg * copy of this software and associated documentation files (the "Software"), 65dfecf96Smrg * to deal in the Software without restriction, including without limitation 75dfecf96Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 85dfecf96Smrg * and/or sell copies of the Software, and to permit persons to whom the 95dfecf96Smrg * Software is furnished to do so, subject to the following conditions: 105dfecf96Smrg * 115dfecf96Smrg * The above copyright notice and this permission notice shall be included in 125dfecf96Smrg * all copies or substantial portions of the Software. 135dfecf96Smrg * 145dfecf96Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 155dfecf96Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 165dfecf96Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 175dfecf96Smrg * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 185dfecf96Smrg * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 195dfecf96Smrg * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 205dfecf96Smrg * SOFTWARE. 215dfecf96Smrg * 225dfecf96Smrg * Except as contained in this notice, the name of the XFree86 Project shall 235dfecf96Smrg * not be used in advertising or otherwise to promote the sale, use or other 245dfecf96Smrg * dealings in this Software without prior written authorization from the 255dfecf96Smrg * XFree86 Project. 265dfecf96Smrg * 275dfecf96Smrg * Author: Paulo César Pereira de Andrade 285dfecf96Smrg */ 295dfecf96Smrg 305dfecf96Smrg/* $XFree86: xc/programs/xedit/lisp/format.c,v 1.29tsi Exp $ */ 315dfecf96Smrg 325dfecf96Smrg#include "lisp/io.h" 335dfecf96Smrg#include "lisp/write.h" 345dfecf96Smrg#include "lisp/format.h" 355dfecf96Smrg#include <ctype.h> 365dfecf96Smrg 375dfecf96Smrg#define MAXFMT 8 385dfecf96Smrg#define NOERROR 0 395dfecf96Smrg 405dfecf96Smrg/* parse error codes */ 415dfecf96Smrg#define PARSE_2MANYPARM 1 /* too many directive parameters */ 425dfecf96Smrg#define PARSE_2MANYATS 2 /* more than one @ in directive */ 435dfecf96Smrg#define PARSE_2MANYCOLS 3 /* more than one : in directive */ 445dfecf96Smrg#define PARSE_NOARGSLEFT 4 /* no arguments left to format */ 455dfecf96Smrg#define PARSE_BADFMTARG 5 /* argument is not an integer or char */ 465dfecf96Smrg#define PARSE_BADDIRECTIVE 6 /* unknown format directive */ 475dfecf96Smrg#define PARSE_BADINTEGER 7 /* bad integer representation */ 485dfecf96Smrg 495dfecf96Smrg/* merge error codes */ 505dfecf96Smrg#define MERGE_2MANY 1 /* too many parameters to directive */ 515dfecf96Smrg#define MERGE_NOCHAR 2 /* parameter must be a character */ 525dfecf96Smrg#define MERGE_NOINT 3 /* parameter must be an integer */ 535dfecf96Smrg 545dfecf96Smrg/* generic error codes */ 555dfecf96Smrg#define GENERIC_RADIX 1 /* radix not in range 2-36 */ 565dfecf96Smrg#define GENERIC_NEGATIVE 2 /* parameter is negative */ 575dfecf96Smrg#define GENERIC_BADSTRING 3 /* argument is not a string */ 585dfecf96Smrg#define GENERIC_BADLIST 4 /* argument is not a list */ 595dfecf96Smrg 605dfecf96Smrg#define IF_SPECIFIED(arg) (arg).specified ? &((arg).value) : NULL 615dfecf96Smrg 625dfecf96Smrg#define UPANDOUT_NORMAL 1 635dfecf96Smrg#define UPANDOUT_COLLON 2 645dfecf96Smrg#define UPANDOUT_HASH 4 /* only useful inside a ~{ iteration 655dfecf96Smrg * forces loop finalization. */ 665dfecf96Smrg 675dfecf96Smrg#define ITERATION_NORMAL 1 685dfecf96Smrg#define ITERATION_LAST 2 695dfecf96Smrg 705dfecf96Smrg/* 715dfecf96Smrg * Types 725dfecf96Smrg */ 735dfecf96Smrg/* parameter to format */ 745dfecf96Smrgtypedef struct { 755dfecf96Smrg unsigned int achar : 1; /* value was specified as a character */ 765dfecf96Smrg unsigned int specified : 1; /* set if value was specified */ 775dfecf96Smrg unsigned int offset : 30; /* offset in format string, for error printing */ 785dfecf96Smrg int value; 795dfecf96Smrg} FmtArg; 805dfecf96Smrg 815dfecf96Smrg/* information about format parameters */ 825dfecf96Smrgtypedef struct { 835dfecf96Smrg unsigned int atsign : 1; /* @ specified */ 845dfecf96Smrg unsigned int collon : 1; /* : specified */ 855dfecf96Smrg unsigned int command : 8; /* the format command */ 865dfecf96Smrg unsigned int count : 4; /* number of arguments processed */ 875dfecf96Smrg unsigned int offset : 10; /* offset in format string, for error printing */ 885dfecf96Smrg char *base, *format; 895dfecf96Smrg FmtArg arguments[MAXFMT]; 905dfecf96Smrg} FmtArgs; 915dfecf96Smrg 925dfecf96Smrg/* used for combining default format parameter values */ 935dfecf96Smrgtypedef struct { 945dfecf96Smrg int achar; 955dfecf96Smrg int value; 965dfecf96Smrg} FmtDef; 975dfecf96Smrg 985dfecf96Smrg/* number of default format parameter values and defaults */ 995dfecf96Smrgtypedef struct { 1005dfecf96Smrg int count; 1015dfecf96Smrg FmtDef defaults[MAXFMT]; 1025dfecf96Smrg} FmtDefs; 1035dfecf96Smrg 1045dfecf96Smrg/* used on recursive calls to LispFormat */ 1055dfecf96Smrgtypedef struct { 1065dfecf96Smrg FmtArgs args; 1075dfecf96Smrg LispObj *base_arguments; /* pointer to first format argument */ 1085dfecf96Smrg int total_arguments; /* number of objects in base_arguments */ 1095dfecf96Smrg char **format; /* if need to update format string pointer */ 1105dfecf96Smrg LispObj **object; /* CAR(arguments), for plural check */ 1115dfecf96Smrg LispObj **arguments; /* current element of base_arguments */ 1125dfecf96Smrg int *num_arguments; /* number of arguments after arguments */ 1135dfecf96Smrg int upandout; /* information for recursive calls */ 1145dfecf96Smrg int iteration; /* only set if in ~:{... or ~:@{ and in the 1155dfecf96Smrg * last argument list, hint for upandout */ 1165dfecf96Smrg} FmtInfo; 1175dfecf96Smrg 1185dfecf96Smrg/* 1195dfecf96Smrg * Prototypes 1205dfecf96Smrg */ 121f765521fSmrgstatic void merge_arguments(FmtArgs*, const FmtDefs*, int*); 1225dfecf96Smrgstatic char *parse_arguments(char*, FmtArgs*, int*, LispObj**, int*); 1235dfecf96Smrgstatic void merge_error(FmtArgs*, int); 1245dfecf96Smrgstatic void parse_error(FmtArgs*, int); 1255dfecf96Smrgstatic void generic_error(FmtArgs*, int); 126f765521fSmrgstatic void format_error(FmtArgs*, const char*); 1275dfecf96Smrg 1285dfecf96Smrgstatic int format_object(LispObj*, LispObj*); 1295dfecf96Smrg 1305dfecf96Smrgstatic void format_ascii(LispObj*, LispObj*, FmtArgs*); 1315dfecf96Smrgstatic void format_in_radix(LispObj*, LispObj*, int, FmtArgs*); 1325dfecf96Smrgstatic void format_radix_special(LispObj*, LispObj*, FmtArgs*); 1335dfecf96Smrgstatic void format_roman(LispObj*, LispObj*, FmtArgs*); 1345dfecf96Smrgstatic void format_english(LispObj*, LispObj*, FmtArgs*); 1355dfecf96Smrgstatic void format_character(LispObj*, LispObj*, FmtArgs*); 1365dfecf96Smrgstatic void format_fixed_float(LispObj*, LispObj*, FmtArgs*); 1375dfecf96Smrgstatic void format_exponential_float(LispObj*, LispObj*, FmtArgs*); 1385dfecf96Smrgstatic void format_general_float(LispObj*, LispObj*, FmtArgs*); 1395dfecf96Smrgstatic void format_dollar_float(LispObj*, LispObj*, FmtArgs*); 1405dfecf96Smrgstatic void format_tabulate(LispObj*, FmtArgs*); 1415dfecf96Smrg 1425dfecf96Smrgstatic void format_goto(FmtInfo*); 1435dfecf96Smrgstatic void format_indirection(LispObj*, LispObj*, FmtInfo*); 1445dfecf96Smrg 1455dfecf96Smrgstatic void list_formats(FmtInfo*, int, char**, char***, int*, int*, int*, int*); 1465dfecf96Smrgstatic void free_formats(char**, int); 1475dfecf96Smrg 1485dfecf96Smrgstatic void format_case_conversion(LispObj*, FmtInfo*); 1495dfecf96Smrgstatic void format_conditional(LispObj*, FmtInfo*); 1505dfecf96Smrgstatic void format_iterate(LispObj*, FmtInfo*); 1515dfecf96Smrgstatic void format_justify(LispObj*, FmtInfo*); 1525dfecf96Smrg 1535dfecf96Smrgstatic void LispFormat(LispObj*, FmtInfo*); 1545dfecf96Smrg 1555dfecf96Smrg/* 1565dfecf96Smrg * Initialization 1575dfecf96Smrg */ 158f765521fSmrgstatic const FmtDefs AsciiDefs = { 1595dfecf96Smrg 4, 1605dfecf96Smrg { 1615dfecf96Smrg {0, 0}, /* mincol */ 1625dfecf96Smrg {0, 1}, /* colinc */ 1635dfecf96Smrg {0, 0}, /* minpad */ 1645dfecf96Smrg {1, ' '}, /* padchar */ 1655dfecf96Smrg }, 1665dfecf96Smrg}; 1675dfecf96Smrg 168f765521fSmrgstatic const FmtDefs IntegerDefs = { 1695dfecf96Smrg 4, 1705dfecf96Smrg { 1715dfecf96Smrg {0, 0}, /* mincol */ 1725dfecf96Smrg {1, ' '}, /* padchar */ 1735dfecf96Smrg {1, ','}, /* commachar */ 1745dfecf96Smrg {0, 3}, /* commainterval */ 1755dfecf96Smrg }, 1765dfecf96Smrg}; 1775dfecf96Smrg 178f765521fSmrgstatic const FmtDefs RadixDefs = { 1795dfecf96Smrg 5, 1805dfecf96Smrg { 1815dfecf96Smrg {0, 10}, /* radix */ 1825dfecf96Smrg {0, 0}, /* mincol */ 1835dfecf96Smrg {1, ' '}, /* padchar */ 1845dfecf96Smrg {1, ','}, /* commachar */ 1855dfecf96Smrg {0, 3}, /* commainterval */ 1865dfecf96Smrg }, 1875dfecf96Smrg}; 1885dfecf96Smrg 189f765521fSmrgstatic const FmtDefs NoneDefs = { 1905dfecf96Smrg 0, 1915dfecf96Smrg}; 1925dfecf96Smrg 193f765521fSmrgstatic const FmtDefs FixedFloatDefs = { 1945dfecf96Smrg 5, 1955dfecf96Smrg { 1965dfecf96Smrg {0, 0}, /* w */ 1975dfecf96Smrg {0, 16}, /* d */ 1985dfecf96Smrg {0, 0}, /* k */ 1995dfecf96Smrg {1, '\0'}, /* overflowchar */ 2005dfecf96Smrg {1, ' '}, /* padchar */ 2015dfecf96Smrg }, 2025dfecf96Smrg}; 2035dfecf96Smrg 204f765521fSmrgstatic const FmtDefs ExponentialFloatDefs = { 2055dfecf96Smrg 7, 2065dfecf96Smrg { 2075dfecf96Smrg {0, 0}, /* w */ 2085dfecf96Smrg {0, 16}, /* d */ 2095dfecf96Smrg {0, 0}, /* e */ 2105dfecf96Smrg {0, 1}, /* k */ 2115dfecf96Smrg {1, '\0'}, /* overflowchar */ 2125dfecf96Smrg {1, ' '}, /* padchar */ 2135dfecf96Smrg {1, 'E'}, /* exponentchar */ 2145dfecf96Smrg /* XXX if/when more than one float format, 2155dfecf96Smrg * should default to object type */ 2165dfecf96Smrg }, 2175dfecf96Smrg}; 2185dfecf96Smrg 219f765521fSmrgstatic const FmtDefs DollarFloatDefs = { 2205dfecf96Smrg 4, 2215dfecf96Smrg { 2225dfecf96Smrg {0, 2}, /* d */ 2235dfecf96Smrg {0, 1}, /* n */ 2245dfecf96Smrg {0, 0}, /* w */ 2255dfecf96Smrg {1, ' '}, /* padchar */ 2265dfecf96Smrg }, 2275dfecf96Smrg}; 2285dfecf96Smrg 229f765521fSmrgstatic const FmtDefs OneDefs = { 2305dfecf96Smrg 1, 2315dfecf96Smrg { 2325dfecf96Smrg {0, 1}, 2335dfecf96Smrg }, 2345dfecf96Smrg}; 2355dfecf96Smrg 236f765521fSmrgstatic const FmtDefs TabulateDefs = { 2375dfecf96Smrg 2, 2385dfecf96Smrg { 2395dfecf96Smrg {0, 0}, /* colnum */ 2405dfecf96Smrg {0, 1}, /* colinc */ 2415dfecf96Smrg }, 2425dfecf96Smrg}; 2435dfecf96Smrg 2445dfecf96Smrgextern LispObj *Oprint_escape; 2455dfecf96Smrg 2465dfecf96Smrg/* 2475dfecf96Smrg * Implementation 2485dfecf96Smrg */ 2495dfecf96Smrgstatic void 250f765521fSmrgmerge_arguments(FmtArgs *arguments, const FmtDefs *defaults, int *code) 2515dfecf96Smrg{ 2525dfecf96Smrg int count; 253f765521fSmrg const FmtDef *defaul; 2545dfecf96Smrg FmtArg *argument; 2555dfecf96Smrg 2565dfecf96Smrg defaul = &(defaults->defaults[0]); 2575dfecf96Smrg argument = &(arguments->arguments[0]); 2585dfecf96Smrg for (count = 0; count < defaults->count; count++, argument++, defaul++) { 2595dfecf96Smrg if (count >= arguments->count) 2605dfecf96Smrg argument->specified = 0; 2615dfecf96Smrg if (argument->specified) { 2625dfecf96Smrg if (argument->achar != defaul->achar) { 2635dfecf96Smrg *code = defaul->achar ? MERGE_NOCHAR : MERGE_NOINT; 2645dfecf96Smrg arguments->offset = argument->offset; 2655dfecf96Smrg return; 2665dfecf96Smrg } 2675dfecf96Smrg } 2685dfecf96Smrg else { 2695dfecf96Smrg argument->specified = 0; 2705dfecf96Smrg argument->achar = defaul->achar; 2715dfecf96Smrg argument->value = defaul->value; 2725dfecf96Smrg } 2735dfecf96Smrg } 2745dfecf96Smrg 2755dfecf96Smrg /* check if extra arguments were provided */ 2765dfecf96Smrg if (arguments->count > defaults->count) 2775dfecf96Smrg *code = MERGE_2MANY; 2785dfecf96Smrg} 2795dfecf96Smrg 2805dfecf96Smrg/* the pointer arguments may be null, useful when just testing/parsing 2815dfecf96Smrg * the directive parameters */ 2825dfecf96Smrgstatic char * 2835dfecf96Smrgparse_arguments(char *format, FmtArgs *arguments, 2845dfecf96Smrg int *num_objects, LispObj **objects, int *code) 2855dfecf96Smrg{ 2865dfecf96Smrg int test; 2875dfecf96Smrg char *ptr; 2885dfecf96Smrg FmtArg *argument; 2895dfecf96Smrg unsigned int tmpcmd = 0; 2905dfecf96Smrg 2915dfecf96Smrg /* initialize */ 2925dfecf96Smrg test = objects == NULL || code == NULL || num_objects == NULL; 2935dfecf96Smrg ptr = format; 2945dfecf96Smrg argument = &(arguments->arguments[0]); 2955dfecf96Smrg arguments->atsign = arguments->collon = arguments->command = 0; 2965dfecf96Smrg 2975dfecf96Smrg /* parse format parameters */ 2985dfecf96Smrg for (arguments->count = 0;; arguments->count++) { 2995dfecf96Smrg arguments->offset = ptr - format + 1; 3005dfecf96Smrg if (arguments->count >= MAXFMT) { 3015dfecf96Smrg if (!test) 3025dfecf96Smrg *code = PARSE_2MANYPARM; 3035dfecf96Smrg return (ptr); 3045dfecf96Smrg } 3055dfecf96Smrg if (*ptr == '\'') { /* character parameter value */ 3065dfecf96Smrg ++ptr; /* skip ' */ 3075dfecf96Smrg argument->achar = argument->specified = 1; 3085dfecf96Smrg argument->value = *ptr++; 3095dfecf96Smrg } 3105dfecf96Smrg else if (*ptr == ',') { /* use default parameter value */ 3115dfecf96Smrg argument->achar = 0; 3125dfecf96Smrg argument->specified = 0; 3135dfecf96Smrg /* don't increment ptr, will be incremented below */ 3145dfecf96Smrg } 3155dfecf96Smrg else if (*ptr == '#') { /* number of arguments is value */ 3165dfecf96Smrg ++ptr; /* skip # */ 3175dfecf96Smrg argument->achar = 0; 3185dfecf96Smrg argument->specified = 1; 3195dfecf96Smrg if (!test) 3205dfecf96Smrg argument->value = *num_objects; 3215dfecf96Smrg } 3225dfecf96Smrg else if (*ptr == 'v' || 3235dfecf96Smrg *ptr == 'V') { /* format object argument is value */ 3245dfecf96Smrg LispObj *object; 3255dfecf96Smrg 3265dfecf96Smrg ++ptr; /* skip V */ 3275dfecf96Smrg if (!test) { 3285dfecf96Smrg if (!CONSP(*objects)) { 3295dfecf96Smrg *code = PARSE_NOARGSLEFT; 3305dfecf96Smrg return (ptr); 3315dfecf96Smrg } 3325dfecf96Smrg object = CAR((*objects)); 3335dfecf96Smrg if (FIXNUMP(object)) { 3345dfecf96Smrg argument->achar = 0; 3355dfecf96Smrg argument->specified = 1; 3365dfecf96Smrg argument->value = FIXNUM_VALUE(object); 3375dfecf96Smrg } 3385dfecf96Smrg else if (SCHARP(object)) { 3395dfecf96Smrg argument->achar = argument->specified = 1; 3405dfecf96Smrg argument->value = SCHAR_VALUE(object); 3415dfecf96Smrg } 3425dfecf96Smrg else { 3435dfecf96Smrg *code = PARSE_BADFMTARG; 3445dfecf96Smrg return (ptr); 3455dfecf96Smrg } 3465dfecf96Smrg *objects = CDR(*objects); 3475dfecf96Smrg --*num_objects; 3485dfecf96Smrg } 3495dfecf96Smrg } 3505dfecf96Smrg else if (isdigit(*ptr) || 3515dfecf96Smrg *ptr == '-' || *ptr == '+') { /* integer parameter value */ 3525dfecf96Smrg int sign; 3535dfecf96Smrg 3545dfecf96Smrg argument->achar = 0; 3555dfecf96Smrg argument->specified = 1; 3565dfecf96Smrg if (!isdigit(*ptr)) { 3575dfecf96Smrg sign = *ptr++ == '-'; 3585dfecf96Smrg } 3595dfecf96Smrg else 3605dfecf96Smrg sign = 0; 3615dfecf96Smrg if (!test && !isdigit(*ptr)) { 3625dfecf96Smrg *code = PARSE_BADINTEGER; 3635dfecf96Smrg return (ptr); 3645dfecf96Smrg } 3655dfecf96Smrg argument->value = *ptr++ - '0'; 3665dfecf96Smrg while (isdigit(*ptr)) { 3675dfecf96Smrg argument->value = (argument->value * 10) + (*ptr++ - '0'); 3685dfecf96Smrg if (argument->value > 65536) { 3695dfecf96Smrg if (!test) { 3705dfecf96Smrg *code = PARSE_BADINTEGER; 3715dfecf96Smrg return (ptr); 3725dfecf96Smrg } 3735dfecf96Smrg } 3745dfecf96Smrg } 3755dfecf96Smrg if (sign) 3765dfecf96Smrg argument->value = -argument->value; 3775dfecf96Smrg } 3785dfecf96Smrg else /* no more arguments to format */ 3795dfecf96Smrg break; 3805dfecf96Smrg 3815dfecf96Smrg if (*ptr == ',') 3825dfecf96Smrg ++ptr; 3835dfecf96Smrg 3845dfecf96Smrg /* remember offset of format parameter, for better error printing */ 3855dfecf96Smrg argument->offset = arguments->offset; 3865dfecf96Smrg argument++; 3875dfecf96Smrg } 3885dfecf96Smrg 3895dfecf96Smrg /* check for extra flags */ 3905dfecf96Smrg for (;;) { 3915dfecf96Smrg if (*ptr == '@') { /* check for special parameter atsign */ 3925dfecf96Smrg if (arguments->atsign) { 3935dfecf96Smrg if (!test) { 3945dfecf96Smrg *code = PARSE_2MANYATS; 3955dfecf96Smrg return (ptr); 3965dfecf96Smrg } 3975dfecf96Smrg } 3985dfecf96Smrg ++ptr; 3995dfecf96Smrg ++arguments->offset; 4005dfecf96Smrg arguments->atsign = 1; 4015dfecf96Smrg } 4025dfecf96Smrg else if (*ptr == ':') { /* check for special parameter collon */ 4035dfecf96Smrg if (arguments->collon) { 4045dfecf96Smrg if (!test) { 4055dfecf96Smrg *code = PARSE_2MANYCOLS; 4065dfecf96Smrg return (ptr); 4075dfecf96Smrg } 4085dfecf96Smrg } 4095dfecf96Smrg ++ptr; 4105dfecf96Smrg ++arguments->offset; 4115dfecf96Smrg arguments->collon = 1; 4125dfecf96Smrg } 4135dfecf96Smrg else /* next value is format command */ 4145dfecf96Smrg break; 4155dfecf96Smrg } 4165dfecf96Smrg 4175dfecf96Smrg if (!test) 4185dfecf96Smrg *code = NOERROR; 4195dfecf96Smrg arguments->command = *ptr++; 4205dfecf96Smrg tmpcmd = arguments->command; 4215dfecf96Smrg if (islower(tmpcmd)) 4225dfecf96Smrg arguments->command = toupper(tmpcmd); 4235dfecf96Smrg ++arguments->offset; 4245dfecf96Smrg 4255dfecf96Smrg return (ptr); 4265dfecf96Smrg} 4275dfecf96Smrg 4285dfecf96Smrgstatic void 4295dfecf96Smrgparse_error(FmtArgs *args, int code) 4305dfecf96Smrg{ 431f765521fSmrg static const char * const errors[] = { 4325dfecf96Smrg NULL, 4335dfecf96Smrg "too many parameters to directive", 4345dfecf96Smrg "too many @ parameters", 4355dfecf96Smrg "too many : parameters", 4365dfecf96Smrg "no arguments left to format", 4375dfecf96Smrg "argument is not a fixnum integer or a character", 4385dfecf96Smrg "unknown format directive", 4395dfecf96Smrg "parameter is not a fixnum integer", 4405dfecf96Smrg }; 4415dfecf96Smrg 4425dfecf96Smrg format_error(args, errors[code]); 4435dfecf96Smrg} 4445dfecf96Smrg 4455dfecf96Smrgstatic void 4465dfecf96Smrgmerge_error(FmtArgs *args, int code) 4475dfecf96Smrg{ 448f765521fSmrg static const char * const errors[] = { 4495dfecf96Smrg NULL, 4505dfecf96Smrg "too many parameters to directive", 4515dfecf96Smrg "argument must be a character", 4525dfecf96Smrg "argument must be a fixnum integer", 4535dfecf96Smrg }; 4545dfecf96Smrg 4555dfecf96Smrg format_error(args, errors[code]); 4565dfecf96Smrg} 4575dfecf96Smrg 4585dfecf96Smrgstatic void 4595dfecf96Smrggeneric_error(FmtArgs *args, int code) 4605dfecf96Smrg{ 461f765521fSmrg static const char * const errors[] = { 4625dfecf96Smrg NULL, 4635dfecf96Smrg "radix must be in the range 2 to 36, inclusive", 4645dfecf96Smrg "parameter must be positive", 4655dfecf96Smrg "argument must be a string", 4665dfecf96Smrg "argument must be a list", 4675dfecf96Smrg }; 4685dfecf96Smrg 4695dfecf96Smrg format_error(args, errors[code]); 4705dfecf96Smrg} 4715dfecf96Smrg 4725dfecf96Smrgstatic void 473f765521fSmrgformat_error(FmtArgs *args, const char *str) 4745dfecf96Smrg{ 4755dfecf96Smrg char *message; 4765dfecf96Smrg int errorlen, formatlen; 4775dfecf96Smrg 4785dfecf96Smrg /* number of bytes of format to be printed */ 4795dfecf96Smrg formatlen = (args->format - args->base) + args->offset; 4805dfecf96Smrg 4815dfecf96Smrg /* length of specific error message */ 4825dfecf96Smrg errorlen = strlen(str) + 1; /* plus '\n' */ 4835dfecf96Smrg 4845dfecf96Smrg /* XXX allocate string with LispMalloc, 4855dfecf96Smrg * so that it will be freed in LispTopLevel */ 4865dfecf96Smrg message = LispMalloc(formatlen + errorlen + 1); 4875dfecf96Smrg 4885dfecf96Smrg sprintf(message, "%s\n", str); 4895dfecf96Smrg memcpy(message + errorlen, args->base, formatlen); 4905dfecf96Smrg message[errorlen + formatlen] = '\0'; 4915dfecf96Smrg 4925dfecf96Smrg LispDestroy("FORMAT: %s", message); 4935dfecf96Smrg} 4945dfecf96Smrg 4955dfecf96Smrgstatic int 4965dfecf96Smrgformat_object(LispObj *stream, LispObj *object) 4975dfecf96Smrg{ 4985dfecf96Smrg int length; 4995dfecf96Smrg 5005dfecf96Smrg length = LispWriteObject(stream, object); 5015dfecf96Smrg 5025dfecf96Smrg return (length); 5035dfecf96Smrg} 5045dfecf96Smrg 5055dfecf96Smrgstatic void 5065dfecf96Smrgformat_ascii(LispObj *stream, LispObj *object, FmtArgs *args) 5075dfecf96Smrg{ 5085dfecf96Smrg GC_ENTER(); 5095dfecf96Smrg LispObj *string = NIL; 5105dfecf96Smrg int length = 0, 5115dfecf96Smrg atsign = args->atsign, 5125dfecf96Smrg collon = args->collon, 5135dfecf96Smrg mincol = args->arguments[0].value, 5145dfecf96Smrg colinc = args->arguments[1].value, 5155dfecf96Smrg minpad = args->arguments[2].value, 5165dfecf96Smrg padchar = args->arguments[3].value; 5175dfecf96Smrg 5185dfecf96Smrg /* check/correct arguments */ 5195dfecf96Smrg if (mincol < 0) 5205dfecf96Smrg mincol = 0; 5215dfecf96Smrg if (colinc < 0) 5225dfecf96Smrg colinc = 1; 5235dfecf96Smrg if (minpad < 0) 5245dfecf96Smrg minpad = 0; 5255dfecf96Smrg /* XXX pachar can be the null character? */ 5265dfecf96Smrg 5275dfecf96Smrg if (object == NIL) 5285dfecf96Smrg length = collon ? 2 : 3; /* () or NIL */ 5295dfecf96Smrg 5305dfecf96Smrg /* left padding */ 5315dfecf96Smrg if (atsign) { 5325dfecf96Smrg /* if length not yet known */ 5335dfecf96Smrg if (object == NIL) { 5345dfecf96Smrg string = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0); 5355dfecf96Smrg GC_PROTECT(string); 5365dfecf96Smrg length = LispWriteObject(string, object); 5375dfecf96Smrg } 5385dfecf96Smrg 5395dfecf96Smrg /* output minpad characters at left */ 5405dfecf96Smrg if (minpad) { 5415dfecf96Smrg length += minpad; 5425dfecf96Smrg LispWriteChars(stream, padchar, minpad); 5435dfecf96Smrg } 5445dfecf96Smrg 5455dfecf96Smrg if (colinc) { 5465dfecf96Smrg /* puts colinc spaces at a time, 5475dfecf96Smrg * until at least mincol chars out */ 5485dfecf96Smrg while (length < mincol) { 5495dfecf96Smrg LispWriteChars(stream, padchar, colinc); 5505dfecf96Smrg length += colinc; 5515dfecf96Smrg } 5525dfecf96Smrg } 5535dfecf96Smrg } 5545dfecf96Smrg 5555dfecf96Smrg if (object == NIL) { 5565dfecf96Smrg if (collon) 5575dfecf96Smrg LispWriteStr(stream, "()", 2); 5585dfecf96Smrg else 559f14f4646Smrg LispWriteStr(stream, Snil->value, 3); 5605dfecf96Smrg } 5615dfecf96Smrg else { 5625dfecf96Smrg /* if string is not NIL, atsign was specified 5635dfecf96Smrg * and object printed to string */ 5645dfecf96Smrg if (string == NIL) 5655dfecf96Smrg length = format_object(stream, object); 5665dfecf96Smrg else { 5675dfecf96Smrg int size; 568f765521fSmrg const char *str = LispGetSstring(SSTREAMP(string), &size); 5695dfecf96Smrg 5705dfecf96Smrg LispWriteStr(stream, str, size); 5715dfecf96Smrg } 5725dfecf96Smrg } 5735dfecf96Smrg 5745dfecf96Smrg /* right padding */ 5755dfecf96Smrg if (!atsign) { 5765dfecf96Smrg /* output minpad characters at left */ 5775dfecf96Smrg if (minpad) { 5785dfecf96Smrg length += minpad; 5795dfecf96Smrg LispWriteChars(stream, padchar, minpad); 5805dfecf96Smrg } 5815dfecf96Smrg if (colinc) { 5825dfecf96Smrg /* puts colinc spaces at a time, 5835dfecf96Smrg * until at least mincol chars out */ 5845dfecf96Smrg while (length < mincol) { 5855dfecf96Smrg LispWriteChars(stream, padchar, colinc); 5865dfecf96Smrg length += colinc; 5875dfecf96Smrg } 5885dfecf96Smrg } 5895dfecf96Smrg } 5905dfecf96Smrg 5915dfecf96Smrg GC_LEAVE(); 5925dfecf96Smrg} 5935dfecf96Smrg 5945dfecf96Smrg/* assumes radix is 0 or in range 2 - 36 */ 5955dfecf96Smrgstatic void 5965dfecf96Smrgformat_in_radix(LispObj *stream, LispObj *object, int radix, FmtArgs *args) 5975dfecf96Smrg{ 5985dfecf96Smrg if (INTEGERP(object)) { 5995dfecf96Smrg int i, atsign, collon, mincol, padchar, commachar, commainterval; 6005dfecf96Smrg 6015dfecf96Smrg i = (radix == 0); 6025dfecf96Smrg atsign = args->atsign; 6035dfecf96Smrg collon = args->collon; 6045dfecf96Smrg if (radix == 0) { 6055dfecf96Smrg radix = args->arguments[0].value; 6065dfecf96Smrg if (radix < 2 || radix > 36) { 6075dfecf96Smrg args->offset = args->arguments[0].offset; 6085dfecf96Smrg generic_error(args, GENERIC_RADIX); 6095dfecf96Smrg } 6105dfecf96Smrg } 6115dfecf96Smrg mincol = args->arguments[i++].value; 6125dfecf96Smrg padchar = args->arguments[i++].value; 6135dfecf96Smrg commachar = args->arguments[i++].value; 6145dfecf96Smrg commainterval = args->arguments[i++].value; 6155dfecf96Smrg 6165dfecf96Smrg LispFormatInteger(stream, object, radix, atsign, collon, 6175dfecf96Smrg mincol, padchar, commachar, commainterval); 6185dfecf96Smrg } 6195dfecf96Smrg else 6205dfecf96Smrg format_object(stream, object); 6215dfecf96Smrg} 6225dfecf96Smrg 6235dfecf96Smrgstatic void 6245dfecf96Smrgformat_radix_special(LispObj *stream, LispObj *object, FmtArgs *args) 6255dfecf96Smrg{ 6265dfecf96Smrg if (FIXNUMP(object)) { 6275dfecf96Smrg if (args->atsign) 6285dfecf96Smrg format_roman(stream, object, args); 6295dfecf96Smrg else 6305dfecf96Smrg format_english(stream, object, args); 6315dfecf96Smrg } 6325dfecf96Smrg else 6335dfecf96Smrg format_object(stream, object); 6345dfecf96Smrg} 6355dfecf96Smrg 6365dfecf96Smrgstatic void 6375dfecf96Smrgformat_roman(LispObj *stream, LispObj *object, FmtArgs *args) 6385dfecf96Smrg{ 6395dfecf96Smrg long value = 0; 6405dfecf96Smrg int cando, new_roman = args->collon == 0; 6415dfecf96Smrg 6425dfecf96Smrg if (FIXNUMP(object)) { 6435dfecf96Smrg value = FIXNUM_VALUE(object); 6445dfecf96Smrg if (new_roman) 6455dfecf96Smrg cando = value >= 1 && value <= 3999; 6465dfecf96Smrg else 6475dfecf96Smrg cando = value >= 1 && value <= 4999; 6485dfecf96Smrg } 6495dfecf96Smrg else 6505dfecf96Smrg cando = 0; 6515dfecf96Smrg 6525dfecf96Smrg if (cando) 6535dfecf96Smrg LispFormatRomanInteger(stream, value, new_roman); 6545dfecf96Smrg else 6555dfecf96Smrg format_object(stream, object); 6565dfecf96Smrg} 6575dfecf96Smrg 6585dfecf96Smrgstatic void 6595dfecf96Smrgformat_english(LispObj *stream, LispObj *object, FmtArgs *args) 6605dfecf96Smrg{ 6615dfecf96Smrg int cando; 6625dfecf96Smrg long number = 0; 6635dfecf96Smrg 6645dfecf96Smrg if (FIXNUMP(object)) { 6655dfecf96Smrg number = FIXNUM_VALUE(object); 6665dfecf96Smrg cando = number >= -999999999 && number <= 999999999; 6675dfecf96Smrg } 6685dfecf96Smrg else 6695dfecf96Smrg cando = 0; 6705dfecf96Smrg 6715dfecf96Smrg if (cando) 6725dfecf96Smrg LispFormatEnglishInteger(stream, number, args->collon); 6735dfecf96Smrg else 6745dfecf96Smrg format_object(stream, object); 6755dfecf96Smrg} 6765dfecf96Smrg 6775dfecf96Smrgstatic void 6785dfecf96Smrgformat_character(LispObj *stream, LispObj *object, FmtArgs *args) 6795dfecf96Smrg{ 6805dfecf96Smrg if (SCHARP(object)) 6815dfecf96Smrg LispFormatCharacter(stream, object, args->atsign, args->collon); 6825dfecf96Smrg else 6835dfecf96Smrg format_object(stream, object); 6845dfecf96Smrg} 6855dfecf96Smrg 6865dfecf96Smrgstatic void 6875dfecf96Smrgformat_fixed_float(LispObj *stream, LispObj *object, FmtArgs *args) 6885dfecf96Smrg{ 6895dfecf96Smrg if (FLOATP(object)) 6905dfecf96Smrg LispFormatFixedFloat(stream, object, args->atsign, 6915dfecf96Smrg args->arguments[0].value, 6925dfecf96Smrg IF_SPECIFIED(args->arguments[1]), 6935dfecf96Smrg args->arguments[2].value, 6945dfecf96Smrg args->arguments[3].value, 6955dfecf96Smrg args->arguments[4].value); 6965dfecf96Smrg else 6975dfecf96Smrg format_object(stream, object); 6985dfecf96Smrg} 6995dfecf96Smrg 7005dfecf96Smrgstatic void 7015dfecf96Smrgformat_exponential_float(LispObj *stream, LispObj *object, FmtArgs *args) 7025dfecf96Smrg{ 7035dfecf96Smrg if (FLOATP(object)) 7045dfecf96Smrg LispFormatExponentialFloat(stream, object, args->atsign, 7055dfecf96Smrg args->arguments[0].value, 7065dfecf96Smrg IF_SPECIFIED(args->arguments[1]), 7075dfecf96Smrg args->arguments[2].value, 7085dfecf96Smrg args->arguments[3].value, 7095dfecf96Smrg args->arguments[4].value, 7105dfecf96Smrg args->arguments[5].value, 7115dfecf96Smrg args->arguments[6].value); 7125dfecf96Smrg else 7135dfecf96Smrg format_object(stream, object); 7145dfecf96Smrg} 7155dfecf96Smrg 7165dfecf96Smrgstatic void 7175dfecf96Smrgformat_general_float(LispObj *stream, LispObj *object, FmtArgs *args) 7185dfecf96Smrg{ 7195dfecf96Smrg if (FLOATP(object)) 7205dfecf96Smrg LispFormatGeneralFloat(stream, object, args->atsign, 7215dfecf96Smrg args->arguments[0].value, 7225dfecf96Smrg IF_SPECIFIED(args->arguments[1]), 7235dfecf96Smrg args->arguments[2].value, 7245dfecf96Smrg args->arguments[3].value, 7255dfecf96Smrg args->arguments[4].value, 7265dfecf96Smrg args->arguments[5].value, 7275dfecf96Smrg args->arguments[6].value); 7285dfecf96Smrg else 7295dfecf96Smrg format_object(stream, object); 7305dfecf96Smrg} 7315dfecf96Smrg 7325dfecf96Smrgstatic void 7335dfecf96Smrgformat_dollar_float(LispObj *stream, LispObj *object, FmtArgs *args) 7345dfecf96Smrg{ 7355dfecf96Smrg if (FLOATP(object)) 7365dfecf96Smrg LispFormatDollarFloat(stream, object, 7375dfecf96Smrg args->atsign, args->collon, 7385dfecf96Smrg args->arguments[0].value, 7395dfecf96Smrg args->arguments[1].value, 7405dfecf96Smrg args->arguments[2].value, 7415dfecf96Smrg args->arguments[3].value); 7425dfecf96Smrg else 7435dfecf96Smrg format_object(stream, object); 7445dfecf96Smrg} 7455dfecf96Smrg 7465dfecf96Smrgstatic void 7475dfecf96Smrgformat_tabulate(LispObj *stream, FmtArgs *args) 7485dfecf96Smrg{ 7495dfecf96Smrg int atsign = args->atsign, 7505dfecf96Smrg colnum = args->arguments[0].value, 7515dfecf96Smrg colinc = args->arguments[1].value, 7525dfecf96Smrg column; 7535dfecf96Smrg 7545dfecf96Smrg column = LispGetColumn(stream); 7555dfecf96Smrg 7565dfecf96Smrg if (atsign) { 7575dfecf96Smrg /* relative tabulation */ 7585dfecf96Smrg if (colnum > 0) { 7595dfecf96Smrg LispWriteChars(stream, ' ', colnum); 7605dfecf96Smrg column += colnum; 7615dfecf96Smrg } 7625dfecf96Smrg /* tabulate until at a multiple of colinc */ 7635dfecf96Smrg if (colinc > 0) 7645dfecf96Smrg LispWriteChars(stream, ' ', colinc - (column % colinc)); 7655dfecf96Smrg } 7665dfecf96Smrg else { 7675dfecf96Smrg /* if colinc not specified, just move to given column */ 7685dfecf96Smrg if (colinc <= 0) 7695dfecf96Smrg LispWriteChars(stream, ' ', column - colnum); 7705dfecf96Smrg else { 7715dfecf96Smrg /* always output at least colinc spaces */ 7725dfecf96Smrg do { 7735dfecf96Smrg LispWriteChars(stream, ' ', colinc); 7745dfecf96Smrg colnum -= colinc; 7755dfecf96Smrg } while (colnum > column); 7765dfecf96Smrg } 7775dfecf96Smrg } 7785dfecf96Smrg} 7795dfecf96Smrg 7805dfecf96Smrgstatic void 7815dfecf96Smrgformat_goto(FmtInfo *info) 7825dfecf96Smrg{ 7835dfecf96Smrg int count, num_arguments; 7845dfecf96Smrg LispObj *object, *arguments; 7855dfecf96Smrg 7865dfecf96Smrg /* number of arguments to ignore or goto offset */ 7875dfecf96Smrg count = info->args.arguments[0].value; 7885dfecf96Smrg if (count < 0) 7895dfecf96Smrg generic_error(&(info->args), GENERIC_NEGATIVE); 7905dfecf96Smrg 7915dfecf96Smrg if (info->args.atsign) { 7925dfecf96Smrg /* absolute goto */ 7935dfecf96Smrg 7945dfecf96Smrg /* if not specified, defaults to zero */ 7955dfecf96Smrg if (!(info->args.arguments[0].specified)) 7965dfecf96Smrg count = 0; 7975dfecf96Smrg 7985dfecf96Smrg /* if offset too large */ 7995dfecf96Smrg if (count > info->total_arguments) 8005dfecf96Smrg parse_error(&(info->args), PARSE_NOARGSLEFT); 8015dfecf96Smrg else if (count != info->total_arguments - *(info->num_arguments)) { 8025dfecf96Smrg /* calculate new parameters */ 8035dfecf96Smrg object = NIL; 8045dfecf96Smrg arguments = info->base_arguments; 8055dfecf96Smrg num_arguments = info->total_arguments - count; 8065dfecf96Smrg 8075dfecf96Smrg for (; count > 0; count--, arguments = CDR(arguments)) 8085dfecf96Smrg object = CAR(arguments); 8095dfecf96Smrg 8105dfecf96Smrg /* update format information */ 8115dfecf96Smrg *(info->object) = object; 8125dfecf96Smrg *(info->arguments) = arguments; 8135dfecf96Smrg *(info->num_arguments) = num_arguments; 8145dfecf96Smrg } 8155dfecf96Smrg } 8165dfecf96Smrg else if (count) { 8175dfecf96Smrg /* relative goto, ignore or go back count arguments */ 8185dfecf96Smrg 8195dfecf96Smrg /* prepare to update parameters */ 8205dfecf96Smrg arguments = *(info->arguments); 8215dfecf96Smrg num_arguments = *(info->num_arguments); 8225dfecf96Smrg 8235dfecf96Smrg /* go back count arguments? */ 8245dfecf96Smrg if (info->args.collon) 8255dfecf96Smrg count = -count; 8265dfecf96Smrg 8275dfecf96Smrg num_arguments -= count; 8285dfecf96Smrg 8295dfecf96Smrg if (count > 0) { 8305dfecf96Smrg if (count > *(info->num_arguments)) 8315dfecf96Smrg parse_error(&(info->args), PARSE_NOARGSLEFT); 8325dfecf96Smrg 8335dfecf96Smrg object = *(info->object); 8345dfecf96Smrg for (; count > 0; count--, arguments = CDR(arguments)) 8355dfecf96Smrg object = CAR(arguments); 8365dfecf96Smrg } 8375dfecf96Smrg else { /* count < 0 */ 8385dfecf96Smrg if (info->total_arguments + count - *(info->num_arguments) < 0) 8395dfecf96Smrg parse_error(&(info->args), PARSE_NOARGSLEFT); 8405dfecf96Smrg 8415dfecf96Smrg object = NIL; 8425dfecf96Smrg arguments = info->base_arguments; 8435dfecf96Smrg for (count = 0; count < info->total_arguments - num_arguments; 8445dfecf96Smrg count++, arguments = CDR(arguments)) 8455dfecf96Smrg object = CAR(arguments); 8465dfecf96Smrg } 8475dfecf96Smrg 8485dfecf96Smrg /* update format parameters */ 8495dfecf96Smrg *(info->object) = object; 8505dfecf96Smrg *(info->arguments) = arguments; 8515dfecf96Smrg *(info->num_arguments) = num_arguments; 8525dfecf96Smrg } 8535dfecf96Smrg} 8545dfecf96Smrg 8555dfecf96Smrgstatic void 8565dfecf96Smrgformat_indirection(LispObj *stream, LispObj *format, FmtInfo *info) 8575dfecf96Smrg{ 8585dfecf96Smrg char *string; 8595dfecf96Smrg LispObj *object; 8605dfecf96Smrg FmtInfo indirect_info; 8615dfecf96Smrg 8625dfecf96Smrg if (!STRINGP(format)) 8635dfecf96Smrg generic_error(&(info->args), GENERIC_BADSTRING); 8645dfecf96Smrg string = THESTR(format); 8655dfecf96Smrg 8665dfecf96Smrg /* most information is the same */ 8675dfecf96Smrg memcpy(&indirect_info, info, sizeof(FmtInfo)); 8685dfecf96Smrg 8695dfecf96Smrg /* set new format string */ 8705dfecf96Smrg indirect_info.args.base = indirect_info.args.format = string; 8715dfecf96Smrg indirect_info.format = &string; 8725dfecf96Smrg 8735dfecf96Smrg if (info->args.atsign) { 8745dfecf96Smrg /* use current arguments */ 8755dfecf96Smrg 8765dfecf96Smrg /* do the indirect format */ 8775dfecf96Smrg LispFormat(stream, &indirect_info); 8785dfecf96Smrg } 8795dfecf96Smrg else { 8805dfecf96Smrg /* next argument is the recursive call arguments */ 8815dfecf96Smrg 8825dfecf96Smrg int num_arguments; 8835dfecf96Smrg 8845dfecf96Smrg /* it is valid to not have a list following string, as string may 8855dfecf96Smrg * not have format directives */ 8865dfecf96Smrg if (CONSP(*(indirect_info.arguments))) 8875dfecf96Smrg object = CAR(*(indirect_info.arguments)); 8885dfecf96Smrg else 8895dfecf96Smrg object = NIL; 8905dfecf96Smrg 8915dfecf96Smrg if (!LISTP(object) || !CONSP(*(info->arguments))) 8925dfecf96Smrg generic_error(&(info->args), GENERIC_BADLIST); 8935dfecf96Smrg 8945dfecf96Smrg /* update information now */ 8955dfecf96Smrg *(info->object) = object; 8965dfecf96Smrg *(info->arguments) = CDR(*(info->arguments)); 8975dfecf96Smrg *(info->num_arguments) -= 1; 8985dfecf96Smrg 8995dfecf96Smrg /* set arguments for recursive call */ 9005dfecf96Smrg indirect_info.base_arguments = object; 9015dfecf96Smrg indirect_info.arguments = &object; 9025dfecf96Smrg for (num_arguments = 0; CONSP(object); object = CDR(object)) 9035dfecf96Smrg ++num_arguments; 9045dfecf96Smrg 9055dfecf96Smrg /* note that indirect_info.arguments is a pointer to "object", 9065dfecf96Smrg * keep it pointing to the correct object */ 9075dfecf96Smrg object = indirect_info.base_arguments; 9085dfecf96Smrg indirect_info.total_arguments = num_arguments; 9095dfecf96Smrg indirect_info.num_arguments = &num_arguments; 9105dfecf96Smrg 9115dfecf96Smrg /* do the indirect format */ 9125dfecf96Smrg LispFormat(stream, &indirect_info); 9135dfecf96Smrg } 9145dfecf96Smrg} 9155dfecf96Smrg 9165dfecf96Smrg/* update pointers to a list of format strings: 9175dfecf96Smrg * for '(' and '{' only one list is required 9185dfecf96Smrg * for '[' and '<' more than one may be returned 9195dfecf96Smrg * has_default is only meaningful for '[' and '<' 9205dfecf96Smrg * comma_width and line_width are only meaningful to '<', and 9215dfecf96Smrg * only valid if has_default set 9225dfecf96Smrg * if the string is finished prematurely, LispDestroy is called 9235dfecf96Smrg * format_ptr is updated to the correct pointer in the "main" format string 9245dfecf96Smrg */ 9255dfecf96Smrgstatic void 9265dfecf96Smrglist_formats(FmtInfo *info, int command, char **format_ptr, 9275dfecf96Smrg char ***format_list, int *format_count, int *has_default, 9285dfecf96Smrg int *comma_width, int *line_width) 9295dfecf96Smrg{ 9305dfecf96Smrg /* instead of processing the directives recursively, just separate the 9315dfecf96Smrg * input formats in separate strings, then see if one of then need to 9325dfecf96Smrg * be used */ 9335dfecf96Smrg FmtArgs args; 9345dfecf96Smrg int counters[] = { 0, 0, 0, 0}; 9355dfecf96Smrg /* '[', '(', '{', '<' */ 9365dfecf96Smrg char *format, *next_format, *start, **formats; 9375dfecf96Smrg int num_formats, format_index, separator, add_format; 9385dfecf96Smrg 9395dfecf96Smrg /* initialize */ 9405dfecf96Smrg formats = NULL; 9415dfecf96Smrg num_formats = format_index = 0; 9425dfecf96Smrg if (has_default != NULL) 9435dfecf96Smrg *has_default = 0; 9445dfecf96Smrg if (comma_width != NULL) 9455dfecf96Smrg *comma_width = 0; 9465dfecf96Smrg if (line_width != NULL) 9475dfecf96Smrg *line_width = 0; 9485dfecf96Smrg format = start = next_format = *format_ptr; 9495dfecf96Smrg switch (command) { 9505dfecf96Smrg case '[': counters[0] = 1; format_index = 0; break; 9515dfecf96Smrg case '(': counters[1] = 1; format_index = 1; break; 9525dfecf96Smrg case '{': counters[2] = 1; format_index = 2; break; 9535dfecf96Smrg case '<': counters[3] = 1; format_index = 3; break; 9545dfecf96Smrg } 9555dfecf96Smrg 9565dfecf96Smrg#define LIST_FORMATS_ADD 1 9575dfecf96Smrg#define LIST_FORMATS_DONE 2 9585dfecf96Smrg 9595dfecf96Smrg /* fill list of format options to conditional */ 9605dfecf96Smrg while (*format) { 9615dfecf96Smrg if (*format == '~') { 9625dfecf96Smrg separator = add_format = 0; 9635dfecf96Smrg args.format = format + 1; 9645dfecf96Smrg next_format = parse_arguments(format + 1, &args, NULL, NULL, NULL); 9655dfecf96Smrg switch (args.command) { 9665dfecf96Smrg case '[': ++counters[0]; break; 9675dfecf96Smrg case ']': --counters[0]; break; 9685dfecf96Smrg case '(': ++counters[1]; break; 9695dfecf96Smrg case ')': --counters[1]; break; 9705dfecf96Smrg case '{': ++counters[2]; break; 9715dfecf96Smrg case '}': --counters[2]; break; 9725dfecf96Smrg case '<': ++counters[3]; break; 9735dfecf96Smrg case '>': --counters[3]; break; 9745dfecf96Smrg case ';': separator = 1; break; 9755dfecf96Smrg } 9765dfecf96Smrg 9775dfecf96Smrg /* check if a new format string must be added */ 9785dfecf96Smrg if (separator && counters[format_index] == 1 && 9795dfecf96Smrg (command == '[' || command == '<')) 9805dfecf96Smrg add_format = LIST_FORMATS_ADD; 9815dfecf96Smrg else if (counters[format_index] == 0) 9825dfecf96Smrg add_format = LIST_FORMATS_DONE; 9835dfecf96Smrg 9845dfecf96Smrg if (add_format) { 9855dfecf96Smrg int length = format - start; 9865dfecf96Smrg 9875dfecf96Smrg formats = LispRealloc(formats, 9885dfecf96Smrg (num_formats + 1) * sizeof(char*)); 9895dfecf96Smrg 9905dfecf96Smrg formats[num_formats] = LispMalloc(length + 1); 9915dfecf96Smrg strncpy(formats[num_formats], start, length); 9925dfecf96Smrg formats[num_formats][length] = '\0'; 9935dfecf96Smrg ++num_formats; 9945dfecf96Smrg /* loop finished? */ 9955dfecf96Smrg if (add_format == LIST_FORMATS_DONE) 9965dfecf96Smrg break; 9975dfecf96Smrg else if (command == '[' && has_default != NULL) 9985dfecf96Smrg /* will be set only for the last parameter, what is 9995dfecf96Smrg * expected, just don't warn about it in the incorrect 10005dfecf96Smrg * place */ 10015dfecf96Smrg *has_default = args.collon != 0; 10025dfecf96Smrg else if (command == '<' && num_formats == 1) { 10035dfecf96Smrg /* if the first parameter to '<', there may be overrides 10045dfecf96Smrg * to comma-width and line-width */ 10055dfecf96Smrg if (args.collon && has_default != NULL) { 10065dfecf96Smrg *has_default = 1; 10075dfecf96Smrg if (comma_width != NULL && 10085dfecf96Smrg args.arguments[0].specified && 10095dfecf96Smrg !args.arguments[0].achar) 10105dfecf96Smrg *comma_width = args.arguments[0].value; 10115dfecf96Smrg if (line_width != NULL && 10125dfecf96Smrg args.arguments[1].specified && 10135dfecf96Smrg !args.arguments[1].achar) 10145dfecf96Smrg *line_width = args.arguments[1].value; 10155dfecf96Smrg } 10165dfecf96Smrg } 10175dfecf96Smrg start = next_format; 10185dfecf96Smrg } 10195dfecf96Smrg format = next_format; 10205dfecf96Smrg } 10215dfecf96Smrg else 10225dfecf96Smrg ++format; 10235dfecf96Smrg } 10245dfecf96Smrg 10255dfecf96Smrg /* check if format string did not finish prematurely */ 10265dfecf96Smrg if (counters[format_index] != 0) { 10275dfecf96Smrg char error_message[64]; 10285dfecf96Smrg 10295dfecf96Smrg sprintf(error_message, "expecting ~%c", command); 10305dfecf96Smrg format_error(&(info->args), error_message); 10315dfecf96Smrg } 10325dfecf96Smrg 10335dfecf96Smrg /* update pointers */ 10345dfecf96Smrg *format_list = formats; 10355dfecf96Smrg *format_count = num_formats; 10365dfecf96Smrg *format_ptr = next_format; 10375dfecf96Smrg} 10385dfecf96Smrg 10395dfecf96Smrgstatic void 10405dfecf96Smrgfree_formats(char **formats, int num_formats) 10415dfecf96Smrg{ 10425dfecf96Smrg if (num_formats) { 10435dfecf96Smrg while (--num_formats >= 0) 10445dfecf96Smrg LispFree(formats[num_formats]); 10455dfecf96Smrg LispFree(formats); 10465dfecf96Smrg } 10475dfecf96Smrg} 10485dfecf96Smrg 10495dfecf96Smrgstatic void 10505dfecf96Smrgformat_case_conversion(LispObj *stream, FmtInfo *info) 10515dfecf96Smrg{ 10525dfecf96Smrg GC_ENTER(); 10535dfecf96Smrg LispObj *string; 10545dfecf96Smrg FmtInfo case_info; 10555dfecf96Smrg char *str, *ptr; 10565dfecf96Smrg char *format, *next_format, **formats; 10575dfecf96Smrg int atsign, collon, num_formats, length; 10585dfecf96Smrg 10595dfecf96Smrg atsign = info->args.atsign; 10605dfecf96Smrg collon = info->args.collon; 10615dfecf96Smrg 10625dfecf96Smrg /* output to a string, before case conversion */ 10635dfecf96Smrg string = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0); 10645dfecf96Smrg GC_PROTECT(string); 10655dfecf96Smrg 10665dfecf96Smrg /* most information is the same */ 10675dfecf96Smrg memcpy(&case_info, info, sizeof(FmtInfo)); 10685dfecf96Smrg 10695dfecf96Smrg /* list formats */ 10705dfecf96Smrg next_format = *(info->format); 10715dfecf96Smrg list_formats(info, '(', &next_format, &formats, &num_formats, 10725dfecf96Smrg NULL, NULL, NULL); 10735dfecf96Smrg 10745dfecf96Smrg /* set new format string */ 10755dfecf96Smrg format = formats[0]; 10765dfecf96Smrg case_info.args.base = case_info.args.format = format; 10775dfecf96Smrg case_info.format = &format; 10785dfecf96Smrg 10795dfecf96Smrg /* format text to string */ 10805dfecf96Smrg LispFormat(string, &case_info); 10815dfecf96Smrg 10825dfecf96Smrg str = ptr = LispGetSstring(SSTREAMP(string), &length); 10835dfecf96Smrg 10845dfecf96Smrg /* do case conversion */ 10855dfecf96Smrg if (!atsign && !collon) { 10865dfecf96Smrg /* convert all upercase to lowercase */ 10875dfecf96Smrg for (; *ptr; ptr++) { 10885dfecf96Smrg if (isupper(*ptr)) 10895dfecf96Smrg *ptr = tolower(*ptr); 10905dfecf96Smrg } 10915dfecf96Smrg } 10925dfecf96Smrg else if (atsign && collon) { 10935dfecf96Smrg /* convert all lowercase to upercase */ 10945dfecf96Smrg for (; *ptr; ptr++) { 10955dfecf96Smrg if (islower(*ptr)) 10965dfecf96Smrg *ptr = toupper(*ptr); 10975dfecf96Smrg } 10985dfecf96Smrg } 10995dfecf96Smrg else { 11005dfecf96Smrg int upper = 1; 11015dfecf96Smrg 11025dfecf96Smrg /* skip non-alphanumeric characters */ 11035dfecf96Smrg for (; *ptr; ptr++) 11045dfecf96Smrg if (isalnum(*ptr)) 11055dfecf96Smrg break; 11065dfecf96Smrg 11075dfecf96Smrg /* capitalize words */ 11085dfecf96Smrg for (; *ptr; ptr++) { 11095dfecf96Smrg if (isalnum(*ptr)) { 11105dfecf96Smrg if (upper) { 11115dfecf96Smrg if (islower(*ptr)) 11125dfecf96Smrg *ptr = toupper(*ptr); 11135dfecf96Smrg upper = 0; 11145dfecf96Smrg } 11155dfecf96Smrg else if (isupper(*ptr)) 11165dfecf96Smrg *ptr = tolower(*ptr); 11175dfecf96Smrg } 11185dfecf96Smrg else 11195dfecf96Smrg upper = collon; 11205dfecf96Smrg /* if collon, capitalize all words, else just first word */ 11215dfecf96Smrg } 11225dfecf96Smrg } 11235dfecf96Smrg 11245dfecf96Smrg /* output case converted string */ 11255dfecf96Smrg LispWriteStr(stream, str, length); 11265dfecf96Smrg 11275dfecf96Smrg /* temporary string stream is not necessary anymore */ 11285dfecf96Smrg GC_LEAVE(); 11295dfecf96Smrg 11305dfecf96Smrg /* free temporary memory */ 11315dfecf96Smrg free_formats(formats, num_formats); 11325dfecf96Smrg 11335dfecf96Smrg /* this information always updated */ 11345dfecf96Smrg *(info->format) = next_format; 11355dfecf96Smrg} 11365dfecf96Smrg 11375dfecf96Smrgstatic void 11385dfecf96Smrgformat_conditional(LispObj *stream, FmtInfo *info) 11395dfecf96Smrg{ 11405dfecf96Smrg LispObj *object, *arguments; 11415dfecf96Smrg char *format, *next_format, **formats; 11425dfecf96Smrg int choice, num_formats, has_default, num_arguments; 11435dfecf96Smrg 11445dfecf96Smrg /* save information that may change */ 11455dfecf96Smrg object = *(info->object); 11465dfecf96Smrg arguments = *(info->arguments); 11475dfecf96Smrg num_arguments = *(info->num_arguments); 11485dfecf96Smrg 11495dfecf96Smrg /* initialize */ 11505dfecf96Smrg choice = -1; 11515dfecf96Smrg next_format = *(info->format); 11525dfecf96Smrg 11535dfecf96Smrg /* list formats */ 11545dfecf96Smrg list_formats(info, '[', 11555dfecf96Smrg &next_format, &formats, &num_formats, &has_default, NULL, NULL); 11565dfecf96Smrg 11575dfecf96Smrg /* ~:[false;true] */ 11585dfecf96Smrg if (info->args.collon) { 11595dfecf96Smrg /* one argument always consumed */ 11605dfecf96Smrg if (!CONSP(arguments)) 11615dfecf96Smrg parse_error(&(info->args), PARSE_NOARGSLEFT); 11625dfecf96Smrg object = CAR(arguments); 11635dfecf96Smrg arguments = CDR(arguments); 11645dfecf96Smrg --num_arguments; 11655dfecf96Smrg choice = object == NIL ? 0 : 1; 11665dfecf96Smrg } 11675dfecf96Smrg /* ~@[true] */ 11685dfecf96Smrg else if (info->args.atsign) { 11695dfecf96Smrg /* argument consumed only if nil, but one must be available */ 11705dfecf96Smrg if (!CONSP(arguments)) 11715dfecf96Smrg parse_error(&(info->args), PARSE_NOARGSLEFT); 11725dfecf96Smrg if (CAR(arguments) != NIL) 11735dfecf96Smrg choice = 0; 11745dfecf96Smrg else { 11755dfecf96Smrg object = CAR(arguments); 11765dfecf96Smrg arguments = CDR(arguments); 11775dfecf96Smrg --num_arguments; 11785dfecf96Smrg } 11795dfecf96Smrg } 11805dfecf96Smrg /* ~n[...~] */ 11815dfecf96Smrg else if (info->args.arguments[0].specified) 11825dfecf96Smrg /* no arguments consumed */ 11835dfecf96Smrg choice = info->args.arguments[0].value; 11845dfecf96Smrg /* ~[...~] */ 11855dfecf96Smrg else { 11865dfecf96Smrg /* one argument consumed, it is the index in the available formats */ 11875dfecf96Smrg if (!CONSP(arguments)) 11885dfecf96Smrg parse_error(&(info->args), PARSE_NOARGSLEFT); 11895dfecf96Smrg object = CAR(arguments); 11905dfecf96Smrg arguments = CDR(arguments); 11915dfecf96Smrg --num_arguments; 11925dfecf96Smrg /* no error if it isn't a number? */ 11935dfecf96Smrg if (FIXNUMP(object)) 11945dfecf96Smrg choice = FIXNUM_VALUE(object); 11955dfecf96Smrg } 11965dfecf96Smrg 11975dfecf96Smrg /* update anything that may have changed */ 11985dfecf96Smrg *(info->object) = object; 11995dfecf96Smrg *(info->arguments) = arguments; 12005dfecf96Smrg *(info->num_arguments) = num_arguments; 12015dfecf96Smrg 12025dfecf96Smrg /* if choice is out of range check if there is a default choice */ 12035dfecf96Smrg if (has_default && (choice < 0 || choice >= num_formats)) 12045dfecf96Smrg choice = num_formats - 1; 12055dfecf96Smrg 12065dfecf96Smrg /* if one of the formats must be parsed */ 12075dfecf96Smrg if (choice >= 0 && choice < num_formats) { 12085dfecf96Smrg FmtInfo conditional_info; 12095dfecf96Smrg 12105dfecf96Smrg /* most information is the same */ 12115dfecf96Smrg memcpy(&conditional_info, info, sizeof(FmtInfo)); 12125dfecf96Smrg 12135dfecf96Smrg /* set new format string */ 12145dfecf96Smrg format = formats[choice]; 12155dfecf96Smrg conditional_info.args.base = conditional_info.args.format = format; 12165dfecf96Smrg conditional_info.format = &format; 12175dfecf96Smrg 12185dfecf96Smrg /* do the conditional format */ 12195dfecf96Smrg LispFormat(stream, &conditional_info); 12205dfecf96Smrg } 12215dfecf96Smrg 12225dfecf96Smrg /* free temporary memory */ 12235dfecf96Smrg free_formats(formats, num_formats); 12245dfecf96Smrg 12255dfecf96Smrg /* this information always updated */ 12265dfecf96Smrg *(info->format) = next_format; 12275dfecf96Smrg} 12285dfecf96Smrg 12295dfecf96Smrgstatic void 12305dfecf96Smrgformat_iterate(LispObj *stream, FmtInfo *info) 12315dfecf96Smrg{ 12325dfecf96Smrg FmtInfo iterate_info; 12335dfecf96Smrg LispObj *object, *arguments, *iarguments, *iobject; 12345dfecf96Smrg char *format, *next_format, *loop_format, **formats; 12355dfecf96Smrg int num_arguments, iterate, iterate_max, has_max, has_min, inum_arguments, 12365dfecf96Smrg num_formats; 12375dfecf96Smrg 12385dfecf96Smrg /* save information that may change */ 12395dfecf96Smrg object = *(info->object); 12405dfecf96Smrg arguments = *(info->arguments); 12415dfecf96Smrg num_arguments = *(info->num_arguments); 12425dfecf96Smrg 12435dfecf96Smrg /* initialize */ 12445dfecf96Smrg iterate = has_min = 0; 12455dfecf96Smrg next_format = *(info->format); 12465dfecf96Smrg 12475dfecf96Smrg /* if has_max set, iterate at most iterate_max times */ 12485dfecf96Smrg has_max = info->args.arguments[0].specified; 12495dfecf96Smrg iterate_max = info->args.arguments[0].value; 12505dfecf96Smrg 12515dfecf96Smrg /* list formats */ 12525dfecf96Smrg list_formats(info, '{', &next_format, &formats, &num_formats, 12535dfecf96Smrg NULL, NULL, NULL); 12545dfecf96Smrg loop_format = formats[0]; 12555dfecf96Smrg 12565dfecf96Smrg /* most information is the same */ 12575dfecf96Smrg memcpy(&iterate_info, info, sizeof(FmtInfo)); 12585dfecf96Smrg 12595dfecf96Smrg /* ~{...~} */ 12605dfecf96Smrg if (!info->args.atsign && !info->args.collon) { 12615dfecf96Smrg /* next argument is the argument list for the iteration */ 12625dfecf96Smrg 12635dfecf96Smrg /* fetch argument list, must exist */ 12645dfecf96Smrg if (!CONSP(arguments)) 12655dfecf96Smrg parse_error(&(info->args), PARSE_NOARGSLEFT); 12665dfecf96Smrg iarguments = object = CAR(arguments); 12675dfecf96Smrg object = CAR(arguments); 12685dfecf96Smrg arguments = CDR(arguments); 12695dfecf96Smrg --num_arguments; 12705dfecf96Smrg 12715dfecf96Smrg inum_arguments = 0; 12725dfecf96Smrg if (CONSP(object)) { 12735dfecf96Smrg /* count arguments to format */ 12745dfecf96Smrg for (iobject = object; CONSP(iobject); iobject = CDR(iobject)) 12755dfecf96Smrg ++inum_arguments; 12765dfecf96Smrg } 12775dfecf96Smrg else if (object != NIL) 12785dfecf96Smrg generic_error(&(info->args), GENERIC_BADLIST); 12795dfecf96Smrg 12805dfecf96Smrg iobject = NIL; 12815dfecf96Smrg 12825dfecf96Smrg /* set new arguments to recursive calls */ 12835dfecf96Smrg iarguments = object; 12845dfecf96Smrg iterate_info.base_arguments = iarguments; 12855dfecf96Smrg iterate_info.total_arguments = inum_arguments; 12865dfecf96Smrg iterate_info.object = &iobject; 12875dfecf96Smrg iterate_info.arguments = &iarguments; 12885dfecf96Smrg iterate_info.num_arguments = &inum_arguments; 12895dfecf96Smrg 12905dfecf96Smrg /* iterate */ 12915dfecf96Smrg for (;; iterate++) { 12925dfecf96Smrg /* if maximum iterations done or all arguments consumed */ 12935dfecf96Smrg if (has_max && iterate > iterate_max) 12945dfecf96Smrg break; 12955dfecf96Smrg else if (inum_arguments == 0 && (!has_min || iterate > 0)) 12965dfecf96Smrg break; 12975dfecf96Smrg 12985dfecf96Smrg format = loop_format; 12995dfecf96Smrg 13005dfecf96Smrg /* set new format string */ 13015dfecf96Smrg iterate_info.args.base = iterate_info.args.format = format; 13025dfecf96Smrg iterate_info.format = &format; 13035dfecf96Smrg 13045dfecf96Smrg /* information for possible ~^, in this case ~:^ is a noop */ 13055dfecf96Smrg iterate_info.iteration = ITERATION_NORMAL; 13065dfecf96Smrg 13075dfecf96Smrg /* do the format */ 13085dfecf96Smrg LispFormat(stream, &iterate_info); 13095dfecf96Smrg 13105dfecf96Smrg /* check for forced loop break */ 13115dfecf96Smrg if (iterate_info.upandout & UPANDOUT_HASH) 13125dfecf96Smrg break; 13135dfecf96Smrg } 13145dfecf96Smrg } 13155dfecf96Smrg /* ~:@{...~} */ 13165dfecf96Smrg else if (info->args.atsign && info->args.collon) { 13175dfecf96Smrg /* every following argument is the argument list for the iteration */ 13185dfecf96Smrg 13195dfecf96Smrg /* iterate */ 13205dfecf96Smrg for (;; iterate++) { 13215dfecf96Smrg /* if maximum iterations done or all arguments consumed */ 13225dfecf96Smrg if (has_max && iterate > iterate_max) 13235dfecf96Smrg break; 13245dfecf96Smrg else if (num_arguments == 0 && (!has_min || iterate > 0)) 13255dfecf96Smrg break; 13265dfecf96Smrg 13275dfecf96Smrg /* fetch argument list, must exist */ 13285dfecf96Smrg if (!CONSP(arguments)) 13295dfecf96Smrg parse_error(&(info->args), PARSE_NOARGSLEFT); 13305dfecf96Smrg iarguments = object = CAR(arguments); 13315dfecf96Smrg object = CAR(arguments); 13325dfecf96Smrg arguments = CDR(arguments); 13335dfecf96Smrg --num_arguments; 13345dfecf96Smrg 13355dfecf96Smrg inum_arguments = 0; 13365dfecf96Smrg if (CONSP(object)) { 13375dfecf96Smrg /* count arguments to format */ 13385dfecf96Smrg for (iobject = object; CONSP(iobject); iobject = CDR(iobject)) 13395dfecf96Smrg ++inum_arguments; 13405dfecf96Smrg } 13415dfecf96Smrg else if (object != NIL) 13425dfecf96Smrg generic_error(&(info->args), GENERIC_BADLIST); 13435dfecf96Smrg 13445dfecf96Smrg iobject = NIL; 13455dfecf96Smrg 13465dfecf96Smrg /* set new arguments to recursive calls */ 13475dfecf96Smrg iarguments = object; 13485dfecf96Smrg iterate_info.base_arguments = iarguments; 13495dfecf96Smrg iterate_info.total_arguments = inum_arguments; 13505dfecf96Smrg iterate_info.object = &iobject; 13515dfecf96Smrg iterate_info.arguments = &iarguments; 13525dfecf96Smrg iterate_info.num_arguments = &inum_arguments; 13535dfecf96Smrg 13545dfecf96Smrg format = loop_format; 13555dfecf96Smrg 13565dfecf96Smrg /* set new format string */ 13575dfecf96Smrg iterate_info.args.base = iterate_info.args.format = format; 13585dfecf96Smrg iterate_info.format = &format; 13595dfecf96Smrg 13605dfecf96Smrg /* information for possible ~^ */ 13615dfecf96Smrg iterate_info.iteration = 13625dfecf96Smrg num_arguments > 0 ? ITERATION_NORMAL : ITERATION_LAST; 13635dfecf96Smrg 13645dfecf96Smrg /* do the format */ 13655dfecf96Smrg LispFormat(stream, &iterate_info); 13665dfecf96Smrg 13675dfecf96Smrg /* check for forced loop break */ 13685dfecf96Smrg if (iterate_info.upandout & UPANDOUT_HASH) 13695dfecf96Smrg break; 13705dfecf96Smrg } 13715dfecf96Smrg } 13725dfecf96Smrg /* ~:{...~} */ 13735dfecf96Smrg else if (info->args.collon) { 13745dfecf96Smrg /* next argument is a list of lists */ 13755dfecf96Smrg 13765dfecf96Smrg LispObj *sarguments, *sobject; 13775dfecf96Smrg int snum_arguments; 13785dfecf96Smrg 13795dfecf96Smrg /* fetch argument list, must exist */ 13805dfecf96Smrg if (!CONSP(arguments)) 13815dfecf96Smrg parse_error(&(info->args), PARSE_NOARGSLEFT); 13825dfecf96Smrg sarguments = object = CAR(arguments); 13835dfecf96Smrg object = CAR(arguments); 13845dfecf96Smrg arguments = CDR(arguments); 13855dfecf96Smrg --num_arguments; 13865dfecf96Smrg 13875dfecf96Smrg snum_arguments = 0; 13885dfecf96Smrg if (CONSP(object)) { 13895dfecf96Smrg /* count arguments to format */ 13905dfecf96Smrg for (sobject = object; CONSP(sobject); sobject = CDR(sobject)) 13915dfecf96Smrg ++snum_arguments; 13925dfecf96Smrg } 13935dfecf96Smrg else 13945dfecf96Smrg generic_error(&(info->args), GENERIC_BADLIST); 13955dfecf96Smrg 13965dfecf96Smrg /* iterate */ 13975dfecf96Smrg for (;; iterate++) { 13985dfecf96Smrg /* if maximum iterations done or all arguments consumed */ 13995dfecf96Smrg if (has_max && iterate > iterate_max) 14005dfecf96Smrg break; 14015dfecf96Smrg else if (snum_arguments == 0 && (!has_min || iterate > 0)) 14025dfecf96Smrg break; 14035dfecf96Smrg 14045dfecf96Smrg /* fetch argument list, must exist */ 14055dfecf96Smrg if (!CONSP(sarguments)) 14065dfecf96Smrg parse_error(&(info->args), PARSE_NOARGSLEFT); 14075dfecf96Smrg iarguments = sobject = CAR(sarguments); 14085dfecf96Smrg sobject = CAR(sarguments); 14095dfecf96Smrg sarguments = CDR(sarguments); 14105dfecf96Smrg --snum_arguments; 14115dfecf96Smrg 14125dfecf96Smrg inum_arguments = 0; 14135dfecf96Smrg if (CONSP(object)) { 14145dfecf96Smrg /* count arguments to format */ 14155dfecf96Smrg for (iobject = sobject; CONSP(iobject); iobject = CDR(iobject)) 14165dfecf96Smrg ++inum_arguments; 14175dfecf96Smrg } 14185dfecf96Smrg else if (sobject != NIL) 14195dfecf96Smrg generic_error(&(info->args), GENERIC_BADLIST); 14205dfecf96Smrg 14215dfecf96Smrg iobject = NIL; 14225dfecf96Smrg 14235dfecf96Smrg /* set new arguments to recursive calls */ 14245dfecf96Smrg iarguments = sobject; 14255dfecf96Smrg iterate_info.base_arguments = iarguments; 14265dfecf96Smrg iterate_info.total_arguments = inum_arguments; 14275dfecf96Smrg iterate_info.object = &iobject; 14285dfecf96Smrg iterate_info.arguments = &iarguments; 14295dfecf96Smrg iterate_info.num_arguments = &inum_arguments; 14305dfecf96Smrg 14315dfecf96Smrg format = loop_format; 14325dfecf96Smrg 14335dfecf96Smrg /* set new format string */ 14345dfecf96Smrg iterate_info.args.base = iterate_info.args.format = format; 14355dfecf96Smrg iterate_info.format = &format; 14365dfecf96Smrg 14375dfecf96Smrg /* information for possible ~^ */ 14385dfecf96Smrg iterate_info.iteration = 14395dfecf96Smrg snum_arguments > 0 ? ITERATION_NORMAL : ITERATION_LAST; 14405dfecf96Smrg 14415dfecf96Smrg /* do the format */ 14425dfecf96Smrg LispFormat(stream, &iterate_info); 14435dfecf96Smrg 14445dfecf96Smrg /* check for forced loop break */ 14455dfecf96Smrg if (iterate_info.upandout & UPANDOUT_HASH) 14465dfecf96Smrg break; 14475dfecf96Smrg } 14485dfecf96Smrg } 14495dfecf96Smrg /* ~@{...~} */ 14505dfecf96Smrg else if (info->args.atsign) { 14515dfecf96Smrg /* current argument list is used */ 14525dfecf96Smrg 14535dfecf96Smrg /* set new arguments to recursive calls */ 14545dfecf96Smrg iterate_info.base_arguments = info->base_arguments; 14555dfecf96Smrg iterate_info.total_arguments = info->total_arguments; 14565dfecf96Smrg iterate_info.object = &object; 14575dfecf96Smrg iterate_info.arguments = &arguments; 14585dfecf96Smrg iterate_info.num_arguments = &num_arguments; 14595dfecf96Smrg 14605dfecf96Smrg for (;; iterate++) { 14615dfecf96Smrg /* if maximum iterations done or all arguments consumed */ 14625dfecf96Smrg if (has_max && iterate > iterate_max) 14635dfecf96Smrg break; 14645dfecf96Smrg else if (num_arguments == 0 && (!has_min || iterate > 0)) 14655dfecf96Smrg break; 14665dfecf96Smrg 14675dfecf96Smrg format = loop_format; 14685dfecf96Smrg 14695dfecf96Smrg /* set new format string */ 14705dfecf96Smrg iterate_info.args.base = iterate_info.args.format = format; 14715dfecf96Smrg iterate_info.format = &format; 14725dfecf96Smrg 14735dfecf96Smrg /* information for possible ~^, in this case ~:^ is a noop */ 14745dfecf96Smrg iterate_info.iteration = ITERATION_NORMAL; 14755dfecf96Smrg 14765dfecf96Smrg /* do the format */ 14775dfecf96Smrg LispFormat(stream, &iterate_info); 14785dfecf96Smrg 14795dfecf96Smrg /* check for forced loop break */ 14805dfecf96Smrg if (iterate_info.upandout & UPANDOUT_HASH) 14815dfecf96Smrg break; 14825dfecf96Smrg } 14835dfecf96Smrg } 14845dfecf96Smrg 14855dfecf96Smrg /* free temporary memory */ 14865dfecf96Smrg free_formats(formats, num_formats); 14875dfecf96Smrg 14885dfecf96Smrg /* update anything that may have changed */ 14895dfecf96Smrg *(info->object) = object; 14905dfecf96Smrg *(info->arguments) = arguments; 14915dfecf96Smrg *(info->num_arguments) = num_arguments; 14925dfecf96Smrg 14935dfecf96Smrg /* this information always updated */ 14945dfecf96Smrg *(info->format) = next_format; 14955dfecf96Smrg} 14965dfecf96Smrg 14975dfecf96Smrgstatic void 14985dfecf96Smrgformat_justify(LispObj *stream, FmtInfo *info) 14995dfecf96Smrg{ 15005dfecf96Smrg GC_ENTER(); 15015dfecf96Smrg FmtInfo justify_info; 1502f765521fSmrg char **formats, *format, *next_format; 1503f765521fSmrg const char *str; 15045dfecf96Smrg LispObj *string, *strings = NIL, *cons; 15055dfecf96Smrg int atsign = info->args.atsign, 15065dfecf96Smrg collon = info->args.collon, 15075dfecf96Smrg mincol = info->args.arguments[0].value, 15085dfecf96Smrg colinc = info->args.arguments[1].value, 15095dfecf96Smrg minpad = info->args.arguments[2].value, 15105dfecf96Smrg padchar = info->args.arguments[3].value; 15115dfecf96Smrg int i, k, total_length, length, padding, num_formats, has_default, 15125dfecf96Smrg comma_width, line_width, size, extra; 15135dfecf96Smrg 15145dfecf96Smrg next_format = *(info->format); 15155dfecf96Smrg 15165dfecf96Smrg /* list formats */ 15175dfecf96Smrg list_formats(info, '<', &next_format, &formats, &num_formats, 15185dfecf96Smrg &has_default, &comma_width, &line_width); 15195dfecf96Smrg 15205dfecf96Smrg /* initialize list of strings streams */ 15215dfecf96Smrg if (num_formats) { 15225dfecf96Smrg string = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0); 15235dfecf96Smrg strings = cons = CONS(string, NIL); 15245dfecf96Smrg GC_PROTECT(strings); 15255dfecf96Smrg for (i = 1; i < num_formats; i++) { 15265dfecf96Smrg string = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0); 15275dfecf96Smrg RPLACD(cons, CONS(string, NIL)); 15285dfecf96Smrg cons = CDR(cons); 15295dfecf96Smrg } 15305dfecf96Smrg } 15315dfecf96Smrg 15325dfecf96Smrg /* most information is the same */ 15335dfecf96Smrg memcpy(&justify_info, info, sizeof(FmtInfo)); 15345dfecf96Smrg 15355dfecf96Smrg /* loop formating strings */ 15365dfecf96Smrg for (i = 0, cons = strings; i < num_formats; i++, cons = CDR(cons)) { 15375dfecf96Smrg /* set new format string */ 15385dfecf96Smrg format = formats[i]; 15395dfecf96Smrg justify_info.args.base = justify_info.args.format = format; 15405dfecf96Smrg justify_info.format = &format; 15415dfecf96Smrg 15425dfecf96Smrg /* format string, maybe consuming arguments */ 15435dfecf96Smrg LispFormat(CAR(cons), &justify_info); 15445dfecf96Smrg 15455dfecf96Smrg /* if format was aborted, it is discarded */ 15465dfecf96Smrg if (justify_info.upandout) 15475dfecf96Smrg RPLACA(cons, NIL); 15485dfecf96Smrg /* check if the entire "main" iteration must be aborted */ 15495dfecf96Smrg if (justify_info.upandout & UPANDOUT_COLLON) { 15505dfecf96Smrg for (cons = CDR(cons); i < num_formats; i++, cons = CDR(cons)) 15515dfecf96Smrg RPLACA(cons, NIL); 15525dfecf96Smrg break; 15535dfecf96Smrg } 15545dfecf96Smrg } 15555dfecf96Smrg 15565dfecf96Smrg /* free temporary format strings */ 15575dfecf96Smrg free_formats(formats, num_formats); 15585dfecf96Smrg 15595dfecf96Smrg /* remove aborted formats */ 15605dfecf96Smrg /* first remove leading discarded formats */ 15615dfecf96Smrg if (CAR(strings) == NIL) { 15625dfecf96Smrg while (CAR(strings) == NIL) { 15635dfecf96Smrg strings = CDR(strings); 15645dfecf96Smrg --num_formats; 15655dfecf96Smrg } 15665dfecf96Smrg /* keep strings gc protected, discarding first entries */ 15675dfecf96Smrg lisp__data.protect.objects[gc__protect] = strings; 15685dfecf96Smrg } 15695dfecf96Smrg /* now remove intermediary discarded formats */ 15705dfecf96Smrg cons = strings; 15715dfecf96Smrg while (CONSP(cons)) { 15725dfecf96Smrg if (CONSP(CDR(cons)) && CAR(CDR(cons)) == NIL) { 15735dfecf96Smrg RPLACD(cons, CDR(CDR(cons))); 15745dfecf96Smrg --num_formats; 15755dfecf96Smrg } 15765dfecf96Smrg else 15775dfecf96Smrg cons = CDR(cons); 15785dfecf96Smrg } 15795dfecf96Smrg 15805dfecf96Smrg /* calculate total length required for output */ 15815dfecf96Smrg if (has_default) 15825dfecf96Smrg cons = CDR(strings); /* if has_defaults, strings is surely a list */ 15835dfecf96Smrg else 15845dfecf96Smrg cons = strings; 15855dfecf96Smrg for (total_length = 0; CONSP(cons); cons = CDR(cons)) 15865dfecf96Smrg total_length += SSTREAMP(CAR(cons))->length; 15875dfecf96Smrg 15885dfecf96Smrg /* initialize pointer to string streams */ 15895dfecf96Smrg if (has_default) 15905dfecf96Smrg cons = CDR(strings); 15915dfecf96Smrg else 15925dfecf96Smrg cons = strings; 15935dfecf96Smrg 15945dfecf96Smrg /* check if padding will need to be printed */ 15955dfecf96Smrg extra = 0; 15965dfecf96Smrg padding = mincol - total_length; 15975dfecf96Smrg if (padding < 0) 15985dfecf96Smrg k = padding = 0; 15995dfecf96Smrg else { 16005dfecf96Smrg int num_fields = num_formats - (has_default != 0); 16015dfecf96Smrg 16025dfecf96Smrg if (num_fields > 1) { 16035dfecf96Smrg /* check if padding is distributed in num_fields or 16045dfecf96Smrg * num_fields - 1 steps */ 16055dfecf96Smrg if (!collon) 16065dfecf96Smrg --num_fields; 16075dfecf96Smrg } 16085dfecf96Smrg 16095dfecf96Smrg if (num_fields) 16105dfecf96Smrg k = padding / num_fields; 16115dfecf96Smrg else 16125dfecf96Smrg k = padding; 16135dfecf96Smrg 16145dfecf96Smrg if (k <= 0) 16155dfecf96Smrg k = colinc; 16165dfecf96Smrg else if (colinc) 16175dfecf96Smrg k = k + (k % colinc); 16185dfecf96Smrg extra = mincol - (num_fields * k + total_length); 16195dfecf96Smrg if (extra < 0) 16205dfecf96Smrg extra = 0; 16215dfecf96Smrg } 16225dfecf96Smrg if (padding && k < minpad) { 16235dfecf96Smrg k = minpad; 16245dfecf96Smrg if (colinc) 16255dfecf96Smrg k = k + (k % colinc); 16265dfecf96Smrg } 16275dfecf96Smrg 16285dfecf96Smrg /* first check for the special case of only one string being justified */ 16295dfecf96Smrg if (num_formats - has_default == 1) { 16305dfecf96Smrg if (has_default && line_width > 0 && comma_width >= 0 && 16315dfecf96Smrg total_length + comma_width > line_width) { 16325dfecf96Smrg str = LispGetSstring(SSTREAMP(CAR(strings)), &size); 16335dfecf96Smrg LispWriteStr(stream, str, size); 16345dfecf96Smrg } 16355dfecf96Smrg string = has_default ? CAR(CDR(strings)) : CAR(strings); 16365dfecf96Smrg /* check if need left padding */ 16375dfecf96Smrg if (k && !atsign) { 16385dfecf96Smrg LispWriteChars(stream, padchar, k); 16395dfecf96Smrg k = 0; 16405dfecf96Smrg } 16415dfecf96Smrg /* check for centralizing text */ 16425dfecf96Smrg else if (k && atsign && collon) { 16435dfecf96Smrg LispWriteChars(stream, padchar, k / 2 + ((k / 2) & 1)); 16445dfecf96Smrg k -= k / 2; 16455dfecf96Smrg } 16465dfecf96Smrg str = LispGetSstring(SSTREAMP(string), &size); 16475dfecf96Smrg LispWriteStr(stream, str, size); 16485dfecf96Smrg /* if any padding remaining */ 16495dfecf96Smrg if (k) 16505dfecf96Smrg LispWriteChars(stream, padchar, k); 16515dfecf96Smrg } 16525dfecf96Smrg else { 16535dfecf96Smrg LispObj *result; 16545dfecf96Smrg int last, spaces_before, padout; 16555dfecf96Smrg 16565dfecf96Smrg /* if has default, need to check output length */ 16575dfecf96Smrg if (has_default && line_width > 0 && comma_width >= 0) { 16585dfecf96Smrg result = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0); 16595dfecf96Smrg GC_PROTECT(result); 16605dfecf96Smrg } 16615dfecf96Smrg /* else write directly to stream */ 16625dfecf96Smrg else 16635dfecf96Smrg result = stream; 16645dfecf96Smrg 16655dfecf96Smrg /* loop printing justified text */ 16665dfecf96Smrg /* padout controls padding for cases where padding is 16675dfecf96Smrg * is separated in n-1 chunks, where n is the number of 16685dfecf96Smrg * formatted strings. 16695dfecf96Smrg */ 16705dfecf96Smrg for (i = padout = 0; CONSP(cons); i++, cons = CDR(cons), --extra) { 16715dfecf96Smrg string = CAR(cons); 16725dfecf96Smrg last = !CONSP(CDR(cons)); 16735dfecf96Smrg 16745dfecf96Smrg spaces_before = (i != 0 || collon) && (!last || !atsign); 16755dfecf96Smrg 16765dfecf96Smrg if (!spaces_before) { 16775dfecf96Smrg /* check for special case */ 16785dfecf96Smrg if (last && atsign && collon && padding > 0) { 16795dfecf96Smrg int spaces; 16805dfecf96Smrg 16815dfecf96Smrg spaces = minpad > colinc ? minpad : colinc; 16825dfecf96Smrg LispWriteChars(result, padchar, spaces + (extra > 0)); 16835dfecf96Smrg k -= spaces; 16845dfecf96Smrg } 16855dfecf96Smrg str = LispGetSstring(SSTREAMP(string), &size); 16865dfecf96Smrg LispWriteStr(result, str, size); 16875dfecf96Smrg padout = 0; 16885dfecf96Smrg } 16895dfecf96Smrg if (!padout) 16905dfecf96Smrg LispWriteChars(result, padchar, k + (extra > 0)); 16915dfecf96Smrg padout = k; 16925dfecf96Smrg /* if not first string, or if left padding specified */ 16935dfecf96Smrg if (spaces_before) { 16945dfecf96Smrg str = LispGetSstring(SSTREAMP(string), &size); 16955dfecf96Smrg LispWriteStr(result, str, size); 16965dfecf96Smrg padout = 0; 16975dfecf96Smrg } 16985dfecf96Smrg padding -= k; 16995dfecf96Smrg } 17005dfecf96Smrg 17015dfecf96Smrg if (has_default && line_width > 0 && comma_width >= 0) { 17025dfecf96Smrg length = SSTREAMP(result)->length + LispGetColumn(stream); 17035dfecf96Smrg 17045dfecf96Smrg /* if current line is too large */ 17055dfecf96Smrg if (has_default && length + comma_width > line_width) { 17065dfecf96Smrg str = LispGetSstring(SSTREAMP(CAR(strings)), &size); 17075dfecf96Smrg LispWriteStr(stream, str, size); 17085dfecf96Smrg } 17095dfecf96Smrg 17105dfecf96Smrg /* write result to stream */ 17115dfecf96Smrg str = LispGetSstring(SSTREAMP(result), &size); 17125dfecf96Smrg LispWriteStr(stream, str, size); 17135dfecf96Smrg } 17145dfecf96Smrg } 17155dfecf96Smrg 17165dfecf96Smrg /* unprotect string streams from GC */ 17175dfecf96Smrg GC_LEAVE(); 17185dfecf96Smrg 17195dfecf96Smrg /* this information always updated */ 17205dfecf96Smrg *(info->format) = next_format; 17215dfecf96Smrg} 17225dfecf96Smrg 17235dfecf96Smrgstatic void 17245dfecf96SmrgLispFormat(LispObj *stream, FmtInfo *info) 17255dfecf96Smrg{ 17265dfecf96Smrg FmtArgs *args; 1727f765521fSmrg const FmtDefs *defs = NULL; 17285dfecf96Smrg LispObj *object, *arguments; 17295dfecf96Smrg char stk[256], *format, *next_format; 17305dfecf96Smrg int length, num_arguments, code, need_update, need_argument, hash, head; 17315dfecf96Smrg 17325dfecf96Smrg /* arguments that will be updated on function exit */ 17335dfecf96Smrg format = *(info->format); 17345dfecf96Smrg object = *(info->object); 17355dfecf96Smrg arguments = *(info->arguments); 17365dfecf96Smrg num_arguments = *(info->num_arguments); 17375dfecf96Smrg 17385dfecf96Smrg /* initialize */ 17395dfecf96Smrg length = 0; 17405dfecf96Smrg args = &(info->args); 17415dfecf96Smrg info->upandout = 0; 17425dfecf96Smrg 17435dfecf96Smrg while (*format) { 17445dfecf96Smrg if (*format == '~') { 17455dfecf96Smrg /* flush non formatted characters */ 17465dfecf96Smrg if (length) { 17475dfecf96Smrg LispWriteStr(stream, stk, length); 17485dfecf96Smrg length = 0; 17495dfecf96Smrg } 17505dfecf96Smrg 17515dfecf96Smrg need_argument = need_update = hash = 0; 17525dfecf96Smrg 17535dfecf96Smrg /* parse parameters */ 17545dfecf96Smrg args->format = format + 1; 17555dfecf96Smrg next_format = parse_arguments(format + 1, args, &num_arguments, 17565dfecf96Smrg &arguments, &code); 17575dfecf96Smrg if (code != NOERROR) 17585dfecf96Smrg parse_error(args, code); 17595dfecf96Smrg 17605dfecf96Smrg /* check parameters */ 17615dfecf96Smrg switch (args->command) { 17625dfecf96Smrg case 'A': case 'S': 17635dfecf96Smrg defs = &AsciiDefs; 17645dfecf96Smrg break; 17655dfecf96Smrg case 'B': case 'O': case 'D': case 'X': 17665dfecf96Smrg defs = &IntegerDefs; 17675dfecf96Smrg break; 17685dfecf96Smrg case 'R': 17695dfecf96Smrg defs = &RadixDefs; 17705dfecf96Smrg break; 17715dfecf96Smrg case 'P': case 'C': 17725dfecf96Smrg defs = &NoneDefs; 17735dfecf96Smrg break; 17745dfecf96Smrg case 'F': 17755dfecf96Smrg defs = &FixedFloatDefs; 17765dfecf96Smrg break; 17775dfecf96Smrg case 'E': case 'G': 17785dfecf96Smrg defs = &ExponentialFloatDefs; 17795dfecf96Smrg break; 17805dfecf96Smrg case '$': 17815dfecf96Smrg defs = &DollarFloatDefs; 17825dfecf96Smrg break; 17835dfecf96Smrg case '%': case '&': case '|': case '~': case '\n': 17845dfecf96Smrg defs = &OneDefs; 17855dfecf96Smrg break; 17865dfecf96Smrg case 'T': 17875dfecf96Smrg defs = &TabulateDefs; 17885dfecf96Smrg break; 17895dfecf96Smrg case '*': 17905dfecf96Smrg defs = &OneDefs; 17915dfecf96Smrg break; 17925dfecf96Smrg case '?': case '(': 17935dfecf96Smrg defs = &NoneDefs; 17945dfecf96Smrg break; 17955dfecf96Smrg case ')': 17965dfecf96Smrg /* this is never seen, processed in format_case_conversion */ 17975dfecf96Smrg format_error(args, "no match for directive ~)"); 17985dfecf96Smrg case '[': 17995dfecf96Smrg defs = &OneDefs; 18005dfecf96Smrg break; 18015dfecf96Smrg case ']': 18025dfecf96Smrg /* this is never seen, processed in format_conditional */ 18035dfecf96Smrg format_error(args, "no match for directive ~]"); 18045dfecf96Smrg case '{': 18055dfecf96Smrg defs = &OneDefs; 18065dfecf96Smrg break; 18075dfecf96Smrg case '}': 18085dfecf96Smrg /* this is never seen, processed in format_iterate */ 18095dfecf96Smrg format_error(args, "no match for directive ~}"); 18105dfecf96Smrg case '<': 18115dfecf96Smrg defs = &AsciiDefs; 18125dfecf96Smrg break; 18135dfecf96Smrg case '>': 18145dfecf96Smrg /* this is never seen, processed in format_justify */ 18155dfecf96Smrg format_error(args, "no match for directive ~>"); 18165dfecf96Smrg case ';': 18175dfecf96Smrg /* this is never seen here */ 18185dfecf96Smrg format_error(args, "misplaced directive ~;"); 18195dfecf96Smrg case '#': 18205dfecf96Smrg /* special handling for ~#^ */ 18215dfecf96Smrg if (*next_format == '^') { 18225dfecf96Smrg ++next_format; 18235dfecf96Smrg hash = 1; 18245dfecf96Smrg defs = &NoneDefs; 18255dfecf96Smrg args->command = '^'; 18265dfecf96Smrg break; 18275dfecf96Smrg } 18285dfecf96Smrg parse_error(args, PARSE_BADDIRECTIVE); 18295dfecf96Smrg case '^': 18305dfecf96Smrg defs = &NoneDefs; 18315dfecf96Smrg break; 18325dfecf96Smrg default: 18335dfecf96Smrg parse_error(args, PARSE_BADDIRECTIVE); 18345dfecf96Smrg break; 18355dfecf96Smrg } 18365dfecf96Smrg merge_arguments(args, defs, &code); 18375dfecf96Smrg if (code != NOERROR) 18385dfecf96Smrg merge_error(args, code); 18395dfecf96Smrg 18405dfecf96Smrg /* check if an argument is required by directive */ 18415dfecf96Smrg switch (args->command) { 18425dfecf96Smrg case 'A': case 'S': 18435dfecf96Smrg case 'B': case 'O': case 'D': case 'X': case 'R': 18445dfecf96Smrg need_argument = 1; 18455dfecf96Smrg break; 18465dfecf96Smrg case 'P': 18475dfecf96Smrg /* if collon specified, plural is the last print argument */ 18485dfecf96Smrg need_argument = !args->collon; 18495dfecf96Smrg break; 18505dfecf96Smrg case 'C': 18515dfecf96Smrg need_argument = 1; 18525dfecf96Smrg break; 18535dfecf96Smrg case 'F': case 'E': case 'G': case '$': 18545dfecf96Smrg need_argument = 1; 18555dfecf96Smrg break; 18565dfecf96Smrg case '%': case '&': case '|': case '~': case '\n': 18575dfecf96Smrg break; 18585dfecf96Smrg case 'T': 18595dfecf96Smrg break; 18605dfecf96Smrg case '*': /* check arguments below */ 18615dfecf96Smrg need_update = 1; 18625dfecf96Smrg break; 18635dfecf96Smrg case '?': 18645dfecf96Smrg need_argument = need_update = 1; 18655dfecf96Smrg break; 18665dfecf96Smrg case '(': case '[': case '{': case '<': 18675dfecf96Smrg need_update = 1; 18685dfecf96Smrg break; 18695dfecf96Smrg case '^': 18705dfecf96Smrg break; 18715dfecf96Smrg } 18725dfecf96Smrg if (need_argument) { 18735dfecf96Smrg if (!CONSP(arguments)) 18745dfecf96Smrg parse_error(args, PARSE_NOARGSLEFT); 18755dfecf96Smrg object = CAR(arguments); 18765dfecf96Smrg arguments = CDR(arguments); 18775dfecf96Smrg --num_arguments; 18785dfecf96Smrg } 18795dfecf96Smrg 18805dfecf96Smrg /* will do recursive calls that change info */ 18815dfecf96Smrg if (need_update) { 18825dfecf96Smrg *(info->format) = next_format; 18835dfecf96Smrg *(info->object) = object; 18845dfecf96Smrg *(info->arguments) = arguments; 18855dfecf96Smrg *(info->num_arguments) = num_arguments; 18865dfecf96Smrg } 18875dfecf96Smrg 18885dfecf96Smrg /* everything seens fine, print the format directive */ 18895dfecf96Smrg switch (args->command) { 18905dfecf96Smrg case 'A': 18915dfecf96Smrg head = lisp__data.env.length; 18925dfecf96Smrg LispAddVar(Oprint_escape, NIL); 18935dfecf96Smrg ++lisp__data.env.head; 18945dfecf96Smrg format_ascii(stream, object, args); 18955dfecf96Smrg lisp__data.env.head = lisp__data.env.length = head; 18965dfecf96Smrg break; 18975dfecf96Smrg case 'S': 18985dfecf96Smrg head = lisp__data.env.length; 18995dfecf96Smrg LispAddVar(Oprint_escape, T); 19005dfecf96Smrg ++lisp__data.env.head; 19015dfecf96Smrg format_ascii(stream, object, args); 19025dfecf96Smrg lisp__data.env.head = lisp__data.env.length = head; 19035dfecf96Smrg break; 19045dfecf96Smrg case 'B': 19055dfecf96Smrg format_in_radix(stream, object, 2, args); 19065dfecf96Smrg break; 19075dfecf96Smrg case 'O': 19085dfecf96Smrg format_in_radix(stream, object, 8, args); 19095dfecf96Smrg break; 19105dfecf96Smrg case 'D': 19115dfecf96Smrg format_in_radix(stream, object, 10, args); 19125dfecf96Smrg break; 19135dfecf96Smrg case 'X': 19145dfecf96Smrg format_in_radix(stream, object, 16, args); 19155dfecf96Smrg break; 19165dfecf96Smrg case 'R': 19175dfecf96Smrg /* if a single argument specified */ 19185dfecf96Smrg if (args->count) 19195dfecf96Smrg format_in_radix(stream, object, 0, args); 19205dfecf96Smrg else 19215dfecf96Smrg format_radix_special(stream, object, args); 19225dfecf96Smrg break; 19235dfecf96Smrg case 'P': 19245dfecf96Smrg if (args->atsign) { 19255dfecf96Smrg if (FIXNUMP(object) && FIXNUM_VALUE(object) == 1) 19265dfecf96Smrg LispWriteChar(stream, 'y'); 19275dfecf96Smrg else 19285dfecf96Smrg LispWriteStr(stream, "ies", 3); 19295dfecf96Smrg } 19305dfecf96Smrg else if (!FIXNUMP(object) || FIXNUM_VALUE(object) != 1) 19315dfecf96Smrg LispWriteChar(stream, 's'); 19325dfecf96Smrg break; 19335dfecf96Smrg case 'C': 19345dfecf96Smrg format_character(stream, object, args); 19355dfecf96Smrg break; 19365dfecf96Smrg case 'F': 19375dfecf96Smrg format_fixed_float(stream, object, args); 19385dfecf96Smrg break; 19395dfecf96Smrg case 'E': 19405dfecf96Smrg format_exponential_float(stream, object, args); 19415dfecf96Smrg break; 19425dfecf96Smrg case 'G': 19435dfecf96Smrg format_general_float(stream, object, args); 19445dfecf96Smrg break; 19455dfecf96Smrg case '$': 19465dfecf96Smrg format_dollar_float(stream, object, args); 19475dfecf96Smrg break; 19485dfecf96Smrg case '&': 19495dfecf96Smrg if (LispGetColumn(stream) == 0) 19505dfecf96Smrg --args->arguments[0].value; 19515dfecf96Smrg case '%': 19525dfecf96Smrg LispWriteChars(stream, '\n', args->arguments[0].value); 19535dfecf96Smrg break; 19545dfecf96Smrg case '|': 19555dfecf96Smrg LispWriteChars(stream, '\f', args->arguments[0].value); 19565dfecf96Smrg break; 19575dfecf96Smrg case '~': 19585dfecf96Smrg LispWriteChars(stream, '~', args->arguments[0].value); 19595dfecf96Smrg break; 19605dfecf96Smrg case '\n': 19615dfecf96Smrg if (!args->collon) { 19625dfecf96Smrg if (args->atsign) 19635dfecf96Smrg LispWriteChar(stream, '\n'); 19645dfecf96Smrg /* ignore newline and following spaces */ 19655dfecf96Smrg while (*next_format && isspace(*next_format)) 19665dfecf96Smrg ++next_format; 19675dfecf96Smrg } 19685dfecf96Smrg break; 19695dfecf96Smrg case 'T': 19705dfecf96Smrg format_tabulate(stream, args); 19715dfecf96Smrg break; 19725dfecf96Smrg case '*': 19735dfecf96Smrg format_goto(info); 19745dfecf96Smrg break; 19755dfecf96Smrg case '?': 19765dfecf96Smrg format_indirection(stream, object, info); 19775dfecf96Smrg need_update = 1; 19785dfecf96Smrg break; 19795dfecf96Smrg case '(': 19805dfecf96Smrg format_case_conversion(stream, info); 19815dfecf96Smrg /* next_format if far from what is set now */ 19825dfecf96Smrg next_format = *(info->format); 19835dfecf96Smrg break; 19845dfecf96Smrg case '[': 19855dfecf96Smrg format_conditional(stream, info); 19865dfecf96Smrg /* next_format if far from what is set now */ 19875dfecf96Smrg next_format = *(info->format); 19885dfecf96Smrg break; 19895dfecf96Smrg case '{': 19905dfecf96Smrg format_iterate(stream, info); 19915dfecf96Smrg /* next_format if far from what is set now */ 19925dfecf96Smrg next_format = *(info->format); 19935dfecf96Smrg break; 19945dfecf96Smrg case '<': 19955dfecf96Smrg format_justify(stream, info); 19965dfecf96Smrg /* next_format if far from what is set now */ 19975dfecf96Smrg next_format = *(info->format); 19985dfecf96Smrg break; 19995dfecf96Smrg case '^': 20005dfecf96Smrg if (args->collon) { 20015dfecf96Smrg if (hash && num_arguments == 0) { 20025dfecf96Smrg info->upandout = UPANDOUT_HASH; 20035dfecf96Smrg goto format_up_and_out; 20045dfecf96Smrg } 20055dfecf96Smrg if (info->iteration && 20065dfecf96Smrg info->iteration == ITERATION_NORMAL) 20075dfecf96Smrg /* not exactly an error, but in this case, 20085dfecf96Smrg * command is ignored */ 20095dfecf96Smrg break; 20105dfecf96Smrg info->upandout = UPANDOUT_COLLON; 20115dfecf96Smrg goto format_up_and_out; 20125dfecf96Smrg } 20135dfecf96Smrg else if (num_arguments == 0) { 20145dfecf96Smrg info->upandout = UPANDOUT_NORMAL; 20155dfecf96Smrg goto format_up_and_out; 20165dfecf96Smrg } 20175dfecf96Smrg break; 20185dfecf96Smrg } 20195dfecf96Smrg 20205dfecf96Smrg if (need_update) { 20215dfecf96Smrg object = *(info->object); 20225dfecf96Smrg arguments = *(info->arguments); 20235dfecf96Smrg num_arguments = *(info->num_arguments); 20245dfecf96Smrg } 20255dfecf96Smrg 20265dfecf96Smrg format = next_format; 20275dfecf96Smrg } 20285dfecf96Smrg else { 20295dfecf96Smrg if (length >= sizeof(stk)) { 20305dfecf96Smrg LispWriteStr(stream, stk, length); 20315dfecf96Smrg length = 0; 20325dfecf96Smrg } 20335dfecf96Smrg stk[length++] = *format++; 20345dfecf96Smrg } 20355dfecf96Smrg } 20365dfecf96Smrg 20375dfecf96Smrg /* flush any peding output */ 20385dfecf96Smrg if (length) 20395dfecf96Smrg LispWriteStr(stream, stk, length); 20405dfecf96Smrg 20415dfecf96Smrgformat_up_and_out: 20425dfecf96Smrg /* update for recursive call */ 20435dfecf96Smrg *(info->format) = format; 20445dfecf96Smrg *(info->object) = object; 20455dfecf96Smrg *(info->arguments) = arguments; 20465dfecf96Smrg *(info->num_arguments) = num_arguments; 20475dfecf96Smrg} 20485dfecf96Smrg 20495dfecf96SmrgLispObj * 20505dfecf96SmrgLisp_Format(LispBuiltin *builtin) 20515dfecf96Smrg/* 20525dfecf96Smrg format destination control-string &rest arguments 20535dfecf96Smrg */ 20545dfecf96Smrg{ 20555dfecf96Smrg GC_ENTER(); 20565dfecf96Smrg FmtInfo info; 20575dfecf96Smrg LispObj *object; 20585dfecf96Smrg char *control_string; 20595dfecf96Smrg int num_arguments; 20605dfecf96Smrg 20615dfecf96Smrg LispObj *stream, *format, *arguments; 20625dfecf96Smrg 20635dfecf96Smrg arguments = ARGUMENT(2); 20645dfecf96Smrg format = ARGUMENT(1); 20655dfecf96Smrg stream = ARGUMENT(0); 20665dfecf96Smrg 20675dfecf96Smrg /* check format and stream */ 20685dfecf96Smrg CHECK_STRING(format); 20695dfecf96Smrg if (stream == NIL) { /* return a string */ 20705dfecf96Smrg stream = LSTRINGSTREAM("", STREAM_READ | STREAM_WRITE, 0); 20715dfecf96Smrg GC_PROTECT(stream); 20725dfecf96Smrg } 20735dfecf96Smrg else if (stream == T || /* print directly to *standard-output* */ 20745dfecf96Smrg stream == STANDARD_OUTPUT) 20755dfecf96Smrg stream = NIL; 20765dfecf96Smrg else { 20775dfecf96Smrg CHECK_STREAM(stream); 20785dfecf96Smrg if (!stream->data.stream.writable) 20795dfecf96Smrg LispDestroy("%s: stream %s is not writable", 20805dfecf96Smrg STRFUN(builtin), STROBJ(stream)); 20815dfecf96Smrg } 20825dfecf96Smrg 20835dfecf96Smrg /* count number of arguments */ 20845dfecf96Smrg for (object = arguments, num_arguments = 0; CONSP(object); 20855dfecf96Smrg object = CDR(object), num_arguments++) 20865dfecf96Smrg ; 20875dfecf96Smrg 20885dfecf96Smrg /* initialize plural/argument info */ 20895dfecf96Smrg object = NIL; 20905dfecf96Smrg 20915dfecf96Smrg /* the format string */ 20925dfecf96Smrg control_string = THESTR(format); 20935dfecf96Smrg 20945dfecf96Smrg /* arguments to recursive calls */ 20955dfecf96Smrg info.args.base = control_string; 20965dfecf96Smrg info.base_arguments = arguments; 20975dfecf96Smrg info.total_arguments = num_arguments; 20985dfecf96Smrg info.format = &control_string; 20995dfecf96Smrg info.object = &object; 21005dfecf96Smrg info.arguments = &arguments; 21015dfecf96Smrg info.num_arguments = &num_arguments; 21025dfecf96Smrg info.iteration = 0; 21035dfecf96Smrg 21045dfecf96Smrg /* format arguments */ 21055dfecf96Smrg LispFormat(stream, &info); 21065dfecf96Smrg 21075dfecf96Smrg /* if printing to stdout */ 21085dfecf96Smrg if (stream == NIL) 21095dfecf96Smrg LispFflush(Stdout); 21105dfecf96Smrg /* else if printing to string-stream, return a string */ 21115dfecf96Smrg else if (stream->data.stream.type == LispStreamString) { 21125dfecf96Smrg int length; 2113f765521fSmrg const char *string; 21145dfecf96Smrg 21155dfecf96Smrg string = LispGetSstring(SSTREAMP(stream), &length); 21165dfecf96Smrg stream = LSTRING(string, length); 21175dfecf96Smrg } 21185dfecf96Smrg 21195dfecf96Smrg GC_LEAVE(); 21205dfecf96Smrg 21215dfecf96Smrg return (stream); 21225dfecf96Smrg} 2123