about summary refs log blame commit diff
path: root/stdio-common/vfprintf.c
blob: f46c7f352806e8a880de8f3eb43b52f557bb73ee (plain) (tree)
1
                                                                        















                                                                      



                   
                   


                   
                   



                                                             






                                                                


                                                                               




                                    
                                                                               

                                                                               
                                                                               










                                                                               



                                                                               




















                                                                               
                                                                               
                                                                               



                                                                               






                                                                               
                                                                               




                                                                               



                                                                             


                                                 
                                                                        
 
   


                        



                                                            
















                                                                              
 
                                        


                                      




                                               



                                                                         
                                             


                                                           
                                                      
                                                                      
                                                                     
                                                                       
                                                                           
                    








                                                                            
     
                               
         







                                                                               
             




                                                                         

             







                                                                       
                                              
                                                          

                                                                      


                                                
                                                                      
                                                 
 
                                                                          
                                                
 
                                    
         










                                                                              
         
     
 





















                                                                               
                                               















                                                                        
         




                                                                            
             
                                                             
             
         
 
                                    
         




                                                                         
         

                                                                 
 




                                                                              
 

                                                                     
 
                                                                 
 






                                                               
           


                                       
                   
















                                                                              

                                            
                                 
                   

                                          
 



                                                
                          














                                                                       

                                       



                                                            
                                             


















                                                                           


































































                                                                              
                                                                            
                                      
                        









                                                                              

                   
                              
                 










                                                                         
                 
                                                  
                                                                       
                                                                    


                                                                             
                                               
                                   
 







































                                                                            
                                
                                                                  
                           
                                                                  
                      
                                                                  
                            



                                                                  
                                                                             





                                                 
           
 

                                                              



              
                   

                         
                                     






                                                
      
 

                                                                       
          


                                 

                    
                                                
                   
 
                












                       
                       
     

                                                  
     

                       
                                                 
                            
                     
     
                         



















































                                                                              










                                                                              
                                        











                                                                       
















                                                   

          


                                   










                                               
                                                           














                                                                  


                                   











                                              
                

















                                                                   
                  
 
                     


                                             
                                            
            


                                

                         
/* Copyright (C) 1991, 92, 93, 94, 95, 96 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/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, 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 of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB.  If
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
Cambridge, MA 02139, USA.  */

#include <ctype.h>
#include <errno.h>
#include <float.h>
#include <limits.h>
#include <math.h>
#include <printf.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <printf.h>
#include <stddef.h>
#include "_itoa.h"
#include "../locale/localeinfo.h"

/* Include the shared code for parsing the format string.  */
#include "printf-parse.h"


/* This function from the GNU C library is also used in libio.
   To compile for use in libio, compile with -DUSE_IN_LIBIO.  */

#ifdef USE_IN_LIBIO
/* This code is for use in libio.  */
#include <libioP.h>
#define PUT(f, s, n)	_IO_sputn (f, s, n)
#define PAD(padchar)							      \
  if (specs[cnt].info.width > 0)					      \
    done += _IO_padn (s, padchar, specs[cnt].info.width)
#define PUTC(c, f)	_IO_putc (c, f)
#define vfprintf	_IO_vfprintf
#define size_t		_IO_size_t
#define FILE		_IO_FILE
#define va_list		_IO_va_list
#undef	BUFSIZ
#define BUFSIZ		_IO_BUFSIZ
#define ARGCHECK(s, format)						      \
  do									      \
    {									      \
      /* Check file argument for consistence.  */			      \
      CHECK_FILE (s, -1);						      \
      if (s->_flags & _IO_NO_WRITES || format == NULL)			      \
	{								      \
	  MAYBE_SET_EINVAL;						      \
	  return -1;							      \
	}								      \
    } while (0)
#define UNBUFFERED_P(s)	((s)->_IO_file_flags & _IO_UNBUFFERED)
#else /* ! USE_IN_LIBIO */
/* This code is for use in the GNU C library.  */
#include <stdio.h>
#define PUTC(c, f)	putc (c, f)
#define PUT(f, s, n)	fwrite (s, 1, n, f)
ssize_t __printf_pad __P ((FILE *, char pad, size_t n));
#define PAD(padchar)							      \
  if (specs[cnt].info.width > 0)					      \
    { if (__printf_pad (s, padchar, specs[cnt].info.width) == -1)	      \
	return -1; else done += specs[cnt].info.width; }
