vfprintf.c

来自「一个C源代码分析器」· C语言 代码 · 共 684 行

C
684
字号
/* Copyright (C) 1991, 1992, 1993, 1994 Free Software Foundation, Inc.This file is part of the GNU C Library.The GNU C Library is free software; you can redistribute it and/ormodify it under the terms of the GNU Library General Public License aspublished by the Free Software Foundation; either version 2 of theLicense, or (at your option) any later version.The GNU C Library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNULibrary General Public License for more details.You should have received a copy of the GNU Library General PublicLicense along with the GNU C Library; see the file COPYING.LIB.  Ifnot, write to the Free Software Foundation, Inc., 675 Mass Ave,Cambridge, MA 02139, USA.  */#include <ansidecl.h>#include <localeinfo.h>#include <ctype.h>#include <errno.h>#include <float.h>#include <limits.h>#include <math.h>#include <stdarg.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <printf.h>#include <assert.h>#include "_itoa.h"/* If it's an unbuffered stream that we provided   temporary buffering for, remove that buffering.  */#define	RETURN(x)							      \  do									      \    {									      \      done = (x);							      \      goto do_return;							      \    } while (0)#define	outchar(x)							      \  do									      \    {									      \      register CONST int outc = (x);					      \      if (putc(outc, s) == EOF)						      \	RETURN(-1);							      \      else								      \	++done;								      \    } while (0)/* Cast the next arg, of type ARGTYPE, into CASTTYPE, and put it in VAR.  */#define	castarg(var, argtype, casttype) \  var = (casttype) va_arg(args, argtype)/* Get the next arg, of type TYPE, and put it in VAR.  */#define	nextarg(var, type)	castarg(var, type, type)static printf_function printf_unknown;extern printf_function **__printf_function_table;#ifdef	__GNUC__#define	HAVE_LONGLONG#define	LONGLONG	long long#else#define	LONGLONG	long#endifintDEFUN(vfprintf, (s, format, args),      register FILE *s AND CONST char *format AND va_list args){  /* Pointer into the format string.  */  register CONST char *f;  /* Number of characters written.  */  register size_t done = 0;  /* Nonzero we're providing buffering.  */  char our_buffer;  /* Temporary buffer for unbuffered streams.  */  char temporary_buffer[BUFSIZ];  if (!__validfp(s) || !s->__mode.__write || format == NULL)    {      errno = EINVAL;      return -1;    }  if (!s->__seen)    {      if (__flshfp(s, EOF) == EOF)	return EOF;    }  our_buffer = s->__buffer == NULL;  if (our_buffer)    {      /* If it's an unbuffered stream, buffer it	 at least inside this function call.  */      s->__bufp = s->__buffer = temporary_buffer;      s->__bufsize = sizeof(temporary_buffer);      s->__put_limit = s->__buffer + s->__bufsize;      s->__get_limit = s->__buffer;    }  /* Reset multibyte characters to their initial state.  */  (void) mblen((char *) NULL, 0);  f = format;  while (*f != '\0')    {      /* Type modifiers.  */      char is_short, is_long, is_long_double;#ifdef	HAVE_LONGLONG      /* We use the `L' modifier for `long long int'.  */#define	is_longlong	is_long_double#else#define	is_longlong	0#endif      /* Format spec modifiers.  */      char space, showsign, left, alt;      /* Padding character: ' ' or '0'.  */      char pad;      /* Width of a field.  */      register int width;      /* Precision of a field.  */      int prec;      /* Decimal integer is negative.  */      char is_neg;      /* Current character of the format.  */      char fc;      /* Base of a number to be written.  */      int base;      /* Integral values to be written.  */      unsigned LONGLONG int num;      LONGLONG int signed_num;      /* String to be written.  */      CONST char *str;      char unknown_error[256];	/* Buffer sometimes used by %m.  */      /* Auxiliary function to do output.  */      printf_function *function;      if (!isascii(*f))	{	  /* Non-ASCII, may be a multibyte.  */	  int len = mblen(f, strlen(f));	  if (len > 0)	    {	      while (len-- > 0)		outchar(*f++);	      continue;	    }	}      if (*f != '%')	{	  /* This isn't a format spec, so write	     everything out until the next one.  */	  CONST char *next = strchr(f + 1, '%');	  if (next == NULL)	    next = strchr(f + 1, '\0');	  if (next - f > 20)	    {	      size_t written = fwrite((PTR) f, 1, next - f, s);	      done += written;	      if (written != next - f)		break;	      f += written;	    }	  else	    while (f < next)	      outchar(*f++);	  continue;	}      ++f;      /* Check for "%%".  Note that although the ANSI standard lists	 '%' as a conversion specifier, it says "The complete format	 specification shall be `%%'," so we can avoid all the width	 and precision processing.  */      if (*f == '%')	{	  ++f;	  outchar('%');	  continue;	}      /* Check for spec modifiers.  */      space = showsign = left = alt = 0;      pad = ' ';      while (*f == ' ' || *f == '+' || *f == '-' || *f == '#' || *f == '0')	switch (*f++)	  {	  case ' ':	    /* Output a space in place of a sign, when there is no sign.  */	    space = 1;	    break;	  case '+':	    /* Always output + or - for numbers.  */	    showsign = 1;	    break;	  case '-':	    /* Left-justify things.  */	    left = 1;	    break;	  case '#':	    /* Use the "alternate form":	       Hex has 0x or 0X, FP always has a decimal point.  */	    alt = 1;	    break;	  case '0':	    /* Pad with 0s.  */	    pad = '0';	    break;	  }      if (left)	pad = ' ';      /* Get the field width.  */      width = 0;      if (*f == '*')	{	  /* The field width is given in an argument.	     A negative field width indicates left justification.  */	  nextarg(width, int);	  if (width < 0)	    {	      width = - width;	      left = 1;	    }	  ++f;	}      else	while (isdigit(*f))	  {	    width *= 10;	    width += *f++ - '0';	  }      /* Get the precision.  */      /* -1 means none given; 0 means explicit 0.  */      prec = -1;      if (*f == '.')	{	  ++f;	  if (*f == '*')	    {	      /* The precision is given in an argument.  */	      nextarg(prec, int);	      /* Avoid idiocy.  */	      if (prec < 0)		prec = -1;	      ++f;	    }	  else if (isdigit(*f))	    {	      prec = 0;	      while (*f != '\0' && isdigit(*f))		{		  prec *= 10;		  prec += *f++ - '0';		}	    }	}      /* Check for type modifiers.  */      is_short = is_long = is_long_double = 0;      while (*f == 'h' || *f == 'l' || *f == 'L' || *f == 'q')	switch (*f++)	  {	  case 'h':	    /* int's are short int's.  */	    is_short = 1;	    break;	  case 'l':#ifdef	HAVE_LONGLONG	    if (is_long)	      /* A double `l' is equivalent to an `L'.  */	      is_longlong = 1;	    else#endif	      /* int's are long int's.  */	      is_long = 1;	    break;	  case 'L':	    /* double's are long double's, and int's are long long int's.  */	    is_long_double = 1;	    break;	  case 'Z':	    /* int's are size_t's.  */#ifdef	HAVE_LONGLONG	    assert (sizeof(size_t) <= sizeof(unsigned long long int));	    is_longlong = sizeof(size_t) > sizeof(unsigned long int);#endif	    is_long = sizeof(size_t) > sizeof(unsigned int);	    break;	  case 'q':	    /* 4.4 uses this for long long.  */#ifdef	HAVE_LONGLONG	    is_longlong = 1;#else	    is_long = 1;#endif	    break;	  }      /* Format specification.  */      fc = *f++;      function = (__printf_function_table == NULL ? NULL :		  __printf_function_table[fc]);      if (function == NULL)	switch (fc)	  {	  case 'i':	  case 'd':	    /* Decimal integer.  */	    base = 10;	    if (is_longlong)	      nextarg(signed_num, LONGLONG int);	    else if (is_long)	      nextarg(signed_num, long int);	    else if (!is_short)	      castarg(signed_num, int, long int);	    else	      castarg(signed_num, int, short int);	    is_neg = signed_num < 0;	    num = is_neg ? (- signed_num) : signed_num;	    goto number;	  case 'u':	    /* Decimal unsigned integer.  */	    base = 10;	    goto unsigned_number;	  case 'o':	    /* Octal unsigned integer.  */	    base = 8;	    goto unsigned_number;	  case 'X':	    /* Hexadecimal unsigned integer.  */	  case 'x':	    /* Hex with lower-case digits.  */	    base = 16;	  unsigned_number:	    /* Unsigned number of base BASE.  */	    if (is_longlong)	      castarg(num, LONGLONG int, unsigned LONGLONG int);	    else if (is_long)	      castarg(num, long int, unsigned long int);	    else if (!is_short)	      castarg(num, int, unsigned int);	    else	      castarg(num, int, unsigned short int);	    /* ANSI only specifies the `+' and	       ` ' flags for signed conversions.  */	    is_neg = showsign = space = 0;	  number:	    /* Number of base BASE.  */	    {	      char work[BUFSIZ];	      char *CONST workend = &work[sizeof(work) - 1];	      register char *w;	      /* Supply a default precision if none was given.  */	      if (prec == -1)		prec = 1;	      /* Put the number in WORK.  */	      w = _itoa (num, workend + 1, base, fc == 'X') - 1;	      width -= workend - w;	      prec -= workend - w;	      if (alt && base == 8 && prec <= 0)		{		  *w-- = '0';		  --width;		}	      if (prec > 0)		{		  width -= prec;		  while (prec-- > 0)		    *w-- = '0';		}	      if (alt && base == 16)		width -= 2;	      if (is_neg || showsign || space)		--width;	      if (!left && pad == ' ')		while (width-- > 0)		  outchar(' ');	      if (is_neg)		outchar('-');	      else if (showsign)		outchar('+');	      else if (space)		outchar(' ');	      if (alt && base == 16)		{		  outchar ('0');		  outchar (fc);		}	      if (!left && pad == '0')		while (width-- > 0)		  outchar('0');	      /* Write the number.  */	      while (++w <= workend)		outchar(*w);	      if (left)		while (width-- > 0)		  outchar(' ');	    }	    break;	  case 'e':	  case 'E':	  case 'f':	  case 'g':	  case 'G':	    {	      /* Floating-point number.  */	      extern printf_function __printf_fp;	      function = __printf_fp;	      goto use_function;	    }	  case 'c':	    /* Character.  */	    nextarg(num, int);	    if (!left)	      while (--width > 0)		outchar(' ');	    outchar((unsigned char) num);	    if (left)	      while (--width > 0)		outchar(' ');	    break;	  case 's':	    {	      static CONST char null[] = "(null)";	      size_t len;	      nextarg(str, CONST char *);	    string:	      if (str == NULL)		/* Write "(null)" if there's space.  */		if (prec == -1 || prec >= (int) sizeof(null) - 1)		  {		    str = null;		    len = sizeof(null) - 1;		  }		else		  {		    str = "";		    len = 0;		  }	      else		len = strlen(str);	      if (prec != -1 && (size_t) prec < len)		len = prec;	      width -= len;	      if (!left)		while (width-- > 0)		  outchar(' ');	      if (len < 20)		while (len-- > 0)		  outchar(*str++);	      else		if (fwrite(str, 1, len, s) != len)		  RETURN(-1);		else		  done += len;	      if (left)		while (width-- > 0)		  outchar(' ');	    }	    break;	  case 'p':	    /* Generic pointer.  */	    {	      CONST PTR ptr;	      nextarg(ptr, CONST PTR);	      if (ptr != NULL)		{		  /* If the pointer is not NULL, write it as a %#x spec.  */		  base = 16;		  fc = 'x';		  alt = 1;		  num = (unsigned LONGLONG int) (unsigned long int) ptr;		  is_neg = 0;		  goto number;		}	      else		{		  /* Write "(nil)" for a nil pointer.  */		  static CONST char nil[] = "(nil)";		  register CONST char *p;		  width -= sizeof (nil) - 1;		  if (!left)		    while (width-- > 0)		      outchar (' ');		  for (p = nil; *p != '\0'; ++p)		    outchar (*p);		  if (left)		    while (width-- > 0)		      outchar (' ');		}	    }	    break;	  case 'n':	    /* Answer the count of characters written.  */	    if (is_longlong)	      {		LONGLONG int *p;		nextarg(p, LONGLONG int *);		*p = done;	      }	    else if (is_long)	      {		long int *p;		nextarg(p, long int *);		*p = done;	      }	    else if (!is_short)	      {		int *p;		nextarg(p, int *);		*p = done;	      }	    else	      {		short int *p;		nextarg(p, short int *);		*p = done;	      }	    break;	  case 'm':#ifndef HAVE_GNU_LD#define _sys_errlist sys_errlist#define _sys_nerr sys_nerr#endif	    if (errno < 0 || errno > _sys_nerr)	      {		sprintf (unknown_error, "Unknown error %d", errno);		str = unknown_error;	      }	    else	      str = _sys_errlist[errno];	    goto string;	  default:	    /* Unrecognized format specifier.  */	    function = printf_unknown;	    goto use_function;	  }      else      use_function:	{	  int function_done;	  struct printf_info info;	  info.prec = prec;	  info.width = width;	  info.spec = fc;	  info.is_long_double = is_long_double;	  info.is_short = is_short;	  info.is_long = is_long;	  info.alt = alt;	  info.space = space;	  info.left = left;	  info.showsign = showsign;	  info.pad = pad;	  function_done = (*function)(s, &info, &args);	  if (function_done < 0)	    RETURN(-1);	  done += function_done;	}    } do_return:;  if (our_buffer)    {      if (fflush(s) == EOF)	return -1;      s->__buffer = s->__bufp = s->__get_limit = s->__put_limit = NULL;      s->__bufsize = 0;    }  return done;}#undef	RETURN#define	RETURN	returnstatic intDEFUN(printf_unknown, (s, type, info, arg),      FILE *s AND CONST struct printf_info *info AND va_list *arg){  int done = 0;  char work[BUFSIZ];  char *CONST workend = &work[sizeof(work) - 1];  register char *w;  register int prec = info->prec, width = info->width;  outchar('%');  if (info->alt)    outchar('#');  if (info->showsign)    outchar('+');  else if (info->space)    outchar(' ');  if (info->left)    outchar('-');  if (info->pad == '0')    outchar('0');  w = workend;  while (width > 0)    {      *w-- = '0' + (width % 10);      width /= 10;    }  while (++w <= workend)    outchar(*w);  if (info->prec != -1)    {      outchar('.');      w = workend;      while (prec > 0)	{	  *w-- = '0' + (prec % 10);	  prec /= 10;	}      while (++w <= workend)	outchar(*w);    }  outchar(info->spec);  return done;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?