mirror of
https://sourceware.org/git/glibc.git
synced 2025-08-01 10:06:57 +03:00
* Makefile (subdirs): Replace stdio with stdio-common and $(stdio).
* configure.in: Grok arg --enable-libio. ($stdio = libio): Define USE_IN_LIBIO. * config.h.in (USE_IN_LIBIO): Add #undef. * config.make.in (stdio): New variable, set by configure. * Makeconfig (stdio): New variable. * stdio.h [USE_IN_LIBIO]: Include libio/stdio.h instead of stdio/stdio.h. * stdio-common/Makefile: New file. * stdio/Makefile: Half the contents moved to stdio-common/Makefile. * stdio/_itoa.c: Moved to stdio-common. * stdio/_itoa.h: Moved to stdio-common. * stdio/asprintf.c: Moved to stdio-common. * stdio/bug1.c: Moved to stdio-common. * stdio/bug1.input: Moved to stdio-common. * stdio/bug2.c: Moved to stdio-common. * stdio/bug3.c: Moved to stdio-common. * stdio/bug4.c: Moved to stdio-common. * stdio/bug5.c: Moved to stdio-common. * stdio/bug6.c: Moved to stdio-common. * stdio/bug6.input: Moved to stdio-common. * stdio/bug7.c: Moved to stdio-common. * stdio/dprintf.c: Moved to stdio-common. * stdio/errnobug.c: Moved to stdio-common. * stdio/getline.c: Moved to stdio-common. * stdio/getw.c: Moved to stdio-common. * stdio/perror.c: Moved to stdio-common. * stdio/printf-parse.h: Moved to stdio-common. * stdio/printf-prs.c: Moved to stdio-common. * stdio/printf.c: Moved to stdio-common. * stdio/printf.h: Moved to stdio-common. * stdio/printf_fp.c: Moved to stdio-common. * stdio/psignal.c: Moved to stdio-common. * stdio/putw.c: Moved to stdio-common. * stdio/reg-printf.c: Moved to stdio-common. * stdio/scanf.c: Moved to stdio-common. * stdio/snprintf.c: Moved to stdio-common. * stdio/sprintf.c: Moved to stdio-common. * stdio/sscanf.c: Moved to stdio-common. * stdio/tempnam.c: Moved to stdio-common. * stdio/temptest.c: Moved to stdio-common. * stdio/test-fseek.c: Moved to stdio-common. * stdio/test-fwrite.c: Moved to stdio-common. * stdio/test-popen.c: Moved to stdio-common. * stdio/test_rdwr.c: Moved to stdio-common. * stdio/tmpfile.c: Moved to stdio-common. * stdio/tmpnam.c: Moved to stdio-common. * stdio/tst-fileno.c: Moved to stdio-common. * stdio/tst-printf.c: Moved to stdio-common. * stdio/tstgetln.c: Moved to stdio-common. * stdio/tstgetln.input: Moved to stdio-common. * stdio/tstscanf.c: Moved to stdio-common. * stdio/tstscanf.input: Moved to stdio-common. * stdio/vfprintf.c: Moved to stdio-common. * stdio/vfscanf.c: Moved to stdio-common. * stdio/vprintf.c: Moved to stdio-common. * stdio/xbug.c: Moved to stdio-common. * sysdeps/generic/Makefile (siglist.c rules): Do this in subdir stdio-common instead of stdio. * sysdeps/unix/Makefile (errlist.c rules): Likewise. * stdio-common/asprintf.c [USE_IN_LIBIO]: Call libio primitive function. * stdio-common/dprintf.c: Likewise. * stdio-common/printf.c: Likewise. * stdio-common/scanf.c: Likewise. * stdio-common/snprintf.c: Likewise. * stdio-common/sprintf.c: Likewise. * stdio-common/sscanf.c: Likewise. * stdio-common/vprintf.c: Likewise. * Makerules: Include $(+depfiles) directly instead of generating depend-$(subdir). (depend-$(subdir)): Target removed. (common-clean): Don't remove depend-$(subdir).
This commit is contained in:
858
stdio-common/vfprintf.c
Normal file
858
stdio-common/vfprintf.c
Normal file
@ -0,0 +1,858 @@
|
||||
/* Copyright (C) 1991, 1992, 1993, 1994, 1995 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));
|
||||
args_value = alloca (nargs * sizeof (union printf_arg));
|
||||
|
||||
/* XXX Could do sanity check here:
|
||||
Initialize args_type elts to zero.
|
||||
If any is still zero after this loop, format is invalid. */
|
||||
|
||||
/* 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 *);
|
||||
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;
|
||||
|
||||
/* Supply a default precision if none was given. */
|
||||
if (specs[cnt].info.prec == -1)
|
||||
specs[cnt].info.prec = 1;
|
||||
|
||||
/* 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. */
|
||||
if (!specs[cnt].info.left)
|
||||
{
|
||||
--specs[cnt].info.width;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/* 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 (s, c)
|
||||
_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 =
|
||||
{
|
||||
_IO_helper_overflow,
|
||||
_IO_default_underflow,
|
||||
_IO_default_xsputn,
|
||||
_IO_default_xsgetn,
|
||||
_IO_default_read,
|
||||
_IO_default_write,
|
||||
_IO_default_doallocate,
|
||||
_IO_default_pbackfail,
|
||||
_IO_default_setbuf,
|
||||
_IO_default_sync,
|
||||
_IO_default_finish,
|
||||
_IO_default_close,
|
||||
_IO_default_stat,
|
||||
_IO_default_seek,
|
||||
_IO_default_seekoff,
|
||||
_IO_default_seekpos,
|
||||
_IO_default_uflow
|
||||
};
|
||||
|
||||
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;
|
||||
hp->_jumps = (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 */
|
Reference in New Issue
Block a user