#define ARGCHECK(s, format) \
  do									      \
    {									      \
      /* Check file argument for consistence.  */			      \
      if (!__validfp(s) || !s->__mode.__write || format == NULL)	      \
	{								      \
	  errno = EINVAL;						      \
	  return -1;							      \
	}								      \
      if (!s->__seen)							      \
	{								      \
	  if (__flshfp (s, EOF) == EOF)					      \
	    return -1;							      \
	}								      \
    } while (0)
#define UNBUFFERED_P(s)	((s)->__buffer == NULL)
#endif /* USE_IN_LIBIO */


#define	outchar(x)							      \
  do									      \
    {									      \
      register const int outc = (x);					      \
      if (PUTC (outc, s) == EOF)					      \
	return -1;							      \
      else								      \
	++done;								      \
    } while (0)

#define outstring(string, len)						      \
  do									      \
    {									      \
      if (len > 20)							      \
	{								      \
	  if (PUT (s, string, len) != len)				      \
	    return -1;							      \
	  done += len;							      \
	}								      \
      else								      \
	{								      \
	  register const char *cp = string;				      \
	  register int l = len;						      \
	  while (l-- > 0)						      \
	    outchar (*cp++);						      \
	}								      \
    } while (0)

/* Helper function to provide temporary buffering for unbuffered streams.  */
static int buffered_vfprintf __P ((FILE *stream, const char *fmt, va_list));

static printf_function printf_unknown;

extern printf_function **__printf_function_table;

static char *group_number __P ((char *, char *, const char *, wchar_t));


