/* =====> 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(""); next++; } else { buffer = fmt_unsigned(*next); } next++; break; case 'x': /* hexadecimal integer */ if (long_object) { prtf_string(""); next++; } else { buffer = fmt_hex(*next); } next++; break; case '%': /* the percent sign itself */ outchar('%'); break; case '\0': /* unexpected end of format specification */ prtf_string("\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); }