539 lines
18 KiB
C
539 lines
18 KiB
C
/* =====> SPRINTF.C */
|
||
/* TIPC Scheme '84 Runtime Support - C Compatible I/O Routines
|
||
(C) Copyright 1984,1985 by Texas Instruments Incorporated.
|
||
All rights reserved.
|
||
|
||
Author: John C. Jensen
|
||
Installation: Texas Instruments Incorporated, Dallas, Texas
|
||
Division: Central Research Laboratories
|
||
Cost Center: Computer Science Laboratory
|
||
Project: Computer Architecture Branch
|
||
Date Written: 7 December 1984
|
||
Last Modification: 10 September 1985 by Rusty Haddock
|
||
*/
|
||
#include "scheme.h"
|
||
|
||
/************************************************************************/
|
||
/* Main Print Driver - printf */
|
||
/* */
|
||
/* Acknowledgement: This routines perform formatted output functions */
|
||
/* similar to that of the Lattice C compiler. */
|
||
/* Some of the following descriptions are */
|
||
/* excerpted from the Lattice 8086/8088 C Compiler */
|
||
/* manual. */
|
||
/* */
|
||
/* Description: The control string (format statement) contains */
|
||
/* ordinary characters, which are sent without */
|
||
/* modification to the standard output port, and */
|
||
/* format specifiers of the form: */
|
||
/* */
|
||
/* %-m.plX */
|
||
/* */
|
||
/* where (1) the optional "-" indicates the field */
|
||
/* is to be left justified (right justified is the */
|
||
/* default); (2) the optional "m" field is a */
|
||
/* decimal number specifying a minimum field width;*/
|
||
/* (3) the optional ".p" field is the character '.'*/
|
||
/* followed by a decimal number specifying the */
|
||
/* precision of a floating point image or the */
|
||
/* maximum number of characters to be printed from */
|
||
/* a string; (4) the optional "l" (letter ell) */
|
||
/* indicates that the item to be formatted is */
|
||
/* "long"; and (5) "X" is one of the format type */
|
||
/* indicators from the following list: */
|
||
/* */
|
||
/* d -- decimal signed integer */
|
||
/* u -- decimal unsigned integer */
|
||
/* x -- hexadecimal integer */
|
||
/* o -- octal integer */
|
||
/* s -- character string */
|
||
/* c -- single character */
|
||
/* f -- fixed decimal floating point */
|
||
/* e -- exponential floating point */
|
||
/* g -- use "e" or "f", whichever is shorter */
|
||
/* */
|
||
/* The format type must be specified in lower case.*/
|
||
/* Characters in the control string which are not */
|
||
/* part of a format specified are sent to the */
|
||
/* output port; a % may be sent by using the */
|
||
/* sequence %%. See the Kernighan and Ritchie */
|
||
/* text for a more detailed explanation of the */
|
||
/* formatted output functions. */
|
||
/************************************************************************/
|
||
|
||
/* working variables visible to all output routines in this module */
|
||
static int auto_CR = TRUE; /* flag for inserting a "carriage return"
|
||
whenever a "line feed" is encountered */
|
||
static int field_width = 0;/* width (number of characters) of format field */
|
||
static int leading_zeros = FALSE; /* indicates if leading zeros desired */
|
||
static int left_justified = FALSE; /* indicates if field left justified */
|
||
static int long_object = FALSE; /* indicates if a data item is "long" */
|
||
static int precision = 0; /* the precision of a floating point image, or
|
||
the maximum number of character to be printed
|
||
from a string */
|
||
|
||
static char digit[16] = {'0','1','2','3','4','5','6','7','8','9',
|
||
'A','B','C','D','E','F'};
|
||
|
||
char *concat_str(); /* concatenate two character strings */
|
||
char *copy_str(); /* make copy of a character string */
|
||
char *fmt_hex(); /* format a hexadecimal value */
|
||
char *fmt_int(); /* format a signed integer value */
|
||
char *fmt_long(); /* format a long signed integer value */
|
||
char *fmt_unsigned(); /* format an unsigned integer value */
|
||
char *getmem(); /* Lattice C's memory allocation routine */
|
||
|
||
printf(fmt, data)
|
||
char *fmt; /* the "format statement" describing the I/O */
|
||
int data; /* the first data object to be printed */
|
||
{
|
||
char *buffer; /* temporary output buffer for converted values */
|
||
int *next; /* pointer to the next object to be printed */
|
||
double *next_float; /* pointer to the next floating point object */
|
||
long *next_long; /* pointer to the next long object to be printed */
|
||
|
||
ENTER(printf);
|
||
|
||
/* set the default port address for the I/O operation */
|
||
ssetadr(ADJPAGE(OUT_PAGE), OUT_DISP);
|
||
|
||
next = &data; /* create a pointer to the next object to print */
|
||
|
||
/* Continue output until format is exhausted */
|
||
while (*fmt)
|
||
{
|
||
if (*fmt == '%')
|
||
{
|
||
buffer = NULL;
|
||
leading_zeros = FALSE;
|
||
left_justified = FALSE;
|
||
long_object = FALSE;
|
||
field_width = 0;
|
||
precision = 0;
|
||
fmt++; /* advance pointer to next character in format */
|
||
|
||
/* test if field is to be left justified */
|
||
if (*fmt == '-')
|
||
{
|
||
left_justified = TRUE;
|
||
fmt++;
|
||
}
|
||
|
||
/* test for request for leading zeros ('0' follows the '%') */
|
||
if (*fmt == '0')
|
||
{
|
||
leading_zeros = TRUE;
|
||
fmt++;
|
||
}
|
||
|
||
/* determine the field width, if specified */
|
||
while (isdigit(*fmt))
|
||
{
|
||
field_width = (field_width * 10) + *fmt - '0';
|
||
fmt++;
|
||
}
|
||
|
||
/* test for a "precision" field */
|
||
if (*fmt == '.')
|
||
{
|
||
fmt++;
|
||
while (isdigit(*fmt))
|
||
{
|
||
precision = (precision * 10) + *fmt - '0';
|
||
fmt++;
|
||
}
|
||
} /* end: if (*fmt == '.') */
|
||
|
||
/* test for a "long" object */
|
||
if (*fmt == 'l')
|
||
{
|
||
long_object = TRUE;
|
||
fmt++;
|
||
}
|
||
|
||
/* decode the format specifier */
|
||
switch (*fmt)
|
||
{
|
||
case 'c': /* single character */
|
||
prtf_character(*next);
|
||
next++;
|
||
break;
|
||
|
||
case 'd': /* decimal signed integer */
|
||
if (long_object)
|
||
{
|
||
next_long = (long *) next;
|
||
buffer = fmt_long(*next_long);
|
||
next++;
|
||
}
|
||
else
|
||
{
|
||
buffer = fmt_int(*next);
|
||
}
|
||
next++;
|
||
break;
|
||
|
||
case 'e':
|
||
case 'f':
|
||
case 'g': /* Floating-point numbers */
|
||
next_float = (double *) next;
|
||
fmt_float(*next_float,*fmt);
|
||
next += 4;
|
||
break;
|
||
|
||
/***** WATCH OUT!!! Lattice C allows nested comments!!!
|
||
case 'o': /* octal integer */
|
||
prtf_string("<%o not implemented>");
|
||
next++;
|
||
break;
|
||
*****/
|
||
case 's': /* character string */
|
||
prtf_string(*next);
|
||
next++;
|
||
break;
|
||
|
||
case 'u': /* decimal unsigned integer */
|
||
if (long_object)
|
||
{
|
||
prtf_string("<long objects not implemented>");
|
||
next++;
|
||
}
|
||
else
|
||
{
|
||
buffer = fmt_unsigned(*next);
|
||
}
|
||
next++;
|
||
break;
|
||
|
||
case 'x': /* hexadecimal integer */
|
||
if (long_object)
|
||
{
|
||
prtf_string("<long objects not implemented>");
|
||
next++;
|
||
}
|
||
else
|
||
{
|
||
buffer = fmt_hex(*next);
|
||
}
|
||
next++;
|
||
break;
|
||
|
||
case '%': /* the percent sign itself */
|
||
outchar('%');
|
||
break;
|
||
|
||
case '\0': /* unexpected end of format specification */
|
||
prtf_string("<unexpected end of format>\n");
|
||
fmt--;
|
||
break;
|
||
|
||
default: /* unexpected format specifier */
|
||
outchar('%');
|
||
outchar(*fmt);
|
||
prtf_string("< invalid format specifier>", 0, 0, 0);
|
||
break;
|
||
} /* end: switch (*fmt) */
|
||
|
||
if (buffer)
|
||
{
|
||
prtf_string(buffer);
|
||
rlsstr(buffer);
|
||
}
|
||
}
|
||
else /* just a character to print (not a % formatting directive) */
|
||
{
|
||
outchar(*fmt);
|
||
}
|
||
|
||
fmt++; /* advance pointer to next character in format */
|
||
} /* end: while (*fmt) */
|
||
|
||
/* reset format control variables to permit calls to sub-entry points */
|
||
leading_zeros = FALSE;
|
||
left_justified = FALSE;
|
||
long_object = FALSE;
|
||
field_width = 0;
|
||
precision = 0;
|
||
}
|
||
|
||
/************************************************************************/
|
||
/* Print a single character */
|
||
/************************************************************************/
|
||
prtf_character(ch)
|
||
char ch;
|
||
{
|
||
/*%%int i; /* our old favorite index variable */*/
|
||
|
||
if (field_width)
|
||
{
|
||
if (left_justified)
|
||
{
|
||
outchar(ch);
|
||
pad_with_blanks(field_width - 1);
|
||
}
|
||
else /* right justified */
|
||
{
|
||
pad_with_blanks(field_width - 1);
|
||
outchar(ch);
|
||
}
|
||
}
|
||
else outchar(ch); /* just print the single character */
|
||
|
||
} /* end of function: prtf_character(ch) */
|
||
|
||
/************************************************************************/
|
||
/* Print a character string */
|
||
/************************************************************************/
|
||
prtf_string(string)
|
||
char *string;
|
||
{
|
||
int i; /* our old favorite index variable */
|
||
int len = 0; /* the actual length of the character string */
|
||
|
||
/* determine string length (search for '\0' end-of-string mark) */
|
||
while (string[len]) len++;
|
||
|
||
/* if precision field specified, reduce "len" if too long */
|
||
if (precision && precision < len) len = precision;
|
||
|
||
/* output string with appropriate justification */
|
||
if (left_justified)
|
||
{
|
||
for (i = 0; i < len; i++) outchar(string[i]);
|
||
pad_with_blanks(field_width - len);
|
||
}
|
||
else /* right justified */
|
||
{
|
||
pad_with_blanks(field_width - len);
|
||
for (i = 0; i < len; i++) outchar(string[i]);
|
||
}
|
||
}
|
||
|
||
|
||
/************************************************************************/
|
||
/* Format a floating point number */
|
||
/************************************************************************/
|
||
fmt_float(value,type)
|
||
double value;
|
||
char type;
|
||
{
|
||
char buf[32];
|
||
int siz;
|
||
siz = ((type=='g')? (outrange(value)) : (type=='e'));
|
||
siz = makeflo(value,buf,precision,siz);
|
||
buf[siz] = '\0';
|
||
prtf_string(buf);
|
||
} /* end of function: fmt_float(value,type) */
|
||
|
||
|
||
/************************************************************************/
|
||
/* Format a signed decimal integer */
|
||
/************************************************************************/
|
||
char *fmt_int(value)
|
||
int value;
|
||
{
|
||
long lvalue;
|
||
lvalue = value;
|
||
return(fmt_long(lvalue));
|
||
/*****
|
||
char buffer[100]; /* buffer to hold characters of converted value */
|
||
int digit_count = 0; /* count of significant digits */
|
||
int i,j; /* index variables */
|
||
int negative = FALSE; /* flag to indicate sign of number */
|
||
|
||
/* test the sign of the integer-- if negative, record that fact */
|
||
if (value < 0)
|
||
{
|
||
negative = TRUE;
|
||
value = -value;
|
||
field_width--;
|
||
}
|
||
|
||
/* convert the integer to ASCII */
|
||
i = sizeof(buffer) - 1;
|
||
buffer[i--] = '\0'; /* insert end of string indicator */
|
||
do {
|
||
buffer[i--] = digit[value % 10];
|
||
value /= 10;
|
||
digit_count++;
|
||
} while (value);
|
||
|
||
/* if leading zeros are required, insert said */
|
||
if (leading_zeros)
|
||
{
|
||
for (j = digit_count; j < field_width; j++)
|
||
buffer[i--] = '0';
|
||
}
|
||
|
||
/* if negative, insert a '-' sign */
|
||
if (negative)
|
||
{
|
||
buffer[i--] = '-';
|
||
}
|
||
|
||
/* return the formatted integer */
|
||
return(copy_str(buffer+i+1));
|
||
*****/
|
||
} /* end of function: char *fmt_int(value) */
|
||
|
||
|
||
/************************************************************************/
|
||
/* Format a long signed decimal integer */
|
||
/************************************************************************/
|
||
char *fmt_long(value)
|
||
long value;
|
||
{
|
||
char buffer[100]; /* buffer to hold characters of converted value */
|
||
int digit_count = 0; /* count of significant digits */
|
||
int i,j; /* index variables */
|
||
int negative = FALSE; /* flag to indicate sign of number */
|
||
|
||
/* test the sign of the integer-- if negative, record that fact */
|
||
if (value < 0)
|
||
{
|
||
negative = TRUE;
|
||
value = -value;
|
||
field_width--;
|
||
}
|
||
|
||
/* convert the integer to ASCII */
|
||
i = sizeof(buffer) - 1;
|
||
buffer[i--] = '\0'; /* insert end of string indicator */
|
||
do {
|
||
buffer[i--] = digit[value % 10];
|
||
value /= 10;
|
||
digit_count++;
|
||
} while (value);
|
||
|
||
/* if leading zeros are required, insert said */
|
||
if (leading_zeros)
|
||
{
|
||
for (j = digit_count; j < field_width; j++)
|
||
buffer[i--] = '0';
|
||
}
|
||
|
||
/* if negative, insert a '-' sign */
|
||
if (negative)
|
||
{
|
||
buffer[i--] = '-';
|
||
}
|
||
|
||
/* return the formatted integer */
|
||
return(copy_str(buffer+i+1));
|
||
|
||
} /* end of function: char *fmt_long(value) */
|
||
|
||
|
||
/************************************************************************/
|
||
/* Format an unsigned decimal integer */
|
||
/************************************************************************/
|
||
char *fmt_unsigned(value)
|
||
unsigned value;
|
||
{
|
||
int i,j; /* index variables */
|
||
char buffer[100]; /* buffer to hold characters of converted value */
|
||
int digit_count = 0; /* count of significant digits */
|
||
|
||
/* convert the integer to ASCII */
|
||
i = sizeof(buffer) - 1;
|
||
buffer[i--] = '\0'; /* insert end of string indicator */
|
||
do {
|
||
buffer[i--] = digit[value % 10];
|
||
value /= 10;
|
||
digit_count++;
|
||
} while (value);
|
||
|
||
/* if leading zeros are required, insert said */
|
||
if (leading_zeros)
|
||
{
|
||
for (j = digit_count; j < field_width; j++)
|
||
buffer[i--] = '0';
|
||
}
|
||
|
||
/* return the formatted unsigned integer */
|
||
return(copy_str(buffer+i+1));
|
||
|
||
} /* end of function: char *fmt_unsigned(value) */
|
||
|
||
|
||
/************************************************************************/
|
||
/* Format a hexadecimal integer */
|
||
/************************************************************************/
|
||
char *fmt_hex(value)
|
||
unsigned value;
|
||
{
|
||
int i,j; /* index variables */
|
||
char buffer[100]; /* buffer to hold characters of converted value */
|
||
int digit_count = 0; /* count of significant digits */
|
||
|
||
/* convert the integer to ASCII */
|
||
i = sizeof(buffer) - 1;
|
||
buffer[i--] = '\0'; /* insert end of string indicator */
|
||
do {
|
||
buffer[i--] = digit[value & 0xf];
|
||
value = value >> 4;
|
||
digit_count++;
|
||
} while (value);
|
||
|
||
/* if leading zeros are required, insert said */
|
||
if (leading_zeros)
|
||
{
|
||
for (j = digit_count; j < field_width; j++)
|
||
buffer[i--] = '0';
|
||
}
|
||
|
||
/* return the hexadecimal integer value */
|
||
return(copy_str(buffer+i+1));
|
||
|
||
} /* end of function: char *fmt_hex(value) */
|
||
|
||
|
||
/************************************************************************/
|
||
/* Pad with blanks */
|
||
/************************************************************************/
|
||
pad_with_blanks(number)
|
||
int number; /* the number of blanks to output */
|
||
{
|
||
int i; /* index variable */
|
||
|
||
/* spew out the appropriate number of blanks */
|
||
for (i = 0; i < number; i++) outchar(' ');
|
||
|
||
} /* end of function: pad_with_blanks */
|
||
|
||
|
||
/************************************************************************/
|
||
/* Create copy of a character string */
|
||
/************************************************************************/
|
||
char *copy_str(string)
|
||
char *string;
|
||
{
|
||
char *ret_str;
|
||
ENTER(copy_str);
|
||
if (!(ret_str = getmem(strlen(string)+1))) getmem_error(rtn_name);
|
||
else
|
||
{
|
||
strcpy(ret_str, string);
|
||
}
|
||
return(ret_str);
|
||
}
|
||
|
||
|
||
/************************************************************************/
|
||
/* Create concatenation of two character strings */
|
||
/************************************************************************/
|
||
char *concat_str(str1,str2)
|
||
char *str1,*str2;
|
||
{
|
||
ENTER(concat_str);
|
||
char *ret_str;
|
||
if (!(ret_str = getmem(strlen(str1) + strlen(str2) + 1)))
|
||
getmem_error(rtn_name);
|
||
else
|
||
{
|
||
strcpy(ret_str, str1);
|
||
strcat(ret_str, str2);
|
||
}
|
||
return(ret_str);
|
||
}
|
||
|