int
vfprintf (s, format, ap)
    register FILE *s;
    const char *format;
    va_list ap;
{
  /* The character used as thousands separator.  */
  wchar_t thousands_sep;

  /* The string describing the size of groups of digits.  */
  const char *grouping;

  /* Array with information about the needed arguments.  This has to be
     dynamically extendable.  */
  size_t nspecs;
  size_t nspecs_max;
  struct printf_spec *specs;

  /* The number of arguments the format string requests.  This will
     determine the size of the array needed to store the argument
     attributes.  */
  size_t nargs;
  int *args_type;
  union printf_arg *args_value;

  /* Positional parameters refer to arguments directly.  This could also
     determine the maximum number of arguments.  Track the maximum number.  */
  size_t max_ref_arg;

  /* End of leading constant string.  */
  const char *lead_str_end;

  /* Number of characters written.  */
  register size_t done = 0;

  /* Running pointer through format string.  */
  const char *f;

  /* Just a counter.  */
  int cnt;

  ARGCHECK (s, format);

  if (UNBUFFERED_P (s))
    /* Use a helper function which will allocate a local temporary buffer
       for the stream and then call us again.  */
    return buffered_vfprintf (s, format, ap);

  /* Reset multibyte characters to their initial state.  */
  (void) mblen ((char *) NULL, 0);

  /* Figure out the thousands separator character.  */
  if (mbtowc (&thousands_sep, _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP),
              strlen (_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP))) <= 0)
    thousands_sep = (wchar_t) *_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP);
  grouping = _NL_CURRENT (LC_NUMERIC, GROUPING);
  if (*grouping == '\0' || *grouping == CHAR_MAX || thousands_sep == L'\0')
    grouping = NULL;

  nspecs_max = 32;		/* A more or less arbitrary start value.  */
  specs = alloca (nspecs_max * sizeof (struct printf_spec));
  nspecs = 0;
  nargs = 0;
  max_ref_arg = 0;

  /* Find the first format specifier.  */
  lead_str_end = find_spec (format);

  for (f = lead_str_end; *f != '\0'; f = specs[nspecs++].next_fmt)
    {
      if (nspecs >= nspecs_max)
	{
	  /* Extend the array of format specifiers.  */
	  struct printf_spec *old = specs;

	  nspecs_max *= 2;
	  specs = alloca (nspecs_max * sizeof (struct printf_spec));
	  if (specs == &old[nspecs])
	    /* Stack grows up, OLD was the last thing allocated; extend it.  */
	    nspecs_max += nspecs_max / 2;
	  else
	    {
	      /* Copy the old array's elements to the new space.  */
	      memcpy (specs, old, nspecs * sizeof (struct printf_spec));
	      if (old == &specs[nspecs])
		/* Stack grows down, OLD was just below the new SPECS.
		   We can use that space when the new space runs out.  */
		nspecs_max += nspecs_max / 2;
	    }
	}

      /* Parse the format specifier.  */
      nargs += parse_one_spec (f, nargs, &specs[nspecs], &max_ref_arg);
    }

  /* Determine the number of arguments the format string consumes.  */
  nargs = MAX (nargs, max_ref_arg);

  /* Allocate memory for the argument descriptions.  */
  args_type = alloca (nargs * sizeof (int));
  memset (args_type, 0, nargs * sizeof (int));
  args_value = alloca (nargs * sizeof (union printf_arg));

  /* XXX Could do sanity check here: If any element in ARGS_TYPE is
     still zero after this loop, format is invalid.  For now we simply
     use 0 as the value.  */

  /* Fill in the types of all the arguments.  */
  for (cnt = 0; cnt < nspecs; ++cnt)
    {
      /* If the width is determined by an argument this is an int.  */
      if (specs[cnt].width_arg != -1)
	args_type[specs[cnt].width_arg] = PA_INT;

      /* If the precision is determined by an argument this is an int.  */
      if (specs[cnt].prec_arg != -1)
	args_type[specs[cnt].prec_arg] = PA_INT;

      switch (specs[cnt].ndata_args)
	{
	case 0:			/* No arguments.  */
	  break;
	case 1:			/* One argument; we already have the type.  */
	  args_type[specs[cnt].data_arg] = specs[cnt].data_arg_type;
	  break;
	default:
	  /* We have more than one argument for this format spec.  We must
	     call the arginfo function again to determine all the types.  */
	  (void) (*__printf_arginfo_table[specs[cnt].info.spec])
	    (&specs[cnt].info,
	     specs[cnt].ndata_args, &args_type[specs[cnt].data_arg]);
	  break;
	}
    }

  /* Now we know all the types and the order.  Fill in the argument values.  */
  for (cnt = 0; cnt < nargs; ++cnt)
    switch (args_type[cnt])
      {
#define T(tag, mem, type)						      \
      case tag:								      \
	args_value[cnt].mem = va_arg (ap, type);			      \
	break

	T (PA_CHAR, pa_char, int); /* Promoted.  */
	T (PA_INT|PA_FLAG_SHORT, pa_short_int, int); /* Promoted.  */
	T (PA_INT, pa_int, int);
	T (PA_INT|PA_FLAG_LONG, pa_long_int, long int);
	T (PA_INT|PA_FLAG_LONG_LONG, pa_long_long_int, long long int);
	T (PA_FLOAT, pa_float, double);	/* Promoted.  */
	T (PA_DOUBLE, pa_double, double);
	T (PA_DOUBLE|PA_FLAG_LONG_DOUBLE, pa_long_double, long double);
	T (PA_STRING, pa_string, const char *);
	T (PA_POINTER, pa_pointer, void *);
#undef T
      default:
	if ((args_type[cnt] & PA_FLAG_PTR) != 0)
	  args_value[cnt].pa_pointer = va_arg (ap, void *);
	else
	  args_value[cnt].pa_long_double = 0.0;
	break;
      }

  /* Write the literal text before the first format.  */
  outstring (format, lead_str_end - format);

  /* Now walk through all format specifiers and process them.  */
  for (cnt = 0; cnt < nspecs; ++cnt)
    {
      printf_function *function; /* Auxiliary function to do output.  */
      int is_neg;		/* Decimal integer is negative.  */
      int base;			/* Base of a number to be written.  */
      unsigned long long int num; /* Integral number to be written.  */
      const char *str;		/* String to be written.  */
      char errorbuf[1024];      /* Buffer sometimes used by %m.  */

      if (specs[cnt].width_arg != -1)
	{
	  /* Extract the field width from an argument.  */
	  specs[cnt].info.width = args_value[specs[cnt].width_arg].pa_int;

	  if (specs[cnt].info.width < 0)
	    /* If the width value is negative left justification is selected
	       and the value is taken as being positive.  */
	    {
	      specs[cnt].info.width = -specs[cnt].info.width;
	      specs[cnt].info.left = 1;
	    }
	}

      if (specs[cnt].prec_arg != -1)
	{
	  /* Extract the precision from an argument.  */
	  specs[cnt].info.prec = args_value[specs[cnt].prec_arg].pa_int;

	  if (specs[cnt].info.prec < 0)
	    /* If the precision is negative the precision is omitted.  */
	    specs[cnt].info.prec = -1;
	}

      /* Check for a user-defined handler for this spec.  */
      function = (__printf_function_table == NULL ? NULL :
                  __printf_function_table[specs[cnt].info.spec]);

      if (function != NULL)
      use_function:		/* Built-in formats with helpers use this.  */
	{
	  int function_done;
	  unsigned int i;
	  const void *ptr[specs[cnt].ndata_args];

	  /* Fill in an array of pointers to the argument values.  */
	  for (i = 0; i < specs[cnt].ndata_args; ++i)
	    ptr[i] = &args_value[specs[cnt].data_arg + i];

	  /* Call the function.  */
	  function_done = (*function) (s, &specs[cnt].info, ptr);

	  /* If an error occured don't do any further work.  */
	  if (function_done < 0)
	    return -1;

	  done += function_done;
	}
      else
	switch (specs[cnt].info.spec)
	  {
	  case '%':
	    /* Write a literal "%".  */
	    outchar ('%');
	    break;
	  case 'i':
	  case 'd':
	    {
	      long long int signed_num;

	      /* Decimal integer.  */
	      base = 10;
	      if (specs[cnt].info.is_longlong)
		signed_num = args_value[specs[cnt].data_arg].pa_long_long_int;
	      else if (specs[cnt].info.is_long)
		signed_num = args_value[specs[cnt].data_arg].pa_long_int;
	      else if (!specs[cnt].info.is_short)
		signed_num = args_value[specs[cnt].data_arg].pa_int;
	      else
		signed_num = args_value[specs[cnt].data_arg].pa_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 (specs[cnt].info.is_longlong)
	      num = args_value[specs[cnt].data_arg].pa_u_long_long_int;
            else if (specs[cnt].info.is_long)
	      num = args_value[specs[cnt].data_arg].pa_u_long_int;
            else if (!specs[cnt].info.is_short)
	      num = args_value[specs[cnt].data_arg].pa_u_int;
            else
	      num = args_value[specs[cnt].data_arg].pa_u_short_int;

            /* ANSI only specifies the `+' and
               ` ' flags for signed conversions.  */
            is_neg = 0;
	    specs[cnt].info.showsign = 0;
	    specs[cnt].info.space = 0;

	  number:
	    /* Number of base BASE.  */
            {
              char work[BUFSIZ];
              char *const workend = &work[sizeof(work) - 1];
              register char *w;

              if (specs[cnt].info.prec == -1)
		  /* Supply a default precision if none was given.  */
		  specs[cnt].info.prec = 1;
	      else
		/* We have to take care for the '0' flag.  If a
		   precision is given it must be ignored.  */
		specs[cnt].info.pad = ' ';

	      /* If the precision is 0 and the number is 0 nothing has
		 to be written for the number.  */
	      if (specs[cnt].info.prec == 0 && num == 0)
		w = workend;
	      else
		{
		  /* Put the number in WORK.  */
		  w = _itoa (num, workend + 1, base,
			     specs[cnt].info.spec == 'X');
		  w -= 1;
		  if (specs[cnt].info.group && grouping)
		    w = group_number (w, workend, grouping, thousands_sep);
		}
              specs[cnt].info.width -= workend - w;
              specs[cnt].info.prec -= workend - w;

              if (num != 0 && specs[cnt].info.alt && base == 8
		  && specs[cnt].info.prec <= 0)
                {
		  /* Add octal marker.  */
                  *w-- = '0';
                  --specs[cnt].info.width;
                }

              if (specs[cnt].info.prec > 0)
                {
		  /* Add zeros to the precision.  */
                  specs[cnt].info.width -= specs[cnt].info.prec;
                  while (specs[cnt].info.prec-- > 0)
                    *w-- = '0';
                }

              if (num != 0 && specs[cnt].info.alt && base == 16)
		/* Account for 0X hex marker.  */
                specs[cnt].info.width -= 2;

              if (is_neg || specs[cnt].info.showsign || specs[cnt].info.space)
                --specs[cnt].info.width;

              if (!specs[cnt].info.left && specs[cnt].info.pad == ' ')
                PAD (' ');

              if (is_neg)
                outchar ('-');
              else if (specs[cnt].info.showsign)
                outchar ('+');
              else if (specs[cnt].info.space)
                outchar (' ');

              if (num != 0 && specs[cnt].info.alt && base == 16)
                {
                  outchar ('0');
                  outchar (specs[cnt].info.spec);
                }

              if (!specs[cnt].info.left && specs[cnt].info.pad == '0')
                PAD ('0');

              /* Write the number.  */
              while (++w <= workend)
                outchar (*w);

              if (specs[cnt].info.left)
                PAD (' ');
            }
            break;

          case 'e':
          case 'E':
          case 'f':
          case 'g':
          case 'G':
            {
              /* Floating-point number.  This is handled by printf_fp.c.  */
              extern printf_function __printf_fp;
              function = __printf_fp;
              goto use_function;
            }

          case 'c':
            /* Character.  */
	    --specs[cnt].info.width;/* Account for the character itself.  */
            if (!specs[cnt].info.left)
	      PAD (' ');
            outchar ((unsigned char) args_value[specs[cnt].data_arg].pa_char);
            if (specs[cnt].info.left)
              PAD (' ');
            break;

          case 's':
            {
              static const char null[] = "(null)";
              size_t len;

	      str = args_value[specs[cnt].data_arg].pa_string;

	    string:

              if (str == NULL)
		{
		  /* Write "(null)" if there's space.  */
		  if (specs[cnt].info.prec == -1
		      || specs[cnt].info.prec >= (int) sizeof (null) - 1)
		    {
		      str = null;
		      len = sizeof (null) - 1;
		    }
		  else
		    {
		      str = "";
		      len = 0;
		    }
		}
              else if (specs[cnt].info.prec != -1)
		{
		  /* Search for the end of the string, but don't search
		     past the length specified by the precision.  */
		  const char *end = memchr (str, '\0', specs[cnt].info.prec);
		  if (end)
		    len = end - str;
		  else
		    len = specs[cnt].info.prec;
		}
	      else
		len = strlen (str);

              specs[cnt].info.width -= len;

              if (!specs[cnt].info.left)
                PAD (' ');
              outstring (str, len);
              if (specs[cnt].info.left)
                PAD (' ');
            }
            break;

          case 'p':
            /* Generic pointer.  */
            {
              const void *ptr;
              ptr = args_value[specs[cnt].data_arg].pa_pointer;
              if (ptr != NULL)
                {
                  /* If the pointer is not NULL, write it as a %#x spec.  */
                  base = 16;
                  num = (unsigned long long int) (unsigned long int) ptr;
                  is_neg = 0;
                  specs[cnt].info.alt = 1;
		  specs[cnt].info.spec = 'x';
                  specs[cnt].info.group = 0;
                  goto number;
                }
              else
                {
                  /* Write "(nil)" for a nil pointer.  */
                  str = "(nil)";
		  /* Make sure the full string "(nil)" is printed.  */
		  if (specs[cnt].info.prec < 5)
		    specs[cnt].info.prec = 5;
                  goto string;
                }
            }
            break;

          case 'n':
            /* Answer the count of characters written.  */
            if (specs[cnt].info.is_longlong)
	      *(long long int *)
		args_value[specs[cnt].data_arg].pa_pointer = done;
            else if (specs[cnt].info.is_long)
	      *(long int *)
		args_value[specs[cnt].data_arg].pa_pointer = done;
            else if (!specs[cnt].info.is_short)
	      *(int *)
		args_value[specs[cnt].data_arg].pa_pointer = done;
            else
	      *(short int *)
		args_value[specs[cnt].data_arg].pa_pointer = done;
            break;

          case 'm':
            {
              extern char *_strerror_internal __P ((int, char *buf, size_t));
              str = _strerror_internal (errno, errorbuf, sizeof errorbuf);
              goto string;
            }

          default:
            /* Unrecognized format specifier.  */
            function = printf_unknown;
            goto use_function;
	  }

      /* Write the following constant string.  */
      outstring (specs[cnt].end_of_fmt,
		 specs[cnt].next_fmt - specs[cnt].end_of_fmt);
    }

  return done;
}

