直接看源码吧
/*******************************************************************************
*
* fioFormatV - convert a format string
*
* This routine is used by the printf() family of routines to handle the
* actual conversion of a format string. The first argument is a format
* string, as described in the entry for printf(). The second argument is a
* variable argument list <vaList> that was previously established.
*
* As the format string is processed, the result will be passed to the output
* routine whose address is passed as the third parameter, <outRoutine>.
* This output routine may output the result to a device, or put it in a
* buffer. In addition to the buffer and length to output, the fourth
* argument, <outarg>, will be passed through as the third parameter to the
* output routine. This parameter could be a file descriptor, a buffer
* address, or any other value that can be passed in an "int".
*
* The output routine should be declared as follows:
* .CS
* STATUS outRoutine
* (
* char *buffer, /@ buffer passed to routine @/
* int nchars, /@ length of buffer @/
* int outarg /@ arbitrary arg passed to fmt routine @/
* )
* .CE
* The output routine should return OK if successful, or ERROR if unsuccessful.
*
* RETURNS:
* The number of characters output, or ERROR if the output routine
* returned ERROR.
*
* INTERNAL
* Warning, this routine is extremely complex and its integrity is easily
* destroyed. Do not change this code without absolute understanding of all
* ramifications and consequences.
*/
int fioFormatV
(
FAST const char *fmt, /* format string */
va_list vaList, /* pointer to varargs list */
FUNCPTR outRoutine, /* handler for args as they're formatted */
int outarg /* argument to routine */
)
{
FAST int ch; /* character from fmt */
FAST int n; /* handy integer (short term usage) */
FAST char * cp; /* handy char pointer (short term usage) */
int width; /* width from format (%8d), or 0 */
char sign; /* sign prefix (' ', '+', '-', or \0) */
unsigned long long
ulongLongVal; /* unsigned 64 bit arguments %[diouxX] */
#ifdef _WRS_E500_FIXED_POINT_SUPPORT
int e500_sign_flag; /* e500-specific 'r' and 'R' modifiers */
unsigned long long llconst; /* e500-specific (short term usage) */
int a, ndigs; /* e500-specific (short term usage) */
#endif /* _WRS_E500_FIXED_POINT_SUPPORT */
int prec; /* precision from format (%.3d), or -1 */
int oldprec; /* old precision from format (%.3d), or -1 */
int dprec; /* a copy of prec if [diouxX], 0 otherwise */
int fpprec; /* `extra' floating precision in [eEfgG] */
int size; /* size of converted field or string */
int fieldsz; /* field size expanded by sign, etc */
int realsz; /* field size expanded by dprec */
#ifdef _WRS_ALTIVEC_SUPPORT
FAST int i; /* handy integer (short term usage) */
FAST char * vp; /* handy char pointer (short term usage) */
char Separator; /* separator for vector elements */
char C_Separator; /* separator for char vector elements */
VECTOR vec; /* vector argument */
BOOL doVector; /* AltiVec vector */
#endif /* _WRS_ALTIVEC_SUPPORT */
char FMT[20]; /* To collect fmt info */
FAST char * Collect; /* char pointer to FMT */
BOOL doLongInt; /* long integer */
BOOL doLongLongInt; /* long long integer - 64 bit */
BOOL doShortInt; /* short integer */
BOOL doAlt; /* alternate form */
BOOL doLAdjust; /* left adjustment */
BOOL doZeroPad; /* zero (as opposed to blank) pad */
BOOL doHexPrefix; /* add 0x or 0X prefix */
BOOL doSign; /* change sign to '-' */
char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */
#if (CPU_FAMILY != AM29XXX)
char ox[2]; /* space for 0x hex-prefix */
#else
char ox[3]; /* space for 0x hex-prefix */
#endif /*(CPU_FAMILY != AM29XXX)*/
char * xdigs = NULL; /* digits for [xX] conversion */
int ret = 0; /* return value accumulator */
enum {OCT, DEC, HEX} base; /* base for [diouxX] conversion */
FOREVER /* Scan the format for conversions (`%' character) */
{
for (cp = CHAR_FROM_CONST(fmt);((ch=*fmt) != EOS)&&(ch != '%'); fmt++)
;
if ((n = fmt - cp) != 0)
{
if ((*outRoutine) (cp, n, outarg) != OK)
return (ERROR);
ret += n;
}
if (ch == EOS)
return (ret); /* return total length */
fmt++; /* skip over '%' */
#ifdef _WRS_ALTIVEC_SUPPORT
Separator = ' '; /* the default separator for vector elements */
C_Separator = EOS; /* the default separator for char vector elements */
doVector = FALSE; /* no vector modifier */
#endif /* _WRS_ALTIVEC_SUPPORT */
*FMT = EOS;
Collect = FMT;
doLongInt = FALSE; /* long integer */
doLongLongInt = FALSE; /* 64 bit integer */
doShortInt = FALSE; /* short integer */
doAlt = FALSE; /* alternate form */
doLAdjust = FALSE; /* left adjustment */
doZeroPad = FALSE; /* zero (as opposed to blank) pad */
doHexPrefix = FALSE; /* add 0x or 0X prefix */
doSign = FALSE; /* change sign to '-' */
dprec = 0;
fpprec = 0;
width = 0;
prec = -1;
oldprec = -1;
sign = EOS;
#ifdef _WRS_E500_FIXED_POINT_SUPPORT
e500_sign_flag = 1; /* default is %r -- signed fixed point value */
#endif /* _WRS_E500_FIXED_POINT_SUPPORT */
#define get_CHAR (ch = *Collect++ = *fmt++)
#ifdef _WRS_ALTIVEC_SUPPORT
#define SET_VECTOR_FMT(VFMT,NO) \
do \
{ \
char * to; \
\
vec.vul = va_arg (vaList,__vector unsigned long); \
cp = buf; \
\
*Collect = EOS; \
i = (NO); \
to = VFMT = (char *) malloc (i*20); \
while(i-- > 0) { \
\
char * from = FMT; \
\
*to++ = '%'; \
while (*to++ = *from++); \
*(char*)((int)(to)-1) = Separator; \
} \
*(--to) = EOS; \
} \
while (0)
#define RESET_VECTOR_FMT(VFMT) \
\
size = strlen(cp); \
sign = EOS; \
\
free(VFMT)
#endif /* _WRS_ALTIVEC_SUPPORT */
rflag:
get_CHAR;
reswitch:
switch (ch)
{
case ' ':
/* If the space and + flags both appear, the space
* flag will be ignored. -- ANSI X3J11
*/
if (!sign)
sign = ' ';
goto rflag;
#ifdef _WRS_ALTIVEC_SUPPORT
case ',':
case ';':
case ':':
case '_':
Collect--;
Separator = C_Separator = ch;
goto rflag;
#endif /* _WRS_ALTIVEC_SUPPORT */
case '#':
doAlt = TRUE;
goto rflag;
case '*':
/* A negative field width argument is taken as a
* flag followed by a positive field width.
* -- ANSI X3J11
* They don't exclude field widths read from args.
*/
if ((width = va_arg(vaList, int)) >= 0)
goto rflag;
width = -width; /* FALLTHROUGH */
case '-':
doLAdjust = TRUE;
goto rflag;
case '+':
sign = '+';
goto rflag;
case '.':
get_CHAR;
if ( ch == '*' )
{
n = va_arg(vaList, int);
prec = (n < 0) ? -1 : n;
goto rflag;
}
n = 0;
while (is_digit(ch))
{
n = 10 * n + to_digit(ch);
get_CHAR;
}
prec = n < 0 ? -1 : n;
goto reswitch;
case '0':
/* Note that 0 is taken as a flag, not as the
* beginning of a field width. -- ANSI X3J11
*/
doZeroPad = TRUE;
goto rflag;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
n = 0;
do
{
n = 10 * n + to_digit(ch);
get_CHAR;
} while (is_digit(ch));
width = n;
goto reswitch;
case 'h':
doShortInt = TRUE;
goto rflag;
case 'l':
get_CHAR;
if ( ch == 'l' )
{
doLongLongInt = TRUE;
goto rflag;
}
#ifdef _WRS_E500_FIXED_POINT_SUPPORT
else if ( ch == 'r' || ch == 'R' ) /* e500 fixed point */
{
doLongLongInt = TRUE;
goto reswitch;
}
#endif /*_WRS_E500_FIXED_POINT_SUPPORT */
else
{
doLongInt = TRUE;
goto reswitch;
}
#ifdef _WRS_E500_FIXED_POINT_SUPPORT
case 'R': /* e500 fixed point */
e500_sign_flag = 0; /* unsigned fixed point value */
/* FALLTHROUGH */
case 'r': /* e500 fixed point */
sign = EOS;
cp = buf;
if (e500_sign_flag) {
ulongLongVal = SARG();
if ((long long)ulongLongVal < 0)
{
ulongLongVal = -ulongLongVal;
sign = '-';
}
}
else ulongLongVal = UARG();
if (doShortInt) {
ndigs = 5;
ulongLongVal <<= (32+16);
} else if (!(doLongLongInt)) {
ndigs = 10;
ulongLongVal <<= 32;
} else {
ndigs = 19;
}
n = 0;
if (e500_sign_flag) {
if (ulongLongVal & (1LL << 63)) {
n = 1;
}
ulongLongVal <<= 1;
ndigs--;
}
*cp++ = n + '0';
*cp++ = '.';
ndigs = (prec >0 ? prec : ndigs);
llconst = ((1LL << (64-1))-1);
while (ndigs) {
a = (ulongLongVal >> (64-3)) + (ulongLongVal >> (64-1)) ;
ulongLongVal = (ulongLongVal & llconst) + ((ulongLongVal << 2) & llconst);
a += (ulongLongVal >> 63);
*cp++ = a + '0';
ndigs--;
ulongLongVal <<= 1;
}
*cp = '\0';
cp = buf;
size = strlen(cp);
break;
#endif /* _WRS_E500_FIXED_POINT_SUPPORT */
#ifdef _WRS_ALTIVEC_SUPPORT
case 'v':
Collect--;
doVector = TRUE;
goto rflag;
#endif /* _WRS_ALTIVEC_SUPPORT */
case 'c':
#ifdef _WRS_ALTIVEC_SUPPORT
if (doVector)
{
vec.vul = va_arg (vaList,__vector unsigned long);
cp = buf;
vp = (unsigned char *)&vec.u8;
i = 15;
while(i-- > 0) {
*cp++ = *vp++;
if (C_Separator) *cp++ = C_Separator;
}
*cp++ = *vp++;
cp = buf;
size = 16 + (C_Separator ? 15:0);
sign = EOS;
}
else
{
#endif /* _WRS_ALTIVEC_SUPPORT */
*(cp = buf) = va_arg(vaList, int);
size = 1;
sign = EOS;
#ifdef _WRS_ALTIVEC_SUPPORT
}
#endif /* _WRS_ALTIVEC_SUPPORT */
break;
case 'D':
doLongInt = TRUE; /* FALLTHROUGH */
case 'd':
case 'i':
#ifdef _WRS_ALTIVEC_SUPPORT
if (doVector)
{
SET_VECTOR_FMT(vp,doShortInt?8:4);
if (doShortInt)
sprintf(cp,vp,vec.s16[0],vec.s16[1],vec.s16[2],vec.s16[3],
vec.s16[4],vec.s16[5],vec.s16[6],vec.s16[7]);
else
sprintf(cp,vp,vec.s32[0],vec.s32[1],vec.s32[2],vec.s32[3]);
RESET_VECTOR_FMT(vp);
break;
}
#endif /* _WRS_ALTIVEC_SUPPORT */
ulongLongVal = SARG();
if ((long long)ulongLongVal < 0)
{
ulongLongVal = -ulongLongVal;
sign = '-';
}
base = DEC;
goto number;
case 'n':
/* ret is int, so effectively %lln = %ln */
if (doLongLongInt)
*va_arg(vaList, long long *) = (long long) ret;
else if (doLongInt)
*va_arg(vaList, long *) = ret;
else if (doShortInt)
*va_arg(vaList, short *) = ret;
else
*va_arg(vaList, int *) = ret;
continue; /* no output */
case 'O':
doLongInt = TRUE; /* FALLTHROUGH */
case 'o':
#ifdef _WRS_ALTIVEC_SUPPORT
if (doVector)
{
SET_VECTOR_FMT(vp,doShortInt?8:4);
if (doShortInt)
sprintf(cp,vp,vec.s16[0],vec.s16[1],vec.s16[2],vec.s16[3],
vec.s16[4],vec.s16[5],vec.s16[6],vec.s16[7]);
else
sprintf(cp,vp,vec.s32[0],vec.s32[1],vec.s32[2],vec.s32[3]);
RESET_VECTOR_FMT(vp);
break;
}
#endif /* _WRS_ALTIVEC_SUPPORT */
ulongLongVal = UARG();
base = OCT;
goto nosign;
case 'p':
/* The argument shall be a pointer to void. The
* value of the pointer is converted to a sequence
* of printable characters, in an implementation
* defined manner. -- ANSI X3J11
*/
#ifdef _WRS_ALTIVEC_SUPPORT
if (doVector)
{
SET_VECTOR_FMT(vp,4);
sprintf(cp,vp,vec.u32[0],vec.u32[1],vec.u32[2],vec.u32[3]);
RESET_VECTOR_FMT(vp);
break;
}
#endif /* _WRS_ALTIVEC_SUPPORT */
ulongLongVal = (unsigned long long) (unsigned int)
va_arg(vaList, void *);/* NOSTRICT */
base = HEX;
xdigs = "0123456789abcdef";
doHexPrefix = TRUE;
ch = 'x';
goto nosign;
case 's':
if ((cp = va_arg(vaList, char *)) == NULL)
cp = "(null)";
if (prec >= 0)
{
/* can't use strlen; can only look for the
* NUL in the first `prec' characters, and
* strlen() will go further.
*/
char *p = (char *)memchr(cp, 0, prec);
if (p != NULL)
{
size = p - cp;
if (size > prec)
size = prec;
}
else
size = prec;
}
else
size = strlen(cp);
sign = EOS;
break;
case 'U':
doLongInt = TRUE; /* FALLTHROUGH */
case 'u':
#ifdef _WRS_ALTIVEC_SUPPORT
if (doVector)
{
SET_VECTOR_FMT(vp,doShortInt?8:4);
if (doShortInt)
sprintf(cp,vp,vec.u16[0],vec.u16[1],vec.u16[2],vec.u16[3],
vec.u16[4],vec.u16[5],vec.u16[6],vec.u16[7]);
else
sprintf(cp,vp,vec.u32[0],vec.u32[1],vec.u32[2],vec.u32[3]);
RESET_VECTOR_FMT(vp);
break;
}
#endif /* _WRS_ALTIVEC_SUPPORT */
ulongLongVal = UARG();
base = DEC;
goto nosign;
case 'X':
xdigs = "0123456789ABCDEF";
goto hex;
case 'x':
xdigs = "0123456789abcdef";
hex:
#ifdef _WRS_ALTIVEC_SUPPORT
if (doVector)
{
SET_VECTOR_FMT(vp,doShortInt?8:4);
if (doShortInt)
sprintf(cp,vp,vec.s16[0],vec.s16[1],vec.s16[2],vec.s16[3],
vec.s16[4],vec.s16[5],vec.s16[6],vec.s16[7]);
else
sprintf(cp,vp,vec.s32[0],vec.s32[1],vec.s32[2],vec.s32[3]);
RESET_VECTOR_FMT(vp);
break;
}
#endif /* _WRS_ALTIVEC_SUPPORT */
ulongLongVal = UARG();
base = HEX;
/* leading 0x/X only if non-zero */
if (doAlt && (ulongLongVal != 0))
doHexPrefix = TRUE;
/* unsigned conversions */
nosign: sign = EOS;
/* ... diouXx conversions ... if a precision is
* specified, the 0 flag will be ignored. -- ANSI X3J11
*/
number: if ((dprec = prec) >= 0)
doZeroPad = FALSE;
/* The result of converting a zero value with an
* explicit precision of zero is no characters.
* -- ANSI X3J11
*/
cp = buf + BUF;
if ((ulongLongVal != 0) || (prec != 0))
{
/* unsigned mod is hard, and unsigned mod
* by a constant is easier than that by
* a variable; hence this switch.
*/
switch (base)
{
case OCT:
do
{
*--cp = to_char(ulongLongVal & 7);
ulongLongVal >>= 3;
} while (ulongLongVal);
/* handle octal leading 0 */
if (doAlt && (*cp != '0'))
*--cp = '0';
break;
case DEC:
/* many numbers are 1 digit */
while (ulongLongVal >= 10)
{
*--cp = to_char(ulongLongVal % 10);
ulongLongVal /= 10;
}
*--cp = to_char(ulongLongVal);
break;
case HEX:
do
{
*--cp = xdigs[ulongLongVal & 15];
ulongLongVal >>= 4;
} while (ulongLongVal);
break;
default:
cp = "bug in vfprintf: bad base";
size = strlen(cp);
goto skipsize;
}
}
size = buf + BUF - cp;
skipsize:
break;
case 'L':
/* NOT IMPLEMENTED */
goto rflag;
case 'e':
case 'E':
case 'f':
case 'g':
case 'G':
#ifdef _WRS_ALTIVEC_SUPPORT
if (doVector)
{
SET_VECTOR_FMT(vp,4);
sprintf(cp,vp,vec.f32[0],vec.f32[1],vec.f32[2],vec.f32[3]);
RESET_VECTOR_FMT(vp);
break;
}
#endif /* _WRS_ALTIVEC_SUPPORT */
if (fioFltFormatRtn != NULL)
{
oldprec = prec; /* in case of strange float */
if (prec > MAXFRACT) /* do realistic precision */
{
if (((ch != 'g') && (ch != 'G')) || doAlt)
fpprec = prec - MAXFRACT;
prec = MAXFRACT; /* they asked for it! */
}
else if (prec == -1)
prec = 6; /* ANSI default precision */
cp = buf; /* where to fill in result */
*cp = EOS; /* EOS terminate just in case */
#if ((CPU_FAMILY != I960) && (CPU_FAMILY != PPC))
size = (*fioFltFormatRtn) (&vaList,prec,doAlt,ch,
&doSign,cp,buf+sizeof(buf));
#else
size = (*fioFltFormatRtn) (vaList, prec, doAlt, ch,
&doSign,cp,buf+sizeof(buf));
#endif
if ((int)size < 0) /* strange value (Nan,Inf,..) */
{
size = -size; /* get string length */
prec = oldprec; /* old precision (not default)*/
doZeroPad = FALSE; /* don't pad with zeroes */
if (doSign) /* is strange value signed? */
sign = '-';
}
else
{
if (doSign)
sign = '-';
if (*cp == EOS)
cp++;
}
break;
}
/* FALLTHROUGH if no floating point format routine */
default: /* "%?" prints ?, unless ? is NULL */
if (ch == EOS)
return (ret);
/* pretend it was %c with argument ch */
cp = buf;
*cp = ch;
size = 1;
sign = EOS;
break;
}
/* All reasonable formats wind up here. At this point,
* `cp' points to a string which (if not doLAdjust)
* should be padded out to `width' places. If
* doZeroPad, it should first be prefixed by any
* sign or other prefix; otherwise, it should be blank
* padded before the prefix is emitted. After any
* left-hand padding and prefixing, emit zeroes
* required by a decimal [diouxX] precision, then print
* the string proper, then emit zeroes required by any
* leftover floating precision; finally, if doLAdjust,
* pad with blanks.
*/
/*
* compute actual size, so we know how much to pad.
* fieldsz excludes decimal prec; realsz includes it
*/
fieldsz = size + fpprec;
if (sign)
{
fieldsz++;
if (fieldSzIncludeSign)
dprec++;
}
else if (doHexPrefix)
fieldsz += 2;
realsz = (dprec > fieldsz) ? dprec : fieldsz;
/* right-adjusting blank padding */
if (!doLAdjust && !doZeroPad)
PAD(width - realsz, blanks);
/* prefix */
if (sign)
{
if ((*outRoutine) (&sign, 1, outarg) != OK)
return (ERROR);
}
else if (doHexPrefix)
{
ox[0] = '0';
ox[1] = ch;
if ((*outRoutine) (ox, 2, outarg) != OK)
return (ERROR);
}
/* right-adjusting zero padding */
if (!doLAdjust && doZeroPad)
PAD(width - realsz, zeroes);
/* leading zeroes from decimal precision */
PAD(dprec - fieldsz, zeroes);
/* the string or number proper */
if ((*outRoutine) (cp, size, outarg) != OK)
return (ERROR);
/* trailing floating point zeroes */
PAD(fpprec, zeroes);
/* left-adjusting padding (always blank) */
if (doLAdjust)
PAD(width - realsz, blanks);
/* finally, adjust ret */
ret += (width > realsz) ? width : realsz;
}
}