mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-27 17:23:03 +01:00
5632 lines
121 KiB
C++
5632 lines
121 KiB
C++
/*
|
|
* This is a substitute for stdio, incorporated into InterBase for use
|
|
* on platforms, such as Solaris, where the vendor-supplied stdio
|
|
* prevents fopen() and its kin from working if the open() system call
|
|
* returns a file descriptor greater than 255.
|
|
*
|
|
* Helloooo Suuun! We're not running on PDP-11s any more!
|
|
*/
|
|
|
|
/*-
|
|
* Copyright (c) 1990, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to Berkeley by
|
|
* Chris Torek.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
/* The following copyright notice applies to the function ib__dtoa()
|
|
* below, which was extracted from stdlib/netbsd_strtod.c on FreeBSD.
|
|
*/
|
|
|
|
/****************************************************************
|
|
*
|
|
* The author of this software is David M. Gay.
|
|
*
|
|
* Copyright (c) 1991 by AT&T.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose without fee is hereby granted, provided that this entire notice
|
|
* is included in all copies of any software which is or includes a copy
|
|
* or modification of this software and in all copies of the supporting
|
|
* documentation for such software.
|
|
*
|
|
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
|
|
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR AT&T MAKES ANY
|
|
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
|
|
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
|
|
*
|
|
***************************************************************/
|
|
|
|
/* Please send bug reports to
|
|
David M. Gay
|
|
AT&T Bell Laboratories, Room 2C-463
|
|
600 Mountain Avenue
|
|
Murray Hill, NJ 07974-2070
|
|
U.S.A.
|
|
dmg@research.att.com or research!dmg
|
|
*/
|
|
|
|
|
|
/*
|
|
* We started with /usr/src/lib/libc/stdio from FreeBSD 3.4, for those
|
|
* trying to track the origin of this code.
|
|
*/
|
|
|
|
/* Firebird changes:-
|
|
* Small fixes to get gcc to compile module on Solaris sparc - Neil McCalden
|
|
* Add support for Solaris x86 - Neil McCalden
|
|
*/
|
|
|
|
/*
|
|
$Id: ib_stdio.cpp,v 1.9 2003-02-12 12:51:06 brodsom Exp $
|
|
*/
|
|
|
|
#include "firebird.h"
|
|
//#include "source/jrd/common.h"
|
|
#ifdef NEED_IB_STDIO
|
|
#include "ib_stdio.h"
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <sys/param.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <sys/file.h>
|
|
#include <stdarg.h>
|
|
#include <math.h>
|
|
#ifdef SOLARIS
|
|
#include <ieeefp.h>
|
|
#endif
|
|
#include <ctype.h>
|
|
#include <limits.h>
|
|
#include <signal.h>
|
|
|
|
/* The following definition was included from <machine/param.h> on BSD,
|
|
* but that header doesn't exist on some other platforms.
|
|
* This definition may be wrong for some platforms.
|
|
*/
|
|
#define ALIGNBYTES (sizeof(int) - 1)
|
|
|
|
/* From <paths.h> on BSD */
|
|
#define _PATH_TMP "/tmp/"
|
|
|
|
#define DEFFILEMODE (0666)
|
|
|
|
/* On BSD, these types come from sys/types.h or whatever. We equate them
|
|
* to types which we define for each platform in jrd/common.h.
|
|
*/
|
|
#define u_quad_t UINT64
|
|
#define quad_t SINT64
|
|
#define QUAD_MAX MAX_SINT64
|
|
|
|
/* Give some BSD <stdlib.h> function names Solaris equivalents. */
|
|
#define strtoq strtoll
|
|
#define strtouq strtoull
|
|
|
|
/* These macros call do-nothing functions on BSD: we'll make the macro
|
|
definitions empty for our purposes. */
|
|
#define FLOCKFILE(fp)
|
|
#define FUNLOCKFILE(fp)
|
|
|
|
/* These come from stdio/local.h on BSD */
|
|
|
|
/*
|
|
* Return true iff the given FILE cannot be written now.
|
|
*/
|
|
#define cantwrite(fp) \
|
|
((((fp)->_flags & IB__SWR) == 0 || (fp)->_bf._base == NULL) && \
|
|
ib__swsetup(fp))
|
|
|
|
/*
|
|
* Test whether the given stdio file has an active ungetc buffer;
|
|
* release such a buffer, without restoring ordinary unread data.
|
|
*/
|
|
#define HASUB(fp) ((fp)->_ub._base != NULL)
|
|
#define FREEUB(fp) { \
|
|
if ((fp)->_ub._base != (fp)->_ubuf) \
|
|
free((char *)(fp)->_ub._base); \
|
|
(fp)->_ub._base = NULL; \
|
|
}
|
|
|
|
/*
|
|
* test for an fgetln() buffer.
|
|
*/
|
|
#define HASLB(fp) ((fp)->_lb._base != NULL)
|
|
#define FREELB(fp) { \
|
|
free((char *)(fp)->_lb._base); \
|
|
(fp)->_lb._base = NULL; \
|
|
}
|
|
|
|
|
|
/* BSD stdio wants to see __collate_load_error, which is privately
|
|
* available from locale/collate.c in libc. In moving this off BSD,
|
|
* we will treat this as a constant. The value 1 is what the BSD
|
|
* library initializes the variable to: the value is unchanged when
|
|
* the client program asks for either of the locales "C" or "POSIX",
|
|
* so we use that as the fixed value of this unvarying variable.
|
|
*/
|
|
static const int ib__collate_load_error = 1;
|
|
|
|
/*
|
|
* The first few FILEs are statically allocated; others are dynamically
|
|
* allocated and linked in via this glue structure.
|
|
*/
|
|
static struct glue {
|
|
struct glue *next;
|
|
int niobs;
|
|
IB_FILE *iobs;
|
|
};
|
|
|
|
/* The following 2 defines are taken from stdio/floatio.h in BSD */
|
|
|
|
/*
|
|
* Floating point scanf/printf (input/output) definitions.
|
|
*/
|
|
|
|
/* 11-bit exponent (VAX G floating point) is 308 decimal digits */
|
|
#define MAXEXP 308
|
|
/* 128 bit fraction takes up 39 decimal digits; max reasonable precision */
|
|
#define MAXFRACT 39
|
|
|
|
|
|
|
|
/* Prototypes for functions used before their definitions. */
|
|
|
|
static int ib__sread(void *, char *, int);
|
|
static int ib__swrite(void *, char const *, int n);
|
|
static ib_fpos_t ib__sseek(void *, ib_fpos_t, int);
|
|
static int ib__sclose(void *);
|
|
static IB_FILE *ib__sfp(void);
|
|
static void ib__sinit(void);
|
|
static void ib__smakebuf(IB_FILE *);
|
|
static char *ib__dtoa(double, int, int, int *, int *, char **);
|
|
int ib__sflush(IB_FILE * fp);
|
|
|
|
/*
|
|
* I/O descriptors for ib__sfvwrite().
|
|
*/
|
|
struct ib__siov {
|
|
void *iov_base;
|
|
size_t iov_len;
|
|
};
|
|
struct ib__suio {
|
|
struct ib__siov *uio_iov;
|
|
int uio_iovcnt;
|
|
int uio_resid;
|
|
};
|
|
|
|
/* The following function comes from stdlib on BSD. */
|
|
|
|
static void *reallocf(void *ptr, size_t size)
|
|
{
|
|
void *nptr;
|
|
|
|
nptr = realloc(ptr, size);
|
|
if (!nptr && ptr)
|
|
free(ptr);
|
|
return (nptr);
|
|
}
|
|
|
|
|
|
|
|
|
|
#undef ib_clearerr
|
|
|
|
void ib_clearerr(IB_FILE * fp)
|
|
{
|
|
FLOCKFILE(fp);
|
|
ib__sclearerr(fp);
|
|
FUNLOCKFILE(fp);
|
|
}
|
|
|
|
int ib_fclose(IB_FILE * fp)
|
|
{
|
|
int r;
|
|
|
|
if (fp->_flags == 0) { /* not open! */
|
|
errno = EBADF;
|
|
return (EOF);
|
|
}
|
|
FLOCKFILE(fp);
|
|
r = fp->_flags & IB__SWR ? ib__sflush(fp) : 0;
|
|
if (fp->_close != NULL && (*fp->_close) (fp->_cookie) < 0)
|
|
r = EOF;
|
|
if (fp->_flags & IB__SMBF)
|
|
free((char *) fp->_bf._base);
|
|
if (HASUB(fp))
|
|
FREEUB(fp);
|
|
if (HASLB(fp))
|
|
FREELB(fp);
|
|
FUNLOCKFILE(fp);
|
|
fp->_flags = 0; /* Release this FILE for reuse. */
|
|
fp->_file = -1;
|
|
fp->_r = fp->_w = 0; /* Mess up if reaccessed. */
|
|
return (r);
|
|
}
|
|
|
|
IB_FILE *ib_fdopen(int fd, const char *mode)
|
|
{
|
|
IB_FILE *fp;
|
|
static int nofile;
|
|
int flags, oflags, fdflags, tmp;
|
|
|
|
if (nofile == 0)
|
|
nofile = getdtablesize();
|
|
|
|
if ((flags = ib__sflags(mode, &oflags)) == 0)
|
|
return (NULL);
|
|
|
|
/* Make sure the mode the user wants is a subset of the actual mode. */
|
|
if ((fdflags = fcntl(fd, F_GETFL, 0)) < 0)
|
|
return (NULL);
|
|
tmp = fdflags & O_ACCMODE;
|
|
if (tmp != O_RDWR && (tmp != (oflags & O_ACCMODE))) {
|
|
errno = EINVAL;
|
|
return (NULL);
|
|
}
|
|
|
|
if ((fp = ib__sfp()) == NULL)
|
|
return (NULL);
|
|
fp->_flags = flags;
|
|
/*
|
|
* If opened for appending, but underlying descriptor does not have
|
|
* O_APPEND bit set, assert IB__SAPP so that ib__swrite() will lseek
|
|
* to end before each write.
|
|
*/
|
|
if ((oflags & O_APPEND) && !(fdflags & O_APPEND))
|
|
fp->_flags |= IB__SAPP;
|
|
fp->_file = fd;
|
|
fp->_cookie = fp;
|
|
fp->_read = ib__sread;
|
|
fp->_write = ib__swrite;
|
|
fp->_seek = ib__sseek;
|
|
fp->_close = ib__sclose;
|
|
return (fp);
|
|
}
|
|
|
|
/*
|
|
* A subroutine version of the macro feof.
|
|
*/
|
|
#undef ib_feof
|
|
|
|
int ib_feof(IB_FILE * fp)
|
|
{
|
|
return (ib__sfeof(fp));
|
|
}
|
|
|
|
/*
|
|
* A subroutine version of the macro ferror.
|
|
*/
|
|
#undef ib_ferror
|
|
|
|
int ib_ferror(IB_FILE * fp)
|
|
{
|
|
return (ib__sferror(fp));
|
|
}
|
|
|
|
|
|
/* Flush a single file, or (if fp is NULL) all files. */
|
|
int ib_fflush(IB_FILE * fp)
|
|
{
|
|
int retval;
|
|
|
|
if (fp == NULL)
|
|
return (ib_fwalk(ib__sflush));
|
|
FLOCKFILE(fp);
|
|
if ((fp->_flags & (IB__SWR | IB__SRW)) == 0) {
|
|
errno = EBADF;
|
|
retval = EOF;
|
|
}
|
|
else {
|
|
retval = ib__sflush(fp);
|
|
}
|
|
FUNLOCKFILE(fp);
|
|
return (retval);
|
|
}
|
|
|
|
int ib__sflush(IB_FILE * fp)
|
|
{
|
|
unsigned char *p;
|
|
int n, t;
|
|
|
|
t = fp->_flags;
|
|
if ((t & IB__SWR) == 0)
|
|
return (0);
|
|
|
|
if ((p = fp->_bf._base) == NULL)
|
|
return (0);
|
|
|
|
n = fp->_p - p; /* write this much */
|
|
|
|
/*
|
|
* Set these immediately to allow exchange buffering
|
|
* (via setvbuf) in user write function.
|
|
*/
|
|
fp->_p = p;
|
|
fp->_w = t & (IB__SLBF | IB__SNBF) ? 0 : fp->_bf._size;
|
|
|
|
for (; n > 0; n -= t, p += t) {
|
|
t = (*fp->_write) (fp->_cookie, (char *) p, n);
|
|
if (t <= 0) {
|
|
fp->_flags |= IB__SERR;
|
|
return (EOF);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
|
|
int ib_fgetc(IB_FILE * fp)
|
|
{
|
|
int retval;
|
|
FLOCKFILE(fp);
|
|
retval = ib__sgetc(fp);
|
|
FUNLOCKFILE(fp);
|
|
return (retval);
|
|
}
|
|
|
|
|
|
/*
|
|
* Expand the line buffer. Return -1 on error.
|
|
*/
|
|
int ib__slbexpand(IB_FILE * fp, size_t newsize)
|
|
{
|
|
void *p;
|
|
|
|
if (fp->_lb._size >= newsize)
|
|
return (0);
|
|
if ((p = realloc(fp->_lb._base, newsize)) == NULL)
|
|
return (-1);
|
|
fp->_lb._base = p;
|
|
fp->_lb._size = newsize;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Get an input line. The returned pointer often (but not always)
|
|
* points into a stdio buffer. Fgetln does not alter the text of
|
|
* the returned line (which is thus not a C string because it will
|
|
* not necessarily end with '\0'), but does allow callers to modify
|
|
* it if they wish. Thus, we set __SMOD in case the caller does.
|
|
*/
|
|
char *ib_fgetln(IB_FILE * fp, size_t * lenp)
|
|
{
|
|
unsigned char *p;
|
|
size_t len;
|
|
size_t off;
|
|
|
|
/* make sure there is input */
|
|
if (fp->_r <= 0 && ib__srefill(fp)) {
|
|
*lenp = 0;
|
|
return (NULL);
|
|
}
|
|
|
|
/* look for a newline in the input */
|
|
if ((p = memchr((void *) fp->_p, '\n', (size_t) fp->_r)) != NULL) {
|
|
char *ret;
|
|
|
|
/*
|
|
* Found one. Flag buffer as modified to keep fseek from
|
|
* `optimising' a backward seek, in case the user stomps on
|
|
* the text.
|
|
*/
|
|
p++; /* advance over it */
|
|
ret = (char *) fp->_p;
|
|
*lenp = len = p - fp->_p;
|
|
fp->_flags |= IB__SMOD;
|
|
fp->_r -= len;
|
|
fp->_p = p;
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* We have to copy the current buffered data to the line buffer.
|
|
* As a bonus, though, we can leave off the IB__SMOD.
|
|
*
|
|
* OPTIMISTIC is length that we (optimistically) expect will
|
|
* accomodate the `rest' of the string, on each trip through the
|
|
* loop below.
|
|
*/
|
|
#define OPTIMISTIC 80
|
|
|
|
for (len = fp->_r, off = 0;; len += fp->_r) {
|
|
size_t diff;
|
|
|
|
/*
|
|
* Make sure there is room for more bytes. Copy data from
|
|
* file buffer to line buffer, refill file and look for
|
|
* newline. The loop stops only when we find a newline.
|
|
*/
|
|
if (ib__slbexpand(fp, len + OPTIMISTIC))
|
|
goto error;
|
|
(void) memcpy((void *) (fp->_lb._base + off), (void *) fp->_p,
|
|
len - off);
|
|
off = len;
|
|
if (ib__srefill(fp))
|
|
break; /* EOF or error: return partial line */
|
|
if ((p = memchr((void *) fp->_p, '\n', (size_t) fp->_r)) == NULL)
|
|
continue;
|
|
|
|
/* got it: finish up the line (like code above) */
|
|
p++;
|
|
diff = p - fp->_p;
|
|
len += diff;
|
|
if (ib__slbexpand(fp, len))
|
|
goto error;
|
|
(void) memcpy((void *) (fp->_lb._base + off), (void *) fp->_p, diff);
|
|
fp->_r -= diff;
|
|
fp->_p = p;
|
|
break;
|
|
}
|
|
*lenp = len;
|
|
return ((char *) fp->_lb._base);
|
|
|
|
error:
|
|
*lenp = 0; /* ??? */
|
|
return (NULL); /* ??? */
|
|
}
|
|
|
|
int ib_fgetpos(IB_FILE * fp, ib_fpos_t * pos)
|
|
{
|
|
int retval;
|
|
FLOCKFILE(fp);
|
|
retval = (*pos = ib_ftello(fp)) == (ib_fpos_t) - 1;
|
|
FUNLOCKFILE(fp);
|
|
return (retval);
|
|
}
|
|
|
|
/*
|
|
* Read at most n-1 characters from the given file.
|
|
* Stop when a newline has been read, or the count runs out.
|
|
* Return first argument, or NULL if no characters were read.
|
|
*/
|
|
char *ib_fgets(char *buf, int n, IB_FILE * fp)
|
|
{
|
|
size_t len;
|
|
char *s;
|
|
unsigned char *p, *t;
|
|
|
|
if (n <= 0) /* sanity check */
|
|
return (NULL);
|
|
|
|
FLOCKFILE(fp);
|
|
s = buf;
|
|
n--; /* leave space for NUL */
|
|
while (n != 0) {
|
|
/*
|
|
* If the buffer is empty, refill it.
|
|
*/
|
|
if ((len = fp->_r) <= 0) {
|
|
if (ib__srefill(fp)) {
|
|
/* EOF/error: stop with partial or no line */
|
|
if (s == buf) {
|
|
FUNLOCKFILE(fp);
|
|
return (NULL);
|
|
}
|
|
break;
|
|
}
|
|
len = fp->_r;
|
|
}
|
|
p = fp->_p;
|
|
|
|
/*
|
|
* Scan through at most n bytes of the current buffer,
|
|
* looking for '\n'. If found, copy up to and including
|
|
* newline, and stop. Otherwise, copy entire chunk
|
|
* and loop.
|
|
*/
|
|
if (len > n)
|
|
len = n;
|
|
t = memchr((void *) p, '\n', len);
|
|
if (t != NULL) {
|
|
len = ++t - p;
|
|
fp->_r -= len;
|
|
fp->_p = t;
|
|
(void) memcpy((void *) s, (void *) p, len);
|
|
s[len] = 0;
|
|
FUNLOCKFILE(fp);
|
|
return (buf);
|
|
}
|
|
fp->_r -= len;
|
|
fp->_p += len;
|
|
(void) memcpy((void *) s, (void *) p, len);
|
|
s += len;
|
|
n -= len;
|
|
}
|
|
*s = 0;
|
|
FUNLOCKFILE(fp);
|
|
return (buf);
|
|
}
|
|
|
|
/*
|
|
* A subroutine version of the macro fileno.
|
|
*/
|
|
#undef ib_fileno
|
|
|
|
int ib_fileno(IB_FILE * fp)
|
|
{
|
|
return (ib__sfileno(fp));
|
|
}
|
|
|
|
|
|
static int ib__sdidinit;
|
|
|
|
#define NDYNAMIC 10 /* add ten more whenever necessary */
|
|
|
|
#define std(flags, file) \
|
|
{0,0,0,flags,file,{0},0,ib__sF+file,ib__sclose,ib__sread,ib__sseek,ib__swrite}
|
|
/* p r w flags file _bf z cookie close read seek write */
|
|
|
|
/* the usual - (stdin + stdout + stderr) */
|
|
static IB_FILE usual[IB_FOPEN_MAX - 3];
|
|
static struct glue uglue = { 0, IB_FOPEN_MAX - 3, usual };
|
|
|
|
#define IB_STDIN_FILENO 0
|
|
#define IB_STDOUT_FILENO 1
|
|
#define IB_STDERR_FILENO 2
|
|
|
|
IB_FILE ib__sF[3] = {
|
|
std(IB__SRD, IB_STDIN_FILENO), /* stdin */
|
|
std(IB__SWR, IB_STDOUT_FILENO), /* stdout */
|
|
std(IB__SWR | IB__SNBF, IB_STDERR_FILENO) /* stderr */
|
|
};
|
|
|
|
struct glue ib__sglue = { &uglue, 3, ib__sF };
|
|
|
|
static struct glue *moreglue(int);
|
|
|
|
static struct glue *moreglue(int n)
|
|
{
|
|
struct glue *g;
|
|
IB_FILE *p;
|
|
static IB_FILE empty;
|
|
|
|
g = (struct glue *) malloc(sizeof(*g) + ALIGNBYTES + n * sizeof(IB_FILE));
|
|
if (g == NULL)
|
|
return (NULL);
|
|
/*
|
|
* Note that the ALIGN macro invoked is the one in
|
|
* jrd/common.h, not the one which BSD code expects to find in
|
|
* <machine/param.h>, which has a different number of arguments
|
|
*/
|
|
p = (IB_FILE *) FB_ALIGN(((unsigned) (g + 1)), (sizeof(int)));
|
|
g->next = NULL;
|
|
g->niobs = n;
|
|
g->iobs = p;
|
|
while (--n >= 0)
|
|
*p++ = empty;
|
|
return (g);
|
|
}
|
|
|
|
/*
|
|
* Find a free FILE for fopen et al.
|
|
*/
|
|
IB_FILE *ib__sfp(void)
|
|
{
|
|
IB_FILE *fp;
|
|
int n;
|
|
struct glue *g;
|
|
|
|
if (!ib__sdidinit)
|
|
ib__sinit();
|
|
for (g = &ib__sglue;; g = g->next) {
|
|
for (fp = g->iobs, n = g->niobs; --n >= 0; fp++)
|
|
if (fp->_flags == 0)
|
|
goto found;
|
|
if (g->next == NULL && (g->next = moreglue(NDYNAMIC)) == NULL)
|
|
break;
|
|
}
|
|
return (NULL);
|
|
found:
|
|
fp->_flags = 1; /* reserve this slot; caller sets real flags */
|
|
fp->_p = NULL; /* no current pointer */
|
|
fp->_w = 0; /* nothing to read or write */
|
|
fp->_r = 0;
|
|
fp->_bf._base = NULL; /* no buffer */
|
|
fp->_bf._size = 0;
|
|
fp->_lbfsize = 0; /* not line buffered */
|
|
fp->_file = -1; /* no file */
|
|
/* fp->_cookie = <any>; *//* caller sets cookie, _read/_write etc */
|
|
fp->_ub._base = NULL; /* no ungetc buffer */
|
|
fp->_ub._size = 0;
|
|
fp->_lb._base = NULL; /* no line buffer */
|
|
fp->_lb._size = 0;
|
|
return (fp);
|
|
}
|
|
|
|
/*
|
|
* exit() calls _cleanup() through *__cleanup, set whenever we
|
|
* open or buffer a file. This chicanery is done so that programs
|
|
* that do not use stdio need not link it all in.
|
|
*
|
|
* The name `_cleanup' is, alas, fairly well known outside stdio.
|
|
*
|
|
* This InterBase version will use the atexit() mechanism for cleanup
|
|
* so the _cleanup magic name is not an issue for us.
|
|
*/
|
|
void ib_cleanup(void)
|
|
{
|
|
/* (void) ib_fwalk(ib_fclose); */
|
|
(void) ib_fwalk(ib__sflush); /* `cheating' */
|
|
}
|
|
|
|
/*
|
|
* ib__sinit() is called whenever stdio's internal variables must be set up.
|
|
*/
|
|
static void ib__sinit(void)
|
|
{
|
|
/* make sure we clean up on exit */
|
|
/* ib__cleanup = ib_cleanup; /* conservative */
|
|
atexit(ib_cleanup);
|
|
ib__sdidinit = 1;
|
|
}
|
|
|
|
/*
|
|
* Return the (stdio) flags for a given mode. Store the flags
|
|
* to be passed to an open() syscall through *optr.
|
|
* Return 0 on error.
|
|
*/
|
|
int ib__sflags(const char *mode, int *optr)
|
|
{
|
|
int ret, m, o;
|
|
|
|
switch (*mode++) {
|
|
|
|
case 'r': /* open for reading */
|
|
ret = IB__SRD;
|
|
m = O_RDONLY;
|
|
o = 0;
|
|
break;
|
|
|
|
case 'w': /* open for writing */
|
|
ret = IB__SWR;
|
|
m = O_WRONLY;
|
|
o = O_CREAT | O_TRUNC;
|
|
break;
|
|
|
|
case 'a': /* open for appending */
|
|
ret = IB__SWR;
|
|
m = O_WRONLY;
|
|
o = O_CREAT | O_APPEND;
|
|
break;
|
|
|
|
default: /* illegal mode */
|
|
errno = EINVAL;
|
|
return (0);
|
|
}
|
|
|
|
/* [rwa]\+ or [rwa]b\+ means read and write */
|
|
if (*mode == '+' || (*mode == 'b' && mode[1] == '+')) {
|
|
ret = IB__SRW;
|
|
m = O_RDWR;
|
|
}
|
|
*optr = m | o;
|
|
return (ret);
|
|
}
|
|
|
|
IB_FILE *ib_fopen(const char *file, const char *mode)
|
|
{
|
|
IB_FILE *fp;
|
|
int f;
|
|
int flags, oflags;
|
|
|
|
if ((flags = ib__sflags(mode, &oflags)) == 0)
|
|
return (NULL);
|
|
if ((fp = ib__sfp()) == NULL)
|
|
return (NULL);
|
|
if ((f = open(file, oflags, DEFFILEMODE)) < 0) {
|
|
fp->_flags = 0; /* release */
|
|
return (NULL);
|
|
}
|
|
fp->_file = f;
|
|
fp->_flags = flags;
|
|
fp->_cookie = fp;
|
|
fp->_read = ib__sread;
|
|
fp->_write = ib__swrite;
|
|
fp->_seek = ib__sseek;
|
|
fp->_close = ib__sclose;
|
|
|
|
/*
|
|
* When opening in append mode, even though we use O_APPEND,
|
|
* we need to seek to the end so that ftell() gets the right
|
|
* answer. If the user then alters the seek pointer, or
|
|
* the file extends, this will fail, but there is not much
|
|
* we can do about this. (We could set __SAPP and check in
|
|
* fseek and ftell.)
|
|
*/
|
|
if (oflags & O_APPEND)
|
|
(void) ib__sseek((void *) fp, (fpos_t) 0, SEEK_END);
|
|
return (fp);
|
|
}
|
|
|
|
int ib_fprintf(IB_FILE * fp, const char *fmt, ...)
|
|
{
|
|
int ret;
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
ret = ib_vfprintf(fp, fmt, ap);
|
|
va_end(ap);
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* fpurge: like fflush, but without writing anything: leave the
|
|
* given FILE's buffer empty.
|
|
*/
|
|
int ib_fpurge(IB_FILE * fp)
|
|
{
|
|
int retval;
|
|
FLOCKFILE(fp);
|
|
if (!fp->_flags) {
|
|
errno = EBADF;
|
|
retval = EOF;
|
|
}
|
|
else {
|
|
if (HASUB(fp))
|
|
FREEUB(fp);
|
|
fp->_p = fp->_bf._base;
|
|
fp->_r = 0;
|
|
fp->_w = fp->_flags & (IB__SLBF | IB__SNBF) ? 0 : fp->_bf._size;
|
|
retval = 0;
|
|
}
|
|
FUNLOCKFILE(fp);
|
|
return (retval);
|
|
}
|
|
int ib_fputc(int c, IB_FILE * fp)
|
|
{
|
|
int retval;
|
|
FLOCKFILE(fp);
|
|
retval = ib_putc(c, fp);
|
|
FUNLOCKFILE(fp);
|
|
return (retval);
|
|
}
|
|
|
|
|
|
/* a prototype which is needed now ... */
|
|
int ib__sfvwrite(IB_FILE *, struct ib__suio *);
|
|
|
|
/*
|
|
* Write the given string to the given file.
|
|
*/
|
|
int ib_fputs(const char *s, IB_FILE * fp)
|
|
{
|
|
int retval;
|
|
struct ib__suio uio;
|
|
struct ib__siov iov;
|
|
|
|
iov.iov_base = (void *) s;
|
|
iov.iov_len = uio.uio_resid = strlen(s);
|
|
uio.uio_iov = &iov;
|
|
uio.uio_iovcnt = 1;
|
|
FLOCKFILE(fp);
|
|
retval = ib__sfvwrite(fp, &uio);
|
|
FUNLOCKFILE(fp);
|
|
return (retval);
|
|
}
|
|
|
|
size_t ib_fread(void *buf, size_t size, size_t count, IB_FILE * fp)
|
|
{
|
|
size_t resid;
|
|
char *p;
|
|
int r;
|
|
size_t total;
|
|
|
|
/*
|
|
* The ANSI standard requires a return value of 0 for a count
|
|
* or a size of 0. Peculiarly, it imposes no such requirements
|
|
* on fwrite; it only requires fread to be broken.
|
|
*/
|
|
if ((resid = count * size) == 0)
|
|
return (0);
|
|
FLOCKFILE(fp);
|
|
if (fp->_r < 0)
|
|
fp->_r = 0;
|
|
total = resid;
|
|
p = buf;
|
|
while (resid > (r = fp->_r)) {
|
|
(void) memcpy((void *) p, (void *) fp->_p, (size_t) r);
|
|
fp->_p += r;
|
|
/* fp->_r = 0 ... done in ib__srefill */
|
|
p += r;
|
|
resid -= r;
|
|
if (ib__srefill(fp)) {
|
|
/* no more input: return partial result */
|
|
FUNLOCKFILE(fp);
|
|
return ((total - resid) / size);
|
|
}
|
|
}
|
|
(void) memcpy((void *) p, (void *) fp->_p, resid);
|
|
fp->_r -= resid;
|
|
fp->_p += resid;
|
|
FUNLOCKFILE(fp);
|
|
return (count);
|
|
}
|
|
|
|
/*
|
|
* Re-direct an existing, open (probably) file to some other file.
|
|
* ANSI is written such that the original file gets closed if at
|
|
* all possible, no matter what.
|
|
*/
|
|
IB_FILE *ib_freopen(const char *file, const char *mode, IB_FILE * fp)
|
|
{
|
|
int f;
|
|
int flags, isopen, oflags, sverrno, wantfd;
|
|
|
|
if ((flags = ib__sflags(mode, &oflags)) == 0) {
|
|
(void) ib_fclose(fp);
|
|
return (NULL);
|
|
}
|
|
|
|
if (!ib__sdidinit)
|
|
ib__sinit();
|
|
|
|
/*
|
|
* There are actually programs that depend on being able to "freopen"
|
|
* descriptors that weren't originally open. Keep this from breaking.
|
|
* Remember whether the stream was open to begin with, and which file
|
|
* descriptor (if any) was associated with it. If it was attached to
|
|
* a descriptor, defer closing it; freopen("/dev/stdin", "r", stdin)
|
|
* should work. This is unnecessary if it was not a Unix file.
|
|
*/
|
|
if (fp->_flags == 0) {
|
|
fp->_flags = IB__SEOF; /* hold on to it */
|
|
isopen = 0;
|
|
wantfd = -1;
|
|
}
|
|
else {
|
|
/* flush the stream; ANSI doesn't require this. */
|
|
if (fp->_flags & IB__SWR)
|
|
(void) ib__sflush(fp);
|
|
/* if close is NULL, closing is a no-op, hence pointless */
|
|
isopen = fp->_close != NULL;
|
|
if ((wantfd = fp->_file) < 0 && isopen) {
|
|
(void) (*fp->_close) (fp->_cookie);
|
|
isopen = 0;
|
|
}
|
|
}
|
|
|
|
/* Get a new descriptor to refer to the new file. */
|
|
f = open(file, oflags, DEFFILEMODE);
|
|
if (f < 0 && isopen) {
|
|
/* If out of fd's close the old one and try again. */
|
|
if (errno == ENFILE || errno == EMFILE) {
|
|
(void) (*fp->_close) (fp->_cookie);
|
|
isopen = 0;
|
|
f = open(file, oflags, DEFFILEMODE);
|
|
}
|
|
}
|
|
sverrno = errno;
|
|
|
|
/*
|
|
* Finish closing fp. Even if the open succeeded above, we cannot
|
|
* keep fp->_base: it may be the wrong size. This loses the effect
|
|
* of any setbuffer calls, but stdio has always done this before.
|
|
*/
|
|
if (isopen)
|
|
(void) (*fp->_close) (fp->_cookie);
|
|
if (fp->_flags & IB__SMBF)
|
|
free((char *) fp->_bf._base);
|
|
fp->_w = 0;
|
|
fp->_r = 0;
|
|
fp->_p = NULL;
|
|
fp->_bf._base = NULL;
|
|
fp->_bf._size = 0;
|
|
fp->_lbfsize = 0;
|
|
if (HASUB(fp))
|
|
FREEUB(fp);
|
|
fp->_ub._size = 0;
|
|
if (HASLB(fp))
|
|
FREELB(fp);
|
|
fp->_lb._size = 0;
|
|
|
|
if (f < 0) { /* did not get it after all */
|
|
fp->_flags = 0; /* set it free */
|
|
errno = sverrno; /* restore in case _close clobbered */
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* If reopening something that was open before on a real file, try
|
|
* to maintain the descriptor. Various C library routines (perror)
|
|
* assume stderr is always fd STDERR_FILENO, even if being freopen'd.
|
|
*/
|
|
if (wantfd >= 0 && f != wantfd) {
|
|
if (dup2(f, wantfd) >= 0) {
|
|
(void) close(f);
|
|
f = wantfd;
|
|
}
|
|
}
|
|
|
|
fp->_flags = flags;
|
|
fp->_file = f;
|
|
fp->_cookie = fp;
|
|
fp->_read = ib__sread;
|
|
fp->_write = ib__swrite;
|
|
fp->_seek = ib__sseek;
|
|
fp->_close = ib__sclose;
|
|
return (fp);
|
|
}
|
|
|
|
int ib_fscanf(IB_FILE * fp, char const *fmt, ...)
|
|
{
|
|
int ret;
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
FLOCKFILE(fp);
|
|
ret = ib__svfscanf(fp, fmt, ap);
|
|
va_end(ap);
|
|
FUNLOCKFILE(fp);
|
|
return (ret);
|
|
}
|
|
|
|
#define POS_ERR (-(ib_fpos_t)1)
|
|
|
|
int ib_fseek(IB_FILE * fp, long offset, int whence)
|
|
{
|
|
return (ib_fseeko(fp, offset, whence));
|
|
}
|
|
|
|
/*
|
|
* Seek the given file to the given offset.
|
|
* `Whence' must be one of the three SEEK_* macros.
|
|
*/
|
|
int ib_fseeko(IB_FILE * fp, off_t offset, int whence)
|
|
{
|
|
ib_fpos_t(*seekfn) (void *, fpos_t, int);
|
|
ib_fpos_t target, curoff;
|
|
size_t n;
|
|
struct stat st;
|
|
int havepos;
|
|
|
|
/* make sure stdio is set up */
|
|
if (!ib__sdidinit)
|
|
ib__sinit();
|
|
|
|
FLOCKFILE(fp);
|
|
/*
|
|
* Have to be able to seek.
|
|
*/
|
|
if ((seekfn = fp->_seek) == NULL) {
|
|
errno = ESPIPE; /* historic practice */
|
|
FUNLOCKFILE(fp);
|
|
return (EOF);
|
|
}
|
|
|
|
/*
|
|
* Change any SEEK_CUR to SEEK_SET, and check `whence' argument.
|
|
* After this, whence is either SEEK_SET or SEEK_END.
|
|
*/
|
|
switch (whence) {
|
|
|
|
case SEEK_CUR:
|
|
/*
|
|
* In order to seek relative to the current stream offset,
|
|
* we have to first find the current stream offset a la
|
|
* ftell (see ftell for details).
|
|
*/
|
|
if (fp->_flags & IB__SOFF)
|
|
curoff = fp->_offset;
|
|
else {
|
|
curoff = (*seekfn) (fp->_cookie, (fpos_t) 0, SEEK_CUR);
|
|
if (curoff == -1) {
|
|
FUNLOCKFILE(fp);
|
|
return (EOF);
|
|
}
|
|
}
|
|
if (fp->_flags & IB__SRD) {
|
|
curoff -= fp->_r;
|
|
if (HASUB(fp))
|
|
curoff -= fp->_ur;
|
|
}
|
|
else if (fp->_flags & IB__SWR && fp->_p != NULL)
|
|
curoff += fp->_p - fp->_bf._base;
|
|
|
|
offset += curoff;
|
|
whence = SEEK_SET;
|
|
havepos = 1;
|
|
break;
|
|
|
|
case SEEK_SET:
|
|
case SEEK_END:
|
|
curoff = 0; /* XXX just to keep gcc quiet */
|
|
havepos = 0;
|
|
break;
|
|
|
|
default:
|
|
errno = EINVAL;
|
|
FUNLOCKFILE(fp);
|
|
return (EOF);
|
|
}
|
|
|
|
/*
|
|
* Can only optimise if:
|
|
* reading (and not reading-and-writing);
|
|
* not unbuffered; and
|
|
* this is a `regular' Unix file (and hence seekfn==ib__sseek).
|
|
* We must check IB__NBF first, because it is possible to have IB__NBF
|
|
* and IB__SOPT both set.
|
|
*/
|
|
if (fp->_bf._base == NULL)
|
|
ib__smakebuf(fp);
|
|
if (fp->_flags & (IB__SWR | IB__SRW | IB__SNBF | IB__SNPT))
|
|
goto dumb;
|
|
if ((fp->_flags & IB__SOPT) == 0) {
|
|
if (seekfn != ib__sseek ||
|
|
fp->_file < 0 || fstat(fp->_file, &st) ||
|
|
(st.st_mode & S_IFMT) != S_IFREG) {
|
|
fp->_flags |= IB__SNPT;
|
|
goto dumb;
|
|
}
|
|
fp->_blksize = st.st_blksize;
|
|
fp->_flags |= IB__SOPT;
|
|
}
|
|
|
|
/*
|
|
* We are reading; we can try to optimise.
|
|
* Figure out where we are going and where we are now.
|
|
*/
|
|
if (whence == SEEK_SET)
|
|
target = offset;
|
|
else {
|
|
if (fstat(fp->_file, &st))
|
|
goto dumb;
|
|
target = st.st_size + offset;
|
|
}
|
|
|
|
if (!havepos) {
|
|
if (fp->_flags & IB__SOFF)
|
|
curoff = fp->_offset;
|
|
else {
|
|
curoff = (*seekfn) (fp->_cookie, (fpos_t) 0, SEEK_CUR);
|
|
if (curoff == POS_ERR)
|
|
goto dumb;
|
|
}
|
|
curoff -= fp->_r;
|
|
if (HASUB(fp))
|
|
curoff -= fp->_ur;
|
|
}
|
|
|
|
/*
|
|
* Compute the number of bytes in the input buffer (pretending
|
|
* that any ungetc() input has been discarded). Adjust current
|
|
* offset backwards by this count so that it represents the
|
|
* file offset for the first byte in the current input buffer.
|
|
*/
|
|
if (HASUB(fp)) {
|
|
curoff += fp->_r; /* kill off ungetc */
|
|
n = fp->_up - fp->_bf._base;
|
|
curoff -= n;
|
|
n += fp->_ur;
|
|
}
|
|
else {
|
|
n = fp->_p - fp->_bf._base;
|
|
curoff -= n;
|
|
n += fp->_r;
|
|
}
|
|
|
|
/*
|
|
* If the target offset is within the current buffer,
|
|
* simply adjust the pointers, clear EOF, undo ungetc(),
|
|
* and return. (If the buffer was modified, we have to
|
|
* skip this; see fgetln.c.)
|
|
*/
|
|
if ((fp->_flags & IB__SMOD) == 0 &&
|
|
target >= curoff && target < curoff + n) {
|
|
int o = target - curoff;
|
|
|
|
fp->_p = fp->_bf._base + o;
|
|
fp->_r = n - o;
|
|
if (HASUB(fp))
|
|
FREEUB(fp);
|
|
fp->_flags &= ~IB__SEOF;
|
|
FUNLOCKFILE(fp);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* The place we want to get to is not within the current buffer,
|
|
* but we can still be kind to the kernel copyout mechanism.
|
|
* By aligning the file offset to a block boundary, we can let
|
|
* the kernel use the VM hardware to map pages instead of
|
|
* copying bytes laboriously. Using a block boundary also
|
|
* ensures that we only read one block, rather than two.
|
|
*/
|
|
curoff = target & ~(fp->_blksize - 1);
|
|
if ((*seekfn) (fp->_cookie, curoff, SEEK_SET) == POS_ERR)
|
|
goto dumb;
|
|
fp->_r = 0;
|
|
fp->_p = fp->_bf._base;
|
|
if (HASUB(fp))
|
|
FREEUB(fp);
|
|
fp->_flags &= ~IB__SEOF;
|
|
n = target - curoff;
|
|
if (n) {
|
|
if (ib__srefill(fp) || fp->_r < n)
|
|
goto dumb;
|
|
fp->_p += n;
|
|
fp->_r -= n;
|
|
}
|
|
FUNLOCKFILE(fp);
|
|
return (0);
|
|
|
|
/*
|
|
* We get here if we cannot optimise the seek ... just
|
|
* do it. Allow the seek function to change fp->_bf._base.
|
|
*/
|
|
dumb:
|
|
if (ib__sflush(fp) ||
|
|
(*seekfn) (fp->_cookie, (ib_fpos_t) offset, whence) == POS_ERR) {
|
|
FUNLOCKFILE(fp);
|
|
return (EOF);
|
|
}
|
|
/* success: clear EOF indicator and discard ungetc() data */
|
|
if (HASUB(fp))
|
|
FREEUB(fp);
|
|
fp->_p = fp->_bf._base;
|
|
fp->_r = 0;
|
|
/* fp->_w = 0; *//* unnecessary (I think...) */
|
|
fp->_flags &= ~IB__SEOF;
|
|
FUNLOCKFILE(fp);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* fsetpos: like fseek.
|
|
*/
|
|
int ib_fsetpos(IB_FILE * iop, const ib_fpos_t * pos)
|
|
{
|
|
return (ib_fseeko(iop, (off_t) * pos, SEEK_SET));
|
|
}
|
|
|
|
/*
|
|
* standard ftell function.
|
|
*/
|
|
long ib_ftell(IB_FILE * fp)
|
|
{
|
|
off_t rv;
|
|
rv = ib_ftello(fp);
|
|
if ((long) rv != rv) {
|
|
errno = EOVERFLOW;
|
|
return (-1);
|
|
}
|
|
return (rv);
|
|
}
|
|
|
|
/*
|
|
* ib_ftello: return current offset.
|
|
*/
|
|
off_t ib_ftello(IB_FILE * fp)
|
|
{
|
|
ib_fpos_t pos;
|
|
|
|
if (fp->_seek == NULL) {
|
|
errno = ESPIPE; /* historic practice */
|
|
return (-1L);
|
|
}
|
|
|
|
FLOCKFILE(fp);
|
|
/*
|
|
* Find offset of underlying I/O object, then
|
|
* adjust for buffered bytes.
|
|
*/
|
|
if (fp->_flags & IB__SOFF)
|
|
pos = fp->_offset;
|
|
else {
|
|
pos = (*fp->_seek) (fp->_cookie, (fpos_t) 0, SEEK_CUR);
|
|
if (pos == -1) {
|
|
FUNLOCKFILE(fp);
|
|
return (pos);
|
|
}
|
|
}
|
|
if (fp->_flags & IB__SRD) {
|
|
/*
|
|
* Reading. Any unread characters (including
|
|
* those from ungetc) cause the position to be
|
|
* smaller than that in the underlying object.
|
|
*/
|
|
pos -= fp->_r;
|
|
if (HASUB(fp))
|
|
pos -= fp->_ur;
|
|
}
|
|
else if (fp->_flags & IB__SWR && fp->_p != NULL) {
|
|
/*
|
|
* Writing. Any buffered characters cause the
|
|
* position to be greater than that in the
|
|
* underlying object.
|
|
*/
|
|
pos += fp->_p - fp->_bf._base;
|
|
}
|
|
FUNLOCKFILE(fp);
|
|
return (pos);
|
|
}
|
|
|
|
IB_FILE *ib_funopen(const void *cookie,
|
|
int (*readfn) (),
|
|
int (*writefn) (),
|
|
ib_fpos_t(*seekfn) (void *cookie, ib_fpos_t off,
|
|
int whence), int (*closefn) ())
|
|
{
|
|
IB_FILE *fp;
|
|
int flags;
|
|
|
|
if (readfn == NULL) {
|
|
if (writefn == NULL) { /* illegal */
|
|
errno = EINVAL;
|
|
return (NULL);
|
|
}
|
|
else
|
|
flags = IB__SWR; /* write only */
|
|
}
|
|
else {
|
|
if (writefn == NULL)
|
|
flags = IB__SRD; /* read only */
|
|
else
|
|
flags = IB__SRW; /* read-write */
|
|
}
|
|
if ((fp = ib__sfp()) == NULL)
|
|
return (NULL);
|
|
fp->_flags = flags;
|
|
fp->_file = -1;
|
|
fp->_cookie = (void *) cookie;
|
|
fp->_read = readfn;
|
|
fp->_write = writefn;
|
|
fp->_seek = seekfn;
|
|
fp->_close = closefn;
|
|
return (fp);
|
|
}
|
|
|
|
|
|
int ib_fwalk(int (*function) (IB_FILE *))
|
|
{
|
|
IB_FILE *fp;
|
|
int n, ret;
|
|
struct glue *g;
|
|
|
|
ret = 0;
|
|
for (g = &ib__sglue; g != NULL; g = g->next)
|
|
for (fp = g->iobs, n = g->niobs; --n >= 0; fp++)
|
|
if (fp->_flags != 0)
|
|
ret |= (*function) (fp);
|
|
return (ret);
|
|
}
|
|
|
|
|
|
/*
|
|
* Write `count' objects (each size `size') from memory to the given file.
|
|
* Return the number of whole objects written.
|
|
*/
|
|
size_t ib_fwrite(const void *buf, size_t size, size_t count, IB_FILE * fp)
|
|
{
|
|
size_t n;
|
|
struct ib__suio uio;
|
|
struct ib__siov iov;
|
|
|
|
iov.iov_base = (void *) buf;
|
|
uio.uio_resid = iov.iov_len = n = count * size;
|
|
uio.uio_iov = &iov;
|
|
uio.uio_iovcnt = 1;
|
|
|
|
FLOCKFILE(fp);
|
|
/*
|
|
* The usual case is success (ib__sfvwrite returns 0);
|
|
* skip the divide if this happens, since divides are
|
|
* generally slow and since this occurs whenever size==0.
|
|
*/
|
|
if (ib__sfvwrite(fp, &uio) != 0)
|
|
count = (n - uio.uio_resid) / size;
|
|
FUNLOCKFILE(fp);
|
|
return (count);
|
|
}
|
|
|
|
/*
|
|
* A subroutine version of the macro getc.
|
|
*/
|
|
#undef ib_getc
|
|
|
|
int ib_getc(IB_FILE * fp)
|
|
{
|
|
int retval;
|
|
FLOCKFILE(fp);
|
|
retval = ib__sgetc(fp);
|
|
FUNLOCKFILE(fp);
|
|
return (retval);
|
|
}
|
|
|
|
/*
|
|
* A subroutine version of the macro getchar.
|
|
*/
|
|
|
|
#undef ib_getchar
|
|
|
|
int ib_getchar(void)
|
|
{
|
|
int retval;
|
|
FLOCKFILE(ib_stdin);
|
|
retval = ib_getc(ib_stdin);
|
|
FUNLOCKFILE(ib_stdin);
|
|
return (retval);
|
|
}
|
|
|
|
int ib_getw(IB_FILE * fp)
|
|
{
|
|
int x;
|
|
|
|
return (ib_fread((void *) &x, sizeof(x), 1, fp) == 1 ? x : EOF);
|
|
}
|
|
|
|
/*
|
|
* Allocate a file buffer, or switch to unbuffered I/O.
|
|
* Per the ANSI C standard, ALL tty devices default to line buffered.
|
|
*
|
|
* As a side effect, we set IB__SOPT or IB__SNPT (en/dis-able fseek
|
|
* optimisation) right after the fstat() that finds the buffer size.
|
|
*/
|
|
static void ib__smakebuf(IB_FILE * fp)
|
|
{
|
|
void *p;
|
|
int flags;
|
|
size_t size;
|
|
int couldbetty;
|
|
|
|
if (fp->_flags & IB__SNBF) {
|
|
fp->_bf._base = fp->_p = fp->_nbuf;
|
|
fp->_bf._size = 1;
|
|
return;
|
|
}
|
|
flags = ib__swhatbuf(fp, &size, &couldbetty);
|
|
if ((p = malloc(size)) == NULL) {
|
|
fp->_flags |= IB__SNBF;
|
|
fp->_bf._base = fp->_p = fp->_nbuf;
|
|
fp->_bf._size = 1;
|
|
return;
|
|
}
|
|
/* ib__cleanup = ib_cleanup; */
|
|
flags |= IB__SMBF;
|
|
fp->_bf._base = fp->_p = p;
|
|
fp->_bf._size = size;
|
|
if (couldbetty && isatty(fp->_file))
|
|
flags |= IB__SLBF;
|
|
fp->_flags |= flags;
|
|
}
|
|
|
|
/*
|
|
* Internal routine to determine `proper' buffering for a file.
|
|
*/
|
|
int ib__swhatbuf(IB_FILE * fp, size_t * bufsize, int *couldbetty)
|
|
{
|
|
struct stat st;
|
|
|
|
if (fp->_file < 0 || fstat(fp->_file, &st) < 0) {
|
|
*couldbetty = 0;
|
|
*bufsize = BUFSIZ;
|
|
return (IB__SNPT);
|
|
}
|
|
|
|
/* could be a tty iff it is a character device */
|
|
*couldbetty = (st.st_mode & S_IFMT) == S_IFCHR;
|
|
if (st.st_blksize <= 0) {
|
|
*bufsize = BUFSIZ;
|
|
return (IB__SNPT);
|
|
}
|
|
|
|
/*
|
|
* Optimise ib_fseek() only if it is a regular file. (The test for
|
|
* ib__sseek is mainly paranoia.) It is safe to set _blksize
|
|
* unconditionally; it will only be used if IB__SOPT is also set.
|
|
*/
|
|
*bufsize = st.st_blksize;
|
|
fp->_blksize = st.st_blksize;
|
|
return ((st.st_mode & S_IFMT) == S_IFREG && fp->_seek == ib__sseek ?
|
|
IB__SOPT : IB__SNPT);
|
|
}
|
|
|
|
void ib_perror(const char *s)
|
|
{
|
|
struct iovec *v;
|
|
struct iovec iov[4];
|
|
|
|
v = iov;
|
|
if (s != NULL && *s != '\0') {
|
|
v->iov_base = (char *) s;
|
|
v->iov_len = strlen(s);
|
|
v++;
|
|
v->iov_base = ": ";
|
|
v->iov_len = 2;
|
|
v++;
|
|
}
|
|
v->iov_base = strerror(errno);
|
|
v->iov_len = strlen(v->iov_base);
|
|
v++;
|
|
v->iov_base = "\n";
|
|
v->iov_len = 1;
|
|
(void) writev(STDERR_FILENO, iov, (v - iov) + 1);
|
|
}
|
|
|
|
|
|
int ib_printf(char const *fmt, ...)
|
|
{
|
|
int ret;
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
ret = ib_vfprintf(ib_stdout, fmt, ap);
|
|
va_end(ap);
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* A subroutine version of the macro putc.
|
|
*/
|
|
#undef ib_putc
|
|
|
|
int ib_putc(int c, IB_FILE * fp)
|
|
{
|
|
int retval;
|
|
FLOCKFILE(fp);
|
|
retval = ib__sputc(c, fp);
|
|
FUNLOCKFILE(fp);
|
|
return (retval);
|
|
}
|
|
|
|
#undef ib_putchar
|
|
|
|
/*
|
|
* A subroutine version of the macro putchar
|
|
*/
|
|
int ib_putchar(int c)
|
|
{
|
|
int retval;
|
|
IB_FILE *so = ib_stdout;
|
|
|
|
FLOCKFILE(so);
|
|
retval = ib__sputc(c, so);
|
|
FUNLOCKFILE(so);
|
|
return (retval);
|
|
}
|
|
|
|
/*
|
|
* Write the given string to stdout, appending a newline.
|
|
*/
|
|
int ib_puts(char const *s)
|
|
{
|
|
int retval;
|
|
size_t c = strlen(s);
|
|
struct ib__suio uio;
|
|
struct ib__siov iov[2];
|
|
|
|
iov[0].iov_base = (void *) s;
|
|
iov[0].iov_len = c;
|
|
iov[1].iov_base = "\n";
|
|
iov[1].iov_len = 1;
|
|
uio.uio_resid = c + 1;
|
|
uio.uio_iov = &iov[0];
|
|
uio.uio_iovcnt = 2;
|
|
FLOCKFILE(ib_stdout);
|
|
retval = ib__sfvwrite(ib_stdout, &uio) ? EOF : '\n';
|
|
FUNLOCKFILE(ib_stdout);
|
|
return (retval);
|
|
}
|
|
|
|
int ib_putw(int w, IB_FILE * fp)
|
|
{
|
|
int retval;
|
|
struct ib__suio uio;
|
|
struct ib__siov iov;
|
|
|
|
iov.iov_base = &w;
|
|
iov.iov_len = uio.uio_resid = sizeof(w);
|
|
uio.uio_iov = &iov;
|
|
uio.uio_iovcnt = 1;
|
|
FLOCKFILE(fp);
|
|
retval = ib__sfvwrite(fp, &uio);
|
|
FUNLOCKFILE(fp);
|
|
return (retval);
|
|
}
|
|
|
|
|
|
static int lflush(IB_FILE * fp)
|
|
{
|
|
|
|
if ((fp->_flags & (IB__SLBF | IB__SWR)) == (IB__SLBF | IB__SWR))
|
|
return (ib__sflush(fp));
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Refill a stdio buffer.
|
|
* Return EOF on eof or error, 0 otherwise.
|
|
*/
|
|
int ib__srefill(IB_FILE * fp)
|
|
{
|
|
|
|
/* make sure stdio is set up */
|
|
if (!ib__sdidinit)
|
|
ib__sinit();
|
|
|
|
fp->_r = 0; /* largely a convenience for callers */
|
|
|
|
/* SysV does not make this test; take it out for compatibility */
|
|
if (fp->_flags & IB__SEOF)
|
|
return (EOF);
|
|
|
|
/* if not already reading, have to be reading and writing */
|
|
if ((fp->_flags & IB__SRD) == 0) {
|
|
if ((fp->_flags & IB__SRW) == 0) {
|
|
errno = EBADF;
|
|
return (EOF);
|
|
}
|
|
/* switch to reading */
|
|
if (fp->_flags & IB__SWR) {
|
|
if (ib__sflush(fp))
|
|
return (EOF);
|
|
fp->_flags &= ~IB__SWR;
|
|
fp->_w = 0;
|
|
fp->_lbfsize = 0;
|
|
}
|
|
fp->_flags |= IB__SRD;
|
|
}
|
|
else {
|
|
/*
|
|
* We were reading. If there is an ungetc buffer,
|
|
* we must have been reading from that. Drop it,
|
|
* restoring the previous buffer (if any). If there
|
|
* is anything in that buffer, return.
|
|
*/
|
|
if (HASUB(fp)) {
|
|
FREEUB(fp);
|
|
if ((fp->_r = fp->_ur) != 0) {
|
|
fp->_p = fp->_up;
|
|
return (0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fp->_bf._base == NULL)
|
|
ib__smakebuf(fp);
|
|
|
|
/*
|
|
* Before reading from a line buffered or unbuffered file,
|
|
* flush all line buffered output files, per the ANSI C
|
|
* standard.
|
|
*/
|
|
if (fp->_flags & (IB__SLBF | IB__SNBF))
|
|
(void) ib_fwalk(lflush);
|
|
fp->_p = fp->_bf._base;
|
|
fp->_r = (*fp->_read) (fp->_cookie, (char *) fp->_p, fp->_bf._size);
|
|
fp->_flags &= ~IB__SMOD; /* buffer contents are again pristine */
|
|
if (fp->_r <= 0) {
|
|
if (fp->_r == 0)
|
|
fp->_flags |= IB__SEOF;
|
|
else {
|
|
fp->_r = 0;
|
|
fp->_flags |= IB__SERR;
|
|
}
|
|
return (EOF);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
|
|
void ib_rewind(IB_FILE * fp)
|
|
{
|
|
FLOCKFILE(fp);
|
|
(void) ib_fseek(fp, 0L, SEEK_SET);
|
|
ib_clearerr(fp);
|
|
FUNLOCKFILE(fp);
|
|
errno = 0; /* not required, but seems reasonable */
|
|
}
|
|
|
|
int ib__srefill(IB_FILE *);
|
|
|
|
/*
|
|
* Handle getc() when the buffer ran out:
|
|
* Refill, then return the first character
|
|
* in the newly-filled buffer.
|
|
*/
|
|
int ib__srget(IB_FILE * fp)
|
|
{
|
|
if (ib__srefill(fp) == 0) {
|
|
fp->_r--;
|
|
return (*fp->_p++);
|
|
}
|
|
return (EOF);
|
|
}
|
|
int ib_scanf(char const *fmt, ...)
|
|
{
|
|
int ret;
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
FLOCKFILE(ib_stdin);
|
|
ret = ib__svfscanf(ib_stdin, fmt, ap);
|
|
FUNLOCKFILE(ib_stdin);
|
|
va_end(ap);
|
|
return (ret);
|
|
}
|
|
|
|
void ib_setbuf(IB_FILE * fp, char *buf)
|
|
{
|
|
(void) ib_setvbuf(fp, buf, buf ? _IOFBF : _IONBF, BUFSIZ);
|
|
}
|
|
|
|
void ib_setbuffer(IB_FILE * fp, char *buf, int size)
|
|
{
|
|
|
|
(void) ib_setvbuf(fp, buf, buf ? _IOFBF : _IONBF, (size_t) size);
|
|
}
|
|
|
|
/*
|
|
* set line buffering
|
|
*/
|
|
int ib_setlinebuf(IB_FILE * fp)
|
|
{
|
|
|
|
return (ib_setvbuf(fp, (char *) NULL, _IOLBF, (size_t) 0));
|
|
}
|
|
|
|
/*
|
|
* Set one of the three kinds of buffering, optionally including
|
|
* a buffer.
|
|
*/
|
|
int ib_setvbuf(IB_FILE * fp,
|
|
char *buf, int mode, size_t size)
|
|
{
|
|
int ret, flags;
|
|
size_t iosize;
|
|
int ttyflag;
|
|
|
|
/*
|
|
* Verify arguments. The `int' limit on `size' is due to this
|
|
* particular implementation. Note, buf and size are ignored
|
|
* when setting _IONBF.
|
|
*/
|
|
if (mode != _IONBF)
|
|
if ((mode != _IOFBF && mode != _IOLBF) || (int) size < 0)
|
|
return (EOF);
|
|
|
|
FLOCKFILE(fp);
|
|
/*
|
|
* Write current buffer, if any. Discard unread input (including
|
|
* ungetc data), cancel line buffering, and free old buffer if
|
|
* malloc()ed. We also clear any eof condition, as if this were
|
|
* a seek.
|
|
*/
|
|
ret = 0;
|
|
(void) ib__sflush(fp);
|
|
if (HASUB(fp))
|
|
FREEUB(fp);
|
|
fp->_r = fp->_lbfsize = 0;
|
|
flags = fp->_flags;
|
|
if (flags & IB__SMBF)
|
|
free((void *) fp->_bf._base);
|
|
flags &= ~(IB__SLBF | IB__SNBF | IB__SMBF | IB__SOPT |
|
|
IB__SNPT | IB__SEOF);
|
|
|
|
/* If setting unbuffered mode, skip all the hard work. */
|
|
if (mode == _IONBF)
|
|
goto nbf;
|
|
|
|
/*
|
|
* Find optimal I/O size for seek optimization. This also returns
|
|
* a `tty flag' to suggest that we check isatty(fd), but we do not
|
|
* care since our caller told us how to buffer.
|
|
*/
|
|
flags |= ib__swhatbuf(fp, &iosize, &ttyflag);
|
|
if (size == 0) {
|
|
buf = NULL; /* force local allocation */
|
|
size = iosize;
|
|
}
|
|
|
|
/* Allocate buffer if needed. */
|
|
if (buf == NULL) {
|
|
if ((buf = malloc(size)) == NULL) {
|
|
/*
|
|
* Unable to honor user's request. We will return
|
|
* failure, but try again with file system size.
|
|
*/
|
|
ret = EOF;
|
|
if (size != iosize) {
|
|
size = iosize;
|
|
buf = malloc(size);
|
|
}
|
|
}
|
|
if (buf == NULL) {
|
|
/* No luck; switch to unbuffered I/O. */
|
|
nbf:
|
|
fp->_flags = flags | IB__SNBF;
|
|
fp->_w = 0;
|
|
fp->_bf._base = fp->_p = fp->_nbuf;
|
|
fp->_bf._size = 1;
|
|
FUNLOCKFILE(fp);
|
|
return (ret);
|
|
}
|
|
flags |= IB__SMBF;
|
|
}
|
|
|
|
/*
|
|
* Kill any seek optimization if the buffer is not the
|
|
* right size.
|
|
*
|
|
* SHOULD WE ALLOW MULTIPLES HERE (i.e., ok iff (size % iosize) == 0)?
|
|
*/
|
|
if (size != iosize)
|
|
flags |= IB__SNPT;
|
|
|
|
/*
|
|
* Fix up the FILE fields, and set ib__cleanup for output flush on
|
|
* exit (since we are buffered in some way).
|
|
*/
|
|
if (mode == _IOLBF)
|
|
flags |= IB__SLBF;
|
|
fp->_flags = flags;
|
|
fp->_bf._base = fp->_p = (unsigned char *) buf;
|
|
fp->_bf._size = size;
|
|
/* fp->_lbfsize is still 0 */
|
|
if (flags & IB__SWR) {
|
|
/*
|
|
* Begin or continue writing: see ib__swsetup(). Note
|
|
* that IB__SNBF is impossible (it was handled earlier).
|
|
*/
|
|
if (flags & IB__SLBF) {
|
|
fp->_w = 0;
|
|
fp->_lbfsize = -fp->_bf._size;
|
|
}
|
|
else
|
|
fp->_w = size;
|
|
}
|
|
else {
|
|
/* begin/continue reading, or stay in intermediate state */
|
|
fp->_w = 0;
|
|
}
|
|
/* ib__cleanup = ib_cleanup; */
|
|
|
|
FUNLOCKFILE(fp);
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* Small standard I/O/seek/close functions.
|
|
* These maintain the `known seek offset' for seek optimisation.
|
|
*/
|
|
static int ib__sread(void *cookie, char *buf, int n)
|
|
{
|
|
IB_FILE *fp = cookie;
|
|
int ret;
|
|
|
|
ret = read(fp->_file, buf, (size_t) n);
|
|
/* if the read succeeded, update the current offset */
|
|
if (ret >= 0)
|
|
fp->_offset += ret;
|
|
else
|
|
fp->_flags &= ~IB__SOFF; /* paranoia */
|
|
return (ret);
|
|
}
|
|
|
|
static int ib__swrite(void *cookie, char const *buf, int n)
|
|
{
|
|
IB_FILE *fp = cookie;
|
|
|
|
if (fp->_flags & IB__SAPP)
|
|
(void) lseek(fp->_file, (off_t) 0, SEEK_END);
|
|
fp->_flags &= ~IB__SOFF; /* in case FAPPEND mode is set */
|
|
return (write(fp->_file, buf, (size_t) n));
|
|
}
|
|
|
|
static ib_fpos_t ib__sseek(void *cookie, ib_fpos_t offset, int whence)
|
|
{
|
|
IB_FILE *fp = cookie;
|
|
off_t ret;
|
|
|
|
ret = lseek(fp->_file, (off_t) offset, whence);
|
|
if (ret == -1)
|
|
fp->_flags &= ~IB__SOFF;
|
|
else {
|
|
fp->_flags |= IB__SOFF;
|
|
fp->_offset = ret;
|
|
}
|
|
return (ret);
|
|
}
|
|
|
|
static int ib__sclose(void *cookie)
|
|
{
|
|
return (close(((IB_FILE *) cookie)->_file));
|
|
}
|
|
|
|
IB_FILE *ib_tmpfile(void)
|
|
{
|
|
sigset_t set, oset;
|
|
IB_FILE *fp;
|
|
int fd, sverrno;
|
|
#define TRAILER "tmp.XXXXXX"
|
|
char buf[sizeof(_PATH_TMP) + sizeof(TRAILER)];
|
|
|
|
(void) memcpy(buf, _PATH_TMP, sizeof(_PATH_TMP) - 1);
|
|
(void) memcpy(buf + sizeof(_PATH_TMP) - 1, TRAILER, sizeof(TRAILER));
|
|
|
|
sigfillset(&set);
|
|
(void) sigprocmask(SIG_BLOCK, &set, &oset);
|
|
|
|
fd = mkstemp(buf);
|
|
if (fd != -1)
|
|
(void) unlink(buf);
|
|
|
|
(void) sigprocmask(SIG_SETMASK, &oset, NULL);
|
|
|
|
if (fd == -1)
|
|
return (NULL);
|
|
|
|
if ((fp = ib_fdopen(fd, "w+")) == NULL) {
|
|
sverrno = errno;
|
|
(void) close(fd);
|
|
errno = sverrno;
|
|
return (NULL);
|
|
}
|
|
return (fp);
|
|
}
|
|
|
|
/*
|
|
* Expand the ungetc buffer `in place'. That is, adjust fp->_p when
|
|
* the buffer moves, so that it points the same distance from the end,
|
|
* and move the bytes in the buffer around as necessary so that they
|
|
* are all at the end (stack-style).
|
|
*/
|
|
static int ib__submore(IB_FILE * fp)
|
|
{
|
|
int i;
|
|
unsigned char *p;
|
|
|
|
if (fp->_ub._base == fp->_ubuf) {
|
|
/*
|
|
* Get a new buffer (rather than expanding the old one).
|
|
*/
|
|
if ((p = malloc((size_t) BUFSIZ)) == NULL)
|
|
return (EOF);
|
|
fp->_ub._base = p;
|
|
fp->_ub._size = BUFSIZ;
|
|
p += BUFSIZ - sizeof(fp->_ubuf);
|
|
for (i = sizeof(fp->_ubuf); --i >= 0;)
|
|
p[i] = fp->_ubuf[i];
|
|
fp->_p = p;
|
|
return (0);
|
|
}
|
|
i = fp->_ub._size;
|
|
p = realloc(fp->_ub._base, (size_t) (i << 1));
|
|
if (p == NULL)
|
|
return (EOF);
|
|
/* no overlap (hence can use memcpy) because we doubled the size */
|
|
(void) memcpy((void *) (p + i), (void *) p, (size_t) i);
|
|
fp->_p = p + i;
|
|
fp->_ub._base = p;
|
|
fp->_ub._size = i << 1;
|
|
return (0);
|
|
}
|
|
|
|
int ib_ungetc(int c, IB_FILE * fp)
|
|
{
|
|
if (c == EOF)
|
|
return (EOF);
|
|
if (!ib__sdidinit)
|
|
ib__sinit();
|
|
FLOCKFILE(fp);
|
|
if ((fp->_flags & IB__SRD) == 0) {
|
|
/*
|
|
* Not already reading: no good unless reading-and-writing.
|
|
* Otherwise, flush any current write stuff.
|
|
*/
|
|
if ((fp->_flags & IB__SRW) == 0) {
|
|
FUNLOCKFILE(fp);
|
|
return (EOF);
|
|
}
|
|
if (fp->_flags & IB__SWR) {
|
|
if (ib__sflush(fp)) {
|
|
FUNLOCKFILE(fp);
|
|
return (EOF);
|
|
}
|
|
fp->_flags &= ~IB__SWR;
|
|
fp->_w = 0;
|
|
fp->_lbfsize = 0;
|
|
}
|
|
fp->_flags |= IB__SRD;
|
|
}
|
|
c = (unsigned char) c;
|
|
|
|
/*
|
|
* If we are in the middle of ungetc'ing, just continue.
|
|
* This may require expanding the current ungetc buffer.
|
|
*/
|
|
if (HASUB(fp)) {
|
|
if (fp->_r >= fp->_ub._size && ib__submore(fp)) {
|
|
FUNLOCKFILE(fp);
|
|
return (EOF);
|
|
}
|
|
*--fp->_p = c;
|
|
fp->_r++;
|
|
FUNLOCKFILE(fp);
|
|
return (c);
|
|
}
|
|
fp->_flags &= ~IB__SEOF;
|
|
|
|
/*
|
|
* If we can handle this by simply backing up, do so,
|
|
* but never replace the original character.
|
|
* (This makes sscanf() work when scanning `const' data.)
|
|
*/
|
|
if (fp->_bf._base != NULL && fp->_p > fp->_bf._base && fp->_p[-1] == c) {
|
|
fp->_p--;
|
|
fp->_r++;
|
|
FUNLOCKFILE(fp);
|
|
return (c);
|
|
}
|
|
|
|
/*
|
|
* Create an ungetc buffer.
|
|
* Initially, we will use the `reserve' buffer.
|
|
*/
|
|
fp->_ur = fp->_r;
|
|
fp->_up = fp->_p;
|
|
fp->_ub._base = fp->_ubuf;
|
|
fp->_ub._size = sizeof(fp->_ubuf);
|
|
fp->_ubuf[sizeof(fp->_ubuf) - 1] = c;
|
|
fp->_p = &fp->_ubuf[sizeof(fp->_ubuf) - 1];
|
|
fp->_r = 1;
|
|
FUNLOCKFILE(fp);
|
|
return (c);
|
|
}
|
|
|
|
/*
|
|
* Actual printf innards.
|
|
*
|
|
* This code is large and complicated...
|
|
*/
|
|
|
|
/* Define FLOATING_POINT to get floating point. */
|
|
#define FLOATING_POINT
|
|
|
|
static int ib__sprint(IB_FILE *, struct ib__suio *);
|
|
static int ib__sbprintf(IB_FILE *, const char *, va_list);
|
|
static char *ib__ultoa(u_long, char *, int, int, char *);
|
|
static char *ib__uqtoa(u_quad_t, char *, int, int, char *);
|
|
static void ib__find_arguments(const char *, va_list, void ***);
|
|
static void ib__grow_type_table(int, unsigned char **, int *);
|
|
|
|
/*
|
|
* Flush out all the vectors defined by the given uio,
|
|
* then reset it so that it can be reused.
|
|
*/
|
|
static int ib__sprint(IB_FILE * fp, struct ib__suio *uio)
|
|
{
|
|
int err;
|
|
|
|
if (uio->uio_resid == 0) {
|
|
uio->uio_iovcnt = 0;
|
|
return (0);
|
|
}
|
|
err = ib__sfvwrite(fp, uio);
|
|
uio->uio_resid = 0;
|
|
uio->uio_iovcnt = 0;
|
|
return (err);
|
|
}
|
|
|
|
/*
|
|
* Helper function for `fprintf to unbuffered unix file': creates a
|
|
* temporary buffer. We only work on write-only files; this avoids
|
|
* worries about ungetc buffers and so forth.
|
|
*/
|
|
static int ib__sbprintf(IB_FILE * fp, const char *fmt, va_list ap)
|
|
{
|
|
int ret;
|
|
IB_FILE fake;
|
|
unsigned char buf[BUFSIZ];
|
|
|
|
/* copy the important variables */
|
|
fake._flags = fp->_flags & ~IB__SNBF;
|
|
fake._file = fp->_file;
|
|
fake._cookie = fp->_cookie;
|
|
fake._write = fp->_write;
|
|
|
|
/* set up the buffer */
|
|
fake._bf._base = fake._p = buf;
|
|
fake._bf._size = fake._w = sizeof(buf);
|
|
fake._lbfsize = 0; /* not actually used, but Just In Case */
|
|
|
|
/* do the work, then copy any error status */
|
|
ret = ib_vfprintf(&fake, fmt, ap);
|
|
if (ret >= 0 && ib_fflush(&fake))
|
|
ret = EOF;
|
|
if (fake._flags & IB__SERR)
|
|
fp->_flags |= IB__SERR;
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* Macros for converting digits to letters and vice versa
|
|
*/
|
|
#define to_digit(c) ((c) - '0')
|
|
#define is_digit(c) ((unsigned)to_digit(c) <= 9)
|
|
#define to_char(n) ((n) + '0')
|
|
|
|
/*
|
|
* Convert an unsigned long to ASCII for printf purposes, returning
|
|
* a pointer to the first character of the string representation.
|
|
* Octal numbers can be forced to have a leading zero; hex numbers
|
|
* use the given digits.
|
|
*/
|
|
static char *ib__ultoa(u_long val, char *endp, int base, int octzero,
|
|
char *xdigs)
|
|
{
|
|
char *cp = endp;
|
|
long sval;
|
|
|
|
/*
|
|
* Handle the three cases separately, in the hope of getting
|
|
* better/faster code.
|
|
*/
|
|
switch (base) {
|
|
case 10:
|
|
if (val < 10) { /* many numbers are 1 digit */
|
|
*--cp = to_char(val);
|
|
return (cp);
|
|
}
|
|
/*
|
|
* On many machines, unsigned arithmetic is harder than
|
|
* signed arithmetic, so we do at most one unsigned mod and
|
|
* divide; this is sufficient to reduce the range of
|
|
* the incoming value to where signed arithmetic works.
|
|
*/
|
|
if (val > LONG_MAX) {
|
|
*--cp = to_char(val % 10);
|
|
sval = val / 10;
|
|
}
|
|
else
|
|
sval = val;
|
|
do {
|
|
*--cp = to_char(sval % 10);
|
|
sval /= 10;
|
|
} while (sval != 0);
|
|
break;
|
|
|
|
case 8:
|
|
do {
|
|
*--cp = to_char(val & 7);
|
|
val >>= 3;
|
|
} while (val);
|
|
if (octzero && *cp != '0')
|
|
*--cp = '0';
|
|
break;
|
|
|
|
case 16:
|
|
do {
|
|
*--cp = xdigs[val & 15];
|
|
val >>= 4;
|
|
} while (val);
|
|
break;
|
|
|
|
default: /* oops */
|
|
abort();
|
|
}
|
|
return (cp);
|
|
}
|
|
|
|
/* Identical to ib__ultoa, but for quads. */
|
|
static char *ib__uqtoa(u_quad_t val, char *endp,
|
|
int base, int octzero, char *xdigs)
|
|
{
|
|
char *cp = endp;
|
|
quad_t sval;
|
|
|
|
/* quick test for small values; ib__ultoa is typically much faster */
|
|
/* (perhaps instead we should run until small, then call ib__ultoa?) */
|
|
if (val <= ULONG_MAX)
|
|
return (ib__ultoa((u_long) val, endp, base, octzero, xdigs));
|
|
switch (base) {
|
|
case 10:
|
|
if (val < 10) {
|
|
*--cp = to_char(val % 10);
|
|
return (cp);
|
|
}
|
|
if (val > QUAD_MAX) {
|
|
*--cp = to_char(val % 10);
|
|
sval = val / 10;
|
|
}
|
|
else
|
|
sval = val;
|
|
do {
|
|
*--cp = to_char(sval % 10);
|
|
sval /= 10;
|
|
} while (sval != 0);
|
|
break;
|
|
|
|
case 8:
|
|
do {
|
|
*--cp = to_char(val & 7);
|
|
val >>= 3;
|
|
} while (val);
|
|
if (octzero && *cp != '0')
|
|
*--cp = '0';
|
|
break;
|
|
|
|
case 16:
|
|
do {
|
|
*--cp = xdigs[val & 15];
|
|
val >>= 4;
|
|
} while (val);
|
|
break;
|
|
|
|
default:
|
|
abort();
|
|
}
|
|
return (cp);
|
|
}
|
|
|
|
|
|
#define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */
|
|
#define DEFPREC 6
|
|
|
|
static char *cvt(double, int, int, char *, int *, int, int *);
|
|
static int exponent(char *, int, int);
|
|
|
|
#ifdef SOLARIS
|
|
|
|
/* On Solaris 2.6 or 7, "man isinf" will tell you to include <sunmath.h>
|
|
* and link with -lsunmath. Unfortunatel, neither /usr/include/sunmath.h
|
|
* nor /usr/lib/libsunmath.* exists on Sparc Solaris 2.6 or 7 systems
|
|
* available to us, so we supply a substitute here.
|
|
*/
|
|
static int isinf(double x)
|
|
{
|
|
fpclass_t class_;
|
|
|
|
class_ = fpclass(x);
|
|
return (FP_NINF == class_) || (FP_PINF == class_);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
#define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */
|
|
|
|
/*
|
|
* Flags used during conversion.
|
|
*/
|
|
#define ALT 0x001 /* alternate form */
|
|
#define HEXPREFIX 0x002 /* add 0x or 0X prefix */
|
|
#define LADJUST 0x004 /* left adjustment */
|
|
#define LONGDBL 0x008 /* long double */
|
|
#define LONGINT 0x010 /* long integer */
|
|
#define QUADINT 0x020 /* quad integer */
|
|
#define SHORTINT 0x040 /* short integer */
|
|
#define ZEROPAD 0x080 /* zero (as opposed to blank) pad */
|
|
#define FPT 0x100 /* Floating point number */
|
|
int ib_vfprintf(IB_FILE * fp, const char *fmt0, va_list ap)
|
|
{
|
|
char *fmt; /* format string */
|
|
int ch; /* character from fmt */
|
|
int n, n2; /* handy integer (short term usage) */
|
|
char *cp; /* handy char pointer (short term usage) */
|
|
struct ib__siov *iovp; /* for PRINT macro */
|
|
int flags; /* flags as above */
|
|
int ret; /* return value accumulator */
|
|
int width; /* width from format (%8d), or 0 */
|
|
int prec; /* precision from format (%.3d), or -1 */
|
|
char sign; /* sign prefix (' ', '+', '-', or \0) */
|
|
char softsign; /* temporary negative sign for floats */
|
|
double _double; /* double precision arguments %[eEfgG] */
|
|
int expt; /* integer value of exponent */
|
|
int expsize; /* character count for expstr */
|
|
int ndig; /* actual number of digits returned by cvt */
|
|
char expstr[7]; /* buffer for exponent string */
|
|
u_long ulval; /* integer arguments %[diouxX] */
|
|
u_quad_t uqval; /* %q integers */
|
|
int base; /* base for [diouxX] conversion */
|
|
int dprec; /* a copy of prec if [diouxX], 0 otherwise */
|
|
int realsz; /* field size expanded by dprec, sign, etc */
|
|
int size; /* size of converted field or string */
|
|
int prsize; /* max size of printed field */
|
|
char *xdigs; /* digits for [xX] conversion */
|
|
#define NIOV 8
|
|
struct ib__suio uio; /* output information: summary */
|
|
struct ib__siov iov[NIOV]; /* ... and individual io vectors */
|
|
char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */
|
|
char ox[2]; /* space for 0x hex-prefix */
|
|
void **argtable; /* args, built due to positional arg */
|
|
void *statargtable[STATIC_ARG_TBL_SIZE];
|
|
int nextarg; /* 1-based argument index */
|
|
va_list orgap; /* original argument pointer */
|
|
|
|
/*
|
|
* Choose PADSIZE to trade efficiency vs. size. If larger printf
|
|
* fields occur frequently, increase PADSIZE and make the initialisers
|
|
* below longer.
|
|
*/
|
|
#define PADSIZE 16 /* pad chunk size */
|
|
static char blanks[PADSIZE] =
|
|
{ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
|
|
' ', ' ', ' '
|
|
};
|
|
static char zeroes[PADSIZE] =
|
|
{ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
|
|
'0', '0', '0'
|
|
};
|
|
|
|
/*
|
|
* BEWARE, these `goto error' on error, and PAD uses `n'.
|
|
*/
|
|
#define PRINT(ptr, len) { \
|
|
iovp->iov_base = (ptr); \
|
|
iovp->iov_len = (len); \
|
|
uio.uio_resid += (len); \
|
|
iovp++; \
|
|
if (++uio.uio_iovcnt >= NIOV) { \
|
|
if (ib__sprint(fp, &uio)) \
|
|
goto error; \
|
|
iovp = iov; \
|
|
} \
|
|
}
|
|
#define PAD(howmany, with) { \
|
|
if ((n = (howmany)) > 0) { \
|
|
while (n > PADSIZE) { \
|
|
PRINT(with, PADSIZE); \
|
|
n -= PADSIZE; \
|
|
} \
|
|
PRINT(with, n); \
|
|
} \
|
|
}
|
|
#define FLUSH() { \
|
|
if (uio.uio_resid && ib__sprint(fp, &uio)) \
|
|
goto error; \
|
|
uio.uio_iovcnt = 0; \
|
|
iovp = iov; \
|
|
}
|
|
|
|
/*
|
|
* Get the argument indexed by nextarg. If the argument table is
|
|
* built, use it to get the argument. If its not, get the next
|
|
* argument (and arguments must be gotten sequentially).
|
|
*/
|
|
#define GETARG(type) \
|
|
((argtable != NULL) ? *((type*)(argtable[nextarg++])) : \
|
|
(nextarg++, va_arg(ap, type)))
|
|
|
|
/*
|
|
* To extend shorts properly, we need both signed and unsigned
|
|
* argument extraction methods.
|
|
*/
|
|
#define SARG() \
|
|
(flags&LONGINT ? GETARG(long) : \
|
|
flags&SHORTINT ? (long)(short)GETARG(int) : \
|
|
(long)GETARG(int))
|
|
#define UARG() \
|
|
(flags&LONGINT ? GETARG(u_long) : \
|
|
flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \
|
|
(u_long)GETARG(u_int))
|
|
|
|
/*
|
|
* Get * arguments, including the form *nn$. Preserve the nextarg
|
|
* that the argument can be gotten once the type is determined.
|
|
*/
|
|
#define GETASTER(val) \
|
|
n2 = 0; \
|
|
cp = fmt; \
|
|
while (is_digit(*cp)) { \
|
|
n2 = 10 * n2 + to_digit(*cp); \
|
|
cp++; \
|
|
} \
|
|
if (*cp == '$') { \
|
|
int hold = nextarg; \
|
|
if (argtable == NULL) { \
|
|
argtable = statargtable; \
|
|
ib__find_arguments (fmt0, orgap, &argtable); \
|
|
} \
|
|
nextarg = n2; \
|
|
val = GETARG (int); \
|
|
nextarg = hold; \
|
|
fmt = ++cp; \
|
|
} else { \
|
|
val = GETARG (int); \
|
|
}
|
|
|
|
|
|
FLOCKFILE(fp);
|
|
/* sorry, ib_fprintf(read_only_file, "") returns EOF, not 0 */
|
|
if (cantwrite(fp)) {
|
|
FUNLOCKFILE(fp);
|
|
return (EOF);
|
|
}
|
|
|
|
/* optimise ib_fprintf(stderr) (and other unbuffered Unix files) */
|
|
if ((fp->_flags & (IB__SNBF | IB__SWR | IB__SRW)) == (IB__SNBF | IB__SWR)
|
|
&& fp->_file >= 0) {
|
|
FUNLOCKFILE(fp);
|
|
return (ib__sbprintf(fp, fmt0, ap));
|
|
}
|
|
|
|
fmt = (char *) fmt0;
|
|
argtable = NULL;
|
|
nextarg = 1;
|
|
orgap = ap;
|
|
uio.uio_iov = iovp = iov;
|
|
uio.uio_resid = 0;
|
|
uio.uio_iovcnt = 0;
|
|
ret = 0;
|
|
|
|
/*
|
|
* Scan the format for conversions (`%' character).
|
|
*/
|
|
for (;;) {
|
|
for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
|
|
/* void */ ;
|
|
if ((n = fmt - cp) != 0) {
|
|
if ((unsigned) ret + n > INT_MAX) {
|
|
ret = EOF;
|
|
goto error;
|
|
}
|
|
PRINT(cp, n);
|
|
ret += n;
|
|
}
|
|
if (ch == '\0')
|
|
goto done;
|
|
fmt++; /* skip over '%' */
|
|
|
|
flags = 0;
|
|
dprec = 0;
|
|
width = 0;
|
|
prec = -1;
|
|
sign = '\0';
|
|
|
|
rflag:ch = *fmt++;
|
|
reswitch:switch (ch) {
|
|
case ' ':
|
|
/*
|
|
* ``If the space and + flags both appear, the space
|
|
* flag will be ignored.''
|
|
* -- ANSI X3J11
|
|
*/
|
|
if (!sign)
|
|
sign = ' ';
|
|
goto rflag;
|
|
case '#':
|
|
flags |= ALT;
|
|
goto rflag;
|
|
case '*':
|
|
/*
|
|
* ``A negative field width argument is taken as a
|
|
* - flag followed by a positive field width.''
|
|
* -- ANSI X3J11
|
|
* They don't exclude field widths read from args.
|
|
*/
|
|
GETASTER(width);
|
|
if (width >= 0)
|
|
goto rflag;
|
|
width = -width;
|
|
/* FALLTHROUGH */
|
|
case '-':
|
|
flags |= LADJUST;
|
|
goto rflag;
|
|
case '+':
|
|
sign = '+';
|
|
goto rflag;
|
|
case '.':
|
|
if ((ch = *fmt++) == '*') {
|
|
GETASTER(n);
|
|
prec = n < 0 ? -1 : n;
|
|
goto rflag;
|
|
}
|
|
n = 0;
|
|
while (is_digit(ch)) {
|
|
n = 10 * n + to_digit(ch);
|
|
ch = *fmt++;
|
|
}
|
|
prec = n < 0 ? -1 : n;
|
|
goto reswitch;
|
|
case '0':
|
|
/*
|
|
* ``Note that 0 is taken as a flag, not as the
|
|
* beginning of a field width.''
|
|
* -- ANSI X3J11
|
|
*/
|
|
flags |= ZEROPAD;
|
|
goto rflag;
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
n = 0;
|
|
do {
|
|
n = 10 * n + to_digit(ch);
|
|
ch = *fmt++;
|
|
} while (is_digit(ch));
|
|
if (ch == '$') {
|
|
nextarg = n;
|
|
if (argtable == NULL) {
|
|
argtable = statargtable;
|
|
ib__find_arguments(fmt0, orgap, &argtable);
|
|
}
|
|
goto rflag;
|
|
}
|
|
width = n;
|
|
goto reswitch;
|
|
#ifdef FLOATING_POINT
|
|
case 'L':
|
|
flags |= LONGDBL;
|
|
goto rflag;
|
|
#endif
|
|
case 'h':
|
|
flags |= SHORTINT;
|
|
goto rflag;
|
|
case 'l':
|
|
if (flags & LONGINT)
|
|
flags |= QUADINT;
|
|
else
|
|
flags |= LONGINT;
|
|
goto rflag;
|
|
case 'q':
|
|
flags |= QUADINT;
|
|
goto rflag;
|
|
case 'c':
|
|
*(cp = buf) = GETARG(int);
|
|
size = 1;
|
|
sign = '\0';
|
|
break;
|
|
case 'D':
|
|
flags |= LONGINT;
|
|
/*FALLTHROUGH*/ case 'd':
|
|
case 'i':
|
|
if (flags & QUADINT) {
|
|
uqval = GETARG(quad_t);
|
|
if ((quad_t) uqval < 0) {
|
|
uqval = -uqval;
|
|
sign = '-';
|
|
}
|
|
}
|
|
else {
|
|
ulval = SARG();
|
|
if ((long) ulval < 0) {
|
|
ulval = -ulval;
|
|
sign = '-';
|
|
}
|
|
}
|
|
base = 10;
|
|
goto number;
|
|
case 'e':
|
|
case 'E':
|
|
case 'f':
|
|
goto fp_begin;
|
|
case 'g':
|
|
case 'G':
|
|
if (prec == 0)
|
|
prec = 1;
|
|
fp_begin:if (prec == -1)
|
|
prec = DEFPREC;
|
|
if (flags & LONGDBL)
|
|
/* XXX this loses precision. */
|
|
_double = (double) GETARG(long double);
|
|
else
|
|
_double = GETARG(double);
|
|
/* do this before tricky precision changes */
|
|
if (isinf(_double)) {
|
|
if (_double < 0)
|
|
sign = '-';
|
|
cp = "Inf";
|
|
size = 3;
|
|
break;
|
|
}
|
|
if (isnan(_double)) {
|
|
cp = "NaN";
|
|
size = 3;
|
|
break;
|
|
}
|
|
flags |= FPT;
|
|
cp = cvt(_double, prec, flags, &softsign, &expt, ch, &ndig);
|
|
if (ch == 'g' || ch == 'G') {
|
|
if (expt <= -4 || expt > prec)
|
|
ch = (ch == 'g') ? 'e' : 'E';
|
|
else
|
|
ch = 'g';
|
|
}
|
|
if (ch <= 'e') { /* 'e' or 'E' fmt */
|
|
--expt;
|
|
expsize = exponent(expstr, expt, ch);
|
|
size = expsize + ndig;
|
|
if (ndig > 1 || flags & ALT)
|
|
++size;
|
|
}
|
|
else if (ch == 'f') { /* f fmt */
|
|
if (expt > 0) {
|
|
size = expt;
|
|
if (prec || flags & ALT)
|
|
size += prec + 1;
|
|
}
|
|
else /* "0.X" */
|
|
size = prec + 2;
|
|
}
|
|
else if (expt >= ndig) { /* fixed g fmt */
|
|
size = expt;
|
|
if (flags & ALT)
|
|
++size;
|
|
}
|
|
else
|
|
size = ndig + (expt > 0 ? 1 : 2 - expt);
|
|
|
|
if (softsign)
|
|
sign = '-';
|
|
break;
|
|
|
|
case 'n':
|
|
if (flags & QUADINT)
|
|
*GETARG(quad_t *) = ret;
|
|
else if (flags & LONGINT)
|
|
*GETARG(long *) = ret;
|
|
else if (flags & SHORTINT)
|
|
*GETARG(short *) = ret;
|
|
else
|
|
*GETARG(int *) = ret;
|
|
continue; /* no output */
|
|
case 'O':
|
|
flags |= LONGINT;
|
|
/*FALLTHROUGH*/ case 'o':
|
|
if (flags & QUADINT)
|
|
uqval = GETARG(u_quad_t);
|
|
else
|
|
ulval = UARG();
|
|
base = 8;
|
|
goto nosign;
|
|
case 'p':
|
|
/*
|
|
* ``The argument shall be a pointer to void. The
|
|
* value of the pointer is converted to a sequence
|
|
* of printable characters, in an implementation-
|
|
* defined manner.''
|
|
* -- ANSI X3J11
|
|
*/
|
|
ulval = (u_long) GETARG(void *);
|
|
base = 16;
|
|
xdigs = "0123456789abcdef";
|
|
flags = (flags & ~QUADINT) | HEXPREFIX;
|
|
ch = 'x';
|
|
goto nosign;
|
|
case 's':
|
|
if ((cp = GETARG(char *)) == NULL)
|
|
cp = "(null)";
|
|
if (prec >= 0) {
|
|
/*
|
|
* can't use strlen; can only look for the
|
|
* NUL in the first `prec' characters, and
|
|
* strlen() will go further.
|
|
*/
|
|
char *p = memchr(cp, 0, (size_t) prec);
|
|
|
|
if (p != NULL) {
|
|
size = p - cp;
|
|
if (size > prec)
|
|
size = prec;
|
|
}
|
|
else
|
|
size = prec;
|
|
}
|
|
else
|
|
size = strlen(cp);
|
|
sign = '\0';
|
|
break;
|
|
case 'U':
|
|
flags |= LONGINT;
|
|
/*FALLTHROUGH*/ case 'u':
|
|
if (flags & QUADINT)
|
|
uqval = GETARG(u_quad_t);
|
|
else
|
|
ulval = UARG();
|
|
base = 10;
|
|
goto nosign;
|
|
case 'X':
|
|
xdigs = "0123456789ABCDEF";
|
|
goto hex;
|
|
case 'x':
|
|
xdigs = "0123456789abcdef";
|
|
hex:if (flags & QUADINT)
|
|
uqval = GETARG(u_quad_t);
|
|
else
|
|
ulval = UARG();
|
|
base = 16;
|
|
/* leading 0x/X only if non-zero */
|
|
if (flags & ALT && (flags & QUADINT ? uqval != 0 : ulval != 0))
|
|
flags |= HEXPREFIX;
|
|
|
|
/* unsigned conversions */
|
|
nosign:sign = '\0';
|
|
/*
|
|
* ``... diouXx conversions ... if a precision is
|
|
* specified, the 0 flag will be ignored.''
|
|
* -- ANSI X3J11
|
|
*/
|
|
number:if ((dprec = prec) >= 0)
|
|
flags &= ~ZEROPAD;
|
|
|
|
/*
|
|
* ``The result of converting a zero value with an
|
|
* explicit precision of zero is no characters.''
|
|
* -- ANSI X3J11
|
|
*/
|
|
cp = buf + BUF;
|
|
if (flags & QUADINT) {
|
|
if (uqval != 0 || prec != 0)
|
|
cp = ib__uqtoa(uqval, cp, base, flags & ALT, xdigs);
|
|
}
|
|
else {
|
|
if (ulval != 0 || prec != 0)
|
|
cp = ib__ultoa(ulval, cp, base, flags & ALT, xdigs);
|
|
}
|
|
size = buf + BUF - cp;
|
|
break;
|
|
default: /* "%?" prints ?, unless ? is NUL */
|
|
if (ch == '\0')
|
|
goto done;
|
|
/* pretend it was %c with argument ch */
|
|
cp = buf;
|
|
*cp = ch;
|
|
size = 1;
|
|
sign = '\0';
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* All reasonable formats wind up here. At this point, `cp'
|
|
* points to a string which (if not flags&LADJUST) should be
|
|
* padded out to `width' places. If flags&ZEROPAD, it should
|
|
* first be prefixed by any sign or other prefix; otherwise,
|
|
* it should be blank padded before the prefix is emitted.
|
|
* After any left-hand padding and prefixing, emit zeroes
|
|
* required by a decimal [diouxX] precision, then print the
|
|
* string proper, then emit zeroes required by any leftover
|
|
* floating precision; finally, if LADJUST, pad with blanks.
|
|
*
|
|
* Compute actual size, so we know how much to pad.
|
|
* size excludes decimal prec; realsz includes it.
|
|
*/
|
|
realsz = dprec > size ? dprec : size;
|
|
if (sign)
|
|
realsz++;
|
|
else if (flags & HEXPREFIX)
|
|
realsz += 2;
|
|
|
|
prsize = width > realsz ? width : realsz;
|
|
if ((unsigned) ret + prsize > INT_MAX) {
|
|
ret = EOF;
|
|
goto error;
|
|
}
|
|
|
|
/* right-adjusting blank padding */
|
|
if ((flags & (LADJUST | ZEROPAD)) == 0)
|
|
PAD(width - realsz, blanks);
|
|
|
|
/* prefix */
|
|
if (sign) {
|
|
PRINT(&sign, 1);
|
|
}
|
|
else if (flags & HEXPREFIX) {
|
|
ox[0] = '0';
|
|
ox[1] = ch;
|
|
PRINT(ox, 2);
|
|
}
|
|
|
|
/* right-adjusting zero padding */
|
|
if ((flags & (LADJUST | ZEROPAD)) == ZEROPAD)
|
|
PAD(width - realsz, zeroes);
|
|
|
|
/* leading zeroes from decimal precision */
|
|
PAD(dprec - size, zeroes);
|
|
|
|
/* the string or number proper */
|
|
|
|
if ((flags & FPT) == 0) {
|
|
PRINT(cp, size);
|
|
}
|
|
else { /* glue together f_p fragments */
|
|
if (ch >= 'f') { /* 'f' or 'g' */
|
|
if (_double == 0) {
|
|
/* kludge for ib__dtoa irregularity */
|
|
if (expt >= ndig && (flags & ALT) == 0) {
|
|
PRINT("0", 1);
|
|
}
|
|
else {
|
|
PRINT("0.", 2);
|
|
PAD(ndig - 1, zeroes);
|
|
}
|
|
}
|
|
else if (expt <= 0) {
|
|
PRINT("0.", 2);
|
|
PAD(-expt, zeroes);
|
|
PRINT(cp, ndig);
|
|
}
|
|
else if (expt >= ndig) {
|
|
PRINT(cp, ndig);
|
|
PAD(expt - ndig, zeroes);
|
|
if (flags & ALT)
|
|
PRINT(".", 1);
|
|
}
|
|
else {
|
|
PRINT(cp, expt);
|
|
cp += expt;
|
|
PRINT(".", 1);
|
|
PRINT(cp, ndig - expt);
|
|
}
|
|
}
|
|
else { /* 'e' or 'E' */
|
|
if (ndig > 1 || flags & ALT) {
|
|
ox[0] = *cp++;
|
|
ox[1] = '.';
|
|
PRINT(ox, 2);
|
|
if (_double) {
|
|
PRINT(cp, ndig - 1);
|
|
}
|
|
else /* 0.[0..] */
|
|
/* ib__dtoa irregularity */
|
|
PAD(ndig - 1, zeroes);
|
|
}
|
|
else /* XeYYY */
|
|
PRINT(cp, 1);
|
|
PRINT(expstr, expsize);
|
|
}
|
|
}
|
|
|
|
/* left-adjusting padding (always blank) */
|
|
if (flags & LADJUST)
|
|
PAD(width - realsz, blanks);
|
|
|
|
/* finally, adjust ret */
|
|
ret += prsize;
|
|
|
|
FLUSH(); /* copy out the I/O vectors */
|
|
}
|
|
done:
|
|
FLUSH();
|
|
error:
|
|
if (ib__sferror(fp))
|
|
ret = EOF;
|
|
FUNLOCKFILE(fp);
|
|
if ((argtable != NULL) && (argtable != statargtable))
|
|
free(argtable);
|
|
return (ret);
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
/*
|
|
* Type ids for argument type table.
|
|
*/
|
|
#define T_UNUSED 0
|
|
#define T_SHORT 1
|
|
#define T_U_SHORT 2
|
|
#define TP_SHORT 3
|
|
#define T_INT 4
|
|
#define T_U_INT 5
|
|
#define TP_INT 6
|
|
#define T_LONG 7
|
|
#define T_U_LONG 8
|
|
#define TP_LONG 9
|
|
#define T_QUAD 10
|
|
#define T_U_QUAD 11
|
|
#define TP_QUAD 12
|
|
#define T_DOUBLE 13
|
|
#define T_LONG_DOUBLE 14
|
|
#define TP_CHAR 15
|
|
#define TP_VOID 16
|
|
|
|
/*
|
|
* Find all arguments when a positional parameter is encountered. Returns a
|
|
* table, indexed by argument number, of pointers to each arguments. The
|
|
* initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
|
|
* It will be replaces with a malloc-ed on if it overflows.
|
|
*/
|
|
static void ib__find_arguments(const char *fmt0, va_list ap, void ***argtable)
|
|
{
|
|
char *fmt; /* format string */
|
|
int ch; /* character from fmt */
|
|
int n, n2; /* handy integer (short term usage) */
|
|
char *cp; /* handy char pointer (short term usage) */
|
|
int flags; /* flags as above */
|
|
int width; /* width from format (%8d), or 0 */
|
|
unsigned char *typetable; /* table of types */
|
|
unsigned char stattypetable[STATIC_ARG_TBL_SIZE];
|
|
int tablesize; /* current size of type table */
|
|
int tablemax; /* largest used index in table */
|
|
int nextarg; /* 1-based argument index */
|
|
|
|
/*
|
|
* Add an argument type to the table, expanding if necessary.
|
|
*/
|
|
/* Solaris C was complaining that the two sides of the next ":"
|
|
* operator have different types, so we cast the 0 to void,
|
|
* so it will match the void return type of the function on the left.
|
|
*/
|
|
/* ib__grow_type_table(nextarg, &typetable, &tablesize) : 0, */
|
|
#define ADDTYPE(type) \
|
|
((nextarg >= tablesize) ? \
|
|
ib__grow_type_table(nextarg, &typetable, &tablesize) : \
|
|
(void) 0, \
|
|
typetable[nextarg++] = type, \
|
|
(nextarg > tablemax) ? tablemax = nextarg : 0)
|
|
|
|
#define ADDSARG() \
|
|
((flags&LONGINT) ? ADDTYPE(T_LONG) : \
|
|
((flags&SHORTINT) ? ADDTYPE(T_SHORT) : ADDTYPE(T_INT)))
|
|
|
|
#define ADDUARG() \
|
|
((flags&LONGINT) ? ADDTYPE(T_U_LONG) : \
|
|
((flags&SHORTINT) ? ADDTYPE(T_U_SHORT) : ADDTYPE(T_U_INT)))
|
|
|
|
/*
|
|
* Add * arguments to the type array.
|
|
*/
|
|
#define ADDASTER() \
|
|
n2 = 0; \
|
|
cp = fmt; \
|
|
while (is_digit(*cp)) { \
|
|
n2 = 10 * n2 + to_digit(*cp); \
|
|
cp++; \
|
|
} \
|
|
if (*cp == '$') { \
|
|
int hold = nextarg; \
|
|
nextarg = n2; \
|
|
ADDTYPE (T_INT); \
|
|
nextarg = hold; \
|
|
fmt = ++cp; \
|
|
} else { \
|
|
ADDTYPE (T_INT); \
|
|
}
|
|
fmt = (char *) fmt0;
|
|
typetable = stattypetable;
|
|
tablesize = STATIC_ARG_TBL_SIZE;
|
|
tablemax = 0;
|
|
nextarg = 1;
|
|
memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
|
|
|
|
/*
|
|
* Scan the format for conversions (`%' character).
|
|
*/
|
|
for (;;) {
|
|
for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
|
|
/* void */ ;
|
|
if (ch == '\0')
|
|
goto done;
|
|
fmt++; /* skip over '%' */
|
|
|
|
flags = 0;
|
|
width = 0;
|
|
|
|
rflag:ch = *fmt++;
|
|
reswitch:switch (ch) {
|
|
case ' ':
|
|
case '#':
|
|
goto rflag;
|
|
case '*':
|
|
/* ADDASTER (); */
|
|
goto rflag;
|
|
case '-':
|
|
case '+':
|
|
goto rflag;
|
|
case '.':
|
|
if ((ch = *fmt++) == '*') {
|
|
ADDASTER();
|
|
goto rflag;
|
|
}
|
|
while (is_digit(ch)) {
|
|
ch = *fmt++;
|
|
}
|
|
goto reswitch;
|
|
case '0':
|
|
goto rflag;
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
n = 0;
|
|
do {
|
|
n = 10 * n + to_digit(ch);
|
|
ch = *fmt++;
|
|
} while (is_digit(ch));
|
|
if (ch == '$') {
|
|
nextarg = n;
|
|
goto rflag;
|
|
}
|
|
width = n;
|
|
goto reswitch;
|
|
#ifdef FLOATING_POINT
|
|
case 'L':
|
|
flags |= LONGDBL;
|
|
goto rflag;
|
|
#endif
|
|
case 'h':
|
|
flags |= SHORTINT;
|
|
goto rflag;
|
|
case 'l':
|
|
if (flags & LONGINT)
|
|
flags |= QUADINT;
|
|
else
|
|
flags |= LONGINT;
|
|
goto rflag;
|
|
case 'q':
|
|
flags |= QUADINT;
|
|
goto rflag;
|
|
case 'c':
|
|
ADDTYPE(T_INT);
|
|
break;
|
|
case 'D':
|
|
flags |= LONGINT;
|
|
/*FALLTHROUGH*/ case 'd':
|
|
case 'i':
|
|
if (flags & QUADINT) {
|
|
ADDTYPE(T_QUAD);
|
|
}
|
|
else {
|
|
ADDSARG();
|
|
}
|
|
break;
|
|
#ifdef FLOATING_POINT
|
|
case 'e':
|
|
case 'E':
|
|
case 'f':
|
|
case 'g':
|
|
case 'G':
|
|
if (flags & LONGDBL)
|
|
ADDTYPE(T_LONG_DOUBLE);
|
|
else
|
|
ADDTYPE(T_DOUBLE);
|
|
break;
|
|
#endif /* FLOATING_POINT */
|
|
case 'n':
|
|
if (flags & QUADINT)
|
|
ADDTYPE(TP_QUAD);
|
|
else if (flags & LONGINT)
|
|
ADDTYPE(TP_LONG);
|
|
else if (flags & SHORTINT)
|
|
ADDTYPE(TP_SHORT);
|
|
else
|
|
ADDTYPE(TP_INT);
|
|
continue; /* no output */
|
|
case 'O':
|
|
flags |= LONGINT;
|
|
/*FALLTHROUGH*/ case 'o':
|
|
if (flags & QUADINT)
|
|
ADDTYPE(T_U_QUAD);
|
|
else
|
|
ADDUARG();
|
|
break;
|
|
case 'p':
|
|
ADDTYPE(TP_VOID);
|
|
break;
|
|
case 's':
|
|
ADDTYPE(TP_CHAR);
|
|
break;
|
|
case 'U':
|
|
flags |= LONGINT;
|
|
/*FALLTHROUGH*/ case 'u':
|
|
if (flags & QUADINT)
|
|
ADDTYPE(T_U_QUAD);
|
|
else
|
|
ADDUARG();
|
|
break;
|
|
case 'X':
|
|
case 'x':
|
|
if (flags & QUADINT)
|
|
ADDTYPE(T_U_QUAD);
|
|
else
|
|
ADDUARG();
|
|
break;
|
|
default: /* "%?" prints ?, unless ? is NUL */
|
|
if (ch == '\0')
|
|
goto done;
|
|
break;
|
|
}
|
|
}
|
|
done:
|
|
/*
|
|
* Build the argument table.
|
|
*/
|
|
if (tablemax >= STATIC_ARG_TBL_SIZE) {
|
|
*argtable = (void **)
|
|
malloc(sizeof(void *) * (tablemax + 1));
|
|
}
|
|
|
|
(*argtable)[0] = NULL;
|
|
for (n = 1; n <= tablemax; n++) {
|
|
switch (typetable[n]) {
|
|
case T_UNUSED:
|
|
(*argtable)[n] = (void *) &va_arg(ap, int);
|
|
break;
|
|
case T_SHORT:
|
|
(*argtable)[n] = (void *) &va_arg(ap, int);
|
|
break;
|
|
case T_U_SHORT:
|
|
(*argtable)[n] = (void *) &va_arg(ap, int);
|
|
break;
|
|
case TP_SHORT:
|
|
(*argtable)[n] = (void *) &va_arg(ap, short *);
|
|
break;
|
|
case T_INT:
|
|
(*argtable)[n] = (void *) &va_arg(ap, int);
|
|
break;
|
|
case T_U_INT:
|
|
(*argtable)[n] = (void *) &va_arg(ap, unsigned int);
|
|
break;
|
|
case TP_INT:
|
|
(*argtable)[n] = (void *) &va_arg(ap, int *);
|
|
break;
|
|
case T_LONG:
|
|
(*argtable)[n] = (void *) &va_arg(ap, long);
|
|
break;
|
|
case T_U_LONG:
|
|
(*argtable)[n] = (void *) &va_arg(ap, unsigned long);
|
|
break;
|
|
case TP_LONG:
|
|
(*argtable)[n] = (void *) &va_arg(ap, long *);
|
|
break;
|
|
case T_QUAD:
|
|
(*argtable)[n] = (void *) &va_arg(ap, quad_t);
|
|
break;
|
|
case T_U_QUAD:
|
|
(*argtable)[n] = (void *) &va_arg(ap, u_quad_t);
|
|
break;
|
|
case TP_QUAD:
|
|
(*argtable)[n] = (void *) &va_arg(ap, quad_t *);
|
|
break;
|
|
case T_DOUBLE:
|
|
(*argtable)[n] = (void *) &va_arg(ap, double);
|
|
break;
|
|
case T_LONG_DOUBLE:
|
|
(*argtable)[n] = (void *) &va_arg(ap, long double);
|
|
break;
|
|
case TP_CHAR:
|
|
(*argtable)[n] = (void *) &va_arg(ap, char *);
|
|
break;
|
|
case TP_VOID:
|
|
(*argtable)[n] = (void *) &va_arg(ap, void *);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((typetable != NULL) && (typetable != stattypetable))
|
|
free(typetable);
|
|
}
|
|
|
|
/*
|
|
* Increase the size of the type table.
|
|
*/
|
|
static void
|
|
ib__grow_type_table(int nextarg, unsigned char **typetable, int *tablesize)
|
|
{
|
|
unsigned char *oldtable = *typetable;
|
|
int newsize = *tablesize * 2;
|
|
|
|
if (*tablesize == STATIC_ARG_TBL_SIZE) {
|
|
*typetable = (unsigned char *)
|
|
malloc(sizeof(unsigned char) * newsize);
|
|
bcopy(oldtable, *typetable, *tablesize);
|
|
}
|
|
else {
|
|
*typetable = (unsigned char *)
|
|
reallocf(typetable, sizeof(unsigned char) * newsize);
|
|
|
|
}
|
|
memset(&typetable[*tablesize], T_UNUSED, (newsize - *tablesize));
|
|
|
|
*tablesize = newsize;
|
|
}
|
|
|
|
|
|
static char *cvt(double value, int ndigits, int flags, char *sign,
|
|
int *decpt, int ch, int *length)
|
|
{
|
|
int mode, dsgn;
|
|
char *digits, *bp, *rve;
|
|
|
|
if (ch == 'f')
|
|
mode = 3; /* ndigits after the decimal point */
|
|
else {
|
|
/*
|
|
* To obtain ndigits after the decimal point for the 'e'
|
|
* and 'E' formats, round to ndigits + 1 significant
|
|
* figures.
|
|
*/
|
|
if (ch == 'e' || ch == 'E')
|
|
ndigits++;
|
|
mode = 2; /* ndigits significant digits */
|
|
}
|
|
if (value < 0) {
|
|
value = -value;
|
|
*sign = '-';
|
|
}
|
|
else
|
|
*sign = '\000';
|
|
digits = ib__dtoa(value, mode, ndigits, decpt, &dsgn, &rve);
|
|
if ((ch != 'g' && ch != 'G') || flags & ALT) {
|
|
/* print trailing zeros */
|
|
bp = digits + ndigits;
|
|
if (ch == 'f') {
|
|
if (*digits == '0' && value)
|
|
*decpt = -ndigits + 1;
|
|
bp += *decpt;
|
|
}
|
|
if (value == 0) /* kludge for ib__dtoa irregularity */
|
|
rve = bp;
|
|
while (rve < bp)
|
|
*rve++ = '0';
|
|
}
|
|
*length = rve - digits;
|
|
return (digits);
|
|
}
|
|
|
|
static int exponent(char *p0, int exp, int fmtch)
|
|
{
|
|
char *p, *t;
|
|
char expbuf[MAXEXP];
|
|
|
|
p = p0;
|
|
*p++ = fmtch;
|
|
if (exp < 0) {
|
|
exp = -exp;
|
|
*p++ = '-';
|
|
}
|
|
else
|
|
*p++ = '+';
|
|
t = expbuf + MAXEXP;
|
|
if (exp > 9) {
|
|
do {
|
|
*--t = to_char(exp % 10);
|
|
} while ((exp /= 10) > 9);
|
|
*--t = to_char(exp);
|
|
for (; t < expbuf + MAXEXP; *p++ = *t++);
|
|
}
|
|
else {
|
|
*p++ = '0';
|
|
*p++ = to_char(exp);
|
|
}
|
|
return (p - p0);
|
|
}
|
|
|
|
|
|
#define BUF 513 /* Maximum length of numeric string. */
|
|
|
|
/*
|
|
* Flags used during conversion.
|
|
*/
|
|
#define LONG 0x01 /* l: long or double */
|
|
#define LONGDBL 0x02 /* L: long double */
|
|
#define SHORT 0x04 /* h: short */
|
|
#define SUPPRESS 0x08 /* suppress assignment */
|
|
#define POINTER 0x10 /* weird %p pointer (`fake hex') */
|
|
#define NOSKIP 0x20 /* do not skip blanks */
|
|
#define QUAD 0x400
|
|
|
|
/*
|
|
* The following are used in numeric conversions only:
|
|
* SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point;
|
|
* SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral.
|
|
*/
|
|
#define SIGNOK 0x40 /* +/- is (still) legal */
|
|
#define NDIGITS 0x80 /* no digits detected */
|
|
|
|
#define DPTOK 0x100 /* (float) decimal point is still legal */
|
|
#define EXPOK 0x200 /* (float) exponent (e+3, etc) still legal */
|
|
|
|
#define PFXOK 0x100 /* 0x prefix is (still) legal */
|
|
#define NZDIGITS 0x200 /* no zero digits detected */
|
|
|
|
/*
|
|
* Conversion types.
|
|
*/
|
|
#define CT_CHAR 0 /* %c conversion */
|
|
#define CT_CCL 1 /* %[...] conversion */
|
|
#define CT_STRING 2 /* %s conversion */
|
|
#define CT_INT 3 /* integer, i.e., strtoq or strtouq */
|
|
#define CT_FLOAT 4 /* floating, i.e., strtod */
|
|
|
|
#define u_char unsigned char
|
|
#define u_long unsigned long
|
|
|
|
static u_char *ib__sccl(char *, u_char *);
|
|
|
|
/*
|
|
* vfscanf
|
|
*/
|
|
int ib__svfscanf(IB_FILE * fp, char const *fmt0, va_list ap)
|
|
{
|
|
u_char *fmt = (u_char *) fmt0;
|
|
int c; /* character from format, or conversion */
|
|
size_t width; /* field width, or 0 */
|
|
char *p; /* points into all kinds of strings */
|
|
int n; /* handy integer */
|
|
int flags; /* flags as defined above */
|
|
char *p0; /* saves original value of p when necessary */
|
|
int nassigned; /* number of fields assigned */
|
|
int nconversions; /* number of conversions */
|
|
int nread; /* number of characters consumed from fp */
|
|
int base; /* base argument to strtoq/strtouq */
|
|
u_quad_t(*ccfn) (); /* conversion function (strtoq/strtouq) */
|
|
char ccltab[256]; /* character class table for %[...] */
|
|
char buf[BUF]; /* buffer for numeric conversions */
|
|
|
|
/* `basefix' is used to avoid `if' tests in the integer scanner */
|
|
static short basefix[17] =
|
|
{ 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
|
|
|
|
nassigned = 0;
|
|
nconversions = 0;
|
|
nread = 0;
|
|
base = 0; /* XXX just to keep gcc happy */
|
|
ccfn = NULL; /* XXX just to keep gcc happy */
|
|
for (;;) {
|
|
c = *fmt++;
|
|
if (c == 0)
|
|
return (nassigned);
|
|
if (isspace(c)) {
|
|
while ((fp->_r > 0 || ib__srefill(fp) == 0) && isspace(*fp->_p))
|
|
nread++, fp->_r--, fp->_p++;
|
|
continue;
|
|
}
|
|
if (c != '%')
|
|
goto literal;
|
|
width = 0;
|
|
flags = 0;
|
|
/*
|
|
* switch on the format. continue if done;
|
|
* break once format type is derived.
|
|
*/
|
|
again:c = *fmt++;
|
|
switch (c) {
|
|
case '%':
|
|
literal:
|
|
if (fp->_r <= 0 && ib__srefill(fp))
|
|
goto input_failure;
|
|
if (*fp->_p != c)
|
|
goto match_failure;
|
|
fp->_r--, fp->_p++;
|
|
nread++;
|
|
continue;
|
|
|
|
case '*':
|
|
flags |= SUPPRESS;
|
|
goto again;
|
|
case 'l':
|
|
flags |= LONG;
|
|
goto again;
|
|
case 'q':
|
|
flags |= QUAD;
|
|
goto again;
|
|
case 'L':
|
|
flags |= LONGDBL;
|
|
goto again;
|
|
case 'h':
|
|
flags |= SHORT;
|
|
goto again;
|
|
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
width = width * 10 + c - '0';
|
|
goto again;
|
|
|
|
/*
|
|
* Conversions.
|
|
* Those marked `compat' are for 4.[123]BSD compatibility.
|
|
*
|
|
* (According to ANSI, E and X formats are supposed
|
|
* to the same as e and x. Sorry about that.)
|
|
*/
|
|
case 'D': /* compat */
|
|
flags |= LONG;
|
|
/* FALLTHROUGH */
|
|
case 'd':
|
|
c = CT_INT;
|
|
ccfn = (u_quad_t(*)())strtoq;
|
|
base = 10;
|
|
break;
|
|
|
|
case 'i':
|
|
c = CT_INT;
|
|
ccfn = (u_quad_t(*)())strtoq;
|
|
base = 0;
|
|
break;
|
|
|
|
case 'O': /* compat */
|
|
flags |= LONG;
|
|
/* FALLTHROUGH */
|
|
case 'o':
|
|
c = CT_INT;
|
|
ccfn = strtouq;
|
|
base = 8;
|
|
break;
|
|
|
|
case 'u':
|
|
c = CT_INT;
|
|
ccfn = strtouq;
|
|
base = 10;
|
|
break;
|
|
|
|
case 'X': /* compat XXX */
|
|
flags |= LONG;
|
|
/* FALLTHROUGH */
|
|
case 'x':
|
|
flags |= PFXOK; /* enable 0x prefixing */
|
|
c = CT_INT;
|
|
ccfn = strtouq;
|
|
base = 16;
|
|
break;
|
|
|
|
case 'E': /* compat XXX */
|
|
case 'F': /* compat */
|
|
flags |= LONG;
|
|
/* FALLTHROUGH */
|
|
case 'e':
|
|
case 'f':
|
|
case 'g':
|
|
c = CT_FLOAT;
|
|
break;
|
|
|
|
case 's':
|
|
c = CT_STRING;
|
|
break;
|
|
|
|
case '[':
|
|
fmt = ib__sccl(ccltab, fmt);
|
|
flags |= NOSKIP;
|
|
c = CT_CCL;
|
|
break;
|
|
|
|
case 'c':
|
|
flags |= NOSKIP;
|
|
c = CT_CHAR;
|
|
break;
|
|
|
|
case 'p': /* pointer format is like hex */
|
|
flags |= POINTER | PFXOK;
|
|
c = CT_INT;
|
|
ccfn = strtouq;
|
|
base = 16;
|
|
break;
|
|
|
|
case 'n':
|
|
nconversions++;
|
|
if (flags & SUPPRESS) /* ??? */
|
|
continue;
|
|
if (flags & SHORT)
|
|
*va_arg(ap, short *) = nread;
|
|
else if (flags & LONG)
|
|
*va_arg(ap, long *) = nread;
|
|
else if (flags & QUAD)
|
|
*va_arg(ap, quad_t *) = nread;
|
|
else
|
|
*va_arg(ap, int *) = nread;
|
|
continue;
|
|
|
|
/*
|
|
* Disgusting backwards compatibility hacks. XXX
|
|
*/
|
|
case '\0': /* compat */
|
|
return (EOF);
|
|
|
|
default: /* compat */
|
|
if (isupper(c))
|
|
flags |= LONG;
|
|
c = CT_INT;
|
|
ccfn = (u_quad_t(*)())strtoq;
|
|
base = 10;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* We have a conversion that requires input.
|
|
*/
|
|
if (fp->_r <= 0 && ib__srefill(fp))
|
|
goto input_failure;
|
|
|
|
/*
|
|
* Consume leading white space, except for formats
|
|
* that suppress this.
|
|
*/
|
|
if ((flags & NOSKIP) == 0) {
|
|
while (isspace(*fp->_p)) {
|
|
nread++;
|
|
if (--fp->_r > 0)
|
|
fp->_p++;
|
|
else if (ib__srefill(fp))
|
|
goto input_failure;
|
|
}
|
|
/*
|
|
* Note that there is at least one character in
|
|
* the buffer, so conversions that do not set NOSKIP
|
|
* ca no longer result in an input failure.
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* Do the conversion.
|
|
*/
|
|
switch (c) {
|
|
|
|
case CT_CHAR:
|
|
/* scan arbitrary characters (sets NOSKIP) */
|
|
if (width == 0)
|
|
width = 1;
|
|
if (flags & SUPPRESS) {
|
|
size_t sum = 0;
|
|
for (;;) {
|
|
if ((n = fp->_r) < width) {
|
|
sum += n;
|
|
width -= n;
|
|
fp->_p += n;
|
|
if (ib__srefill(fp)) {
|
|
if (sum == 0)
|
|
goto input_failure;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
sum += width;
|
|
fp->_r -= width;
|
|
fp->_p += width;
|
|
break;
|
|
}
|
|
}
|
|
nread += sum;
|
|
}
|
|
else {
|
|
size_t r = ib_fread((void *) va_arg(ap, char *), 1,
|
|
width, fp);
|
|
|
|
if (r == 0)
|
|
goto input_failure;
|
|
nread += r;
|
|
nassigned++;
|
|
}
|
|
nconversions++;
|
|
break;
|
|
|
|
case CT_CCL:
|
|
/* scan a (nonempty) character class (sets NOSKIP) */
|
|
if (width == 0)
|
|
width = (size_t) ~ 0; /* `infinity' */
|
|
/* take only those things in the class */
|
|
if (flags & SUPPRESS) {
|
|
n = 0;
|
|
while (ccltab[*fp->_p]) {
|
|
n++, fp->_r--, fp->_p++;
|
|
if (--width == 0)
|
|
break;
|
|
if (fp->_r <= 0 && ib__srefill(fp)) {
|
|
if (n == 0)
|
|
goto input_failure;
|
|
break;
|
|
}
|
|
}
|
|
if (n == 0)
|
|
goto match_failure;
|
|
}
|
|
else {
|
|
p0 = p = va_arg(ap, char *);
|
|
while (ccltab[*fp->_p]) {
|
|
fp->_r--;
|
|
*p++ = *fp->_p++;
|
|
if (--width == 0)
|
|
break;
|
|
if (fp->_r <= 0 && ib__srefill(fp)) {
|
|
if (p == p0)
|
|
goto input_failure;
|
|
break;
|
|
}
|
|
}
|
|
n = p - p0;
|
|
if (n == 0)
|
|
goto match_failure;
|
|
*p = 0;
|
|
nassigned++;
|
|
}
|
|
nread += n;
|
|
nconversions++;
|
|
break;
|
|
|
|
case CT_STRING:
|
|
/* like CCL, but zero-length string OK, & no NOSKIP */
|
|
if (width == 0)
|
|
width = (size_t) ~ 0;
|
|
if (flags & SUPPRESS) {
|
|
n = 0;
|
|
while (!isspace(*fp->_p)) {
|
|
n++, fp->_r--, fp->_p++;
|
|
if (--width == 0)
|
|
break;
|
|
if (fp->_r <= 0 && ib__srefill(fp))
|
|
break;
|
|
}
|
|
nread += n;
|
|
}
|
|
else {
|
|
p0 = p = va_arg(ap, char *);
|
|
while (!isspace(*fp->_p)) {
|
|
fp->_r--;
|
|
*p++ = *fp->_p++;
|
|
if (--width == 0)
|
|
break;
|
|
if (fp->_r <= 0 && ib__srefill(fp))
|
|
break;
|
|
}
|
|
*p = 0;
|
|
nread += p - p0;
|
|
nassigned++;
|
|
}
|
|
nconversions++;
|
|
continue;
|
|
|
|
case CT_INT:
|
|
/* scan an integer as if by strtoq/strtouq */
|
|
/* size_t is unsigned, hence this optimisation */
|
|
if (--width > sizeof(buf) - 2)
|
|
width = sizeof(buf) - 2;
|
|
width++;
|
|
flags |= SIGNOK | NDIGITS | NZDIGITS;
|
|
for (p = buf; width; width--) {
|
|
c = *fp->_p;
|
|
/*
|
|
* Switch on the character; `goto ok'
|
|
* if we accept it as a part of number.
|
|
*/
|
|
switch (c) {
|
|
|
|
/*
|
|
* The digit 0 is always legal, but is
|
|
* special. For %i conversions, if no
|
|
* digits (zero or nonzero) have been
|
|
* scanned (only signs), we will have
|
|
* base==0. In that case, we should set
|
|
* it to 8 and enable 0x prefixing.
|
|
* Also, if we have not scanned zero digits
|
|
* before this, do not turn off prefixing
|
|
* (someone else will turn it off if we
|
|
* have scanned any nonzero digits).
|
|
*/
|
|
case '0':
|
|
if (base == 0) {
|
|
base = 8;
|
|
flags |= PFXOK;
|
|
}
|
|
if (flags & NZDIGITS)
|
|
flags &= ~(SIGNOK | NZDIGITS | NDIGITS);
|
|
else
|
|
flags &= ~(SIGNOK | PFXOK | NDIGITS);
|
|
goto ok;
|
|
|
|
/* 1 through 7 always legal */
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
base = basefix[base];
|
|
flags &= ~(SIGNOK | PFXOK | NDIGITS);
|
|
goto ok;
|
|
|
|
/* digits 8 and 9 ok iff decimal or hex */
|
|
case '8':
|
|
case '9':
|
|
base = basefix[base];
|
|
if (base <= 8)
|
|
break; /* not legal here */
|
|
flags &= ~(SIGNOK | PFXOK | NDIGITS);
|
|
goto ok;
|
|
|
|
/* letters ok iff hex */
|
|
case 'A':
|
|
case 'B':
|
|
case 'C':
|
|
case 'D':
|
|
case 'E':
|
|
case 'F':
|
|
case 'a':
|
|
case 'b':
|
|
case 'c':
|
|
case 'd':
|
|
case 'e':
|
|
case 'f':
|
|
/* no need to fix base here */
|
|
if (base <= 10)
|
|
break; /* not legal here */
|
|
flags &= ~(SIGNOK | PFXOK | NDIGITS);
|
|
goto ok;
|
|
|
|
/* sign ok only as first character */
|
|
case '+':
|
|
case '-':
|
|
if (flags & SIGNOK) {
|
|
flags &= ~SIGNOK;
|
|
goto ok;
|
|
}
|
|
break;
|
|
|
|
/* x ok iff flag still set & 2nd char */
|
|
case 'x':
|
|
case 'X':
|
|
if (flags & PFXOK && p == buf + 1) {
|
|
base = 16; /* if %i */
|
|
flags &= ~PFXOK;
|
|
goto ok;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* If we got here, c is not a legal character
|
|
* for a number. Stop accumulating digits.
|
|
*/
|
|
break;
|
|
ok:
|
|
/*
|
|
* c is legal: store it and look at the next.
|
|
*/
|
|
*p++ = c;
|
|
if (--fp->_r > 0)
|
|
fp->_p++;
|
|
else if (ib__srefill(fp))
|
|
break; /* EOF */
|
|
}
|
|
/*
|
|
* If we had only a sign, it is no good; push
|
|
* back the sign. If the number ends in `x',
|
|
* it was [sign] '0' 'x', so push back the x
|
|
* and treat it as [sign] '0'.
|
|
*/
|
|
if (flags & NDIGITS) {
|
|
if (p > buf)
|
|
(void) ib_ungetc(*(u_char *)-- p, fp);
|
|
goto match_failure;
|
|
}
|
|
c = ((u_char *) p)[-1];
|
|
if (c == 'x' || c == 'X') {
|
|
--p;
|
|
(void) ib_ungetc(c, fp);
|
|
}
|
|
if ((flags & SUPPRESS) == 0) {
|
|
u_quad_t res;
|
|
|
|
*p = 0;
|
|
res = (*ccfn) (buf, (char **) NULL, base);
|
|
if (flags & POINTER)
|
|
*va_arg(ap, void **) = (void *) (u_long) res;
|
|
else if (flags & SHORT)
|
|
*va_arg(ap, short *) = res;
|
|
else if (flags & LONG)
|
|
*va_arg(ap, long *) = res;
|
|
else if (flags & QUAD)
|
|
*va_arg(ap, quad_t *) = res;
|
|
else
|
|
*va_arg(ap, int *) = res;
|
|
nassigned++;
|
|
}
|
|
nread += p - buf;
|
|
nconversions++;
|
|
break;
|
|
|
|
case CT_FLOAT:
|
|
/* scan a floating point number as if by strtod */
|
|
/* size_t is unsigned, hence this optimisation */
|
|
if (--width > sizeof(buf) - 2)
|
|
width = sizeof(buf) - 2;
|
|
width++;
|
|
flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
|
|
for (p = buf; width; width--) {
|
|
c = *fp->_p;
|
|
/*
|
|
* This code mimicks the integer conversion
|
|
* code, but is much simpler.
|
|
*/
|
|
switch (c) {
|
|
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
flags &= ~(SIGNOK | NDIGITS);
|
|
goto fok;
|
|
|
|
case '+':
|
|
case '-':
|
|
if (flags & SIGNOK) {
|
|
flags &= ~SIGNOK;
|
|
goto fok;
|
|
}
|
|
break;
|
|
case '.':
|
|
if (flags & DPTOK) {
|
|
flags &= ~(SIGNOK | DPTOK);
|
|
goto fok;
|
|
}
|
|
break;
|
|
case 'e':
|
|
case 'E':
|
|
/* no exponent without some digits */
|
|
if ((flags & (NDIGITS | EXPOK)) == EXPOK) {
|
|
flags = (flags & ~(EXPOK | DPTOK)) | SIGNOK | NDIGITS;
|
|
goto fok;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
fok:
|
|
*p++ = c;
|
|
if (--fp->_r > 0)
|
|
fp->_p++;
|
|
else if (ib__srefill(fp))
|
|
break; /* EOF */
|
|
}
|
|
/*
|
|
* If no digits, might be missing exponent digits
|
|
* (just give back the exponent) or might be missing
|
|
* regular digits, but had sign and/or decimal point.
|
|
*/
|
|
if (flags & NDIGITS) {
|
|
if (flags & EXPOK) {
|
|
/* no digits at all */
|
|
while (p > buf)
|
|
ib_ungetc(*(u_char *)-- p, fp);
|
|
goto match_failure;
|
|
}
|
|
/* just a bad exponent (e and maybe sign) */
|
|
c = *(u_char *)-- p;
|
|
if (c != 'e' && c != 'E') {
|
|
(void) ib_ungetc(c, fp); /* sign */
|
|
c = *(u_char *)-- p;
|
|
}
|
|
(void) ib_ungetc(c, fp);
|
|
}
|
|
if ((flags & SUPPRESS) == 0) {
|
|
double res;
|
|
|
|
*p = 0;
|
|
/* XXX this loses precision for long doubles. */
|
|
res = strtod(buf, (char **) NULL);
|
|
if (flags & LONGDBL)
|
|
*va_arg(ap, long double *) = res;
|
|
else if (flags & LONG)
|
|
*va_arg(ap, double *) = res;
|
|
else
|
|
*va_arg(ap, float *) = res;
|
|
nassigned++;
|
|
}
|
|
nread += p - buf;
|
|
nconversions++;
|
|
break;
|
|
}
|
|
}
|
|
input_failure:
|
|
return (nconversions != 0 ? nassigned : EOF);
|
|
match_failure:
|
|
return (nassigned);
|
|
}
|
|
|
|
/*
|
|
* Fill in the given table from the scanset at the given format
|
|
* (just after `['). Return a pointer to the character past the
|
|
* closing `]'. The table has a 1 wherever characters should be
|
|
* considered part of the scanset.
|
|
*/
|
|
static u_char *ib__sccl(char *tab, u_char * fmt)
|
|
{
|
|
int c, n, v, i;
|
|
|
|
/* first `clear' the whole table */
|
|
c = *fmt++; /* first char hat => negated scanset */
|
|
if (c == '^') {
|
|
v = 1; /* default => accept */
|
|
c = *fmt++; /* get new first char */
|
|
}
|
|
else
|
|
v = 0; /* default => reject */
|
|
|
|
/* XXX: Will not work if sizeof(tab*) > sizeof(char) */
|
|
(void) memset(tab, v, 256);
|
|
|
|
if (c == 0)
|
|
return (fmt - 1); /* format ended before closing ] */
|
|
|
|
/*
|
|
* Now set the entries corresponding to the actual scanset
|
|
* to the opposite of the above.
|
|
*
|
|
* The first character may be ']' (or '-') without being special;
|
|
* the last character may be '-'.
|
|
*/
|
|
v = 1 - v;
|
|
for (;;) {
|
|
tab[c] = v; /* take character c */
|
|
doswitch:
|
|
n = *fmt++; /* and examine the next */
|
|
switch (n) {
|
|
|
|
case 0: /* format ended too soon */
|
|
return (fmt - 1);
|
|
|
|
case '-':
|
|
/*
|
|
* A scanset of the form
|
|
* [01+-]
|
|
* is defined as `the digit 0, the digit 1,
|
|
* the character +, the character -', but
|
|
* the effect of a scanset such as
|
|
* [a-zA-Z0-9]
|
|
* is implementation defined. The V7 Unix
|
|
* scanf treats `a-z' as `the letters a through
|
|
* z', but treats `a-a' as `the letter a, the
|
|
* character -, and the letter a'.
|
|
*
|
|
* For compatibility, the `-' is not considerd
|
|
* to define a range if the character following
|
|
* it is either a close bracket (required by ANSI)
|
|
* or is not numerically greater than the character
|
|
* we just stored in the table (c).
|
|
*/
|
|
n = *fmt;
|
|
if (n == ']'
|
|
|| (ib__collate_load_error ? n < c :
|
|
ib__collate_range_cmp(n, c) < 0)
|
|
) {
|
|
c = '-';
|
|
break; /* resume the for(;;) */
|
|
}
|
|
fmt++;
|
|
/* fill in the range */
|
|
if (ib__collate_load_error) {
|
|
do {
|
|
tab[++c] = v;
|
|
} while (c < n);
|
|
}
|
|
else {
|
|
for (i = 0; i < 256; i++)
|
|
if (ib__collate_range_cmp(c, i) < 0
|
|
&& ib__collate_range_cmp(i, n) <= 0)
|
|
tab[i] = v;
|
|
}
|
|
#ifdef NOT_USED_OR_REPLACED /* XXX another disgusting compatibility hack */
|
|
c = *fmt++;
|
|
if (c == 0)
|
|
return (fmt - 1);
|
|
if (c == ']')
|
|
return (fmt);
|
|
#else
|
|
c = n;
|
|
/*
|
|
* Alas, the V7 Unix scanf also treats formats
|
|
* such as [a-c-e] as `the letters a through e'.
|
|
* This too is permitted by the standard....
|
|
*/
|
|
goto doswitch;
|
|
#endif
|
|
break;
|
|
|
|
case ']': /* end of scanset */
|
|
return (fmt);
|
|
|
|
default: /* just another character */
|
|
c = n;
|
|
break;
|
|
}
|
|
}
|
|
/* NOTREACHED */
|
|
}
|
|
int ib_vprintf(char const *fmt, va_list ap)
|
|
{
|
|
return (ib_vfprintf(ib_stdout, fmt, ap));
|
|
}
|
|
int ib_vscanf(const char *fmt, va_list ap)
|
|
{
|
|
int retval;
|
|
|
|
FLOCKFILE(stdin);
|
|
retval = ib__svfscanf(ib_stdin, fmt, ap);
|
|
FUNLOCKFILE(ib_stdin);
|
|
return (retval);
|
|
}
|
|
|
|
|
|
/*
|
|
* Write the given character into the (probably full) buffer for
|
|
* the given file. Flush the buffer out if it is or becomes full,
|
|
* or if c=='\n' and the file is line buffered.
|
|
*/
|
|
int ib__swbuf(int c, IB_FILE * fp)
|
|
{
|
|
int n;
|
|
|
|
/*
|
|
* In case we cannot write, or an exception takes us out early,
|
|
* make sure _w is 0 (if fully- or un-buffered) or -_bf._size
|
|
* (if line buffered) so that we will get called again.
|
|
* If we did not do this, a sufficient number of ib_putc()
|
|
* calls might wrap _w from negative to positive.
|
|
*/
|
|
fp->_w = fp->_lbfsize;
|
|
if (cantwrite(fp))
|
|
return (EOF);
|
|
c = (unsigned char) c;
|
|
|
|
/*
|
|
* If it is completely full, flush it out. Then, in any case,
|
|
* stuff c into the buffer. If this causes the buffer to fill
|
|
* completely, or if c is '\n' and the file is line buffered,
|
|
* flush it (perhaps a second time). The second flush will always
|
|
* happen on unbuffered streams, where _bf._size==1; fflush()
|
|
* guarantees that ib_putc() will always call wbuf() by setting _w
|
|
* to 0, so we need not do anything else.
|
|
*/
|
|
n = fp->_p - fp->_bf._base;
|
|
if (n >= fp->_bf._size) {
|
|
if (ib_fflush(fp))
|
|
return (EOF);
|
|
n = 0;
|
|
}
|
|
fp->_w--;
|
|
*fp->_p++ = c;
|
|
if (++n == fp->_bf._size || (fp->_flags & IB__SLBF && c == '\n'))
|
|
if (ib_fflush(fp))
|
|
return (EOF);
|
|
return (c);
|
|
}
|
|
|
|
|
|
/*
|
|
* Various output routines call wsetup to be sure it is safe to write,
|
|
* because either _flags does not include IB__SWR, or _buf is NULL.
|
|
* _wsetup returns 0 if OK to write, nonzero otherwise.
|
|
*/
|
|
int ib__swsetup(IB_FILE * fp)
|
|
{
|
|
/* make sure stdio is set up */
|
|
if (!ib__sdidinit)
|
|
ib__sinit();
|
|
|
|
/*
|
|
* If we are not writing, we had better be reading and writing.
|
|
*/
|
|
if ((fp->_flags & IB__SWR) == 0) {
|
|
if ((fp->_flags & IB__SRW) == 0)
|
|
return (EOF);
|
|
if (fp->_flags & IB__SRD) {
|
|
/* clobber any ib_ungetc data */
|
|
if (HASUB(fp))
|
|
FREEUB(fp);
|
|
fp->_flags &= ~(IB__SRD | IB__SEOF);
|
|
fp->_r = 0;
|
|
fp->_p = fp->_bf._base;
|
|
}
|
|
fp->_flags |= IB__SWR;
|
|
}
|
|
|
|
/*
|
|
* Make a buffer if necessary, then set _w.
|
|
*/
|
|
if (fp->_bf._base == NULL)
|
|
ib__smakebuf(fp);
|
|
if (fp->_flags & IB__SLBF) {
|
|
/*
|
|
* It is line buffered, so make _lbfsize be -_bufsize
|
|
* for the putc() macro. We will change _lbfsize back
|
|
* to 0 whenever we turn off IB__SWR.
|
|
*/
|
|
fp->_w = 0;
|
|
fp->_lbfsize = -fp->_bf._size;
|
|
}
|
|
else
|
|
fp->_w = fp->_flags & IB__SNBF ? 0 : fp->_bf._size;
|
|
return (0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Write some memory regions. Return zero on success, EOF on error.
|
|
*
|
|
* This routine is large and unsightly, but most of the ugliness due
|
|
* to the three different kinds of output buffering is handled here.
|
|
*/
|
|
int ib__sfvwrite(IB_FILE * fp, struct ib__suio *uio)
|
|
{
|
|
size_t len;
|
|
char *p;
|
|
struct ib__siov *iov;
|
|
int w, s;
|
|
char *nl;
|
|
int nlknown, nldist;
|
|
|
|
if ((len = uio->uio_resid) == 0)
|
|
return (0);
|
|
/* make sure we can write */
|
|
if (cantwrite(fp))
|
|
return (EOF);
|
|
|
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
|
#define COPY(n) (void)memcpy((void *)fp->_p, (void *)p, (size_t)(n))
|
|
|
|
iov = uio->uio_iov;
|
|
p = iov->iov_base;
|
|
len = iov->iov_len;
|
|
iov++;
|
|
#define GETIOV(extra_work) \
|
|
while (len == 0) { \
|
|
extra_work; \
|
|
p = iov->iov_base; \
|
|
len = iov->iov_len; \
|
|
iov++; \
|
|
}
|
|
if (fp->_flags & IB__SNBF) {
|
|
/*
|
|
* Unbuffered: write up to BUFSIZ bytes at a time.
|
|
*/
|
|
do {
|
|
GETIOV(;);
|
|
w = (*fp->_write) (fp->_cookie, p, MIN(len, BUFSIZ));
|
|
if (w <= 0)
|
|
goto err;
|
|
p += w;
|
|
len -= w;
|
|
} while ((uio->uio_resid -= w) != 0);
|
|
}
|
|
else if ((fp->_flags & IB__SLBF) == 0) {
|
|
/*
|
|
* Fully buffered: fill partially full buffer, if any,
|
|
* and then flush. If there is no partial buffer, write
|
|
* one _bf._size byte chunk directly (without copying).
|
|
*
|
|
* String output is a special case: write as many bytes
|
|
* as fit, but pretend we wrote everything. This makes
|
|
* snprintf() return the number of bytes needed, rather
|
|
* than the number used, and avoids its write function
|
|
* (so that the write function can be invalid).
|
|
*/
|
|
do {
|
|
GETIOV(;);
|
|
if ((fp->_flags & (IB__SALC | IB__SSTR)) ==
|
|
(IB__SALC | IB__SSTR) && fp->_w < len) {
|
|
size_t blen = fp->_p - fp->_bf._base;
|
|
|
|
/*
|
|
* Alloc an extra 128 bytes (+ 1 for NULL)
|
|
* so we don't call realloc(3) so often.
|
|
*/
|
|
fp->_w = len + 128;
|
|
fp->_bf._size = blen + len + 128;
|
|
fp->_bf._base = reallocf(fp->_bf._base, fp->_bf._size + 1);
|
|
if (fp->_bf._base == NULL)
|
|
goto err;
|
|
fp->_p = fp->_bf._base + blen;
|
|
}
|
|
w = fp->_w;
|
|
if (fp->_flags & IB__SSTR) {
|
|
if (len < w)
|
|
w = len;
|
|
if (w > 0) {
|
|
COPY(w); /* copy MIN(fp->_w,len), */
|
|
fp->_w -= w;
|
|
fp->_p += w;
|
|
}
|
|
w = len; /* but pretend copied all */
|
|
}
|
|
else if (fp->_p > fp->_bf._base && len > w) {
|
|
/* fill and flush */
|
|
COPY(w);
|
|
/* fp->_w -= w; *//* unneeded */
|
|
fp->_p += w;
|
|
if (ib_fflush(fp))
|
|
goto err;
|
|
}
|
|
else if (len >= (w = fp->_bf._size)) {
|
|
/* write directly */
|
|
w = (*fp->_write) (fp->_cookie, p, w);
|
|
if (w <= 0)
|
|
goto err;
|
|
}
|
|
else {
|
|
/* fill and done */
|
|
w = len;
|
|
COPY(w);
|
|
fp->_w -= w;
|
|
fp->_p += w;
|
|
}
|
|
p += w;
|
|
len -= w;
|
|
} while ((uio->uio_resid -= w) != 0);
|
|
}
|
|
else {
|
|
/*
|
|
* Line buffered: like fully buffered, but we
|
|
* must check for newlines. Compute the distance
|
|
* to the first newline (including the newline),
|
|
* or `infinity' if there is none, then pretend
|
|
* that the amount to write is MIN(len,nldist).
|
|
*/
|
|
nlknown = 0;
|
|
nldist = 0; /* XXX just to keep gcc happy */
|
|
do {
|
|
GETIOV(nlknown = 0);
|
|
if (!nlknown) {
|
|
nl = memchr((void *) p, '\n', len);
|
|
nldist = nl ? nl + 1 - p : len + 1;
|
|
nlknown = 1;
|
|
}
|
|
s = MIN(len, nldist);
|
|
w = fp->_w + fp->_bf._size;
|
|
if (fp->_p > fp->_bf._base && s > w) {
|
|
COPY(w);
|
|
/* fp->_w -= w; */
|
|
fp->_p += w;
|
|
if (ib_fflush(fp))
|
|
goto err;
|
|
}
|
|
else if (s >= (w = fp->_bf._size)) {
|
|
w = (*fp->_write) (fp->_cookie, p, w);
|
|
if (w <= 0)
|
|
goto err;
|
|
}
|
|
else {
|
|
w = s;
|
|
COPY(w);
|
|
fp->_w -= w;
|
|
fp->_p += w;
|
|
}
|
|
if ((nldist -= w) == 0) {
|
|
/* copied the newline: flush and forget */
|
|
if (ib_fflush(fp))
|
|
goto err;
|
|
nlknown = 0;
|
|
}
|
|
p += w;
|
|
len -= w;
|
|
} while ((uio->uio_resid -= w) != 0);
|
|
}
|
|
return (0);
|
|
|
|
err:
|
|
fp->_flags |= IB__SERR;
|
|
return (EOF);
|
|
}
|
|
|
|
/* This stuff all comes from stdlib/netbsd_strtod.c */
|
|
|
|
|
|
#ifdef MALLOC
|
|
#ifdef KR_headers
|
|
extern char *MALLOC();
|
|
#else
|
|
extern void *MALLOC(size_t);
|
|
#endif
|
|
#else
|
|
#define MALLOC malloc
|
|
#endif
|
|
|
|
#ifdef SOLARIS
|
|
#ifdef sparc
|
|
#define IEEE_BIG_ENDIAN
|
|
#undef WORDS_BIGENDIAN /* EKU: why do we undefine WORDS_BIGENDIAN here ??? */
|
|
#else
|
|
#define IEEE_LITTLE_ENDIAN
|
|
#endif
|
|
#define IEEE_ARITHMETIC
|
|
#endif
|
|
|
|
|
|
#ifdef IEEE_ARITHMETIC
|
|
#define DBL_DIG 15
|
|
#define DBL_MAX_10_EXP 308
|
|
#define DBL_MAX_EXP 1024
|
|
#define FLT_RADIX 2
|
|
#define FLT_ROUNDS 1
|
|
#define DBL_MAX 1.7976931348623157e+308
|
|
#endif
|
|
|
|
|
|
#ifdef IEEE_LITTLE_ENDIAN
|
|
#define word0(x) ((ULONG *)&x)[1]
|
|
#define word1(x) ((ULONG *)&x)[0]
|
|
#else
|
|
#define word0(x) ((ULONG *)&x)[0]
|
|
#define word1(x) ((ULONG *)&x)[1]
|
|
#endif
|
|
|
|
#if defined(IEEE_LITTLE_ENDIAN) + defined(IEEE_BIG_ENDIAN) + !defined(WORDS_BIGENDIAN) + defined(IBM) != 1
|
|
Exactly one of IEEE_LITTLE_ENDIAN IEEE_BIG_ENDIAN, WORDS_BIGENDIAN, or IBM should be defined.
|
|
#endif
|
|
#if defined(IEEE_LITTLE_ENDIAN) + defined(IEEE_BIG_ENDIAN)
|
|
#define Exp_shift 20
|
|
#define Exp_shift1 20
|
|
#define Exp_msk1 0x100000
|
|
#define Exp_msk11 0x100000
|
|
#define Exp_mask 0x7ff00000
|
|
#define P 53
|
|
#define Bias 1023
|
|
#define IEEE_Arith
|
|
#define Emin (-1022)
|
|
#define Exp_1 0x3ff00000
|
|
#define Exp_11 0x3ff00000
|
|
#define Ebits 11
|
|
#define Frac_mask 0xfffff
|
|
#define Frac_mask1 0xfffff
|
|
#define Ten_pmax 22
|
|
#define Bletch 0x10
|
|
#define Bndry_mask 0xfffff
|
|
#define Bndry_mask1 0xfffff
|
|
#define LSB 1
|
|
#define Sign_bit 0x80000000
|
|
#define Log2P 1
|
|
#define Tiny0 0
|
|
#define Tiny1 1
|
|
#define Quick_max 14
|
|
#define Int_max 14
|
|
#define Infinite(x) (word0(x) == 0x7ff00000) /* sufficient test for here */
|
|
#else
|
|
#undef Sudden_Underflow
|
|
#define Sudden_Underflow
|
|
#ifdef IBM
|
|
#define Exp_shift 24
|
|
#define Exp_shift1 24
|
|
#define Exp_msk1 0x1000000
|
|
#define Exp_msk11 0x1000000
|
|
#define Exp_mask 0x7f000000
|
|
#define P 14
|
|
#define Bias 65
|
|
#define Exp_1 0x41000000
|
|
#define Exp_11 0x41000000
|
|
#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */
|
|
#define Frac_mask 0xffffff
|
|
#define Frac_mask1 0xffffff
|
|
#define Bletch 4
|
|
#define Ten_pmax 22
|
|
#define Bndry_mask 0xefffff
|
|
#define Bndry_mask1 0xffffff
|
|
#define LSB 1
|
|
#define Sign_bit 0x80000000
|
|
#define Log2P 4
|
|
#define Tiny0 0x100000
|
|
#define Tiny1 0
|
|
#define Quick_max 14
|
|
#define Int_max 15
|
|
#else /* VAX */
|
|
#define Exp_shift 23
|
|
#define Exp_shift1 7
|
|
#define Exp_msk1 0x80
|
|
#define Exp_msk11 0x800000
|
|
#define Exp_mask 0x7f80
|
|
#define P 56
|
|
#define Bias 129
|
|
#define Exp_1 0x40800000
|
|
#define Exp_11 0x4080
|
|
#define Ebits 8
|
|
#define Frac_mask 0x7fffff
|
|
#define Frac_mask1 0xffff007f
|
|
#define Ten_pmax 24
|
|
#define Bletch 2
|
|
#define Bndry_mask 0xffff007f
|
|
#define Bndry_mask1 0xffff007f
|
|
#define LSB 0x10000
|
|
#define Sign_bit 0x8000
|
|
#define Log2P 1
|
|
#define Tiny0 0x80
|
|
#define Tiny1 0
|
|
#define Quick_max 15
|
|
#define Int_max 15
|
|
#endif
|
|
#endif
|
|
#ifndef IEEE_Arith
|
|
#define ROUND_BIASED
|
|
#endif
|
|
#define Sign_Extend(a,b) /*no-op */
|
|
struct
|
|
Bigint {
|
|
struct Bigint *next;
|
|
int k, maxwds, sign, wds;
|
|
ULONG x[1];
|
|
};
|
|
|
|
typedef struct Bigint Bigint;
|
|
|
|
static const double tens[] = {
|
|
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
|
|
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
|
1e20, 1e21, 1e22
|
|
#ifndef WORDS_BIGENDIAN
|
|
, 1e23, 1e24
|
|
#endif
|
|
};
|
|
|
|
#ifdef IEEE_Arith
|
|
static const double bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 };
|
|
static const double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, 1e-256 };
|
|
#define n_bigtens 5
|
|
#else
|
|
#ifdef IBM
|
|
static const double bigtens[] = { 1e16, 1e32, 1e64 };
|
|
static const double tinytens[] = { 1e-16, 1e-32, 1e-64 };
|
|
#define n_bigtens 3
|
|
#else
|
|
static const double bigtens[] = { 1e16, 1e32 };
|
|
static const double tinytens[] = { 1e-16, 1e-32 };
|
|
#define n_bigtens 2
|
|
#endif
|
|
#endif
|
|
|
|
static Bigint *Balloc(int);
|
|
static void Bfree(Bigint *);
|
|
static Bigint *multadd(Bigint *, int, int);
|
|
static Bigint *mult(Bigint *, Bigint *);
|
|
static Bigint *i2b(int);
|
|
|
|
static Bigint *p5s;
|
|
|
|
static Bigint *pow5mult(Bigint * b, int k)
|
|
{
|
|
Bigint *b1, *p5, *p51;
|
|
int i;
|
|
static int p05[3] = { 5, 25, 125 };
|
|
|
|
if ((i = k & 3) != 0)
|
|
b = multadd(b, p05[i - 1], 0);
|
|
|
|
if (!(k >>= 2))
|
|
return b;
|
|
if (!(p5 = p5s)) {
|
|
/* first time */
|
|
p5 = p5s = i2b(625);
|
|
p5->next = 0;
|
|
}
|
|
for (;;) {
|
|
if (k & 1) {
|
|
b1 = mult(b, p5);
|
|
Bfree(b);
|
|
b = b1;
|
|
}
|
|
if (!(k >>= 1))
|
|
break;
|
|
if (!(p51 = p5->next)) {
|
|
p51 = p5->next = mult(p5, p5);
|
|
p51->next = 0;
|
|
}
|
|
p5 = p51;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
|
|
static Bigint *lshift(Bigint * b, int k)
|
|
{
|
|
int i, k1, n, n1;
|
|
Bigint *b1;
|
|
ULONG *x, *x1, *xe, z;
|
|
|
|
n = k >> 4;
|
|
k1 = b->k;
|
|
n1 = n + b->wds + 1;
|
|
for (i = b->maxwds; n1 > i; i <<= 1)
|
|
k1++;
|
|
b1 = Balloc(k1);
|
|
x1 = b1->x;
|
|
for (i = 0; i < n; i++)
|
|
*x1++ = 0;
|
|
x = b->x;
|
|
xe = x + b->wds;
|
|
if (k &= 0xf) {
|
|
k1 = 16 - k;
|
|
z = 0;
|
|
do {
|
|
*x1++ = *x << k & 0xffff | z;
|
|
z = *x++ >> k1;
|
|
}
|
|
while (x < xe);
|
|
if (*x1 = z)
|
|
++n1;
|
|
}
|
|
else
|
|
do
|
|
*x1++ = *x++;
|
|
while (x < xe);
|
|
b1->wds = n1 - 1;
|
|
Bfree(b);
|
|
return b1;
|
|
}
|
|
|
|
static int cmp(Bigint * a, Bigint * b)
|
|
{
|
|
ULONG *xa, *xa0, *xb, *xb0;
|
|
int i, j;
|
|
|
|
i = a->wds;
|
|
j = b->wds;
|
|
#ifdef DEBUG
|
|
if (i > 1 && !a->x[i - 1])
|
|
Bug("cmp called with a->x[a->wds-1] == 0");
|
|
if (j > 1 && !b->x[j - 1])
|
|
Bug("cmp called with b->x[b->wds-1] == 0");
|
|
#endif
|
|
if (i -= j)
|
|
return i;
|
|
xa0 = a->x;
|
|
xa = xa0 + j;
|
|
xb0 = b->x;
|
|
xb = xb0 + j;
|
|
for (;;) {
|
|
if (*--xa != *--xb)
|
|
return *xa < *xb ? -1 : 1;
|
|
if (xa <= xa0)
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static Bigint *diff(Bigint * a, Bigint * b)
|
|
{
|
|
Bigint *c;
|
|
int i, wa, wb;
|
|
SLONG borrow, y; /* We need signed shifts here. */
|
|
ULONG *xa, *xae, *xb, *xbe, *xc;
|
|
|
|
i = cmp(a, b);
|
|
if (!i) {
|
|
c = Balloc(0);
|
|
c->wds = 1;
|
|
c->x[0] = 0;
|
|
return c;
|
|
}
|
|
if (i < 0) {
|
|
c = a;
|
|
a = b;
|
|
b = c;
|
|
i = 1;
|
|
}
|
|
else
|
|
i = 0;
|
|
c = Balloc(a->k);
|
|
c->sign = i;
|
|
wa = a->wds;
|
|
xa = a->x;
|
|
xae = xa + wa;
|
|
wb = b->wds;
|
|
xb = b->x;
|
|
xbe = xb + wb;
|
|
xc = c->x;
|
|
borrow = 0;
|
|
do {
|
|
y = *xa++ - *xb++ + borrow;
|
|
borrow = y >> 16;
|
|
Sign_Extend(borrow, y);
|
|
*xc++ = y & 0xffff;
|
|
}
|
|
while (xb < xbe);
|
|
while (xa < xae) {
|
|
y = *xa++ + borrow;
|
|
borrow = y >> 16;
|
|
Sign_Extend(borrow, y);
|
|
*xc++ = y & 0xffff;
|
|
}
|
|
while (!*--xc)
|
|
wa--;
|
|
c->wds = wa;
|
|
return c;
|
|
}
|
|
|
|
static Bigint *d2b(double d, int *e, int *bits)
|
|
{
|
|
Bigint *b;
|
|
int de, i, k;
|
|
ULONG *x, y, z;
|
|
#ifndef WORDS_BIGENDIAN
|
|
ULONG d0, d1;
|
|
d0 = word0(d) >> 16 | word0(d) << 16;
|
|
d1 = word1(d) >> 16 | word1(d) << 16;
|
|
#else
|
|
#define d0 word0(d)
|
|
#define d1 word1(d)
|
|
#endif
|
|
|
|
b = Balloc(2);
|
|
x = b->x;
|
|
|
|
z = d0 & Frac_mask;
|
|
d0 &= 0x7fffffff; /* clear sign bit, which we ignore */
|
|
#ifdef Sudden_Underflow
|
|
de = (int) (d0 >> Exp_shift);
|
|
#ifndef IBM
|
|
z |= Exp_msk11;
|
|
#endif
|
|
#else
|
|
if ((de = (int) (d0 >> Exp_shift)) != 0)
|
|
z |= Exp_msk1;
|
|
#endif
|
|
if (y = d1) {
|
|
if (k = lo0bits(&y))
|
|
if (k >= 16) {
|
|
x[0] = y | z << 32 - k & 0xffff;
|
|
x[1] = z >> k - 16 & 0xffff;
|
|
x[2] = z >> k;
|
|
i = 2;
|
|
}
|
|
else {
|
|
x[0] = y & 0xffff;
|
|
x[1] = y >> 16 | z << 16 - k & 0xffff;
|
|
x[2] = z >> k & 0xffff;
|
|
x[3] = z >> k + 16;
|
|
i = 3;
|
|
}
|
|
else {
|
|
x[0] = y & 0xffff;
|
|
x[1] = y >> 16;
|
|
x[2] = z & 0xffff;
|
|
x[3] = z >> 16;
|
|
i = 3;
|
|
}
|
|
}
|
|
else {
|
|
#ifdef DEBUG
|
|
if (!z)
|
|
Bug("Zero passed to d2b");
|
|
#endif
|
|
k = lo0bits(&z);
|
|
if (k >= 16) {
|
|
x[0] = z;
|
|
i = 0;
|
|
}
|
|
else {
|
|
x[0] = z & 0xffff;
|
|
x[1] = z >> 16;
|
|
i = 1;
|
|
}
|
|
k += 32;
|
|
}
|
|
while (!x[i])
|
|
--i;
|
|
b->wds = i + 1;
|
|
#ifndef Sudden_Underflow
|
|
if (de) {
|
|
#endif
|
|
#ifdef IBM
|
|
*e = (de - Bias - (P - 1) << 2) + k;
|
|
*bits = 4 * P + 8 - k - hi0bits(word0(d) & Frac_mask);
|
|
#else
|
|
*e = de - Bias - (P - 1) + k;
|
|
*bits = P - k;
|
|
#endif
|
|
#ifndef Sudden_Underflow
|
|
}
|
|
else {
|
|
*e = de - Bias - (P - 1) + 1 + k;
|
|
*bits = (i + 2) * 16 - hi0bits(x[i]);
|
|
}
|
|
#endif
|
|
return b;
|
|
}
|
|
|
|
#undef d0
|
|
#undef d1
|
|
|
|
#define Kmax 15
|
|
static Bigint *freelist[Kmax + 1];
|
|
|
|
static Bigint *Balloc(int k)
|
|
{
|
|
int x;
|
|
Bigint *rv;
|
|
|
|
if ((rv = freelist[k]) != NULL) {
|
|
freelist[k] = rv->next;
|
|
}
|
|
else {
|
|
x = 1 << k;
|
|
rv = (Bigint *) MALLOC(sizeof(Bigint) + (x - 1) * sizeof(SLONG));
|
|
rv->k = k;
|
|
rv->maxwds = x;
|
|
}
|
|
rv->sign = rv->wds = 0;
|
|
return rv;
|
|
}
|
|
|
|
static void Bfree(Bigint * v)
|
|
{
|
|
if (v) {
|
|
v->next = freelist[v->k];
|
|
freelist[v->k] = v;
|
|
}
|
|
}
|
|
|
|
#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \
|
|
y->wds*sizeof(SLONG) + 2*sizeof(int))
|
|
|
|
static Bigint *multadd(Bigint * b, int m, int a)
|
|
/* multiply by m and add a */
|
|
{
|
|
int i, wds;
|
|
ULONG *x, y;
|
|
Bigint *b1;
|
|
|
|
wds = b->wds;
|
|
x = b->x;
|
|
i = 0;
|
|
do {
|
|
y = *x * m + a;
|
|
a = (int) (y >> 16);
|
|
*x++ = y & 0xffff;
|
|
}
|
|
while (++i < wds);
|
|
if (a) {
|
|
if (wds >= b->maxwds) {
|
|
b1 = Balloc(b->k + 1);
|
|
Bcopy(b1, b);
|
|
Bfree(b);
|
|
b = b1;
|
|
}
|
|
b->x[wds++] = a;
|
|
b->wds = wds;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
|
|
|
|
static int hi0bits(ULONG x)
|
|
{
|
|
int k = 0;
|
|
|
|
if (!(x & 0xffff0000)) {
|
|
k = 16;
|
|
x <<= 16;
|
|
}
|
|
if (!(x & 0xff000000)) {
|
|
k += 8;
|
|
x <<= 8;
|
|
}
|
|
if (!(x & 0xf0000000)) {
|
|
k += 4;
|
|
x <<= 4;
|
|
}
|
|
if (!(x & 0xc0000000)) {
|
|
k += 2;
|
|
x <<= 2;
|
|
}
|
|
if (!(x & 0x80000000)) {
|
|
k++;
|
|
if (!(x & 0x40000000))
|
|
return 32;
|
|
}
|
|
return k;
|
|
}
|
|
|
|
static int lo0bits(ULONG * y)
|
|
{
|
|
int k;
|
|
ULONG x = *y;
|
|
|
|
if (x & 7) {
|
|
if (x & 1)
|
|
return 0;
|
|
if (x & 2) {
|
|
*y = x >> 1;
|
|
return 1;
|
|
}
|
|
*y = x >> 2;
|
|
return 2;
|
|
}
|
|
k = 0;
|
|
if (!(x & 0xffff)) {
|
|
k = 16;
|
|
x >>= 16;
|
|
}
|
|
if (!(x & 0xff)) {
|
|
k += 8;
|
|
x >>= 8;
|
|
}
|
|
if (!(x & 0xf)) {
|
|
k += 4;
|
|
x >>= 4;
|
|
}
|
|
if (!(x & 0x3)) {
|
|
k += 2;
|
|
x >>= 2;
|
|
}
|
|
if (!(x & 1)) {
|
|
k++;
|
|
x >>= 1;
|
|
if (!x & 1)
|
|
return 32;
|
|
}
|
|
*y = x;
|
|
return k;
|
|
}
|
|
|
|
static Bigint *i2b(int i)
|
|
{
|
|
Bigint *b;
|
|
|
|
b = Balloc(1);
|
|
b->x[0] = i;
|
|
b->wds = 1;
|
|
return b;
|
|
}
|
|
|
|
static Bigint *mult(Bigint * a, Bigint * b)
|
|
{
|
|
Bigint *c;
|
|
int k, wa, wb, wc;
|
|
ULONG carry, y, z;
|
|
ULONG *x, *xa, *xae, *xb, *xbe, *xc, *xc0;
|
|
|
|
if (a->wds < b->wds) {
|
|
c = a;
|
|
a = b;
|
|
b = c;
|
|
}
|
|
k = a->k;
|
|
wa = a->wds;
|
|
wb = b->wds;
|
|
wc = wa + wb;
|
|
if (wc > a->maxwds)
|
|
k++;
|
|
c = Balloc(k);
|
|
for (x = c->x, xa = x + wc; x < xa; x++)
|
|
*x = 0;
|
|
xa = a->x;
|
|
xae = xa + wa;
|
|
xb = b->x;
|
|
xbe = xb + wb;
|
|
xc0 = c->x;
|
|
for (; xb < xbe; xc0++) {
|
|
if (y = *xb++) {
|
|
x = xa;
|
|
xc = xc0;
|
|
carry = 0;
|
|
do {
|
|
z = *x++ * y + *xc + carry;
|
|
carry = z >> 16;
|
|
*xc++ = z & 0xffff;
|
|
}
|
|
while (x < xae);
|
|
*xc = carry;
|
|
}
|
|
}
|
|
for (xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc);
|
|
c->wds = wc;
|
|
return c;
|
|
}
|
|
|
|
static int quorem(Bigint * b, Bigint * S)
|
|
{
|
|
int n;
|
|
SLONG borrow, y;
|
|
ULONG carry, q, ys;
|
|
ULONG *bx, *bxe, *sx, *sxe;
|
|
|
|
n = S->wds;
|
|
#ifdef DEBUG
|
|
/*debug */ if (b->wds > n)
|
|
/*debug */
|
|
Bug("oversize b in quorem");
|
|
#endif
|
|
if (b->wds < n)
|
|
return 0;
|
|
sx = S->x;
|
|
sxe = sx + --n;
|
|
bx = b->x;
|
|
bxe = bx + n;
|
|
q = *bxe / (*sxe + 1); /* ensure q <= true quotient */
|
|
#ifdef DEBUG
|
|
/*debug */ if (q > 9)
|
|
/*debug */
|
|
Bug("oversized quotient in quorem");
|
|
#endif
|
|
if (q) {
|
|
borrow = 0;
|
|
carry = 0;
|
|
do {
|
|
ys = *sx++ * q + carry;
|
|
carry = ys >> 16;
|
|
y = *bx - (ys & 0xffff) + borrow;
|
|
borrow = y >> 16;
|
|
Sign_Extend(borrow, y);
|
|
*bx++ = y & 0xffff;
|
|
}
|
|
while (sx <= sxe);
|
|
if (!*bxe) {
|
|
bx = b->x;
|
|
while (--bxe > bx && !*bxe)
|
|
--n;
|
|
b->wds = n;
|
|
}
|
|
}
|
|
if (cmp(b, S) >= 0) {
|
|
q++;
|
|
borrow = 0;
|
|
carry = 0;
|
|
bx = b->x;
|
|
sx = S->x;
|
|
do {
|
|
ys = *sx++ + carry;
|
|
carry = ys >> 16;
|
|
y = *bx - (ys & 0xffff) + borrow;
|
|
borrow = y >> 16;
|
|
Sign_Extend(borrow, y);
|
|
*bx++ = y & 0xffff;
|
|
}
|
|
while (sx <= sxe);
|
|
bx = b->x;
|
|
bxe = bx + n;
|
|
if (!*bxe) {
|
|
while (--bxe > bx && !*bxe)
|
|
--n;
|
|
b->wds = n;
|
|
}
|
|
}
|
|
return q;
|
|
}
|
|
|
|
/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string.
|
|
*
|
|
* Inspired by "How to Print Floating-Point Numbers Accurately" by
|
|
* Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101].
|
|
*
|
|
* Modifications:
|
|
* 1. Rather than iterating, we use a simple numeric overestimate
|
|
* to determine k = floor(log10(d)). We scale relevant
|
|
* quantities using O(log2(k)) rather than O(k) multiplications.
|
|
* 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't
|
|
* try to generate digits strictly left to right. Instead, we
|
|
* compute with fewer bits and propagate the carry if necessary
|
|
* when rounding the final digit up. This is often faster.
|
|
* 3. Under the assumption that input will be rounded nearest,
|
|
* mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22.
|
|
* That is, we allow equality in stopping tests when the
|
|
* round-nearest rule will give the same floating-point value
|
|
* as would satisfaction of the stopping test with strict
|
|
* inequality.
|
|
* 4. We remove common factors of powers of 2 from relevant
|
|
* quantities.
|
|
* 5. When converting floating-point integers less than 1e16,
|
|
* we use floating-point arithmetic rather than resorting
|
|
* to multiple-precision integers.
|
|
* 6. When asked to produce fewer than 15 digits, we first try
|
|
* to get by with floating-point arithmetic; we resort to
|
|
* multiple-precision integer arithmetic only if we cannot
|
|
* guarantee that the floating-point calculation has given
|
|
* the correctly rounded result. For k requested digits and
|
|
* "uniformly" distributed input, the probability is
|
|
* something like 10^(k-15) that we must resort to the SLONG
|
|
* calculation.
|
|
*/
|
|
|
|
char *ib__dtoa
|
|
(double d, int mode, int ndigits, int *decpt, int *sign, char **rve) {
|
|
/* Arguments ndigits, decpt, sign are similar to those
|
|
of ecvt and fcvt; trailing zeros are suppressed from
|
|
the returned string. If not null, *rve is set to point
|
|
to the end of the return value. If d is +-Infinity or NaN,
|
|
then *decpt is set to 9999.
|
|
|
|
mode:
|
|
0 ==> shortest string that yields d when read in
|
|
and rounded to nearest.
|
|
1 ==> like 0, but with Steele & White stopping rule;
|
|
e.g. with IEEE P754 arithmetic , mode 0 gives
|
|
1e23 whereas mode 1 gives 9.999999999999999e22.
|
|
2 ==> max(1,ndigits) significant digits. This gives a
|
|
return value similar to that of ecvt, except
|
|
that trailing zeros are suppressed.
|
|
3 ==> through ndigits past the decimal point. This
|
|
gives a return value similar to that from fcvt,
|
|
except that trailing zeros are suppressed, and
|
|
ndigits can be negative.
|
|
4-9 should give the same return values as 2-3, i.e.,
|
|
4 <= mode <= 9 ==> same return as mode
|
|
2 + (mode & 1). These modes are mainly for
|
|
debugging; often they run slower but sometimes
|
|
faster than modes 2-3.
|
|
4,5,8,9 ==> left-to-right digit generation.
|
|
6-9 ==> don't try fast floating-point estimate
|
|
(if applicable).
|
|
|
|
Values of mode other than 0-9 are treated as mode 0.
|
|
|
|
Sufficient space is allocated to the return value
|
|
to hold the suppressed trailing zeros.
|
|
*/
|
|
|
|
int bbits, b2, b5, be, dig, i, ieps, ilim0,
|
|
j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, try_quick;
|
|
int ilim = 0, ilim1 = 0, spec_case = 0; /* pacify gcc */
|
|
SLONG L;
|
|
#ifndef Sudden_Underflow
|
|
int denorm;
|
|
ULONG x;
|
|
#endif
|
|
Bigint *b, *b1, *delta, *mhi, *S;
|
|
Bigint *mlo = NULL; /* pacify gcc */
|
|
double d2, ds, eps;
|
|
char *s, *s0;
|
|
static Bigint *result;
|
|
static int result_k;
|
|
|
|
if (result) {
|
|
result->k = result_k;
|
|
result->maxwds = 1 << result_k;
|
|
Bfree(result);
|
|
result = 0;
|
|
}
|
|
|
|
if (word0(d) & Sign_bit) {
|
|
/* set sign for everything, including 0's and NaNs */
|
|
*sign = 1;
|
|
word0(d) &= ~Sign_bit; /* clear sign bit */
|
|
}
|
|
else
|
|
*sign = 0;
|
|
|
|
#if defined(IEEE_Arith) + !defined(WORDS_BIGENDIAN)
|
|
#ifdef IEEE_Arith
|
|
if ((word0(d) & Exp_mask) == Exp_mask)
|
|
#else
|
|
if (word0(d) == 0x8000)
|
|
#endif
|
|
{
|
|
/* Infinity or NaN */
|
|
*decpt = 9999;
|
|
s =
|
|
#ifdef IEEE_Arith
|
|
!word1(d) && !(word0(d) & 0xfffff) ? "Infinity" :
|
|
#endif
|
|
"NaN";
|
|
if (rve)
|
|
*rve =
|
|
#ifdef IEEE_Arith
|
|
s[3] ? s + 8 :
|
|
#endif
|
|
s + 3;
|
|
return s;
|
|
}
|
|
#endif
|
|
#ifdef IBM
|
|
d += 0; /* normalize */
|
|
#endif
|
|
if (!d) {
|
|
*decpt = 1;
|
|
s = "0";
|
|
if (rve)
|
|
*rve = s + 1;
|
|
return s;
|
|
}
|
|
|
|
b = d2b(d, &be, &bbits);
|
|
#ifdef Sudden_Underflow
|
|
i = (int) (word0(d) >> Exp_shift1 & (Exp_mask >> Exp_shift1));
|
|
#else
|
|
if ((i = (int) (word0(d) >> Exp_shift1 & (Exp_mask >> Exp_shift1))) != 0) {
|
|
#endif
|
|
d2 = d;
|
|
word0(d2) &= Frac_mask1;
|
|
word0(d2) |= Exp_11;
|
|
#ifdef IBM
|
|
if (j = 11 - hi0bits(word0(d2) & Frac_mask))
|
|
d2 /= 1 << j;
|
|
#endif
|
|
|
|
/* log(x) ~=~ log(1.5) + (x-1.5)/1.5
|
|
* log10(x) = log(x) / log(10)
|
|
* ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10))
|
|
* log10(d) = (i-Bias)*log(2)/log(10) + log10(d2)
|
|
*
|
|
* This suggests computing an approximation k to log10(d) by
|
|
*
|
|
* k = (i - Bias)*0.301029995663981
|
|
* + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 );
|
|
*
|
|
* We want k to be too large rather than too small.
|
|
* The error in the first-order Taylor series approximation
|
|
* is in our favor, so we just round up the constant enough
|
|
* to compensate for any error in the multiplication of
|
|
* (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077,
|
|
* and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14,
|
|
* adding 1e-13 to the constant term more than suffices.
|
|
* Hence we adjust the constant term to 0.1760912590558.
|
|
* (We could get a more accurate k by invoking log10,
|
|
* but this is probably not worthwhile.)
|
|
*/
|
|
|
|
i -= Bias;
|
|
#ifdef IBM
|
|
i <<= 2;
|
|
i += j;
|
|
#endif
|
|
#ifndef Sudden_Underflow
|
|
denorm = 0;
|
|
}
|
|
else {
|
|
/* d is denormalized */
|
|
|
|
i = bbits + be + (Bias + (P - 1) - 1);
|
|
x = i > 32 ? word0(d) << (64 - i) | word1(d) >> (i - 32)
|
|
: word1(d) << (32 - i);
|
|
d2 = x;
|
|
word0(d2) -= 31 * Exp_msk1; /* adjust exponent */
|
|
i -= (Bias + (P - 1) - 1) + 1;
|
|
denorm = 1;
|
|
}
|
|
#endif
|
|
ds =
|
|
(d2 - 1.5) * 0.289529654602168 + 0.1760912590558 +
|
|
i * 0.301029995663981;
|
|
k = (int) ds;
|
|
if (ds < 0. && ds != k)
|
|
k--; /* want k = floor(ds) */
|
|
k_check = 1;
|
|
if (k >= 0 && k <= Ten_pmax) {
|
|
if (d < tens[k])
|
|
k--;
|
|
k_check = 0;
|
|
}
|
|
j = bbits - i - 1;
|
|
if (j >= 0) {
|
|
b2 = 0;
|
|
s2 = j;
|
|
}
|
|
else {
|
|
b2 = -j;
|
|
s2 = 0;
|
|
}
|
|
if (k >= 0) {
|
|
b5 = 0;
|
|
s5 = k;
|
|
s2 += k;
|
|
}
|
|
else {
|
|
b2 -= k;
|
|
b5 = -k;
|
|
s5 = 0;
|
|
}
|
|
if (mode < 0 || mode > 9)
|
|
mode = 0;
|
|
try_quick = 1;
|
|
if (mode > 5) {
|
|
mode -= 4;
|
|
try_quick = 0;
|
|
}
|
|
leftright = 1;
|
|
switch (mode) {
|
|
case 0:
|
|
case 1:
|
|
ilim = ilim1 = -1;
|
|
i = 18;
|
|
ndigits = 0;
|
|
break;
|
|
case 2:
|
|
leftright = 0;
|
|
/* no break */
|
|
case 4:
|
|
if (ndigits <= 0)
|
|
ndigits = 1;
|
|
ilim = ilim1 = i = ndigits;
|
|
break;
|
|
case 3:
|
|
leftright = 0;
|
|
/* no break */
|
|
case 5:
|
|
i = ndigits + k + 1;
|
|
ilim = i;
|
|
ilim1 = i - 1;
|
|
if (i <= 0)
|
|
i = 1;
|
|
}
|
|
j = sizeof(ULONG);
|
|
for (result_k = 0; sizeof(Bigint) - sizeof(ULONG) + j <= i; j <<= 1)
|
|
result_k++;
|
|
result = Balloc(result_k);
|
|
s = s0 = (char *) result;
|
|
|
|
if (ilim >= 0 && ilim <= Quick_max && try_quick) {
|
|
|
|
/* Try to get by with floating-point arithmetic. */
|
|
|
|
i = 0;
|
|
d2 = d;
|
|
k0 = k;
|
|
ilim0 = ilim;
|
|
ieps = 2; /* conservative */
|
|
if (k > 0) {
|
|
ds = tens[k & 0xf];
|
|
j = k >> 4;
|
|
if (j & Bletch) {
|
|
/* prevent overflows */
|
|
j &= Bletch - 1;
|
|
d /= bigtens[n_bigtens - 1];
|
|
ieps++;
|
|
}
|
|
for (; j; j >>= 1, i++)
|
|
if (j & 1) {
|
|
ieps++;
|
|
ds *= bigtens[i];
|
|
}
|
|
d /= ds;
|
|
}
|
|
else if ((j1 = -k) != 0) {
|
|
d *= tens[j1 & 0xf];
|
|
for (j = j1 >> 4; j; j >>= 1, i++)
|
|
if (j & 1) {
|
|
ieps++;
|
|
d *= bigtens[i];
|
|
}
|
|
}
|
|
if (k_check && d < 1. && ilim > 0) {
|
|
if (ilim1 <= 0)
|
|
goto fast_failed;
|
|
ilim = ilim1;
|
|
k--;
|
|
d *= 10.;
|
|
ieps++;
|
|
}
|
|
eps = ieps * d + 7.;
|
|
word0(eps) -= (P - 1) * Exp_msk1;
|
|
if (ilim == 0) {
|
|
S = mhi = 0;
|
|
d -= 5.;
|
|
if (d > eps)
|
|
goto one_digit;
|
|
if (d < -eps)
|
|
goto no_digits;
|
|
goto fast_failed;
|
|
}
|
|
if (leftright) {
|
|
/* Use Steele & White method of only
|
|
* generating digits needed.
|
|
*/
|
|
eps = 0.5 / tens[ilim - 1] - eps;
|
|
for (i = 0;;) {
|
|
L = d;
|
|
d -= L;
|
|
*s++ = '0' + (int) L;
|
|
if (d < eps)
|
|
goto ret1;
|
|
if (1. - d < eps)
|
|
goto bump_up;
|
|
if (++i >= ilim)
|
|
break;
|
|
eps *= 10.;
|
|
d *= 10.;
|
|
}
|
|
}
|
|
else {
|
|
/* Generate ilim digits, then fix them up. */
|
|
eps *= tens[ilim - 1];
|
|
for (i = 1;; i++, d *= 10.) {
|
|
L = d;
|
|
d -= L;
|
|
*s++ = '0' + (int) L;
|
|
if (i == ilim) {
|
|
if (d > 0.5 + eps)
|
|
goto bump_up;
|
|
else if (d < 0.5 - eps) {
|
|
while (*--s == '0');
|
|
s++;
|
|
goto ret1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
fast_failed:
|
|
s = s0;
|
|
d = d2;
|
|
k = k0;
|
|
ilim = ilim0;
|
|
}
|
|
|
|
/* Do we have a "small" integer? */
|
|
|
|
if (be >= 0 && k <= Int_max) {
|
|
/* Yes. */
|
|
ds = tens[k];
|
|
if (ndigits < 0 && ilim <= 0) {
|
|
S = mhi = 0;
|
|
if (ilim < 0 || d <= 5 * ds)
|
|
goto no_digits;
|
|
goto one_digit;
|
|
}
|
|
for (i = 1;; i++) {
|
|
L = d / ds;
|
|
d -= L * ds;
|
|
*s++ = '0' + (int) L;
|
|
if (i == ilim) {
|
|
d += d;
|
|
if (d > ds || (d == ds && L & 1)) {
|
|
bump_up:
|
|
while (*--s == '9')
|
|
if (s == s0) {
|
|
k++;
|
|
*s = '0';
|
|
break;
|
|
}
|
|
++*s++;
|
|
}
|
|
break;
|
|
}
|
|
if (!(d *= 10.))
|
|
break;
|
|
}
|
|
goto ret1;
|
|
}
|
|
|
|
m2 = b2;
|
|
m5 = b5;
|
|
mhi = mlo = 0;
|
|
if (leftright) {
|
|
if (mode < 2) {
|
|
i =
|
|
#ifndef Sudden_Underflow
|
|
denorm ? be + (Bias + (P - 1) - 1 + 1) :
|
|
#endif
|
|
#ifdef IBM
|
|
1 + 4 * P - 3 - bbits + ((bbits + be - 1) & 3);
|
|
#else
|
|
1 + P - bbits;
|
|
#endif
|
|
}
|
|
else {
|
|
j = ilim - 1;
|
|
if (m5 >= j)
|
|
m5 -= j;
|
|
else {
|
|
s5 += j -= m5;
|
|
b5 += j;
|
|
m5 = 0;
|
|
}
|
|
if ((i = ilim) < 0) {
|
|
m2 -= i;
|
|
i = 0;
|
|
}
|
|
}
|
|
b2 += i;
|
|
s2 += i;
|
|
mhi = i2b(1);
|
|
}
|
|
if (m2 > 0 && s2 > 0) {
|
|
i = m2 < s2 ? m2 : s2;
|
|
b2 -= i;
|
|
m2 -= i;
|
|
s2 -= i;
|
|
}
|
|
if (b5 > 0) {
|
|
if (leftright) {
|
|
if (m5 > 0) {
|
|
mhi = pow5mult(mhi, m5);
|
|
b1 = mult(mhi, b);
|
|
Bfree(b);
|
|
b = b1;
|
|
}
|
|
if ((j = b5 - m5) != 0)
|
|
b = pow5mult(b, j);
|
|
}
|
|
else
|
|
b = pow5mult(b, b5);
|
|
}
|
|
S = i2b(1);
|
|
if (s5 > 0)
|
|
S = pow5mult(S, s5);
|
|
|
|
/* Check for special case that d is a normalized power of 2. */
|
|
|
|
if (mode < 2) {
|
|
if (!word1(d) && !(word0(d) & Bndry_mask)
|
|
#ifndef Sudden_Underflow
|
|
&& word0(d) & Exp_mask
|
|
#endif
|
|
) {
|
|
/* The special case */
|
|
b2 += Log2P;
|
|
s2 += Log2P;
|
|
spec_case = 1;
|
|
}
|
|
else
|
|
spec_case = 0;
|
|
}
|
|
|
|
/* Arrange for convenient computation of quotients:
|
|
* shift left if necessary so divisor has 4 leading 0 bits.
|
|
*
|
|
* Perhaps we should just compute leading 28 bits of S once
|
|
* and for all and pass them and a shift to quorem, so it
|
|
* can do shifts and ors to compute the numerator for q.
|
|
*/
|
|
if (i = ((s5 ? 32 - hi0bits(S->x[S->wds - 1]) : 1) + s2) & 0xf)
|
|
i = 16 - i;
|
|
if (i > 4) {
|
|
i -= 4;
|
|
b2 += i;
|
|
m2 += i;
|
|
s2 += i;
|
|
}
|
|
else if (i < 4) {
|
|
i += 28;
|
|
b2 += i;
|
|
m2 += i;
|
|
s2 += i;
|
|
}
|
|
if (b2 > 0)
|
|
b = lshift(b, b2);
|
|
if (s2 > 0)
|
|
S = lshift(S, s2);
|
|
if (k_check) {
|
|
if (cmp(b, S) < 0) {
|
|
k--;
|
|
b = multadd(b, 10, 0); /* we botched the k estimate */
|
|
if (leftright)
|
|
mhi = multadd(mhi, 10, 0);
|
|
ilim = ilim1;
|
|
}
|
|
}
|
|
if (ilim <= 0 && mode > 2) {
|
|
if (ilim < 0 || cmp(b, S = multadd(S, 5, 0)) <= 0) {
|
|
/* no digits, fcvt style */
|
|
no_digits:
|
|
k = -1 - ndigits;
|
|
goto ret;
|
|
}
|
|
one_digit:
|
|
*s++ = '1';
|
|
k++;
|
|
goto ret;
|
|
}
|
|
if (leftright) {
|
|
if (m2 > 0)
|
|
mhi = lshift(mhi, m2);
|
|
|
|
/* Compute mlo -- check for special case
|
|
* that d is a normalized power of 2.
|
|
*/
|
|
|
|
mlo = mhi;
|
|
if (spec_case) {
|
|
mhi = Balloc(mhi->k);
|
|
Bcopy(mhi, mlo);
|
|
mhi = lshift(mhi, Log2P);
|
|
}
|
|
|
|
for (i = 1;; i++) {
|
|
dig = quorem(b, S) + '0';
|
|
/* Do we yet have the shortest decimal string
|
|
* that will round to d?
|
|
*/
|
|
j = cmp(b, mlo);
|
|
delta = diff(S, mhi);
|
|
j1 = delta->sign ? 1 : cmp(b, delta);
|
|
Bfree(delta);
|
|
#ifndef ROUND_BIASED
|
|
if (j1 == 0 && !mode && !(word1(d) & 1)) {
|
|
if (dig == '9')
|
|
goto round_9_up;
|
|
if (j > 0)
|
|
dig++;
|
|
*s++ = dig;
|
|
goto ret;
|
|
}
|
|
#endif
|
|
if (j < 0 || (j == 0 && !mode
|
|
#ifndef ROUND_BIASED
|
|
&& !(word1(d) & 1)
|
|
#endif
|
|
)) {
|
|
if (j1 > 0) {
|
|
b = lshift(b, 1);
|
|
j1 = cmp(b, S);
|
|
if ((j1 > 0 || (j1 == 0 && dig & 1))
|
|
&& dig++ == '9')
|
|
goto round_9_up;
|
|
}
|
|
*s++ = dig;
|
|
goto ret;
|
|
}
|
|
if (j1 > 0) {
|
|
if (dig == '9') { /* possible if i == 1 */
|
|
round_9_up:
|
|
*s++ = '9';
|
|
goto roundoff;
|
|
}
|
|
*s++ = dig + 1;
|
|
goto ret;
|
|
}
|
|
*s++ = dig;
|
|
if (i == ilim)
|
|
break;
|
|
b = multadd(b, 10, 0);
|
|
if (mlo == mhi)
|
|
mlo = mhi = multadd(mhi, 10, 0);
|
|
else {
|
|
mlo = multadd(mlo, 10, 0);
|
|
mhi = multadd(mhi, 10, 0);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
for (i = 1;; i++) {
|
|
*s++ = dig = quorem(b, S) + '0';
|
|
if (i >= ilim)
|
|
break;
|
|
b = multadd(b, 10, 0);
|
|
}
|
|
|
|
/* Round off last digit */
|
|
|
|
b = lshift(b, 1);
|
|
j = cmp(b, S);
|
|
if (j > 0 || (j == 0 && dig & 1)) {
|
|
roundoff:
|
|
while (*--s == '9')
|
|
if (s == s0) {
|
|
k++;
|
|
*s++ = '1';
|
|
goto ret;
|
|
}
|
|
++*s++;
|
|
}
|
|
else {
|
|
while (*--s == '0');
|
|
s++;
|
|
}
|
|
ret:
|
|
Bfree(S);
|
|
if (mhi) {
|
|
if (mlo && mlo != mhi)
|
|
Bfree(mlo);
|
|
Bfree(mhi);
|
|
}
|
|
ret1:
|
|
Bfree(b);
|
|
if (s == s0) { /* don't return empty string */
|
|
*s++ = '0';
|
|
k = 0;
|
|
}
|
|
*s = 0;
|
|
*decpt = k + 1;
|
|
if (rve)
|
|
*rve = s;
|
|
return s0;
|
|
}
|
|
|
|
/* end of inclusions from libc/stdlib/netbsd_strtod.c */
|
|
|
|
/* From libc/locale/collcmp.c */
|
|
|
|
/*
|
|
* Copyright (C) 1996 by Andrey A. Chernov, Moscow, Russia.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $FreeBSD: src/lib/libc/locale/collcmp.c,v 1.11.2.1 1999/08/29 14:47:13 peter Exp $
|
|
*/
|
|
|
|
#define ASCII_COMPATIBLE_COLLATE /* see usr.bin/colldef/data */
|
|
|
|
/*
|
|
* Compare two characters converting collate information
|
|
* into ASCII-compatible range, it allows to handle
|
|
* "[a-z]"-type ranges with national characters.
|
|
*/
|
|
|
|
static int ib__collate_range_cmp(c1, c2)
|
|
int c1, c2;
|
|
{
|
|
static char s1[2], s2[2];
|
|
int ret;
|
|
#ifndef ASCII_COMPATIBLE_COLLATE
|
|
int as1, as2, al1, al2;
|
|
#endif
|
|
|
|
c1 &= UCHAR_MAX;
|
|
c2 &= UCHAR_MAX;
|
|
if (c1 == c2)
|
|
return (0);
|
|
|
|
#ifndef ASCII_COMPATIBLE_COLLATE
|
|
as1 = isascii(c1);
|
|
as2 = isascii(c2);
|
|
al1 = isalpha(c1);
|
|
al2 = isalpha(c2);
|
|
|
|
if (as1 || as2 || al1 || al2) {
|
|
if ((as1 && as2) || (!al1 && !al2))
|
|
return (c1 - c2);
|
|
if (al1 && !al2) {
|
|
if (isupper(c1))
|
|
return ('A' - c2);
|
|
else
|
|
return ('a' - c2);
|
|
}
|
|
else if (al2 && !al1) {
|
|
if (isupper(c2))
|
|
return (c1 - 'A');
|
|
else
|
|
return (c1 - 'a');
|
|
}
|
|
}
|
|
#endif
|
|
s1[0] = c1;
|
|
s2[0] = c2;
|
|
if ((ret = strcoll(s1, s2)) != 0)
|
|
return (ret);
|
|
return (c1 - c2);
|
|
}
|
|
|
|
|
|
#endif /* NEED_IB_STDIO */
|