#ifdef USE_IN_LIBIO
# undef vfprintf
# ifdef strong_alias
/* This is for glibc.  */
strong_alias (_IO_vfprintf, vfprintf)
# else
#  if defined __ELF__ || defined __GNU_LIBRARY__
#   include <gnu-stabs.h>
#   ifdef weak_alias
weak_alias (_IO_vfprintf, vfprintf);
#   endif
#  endif
# endif
#endif


/* Handle an unknown format specifier.  This prints out a canonicalized
   representation of the format spec itself.  */

static int
printf_unknown (s, info, args)
  FILE *s;
  const struct printf_info *info;
  const void **const args;
{
  int done = 0;
  char work[BUFSIZ];
  char *const workend = &work[sizeof(work) - 1];
  register char *w;

  outchar ('%');

  if (info->alt)
    outchar ('#');
  if (info->group)
    outchar ('\'');
  if (info->showsign)
    outchar ('+');
  else if (info->space)
    outchar (' ');
  if (info->left)
    outchar ('-');
  if (info->pad == '0')
    outchar ('0');

  if (info->width != 0)
    {
      w = _itoa (info->width, workend + 1, 10, 0);
      while (++w <= workend)
	outchar (*w);
    }

  if (info->prec != -1)
    {
      outchar ('.');
      w = _itoa (info->prec, workend + 1, 10, 0);
      while (++w <= workend)
	outchar (*w);
    }

  if (info->spec != '\0')
    outchar (info->spec);

  return done;
}

