C语言printf函数是怎么解析格式化字符串的?

直接看源码吧

/*******************************************************************************
*
* 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;
	}
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

机载软件与适航

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值