add directory gnu
This commit is contained in:
669
gnu/glibc/glibc-1.03/stdio/vfprintf.c
Normal file
669
gnu/glibc/glibc-1.03/stdio/vfprintf.c
Normal file
@@ -0,0 +1,669 @@
|
||||
/* Copyright (C) 1991, 1992 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 <ansidecl.h>
|
||||
#include <localeinfo.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <float.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <printf.h>
|
||||
|
||||
|
||||
/* If it's an unbuffered stream that we provided
|
||||
temporary buffering for, remove that buffering. */
|
||||
#define RETURN(x) \
|
||||
do \
|
||||
{ \
|
||||
done = (x); \
|
||||
goto do_return; \
|
||||
} while (0)
|
||||
|
||||
#define outchar(x) \
|
||||
do \
|
||||
{ \
|
||||
register CONST int outc = (x); \
|
||||
if (putc(outc, s) == EOF) \
|
||||
RETURN(-1); \
|
||||
else \
|
||||
++done; \
|
||||
} while (0)
|
||||
|
||||
/* Cast the next arg, of type ARGTYPE, into CASTTYPE, and put it in VAR. */
|
||||
#define castarg(var, argtype, casttype) \
|
||||
var = (casttype) va_arg(args, argtype)
|
||||
/* Get the next arg, of type TYPE, and put it in VAR. */
|
||||
#define nextarg(var, type) castarg(var, type, type)
|
||||
|
||||
static printf_function printf_unknown;
|
||||
|
||||
extern printf_function **__printf_function_table;
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define HAVE_LONGLONG
|
||||
#define LONGLONG long long
|
||||
#else
|
||||
#define LONGLONG long
|
||||
#endif
|
||||
|
||||
|
||||
int
|
||||
DEFUN(vfprintf, (s, format, args),
|
||||
register FILE *s AND CONST char *format AND PTR argptr)
|
||||
{
|
||||
va_list args = (va_list) argptr;
|
||||
|
||||
/* Lower-case digits. */
|
||||
static CONST char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
/* Upper-case digits. */
|
||||
static CONST char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
/* Base-36 digits for numbers. */
|
||||
CONST char *digits = lower_digits;
|
||||
|
||||
/* Pointer into the format string. */
|
||||
register CONST char *f;
|
||||
|
||||
/* Number of characters written. */
|
||||
register size_t done = 0;
|
||||
|
||||
/* Nonzero we're providing buffering. */
|
||||
char our_buffer;
|
||||
/* Temporary buffer for unbuffered streams. */
|
||||
char temporary_buffer[BUFSIZ];
|
||||
|
||||
if (!__validfp(s) || !s->__mode.__write || format == NULL)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!s->__seen)
|
||||
{
|
||||
if (__flshfp(s, EOF) == EOF)
|
||||
return EOF;
|
||||
}
|
||||
|
||||
our_buffer = s->__buffer == NULL;
|
||||
if (our_buffer)
|
||||
{
|
||||
/* If it's an unbuffered stream, buffer it
|
||||
at least inside this function call. */
|
||||
s->__bufp = s->__buffer = temporary_buffer;
|
||||
s->__bufsize = sizeof(temporary_buffer);
|
||||
s->__put_limit = s->__buffer + s->__bufsize;
|
||||
s->__get_limit = s->__buffer;
|
||||
}
|
||||
|
||||
/* Reset multibyte characters to their initial state. */
|
||||
(void) mblen((char *) NULL, 0);
|
||||
|
||||
f = format;
|
||||
while (*f != '\0')
|
||||
{
|
||||
/* Type modifiers. */
|
||||
char is_short, is_long, is_long_double;
|
||||
#ifdef HAVE_LONGLONG
|
||||
/* We use the `L' modifier for `long long int'. */
|
||||
#define is_longlong is_long_double
|
||||
#else
|
||||
#define is_longlong 0
|
||||
#endif
|
||||
/* Format spec modifiers. */
|
||||
char space, showsign, left, alt;
|
||||
|
||||
/* Padding character: ' ' or '0'. */
|
||||
char pad;
|
||||
/* Width of a field. */
|
||||
register int width;
|
||||
/* Precision of a field. */
|
||||
int prec;
|
||||
|
||||
/* Decimal integer is negative. */
|
||||
char is_neg;
|
||||
|
||||
/* Current character of the format. */
|
||||
char fc;
|
||||
|
||||
/* Base of a number to be written. */
|
||||
int base;
|
||||
/* Integral values to be written. */
|
||||
unsigned LONGLONG int num;
|
||||
LONGLONG int signed_num;
|
||||
|
||||
/* Auxiliary function to do output. */
|
||||
printf_function *function;
|
||||
|
||||
if (!isascii(*f))
|
||||
{
|
||||
/* Non-ASCII, may be a multibyte. */
|
||||
int len = mblen(f, strlen(f));
|
||||
if (len > 0)
|
||||
{
|
||||
while (len-- > 0)
|
||||
outchar(*f++);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (*f != '%')
|
||||
{
|
||||
/* This isn't a format spec, so write
|
||||
everything out until the next one. */
|
||||
CONST char *next = strchr(f + 1, '%');
|
||||
if (next == NULL)
|
||||
next = strchr(f + 1, '\0');
|
||||
if (next - f > 20)
|
||||
{
|
||||
size_t written = fwrite((PTR) f, 1, next - f, s);
|
||||
done += written;
|
||||
if (written != next - f)
|
||||
break;
|
||||
f += written;
|
||||
}
|
||||
else
|
||||
while (f < next)
|
||||
outchar(*f++);
|
||||
continue;
|
||||
}
|
||||
|
||||
++f;
|
||||
|
||||
/* Check for "%%". Note that although the ANSI standard lists
|
||||
'%' as a conversion specifier, it says "The complete format
|
||||
specification shall be `%%'," so we can avoid all the width
|
||||
and precision processing. */
|
||||
if (*f == '%')
|
||||
{
|
||||
++f;
|
||||
outchar('%');
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check for spec modifiers. */
|
||||
space = showsign = left = alt = 0;
|
||||
pad = ' ';
|
||||
while (*f == ' ' || *f == '+' || *f == '-' || *f == '#' || *f == '0')
|
||||
switch (*f++)
|
||||
{
|
||||
case ' ':
|
||||
/* Output a space in place of a sign, when there is no sign. */
|
||||
space = 1;
|
||||
break;
|
||||
case '+':
|
||||
/* Always output + or - for numbers. */
|
||||
showsign = 1;
|
||||
break;
|
||||
case '-':
|
||||
/* Left-justify things. */
|
||||
left = 1;
|
||||
break;
|
||||
case '#':
|
||||
/* Use the "alternate form":
|
||||
Hex has 0x or 0X, FP always has a decimal point. */
|
||||
alt = 1;
|
||||
break;
|
||||
case '0':
|
||||
/* Pad with 0s. */
|
||||
pad = '0';
|
||||
break;
|
||||
}
|
||||
if (left)
|
||||
pad = ' ';
|
||||
|
||||
/* Get the field width. */
|
||||
width = 0;
|
||||
if (*f == '*')
|
||||
{
|
||||
/* The field width is given in an argument.
|
||||
A negative field width indicates left justification. */
|
||||
nextarg(width, int);
|
||||
if (width < 0)
|
||||
{
|
||||
width = - width;
|
||||
left = 1;
|
||||
}
|
||||
++f;
|
||||
}
|
||||
else
|
||||
while (isdigit(*f))
|
||||
{
|
||||
width *= 10;
|
||||
width += *f++ - '0';
|
||||
}
|
||||
|
||||
/* Get the precision. */
|
||||
/* -1 means none given; 0 means explicit 0. */
|
||||
prec = -1;
|
||||
if (*f == '.')
|
||||
{
|
||||
++f;
|
||||
if (*f == '*')
|
||||
{
|
||||
/* The precision is given in an argument. */
|
||||
nextarg(prec, int);
|
||||
/* Avoid idiocy. */
|
||||
if (prec < 0)
|
||||
prec = -1;
|
||||
++f;
|
||||
}
|
||||
else if (isdigit(*f))
|
||||
{
|
||||
prec = 0;
|
||||
while (*f != '\0' && isdigit(*f))
|
||||
{
|
||||
prec *= 10;
|
||||
prec += *f++ - '0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for type modifiers. */
|
||||
is_short = is_long = is_long_double = 0;
|
||||
while (*f == 'h' || *f == 'l' || *f == 'L')
|
||||
switch (*f++)
|
||||
{
|
||||
case 'h':
|
||||
/* int's are short int's. */
|
||||
is_short = 1;
|
||||
break;
|
||||
case 'l':
|
||||
#ifdef HAVE_LONGLONG
|
||||
if (is_long)
|
||||
/* A double `l' is equivalent to an `L'. */
|
||||
is_longlong = 1;
|
||||
else
|
||||
#endif
|
||||
/* int's are long int's. */
|
||||
is_long = 1;
|
||||
break;
|
||||
case 'L':
|
||||
/* double's are long double's, and int's are long long int's. */
|
||||
is_long_double = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Format specification. */
|
||||
fc = *f++;
|
||||
function = (__printf_function_table == NULL ? NULL :
|
||||
__printf_function_table[fc]);
|
||||
if (function == NULL)
|
||||
switch (fc)
|
||||
{
|
||||
case 'i':
|
||||
case 'd':
|
||||
/* Decimal integer. */
|
||||
base = 10;
|
||||
if (is_longlong)
|
||||
nextarg(signed_num, LONGLONG int);
|
||||
else if (is_long)
|
||||
nextarg(signed_num, long int);
|
||||
else if (!is_short)
|
||||
castarg(signed_num, int, long int);
|
||||
else
|
||||
castarg(signed_num, int, short int);
|
||||
|
||||
is_neg = signed_num < 0;
|
||||
num = is_neg ? (- signed_num) : signed_num;
|
||||
goto number;
|
||||
|
||||
case 'Z':
|
||||
/* `size_t' value. */
|
||||
#ifdef HAVE_LONGLONG
|
||||
if (sizeof(size_t) > sizeof(unsigned long long int))
|
||||
__libc_fatal("`size_t' is bigger than any known type!");
|
||||
else
|
||||
is_longlong = sizeof(size_t) > sizeof(unsigned long int);
|
||||
#endif
|
||||
is_long = sizeof(size_t) > sizeof(unsigned int);
|
||||
|
||||
/* Fall through, to print a `size_t' as a decimal integer. */
|
||||
|
||||
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. */
|
||||
|
||||
digits = fc == 'X' ? upper_digits : lower_digits;
|
||||
|
||||
base = 16;
|
||||
|
||||
unsigned_number:;
|
||||
/* Unsigned number of base BASE. */
|
||||
|
||||
if (is_longlong)
|
||||
castarg(num, LONGLONG int, unsigned LONGLONG int);
|
||||
else if (is_long)
|
||||
castarg(num, long int, unsigned long int);
|
||||
else if (!is_short)
|
||||
castarg(num, int, unsigned int);
|
||||
else
|
||||
castarg(num, int, unsigned short int);
|
||||
|
||||
is_neg = 0;
|
||||
|
||||
number:;
|
||||
/* Number of base BASE. */
|
||||
{
|
||||
char work[BUFSIZ];
|
||||
char *CONST workend = &work[sizeof(work) - 1];
|
||||
register char *w;
|
||||
|
||||
/* Supply a default precision if none was given. */
|
||||
if (prec == -1)
|
||||
prec = 1;
|
||||
|
||||
/* Put the number in WORK. */
|
||||
w = workend;
|
||||
while (num > 0)
|
||||
{
|
||||
*w-- = digits[num % base];
|
||||
num /= base;
|
||||
}
|
||||
width -= workend - w;
|
||||
prec -= workend - w;
|
||||
|
||||
if (alt && base == 8 && prec <= 0)
|
||||
{
|
||||
*w-- = '0';
|
||||
--width;
|
||||
}
|
||||
|
||||
if (prec > 0)
|
||||
{
|
||||
width -= prec;
|
||||
while (prec-- > 0)
|
||||
*w-- = '0';
|
||||
}
|
||||
|
||||
if (alt && base == 16)
|
||||
width -= 2;
|
||||
|
||||
if (is_neg || showsign || space)
|
||||
--width;
|
||||
|
||||
if (!left && pad == ' ')
|
||||
while (width-- > 0)
|
||||
outchar(' ');
|
||||
|
||||
if (is_neg)
|
||||
outchar('-');
|
||||
else if (showsign)
|
||||
outchar('+');
|
||||
else if (space)
|
||||
outchar(' ');
|
||||
|
||||
if (!left && pad == '0')
|
||||
while (width-- > 0)
|
||||
outchar('0');
|
||||
|
||||
if (alt && base == 16)
|
||||
{
|
||||
outchar ('0');
|
||||
outchar (fc);
|
||||
}
|
||||
|
||||
/* Write the number. */
|
||||
while (++w <= workend)
|
||||
outchar(*w);
|
||||
|
||||
if (left)
|
||||
while (width-- > 0)
|
||||
outchar(' ');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
case 'E':
|
||||
case 'f':
|
||||
case 'g':
|
||||
case 'G':
|
||||
{
|
||||
/* Floating-point number. */
|
||||
extern printf_function __printf_fp;
|
||||
function = __printf_fp;
|
||||
goto use_function;
|
||||
}
|
||||
|
||||
case 'c':
|
||||
/* Character. */
|
||||
nextarg(num, int);
|
||||
if (left)
|
||||
while (--width > 0)
|
||||
outchar(' ');
|
||||
outchar((unsigned char) num);
|
||||
if (!left)
|
||||
while (--width > 0)
|
||||
outchar(' ');
|
||||
break;
|
||||
|
||||
case 's':
|
||||
/* String. */
|
||||
{
|
||||
static CONST char null[] = "(null)";
|
||||
CONST char *str;
|
||||
size_t len;
|
||||
|
||||
nextarg(str, CONST char *);
|
||||
if (str == NULL)
|
||||
/* Write "(null)" if there's space. */
|
||||
if (prec == -1 || prec >= (int) sizeof(null) - 1)
|
||||
{
|
||||
str = null;
|
||||
len = sizeof(null) - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
str = "";
|
||||
len = 0;
|
||||
}
|
||||
else
|
||||
len = strlen(str);
|
||||
|
||||
if (prec != -1 && (size_t) prec < len)
|
||||
len = prec;
|
||||
width -= len;
|
||||
|
||||
if (!left)
|
||||
while (width-- > 0)
|
||||
outchar(' ');
|
||||
if (len < 20)
|
||||
while (len-- > 0)
|
||||
outchar(*str++);
|
||||
else
|
||||
if (fwrite(str, 1, len, s) != len)
|
||||
RETURN(-1);
|
||||
else
|
||||
done += len;
|
||||
if (left)
|
||||
while (width-- > 0)
|
||||
outchar(' ');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
/* Generic pointer. */
|
||||
{
|
||||
CONST PTR ptr;
|
||||
nextarg(ptr, CONST PTR);
|
||||
if (ptr != NULL)
|
||||
{
|
||||
/* If the pointer is not NULL, write it as a %#x spec. */
|
||||
base = 16;
|
||||
fc = 'x';
|
||||
alt = 1;
|
||||
num = (unsigned LONGLONG int) (unsigned long int) ptr;
|
||||
is_neg = 0;
|
||||
goto number;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Write "(nil)" for a nil pointer. */
|
||||
static CONST char nil[] = "(nil)";
|
||||
register CONST char *p;
|
||||
|
||||
width -= sizeof(nil) - 1;
|
||||
if (left)
|
||||
while (width-- > 0)
|
||||
outchar(' ');
|
||||
for (p = nil; *p != '\0'; ++p)
|
||||
outchar(*p);
|
||||
if (!left)
|
||||
while (width-- > 0)
|
||||
outchar(' ');
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
/* Answer the count of characters written. */
|
||||
if (is_longlong)
|
||||
{
|
||||
LONGLONG int *p;
|
||||
nextarg(p, LONGLONG int *);
|
||||
*p = done;
|
||||
}
|
||||
else if (is_long)
|
||||
{
|
||||
long int *p;
|
||||
nextarg(p, long int *);
|
||||
*p = done;
|
||||
}
|
||||
else if (!is_short)
|
||||
{
|
||||
int *p;
|
||||
nextarg(p, int *);
|
||||
*p = done;
|
||||
}
|
||||
else
|
||||
{
|
||||
short int *p;
|
||||
nextarg(p, short int *);
|
||||
*p = done;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Unrecognized format specifier. */
|
||||
function = printf_unknown;
|
||||
goto use_function;
|
||||
}
|
||||
else
|
||||
use_function:
|
||||
{
|
||||
int function_done;
|
||||
struct printf_info info;
|
||||
|
||||
info.prec = prec;
|
||||
info.width = width;
|
||||
info.spec = fc;
|
||||
info.is_long_double = is_long_double;
|
||||
info.is_short = is_short;
|
||||
info.is_long = is_long;
|
||||
info.alt = alt;
|
||||
info.space = space;
|
||||
info.left = left;
|
||||
info.showsign = showsign;
|
||||
info.pad = pad;
|
||||
|
||||
function_done = (*function)(s, &info, &args);
|
||||
if (function_done < 0)
|
||||
RETURN(-1);
|
||||
|
||||
done += function_done;
|
||||
}
|
||||
}
|
||||
|
||||
do_return:;
|
||||
if (our_buffer)
|
||||
{
|
||||
if (fflush(s) == EOF)
|
||||
return -1;
|
||||
s->__buffer = s->__bufp = s->__get_limit = s->__put_limit = NULL;
|
||||
s->__bufsize = 0;
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
|
||||
#undef RETURN
|
||||
#define RETURN return
|
||||
|
||||
static int
|
||||
DEFUN(printf_unknown, (s, type, info, arg),
|
||||
FILE *s AND CONST struct printf_info *info AND va_list *arg)
|
||||
{
|
||||
int done = 0;
|
||||
char work[BUFSIZ];
|
||||
char *CONST workend = &work[sizeof(work) - 1];
|
||||
register char *w;
|
||||
register int prec = info->prec, width = info->width;
|
||||
|
||||
outchar('%');
|
||||
|
||||
if (info->alt)
|
||||
outchar('#');
|
||||
if (info->showsign)
|
||||
outchar('+');
|
||||
else if (info->space)
|
||||
outchar(' ');
|
||||
if (info->left)
|
||||
outchar('-');
|
||||
if (info->pad == '0')
|
||||
outchar('0');
|
||||
|
||||
w = workend;
|
||||
while (width > 0)
|
||||
{
|
||||
*w-- = '0' + (width % 10);
|
||||
width /= 10;
|
||||
}
|
||||
while (++w <= workend)
|
||||
outchar(*w);
|
||||
|
||||
if (info->prec != -1)
|
||||
{
|
||||
outchar('.');
|
||||
w = workend;
|
||||
while (prec > 0)
|
||||
{
|
||||
*w-- = '0' + (prec % 10);
|
||||
prec /= 10;
|
||||
}
|
||||
while (++w <= workend)
|
||||
outchar(*w);
|
||||
}
|
||||
|
||||
outchar(info->spec);
|
||||
|
||||
return done;
|
||||
}
|
||||
Reference in New Issue
Block a user