/* Group the digits according to the grouping rules of the current locale.
   The interpretation of GROUPING is as in `struct lconv' from <locale.h>.  */

static char *
group_number (char *w, char *workend, const char *grouping,
	      wchar_t thousands_sep)
{
  int len;
  char *src, *s;

  /* We treat all negative values like CHAR_MAX.  */

  if (*grouping == CHAR_MAX || *grouping < 0)
    /* No grouping should be done.  */
    return w;

  len = *grouping;

  /* Copy existing string so that nothing gets overwritten.  */
  src = (char *) alloca (workend - w);
  memcpy (src, w + 1, workend - w);
  s = &src[workend - w - 1];
  w = workend;

  /* Process all characters in the string.  */
  while (s >= src)
    {
      *w-- = *s--;

      if (--len == 0 && s >= src)
	{
	  /* A new group begins.  */
	  *w-- = thousands_sep;

	  len = *grouping++;
	  if (*grouping == '\0')
	    /* The previous grouping repeats ad infinitum.  */
	    --grouping;
	  else if (*grouping == CHAR_MAX || *grouping < 0)
	    {
	      /* No further grouping to be done.
		 Copy the rest of the number.  */
	      do
		*w-- = *s--;
	      while (s >= src);
	      break;
	    }
	}
    }
  return w;
}

