1995-02-18 01:27:10 +00:00
|
|
|
|
/* 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>
|
1995-03-24 07:44:08 +00:00
|
|
|
|
#include <printf.h>
|
1995-02-18 01:27:10 +00:00
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <printf.h>
|
|
|
|
|
#include <stddef.h>
|
|
|
|
|
#include "_itoa.h"
|
1995-03-24 07:44:08 +00:00
|
|
|
|
#include "../locale/localeinfo.h"
|
|
|
|
|
|
|
|
|
|
/* Include the shared code for parsing the format string. */
|
|
|
|
|
#include "printf-parse.h"
|
|
|
|
|
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
|
|
|
|
/* 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)
|
1995-03-24 07:44:08 +00:00
|
|
|
|
#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)
|
1995-02-18 01:27:10 +00:00
|
|
|
|
#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
|
1995-03-24 07:44:08 +00:00
|
|
|
|
#define ARGCHECK(s, format) \
|
1995-02-18 01:27:10 +00:00
|
|
|
|
do \
|
|
|
|
|
{ \
|
|
|
|
|
/* Check file argument for consistence. */ \
|
1995-03-24 07:44:08 +00:00
|
|
|
|
CHECK_FILE (s, -1); \
|
1995-02-18 01:27:10 +00:00
|
|
|
|
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)
|
1995-03-24 07:44:08 +00:00
|
|
|
|
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; }
|
1995-02-18 01:27:10 +00:00
|
|
|
|
#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 \
|
|
|
|
|
{ \
|
1995-03-24 07:44:08 +00:00
|
|
|
|
register const int outc = (x); \
|
|
|
|
|
if (putc (outc, s) == EOF) \
|
1995-02-18 01:27:10 +00:00
|
|
|
|
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 \
|
1995-03-24 07:44:08 +00:00
|
|
|
|
{ \
|
|
|
|
|
register const char *cp = string; \
|
|
|
|
|
register int l = len; \
|
|
|
|
|
while (l-- > 0) \
|
|
|
|
|
outchar (*cp++); \
|
|
|
|
|
} \
|
1995-02-18 01:27:10 +00:00
|
|
|
|
} 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));
|
|
|
|
|
|
1995-03-24 07:44:08 +00:00
|
|
|
|
|
1995-02-18 01:27:10 +00:00
|
|
|
|
int
|
1995-03-24 07:44:08 +00:00
|
|
|
|
vfprintf (s, format, ap)
|
|
|
|
|
register FILE *s;
|
|
|
|
|
const char *format;
|
|
|
|
|
va_list ap;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
{
|
|
|
|
|
/* The character used as thousands separator. */
|
|
|
|
|
wchar_t thousands_sep;
|
|
|
|
|
|
|
|
|
|
/* The string describing the size of groups of digits. */
|
1995-03-24 07:44:08 +00:00
|
|
|
|
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;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
1995-03-24 07:44:08 +00:00
|
|
|
|
/* End of leading constant string. */
|
|
|
|
|
const char *lead_str_end;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
|
|
|
|
/* Number of characters written. */
|
|
|
|
|
register size_t done = 0;
|
|
|
|
|
|
1995-03-24 07:44:08 +00:00
|
|
|
|
/* Running pointer through format string. */
|
|
|
|
|
const char *f;
|
|
|
|
|
|
|
|
|
|
/* Just a counter. */
|
|
|
|
|
int cnt;
|
|
|
|
|
|
1995-02-18 01:27:10 +00:00
|
|
|
|
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. */
|
1995-03-24 07:44:08 +00:00
|
|
|
|
return buffered_vfprintf (s, format, ap);
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
|
|
|
|
/* Reset multibyte characters to their initial state. */
|
|
|
|
|
(void) mblen ((char *) NULL, 0);
|
|
|
|
|
|
1995-03-24 07:44:08 +00:00
|
|
|
|
/* Figure out the thousands separator character. */
|
Sun Mar 5 19:40:13 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
* locale/localeinfo.h: Rewritten for new locale system, using
locale data files and with <langinfo.h> interface.
* locale/setlocale.c: Rewritten to use locale data files.
* langinfo.h: New file.
* locale/langinfo.h: New file.
* locale/nl_langinfo.c: New file.
* locale/loadlocale.c: New file.
* locale/lc-ctype.c: New file.
* locale/lc-messages.c: New file.
* locale/lc-monetary.c: New file.
* locale/lc-numeric.c: New file.
* locale/lc-time.c: New file.
* locale/categories.def: New file.
* locale/Makefile (headers): Remove localeinfo.h.
(distribute): New variable; put localeinfo.h here, and categories.def.
(routines): Add loadlocale.
(categories): New variable.
(aux): Use that to get C-category and lc-category.
* ctype/ctype.h (_IS*): Use independent bits for all but _ISalnum.
* locale/C-ctype.c, locale/C-messages.c: New files.
* locale/C-monetary.c, locale/C-numeric.c, locale/C-time.c:
Default "C" locale data updated for new locale system.
* locale/C-collate.c: File removed.
* locale/C-ctype_ct.c: File removed.
* locale/C-ctype_mb.c: File removed.
* locale/C-response.c: File removed.
* locale/localeconv.c: Use _NL_CURRENT macro to access locale data.
* stdio/printf_fp.c, stdio/vfprintf.c, stdio/vfscanf.c,
stdlib/strtod.c, time/asctime.c, time/strftime.c:
Include ../locale/localeinfo.h and use _NL_CURRENT macro to access
locale data.
* time/localtime.c: Don't include <localeinfo.h>.
* time/tzset.c: Don't use locale items for default TZ value or
"GMT" string (use "UTC").
* stdio/vfprintf.c [USE_IN_LIBIO] (PAD): Only call the function if
WIDTH>0; update DONE.
* malloc/malloc.c (morecore): Fix last change to calculate by
blocks instead of bytes.
1995-03-06 03:00:08 +00:00
|
|
|
|
if (mbtowc (&thousands_sep, _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP),
|
1995-03-24 07:44:08 +00:00
|
|
|
|
strlen (_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP))) <= 0)
|
Sun Mar 5 19:40:13 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
* locale/localeinfo.h: Rewritten for new locale system, using
locale data files and with <langinfo.h> interface.
* locale/setlocale.c: Rewritten to use locale data files.
* langinfo.h: New file.
* locale/langinfo.h: New file.
* locale/nl_langinfo.c: New file.
* locale/loadlocale.c: New file.
* locale/lc-ctype.c: New file.
* locale/lc-messages.c: New file.
* locale/lc-monetary.c: New file.
* locale/lc-numeric.c: New file.
* locale/lc-time.c: New file.
* locale/categories.def: New file.
* locale/Makefile (headers): Remove localeinfo.h.
(distribute): New variable; put localeinfo.h here, and categories.def.
(routines): Add loadlocale.
(categories): New variable.
(aux): Use that to get C-category and lc-category.
* ctype/ctype.h (_IS*): Use independent bits for all but _ISalnum.
* locale/C-ctype.c, locale/C-messages.c: New files.
* locale/C-monetary.c, locale/C-numeric.c, locale/C-time.c:
Default "C" locale data updated for new locale system.
* locale/C-collate.c: File removed.
* locale/C-ctype_ct.c: File removed.
* locale/C-ctype_mb.c: File removed.
* locale/C-response.c: File removed.
* locale/localeconv.c: Use _NL_CURRENT macro to access locale data.
* stdio/printf_fp.c, stdio/vfprintf.c, stdio/vfscanf.c,
stdlib/strtod.c, time/asctime.c, time/strftime.c:
Include ../locale/localeinfo.h and use _NL_CURRENT macro to access
locale data.
* time/localtime.c: Don't include <localeinfo.h>.
* time/tzset.c: Don't use locale items for default TZ value or
"GMT" string (use "UTC").
* stdio/vfprintf.c [USE_IN_LIBIO] (PAD): Only call the function if
WIDTH>0; update DONE.
* malloc/malloc.c (morecore): Fix last change to calculate by
blocks instead of bytes.
1995-03-06 03:00:08 +00:00
|
|
|
|
thousands_sep = (wchar_t) *_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP);
|
|
|
|
|
grouping = _NL_CURRENT (LC_NUMERIC, GROUPING);
|
1995-03-24 07:44:08 +00:00
|
|
|
|
if (*grouping == '\0' || *grouping == CHAR_MAX || thousands_sep == L'\0')
|
1995-02-18 01:27:10 +00:00
|
|
|
|
grouping = NULL;
|
|
|
|
|
|
1995-03-24 07:44:08 +00:00
|
|
|
|
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)
|
1995-02-18 01:27:10 +00:00
|
|
|
|
{
|
1995-03-24 07:44:08 +00:00
|
|
|
|
if (nspecs >= nspecs_max)
|
1995-02-18 01:27:10 +00:00
|
|
|
|
{
|
1995-03-24 07:44:08 +00:00
|
|
|
|
/* 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
|
1995-02-18 01:27:10 +00:00
|
|
|
|
{
|
1995-03-24 07:44:08 +00:00
|
|
|
|
/* 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;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1995-03-24 07:44:08 +00:00
|
|
|
|
/* 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;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
1995-03-24 07:44:08 +00:00
|
|
|
|
/* 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;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
1995-03-24 07:44:08 +00:00
|
|
|
|
switch (specs[cnt].ndata_args)
|
1995-02-18 01:27:10 +00:00
|
|
|
|
{
|
1995-03-24 07:44:08 +00:00
|
|
|
|
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;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
}
|
1995-03-24 07:44:08 +00:00
|
|
|
|
}
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
1995-03-24 07:44:08 +00:00
|
|
|
|
/* 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)
|
1995-02-18 01:27:10 +00:00
|
|
|
|
{
|
1995-03-24 07:44:08 +00:00
|
|
|
|
/* 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. */
|
1995-02-18 01:27:10 +00:00
|
|
|
|
{
|
1995-03-24 07:44:08 +00:00
|
|
|
|
specs[cnt].info.width = -specs[cnt].info.width;
|
|
|
|
|
specs[cnt].info.left = 1;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1995-03-24 07:44:08 +00:00
|
|
|
|
if (specs[cnt].prec_arg != -1)
|
1995-02-18 01:27:10 +00:00
|
|
|
|
{
|
1995-03-24 07:44:08 +00:00
|
|
|
|
/* 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;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
1995-03-24 07:44:08 +00:00
|
|
|
|
/* Check for a user-defined handler for this spec. */
|
|
|
|
|
function = (__printf_function_table == NULL ? NULL :
|
|
|
|
|
__printf_function_table[specs[cnt].info.spec]);
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
1995-03-24 07:44:08 +00:00
|
|
|
|
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];
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
1995-03-24 07:44:08 +00:00
|
|
|
|
/* 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];
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
1995-03-24 07:44:08 +00:00
|
|
|
|
/* Call the function. */
|
|
|
|
|
function_done = (*function) (s, &specs[cnt].info, ptr);
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
1995-03-24 07:44:08 +00:00
|
|
|
|
/* 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)
|
1995-02-18 01:27:10 +00:00
|
|
|
|
{
|
1995-03-24 07:44:08 +00:00
|
|
|
|
case '%':
|
|
|
|
|
/* Write a literal "%". */
|
|
|
|
|
outchar ('%');
|
|
|
|
|
break;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
case 'i':
|
|
|
|
|
case 'd':
|
1995-03-24 07:44:08 +00:00
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
|
|
|
|
case 'u':
|
|
|
|
|
/* Decimal unsigned integer. */
|
1995-03-24 07:44:08 +00:00
|
|
|
|
base = 10;
|
|
|
|
|
goto unsigned_number;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
|
|
|
|
case 'o':
|
1995-03-24 07:44:08 +00:00
|
|
|
|
/* Octal unsigned integer. */
|
|
|
|
|
base = 8;
|
|
|
|
|
goto unsigned_number;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
1995-03-24 07:44:08 +00:00
|
|
|
|
case 'X':
|
|
|
|
|
/* Hexadecimal unsigned integer. */
|
|
|
|
|
case 'x':
|
|
|
|
|
/* Hex with lower-case digits. */
|
|
|
|
|
base = 16;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
|
|
|
|
unsigned_number:
|
1995-03-24 07:44:08 +00:00
|
|
|
|
/* 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;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
|
|
|
|
number:
|
|
|
|
|
/* Number of base BASE. */
|
1995-03-24 07:44:08 +00:00
|
|
|
|
{
|
|
|
|
|
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;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
|
|
|
|
string:
|
|
|
|
|
|
1995-03-24 07:44:08 +00:00
|
|
|
|
if (str == NULL)
|
1995-02-18 01:27:10 +00:00
|
|
|
|
{
|
1995-03-24 07:44:08 +00:00
|
|
|
|
/* 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;
|
|
|
|
|
}
|
1995-02-18 01:27:10 +00:00
|
|
|
|
}
|
1995-07-25 14:47:53 +00:00
|
|
|
|
else if (specs[cnt].info.prec != -1)
|
|
|
|
|
{
|
1995-08-25 19:23:32 +00:00
|
|
|
|
/* Search for the end of the string, but don't search
|
Sat Sep 16 17:47:19 1995 Ulrich Drepper <drepper@ipd.info.uni-karlsruhe.de>
* elf/elf.h (AT_GID): Fix typo: Read -> Real.
* misc/efgvt_r.c: New file. Reentrant version of [efg]cvt functions.
* misc/efgcvt.c: Rewrite to use reentrant functions.
* misc/hsearch_r.c: New file. Reentrant version of functions from
hsearch family.
* misc/hsearch.c, misc/tsearch.c: New files.
* misc/Makefile (routines): Add efgcvt_r, hsearch_r, hsearch, tsearch.
* posix/unistd.h (ttyname_r): Add prototype for new function.
* stdlib/drand48_r.c, stdlib/erand48_r.c, stdlib/jrand48_r.c,
stdlib/lrand48_r.c, stdlib/mrand48_r.c, stdlib/nrand48_r.c,
stdlib/seed48_r.c, stdlib/srand48_r.c, stdlib/lcong48_r.c,
stdlib/drand48-iter.c: New files implementing reentrant versions
of functions from drand48 family.
* stdlib/seed48.c, stdlib/drand48.c, stdlib/erand48.c,
stdlib/jrand48.c, stdlib/lrand48.c, stdlib/mrand48.c,
stdlib/nrand48.c, stdlib/srand48.c, stdlib/lcong48.c:
Rewrite to use reentrant versions.
* stdlib/a64l.c, stdlib/l64a.c: New files. Implement a64l()
and l64a() functions from SysV library.
* stdlib/Makefile (routines): Add drand48_r, erand48_r, lrand48_r,
nrand48_r, mrand48_r, jrand48_r, srand48_r, seed48_r, lcong48_r,
drand48-iter, a64l, l64a.
* stdlib/stdlib.h: Declare them.
* stdlib/random_r.c: New file. Reentrant version of functions
from random family.
* stdlib/stdlib.h: Declare them.
* stdlib/random.c: Rewrite to use reentrant functions.
* string/strerror_r.c: New file. Reentrant version.
* string/strerror.c: Change for new _strerror_internal form.
* string/Makefile (routines): Add strerror_r.
* sysdeps/generic/dl-sysdep.c (_dl_sysdep_start): Set default
value of user_entry to `_start'.
Close AT_ENTRY case with `break'.
* sysdeps/generic/strstr.c: New and much faster implementation
by Stephen R. van den Berg.
* sysdeps/generic/_strerror.c: _strerror_internal now takes
three argument and has and explicit buffer length.
* sysdeps/mach/_strerror.c: Change for new interface with three
arguments.
* stdio/perror.c, stdio/vfprintf.c: Callers changed.
* sysdeps/mach/hurd/ttyname_r.c: New file. Reentrant version.
* sysdeps/posix/ttyname_r.c: New file. Reentrant version.
* sysdeps/stub/ttyname_r: New file. Define as dummy function.
* sysdeps/posix/utimes.c: Include <utime.h> for prototype.
(utimes): First parameter to utime must be file, not path.
* sysdeps/posix/sysconf.c (__sysconf): Test for CLK_TCK in case
_SC_CLK_TCK and return it when available.
Test for STREAM_MAX in case _SC_STREAM_MAX and return it when
available.
Add case for _SC_2_LOCALEDEF which is now available.
* posix/sys/types.h [__USE_SVID] (key_t): New type.
* sysvipc/Makefile, sysvipc/ftok.c, sysvipc/sys/ipc.h,
sysvipc/sys/msg.h, sysvipc/sys/sem.h, sysvipc/sys/shm.h,
sysdeps/stub/sys/msq_buf.h, sysdeps/stub/sys/sem_buf.h,
sysdeps/stub/sys/shm_buf.h, sysdeps/stub/sys/ipc_buf.h,
sysdeps/stub/semctl.c, sysdeps/stub/semget.c, sysdeps/stub/semop.c,
sysdeps/stub/shmat.c, sysdeps/stub/shmctl.c, sysdeps/stub/shmdt.c,
sysdeps/stub/shmget.c, sysdeps/stub/msgctl.c, sysdeps/stub/msgget.c,
sysdeps/stub/msgrcv.c, sysdeps/stub/msgsnd.c: New files.
Add implementation of System V IPC.
1995-09-17 20:23:15 +00:00
|
|
|
|
past the length specified by the precision. */
|
1995-07-25 14:47:53 +00:00
|
|
|
|
const char *end = memchr (str, '\0', specs[cnt].info.prec);
|
|
|
|
|
if (end)
|
|
|
|
|
len = end - str;
|
|
|
|
|
else
|
|
|
|
|
len = specs[cnt].info.prec;
|
|
|
|
|
}
|
1995-08-25 19:23:32 +00:00
|
|
|
|
else
|
|
|
|
|
len = strlen (str);
|
1995-03-24 07:44:08 +00:00
|
|
|
|
|
|
|
|
|
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':
|
|
|
|
|
{
|
Sat Sep 16 17:47:19 1995 Ulrich Drepper <drepper@ipd.info.uni-karlsruhe.de>
* elf/elf.h (AT_GID): Fix typo: Read -> Real.
* misc/efgvt_r.c: New file. Reentrant version of [efg]cvt functions.
* misc/efgcvt.c: Rewrite to use reentrant functions.
* misc/hsearch_r.c: New file. Reentrant version of functions from
hsearch family.
* misc/hsearch.c, misc/tsearch.c: New files.
* misc/Makefile (routines): Add efgcvt_r, hsearch_r, hsearch, tsearch.
* posix/unistd.h (ttyname_r): Add prototype for new function.
* stdlib/drand48_r.c, stdlib/erand48_r.c, stdlib/jrand48_r.c,
stdlib/lrand48_r.c, stdlib/mrand48_r.c, stdlib/nrand48_r.c,
stdlib/seed48_r.c, stdlib/srand48_r.c, stdlib/lcong48_r.c,
stdlib/drand48-iter.c: New files implementing reentrant versions
of functions from drand48 family.
* stdlib/seed48.c, stdlib/drand48.c, stdlib/erand48.c,
stdlib/jrand48.c, stdlib/lrand48.c, stdlib/mrand48.c,
stdlib/nrand48.c, stdlib/srand48.c, stdlib/lcong48.c:
Rewrite to use reentrant versions.
* stdlib/a64l.c, stdlib/l64a.c: New files. Implement a64l()
and l64a() functions from SysV library.
* stdlib/Makefile (routines): Add drand48_r, erand48_r, lrand48_r,
nrand48_r, mrand48_r, jrand48_r, srand48_r, seed48_r, lcong48_r,
drand48-iter, a64l, l64a.
* stdlib/stdlib.h: Declare them.
* stdlib/random_r.c: New file. Reentrant version of functions
from random family.
* stdlib/stdlib.h: Declare them.
* stdlib/random.c: Rewrite to use reentrant functions.
* string/strerror_r.c: New file. Reentrant version.
* string/strerror.c: Change for new _strerror_internal form.
* string/Makefile (routines): Add strerror_r.
* sysdeps/generic/dl-sysdep.c (_dl_sysdep_start): Set default
value of user_entry to `_start'.
Close AT_ENTRY case with `break'.
* sysdeps/generic/strstr.c: New and much faster implementation
by Stephen R. van den Berg.
* sysdeps/generic/_strerror.c: _strerror_internal now takes
three argument and has and explicit buffer length.
* sysdeps/mach/_strerror.c: Change for new interface with three
arguments.
* stdio/perror.c, stdio/vfprintf.c: Callers changed.
* sysdeps/mach/hurd/ttyname_r.c: New file. Reentrant version.
* sysdeps/posix/ttyname_r.c: New file. Reentrant version.
* sysdeps/stub/ttyname_r: New file. Define as dummy function.
* sysdeps/posix/utimes.c: Include <utime.h> for prototype.
(utimes): First parameter to utime must be file, not path.
* sysdeps/posix/sysconf.c (__sysconf): Test for CLK_TCK in case
_SC_CLK_TCK and return it when available.
Test for STREAM_MAX in case _SC_STREAM_MAX and return it when
available.
Add case for _SC_2_LOCALEDEF which is now available.
* posix/sys/types.h [__USE_SVID] (key_t): New type.
* sysvipc/Makefile, sysvipc/ftok.c, sysvipc/sys/ipc.h,
sysvipc/sys/msg.h, sysvipc/sys/sem.h, sysvipc/sys/shm.h,
sysdeps/stub/sys/msq_buf.h, sysdeps/stub/sys/sem_buf.h,
sysdeps/stub/sys/shm_buf.h, sysdeps/stub/sys/ipc_buf.h,
sysdeps/stub/semctl.c, sysdeps/stub/semget.c, sysdeps/stub/semop.c,
sysdeps/stub/shmat.c, sysdeps/stub/shmctl.c, sysdeps/stub/shmdt.c,
sysdeps/stub/shmget.c, sysdeps/stub/msgctl.c, sysdeps/stub/msgget.c,
sysdeps/stub/msgrcv.c, sysdeps/stub/msgsnd.c: New files.
Add implementation of System V IPC.
1995-09-17 20:23:15 +00:00
|
|
|
|
extern char *_strerror_internal __P ((int, char *buf, size_t));
|
|
|
|
|
str = _strerror_internal (errno, errorbuf, sizeof errorbuf);
|
1995-03-24 07:44:08 +00:00
|
|
|
|
goto string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
/* Unrecognized format specifier. */
|
|
|
|
|
function = printf_unknown;
|
|
|
|
|
goto use_function;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
1995-03-24 07:44:08 +00:00
|
|
|
|
/* Write the following constant string. */
|
|
|
|
|
outstring (specs[cnt].end_of_fmt,
|
|
|
|
|
specs[cnt].next_fmt - specs[cnt].end_of_fmt);
|
1995-02-18 01:27:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
1995-03-24 07:44:08 +00:00
|
|
|
|
/* Handle an unknown format specifier. This prints out a canonicalized
|
|
|
|
|
representation of the format spec itself. */
|
|
|
|
|
|
1995-02-18 01:27:10 +00:00
|
|
|
|
static int
|
1995-03-24 07:44:08 +00:00
|
|
|
|
printf_unknown (s, info, args)
|
|
|
|
|
FILE *s;
|
|
|
|
|
const struct printf_info *info;
|
|
|
|
|
const void **const args;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
{
|
|
|
|
|
int done = 0;
|
|
|
|
|
char work[BUFSIZ];
|
1995-03-24 07:44:08 +00:00
|
|
|
|
char *const workend = &work[sizeof(work) - 1];
|
1995-02-18 01:27:10 +00:00
|
|
|
|
register char *w;
|
|
|
|
|
|
1995-03-24 07:44:08 +00:00
|
|
|
|
outchar ('%');
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
|
|
|
|
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');
|
|
|
|
|
|
1995-03-24 07:44:08 +00:00
|
|
|
|
if (info->width != 0)
|
1995-02-18 01:27:10 +00:00
|
|
|
|
{
|
1995-03-24 07:44:08 +00:00
|
|
|
|
w = _itoa (info->width, workend + 1, 10, 0);
|
|
|
|
|
while (++w <= workend)
|
|
|
|
|
outchar (*w);
|
1995-02-18 01:27:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (info->prec != -1)
|
|
|
|
|
{
|
1995-03-24 07:44:08 +00:00
|
|
|
|
outchar ('.');
|
|
|
|
|
w = _itoa (info->prec, workend + 1, 10, 0);
|
1995-02-18 01:27:10 +00:00
|
|
|
|
while (++w <= workend)
|
1995-03-24 07:44:08 +00:00
|
|
|
|
outchar (*w);
|
1995-02-18 01:27:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
1995-03-24 07:44:08 +00:00
|
|
|
|
if (info->spec != '\0')
|
|
|
|
|
outchar (info->spec);
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
|
|
|
|
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
|
1995-03-24 07:44:08 +00:00
|
|
|
|
_IO_helper_overflow (s, c)
|
|
|
|
|
_IO_FILE *s;
|
|
|
|
|
int c;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
{
|
|
|
|
|
_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
|
1995-03-24 07:44:08 +00:00
|
|
|
|
buffered_vfprintf (s, format, args)
|
|
|
|
|
register _IO_FILE *s;
|
|
|
|
|
char const *format;
|
|
|
|
|
_IO_va_list args;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
{
|
|
|
|
|
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
|
1995-03-24 07:44:08 +00:00
|
|
|
|
buffered_vfprintf (s, format, args)
|
|
|
|
|
register FILE *s;
|
|
|
|
|
char const *format;
|
|
|
|
|
va_list args;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
{
|
|
|
|
|
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)
|
1995-03-10 12:38:16 +00:00
|
|
|
|
result = -1;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
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;
|
1995-03-24 07:44:08 +00:00
|
|
|
|
size_t count;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
{
|
1995-03-24 07:44:08 +00:00
|
|
|
|
const char *padptr;
|
|
|
|
|
register size_t i;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
|
|
|
|
|
padptr = pad == ' ' ? blanks : zeroes;
|
|
|
|
|
|
|
|
|
|
for (i = count; i >= PADSIZE; i -= PADSIZE)
|
1995-03-24 07:44:08 +00:00
|
|
|
|
if (PUT (s, padptr, PADSIZE) != PADSIZE)
|
|
|
|
|
return -1;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
if (i > 0)
|
1995-03-24 07:44:08 +00:00
|
|
|
|
if (PUT (s, padptr, i) != i)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return count;
|
1995-02-18 01:27:10 +00:00
|
|
|
|
}
|
|
|
|
|
#undef PADSIZE
|
|
|
|
|
#endif /* USE_IN_LIBIO */
|