doprnt.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 1988-1995, by Sun Microsystems, Inc.
* All rights reserved
*/
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* _doprnt: common code for printf, fprintf, sprintf
* Floating-point code is included or not, depending
* on whether the preprocessor variable FLOAT is 1 or 0.
*/
#define MAXARGS 50
#ifndef FLOAT
#endif
#include <stdio.h>
#include <ctype.h>
#include <varargs.h>
#include <values.h>
#include <locale.h>
#include "doprnt.h"
#include "stdiom.h"
#include <string.h> /* strchr, strlen, strspn */
#define max(a,b) ((a) > (b) ? (a) : (b))
#define min(a,b) ((a) < (b) ? (a) : (b))
/* If this symbol is nonzero, allow '0' as a flag */
/* If this symbol is nonzero, allow '0' as a flag */
#define FZERO 1
#if FLOAT
/*
*/
#include <floatingpoint.h>
extern void _fourdigitsquick();
#endif
void _mkarglst();
void _getarg();
static char *_check_dol();
else \
filecnt--; \
} \
} \
*fileptr++ = (unsigned)(c); \
count++; \
}
static char *nullstr = "(null)";
static char *lowerhex = "0123456789abcdef";
static char *upperhex = "0123456789ABCDEF";
/* stva_list is used to subvert C's restriction that a variable with an
* array type can not appear on the left hand side of an assignment operator.
* By putting the array inside a structure, the functionality of assigning to
* the whole array through a simple assignment is achieved..
*/
typedef struct stva_list {
} stva_list;
char *format;
{
char convertbuffer[1024] ;
/* Current position in format */
register char *cp;
/* Starting and ending points for value to be printed */
register char *bp;
char *p;
/* Pointer and count for I/O buffer */
register unsigned char *fileptr;
register int filecnt;
/* Field width and precision */
int width;
register int prec;
/* Format code */
char fcode;
/* Number of padding zeroes required on the left */
int lzero;
/* Flags - nonzero if corresponding character appears in format */
bool fplus; /* + */
bool fminus; /* - */
bool fblank; /* blank */
bool fsharp; /* # */
#if FZERO
bool ansi_fzero; /* 0 for ansi-dictated formats */
bool compat_fzero; /* 0 for backward compatibility */
#endif
bool Lsize; /* Capital L for size = long double = quadruple */
/* Pointer to sign, "0x", "0X", or empty */
char *prefix;
/* Scratch */
int nblank;
#if FLOAT
/* Exponent or empty */
char *suffix;
/* Buffer to create exponent */
/* Number of padding zeroes required on the right */
int rzero;
/* Length of exponent suffix. */
int suffixlength;
/* The value being converted, if real or quadruple */
double dval;
/* Output values from fconvert and econvert */
/* Values are developed in this buffer */
/* Current locale's decimal point */
#else
/* Values are developed in this buffer */
#endif
/* The value being converted, if integer */
register unsigned long val;
/* Work variables */
register int n;
register char c;
char radix;
int svswitch = 0;
/* count of output characters */
register int count;
/* variables for positional parameters */
args_width, /* for width */
args_prec, /* for prec */
sargs; /* used to save the start of the argument list */
* for va_arg() to retrieve the
* corresponding argument:
* arglst[0] is the first argument
* arglst[1] is the second argument, etc.
*/
int index = 0; /* argument placeolder */
/* Initialize args and sargs to the start of the argument list.
* Note that ANSI guarantees that the address of the first member of
* a structure will be the same as the address of the structure. */
/* initialize p an bp (starting and ending points) bugid 1141781 */
if ((c = *cp++) != '\0') {
/*
* We know we're going to write something; make sure
* we can write and set up buffers, etc..
*/
return(EOF);
} else
return(0); /* no fault, no error */
count = 0;
/*
* The main loop -- this loop goes through one iteration
* for each ordinary character or format specification.
*/
do {
if (c != '%') {
/* Ordinary (non-%) character */
emitchar(c);
} else {
/*
* % has been spotted!
*
* First, try the 99% cases.
* then parse the format specification.
*
* Note that this code assumes the Sun
* Workstation environment (all params
* passed as int == long, no interrupts
* for fixed point overflow from negating
* the most negative number).
*/
switch(c = *cp++) {
case 'l':
case 'h':
/* Quickly ignore long & short specifiers */
goto skipit;
case 's':
while (c = *bp++)
emitchar(c);
p = bp;
continue;
case 'c':
emitchar(c);
continue;
case 'i':
case 'd':
case 'D':
if ((long) val < 0) {
emitchar('-');
}
goto udcommon;
case 'U':
case 'u':
{
do {
val /= 10;
} while (val);
}
goto intout;
case 'X':
{
if (val == 0)
goto zero;
while (val) {
val /= 16;
}
}
goto intout;
case 'x':
case 'p':
{
if (val == 0)
goto zero;
while (val) {
val /= 16;
}
}
goto intout;
case 'O':
case 'o':
{
if (val == 0)
goto zero;
while (val) {
val /= 8;
}
}
/* Common code to output integers */
while (bp < p) {
c = *bp++;
emitchar(c);
}
continue;
zero:
c = '0';
goto emitc;
default:
/*
* let AT&T deal with it
*/
cp-= 2;
}
Lsize = 0; /* Not long double unless we say so. */
/* Scan the <flags> */
fplus = 0;
fminus = 0;
fblank = 0;
fsharp = 0;
#if FZERO
ansi_fzero = 0;
compat_fzero = 0;
#endif
case '+':
fplus = 1;
goto scan;
case '-':
fminus = 1;
goto scan;
case ' ':
fblank = 1;
goto scan;
case '#':
fsharp = 1;
goto scan;
#if FZERO
case '0':
ansi_fzero = 1;
compat_fzero = 1;
goto scan;
#endif
}
/* Scan the field width */
if (*cp == '*') {
char *p;
int val;
if (p != (char *)NULL) {
/*
* argument re-order
*/
if (fpos) {
fpos = 0;
}
} else {
}
if (width < 0) {
fminus = 1;
}
cp = p;
}
else {
if (width < 0) {
fminus = 1;
}
cp++;
}
} else {
}
}
/* Scan the precision */
if (*cp == '.') {
/* '*' instead of digits? */
if (*++cp == '*') {
char *p;
int val;
if (p != (char *)NULL) {
/*
* argument re-order
*/
if (fpos) {
fpos = 0;
}
} else {
}
cp = p;
}
else {
cp++;
}
} else {
prec = 0;
}
}
} else
prec = -1;
if (*cp == '$') {
if (fpos) {
fpos = 0;
}
} else {
}
goto scan;
}
/*
* The character addressed by cp must be the
* format letter -- there is nothing left for
* it to be.
*
* The status of the +, -, #, blank, and 0
* flags are reflected in the variables
* "fplus", "fminus", "fsharp", "fblank",
* and "ansi_fzero"/"compat_fzero", respectively.
* "width" and "prec" contain numbers
* corresponding to the digit strings
* before and after the decimal point,
* respectively. If there was no decimal
* point, "prec" is -1.
*
* The following switch sets things up
* for printing. What ultimately gets
* printed will be padding blanks, a prefix,
* left padding zeroes, a value, right padding
* zeroes, a suffix, and more padding
* blanks. Padding blanks will not appear
* simultaneously on both the left and the
* right. Each case in this switch will
* compute the value, and leave in several
* variables the information necessary to
* construct what is to be printed.
*
* The prefix is a sign, a blank, "0x", "0X",
* or null, and is addressed by "prefix".
*
* The suffix is either null or an exponent,
* and is addressed by "suffix".
*
* The value to be printed starts at "bp"
* and continues up to and not including "p".
*
* "lzero" and "rzero" will contain the number
* of padding zeroes required on the left
* and right, respectively. If either of
* these variables is negative, it will be
* treated as if it were zero.
*
* The number of padding blanks, and whether
* they go on the left or the right, will be
* computed on exit from the switch.
*/
lzero = 0;
prefix = "";
#if FLOAT
rzero = 0;
#endif
#if FZERO
/* if both zero-padding and left-justify flags
* are used, ignore zero-padding, per ansi c
*/
if (ansi_fzero & fminus) {
ansi_fzero = 0;
compat_fzero = 0;
}
/* if zero-padding and precision are specified,
* ignore zero-padding for ansi-dictated formats,
* per ansi c
*/
#endif
next:
/* toss the length modifier, if any */
case 'l':
case 'h':
goto next;
case 'L':
goto next;
/*
* fixed point representations
*
* "radix" is the radix for the conversion.
* Conversion is unsigned unless fcode is 'd'.
* We assume a 2's complement machine and
* that fixed point overflow (from negating
* the largest negative int) is ignored.
*/
case 'i':
case 'D':
case 'U':
case 'd':
case 'u':
radix = 10;
goto fixed;
case 'O':
case 'o':
radix = 8;
goto fixed;
case 'X':
case 'x':
radix = 16;
/* Establish default precision */
if (prec < 0)
prec = 1;
/* Fetch the argument to be printed */
/* If signed conversion, establish sign */
if ((long) val < 0) {
prefix = "-";
} else if (fplus)
prefix = "+";
else if (fblank)
prefix = " ";
}
/* Set translate table for digits */
{
register char *stringp;
if (fcode == 'X')
else
/* Develop the digits of the value */
switch(radix) {
case 8: /*octal*/
while (val) {
val /= 8;
}
break;
case 16:/*hex*/
while (val) {
val /= 16;
}
break;
default:
while (val) {
val /= 10;
}
break;
} /* switch */
}
/* Calculate padding zero requirement */
/* Handle the # flag */
switch (fcode) {
case 'x':
prefix = "0x";
break;
case 'X':
prefix = "0X";
break;
}
}
#if FZERO
if (ansi_fzero) {
if (n > prec)
prec = n;
}
#endif
/* Handle the # flag for 'o' */
lzero < 1) {
lzero = 1;
}
break;
#if FLOAT
#ifdef sparc
#define GETQVAL /* Sun-4 macro to get a quad q from the argument list, passed as a pointer. */ \
#else
#define GETQVAL /* Sun-3 macro to get a quad q from the argument list, passed as a value. */ \
{ int iq ; unsigned long * pl = (unsigned long *) (&qval) ; for(iq=0;iq<4;iq++) pl[iq] = (unsigned long) va_arg(args.ap, unsigned long) ; }
#endif
case 'E':
case 'e':
/*
* E-format. The general strategy
* here is fairly easy: we take
* what econvert gives us and re-format it.
*/
/* Establish default precision */
if (prec < 0)
prec = 6;
/* Fetch the value */
if (Lsize == 0) { /* Double */
} else { /* Long Double = quadruple */
}
/* Determine the prefix */
if (sign)
prefix = "-";
else if (fplus)
prefix = "+";
else if (fblank)
prefix = " ";
if (convertbuffer[0] > '9')
{ /* handle infinity, nan */
bp = &convertbuffer[0];
for (p = bp+1 ; *p != 0 ; p++) ;
goto ebreak ;
}
{
register char *stringp;
/* Place the first digit in the buffer */
/* Put in a decimal point if needed */
*stringp++ = decpt_char;
/* Create the rest of the mantissa */
--rzero;
}
p = stringp;
}
/* Create the exponent */
if (convertbuffer[0] != '0')
n = decpt - 1;
else
n = 0 ;
if (n < 0)
n = -n;
expbuf[6] = 0 ;
if (n < 100)
/*
* Normally two digit exponent field,
* three or four if required.
*/
else if (n < 1000)
else
/* Put in the exponent sign */
/* Put in the e; note kludge in 'g' format */
#if FZERO
if (compat_fzero &! fminus)
/* Calculate padding zero requirement */
#endif
break;
case 'f':
/*
* F-format floating point. This is
* a good deal less simple than E-format.
* The overall strategy will be to call
* fconvert, reformat its result into buf,
* and calculate how many trailing
* zeroes will be required. There will
* never be any leading zeroes needed.
*/
/* Establish default precision */
if (prec < 0)
prec = 6;
if (Lsize == 0) {
} else {
GETQVAL ;
}
/* Determine the prefix */
if (sign)
prefix = "-";
else if (fplus)
prefix = "+";
else if (fblank)
prefix = " ";
if (convertbuffer[0] > '9')
{ /* handle infinity, nan */
bp = &convertbuffer[0];
for (p = bp+1 ; *p != 0 ; p++) ;
goto fbreak ;
}
{
register char *stringp;
/* Initialize buffer pointer */
/* Emit the digits before the decimal point */
n = decpt;
if (n <= 0)
*stringp++ = '0';
else
do
if (*bp == '\0' )
*stringp++ = '0';
else {
}
while (--n != 0);
/* Decide whether we need a decimal point */
*stringp++ = decpt_char;
/* Digits(if any) after the decimal point */
n = prec;
while (--n >= 0) {
*stringp++ = '0';
else {
}
}
#if FZERO
if (compat_fzero &! fminus)
/* Calculate padding zero requirement */
#endif
p = stringp;
}
break;
case 'G':
case 'g':
/*
* g-format. We play around a bit
* and then jump into e or f, as needed.
*/
/* Establish default precision */
if (prec < 0)
prec = 6;
else if (prec == 0)
prec = 1;
if (Lsize == 0) {
} else {
}
bp = convertbuffer ;
if (convertbuffer[0] == '-') {
prefix = "-" ;
bp++;
}
else if (fplus)
prefix = "+";
else if (fblank)
prefix = " ";
{ /* Put in a big E for small minds. */
if (*p == 'e') *p = 'E' ;
for (; (*p != NULL) ; p++) ;
/* Find end of string. */
}
else
/* Find end of string. */
rzero = 0;
#if FZERO
if (compat_fzero & !fminus)
/* Calculate padding zero requirement */
#endif
break ;
#endif
case 'c':
p = bp + 1;
break;
case 's':
if (prec < 0)
/* avoid *(0) */
;
#if FZERO
if (compat_fzero &! fminus)
#endif
p = --bp;
bp -= n;
break;
case '\0':
/* well, what's the punch line? */
goto out;
case 'n':
svswitch = 1;
break;
default:
p++;
break;
}
/* Calculate number of padding blanks */
#if FLOAT
#endif
- (p - bp)
/* Blanks on left if required */
if (!fminus)
while (--nblank >= 0)
emitchar(' ');
/* Prefix, if any */
while (*prefix != '\0') {
prefix++;
}
/* Zeroes on the left */
while (--lzero >= 0)
emitchar('0');
/* The value itself */
while (bp < p) {
bp++;
}
#if FLOAT
/* Zeroes on the right */
while (--rzero >= 0)
emitchar('0');
/* The suffix */
while (*suffix != '\0') {
suffix++;
}
#endif
/* Blanks on the right if required */
if (fminus)
while (--nblank >= 0)
emitchar(' ');
/* If %n is seen, save count in argument */
if (svswitch == 1) {
long *svcount;
svswitch = 0;
}
} /* else */
out:
}
#ifdef sparc
/*
* We use "double *" instead of "quadruple *" to skip over the pointer to
* long double on the argument list since a pointer is a pointer after all.
*/
#define SKIPQVAL { \
}
#else /* Sun-3 */
#define SKIPQVAL { \
int iq; \
}
#endif
/* This function initializes arglst, to contain the appropriate va_list values
* for the first MAXARGS arguments. */
void
char *fmt;
{
/*
* Algorithm 1. set all argument types to zero.
* 2. walk through fmt putting arg types in typelst[].
* 3. walk through args using va_arg(args.ap, typelst[n])
* and set arglst[] to the appropriate values.
* Assumptions: Cannot use %*$... to specify variable position.
*/
maxnum = -1;
curargno = 0;
{
fmt++; /* skip % */
{
fmt += n + 1;
}
flags = 0;
again:;
switch (*fmt++)
{
case '%': /*there is no argument! */
continue;
case 'l':
flags |= 0x1;
goto again;
case 'L':
flags |= 0x8;
goto again;
case '*': /* int argument used for value */
flags |= 0x2;
break;
case 'e':
case 'E':
case 'f':
case 'g':
case 'G':
if (flags & 0x8)
else
break;
case 's':
break;
case 'p':
break;
case 'n':
if (flags & 0x1)
else
break;
default:
if (flags & 0x1)
else
break;
}
{
}
curargno++; /* default to next in list */
{
flags ^= 0x2;
goto again;
}
}
for (n = 0 ; n <= maxnum; n++)
{
if (typelst[n] == 0)
switch (typelst[n])
{
case INT:
break;
case LONG:
break;
case CHAR_PTR:
break;
case DOUBLE:
break;
case LONG_DOUBLE:
break;
case VOID_PTR:
break;
case LONG_PTR:
break;
case INT_PTR:
break;
}
}
}
/*
* This function is used to find the va_list value for arguments whose
* position is greater than MAXARGS. This function is slow, so hopefully
* MAXARGS will be big enough so that this function need only be called in
* unusual circumstances.
* pargs is assumed to contain the value of arglst[MAXARGS - 1].
*/
void
char *fmt;
int argno;
{
int found = 1;
while (found)
{
found = 0;
{
fmt++; /* skip % */
{
fmt += n + 1;
}
/* find conversion specifier for next argument */
if (i != curargno)
{
curargno++;
continue;
} else
found = 1;
flags = 0;
again:;
switch (*fmt++)
{
case '%': /*there is no argument! */
continue;
case 'l':
flags |= 0x1;
goto again;
case 'L':
flags |= 0x8;
goto again;
case '*': /* int argument used for value */
flags |= 0x2;
break;
case 'e':
case 'E':
case 'f':
case 'g':
case 'G':
if (flags & 0x8) {
}
else
break;
case 's':
break;
case 'p':
break;
case 'n':
if (flags & 0x1)
else
break;
default:
if (flags & 0x1)
else
break;
}
i++;
curargno++; /* default to next in list */
{
flags ^= 0x2;
goto again;
}
}
/* missing specifier for parameter, assume parameter is an int */
i++;
curargno++;
found = 1;
}
}
}
/*
* parse a string, mini parse
*/
static char *
_check_dol(s, val)
char *s;
int *val;
{
char *os; /* save old string */
int tmp_val = 0;
int flag = 0;
while (isdigit (*s)) {
++flag;
s++;
}
if (flag == 0)
return ((char *)NULL);
if (*s == '$') {
return(++s);
}
return ((char *)NULL);
}