#ifdef USE_IN_LIBIO
/* Helper "class" for `fprintf to unbuffered': creates a temporary buffer.  */
struct helper_file
  {
    struct _IO_FILE_plus _f;
    _IO_FILE *_put_stream;
  };

static int
_IO_helper_overflow (_IO_FILE *s, int c)
{
  _IO_FILE *target = ((struct helper_file*) s)->_put_stream;
  int used = s->_IO_write_ptr - s->_IO_write_base;
  if (used)
    {
      _IO_size_t written = _IO_sputn (target, s->_IO_write_base, used);
      s->_IO_write_ptr -= written;
    }
  return _IO_putc (c, s);
}

static const struct _IO_jump_t _IO_helper_jumps =
  {
    JUMP_INIT_DUMMY,
    JUMP_INIT (finish, _IO_default_finish),
    JUMP_INIT (overflow, _IO_helper_overflow),
    JUMP_INIT (underflow, _IO_default_underflow),
    JUMP_INIT (uflow, _IO_default_uflow),
    JUMP_INIT (pbackfail, _IO_default_pbackfail),
    JUMP_INIT (xsputn, _IO_default_xsputn),
    JUMP_INIT (xsgetn, _IO_default_xsgetn),
    JUMP_INIT (seekoff, _IO_default_seekoff),
    JUMP_INIT (seekpos, _IO_default_seekpos),
    JUMP_INIT (setbuf, _IO_default_setbuf),
    JUMP_INIT (sync, _IO_default_sync),
    JUMP_INIT (doallocate, _IO_default_doallocate),
    JUMP_INIT (read, _IO_default_read),
    JUMP_INIT (write, _IO_default_write),
    JUMP_INIT (seek, _IO_default_seek),
    JUMP_INIT (close, _IO_default_close),
    JUMP_INIT (stat, _IO_default_stat)
  };

static int
buffered_vfprintf (s, format, args)
  register _IO_FILE *s;
  char const *format;
  _IO_va_list args;
{
  char buf[_IO_BUFSIZ];
  struct helper_file helper;
  register _IO_FILE *hp = (_IO_FILE *) &helper;
  int result, to_flush;

  /* Initialize helper.  */
  helper._put_stream = s;
  hp->_IO_write_base = buf;
  hp->_IO_write_ptr = buf;
  hp->_IO_write_end = buf + sizeof buf;
  hp->_IO_file_flags = _IO_MAGIC|_IO_NO_READS;
  _IO_JUMPS (hp) = (struct _IO_jump_t *) &_IO_helper_jumps;

  /* Now print to helper instead.  */
  result = _IO_vfprintf (hp, format, args);

  /* Now flush anything from the helper to the S. */
  if ((to_flush = hp->_IO_write_ptr - hp->_IO_write_base) > 0)
    {
      if (_IO_sputn (s, hp->_IO_write_base, to_flush) != to_flush)
	return -1;
    }

  return result;
}

#else /* !USE_IN_LIBIO */

static int
buffered_vfprintf (s, format, args)
  register FILE *s;
  char const *format;
  va_list args;
{
  char buf[BUFSIZ];
  int result;

  s->__bufp = s->__buffer = buf;
  s->__bufsize = sizeof buf;
  s->__put_limit = s->__buffer + s->__bufsize;
  s->__get_limit = s->__buffer;

  /* Now use buffer to print.  */
  result = vfprintf (s, format, args);

  if (fflush (s) == EOF)
    result = -1;
  s->__buffer = s->__bufp = s->__get_limit = s->__put_limit = NULL;
  s->__bufsize = 0;

  return result;
}


/* Pads string with given number of a specified character.
   This code is taken from iopadn.c of the GNU I/O library.  */
#define PADSIZE 16
static const char blanks[PADSIZE] =
{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
static const char zeroes[PADSIZE] =
{'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};

ssize_t
__printf_pad (s, pad, count)
     FILE *s;
     char pad;
     size_t count;
{
  const char *padptr;
  register size_t i;

  padptr = pad == ' ' ? blanks : zeroes;

  for (i = count; i >= PADSIZE; i -= PADSIZE)
    if (PUT (s, padptr, PADSIZE) != PADSIZE)
      return -1;
  if (i > 0)
    if (PUT (s, padptr, i) != i)
      return -1;

  return count;
}
#undef PADSIZE
#endif /* USE_IN_LIBIO */