OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /or1k/trunk/linux/uClibc/libc/stdio
    from Rev 1325 to Rev 1765
    Reverse comparison

Rev 1325 → Rev 1765

/tmpfile.c
0,0 → 1,51
/* Copyright (C) 1991, 1993, 1996, 1997, 1998 Free Software Foundation, Inc.
This file is part of the GNU C Library.
 
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
 
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
 
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
 
#include <features.h>
#include <stdio.h>
#include <unistd.h>
#include "../misc/internals/tempname.h"
 
/* This returns a new stream opened on a temporary file (generated
by tmpnam). The file is opened with mode "w+b" (binary read/write).
If we couldn't generate a unique filename or the file couldn't
be opened, NULL is returned. */
FILE * tmpfile (void)
{
char buf[FILENAME_MAX];
int fd;
FILE *f;
 
if (__path_search (buf, FILENAME_MAX, NULL, "tmpf", 0))
return NULL;
fd = __gen_tempname (buf, __GT_FILE);
if (fd < 0)
return NULL;
 
/* Note that this relies on the Unix semantics that
a file is not really removed until it is closed. */
(void) remove (buf);
 
if ((f = fdopen (fd, "w+b")) == NULL)
close (fd);
 
return f;
}
#ifdef __UCLIBC_HAS_LFS__
weak_alias(tmpfile, tmpfile64);
#endif
/popen.c
0,0 → 1,202
/* Copyright (C) 2004 Manuel Novoa III
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
/* Jan 1, 2004
*
* Rewrite popen for SUSv3 compliance.
* Added a list of popen()'d to store pids and use waitpid() in pclose().
* Loop on waitpid() failure due to EINTR as required.
* Close parent's popen()'d FILEs in the {v}fork()'d child.
* Fix failure exit code for failed execve().
*/
 
 
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
 
/* uClinux-2.0 has vfork, but Linux 2.0 doesn't */
#include <sys/syscall.h>
#if ! defined __NR_vfork
# define vfork fork
# define VFORK_LOCK ((void) 0)
# define VFORK_UNLOCK ((void) 0)
#endif
 
#ifdef __UCLIBC_HAS_THREADS__
#include <pthread.h>
static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
# define LOCK __pthread_mutex_lock(&mylock)
# define UNLOCK __pthread_mutex_unlock(&mylock);
#else
# define LOCK ((void) 0)
# define UNLOCK ((void) 0)
#endif
 
#ifndef VFORK_LOCK
# define VFORK_LOCK LOCK
# define VFORK_UNLOCK UNLOCK
#endif
 
/* Temporarily support old stdio code. */
#ifndef __MASK_READING
#define __filedes filedes
#endif
 
struct popen_list_item {
struct popen_list_item *next;
FILE *f;
pid_t pid;
};
 
static struct popen_list_item *popen_list /* = NULL (bss initialized) */;
 
FILE *popen(const char *command, const char *modes)
{
FILE *fp;
struct popen_list_item *pi;
struct popen_list_item *po;
int pipe_fd[2];
int parent_fd;
int child_fd;
int child_writing; /* Doubles as the desired child fildes. */
pid_t pid;
 
child_writing = 0; /* Assume child is writing. */
if (modes[0] != 'w') { /* Parent not writing... */
++child_writing; /* so child must be writing. */
if (modes[0] != 'r') { /* Oops! Parent not reading either! */
__set_errno(EINVAL);
goto RET_NULL;
}
}
 
if (!(pi = malloc(sizeof(struct popen_list_item)))) {
goto RET_NULL;
}
 
if (pipe(pipe_fd)) {
goto FREE_PI;
}
 
child_fd = pipe_fd[child_writing];
parent_fd = pipe_fd[1-child_writing];
 
if (!(fp = fdopen(parent_fd, modes))) {
close(parent_fd);
close(child_fd);
goto FREE_PI;
}
 
VFORK_LOCK;
if ((pid = vfork()) == 0) { /* Child of vfork... */
close(parent_fd);
if (child_fd != child_writing) {
dup2(child_fd, child_writing);
close(child_fd);
}
 
/* SUSv3 requires that any previously popen()'d streams in the
* parent shall be closed in the child. */
for (po = popen_list ; po ; po = po->next) {
close(po->f->__filedes);
}
 
execl("/bin/sh", "sh", "-c", command, (char *)0);
 
/* SUSv3 mandates an exit code of 127 for the child if the
* command interpreter can not be invoked. */
_exit(127);
}
VFORK_UNLOCK;
 
/* We need to close the child filedes whether vfork failed or
* it succeeded and we're in the parent. */
close(child_fd);
 
if (pid > 0) { /* Parent of vfork... */
pi->pid = pid;
pi->f = fp;
LOCK;
pi->next = popen_list;
popen_list = pi;
UNLOCK;
return fp;
}
 
/* If we get here, vfork failed. */
fclose(fp); /* Will close parent_fd. */
 
FREE_PI:
free(pi);
 
RET_NULL:
return NULL;
}
 
int pclose(FILE *stream)
{
struct popen_list_item *p;
int stat;
pid_t pid;
 
/* First, find the list entry corresponding to stream and remove it
* from the list. Set p to the list item (NULL if not found). */
LOCK;
if ((p = popen_list) != NULL) {
if (p->f == stream) {
popen_list = p->next;
} else {
struct popen_list_item *t;
do {
t = p;
if (!(p = t->next)) {
__set_errno(EINVAL); /* Not required by SUSv3. */
break;
}
if (p->f == stream) {
t->next = p->next;
break;
}
} while (1);
}
}
UNLOCK;
 
if (p) {
pid = p->pid; /* Save the pid we need */
free(p); /* and free the list item. */
 
fclose(stream); /* The SUSv3 example code ignores the return. */
 
/* SUSv3 specificly requires that pclose not return before the child
* terminates, in order to disallow pclose from returning on EINTR. */
do {
if (waitpid(pid, &stat, 0) >= 0) {
return stat;
}
if (errno != EINTR) {
break;
}
} while (1);
}
 
return -1;
}
/tempnam.c
0,0 → 1,43
/* Copyright (C) 1991,1993,1996-1999,2000 Free Software Foundation, Inc.
This file is part of the GNU C Library.
 
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
 
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
 
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
 
#include <stdio.h>
#include <string.h>
#include "../misc/internals/tempname.h"
 
/* Generate a unique temporary filename using up to five characters of PFX
if it is not NULL. The directory to put this file in is searched for
as follows: First the environment variable "TMPDIR" is checked.
If it contains the name of a writable directory, that directory is used.
If not and if DIR is not NULL, that value is checked. If that fails,
P_tmpdir is tried and finally "/tmp". The storage for the filename
is allocated by `malloc'. */
char *
tempnam (const char *dir, const char *pfx)
{
char buf[FILENAME_MAX];
 
if (__path_search (buf, FILENAME_MAX, dir, pfx, 1))
return NULL;
 
if (__gen_tempname (buf, __GT_NOCREATE))
return NULL;
 
return strdup (buf);
}
 
/stdio.c
0,0 → 1,3677
/* Copyright (C) 2002,2003,2004 Manuel Novoa III
* My stdio library for linux and (soon) elks.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
/* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION!
*
* This code is currently under development. Also, I plan to port
* it to elks which is a 16-bit environment with a fairly limited
* compiler. Therefore, please refrain from modifying this code
* and, instead, pass any bug-fixes, etc. to me. Thanks. Manuel
*
* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! */
 
/* 8-05-2002
* Changed fflush() behavior to no-op for r/w streams in read-mode.
* This falls under undefined behavior wrt ANSI/ISO C99, but
* SUSv3 seems to treat it as a no-op and it occurs in some apps.
* Fixed a problem with _stdio_fwrite() not checking for underlying
* write() failures.
* Fixed both _stdio_fwrite() and _stdio_fread() to make sure that
* the putc and getc macros were disabled if the stream was in
* and error state.
* The above changes should take care of a problem initially reported
* by "Steven J. Hill" <sjhill@realitydiluted.com>.
*
* 8-25-2002
* Changed fclose behavior when custom streams were enabled. Previously,
* the cookie pointer was set to NULL as a debugging aid. However,
* some of the perl 5.8 test rely on being able to close stderr and
* still try writing to it. So now, the cookie pointer and handler
* function pointers are set to that it is a "normal" file with a
* file descriptor of -1. Note: The cookie pointer is reset to NULL
* if the FILE struct is free'd by fclose.
*
* Nov 21, 2002
* Added internal function _wstdio_fwrite.
* Jan 3, 2003
* Fixed a bug in _wstdio_fwrite.
*
* Jan 22, 2003
* Fixed a bug related file position in append mode. _stdio_fwrite now
* seeks to the end of the stream when append mode is set and we are
* transitioning to write mode, so that subsequent ftell() return
* values are correct.
* Also fix _stdio_fopen to support fdopen() with append specified when
* the underlying file didn't have O_APPEND set. It now sets the
* O_APPEND flag as recommended by SUSv3 and is done by glibc.
*
* May 15, 2003
* Modify __stdio_fread to deal with fake streams used by *sscanf.
* Set EOF to end of buffer when fmemopen used on a readonly stream.
* Note: I really need to run some tests on this to see what the
* glibc code does in each case.
*
* Sept 21, 2003
* Modify _stdio_READ to conform with C99, as stdio input behavior upon
* encountering EOF changed with Defect Report #141. In the current
* standard, the stream's EOF indicator is "sticky". Once it is set,
* all further input from the stream should fail until the application
* explicitly clears the EOF indicator (clearerr(), file positioning),
* even if more data becomes available.
* Fixed a bug in fgets. Wasn't checking for read errors.
* Minor thread locking optimizations to avoid some unnecessary locking.
* Remove the explicit calls to __builtin_* funcs, as we really need to
* implement a more general solution.
*
* Nov 17, 2003
* Fix the return value for fputs when passed an empty string.
*
* Jan 1, 2004
* Fix __freadable and __fwritable... were using '~' instead of '!'. (ugh)
* Fix (hopefully) a potential problem with failed freopen() calls. The
* fix isn't tested since I've been working on the replacement stdio
* core code which will go in after the next release.
*/
 
/* Before we include anything, convert L_ctermid to L_ctermid_function
* and undef L_ctermid if defined. This is necessary as L_ctermid is
* a SUSv3 standard macro defined in stdio.h. */
#ifdef L_ctermid
#define L_ctermid_function
#undef L_ctermid
#endif
 
#define _ISOC99_SOURCE /* for ULLONG primarily... */
#define _GNU_SOURCE
#define _STDIO_UTILITY /* for _stdio_fdout and _uintmaxtostr. */
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdint.h>
#include <stdarg.h>
#include <errno.h>
#include <assert.h>
#include <stdio_ext.h>
#include <unistd.h>
#include <fcntl.h>
 
#ifndef O_LARGEFILE /* uClibc undefines this if no large file support. */
#ifdef __STDIO_LARGE_FILES
#error missing define for O_LARGEFILE!
#endif
#define O_LARGEFILE 0
#endif
 
/**********************************************************************/
/* First deal with some build issues... */
 
#ifndef __STDIO_THREADSAFE
/* Just build empty object files if any of these were defined. */
/* Note though that we do keep the various *_unlocked names as aliases. */
#undef L___fsetlocking
#undef L___flockfile
#undef L___ftrylockfile
#undef L___funlockfile
#endif
 
#ifndef __STDIO_LARGE_FILES
/* Just build empty object files if any of these were defined. */
#undef L_fopen64
#undef L_freopen64
#undef L_ftello64
#undef L_fseeko64
#undef L_fsetpos64
#undef L_fgetpos64
#endif
 
/**********************************************************************/
 
#ifndef __STDIO_THREADSAFE
 
#if defined(__BCC__) && 0
#define UNLOCKED_STREAM(RETURNTYPE,NAME,PARAMS,ARGS,STREAM) \
asm(".text\nexport _" "NAME" "_unlocked\n_" "NAME" "_unlocked = _" "NAME"); \
RETURNTYPE NAME PARAMS
#else
#define UNLOCKED_STREAM(RETURNTYPE,NAME,PARAMS,ARGS,STREAM) \
strong_alias(NAME,NAME##_unlocked) \
RETURNTYPE NAME PARAMS
#endif
 
#define UNLOCKED(RETURNTYPE,NAME,PARAMS,ARGS) \
UNLOCKED_STREAM(RETURNTYPE,NAME,PARAMS,ARGS,stream)
 
#if defined(__BCC__) && 0
#define UNLOCKED_VOID_RETURN(NAME,PARAMS,ARGS) \
asm(".text\nexport _" "NAME" "_unlocked\n_" "NAME" "_unlocked = _" "NAME"); \
void NAME PARAMS
#else
#define UNLOCKED_VOID_RETURN(NAME,PARAMS,ARGS) \
strong_alias(NAME,NAME##_unlocked) \
void NAME PARAMS
#endif
 
#define __STDIO_THREADLOCK_OPENLIST
#define __STDIO_THREADUNLOCK_OPENLIST
 
#else /* __STDIO_THREADSAFE */
 
#include <pthread.h>
 
#define UNLOCKED_STREAM(RETURNTYPE,NAME,PARAMS,ARGS,STREAM) \
RETURNTYPE NAME PARAMS \
{ \
RETURNTYPE retval; \
__STDIO_THREADLOCK(STREAM); \
retval = NAME##_unlocked ARGS ; \
__STDIO_THREADUNLOCK(STREAM); \
return retval; \
} \
RETURNTYPE NAME##_unlocked PARAMS
 
#define UNLOCKED(RETURNTYPE,NAME,PARAMS,ARGS) \
UNLOCKED_STREAM(RETURNTYPE,NAME,PARAMS,ARGS,stream)
 
#define UNLOCKED_VOID_RETURN(NAME,PARAMS,ARGS) \
void NAME PARAMS \
{ \
__STDIO_THREADLOCK(stream); \
NAME##_unlocked ARGS ; \
__STDIO_THREADUNLOCK(stream); \
} \
void NAME##_unlocked PARAMS
 
#define __STDIO_THREADLOCK_OPENLIST \
__pthread_mutex_lock(&_stdio_openlist_lock)
 
#define __STDIO_THREADUNLOCK_OPENLIST \
__pthread_mutex_unlock(&_stdio_openlist_lock)
 
#define __STDIO_THREADTRYLOCK_OPENLIST \
__pthread_mutex_trylock(&_stdio_openlist_lock)
 
#endif /* __STDIO_THREADSAFE */
 
/**********************************************************************/
 
#ifdef __STDIO_WIDE
#define __STDIO_FILE_INIT_UNGOT { 0, 0 }, { 0, 0 },
#else
#define __STDIO_FILE_INIT_UNGOT { 0, 0 },
#endif
 
#ifdef __STDIO_GETC_MACRO
#define __STDIO_FILE_INIT_BUFGETC(x) x,
#else
#define __STDIO_FILE_INIT_BUFGETC(x)
#endif
 
#ifdef __STDIO_PUTC_MACRO
#define __STDIO_FILE_INIT_BUFPUTC(x) x,
#else
#define __STDIO_FILE_INIT_BUFPUTC(x)
#endif
 
#if defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS)
#define __STDIO_FILE_INIT_NEXT(next) (next),
#else /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */
#define __STDIO_FILE_INIT_NEXT(next)
#endif /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */
 
#ifdef __STDIO_BUFFERS
#define __STDIO_FILE_INIT_BUFFERS(buf,bufsize) \
(buf), (buf)+(bufsize), (buf), (buf),
#else
#define __STDIO_FILE_INIT_BUFFERS(buf,bufsize)
#endif
 
#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
#define __STDIO_FILE_INIT_CUSTOM_STREAM(stream) \
&((stream).filedes), { _cs_read, _cs_write, NULL, _cs_close },
#else
#define __STDIO_FILE_INIT_CUSTOM_STREAM(stream)
#endif
 
#ifdef __STDIO_MBSTATE
#define __STDIO_FILE_INIT_MBSTATE \
{ 0, 0 },
#else
#define __STDIO_FILE_INIT_MBSTATE
#endif
 
 
#ifdef __STDIO_THREADSAFE
#define __STDIO_FILE_INIT_THREADSAFE \
0, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,
#else
#define __STDIO_FILE_INIT_THREADSAFE
#endif
 
#define __STDIO_INIT_FILE_STRUCT(stream, flags, filedes, next, buf, bufsize) \
{ (flags), \
__STDIO_FILE_INIT_UNGOT \
(filedes), \
__STDIO_FILE_INIT_NEXT(next) \
__STDIO_FILE_INIT_BUFFERS(buf,bufsize) \
__STDIO_FILE_INIT_BUFGETC((buf)) \
__STDIO_FILE_INIT_BUFPUTC((buf)) \
__STDIO_FILE_INIT_CUSTOM_STREAM(stream) \
__STDIO_FILE_INIT_MBSTATE \
__STDIO_FILE_INIT_THREADSAFE \
} /* TODO: mbstate and builtin buf */
 
#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
 
/* TODO -- what does glibc do for undefined funcs? errno set? */
#define __READ(STREAMPTR,BUF,SIZE) \
((((STREAMPTR)->gcs.read) == NULL) ? -1 : \
(((STREAMPTR)->gcs.read)((STREAMPTR)->cookie,(BUF),(SIZE))))
#define __WRITE(STREAMPTR,BUF,SIZE) \
((((STREAMPTR)->gcs.write) == NULL) ? -1 : \
(((STREAMPTR)->gcs.write)((STREAMPTR)->cookie,(BUF),(SIZE))))
#define __CLOSE(STREAMPTR) \
((((STREAMPTR)->gcs.close) == NULL) ? 0 : \
(((STREAMPTR)->gcs.close)((STREAMPTR)->cookie)))
 
#else /* __STDIO_GLIBC_CUSTOM_STREAMS */
 
#define __READ(STREAMPTR,BUF,SIZE) \
(read((STREAMPTR)->filedes,(BUF),(SIZE)))
#define __WRITE(STREAMPTR,BUF,SIZE) \
(write((STREAMPTR)->filedes,(BUF),(SIZE)))
#define __CLOSE(STREAMPTR) \
(close((STREAMPTR)->filedes))
 
#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */
 
/**********************************************************************/
/* POSIX functions */
/**********************************************************************/
#ifdef L_getw
 
/* SUSv2 Legacy function -- need not be reentrant. */
 
int getw(FILE *stream)
{
int aw[1];
 
#ifdef __STDIO_WIDE
 
return (fread_unlocked((void *)aw, sizeof(int), 1, stream) > 0)
? (*aw) : EOF;
 
#else /* __STDIO_WIDE */
 
return (_stdio_fread((unsigned char *)(aw), sizeof(int), stream)
== sizeof(int)) ? (*aw) : EOF;
 
#endif /* __STDIO_WIDE */
}
 
#endif
/**********************************************************************/
#ifdef L_putw
 
/* SUSv2 Legacy function -- need not be reentrant. */
 
int putw(int w, FILE *stream)
{
int aw[1];
 
*aw = w; /* In case 'w' is in a register... */
 
#ifdef __STDIO_WIDE
 
return (fwrite_unlocked((void *)aw, sizeof(int), 1, stream) == 1)
? 0 : EOF;
 
#else /* __STDIO_WIDE */
 
return (_stdio_fwrite((unsigned char *)aw, sizeof(int), stream)
== sizeof(int)) ? 0 : EOF;
 
#endif /* __STDIO_WIDE */
}
 
#endif
/**********************************************************************/
#ifdef L_fileno
 
/* Reentrancy handled by UNLOCKED() macro. */
 
UNLOCKED(int,fileno,(register FILE *stream),(stream))
{
#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
return ( (stream && (stream->cookie == &(stream->filedes)) && (stream->filedes >= 0))
? stream->filedes
: (__set_errno(EBADF), -1) );
#else /* __STDIO_GLIBC_CUSTOM_STREAMS */
return ((stream && stream->filedes >= 0)) ? stream->filedes : (__set_errno(EBADF), -1);
#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */
}
 
#endif
/**********************************************************************/
#ifdef L_fdopen
 
/* No reentrancy issues. */
 
FILE *fdopen(int filedes, const char *mode)
{
register char *cur_mode; /* TODO -- use intptr_t?? (also fopencookie) */
 
return (((int)(cur_mode = (char *) fcntl(filedes, F_GETFL))) != -1)
? _stdio_fopen(cur_mode, mode, NULL, filedes)
: NULL;
}
 
#endif
/**********************************************************************/
#ifdef L_fopen64
 
/* No reentrancy issues. */
 
FILE *fopen64(const char * __restrict filename, const char * __restrict mode)
{
return _stdio_fopen(filename, mode, NULL, -2);
}
 
#endif
/**********************************************************************/
#ifdef L_ctermid_function
 
/* Not required to be reentrant. */
 
char *ctermid(register char *s)
{
static char sbuf[L_ctermid];
 
#ifdef __BCC__
/* Currently elks doesn't support /dev/tty. */
if (!s) {
s = sbuf;
}
*s = 0;
 
return s;
#else
/* glibc always returns /dev/tty for linux. */
return strcpy((s ? s : sbuf), "/dev/tty");
#endif
}
 
#endif
/**********************************************************************/
/* BSD functions */
/**********************************************************************/
#ifdef L_setbuffer
 
/* No reentrancy issues. */
 
void setbuffer(FILE * __restrict stream, register char * __restrict buf,
size_t size)
{
#ifdef __STDIO_BUFFERS
setvbuf(stream, buf, (buf ? _IOFBF : _IONBF), size);
#else /* __STDIO_BUFFERS */
/* Nothing to do. */
#endif /* __STDIO_BUFFERS */
}
 
#endif
/**********************************************************************/
#ifdef L_setlinebuf
 
/* No reentrancy issues. */
 
void setlinebuf(FILE * __restrict stream)
{
#ifdef __STDIO_BUFFERS
setvbuf(stream, NULL, _IOLBF, (size_t) 0);
#else /* __STDIO_BUFFERS */
/* Nothing to do. */
#endif /* __STDIO_BUFFERS */
}
 
#endif
/**********************************************************************/
/* GLIBC functions */
/**********************************************************************/
#ifdef L_fcloseall
 
/* NOTE: GLIBC difference!!! -- fcloseall
* According to the info pages, glibc actually fclose()s all open files.
* Apparently, glibc's new version only fflush()s and unbuffers all
* writing streams to cope with unordered destruction of c++ static
* objects. Here we implement the old behavior as default.
*/
 
/* Not reentrant. */
 
int fcloseall (void)
{
#if defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS)
register FILE *stream;
int rv;
 
_stdio_term(); /* Let _stdio_term() do all the work. */
 
rv = 0;
for (stream = _stdio_openlist ; stream ; stream = stream->nextopen) {
if (stream->modeflags & (__FLAG_WRITING|__FLAG_ERROR)) {
/* TODO -- is this correct? Maybe ferror set before flush...
* could check if pending writable but what if term unbuffers?
* in that case, could clear error flag... */
rv = EOF; /* Only care about failed writes. */
}
}
 
/* Make sure _stdio_term() does nothing on exit. */
_stdio_openlist = NULL;
 
return rv;
#else /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */
 
return 0;
 
#endif /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */
}
 
#endif
/**********************************************************************/
#ifdef L_fmemopen
#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
 
typedef struct {
size_t pos;
size_t len;
size_t eof;
int dynbuf;
unsigned char *buf;
FILE *fp;
} __fmo_cookie;
 
#define COOKIE ((__fmo_cookie *) cookie)
 
static ssize_t fmo_read(register void *cookie, char *buf, size_t bufsize)
{
size_t count = COOKIE->len - COOKIE->pos;
 
/* Note: 0 < bufsize < SSIZE_MAX because of _stdio_READ. */
if (!count) { /* EOF! */
return 0;
}
 
if (bufsize > count) {
bufsize = count;
}
 
#if 1 /* TODO - choose code option */
memcpy(buf, COOKIE->buf + COOKIE->pos, bufsize);
COOKIE->pos += bufsize;
#else
{
register char *p = COOKIE->buf + COOKIE->pos;
 
count = bufsize;
while (count) {
*buf++ = *p++;
--count;
}
COOKIE->pos += bufsize;
}
#endif
 
return bufsize;
}
 
static ssize_t fmo_write(register void *cookie, const char *buf, size_t bufsize)
{
size_t count;
 
/* Note: bufsize < SSIZE_MAX because of _stdio_WRITE. */
 
/* If appending, need to seek to end of file!!!! */
if (COOKIE->fp->modeflags & __FLAG_APPEND) {
COOKIE->pos = COOKIE->eof;
}
 
count = COOKIE->len - COOKIE->pos;
 
if (bufsize > count) {
bufsize = count;
if (count == 0) { /* We're at the end of the buffer... */
__set_errno(EFBIG);
return -1;
}
}
 
#if 1 /* TODO - choose code option */
memcpy(COOKIE->buf + COOKIE->pos, buf, bufsize);
COOKIE->pos += bufsize;
 
if (COOKIE->pos > COOKIE->eof) {
COOKIE->eof = COOKIE->pos;
if (bufsize < count) { /* New eof and still room in buffer? */
*(COOKIE->buf + COOKIE->pos) = 0;
}
}
 
#else
{
register char *p = COOKIE->buf + COOKIE->pos;
size_t i = bufsize;
 
while (i > 0) {
*p++ = *buf++;
--i;
}
COOKIE->pos += bufsize;
 
if (COOKIE->pos > COOKIE->eof) {
COOKIE->eof = COOKIE->pos;
if (bufsize < count) { /* New eof and still room in buffer? */
*p = 0;
}
}
}
 
#endif
 
return bufsize;
}
 
/* glibc doesn't allow seeking, but it has in-buffer seeks... we don't. */
static int fmo_seek(register void *cookie, __offmax_t *pos, int whence)
{
__offmax_t p = *pos;
 
/* Note: fseek already checks that whence is legal, so don't check here
* unless debugging. */
assert(((unsigned int) whence) <= 2);
 
if (whence != SEEK_SET) {
p += (whence == SEEK_CUR) ? COOKIE->pos : /* SEEK_END */ COOKIE->eof;
}
 
/* Note: glibc only allows seeking in the buffer. We'll actually restrict
* to the data. */
/* Check for offset < 0, offset > eof, or offset overflow... */
if (((uintmax_t) p) > COOKIE->eof) {
return -1;
}
 
COOKIE->pos = *pos = p;
return 0;
}
 
static int fmo_close(register void *cookie)
{
if (COOKIE->dynbuf) {
free(COOKIE->buf);
}
free(cookie);
return 0;
}
 
#undef COOKIE
 
static const cookie_io_functions_t _fmo_io_funcs = {
fmo_read, fmo_write, fmo_seek, fmo_close
};
 
/* TODO: If we have buffers enabled, it might be worthwile to add a pointer
* to the FILE in the cookie and have read, write, and seek operate directly
* on the buffer itself (ie replace the FILE buffer with the cookie buffer
* and update FILE bufstart, etc. whenever we seek). */
 
FILE *fmemopen(void *s, size_t len, const char *modes)
{
FILE *fp;
register __fmo_cookie *cookie;
size_t i;
 
if ((cookie = malloc(sizeof(__fmo_cookie))) != NULL) {
cookie->len = len;
cookie->eof = cookie->pos = 0; /* pos and eof adjusted below. */
cookie->dynbuf = 0;
if (((cookie->buf = s) == NULL) && (len > 0)) {
if ((cookie->buf = malloc(len)) == NULL) {
goto EXIT_cookie;
}
cookie->dynbuf = 1;
*cookie->buf = 0; /* If we're appending, treat as empty file. */
}
#ifndef __BCC__
fp = fopencookie(cookie, modes, _fmo_io_funcs);
#else
fp = fopencookie(cookie, modes, &_fmo_io_funcs);
#endif
/* Note: We don't need to worry about locking fp in the thread case
* as the only possible access would be a close or flush with
* nothing currently in the FILE's write buffer. */
 
if (fp != NULL) {
cookie->fp = fp;
if (fp->modeflags & __FLAG_READONLY) {
cookie->eof = len;
}
if ((fp->modeflags & __FLAG_APPEND) && (len > 0)) {
for (i = 0 ; i < len ; i++) {
if (cookie->buf[i] == 0) {
break;
}
}
cookie->eof = cookie->pos = i; /* Adjust eof and pos. */
}
return fp;
}
}
 
if (!s) {
free(cookie->buf);
}
EXIT_cookie:
free(cookie);
 
return NULL;
}
 
#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */
#endif
/**********************************************************************/
#ifdef L_open_memstream
#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
 
#define COOKIE ((__oms_cookie *) cookie)
 
typedef struct {
char *buf;
size_t len;
size_t pos;
size_t eof;
char **bufloc;
size_t *sizeloc;
} __oms_cookie;
 
/* Nothing to do here, as memstreams are write-only. */
/* static ssize_t oms_read(void *cookie, char *buf, size_t bufsize) */
/* { */
/* } */
 
static ssize_t oms_write(register void *cookie, const char *buf, size_t bufsize)
{
register char *newbuf;
size_t count;
 
/* Note: we already know bufsize < SSIZE_MAX... */
 
count = COOKIE->len - COOKIE->pos - 1;
assert(COOKIE->pos < COOKIE->len); /* Always nul-terminate! */
 
if (bufsize > count) {
newbuf = realloc(COOKIE->buf, COOKIE->len + bufsize - count);
if (newbuf) {
*COOKIE->bufloc = COOKIE->buf = newbuf;
COOKIE->len += (bufsize - count);
} else {
bufsize = count;
if (count == 0) {
__set_errno(EFBIG); /* TODO: check glibc errno setting... */
return -1;
}
}
}
 
memcpy(COOKIE->buf + COOKIE->pos, buf, bufsize);
COOKIE->pos += bufsize;
 
if (COOKIE->pos > COOKIE->eof) {
*COOKIE->sizeloc = COOKIE->eof = COOKIE->pos;
COOKIE->buf[COOKIE->eof] = 0; /* Need to nul-terminate. */
}
 
return bufsize;
}
 
static int oms_seek(register void *cookie, __offmax_t *pos, int whence)
{
__offmax_t p = *pos;
register char *buf;
size_t leastlen;
 
/* Note: fseek already checks that whence is legal, so don't check here
* unless debugging. */
assert(((unsigned int) whence) <= 2);
 
if (whence != SEEK_SET) {
p += (whence == SEEK_CUR) ? COOKIE->pos : /* SEEK_END */ COOKIE->eof;
}
 
/* Note: glibc only allows seeking in the buffer. We'll actually restrict
* to the data. */
/* Check for offset < 0, offset >= too big (need nul), or overflow... */
if (((uintmax_t) p) >= SIZE_MAX - 1) {
return -1;
}
 
leastlen = ((size_t) p) + 1; /* New pos + 1 for nul if necessary. */
 
if (leastlen >= COOKIE->len) { /* Need to grow buffer... */
buf = realloc(COOKIE->buf, leastlen);
if (buf) {
*COOKIE->bufloc = COOKIE->buf = buf;
COOKIE->len = leastlen;
memset(buf + COOKIE->eof, leastlen - COOKIE->eof, 0); /* 0-fill */
} else {
/* TODO: check glibc errno setting... */
return -1;
}
}
 
*pos = COOKIE->pos = --leastlen;
 
if (leastlen > COOKIE->eof) {
memset(COOKIE->buf + COOKIE->eof, leastlen - COOKIE->eof, 0);
*COOKIE->sizeloc = COOKIE->eof;
}
 
return 0;
}
 
static int oms_close(void *cookie)
{
free(cookie);
return 0;
}
 
#undef COOKIE
 
static const cookie_io_functions_t _oms_io_funcs = {
NULL, oms_write, oms_seek, oms_close
};
 
/* TODO: If we have buffers enabled, it might be worthwile to add a pointer
* to the FILE in the cookie and operate directly on the buffer itself
* (ie replace the FILE buffer with the cookie buffer and update FILE bufstart,
* etc. whenever we seek). */
 
FILE *open_memstream(char **__restrict bufloc, size_t *__restrict sizeloc)
{
register __oms_cookie *cookie;
register FILE *fp;
 
if ((cookie = malloc(sizeof(__oms_cookie))) != NULL) {
if ((cookie->buf = malloc(cookie->len = BUFSIZ)) == NULL) {
goto EXIT_cookie;
}
*cookie->buf = 0; /* Set nul terminator for buffer. */
*(cookie->bufloc = bufloc) = cookie->buf;
*(cookie->sizeloc = sizeloc) = cookie->eof = cookie->pos = 0;
#ifndef __BCC__
fp = fopencookie(cookie, "w", _oms_io_funcs);
#else
fp = fopencookie(cookie, "w", &_oms_io_funcs);
#endif
/* Note: We don't need to worry about locking fp in the thread case
* as the only possible access would be a close or flush with
* nothing currently in the FILE's write buffer. */
 
if (fp != NULL) {
return fp;
}
}
 
if (cookie->buf != NULL) {
free(cookie->buf);
}
EXIT_cookie:
free(cookie);
 
return NULL;
}
 
#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */
#endif
/**********************************************************************/
#ifdef L_fopencookie
#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
 
/* NOTE: GLIBC difference!!! -- fopencookie
* According to the info pages, glibc allows seeking within buffers even if
* no seek function is supplied. We don't. */
 
/* NOTE: GLIBC difference!!! -- fopencookie
* When compiled without large file support, the offset pointer for the
* cookie_seek function is off_t * and not off64_t * as for glibc. */
 
/* Currently no real reentrancy issues other than a possible double close(). */
 
#ifndef __BCC__
 
FILE *fopencookie(void * __restrict cookie, const char * __restrict mode,
cookie_io_functions_t io_functions)
{
FILE *stream;
 
/* Fake an fdopen guaranteed to pass the _stdio_fopen basic agreement
* check without an fcntl call. */
if ((stream = _stdio_fopen(((char *)(INT_MAX-1)),
mode, NULL, INT_MAX)) /* TODO: use intptr_t? */
!= NULL
) {
stream->filedes = -1;
stream->gcs = io_functions;
stream->cookie = cookie;
}
 
#if !defined(__STDIO_BUFFERS) && !defined(__STDIO_THREADSAFE)
/* I we don't have buffers or threads, we only need to worry about
* custom streams on the open list, as no flushing is necessary and
* no locking of possible underlying normal streams need be done.
* We do need to explicitly close custom streams on termination of stdio,
* and we need to lock the list as it can be modified by fclose(). */
__STDIO_THREADLOCK_OPENLIST;
stream->nextopen = _stdio_openlist; /* New files are inserted at */
_stdio_openlist = stream; /* the head of the list. */
__STDIO_THREADUNLOCK_OPENLIST;
#endif /* !defined(__STDIO_BUFFERS) && !defined(__STDIO_THREADSAFE) */
 
return stream;
}
 
#else /* __BCC__ */
 
/* NOTE: GLIBC difference!!! -- fopencookie (bcc only)
* Since bcc doesn't support passing of structs, we define fopencookie as a
* macro in terms of _fopencookie which takes a struct * for the io functions
* instead.
*/
 
FILE *_fopencookie(void * __restrict cookie, const char * __restrict mode,
register cookie_io_functions_t *io_functions)
{
register FILE *stream;
 
/* Fake an fdopen guaranteed to pass the _stdio_fopen basic agreement
* check without an fcntl call. */
if ((stream = _stdio_fopen(((char *)(INT_MAX-1)),
mode, NULL, INT_MAX)) /* TODO: use intptr_t? */
!= NULL
) {
stream->filedes = -1;
stream->gcs.read = io_functions->read;
stream->gcs.write = io_functions->write;
stream->gcs.seek = io_functions->seek;
stream->gcs.close = io_functions->close;
stream->cookie = cookie;
}
 
#if !defined(__STDIO_BUFFERS) && !defined(__STDIO_THREADSAFE)
/* I we don't have buffers or threads, we only need to worry about
* custom streams on the open list, as no flushing is necessary and
* no locking of possible underlying normal streams need be done.
* We do need to explicitly close custom streams on termination of stdio,
* and we need to lock the list as it can be modified by fclose(). */
__STDIO_THREADLOCK_OPENLIST;
stream->nextopen = _stdio_openlist; /* New files are inserted at */
_stdio_openlist = stream; /* the head of the list. */
__STDIO_THREADUNLOCK_OPENLIST;
#endif /* !defined(__STDIO_BUFFERS) && !defined(__STDIO_THREADSAFE) */
 
return stream;
}
 
#endif /* __BCC__ */
 
#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */
#endif
/**********************************************************************/
#ifdef L___fbufsize
 
/* Not reentrant. */
 
size_t __fbufsize(register FILE * __restrict stream)
{
#ifdef __STDIO_BUFFERS
return (stream->modeflags & __FLAG_NBF)
? 0 : (stream->bufend - stream->bufstart);
#else /* __STDIO_BUFFERS */
return 0;
#endif /* __STDIO_BUFFERS */
}
 
#endif
/**********************************************************************/
#ifdef L___freading
 
/* No reentrancy issues. */
 
int __freading(FILE * __restrict stream)
{
return stream->modeflags & (__FLAG_READING|__FLAG_READONLY);
}
 
#endif
/**********************************************************************/
#ifdef L___fwriting
 
/* No reentrancy issues. */
 
int __fwriting(FILE * __restrict stream)
{
return stream->modeflags & (__FLAG_WRITING|__FLAG_WRITEONLY);
}
 
#endif
/**********************************************************************/
#ifdef L___freadable
 
/* No reentrancy issues. */
 
int __freadable(FILE * __restrict stream)
{
return !(stream->modeflags & __FLAG_WRITEONLY);
}
 
#endif
/**********************************************************************/
#ifdef L___fwritable
 
/* No reentrancy issues. */
 
int __fwritable(FILE * __restrict stream)
{
return !(stream->modeflags & __FLAG_READONLY);
}
 
#endif
/**********************************************************************/
#ifdef L___flbf
 
/* No reentrancy issues. */
 
int __flbf(FILE * __restrict stream)
{
#ifdef __STDIO_BUFFERS
return (stream->modeflags & __FLAG_LBF);
#else /* __STDIO_BUFFERS */
/* TODO -- Even though there is no buffer, return flag setting? */
return __FLAG_NBF;
#endif /* __STDIO_BUFFERS */
}
 
#endif
/**********************************************************************/
#ifdef L___fpurge
 
/* Not reentrant. */
 
void __fpurge(register FILE * __restrict stream)
{
#ifdef __STDIO_BUFFERS
#ifdef __STDIO_PUTC_MACRO
stream->bufputc = /* Must disable putc. */
#endif /* __STDIO_PUTC_MACRO */
#ifdef __STDIO_GETC_MACRO
stream->bufgetc = /* Must disable getc. */
#endif
stream->bufpos = stream->bufread = stream->bufstart; /* Reset pointers. */
#endif /* __STDIO_BUFFERS */
/* Reset r/w flags and clear ungots. */
stream->modeflags &= ~(__FLAG_READING|__FLAG_WRITING|__MASK_UNGOT);
}
 
#endif
/**********************************************************************/
#ifdef L___fpending
 
/* Not reentrant. */
 
#ifdef __STDIO_WIDE
#warning Unlike the glibc version, this __fpending returns bytes in buffer for wide streams too!
 
link_warning(__fpending, "This version of __fpending returns bytes remaining in buffer for both narrow and wide streams. glibc's version returns wide chars in buffer for the wide stream case.")
 
#endif /* __STDIO_WIDE */
 
size_t __fpending(register FILE * __restrict stream)
{
#ifdef __STDIO_BUFFERS
/* TODO -- should we check this? should we set errno? just assert? */
return (stream->modeflags & (__FLAG_READING|__FLAG_READONLY))
? 0 : (stream->bufpos - stream->bufstart);
#else /* __STDIO_BUFFERS */
return 0;
#endif /* __STDIO_BUFFERS */
}
 
#endif
/**********************************************************************/
#ifdef L__flushlbf
 
/* No reentrancy issues. */
 
void _flushlbf(void)
{
#if defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS)
fflush((FILE *) &_stdio_openlist); /* Uses an implementation hack!!! */
#else /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */
/* Nothing to do. */
#endif /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */
}
 
#endif
/**********************************************************************/
#ifdef L___fsetlocking
 
/* NOT threadsafe!!! (I don't think glibc's is either)
*
* This interacts badly with internal locking/unlocking. If you use this routine,
* make sure the file isn't being accessed by any other threads. Typical use would
* be to change to user locking immediately after opening the stream.
*/
 
#ifdef __UCLIBC_MJN3_ONLY__
link_warning(__fsetlocking, "Oddly enough, __fsetlocking() is NOT threadsafe.")
#endif
 
int __fsetlocking(FILE *stream, int locking_mode)
{
#ifdef __STDIO_THREADSAFE
int old_mode;
#endif
 
assert((FSETLOCKING_QUERY == 0) && (FSETLOCKING_INTERNAL == 1)
&& (FSETLOCKING_BYCALLER == 2));
 
assert(((unsigned int) locking_mode) <= 2);
 
#ifdef __STDIO_THREADSAFE
old_mode = stream->user_locking;
 
assert(((unsigned int) old_mode) <= 1); /* Must be 0 (internal) or 1 (user). */
 
if (locking_mode != FSETLOCKING_QUERY) {
/* In case we're not debugging, treat any unknown as a request to
* set internal locking, in order to match glibc behavior. */
stream->user_locking = (locking_mode == FSETLOCKING_BYCALLER);
}
 
return 2 - old_mode;
#else
return FSETLOCKING_BYCALLER; /* Well, without thread support... */
#endif
}
 
#endif
/**********************************************************************/
#ifdef L_flockfile
 
void flockfile(FILE *stream)
{
#ifdef __STDIO_THREADSAFE
__pthread_mutex_lock(&stream->lock);
#endif
}
 
#endif
/**********************************************************************/
#ifdef L_ftrylockfile
 
int ftrylockfile(FILE *stream)
{
#ifdef __STDIO_THREADSAFE
return __pthread_mutex_trylock(&stream->lock);
#else
return 1;
#endif
}
 
#endif
/**********************************************************************/
#ifdef L_funlockfile
 
void funlockfile(FILE *stream)
{
#ifdef __STDIO_THREADSAFE
__pthread_mutex_unlock(&stream->lock);
#endif
}
 
#endif
/**********************************************************************/
#ifdef L_getline
 
ssize_t getline(char **__restrict lineptr, size_t *__restrict n,
FILE *__restrict stream)
{
return __getdelim(lineptr, n, '\n', stream);
}
 
#endif
/**********************************************************************/
#ifdef L_getdelim
 
weak_alias(__getdelim,getdelim);
 
#define GETDELIM_GROWBY 64
 
ssize_t __getdelim(char **__restrict lineptr, size_t *__restrict n,
int delimiter, register FILE *__restrict stream)
{
register char *buf;
size_t pos;
int c;
 
if (!lineptr || !n || !stream) { /* Be compatable with glibc... even */
__set_errno(EINVAL); /* though I think we should assert here */
return -1; /* if anything. */
}
 
if (!(buf = *lineptr)) { /* If passed NULL for buffer, */
*n = 0; /* ignore value passed and treat size as 0. */
}
pos = 1; /* Make sure we have space for terminating nul. */
 
__STDIO_THREADLOCK(stream);
 
do {
if (pos >= *n) {
if (!(buf = realloc(buf, *n + GETDELIM_GROWBY))) {
__set_errno(ENOMEM); /* Emulate old uClibc implementation. */
break;
}
*n += GETDELIM_GROWBY;
*lineptr = buf;
}
} while (((c = (getc_unlocked)(stream)) != EOF) /* Disable the macro */
&& ((buf[pos++ - 1] = c) != delimiter));
 
__STDIO_THREADUNLOCK(stream);
 
if (--pos) {
buf[pos] = 0;
return pos;
}
 
return -1; /* Either initial realloc failed or first read was EOF. */
}
 
#endif
/**********************************************************************/
/* my extension functions */
/**********************************************************************/
#ifdef L__stdio_fsfopen
/*
* Stack|Static File open -- open a file where the FILE is either
* stack or staticly allocated.
*/
 
/* No reentrancy issues. */
 
FILE *_stdio_fsfopen(const char * __restrict filename,
const char * __restrict mode,
register FILE * __restrict stream)
{
#ifdef __STDIO_BUFFERS
stream->modeflags = __FLAG_FBF;
#if __STDIO_BUILTIN_BUF_SIZE > 0
stream->bufstart = stream->builtinbuf;
stream->bufend = stream->builtinbuf + sizeof(stream->builtinbuf);
#else /* __STDIO_BUILTIN_BUF_SIZE > 0 */
stream->bufend = stream->bufstart = NULL;
#endif /* __STDIO_BUILTIN_BUF_SIZE > 0 */
#endif /* __STDIO_BUFFERS */
 
return _stdio_fopen(filename, mode, stream, -1);
}
#endif
/**********************************************************************/
/* stdio internal functions */
/**********************************************************************/
#ifdef L__stdio_adjpos
 
/* According to the ANSI/ISO C99 definition of ungetwc()
* For a text or binary stream, the value of its file position indicator
* after a successful call to the ungetwc function is unspecified until
 
* Note however, that this applies only to _user_ calls to ungetwc. We
* need to allow for internal calls by scanf.
 
 
* So we store the byte count
* of the first ungot wide char in ungot_width. If it is 0 (user case)
* then the file position is treated as unknown.
*/
 
/* Internal function -- not reentrant. */
 
int _stdio_adjpos(register FILE * __restrict stream, register __offmax_t *pos)
{
__offmax_t r;
int cor = stream->modeflags & __MASK_UNGOT; /* handle ungots */
 
assert(cor <= 2);
 
#ifdef __STDIO_WIDE
/* Assumed narrow stream so correct if wide. */
if (cor && (stream->modeflags & __FLAG_WIDE)) {
if ((((stream->modeflags & __MASK_UNGOT) > 1) || stream->ungot[1])) {
return -1; /* App did ungetwc, so position is indeterminate. */
}
if (stream->modeflags & __MASK_UNGOT) {
cor = cor - 1 + stream->ungot_width[1];
}
if (stream->state.mask > 0) { /* Incomplete character (possible bad) */
cor -= stream->ungot_width[0];
}
}
#endif /* __STDIO_WIDE */
 
#ifdef __STDIO_BUFFERS
if (stream->modeflags & __FLAG_WRITING) {
cor -= (stream->bufpos - stream->bufstart); /* pending writes */
}
if (stream->modeflags & __FLAG_READING) {
cor += (stream->bufread - stream->bufpos); /* extra's read */
}
#endif /* __STDIO_BUFFERS */
 
r = *pos;
return ((*pos -= cor) > r) ? -cor : cor;
}
 
#endif
/**********************************************************************/
#ifdef L__stdio_lseek
/*
* This function is only called by fseek and ftell.
* fseek -- doesn't care about pos val, just success or failure.
* ftell -- needs pos val but offset == 0 and whence == SET_CUR.
*/
 
/* Internal function -- not reentrant. */
 
int _stdio_lseek(register FILE *stream, register __offmax_t *pos, int whence)
{
__offmax_t res;
 
#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
if (stream->cookie != &stream->filedes) {
return (((stream->gcs.seek == NULL)
|| ((stream->gcs.seek)(stream->cookie, pos, whence) < 0))
? -1 : 0);
}
#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */
#ifdef __STDIO_LARGE_FILES
res = lseek64(stream->filedes, *pos, whence);
#else
res = lseek(stream->filedes, *pos, whence);
#endif /* __STDIO_LARGE_FILES */
return (res >= 0) ? ((*pos = res), 0) : -1;
}
 
#endif
/**********************************************************************/
#ifdef L__stdio_fread
/*
* NOTE!!! This routine is meant to be callable by both narrow and wide
* functions. However, if called by a wide function, there must be
* NO pending ungetwc()s!!!
*/
 
/* Unlike write, it's ok for read to return fewer than bufsize, since
* we may not need all of them. */
static ssize_t _stdio_READ(register FILE *stream, unsigned char *buf, size_t bufsize)
{
ssize_t rv;
 
/* NOTE: C99 change: Input fails once the stream's EOF indicator is set. */
if ((bufsize == 0) || (stream->modeflags & __FLAG_EOF)) {
return 0;
}
 
if (bufsize > SSIZE_MAX) {
bufsize = SSIZE_MAX;
}
 
#ifdef __BCC__
TRY_READ:
#endif
rv = __READ(stream, buf, bufsize);
if (rv > 0) {
#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
assert(rv <= bufsize); /* buggy user handler... TODO: check? */
if (rv > bufsize) { /* Num reported written > number requested */
rv = bufsize; /* Treat as a full read??? */
}
#endif
} else if (rv == 0) {
stream->modeflags |= __FLAG_EOF;
} else {
#ifdef __BCC__
if (errno == EINTR) {
goto TRY_READ;
}
#endif
stream->modeflags |= __FLAG_ERROR;
rv = 0;
}
 
return rv;
}
 
/* Internal function -- not reentrant. */
 
size_t _stdio_fread(unsigned char *buffer, size_t bytes, register FILE *stream)
{
__stdio_validate_FILE(stream); /* debugging only */
 
#ifdef __STDIO_BUFFERS
 
if (stream->modeflags
#ifdef __STDIO_AUTO_RW_TRANSITION
& (__FLAG_WRITEONLY)
#else /* __STDIO_AUTO_RW_TRANSITION */
/* ANSI/ISO and SUSv3 require not currently writing. */
& (__FLAG_WRITEONLY|__FLAG_WRITING)
#endif /* __STDIO_AUTO_RW_TRANSITION */
) {
#ifdef __STDIO_PUTC_MACRO
stream->bufputc = stream->bufstart; /* Must disable putc. */
#endif /* __STDIO_PUTC_MACRO */
stream->modeflags |= __FLAG_ERROR;
/* TODO: This is for posix behavior if writeonly. To save space, we
* use this errno for read attempt while writing, as no errno is
* specified by posix for this case, even though the restriction is
* mentioned in fopen(). */
__set_errno(EBADF);
return 0;
}
 
/* We need to disable putc and getc macros in case of error */
#if defined(__STDIO_PUTC_MACRO) || defined(__STDIO_GETC_MACRO)
#ifdef __STDIO_PUTC_MACRO
stream->bufputc =
#endif /* __STDIO_GETC_MACRO */
#ifdef __STDIO_GETC_MACRO
stream->bufgetc =
#endif /* __STDIO_GETC_MACRO */
stream->bufstart;
#endif /* defined(__STDIO_PUTC_MACRO) || defined(__STDIO_GETC_MACRO) */
 
if (stream->modeflags & __MASK_BUFMODE) {
/* If the stream is readable and not fully buffered, we must first
* flush all line buffered output streams. Do this before the
* error check as this may be a read/write line-buffered stream.
* Note: Uses an implementation-specific hack!!! */
fflush_unlocked((FILE *) &_stdio_openlist);
}
 
#ifdef __STDIO_AUTO_RW_TRANSITION
if ((stream->modeflags & __FLAG_WRITING)
&& (fflush_unlocked(stream) == EOF)
) {
return 0; /* Fail if we need to fflush but can't. */
}
#endif /* __STDIO_AUTO_RW_TRANSITION */
 
stream->modeflags |= __FLAG_READING; /* Make sure Reading flag is set. */
 
{
register unsigned char *p = (unsigned char *) buffer;
 
/* First, grab appropriate ungetc() chars. NOT FOR WIDE ORIENTATED! */
while (bytes && (stream->modeflags & __MASK_UNGOT)) {
#ifdef __STDIO_WIDE
assert(stream->modeflags & __FLAG_NARROW);
#endif /* __STDIO_WIDE */
*p++ = stream->ungot[(--stream->modeflags) & __MASK_UNGOT];
stream->ungot[1] = 0;
--bytes;
}
 
/* Now get any other needed chars from the buffer or the file. */
FROM_BUF:
while (bytes && (stream->bufpos < stream->bufread)) {
--bytes;
*p++ = *stream->bufpos++;
}
 
if (bytes > 0) {
ssize_t len;
 
if (stream->filedes == -2) {
stream->modeflags |= __FLAG_EOF;
goto DONE;
}
 
/* The buffer is exhausted, but we still need chars. */
stream->bufpos = stream->bufread = stream->bufstart;
 
if (bytes <= stream->bufend - stream->bufread) {
/* We have sufficient space in the buffer. */
len = _stdio_READ(stream, stream->bufread,
stream->bufend - stream->bufread);
if (len > 0) {
stream->bufread += len;
goto FROM_BUF;
}
} else {
/* More bytes needed than fit in the buffer, so read */
/* directly into caller's buffer. */
len = _stdio_READ(stream, p, bytes);
if (len > 0) {
p += len;
bytes -= len;
goto FROM_BUF; /* Redundant work, but stops extra read. */
}
}
}
 
#ifdef __STDIO_GETC_MACRO
if (!(stream->modeflags
& (__FLAG_WIDE|__MASK_UNGOT|__MASK_BUFMODE|__FLAG_ERROR))
) {
stream->bufgetc = stream->bufread; /* Enable getc macro. */
}
#endif
 
DONE:
__stdio_validate_FILE(stream); /* debugging only */
return (p - (unsigned char *)buffer);
}
 
#else /* __STDIO_BUFFERS --------------------------------------- */
 
if (stream->modeflags
#ifdef __STDIO_AUTO_RW_TRANSITION
& (__FLAG_WRITEONLY)
#else /* __STDIO_AUTO_RW_TRANSITION */
/* ANSI/ISO and SUSv3 require not currently writing. */
& (__FLAG_WRITEONLY|__FLAG_WRITING)
#endif /* __STDIO_AUTO_RW_TRANSITION */
) {
stream->modeflags |= __FLAG_ERROR;
/* TODO: This is for posix behavior if writeonly. To save space, we
* use this errno for read attempt while writing, as no errno is
* specified by posix for this case, even though the restriction is
* mentioned in fopen(). */
__set_errno(EBADF);
return 0;
}
 
#ifdef __STDIO_AUTO_RW_TRANSITION
stream->modeflags &= ~(__FLAG_WRITING); /* Make sure Writing flag clear. */
#endif /* __STDIO_AUTO_RW_TRANSITION */
 
stream->modeflags |= __FLAG_READING; /* Make sure Reading flag is set. */
 
{
register unsigned char *p = (unsigned char *) buffer;
 
/* First, grab appropriate ungetc() chars. NOT FOR WIDE ORIENTATED! */
while (bytes && (stream->modeflags & __MASK_UNGOT)) {
#ifdef __STDIO_WIDE
assert(stream->modeflags & __FLAG_NARROW);
#endif /* __STDIO_WIDE */
*p++ = stream->ungot[(--stream->modeflags) & __MASK_UNGOT];
stream->ungot[1] = 0;
--bytes;
}
 
while (bytes > 0) {
ssize_t len = _stdio_READ(stream, p, (unsigned) bytes);
if (len == 0) {
break;
}
p += len;
bytes -= len;
}
 
__stdio_validate_FILE(stream); /* debugging only */
return (p - (unsigned char *)buffer);
}
 
#endif /* __STDIO_BUFFERS */
}
 
#endif
/**********************************************************************/
#ifdef L__stdio_fwrite
/*
* If buffer == NULL, attempt to fflush and return number of chars
* remaining in buffer (0 if successful fflush).
*/
 
/* WARNING!!!! Current standards say that termination due to an asyncronous
* signal may not result in stdio streams being flushed. This libary makes
* an effort to do so but there is no way, short of blocking signals for
* each _stdio_fwrite call, that we can maintain the correct state if a
* signal is recieved mid-call. So any stream in mid-_stdio_fwrite could
* not some flush data or even duplicate-flush some data. It is possible
* to avoid the duplicate-flush case by setting/clearing the stream
* error flag before/after the write process, but it doesn't seem worth
* the trouble. */
 
/* Like standard write, but always does a full write unless error, plus
* deals correctly with bufsize > SSIZE_MAX... not much on an issue on linux
* but definitly could be on Elks. Also on Elks, always loops for EINTR..
* Returns number of bytes written, so a short write indicates an error */
static size_t _stdio_WRITE(register FILE *stream,
register const unsigned char *buf, size_t bufsize)
{
size_t todo;
ssize_t rv, stodo;
 
todo = bufsize;
 
while (todo) {
stodo = (todo <= SSIZE_MAX) ? todo : SSIZE_MAX;
rv = __WRITE(stream, buf, stodo);
if (rv >= 0) {
#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
assert(rv <= stodo); /* buggy user handler... TODO: check? */
if (rv > stodo) { /* Num reported written > number requested */
rv = stodo; /* Treat as a full write??? */
}
#endif
todo -= rv;
buf += rv;
} else
#ifdef __BCC__
if (errno != EINTR)
#endif
{
stream->modeflags |= __FLAG_ERROR;
break;
}
}
 
return bufsize - todo;
}
 
/* Internal function -- not reentrant. */
 
size_t _stdio_fwrite(const unsigned char *buffer, size_t bytes,
register FILE *stream)
{
#ifdef __STDIO_BUFFERS
register const unsigned char *p;
 
__stdio_validate_FILE(stream); /* debugging only */
 
if ((stream->modeflags & __FLAG_READONLY)
#ifndef __STDIO_AUTO_RW_TRANSITION
/* ANSI/ISO requires either at EOF or currently not reading. */
|| ((stream->modeflags & (__FLAG_READING|__FLAG_EOF))
== __FLAG_READING)
#endif /* __STDIO_AUTO_RW_TRANSITION */
) {
stream->modeflags |= __FLAG_ERROR;
/* TODO: This is for posix behavior if readonly. To save space, we
* use this errno for write attempt while reading, as no errno is
* specified by posix for this case, even though the restriction is
* mentioned in fopen(). */
__set_errno(EBADF);
return 0;
}
 
#ifdef __STDIO_AUTO_RW_TRANSITION
/* If reading, deal with ungots and read-buffered chars. */
if (stream->modeflags & __FLAG_READING) {
if (((stream->bufpos < stream->bufread)
|| (stream->modeflags & __MASK_UNGOT))
/* If appending, we might as well seek to end to save a seek. */
/* TODO: set EOF in fseek when appropriate? */
&& fseek(stream, 0L,
((stream->modeflags & __FLAG_APPEND)
? SEEK_END : SEEK_CUR))
) {
/* Note: This differs from glibc's apparent behavior of
not setting the error flag and discarding the buffered
read data. */
stream->modeflags |= __FLAG_ERROR; /* fseek may not set this. */
return 0; /* Fail if we need to fseek but can't. */
}
/* Always reset even if fseek called (saves a test). */
#ifdef __STDIO_GETC_MACRO
stream->bufgetc =
#endif /* __STDIO_GETC_MACRO */
stream->bufpos = stream->bufread = stream->bufstart;
} else
#endif
if ((stream->modeflags & (__FLAG_WRITING|__FLAG_APPEND)) == __FLAG_APPEND) {
/* Append mode, but not currently writing. Need to seek to end for proper
* ftell() return values. Don't worry if the stream isn't seekable. */
__offmax_t pos[1];
*pos = 0;
if (_stdio_lseek(stream, pos, SEEK_END) && (errno != EPIPE)) { /* Too big? */
stream->modeflags |= __FLAG_ERROR;
return 0;
}
}
 
#ifdef __STDIO_PUTC_MACRO
/* We need to disable putc macro in case of error */
stream->bufputc = stream->bufstart;
#endif /* __STDIO_GETC_MACRO */
 
/* Clear both reading and writing flags. We need to clear the writing
* flag in case we're fflush()ing or in case of an error. */
stream->modeflags &= ~(__FLAG_READING|__FLAG_WRITING);
 
{
const unsigned char *buf0 = buffer;
size_t write_count = 1; /* 0 means a write failed */
 
if (!buffer) { /* fflush the stream */
FFLUSH:
{
size_t count = stream->bufpos - stream->bufstart;
p = stream->bufstart;
 
if (stream->filedes == -2) { /* TODO -- document this hack! */
stream->modeflags |= __FLAG_WRITING;
return (!buffer) ? 0 : ((buffer - buf0) + bytes);
}
 
{
write_count = _stdio_WRITE(stream, p, count);
p += write_count;
count -= write_count;
}
stream->bufpos = stream->bufstart;
while (count) {
*stream->bufpos++ = *p++;
--count;
}
 
if (!buffer) { /* fflush case... */
__stdio_validate_FILE(stream); /* debugging only */
return stream->bufpos - stream->bufstart;
}
}
}
 
#if 1
/* TODO: If the stream is buffered, we may be able to omit. */
if ((stream->bufpos == stream->bufstart) /* buf empty */
&& (stream->bufend - stream->bufstart <= bytes) /* fills */
&& (stream->filedes != -2)) { /* not strinf fake file */
/* so want to do a direct write of supplied buffer */
{
size_t rv = _stdio_WRITE(stream, buffer, bytes);
buffer += rv;
bytes -= rv;
}
} else
#endif
/* otherwise buffer not empty and/or data fits */
{
size_t count = stream->bufend - stream->bufpos;
p = buffer;
 
if (count > bytes) {
count = bytes;
}
bytes -= count;
 
while (count) {
*stream->bufpos++ = *buffer++;
--count;
}
 
if (write_count) { /* no write errors */
if (bytes) {
goto FFLUSH;
}
 
if (stream->modeflags & __FLAG_LBF) {
while (p < buffer) { /* check for newline. */
if (*p++ == '\n') {
goto FFLUSH;
}
}
}
}
}
 
#ifdef __STDIO_PUTC_MACRO
if (!(stream->modeflags & (__FLAG_WIDE|__MASK_BUFMODE|__FLAG_ERROR))) {
/* Not wide, no errors and fully buffered, so enable putc macro. */
stream->bufputc = stream->bufend;
}
#endif /* __STDIO_GETC_MACRO */
stream->modeflags |= __FLAG_WRITING; /* Ensure Writing flag is set. */
 
__stdio_validate_FILE(stream); /* debugging only */
return buffer - buf0;
 
}
 
#else /* __STDIO_BUFFERS --------------------------------------- */
 
__stdio_validate_FILE(stream); /* debugging only */
 
if ((stream->modeflags & __FLAG_READONLY)
#ifndef __STDIO_AUTO_RW_TRANSITION
/* ANSI/ISO requires either at EOF or currently not reading. */
|| ((stream->modeflags & (__FLAG_READING|__FLAG_EOF))
== __FLAG_READING)
#endif /* __STDIO_AUTO_RW_TRANSITION */
) {
stream->modeflags |= __FLAG_ERROR;
/* TODO: This is for posix behavior if readonly. To save space, we
* use this errno for write attempt while reading, as no errno is
* specified by posix for this case, even though the restriction is
* mentioned in fopen(). */
__set_errno(EBADF);
return 0;
}
 
/* We always clear the reading flag in case at EOF. */
stream->modeflags &= ~(__FLAG_READING);
 
/* Unlike the buffered case, we set the writing flag now since we don't
* need to do anything here for fflush(). */
stream->modeflags |= __FLAG_WRITING;
 
{
register unsigned char *p = (unsigned char *) buffer;
 
ssize_t rv = _stdio_WRITE(stream, p, bytes);
 
p += rv;
bytes -= rv;
 
__stdio_validate_FILE(stream); /* debugging only */
return (p - (unsigned char *)buffer);
}
 
#endif /* __STDIO_BUFFERS *****************************************/
}
 
#endif
/**********************************************************************/
#ifdef L__stdio_init
 
/* Internal functions -- _stdio_init() and __stdio_validate_FILE
* are not reentrant, but _stdio_term() is through fflush().
* Also, the _cs_{read|write|close} functions are not reentrant. */
 
#ifndef NDEBUG
void __stdio_validate_FILE(FILE *stream)
{
if (stream->filedes == -2) { /* fake FILE for sprintf, scanf, etc. */
return;
}
 
__STDIO_THREADLOCK(stream);
 
#ifdef __STDIO_BUFFERS
assert(stream->bufstart <= stream->bufread);
if (stream->modeflags & (__FLAG_READING)) {
assert(stream->bufpos <= stream->bufread);
}
assert(stream->bufread <= stream->bufend);
assert(stream->bufpos <= stream->bufend);
if ((stream->modeflags & __MASK_BUFMODE) == __FLAG_NBF) {
assert(stream->bufstart == stream->bufend);
}
assert((stream->modeflags & __MASK_BUFMODE) <= __FLAG_NBF);
#endif
#ifdef __STDIO_PUTC_MACRO
assert(stream->bufstart <= stream->bufputc);
assert(stream->bufputc <= stream->bufend);
if (stream->bufstart < stream->bufputc) {
assert(stream->bufputc == stream->bufend);
assert(stream->modeflags & (__FLAG_WRITING));
assert(!(stream->modeflags
& (__FLAG_WIDE|__MASK_BUFMODE|__MASK_UNGOT|__FLAG_READONLY))
);
}
#endif
#ifdef __STDIO_GETC_MACRO
assert(stream->bufstart <= stream->bufgetc);
assert(stream->bufgetc <= stream->bufread);
if (stream->bufstart < stream->bufgetc) {
assert(stream->modeflags & (__FLAG_READING));
assert(!(stream->modeflags
& (__FLAG_WIDE|__MASK_BUFMODE|__MASK_UNGOT|__FLAG_WRITEONLY))
);
}
#endif
assert((stream->modeflags & __MASK_UNGOT) != __MASK_UNGOT);
if (stream->modeflags & __MASK_UNGOT1) {
assert(stream->ungot[1] <= 1);
}
if (stream->modeflags & __MASK_UNGOT) {
assert(!(stream->modeflags & __FLAG_EOF));
}
assert((stream->modeflags & (__FLAG_READONLY|__FLAG_WRITEONLY))
!= (__FLAG_READONLY|__FLAG_WRITEONLY));
 
/* TODO -- filepos? ungot_width? filedes? nextopen? */
 
__STDIO_THREADUNLOCK(stream);
}
#endif
 
#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
ssize_t _cs_read(void *cookie, char *buf, size_t bufsize)
{
return read(*((int *) cookie), buf, bufsize);
}
 
ssize_t _cs_write(void *cookie, const char *buf, size_t bufsize)
{
return write(*((int *) cookie), (char *) buf, bufsize);
}
 
int _cs_close(void *cookie)
{
return close(*((int *) cookie));
}
#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */
 
#ifdef __STDIO_BUFFERS
static unsigned char _fixed_buffers[2 * BUFSIZ];
#define bufin (_fixed_buffers)
#define bufout (_fixed_buffers + BUFSIZ)
#endif /* __STDIO_BUFFERS */
 
static FILE _stdio_streams[] = {
__STDIO_INIT_FILE_STRUCT(_stdio_streams[0], __FLAG_LBF|__FLAG_READONLY, \
0, _stdio_streams + 1, bufin, BUFSIZ ),
__STDIO_INIT_FILE_STRUCT(_stdio_streams[1], __FLAG_LBF|__FLAG_WRITEONLY, \
1, _stdio_streams + 2, bufout, BUFSIZ ),
__STDIO_INIT_FILE_STRUCT(_stdio_streams[2], __FLAG_NBF|__FLAG_WRITEONLY, \
2, 0, 0, 0 )
};
 
FILE *stdin = _stdio_streams + 0;
FILE *stdout = _stdio_streams + 1;
FILE *stderr = _stdio_streams + 2;
 
#if defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS)
 
#ifdef __STDIO_BUFFERS
FILE *_stdio_openlist = _stdio_streams;
#else /* __STDIO_BUFFERS */
FILE *_stdio_openlist = NULL;
#endif /* __STDIO_BUFFERS */
 
#ifdef __STDIO_THREADSAFE
pthread_mutex_t _stdio_openlist_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
 
 
void __stdio_init_mutex(pthread_mutex_t *m)
{
static const pthread_mutex_t __stdio_mutex_initializer
= PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
 
memcpy(m, &__stdio_mutex_initializer, sizeof(__stdio_mutex_initializer));
}
#endif /* __STDIO_THREADSAFE */
 
/* TODO - do we need to lock things, or do we just assume we're the only
* remaining thread? */
 
/* Note: We assume here that we are the only remaining thread. */
void _stdio_term(void)
{
#if defined(__STDIO_GLIBC_CUSTOM_STREAMS) || defined(__STDIO_THREADSAFE)
register FILE *ptr;
#endif
 
/* TODO: if called via a signal handler for a signal mid _stdio_fwrite,
* the stream may be in an unstable state... what do we do?
* perhaps set error flag before and clear when done if successful? */
 
#if defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS)
#ifdef __STDIO_THREADSAFE
/* First, forceably unlock the open file list and all files.
* Note: Set locking mode to "by caller" to save some overhead later. */
__stdio_init_mutex(&_stdio_openlist_lock);
for (ptr = _stdio_openlist ; ptr ; ptr = ptr->nextopen ) {
ptr->user_locking = 1;
__stdio_init_mutex(&ptr->lock);
}
#endif /* __STDIO_THREADSAFE */
#endif /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */
 
#ifdef __STDIO_BUFFERS
/* TODO -- set an alarm and flush each file "by hand"? to avoid blocking? */
 
/* Now flush all streams. */
fflush_unlocked(NULL);
#endif /* __STDIO_BUFFERS */
 
/* Next close all custom streams in case of any special cleanup, but
* don't use fclose() because that pulls in free and malloc. Also,
* don't worry about removing them from the list. Just set the cookie
* pointer to NULL so that an error will be generated if someone tries
* to use the stream. */
#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
for (ptr = _stdio_openlist ; ptr ; ptr = ptr->nextopen ) {
if (ptr->cookie != &ptr->filedes) { /* custom stream */
__CLOSE(ptr);
ptr->cookie = NULL; /* Generate an error if used later. */
#if 0
/* #ifdef __STDIO_BUFFERS */
} else {
/* TODO: "unbuffer" files like glibc does? Inconsistent with
* custom stream handling above, but that's necessary to deal
* with special user-defined close behavior. */
stream->bufpos = stream->bufread = stream->bufend
#ifdef __STDIO_GETC_MACRO
= stream->bufgetc
#endif
#ifdef __STDIO_PUTC_MACRO
= stream->bufputc
#endif
= stream->bufstart;
#endif /* __STDIO_BUFFERS */
}
}
#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */
}
 
 
#if defined(__STDIO_BUFFERS) || !defined(__UCLIBC__)
void _stdio_init(void)
{
#ifdef __STDIO_BUFFERS
int old_errno = errno;
/* stdin and stdout uses line buffering when connected to a tty. */
_stdio_streams[0].modeflags ^= (1-isatty(0)) * __FLAG_LBF;
_stdio_streams[1].modeflags ^= (1-isatty(1)) * __FLAG_LBF;
__set_errno(old_errno);
#endif /* __STDIO_BUFFERS */
#ifndef __UCLIBC__
/* __stdio_term is automatically when exiting if stdio is used.
* See misc/internals/__uClibc_main.c and and stdlib/atexit.c. */
atexit(_stdio_term);
#endif /* __UCLIBC__ */
}
#endif /* defined(__STDIO_BUFFERS) || !defined(__UCLIBC__) */
#endif /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */
 
#endif
/**********************************************************************/
/* ANSI/ISO functions. */
/**********************************************************************/
#ifdef L_remove
#include <unistd.h>
#include <errno.h>
 
/* No reentrancy issues. */
 
int remove(register const char *filename)
{
int old_errno = errno;
 
/* SUSv3 says equivalent to rmdir() if a directory, and unlink()
* otherwise. Hence, we need to try rmdir() first. */
 
return (((rmdir(filename) == 0)
|| ((errno == ENOTDIR)
&& ((__set_errno(old_errno), unlink(filename)) == 0)))
? 0 : -1);
}
#endif
/**********************************************************************/
/* rename is a syscall
#ifdef L_rename
int rename(const char *old, const char *new);
#endif
*/
/**********************************************************************/
/* TODO: tmpfile */
/* #ifdef L_tmpfile */
/* FILE *tmpfile(void); */
/* #endif */
/**********************************************************************/
/* TODO: tmpname */
/* #ifdef L_tmpname */
/* char *tmpname(char *s); */
/* #endif */
/**********************************************************************/
#ifdef L_fclose
 
/* We need to be careful here to avoid deadlock when threading, as we
* need to lock both the file and the open file list. This can clash
* with fflush. Since fflush is much more common, we do the extra
* work here. */
 
int fclose(register FILE *stream)
{
#if defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS)
 
register FILE *ptr;
int rv = 0;
 
#ifdef __STDIO_THREADSAFE
/* Need two non-heirchal mutexs... be careful to avoid deadlock*/
do {
__STDIO_THREADLOCK(stream);
if (__STDIO_THREADTRYLOCK_OPENLIST == 0) {
break;
}
__STDIO_THREADUNLOCK(stream);
usleep(10000);
} while (1);
#endif /* __STDIO_THREADSAFE */
 
__stdio_validate_FILE(stream); /* debugging only */
 
#ifdef __STDIO_BUFFERS
if (stream->modeflags & __FLAG_WRITING) {
rv = fflush_unlocked(stream); /* Write any pending buffered chars. */
} /* Also disables putc macro if used. */
 
#ifdef __STDIO_GETC_MACRO
/* Not necessary after fflush, but always do this to reduce size. */
stream->bufgetc = stream->bufstart; /* Disable getc macro for safety. */
#endif /* __STDIO_GETC_MACRO */
#endif /* __STDIO_BUFFERS */
 
/* Remove file from open list before closing file descriptor. */
ptr = _stdio_openlist;
if (ptr == stream) {
_stdio_openlist = stream->nextopen;
} else {
while (ptr) {
if (ptr->nextopen == stream) {
ptr->nextopen = stream->nextopen;
break;
}
ptr = ptr->nextopen;
}
}
__STDIO_THREADUNLOCK_OPENLIST; /* We're done with the open file list. */
 
if (__CLOSE(stream) < 0) { /* Must close even if fflush failed. */
rv = EOF;
}
#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
stream->cookie = &(stream->filedes);
stream->gcs.read = _cs_read;
stream->gcs.write = _cs_write;
stream->gcs.seek = 0; /* The internal seek func handles normals. */
stream->gcs.close = _cs_close;
#endif
stream->filedes = -1; /* To aid debugging... */
 
#ifdef __STDIO_BUFFERS
if (stream->modeflags & __FLAG_FREEBUF) {
free(stream->bufstart);
}
#endif /* __STDIO_BUFFERS */
 
/* TODO -- leave the stream locked to catch any dangling refs? */
__STDIO_THREADUNLOCK(stream);
 
/* At this point, any dangling refs to the stream are the result of
* a programming bug... so free the unlocked stream. */
if (stream->modeflags & __FLAG_FREEFILE) {
#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
stream->cookie = NULL; /* To aid debugging... */
#endif
free(stream);
}
 
return rv;
 
#else /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */
 
int rv = 0;
 
__STDIO_THREADLOCK(stream);
 
__stdio_validate_FILE(stream); /* debugging only */
 
if (__CLOSE(stream) < 0) { /* Must close even if fflush failed. */
rv = EOF;
}
 
#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
stream->cookie = &(stream->filedes);
stream->gcs.read = _cs_read;
stream->gcs.write = _cs_write;
stream->gcs.seek = 0; /* The internal seek func handles normals. */
stream->gcs.close = _cs_close;
#endif
stream->filedes = -1; /* To aid debugging... */
 
__STDIO_THREADUNLOCK(stream);
 
/* At this point, any dangling refs to the stream are the result of
* a programming bug... so free the unlocked stream. */
if (stream->modeflags & __FLAG_FREEFILE) {
#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
stream->cookie = NULL; /* To aid debugging... */
#endif
free(stream);
}
 
return rv;
 
#endif /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS )*/
}
 
#endif
/**********************************************************************/
#ifdef L_fflush
 
/*
* Special cases:
* stream == NULL means fflush all writing streams (ANSI/ISO).
* stream == (FILE *) &_stdio_openlist -- implementation-specific hack
* meaning fflush all line buffered writing streams
*/
 
/*
* NOTE: ANSI/ISO difference!!! According to the standard, fflush is only
* defined for write-only streams, or read/write streams whose last op
* was a write. However, reading is allowed for a read/write stream if
* a file positioning operation was done (fseek, fsetpos) even though there
* is no guarantee of flushing the write data in that case. Hence, for
* this case we keep a flag to indicate whether or not the buffer needs to
* be flushed even if the last operation was a read. This falls under the
* implementation-defined behavior. Otherwise, we would need to flush
* every time we did fseek, etc. even if we were still in the buffer's range.
*/
 
/* Since the stream pointer arg is allowed to be NULL, or the address of the
* stdio open file list if stdio is buffered in this implementation, we can't
* use the UNLOCKED() macro here. */
 
#ifndef __STDIO_THREADSAFE
strong_alias(fflush_unlocked,fflush)
#else /* __STDIO_THREADSAFE */
int fflush(register FILE *stream)
{
int retval;
 
if ((stream != NULL)
#if defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS)
&& (stream != (FILE *) &_stdio_openlist)
#endif /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */
) {
__STDIO_THREADLOCK(stream);
retval = fflush_unlocked(stream);
__STDIO_THREADUNLOCK(stream);
} else {
retval = fflush_unlocked(stream);
}
 
return retval;
}
#endif /* __STDIO_THREADSAFE */
 
int fflush_unlocked(register FILE *stream)
{
#ifdef __STDIO_BUFFERS
 
int rv = 0;
unsigned short mask = (__FLAG_NBF|__FLAG_LBF);
 
#ifndef NDEBUG
if ((stream != NULL) && (stream != (FILE *) &_stdio_openlist)) {
__stdio_validate_FILE(stream); /* debugging only */
}
#endif
 
if (stream == (FILE *) &_stdio_openlist) { /* fflush all line-buffered */
stream = NULL;
mask = __FLAG_LBF;
}
 
if (stream == NULL) { /* flush all (line) buffered writing streams */
/* Note -- We have to lock the list even in the unlocked function. */
__STDIO_THREADLOCK_OPENLIST;
/* TODO -- Can we work around locking the list to avoid keeping it
* locked if the write blocks? */
for (stream = _stdio_openlist; stream; stream = stream->nextopen) {
if (((stream->modeflags ^ __FLAG_NBF) & mask)
&& (stream->modeflags & __FLAG_WRITING)
&& fflush(stream)) {
rv = EOF;
}
}
__STDIO_THREADUNLOCK_OPENLIST;
} else if (stream->modeflags & __FLAG_WRITING) {
if (_stdio_fwrite(NULL, 0, stream) > 0) { /* flush buffer contents. */
rv = -1; /* Not all chars written. */
}
#ifdef __UCLIBC_MJN3_ONLY__
#warning WISHLIST: Add option to test for undefined behavior of fflush.
#endif /* __UCLIBC_MJN3_ONLY__ */
#if 0
} else if (stream->modeflags & (__FLAG_READING|__FLAG_READONLY)) {
/* ANSI/ISO says behavior in this case is undefined but also says you
* shouldn't flush a stream you were reading from. As usual, glibc
* caters to broken programs and simply ignores this. */
stream->modeflags |= __FLAG_ERROR;
__set_errno(EBADF);
rv = -1;
#endif
}
 
#ifndef NDEBUG
if ((stream != NULL) && (stream != (FILE *) &_stdio_openlist)) {
__stdio_validate_FILE(stream); /* debugging only */
}
#endif
return rv;
 
#else /* __STDIO_BUFFERS --------------------------------------- */
 
#ifndef NDEBUG
if ((stream != NULL)
#if defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS)
&& (stream != (FILE *) &_stdio_openlist)
#endif /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */
) {
__stdio_validate_FILE(stream); /* debugging only */
}
#endif
 
#ifdef __UCLIBC_MJN3_ONLY__
#warning WISHLIST: Add option to test for undefined behavior of fflush.
#endif /* __UCLIBC_MJN3_ONLY__ */
#if 0
return ((stream != NULL)
&& (stream->modeflags & (__FLAG_READING|__FLAG_READONLY))
? ((stream->modeflags |= __FLAG_ERROR), __set_errno(EBADF), EOF)
: 0 );
#else
return 0;
#endif
 
#endif /* __STDIO_BUFFERS */
}
#endif
/**********************************************************************/
#ifdef L_fopen
 
/* No reentrancy issues. */
 
FILE *fopen(const char * __restrict filename, const char * __restrict mode)
{
return _stdio_fopen(filename, mode, NULL, -1);
}
 
#endif
/**********************************************************************/
#ifdef L__stdio_fopen
 
/*
* Cases:
* fopen : filename != NULL, stream == NULL, filedes == -1
* freopen : filename != NULL, stream != NULL, filedes == -1
* fdopen : filename == NULL, stream == NULL, filedes valid
* fsfopen : filename != NULL, stream != NULL, filedes == -1
* fopen64 : filename != NULL, stream == NULL, filedes == -2
*/
 
#if O_ACCMODE != 3 || O_RDONLY != 0 || O_WRONLY != 1 || O_RDWR != 2
#error Assumption violated - mode constants
#endif
 
/* Internal function -- reentrant (locks open file list) */
 
FILE *_stdio_fopen(const char * __restrict filename,
register const char * __restrict mode,
register FILE * __restrict stream, int filedes)
{
__mode_t open_mode;
 
/* parse mode */
open_mode = O_RDONLY;
if (*mode != 'r') { /* not read */
open_mode = (O_WRONLY | O_CREAT | O_TRUNC);
if (*mode != 'w') { /* not write (create or truncate)*/
open_mode = (O_WRONLY | O_CREAT | O_APPEND);
if (*mode != 'a') { /* not write (create or append) */
__set_errno(EINVAL); /* then illegal mode */
return NULL;
}
}
}
 
if ((*++mode == 'b')) { /* binary mode (NOP currently) */
++mode;
}
 
if (*mode == '+') { /* read-write */
++mode;
open_mode &= ~(O_RDONLY | O_WRONLY);
open_mode |= O_RDWR;
}
 
#if defined(__STDIO_FOPEN_EXCLUSIVE_MODE) || defined(__STDIO_FOPEN_LARGEFILE_MODE)
for ( ; *mode ; ++mode) { /* ignore everything else except ... */
#ifdef __STDIO_FOPEN_EXCLUSIVE_MODE
if (*mode == 'x') { /* open exclusive -- glibc extension */
open_mode |= O_EXCL;
continue;
}
#endif /* __STDIO_FOPEN_EXCLUSIVE_MODE */
#ifdef __STDIO_FOPEN_LARGEFILE_MODE
if (*mode == 'F') { /* open large file */
open_mode |= O_LARGEFILE;
continue;
}
#endif /* __STDIO_FOPEN_LARGEFILE_MODE */
}
#endif /* __STDIO_FOPEN_EXCLUSIVE_MODE or __STDIO_FOPEN_LARGEFILE_MODE def'd */
 
#ifdef __BCC__
mode = filename; /* TODO: help BCC with register allocation. */
#define filename mode
#endif /* __BCC__ */
 
if (!stream) { /* Need to allocate a FILE. */
#ifdef __STDIO_BUFFERS
if ((stream = malloc(sizeof(FILE))) == NULL) {
return stream;
}
stream->modeflags = __FLAG_FREEFILE;
if ((stream->bufstart = malloc(BUFSIZ)) != 0) {
stream->bufend = stream->bufstart + BUFSIZ;
stream->modeflags |= __FLAG_FREEBUF;
} else {
#if __STDIO_BUILTIN_BUF_SIZE > 0
stream->bufstart = stream->unbuf;
stream->bufend = stream->unbuf + sizeof(stream->unbuf);
#else /* __STDIO_BUILTIN_BUF_SIZE > 0 */
stream->bufstart = stream->bufend = NULL;
#endif /* __STDIO_BUILTIN_BUF_SIZE > 0 */
}
#else /* __STDIO_BUFFERS */
if ((stream = malloc(sizeof(FILE))) == NULL) {
return stream;
}
stream->modeflags = __FLAG_FREEFILE;
#endif /* __STDIO_BUFFERS */
}
 
if (filedes >= 0) { /* Handle fdopen trickery. */
/* NOTE: it is insufficient to just check R/W/RW agreement.
* We must also check largefile compatibility if applicable.
* Also, if append mode is desired for fdopen but O_APPEND isn't
* currently set, then set it as recommended by SUSv3. However,
* if append mode is not specified for fdopen but O_APPEND is set,
* leave it set (glibc compat). */
int i = (open_mode & (O_ACCMODE|O_LARGEFILE)) + 1;
 
/* NOTE: fopencookie needs changing if the basic check changes! */
if (((i & (((int) filename) + 1)) != i) /* Check basic agreement. */
|| (((open_mode & O_APPEND)
&& !(((int) filename) & O_APPEND)
&& fcntl(filedes, F_SETFL, O_APPEND))) /* Need O_APPEND. */
) {
__set_errno(EINVAL);
filedes = -1;
}
#ifdef __STDIO_LARGE_FILES
/* For later... to reflect largefile setting in stream flags. */
open_mode |= (((int) filename) & O_LARGEFILE);
#endif /* __STDIO_LARGE_FILES */
stream->filedes = filedes;
} else {
#ifdef __STDIO_LARGE_FILES
if (filedes < -1) {
open_mode |= O_LARGEFILE;
}
#endif /* __STDIO_LARGE_FILES */
stream->filedes = open(filename, open_mode, 0666);
}
 
if (stream->filedes < 0) {
#ifdef __STDIO_BUFFERS
if (stream->modeflags & __FLAG_FREEBUF) {
free(stream->bufstart);
}
#endif /* __STDIO_BUFFERS */
if (stream->modeflags & __FLAG_FREEFILE) {
free(stream);
}
return NULL;
}
 
#ifdef __STDIO_BUFFERS
{
/* Do not let isatty mess up errno */
int old_errno = errno;
stream->modeflags |= (isatty(stream->filedes) * __FLAG_LBF);
__set_errno(old_errno);
}
#endif
 
stream->modeflags |=
#if (O_APPEND == __FLAG_APPEND) \
&& ((O_LARGEFILE == __FLAG_LARGEFILE) || (O_LARGEFILE == 0))
(open_mode & (O_APPEND|O_LARGEFILE)) | /* i386 linux and elks */
#else /* (O_APPEND == __FLAG_APPEND) && (O_LARGEFILE == __FLAG_LARGEFILE) */
((open_mode & O_APPEND) ? __FLAG_APPEND : 0) |
#ifdef __STDIO_LARGE_FILES
((open_mode & O_LARGEFILE) ? __FLAG_LARGEFILE : 0) |
#endif /* __STDIO_LARGE_FILES */
#endif /* (O_APPEND == __FLAG_APPEND) && (O_LARGEFILE == __FLAG_LARGEFILE) */
((((open_mode & O_ACCMODE) + 1) ^ 0x03) * __FLAG_WRITEONLY);
 
#ifdef __STDIO_BUFFERS
#ifdef __STDIO_GETC_MACRO
stream->bufgetc =
#endif
#ifdef __STDIO_PUTC_MACRO
stream->bufputc =
#endif
stream->bufpos = stream->bufread = stream->bufstart;
#endif /* __STDIO_BUFFERS */
 
#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
stream->cookie = &(stream->filedes);
stream->gcs.read = _cs_read;
stream->gcs.write = _cs_write;
stream->gcs.seek = 0; /* The internal seek func handles normals. */
stream->gcs.close = _cs_close;
#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */
 
#ifdef __STDIO_WIDE
stream->ungot_width[0] = 0;
#endif /* __STDIO_WIDE */
#ifdef __STDIO_MBSTATE
__INIT_MBSTATE(&(stream->state));
#endif /* __STDIO_MBSTATE */
 
#ifdef __STDIO_THREADSAFE
stream->user_locking = 0;
__stdio_init_mutex(&stream->lock);
#endif /* __STDIO_THREADSAFE */
 
#if defined(__STDIO_BUFFERS) \
|| (defined(__STDIO_THREADSAFE) && defined(__STDIO_GLIBC_CUSTOM_STREAMS))
__STDIO_THREADLOCK_OPENLIST;
stream->nextopen = _stdio_openlist; /* New files are inserted at */
_stdio_openlist = stream; /* the head of the list. */
__STDIO_THREADUNLOCK_OPENLIST;
#endif
 
__stdio_validate_FILE(stream); /* debugging only */
return stream;
#ifdef __BCC__
#undef filename
#endif /* __BCC__ */
}
 
#endif
/**********************************************************************/
#ifdef L_freopen
 
/* Reentrant. */
 
FILE *freopen(const char * __restrict filename, const char * __restrict mode,
register FILE * __restrict stream)
{
/*
* ANSI/ISO allow (implementation-defined) change of mode for an
* existing file if filename is NULL. It doesn't look like Linux
* supports this, so we don't here.
*
* NOTE: Whether or not the stream is free'd on failure is unclear
* w.r.t. ANSI/ISO. This implementation chooses to NOT free
* the stream and associated buffer if they were dynamically
* allocated.
* TODO: Check the above.
* TODO: Apparently linux allows setting append mode. Implement?
*/
unsigned short dynmode;
register FILE *fp;
 
__STDIO_THREADLOCK(stream);
 
/* First, flush and close, but don't deallocate, the stream. */
/* This also removes the stream for the open file list. */
dynmode =
#ifdef __STDIO_BUFFERS
/* __MASK_BUFMODE | */ /* TODO: check */
#endif /* __STDIO_BUFFERS */
(stream->modeflags & (__FLAG_FREEBUF|__FLAG_FREEFILE));
 
stream->modeflags &= ~(__FLAG_FREEBUF|__FLAG_FREEFILE);
fclose(stream); /* Failures are ignored. */
 
fp = _stdio_fopen(filename, mode, stream, -1);
 
stream->modeflags |= dynmode;
 
__STDIO_THREADUNLOCK(stream);
 
return fp;
}
#endif
/**********************************************************************/
#ifdef L_freopen64
 
/* Reentrant. */
 
/* TODO -- is it worth collecting the common work (40 bytes) in a function? */
FILE *freopen64(const char * __restrict filename, const char * __restrict mode,
register FILE * __restrict stream)
{
unsigned short dynmode;
register FILE *fp;
 
__STDIO_THREADLOCK(stream);
 
/* First, flush and close, but don't deallocate, the stream. */
/* This also removes the stream for the open file list. */
dynmode =
#ifdef __STDIO_BUFFERS
/* __MASK_BUFMODE | */ /* TODO: check */
#endif /* __STDIO_BUFFERS */
(stream->modeflags & (__FLAG_FREEBUF|__FLAG_FREEFILE));
 
stream->modeflags &= ~(__FLAG_FREEBUF|__FLAG_FREEFILE);
fclose(stream); /* Failures are ignored. */
stream->modeflags = dynmode;
 
fp = _stdio_fopen(filename, mode, stream, -2); /* TODO -- magic const */
 
__STDIO_THREADUNLOCK(stream);
 
return fp;
}
 
#endif
/**********************************************************************/
#ifdef L_setbuf
 
/* Reentrant through setvbuf(). */
 
void setbuf(FILE * __restrict stream, register char * __restrict buf)
{
#ifdef __STDIO_BUFFERS
int mode;
 
mode = (buf != NULL) ? _IOFBF : _IONBF;
setvbuf(stream, buf, mode, BUFSIZ);
#else /* __STDIO_BUFFERS */
/* TODO -- assert on stream? */
/* Nothing to do. */
#endif /* __STDIO_BUFFERS */
}
 
#endif
/**********************************************************************/
#ifdef L_setvbuf
 
/* Reentrant. */
 
int setvbuf(register FILE * __restrict stream, register char * __restrict buf,
int mode, size_t size)
{
#ifdef __STDIO_BUFFERS
 
int allocated_buf_flag;
int rv = EOF;
 
__STDIO_THREADLOCK(stream);
 
__stdio_validate_FILE(stream); /* debugging only */
 
if (((unsigned int) mode) > 2) { /* Illegal mode. */
/* TODO -- set an errno? */
goto DONE;
}
 
#ifdef __STDIO_FLEXIBLE_SETVBUF
/* C89 standard requires no ops before setvbuf, but we can be flexible. */
/* NOTE: This will trash any chars ungetc'd!!! */
/* TODO: hmm could preserve unget count since ungot slots aren't changed (true?)
* but this will fail when buffered chars read from a pipe unless the user buf
* is big enough to copy everything over. */
if (fseek(stream, 0L, SEEK_CUR)) {
goto DONE;
}
#else /* __STDIO_FLEXIBLE_SETVBUF */
/*
* Note: ANSI/ISO requires setvbuf to be called after opening the file
* but before any other operation other than a failed setvbuf call.
* We'll cheat here and only test if the wide or narrow mode flag has
* been set; i.e. no read or write (or unget or fwide) operations have
* taken place.
*/
#ifdef __STDIO_WIDE
if (stream->modeflags & (__FLAG_WIDE|__FLAG_NARROW)) {
goto DONE;
}
#else /* __STDIO_WIDE */
/* Note: This only checks if not currently reading or writing. */
if (stream->modeflags & (__FLAG_READING|__FLAG_WRITING)) {
goto DONE;
}
#endif /* __STDIO_WIDE */
#endif /* __STDIO_FLEXIBLE_SETVBUF */
 
if (mode == _IONBF) {
size = 0;
buf = NULL;
} else if (!buf && !size) {
/* If buf==NULL && size==0 && either _IOFBF or _IOLBF, keep
* current buffer and only set buffering mode. */
size = stream->bufend - stream->bufstart;
}
 
stream->modeflags &= ~(__MASK_BUFMODE); /* Clear current mode */
stream->modeflags |= mode * __FLAG_LBF; /* and set new one. */
 
allocated_buf_flag = 0;
if ((!buf) && (size != (stream->bufend - stream->bufstart))) {
/* No buffer supplied and requested size different from current. */
/* If size == 0, create a (hopefully) bogus non-null pointer... */
if (!(buf = ((size > 0)
? ((allocated_buf_flag = __FLAG_FREEBUF), malloc(size))
: ((char *)NULL) + 1))
) {
/* TODO -- should we really keep current buffer if it was passed
* to us earlier by the app? */
goto DONE; /* Keep current buffer. */
}
}
 
/* TODO: setvbuf "signal" safety */
if (buf && (buf != (char *) stream->bufstart)) { /* Want new buffer. */
if (stream->modeflags & __FLAG_FREEBUF) {
stream->modeflags &= ~(__FLAG_FREEBUF);
free(stream->bufstart);
}
stream->modeflags |= allocated_buf_flag; /* Free-able buffer? */
#ifdef __STDIO_GETC_MACRO
stream->bufgetc =
#endif
#ifdef __STDIO_PUTC_MACRO
stream->bufputc =
#endif
stream->bufpos = stream->bufread = stream->bufstart = buf;
stream->bufend = buf + size;
}
 
__stdio_validate_FILE(stream); /* debugging only */
 
rv = 0;
 
DONE:
__STDIO_THREADUNLOCK(stream);
 
return rv;
 
#else /* __STDIO_BUFFERS */
__stdio_validate_FILE(stream); /* debugging only */
/* TODO -- set errno for illegal mode? */
 
return EOF;
#endif /* __STDIO_BUFFERS */
}
 
#endif
/**********************************************************************
int fprintf(FILE * __restrict stream, const char * __restrict format, ...);
int fscanf(FILE * __restrict stream, const char * __restrict format, ...);
int printf(const char * __restrict format, ...);
int scanf(const char * __restrict format, ...);
int snprintf(char * __restrict s, size_t n,
const char * __restrict format, ...);
int sprintf(char * __restrict s, const char * __restrict format, ...);
int sscanf(char * __restrict s, const char * __restrict format, ...);
int vfprintf(FILE * __restrict stream, const char * __restrict format,
va_list arg);
int vfscanf(FILE * __restrict stream, const char * __restrict format,
va_list arg);
int vprintf(const char * __restrict format, va_list arg);
int vscanf(const char * __restrict format, va_list arg);
int vsnprintf(char * __restrict s, size_t n,
const char * __restrict format, va_list arg);
int vsprintf(char * __restrict s, const char * __restrict format,
va_list arg);
int vsscanf(char * __restrict s, const char * __restrict format,
va_list arg);
**********************************************************************/
#ifdef L_fgetc
 
/* Reentrancy handled by UNLOCKED() macro. */
 
UNLOCKED(int,fgetc,(FILE *stream),(stream))
{
unsigned char buf[1];
 
#ifdef __STDIO_WIDE
 
return (fread_unlocked(buf, (size_t) 1, (size_t) 1, stream) > 0)
? *buf : EOF;
 
#else /* __STDIO_WIDE */
 
return (_stdio_fread(buf, (size_t) 1, stream) > 0) ? *buf : EOF;
 
#endif /* __STDIO_WIDE */
}
 
#endif
/**********************************************************************/
#ifdef L_fgets
 
/* Reentrancy handled by UNLOCKED() macro. */
 
UNLOCKED(char *,fgets,
(char *__restrict s, int n, register FILE * __restrict stream),
(s, n, stream))
{
register char *p;
int c;
 
#ifdef __UCLIBC_MJN3_ONLY__
#warning CONSIDER: What should fgets do if n <= 0?
#endif /* __UCLIBC_MJN3_ONLY__ */
/* Should we assert here? Or set errno? Or just fail... */
if (n <= 0) {
/* __set_errno(EINVAL); */
goto ERROR;
}
 
p = s;
 
while (--n) {
if ((c = (getc_unlocked)(stream)) == EOF) { /* Disable the macro. */
if (__FERROR(stream)) {
goto ERROR;
}
break;
}
if ((*p++ = c) == '\n') {
break;
}
}
 
#ifdef __UCLIBC_MJN3_ONLY__
#warning CONSIDER: If n==1 and not at EOF, should fgets return an empty string?
#endif /* __UCLIBC_MJN3_ONLY__ */
if (p > s) {
*p = 0;
return s;
}
 
ERROR:
return NULL;
}
 
#endif
/**********************************************************************/
#ifdef L_fputc
 
/* Reentrancy handled by UNLOCKED() macro. */
 
UNLOCKED(int,fputc,(int c, FILE *stream),(c,stream))
{
unsigned char buf[1];
 
*buf = (unsigned char) c;
 
#ifdef __STDIO_WIDE
 
return (fwrite_unlocked(buf, (size_t) 1, (size_t) 1, stream) > 0)
? (*buf) : EOF;
 
#else /* __STDIO_WIDE */
 
return (_stdio_fwrite(buf, (size_t) 1, stream) > 0) ? (*buf) : EOF;
 
#endif /* __STDIO_WIDE */
}
#endif
/**********************************************************************/
#ifdef L_fputs
 
/* Reentrancy handled by UNLOCKED() macro. */
 
UNLOCKED(int,fputs,
(register const char * __restrict s, FILE * __restrict stream),
(s, stream))
{
size_t n = strlen(s);
 
#ifdef __STDIO_WIDE
 
return (fwrite_unlocked(s, (size_t) 1, n, stream) == n) ? n : EOF;
 
#else /* __STDIO_WIDE */
 
return (_stdio_fwrite(s, n, stream) == n) ? n : EOF;
 
#endif /* __STDIO_WIDE */
}
 
#endif
/**********************************************************************/
#ifdef L_getc
#undef getc
#undef getc_unlocked
 
/* Reentrancy handled by UNLOCKED() macro. */
 
UNLOCKED(int,getc,(register FILE *stream),(stream))
{
return __GETC(stream); /* Invoke the macro. */
}
 
#endif
/**********************************************************************/
#ifdef L_getchar
#undef getchar /* Just in case. */
 
/* Reentrancy handled by UNLOCKED() macro. */
 
UNLOCKED_STREAM(int,getchar,(void),(),stdin)
{
register FILE *stream = stdin; /* This helps bcc optimize. */
 
return __GETC(stream);
}
 
#endif
/**********************************************************************/
#ifdef L_gets
 
link_warning(gets, "the 'gets' function is dangerous and should not be used.")
 
/* Reentrant. */
 
char *gets(char *s) /* WARNING!!! UNSAFE FUNCTION!!! */
{
register FILE *stream = stdin; /* This helps bcc optimize. */
register char *p = s;
int c;
 
__STDIO_THREADLOCK(stream);
 
/* Note: don't worry about performance here... this shouldn't be used!
* Therefore, force actual function call. */
while (((c = (getc_unlocked)(stream)) != EOF) && ((*p = c) != '\n')) {
++p;
}
if ((c == EOF) || (s == p)) {
s = NULL;
} else {
*p = 0;
}
 
__STDIO_THREADUNLOCK(stream);
 
return s;
}
 
#endif
/**********************************************************************/
#ifdef L_putc
#undef putc
#undef putc_unlocked
 
/* Reentrancy handled by UNLOCKED() macro. */
 
UNLOCKED(int,putc,(int c, register FILE *stream),(c,stream))
{
return __PUTC(c, stream); /* Invoke the macro. */
}
 
#endif
/**********************************************************************/
#ifdef L_putchar
#undef putchar /* Just in case. */
 
/* Reentrancy handled by UNLOCKED() macro. */
 
UNLOCKED_STREAM(int,putchar,(int c),(c),stdout)
{
register FILE *stream = stdout; /* This helps bcc optimize. */
 
return __PUTC(c, stream);
}
 
#endif
/**********************************************************************/
#ifdef L_puts
 
/* Reentrant. */
 
int puts(register const char *s)
{
register FILE *stream = stdout; /* This helps bcc optimize. */
int n;
 
__STDIO_THREADLOCK(stream);
 
n = fputs_unlocked(s,stream) + 1;
if (
#if 1
fputc_unlocked('\n',stream)
#else
fputs_unlocked("\n",stream)
#endif
== EOF) {
n = EOF;
}
 
__STDIO_THREADUNLOCK(stream);
 
return n;
}
 
#endif
/**********************************************************************/
#ifdef L_ungetc
/*
* Note: This is the application-callable ungetc. If scanf calls this, it
* should also set stream->ungot[1] to 0 if this is the only ungot.
*/
 
/* Reentrant. */
 
int ungetc(int c, register FILE *stream)
{
__STDIO_THREADLOCK(stream);
 
__stdio_validate_FILE(stream); /* debugging only */
 
#ifdef __STDIO_WIDE
if (stream->modeflags & __FLAG_WIDE) {
stream->modeflags |= __FLAG_ERROR;
c = EOF;
goto DONE;
}
stream->modeflags |= __FLAG_NARROW;
#endif /* __STDIO_WIDE */
 
/* If can't read or c == EOF or ungot slots already filled, then fail. */
if ((stream->modeflags
& (__MASK_UNGOT2|__FLAG_WRITEONLY
#ifndef __STDIO_AUTO_RW_TRANSITION
|__FLAG_WRITING /* Note: technically no, but yes in spirit */
#endif /* __STDIO_AUTO_RW_TRANSITION */
))
|| ((stream->modeflags & __MASK_UNGOT1) && (stream->ungot[1]))
|| (c == EOF) ) {
c = EOF;
goto DONE;;
}
 
#ifdef __STDIO_BUFFERS
#ifdef __STDIO_AUTO_RW_TRANSITION
if (stream->modeflags & __FLAG_WRITING) {
fflush_unlocked(stream); /* Commit any write-buffered chars. */
}
#endif /* __STDIO_AUTO_RW_TRANSITION */
#endif /* __STDIO_BUFFERS */
 
/* Clear EOF and WRITING flags, and set READING FLAG */
stream->modeflags &= ~(__FLAG_EOF|__FLAG_WRITING);
#ifdef __UCLIBC_MJN3_ONLY__
#warning CONSIDER: Is setting the reading flag after an ungetc necessary?
#endif /* __UCLIBC_MJN3_ONLY__ */
stream->modeflags |= __FLAG_READING;
stream->ungot[1] = 1; /* Flag as app ungetc call; scanf fixes up. */
stream->ungot[(stream->modeflags++) & __MASK_UNGOT] = c;
 
#ifdef __STDIO_GETC_MACRO
stream->bufgetc = stream->bufstart; /* Must disable getc macro. */
#endif
 
__stdio_validate_FILE(stream); /* debugging only */
 
DONE:
__STDIO_THREADUNLOCK(stream);
 
return c;
}
 
#endif
/**********************************************************************/
#ifdef L_fread
/* NOTE: Surely ptr cannot point to a buffer of size > SIZE_MAX.
* Therefore, we treat the case size * nmemb > SIZE_MAX as an error,
* and include an assert() for it. */
 
/* Reentrancy handled by UNLOCKED() macro. */
 
UNLOCKED(size_t,fread,
(void * __restrict ptr, size_t size, size_t nmemb,
FILE * __restrict stream),
(ptr,size,nmemb,stream))
{
#ifdef __STDIO_WIDE
if (stream->modeflags & __FLAG_WIDE) {
stream->modeflags |= __FLAG_ERROR;
/* TODO -- errno? it this correct? */
return 0;
}
stream->modeflags |= __FLAG_NARROW;
#endif /* __STDIO_WIDE */
 
return (size == 0)
? 0
: ( assert( ((size_t)(-1)) / size >= nmemb ),
_stdio_fread(ptr, nmemb * size, stream) / size );
}
 
#endif
/**********************************************************************/
#ifdef L_fwrite
/* NOTE: Surely ptr cannot point to a buffer of size > SIZE_MAX.
* Therefore, we treat the case size * nmemb > SIZE_MAX as an error,
* and include an assert() for it. */
 
/* Reentrancy handled by UNLOCKED() macro. */
 
UNLOCKED(size_t,fwrite,
(const void * __restrict ptr, size_t size, size_t nmemb,
FILE * __restrict stream),
(ptr,size,nmemb,stream))
{
#ifdef __STDIO_WIDE
if (stream->modeflags & __FLAG_WIDE) {
stream->modeflags |= __FLAG_ERROR;
/* TODO -- errno? it this correct? */
return 0;
}
stream->modeflags |= __FLAG_NARROW;
#endif /* __STDIO_WIDE */
 
return (size == 0)
? 0
: ( assert( ((size_t)(-1)) / size >= nmemb ),
_stdio_fwrite(ptr, nmemb * size, stream) / size );
}
 
#endif
/**********************************************************************/
#if defined(L_fgetpos) || defined(L_fgetpos64)
 
/* Reentrant -- fgetpos() and fgetpos64(). */
 
#if defined(L_fgetpos) && defined(L_fgetpos64)
#error L_fgetpos and L_fgetpos64 are defined simultaneously!
#endif
 
#ifndef L_fgetpos64
#define fgetpos64 fgetpos
#define fpos64_t fpos_t
#define ftello64 ftell
#endif
 
 
int fgetpos64(FILE * __restrict stream, register fpos64_t * __restrict pos)
{
int retval = -1;
 
#ifdef __STDIO_WIDE
 
if (pos == NULL) {
__set_errno(EINVAL);
} else {
__STDIO_THREADLOCK(stream);
 
if ((pos->__pos = ftello64(stream)) >= 0) {
__COPY_MBSTATE(&(pos->__mbstate), &(stream->state));
pos->mblen_pending = stream->ungot_width[0];
retval = 0;
}
 
__STDIO_THREADUNLOCK(stream);
}
 
#else /* __STDIO_WIDE */
 
if (pos == NULL) {
__set_errno(EINVAL);
} else if ((pos->__pos = ftello64(stream)) >= 0) {
retval = 0;
}
 
#endif /* __STDIO_WIDE */
 
return retval;
}
 
#ifndef L_fgetpos64
#undef fgetpos64
#undef fpos64_t
#undef ftello64
#endif
 
#endif
/**********************************************************************/
#ifdef L_fseek
strong_alias(fseek,fseeko);
#endif
 
#if defined(L_fseek) && defined(__STDIO_LARGE_FILES)
 
int fseek(register FILE *stream, long int offset, int whence)
{
return fseeko64(stream, offset, whence);
}
 
#endif
 
#if defined(L_fseeko64) || (defined(L_fseek) && !defined(__STDIO_LARGE_FILES))
 
#ifndef L_fseeko64
#define fseeko64 fseek
#define __off64_t long int
#endif
 
/* Reentrant -- fseek(), fseeko(), fseeko64() */
 
int fseeko64(register FILE *stream, __off64_t offset, int whence)
{
#if SEEK_SET != 0 || SEEK_CUR != 1 || SEEK_END != 2
#error Assumption violated -- values of SEEK_SET, SEEK_CUR, SEEK_END
#endif
__offmax_t pos[1];
int retval;
 
if ( ((unsigned int) whence) > 2 ) {
__set_errno(EINVAL);
return -1;
}
 
__STDIO_THREADLOCK(stream);
 
__stdio_validate_FILE(stream); /* debugging only */
 
retval = -1;
*pos = offset;
if (
#ifdef __STDIO_BUFFERS
/* First commit any pending buffered writes. */
((stream->modeflags & __FLAG_WRITING) && fflush_unlocked(stream)) ||
#endif /* __STDIO_BUFFERS */
((whence == SEEK_CUR) && (_stdio_adjpos(stream, pos) < 0))
|| (_stdio_lseek(stream, pos, whence) < 0)
) {
__stdio_validate_FILE(stream); /* debugging only */
goto DONE;
}
 
#ifdef __STDIO_BUFFERS
/* only needed if reading but do it anyway to avoid test */
#ifdef __STDIO_GETC_MACRO
stream->bufgetc = /* Must disable getc. */
#endif
stream->bufpos = stream->bufread = stream->bufstart;
#endif /* __STDIO_BUFFERS */
 
stream->modeflags &=
~(__FLAG_READING|__FLAG_WRITING|__FLAG_EOF|__MASK_UNGOT);
 
#ifdef __UCLIBC_MJN3_ONLY__
#warning CONSIDER: How do we deal with fseek to an ftell position for incomplete or error wide? Right now, we clear all multibyte state info. If we do not clear, then fix rewind() to do so if fseek() succeeds.
#endif /* __UCLIBC_MJN3_ONLY__ */
 
#ifdef __STDIO_WIDE
/* TODO: don't clear state if don't move? */
stream->ungot_width[0] = 0;
#endif /* __STDIO_WIDE */
#ifdef __STDIO_MBSTATE
/* TODO: don't clear state if don't move? */
__INIT_MBSTATE(&(stream->state));
#endif /* __STDIO_MBSTATE */
 
__stdio_validate_FILE(stream); /* debugging only */
 
retval = 0;
 
DONE:
__STDIO_THREADUNLOCK(stream);
 
return retval;
}
 
#ifndef L_fseeko64
#undef fseeko64
#undef __off64_t
#endif
 
#endif
/**********************************************************************/
#if defined(L_fsetpos) || defined(L_fsetpos64)
 
#if defined(L_fsetpos) && defined(L_fsetpos64)
#error L_fsetpos and L_fsetpos64 are defined simultaneously!
#endif
 
#ifndef L_fsetpos64
#define fsetpos64 fsetpos
#define fpos64_t fpos_t
#define fseeko64 fseek
#endif
 
/* Reentrant -- fgetpos{64}() through fseek{64}(). */
 
int fsetpos64(FILE *stream, register const fpos64_t *pos)
{
if (!pos) {
__set_errno(EINVAL);
return EOF;
}
#ifdef __STDIO_WIDE
{
int retval;
 
__STDIO_THREADLOCK(stream);
 
if ((retval = fseeko64(stream, pos->__pos, SEEK_SET)) == 0) {
__COPY_MBSTATE(&(stream->state), &(pos->__mbstate));
#ifdef __UCLIBC_MJN3_ONLY__
#warning CONSIDER: Moving mblen_pending into some mbstate field might be useful. But we would need to modify all the mb<->wc funcs.
#endif /* __UCLIBC_MJN3_ONLY__ */
stream->ungot_width[0] = pos->mblen_pending;
}
 
__STDIO_THREADUNLOCK(stream);
 
return retval;
}
#else /* __STDIO_WIDE */
return fseeko64(stream, pos->__pos, SEEK_SET);
#endif /* __STDIO_WIDE */
}
 
#ifndef L_fsetpos64
#undef fsetpos64
#undef fpos64_t
#undef fseeko64
#endif
 
#endif
/**********************************************************************/
#ifdef L_ftell
strong_alias(ftell,ftello);
#endif
 
#if defined(L_ftell) && defined(__STDIO_LARGE_FILES)
long int ftell(register FILE *stream)
{
__offmax_t pos = ftello64(stream);
 
return (pos == ((long int) pos)) ? pos : (__set_errno(EOVERFLOW), -1);
}
#endif
 
#if defined(L_ftello64) || (defined(L_ftell) && !defined(__STDIO_LARGE_FILES))
 
#ifndef L_ftello64
#define ftello64 ftell
#define __off64_t long int
#endif
 
/* Reentrant -- ftell, ftello, ftello64. */
 
__off64_t ftello64(register FILE *stream)
{
__offmax_t pos[1];
__off64_t retval;
 
__STDIO_THREADLOCK(stream);
 
retval = (((*pos = 0), (_stdio_lseek(stream, pos, SEEK_CUR) < 0))
|| (_stdio_adjpos(stream, pos) < 0)) ? -1 : *pos;
 
__STDIO_THREADUNLOCK(stream);
 
return retval;
}
 
#ifndef L_ftello64
#undef ftello64
#undef __off64_t
#endif
 
#endif
/**********************************************************************/
#ifdef L_rewind
 
void rewind(register FILE *stream)
{
__STDIO_THREADLOCK(stream);
 
__CLEARERR(stream); /* Clear errors first and then seek */
fseek(stream, 0L, SEEK_SET); /* in case there is an error seeking. */
 
__STDIO_THREADUNLOCK(stream);
}
 
#endif
/**********************************************************************/
#ifdef L_clearerr
#undef clearerr
 
/* Reentrancy handled by UNLOCKED_VOID_RETURN() macro. */
 
UNLOCKED_VOID_RETURN(clearerr,(FILE *stream),(stream))
{
__CLEARERR(stream);
}
 
#endif
/**********************************************************************/
#ifdef L_feof
#undef feof
 
/* Reentrancy handled by UNLOCKED() macro. */
 
UNLOCKED(int,feof,(FILE *stream),(stream))
{
return __FEOF(stream);
}
 
#endif
/**********************************************************************/
#ifdef L_ferror
#undef ferror
 
/* Reentrancy handled by UNLOCKED() macro. */
 
UNLOCKED(int,ferror,(FILE *stream),(stream))
{
return __FERROR(stream);
}
 
#endif
/**********************************************************************/
#ifdef L_perror
 
void perror(register const char *s)
{
/* If the program is calling perror, it's a safe bet that printf and
* friends are used as well. It is also possible that the calling
* program could buffer stderr, or reassign it. */
 
register const char *sep;
 
sep = ": ";
if (!(s && *s)) { /* Caller did not supply a prefix message */
s = (sep += 2); /* or passed an empty string. */
}
 
#if 1
#ifdef __STDIO_PRINTF_M_SPEC
fprintf(stderr, "%s%s%m\n", s, sep); /* Use the gnu %m feature. */
#else
{
char buf[64];
fprintf(stderr, "%s%s%s\n", s, sep,
_glibc_strerror_r(errno, buf, sizeof(buf)));
}
#endif
#else
/* Note: Assumes stderr not closed or buffered. */
{
char buf[64];
 
__STDIO_THREADLOCK(stderr);
_stdio_fdout(STDERR_FILENO, s, sep,
_glibc_strerror_r(errno, buf, sizeof(buf)));
__STDIO_THREADUNLOCK(stderr);
}
#endif
}
 
#endif
/**********************************************************************/
/* UTILITY funcs */
/**********************************************************************/
#ifdef L__stdio_fdout
 
/* Not reentrant -- TODO: lock associated stream if a know file descriptor? */
 
void _stdio_fdout(int fd, ...)
{
va_list arg;
register const char *p;
 
va_start(arg, fd);
while ((p = va_arg(arg, const char *)) != NULL) {
write(fd, p, strlen(p));
}
va_end(arg);
}
 
#endif
/**********************************************************************/
#ifdef L__uintmaxtostr
 
/* Avoid using long long / and % operations to cut down dependencies on
* libgcc.a. Definitely helps on i386 at least. */
#if (INTMAX_MAX > INT_MAX) && (((INTMAX_MAX/INT_MAX)/2) - 2 <= INT_MAX)
#define INTERNAL_DIV_MOD
#endif
 
#include <locale.h>
 
char *_uintmaxtostr(register char * __restrict bufend, uintmax_t uval,
int base, __UIM_CASE alphacase)
{
int negative;
unsigned int digit;
#ifdef INTERNAL_DIV_MOD
unsigned int H, L, high, low, rh;
#endif
#ifndef __LOCALE_C_ONLY
int grouping, outdigit;
const char *g; /* This does not need to be initialized. */
#endif /* __LOCALE_C_ONLY */
 
negative = 0;
if (base < 0) { /* signed value */
base = -base;
if (uval > INTMAX_MAX) {
uval = -uval;
negative = 1;
}
}
 
/* this is an internal routine -- we shouldn't need to check this */
assert(!((base < 2) || (base > 36)));
 
#ifndef __LOCALE_C_ONLY
grouping = -1;
outdigit = 0x80 & alphacase;
alphacase ^= outdigit;
if (alphacase == __UIM_GROUP) {
assert(base == 10);
if (*(g = __UCLIBC_CURLOCALE_DATA.grouping)) {
grouping = *g;
}
}
#endif /* __LOCALE_C_ONLY */
 
*bufend = '\0';
 
#ifndef INTERNAL_DIV_MOD
do {
#ifndef __LOCALE_C_ONLY
if (!grouping) { /* Finished a group. */
bufend -= __UCLIBC_CURLOCALE_DATA.thousands_sep_len;
memcpy(bufend, __UCLIBC_CURLOCALE_DATA.thousands_sep,
__UCLIBC_CURLOCALE_DATA.thousands_sep_len);
if (g[1] != 0) { /* g[1] == 0 means repeat last grouping. */
/* Note: g[1] == -1 means no further grouping. But since
* we'll never wrap around, we can set grouping to -1 without
* fear of */
++g;
}
grouping = *g;
}
--grouping;
#endif /* __LOCALE_C_ONLY */
digit = uval % base;
uval /= base;
 
#ifndef __LOCALE_C_ONLY
if (unlikely(outdigit)) {
bufend -= __UCLIBC_CURLOCALE_DATA.outdigit_length[digit];
memcpy(bufend,
(&__UCLIBC_CURLOCALE_DATA.outdigit0_mb)[digit],
__UCLIBC_CURLOCALE_DATA.outdigit_length[digit]);
} else
#endif
{
*--bufend = ( (digit < 10) ? digit + '0' : digit + alphacase );
}
} while (uval);
 
#else /* ************************************************** */
 
H = (UINT_MAX / base);
L = UINT_MAX % base + 1;
if (L == base) {
++H;
L = 0;
}
low = (unsigned int) uval;
high = (unsigned int) (uval >> (sizeof(unsigned int) * CHAR_BIT));
 
do {
#ifndef __LOCALE_C_ONLY
if (!grouping) { /* Finished a group. */
bufend -= __UCLIBC_CURLOCALE_DATA.thousands_sep_len;
memcpy(bufend, __UCLIBC_CURLOCALE_DATA.thousands_sep,
__UCLIBC_CURLOCALE_DATA.thousands_sep_len);
if (g[1] != 0) { /* g[1] == 0 means repeat last grouping. */
/* Note: g[1] == -1 means no further grouping. But since
* we'll never wrap around, we can set grouping to -1 without
* fear of */
++g;
}
grouping = *g;
}
--grouping;
#endif /* __LOCALE_C_ONLY */
 
if (unlikely(high)) {
rh = high % base;
high /= base;
digit = (low % base) + (L * rh);
low = (low / base) + (H * rh) + (digit / base);
digit %= base;
} else {
digit = low % base;
low /= base;
}
#ifndef __LOCALE_C_ONLY
if (unlikely(outdigit)) {
bufend -= __UCLIBC_CURLOCALE_DATA.outdigit_length[digit];
memcpy(bufend,
(&__UCLIBC_CURLOCALE_DATA.outdigit0_mb)[digit],
__UCLIBC_CURLOCALE_DATA.outdigit_length[digit]);
} else
#endif
{
*--bufend = ( (digit < 10) ? digit + '0' : digit + alphacase );
}
} while (low | high);
 
#endif /******************************************************/
 
if (negative) {
*--bufend = '-';
}
 
return bufend;
}
#undef INTERNAL_DIV_MOD
 
#endif
/**********************************************************************/
#ifdef L__wstdio_fwrite
 
#include <wchar.h>
 
size_t _wstdio_fwrite(const wchar_t *__restrict ws, size_t n,
register FILE *__restrict stream)
{
size_t r, count;
char buf[64];
const wchar_t *pw;
 
#if defined(__STDIO_WIDE) && defined(__STDIO_BUFFERS)
if (stream->filedes == -3) { /* Special case to support {v}swprintf. */
count = ((wchar_t *)(stream->bufend)) - ((wchar_t *)(stream->bufpos));
if (count > n) {
count = n;
}
if (count) {
wmemcpy((wchar_t *)(stream->bufpos), ws, count);
stream->bufpos = (char *)(((wchar_t *)(stream->bufpos)) + count);
}
return n;
}
#endif /* defined(__STDIO_WIDE) && defined(__STDIO_BUFFERS) */
 
if (stream->modeflags & __FLAG_NARROW) {
stream->modeflags |= __FLAG_ERROR;
__set_errno(EBADF);
return 0;
}
stream->modeflags |= __FLAG_WIDE;
 
pw = ws;
count = 0;
while (n > count) {
r = wcsnrtombs(buf, &pw, n-count, sizeof(buf), &stream->state);
if (r != ((size_t) -1)) { /* No encoding errors */
if (!r) {
++r; /* 0 is returned when nul is reached. */
pw = ws + count + r; /* pw was set to NULL, so correct. */
}
if (_stdio_fwrite(buf, r, stream) == r) {
count = pw - ws;
continue;
}
}
break;
}
 
/* Note: The count is incorrect if 0 < _stdio_fwrite return < r!!! */
return count;
}
 
#endif
/**********************************************************************/
/printf.c
0,0 → 1,3272
/* Copyright (C) 2002, 2003 Manuel Novoa III
* My stdio library for linux and (soon) elks.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
/* This code needs a lot of clean up. Some of that is on hold until uClibc
* gets a better configuration system (on Erik's todo list).
* The other cleanup will take place during the implementation/integration of
* the wide char (un)formatted i/o functions which I'm currently working on.
*/
 
/* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION!
*
* This code is currently under development. Also, I plan to port
* it to elks which is a 16-bit environment with a fairly limited
* compiler. Therefore, please refrain from modifying this code
* and, instead, pass any bug-fixes, etc. to me. Thanks. Manuel
*
* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! */
 
 
/* April 1, 2002
* Initialize thread locks for fake files in vsnprintf and vdprintf.
* reported by Erik Andersen (andersen@codepoet.com)
* Fix an arg promotion handling bug in _do_one_spec for %c.
* reported by Ilguiz Latypov <ilatypov@superbt.com>
*
* May 10, 2002
* Remove __isdigit and use new ctype.h version.
* Add conditional setting of QUAL_CHARS for size_t and ptrdiff_t.
*
* Aug 16, 2002
* Fix two problems that showed up with the python 2.2.1 tests; one
* involving %o and one involving %f.
*
* Oct 28, 2002
* Fix a problem in vasprintf (reported by vodz a while back) when built
* without custom stream support. In that case, it is necessary to do
* a va_copy.
* Make sure each va_copy has a matching va_end, as required by C99.
*
* Nov 4, 2002
* Add locale-specific grouping support for integer decimal conversion.
* Add locale-specific decimal point support for floating point conversion.
* Note: grouping will have to wait for _dtostr() rewrite.
* Add printf wchar support for %lc (%C) and %ls (%S).
* Require printf format strings to be valid multibyte strings beginning and
* ending in their initial shift state, as per the stds.
*
* Nov 21, 2002
* Add *wprintf functions. Currently they don't support floating point
* conversions. That will wait until the rewrite of _dtostr.
*
* Aug 1, 2003
* Optional hexadecimal float notation support for %a/%A.
* Floating point output now works for *wprintf.
* Support for glibc locale-specific digit grouping for floats.
* Misc bug fixes.
*
* Aug 31, 2003
* Fix precision bug for %g conversion specifier when using %f style.
*
* Sep 5, 2003
* Implement *s*scanf for the non-buffered stdio case with old_vfprintf.
*
* Sep 23, 2003
* vfprintf was not always checking for narrow stream orientation.
*/
 
/* TODO:
*
* Should we validate that *printf format strings are valid multibyte
* strings in the current locale? ANSI/ISO C99 seems to imply this
* and Plauger's printf implementation in his Standard C Library book
* treats this as an error.
*/
 
 
#define _ISOC99_SOURCE /* for ULLONG primarily... */
#define _GNU_SOURCE
#define _STDIO_UTILITY /* We're using _uintmaxtostr. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <ctype.h>
#include <limits.h>
#include <stdarg.h>
#include <assert.h>
#include <stdint.h>
#include <errno.h>
#include <locale.h>
 
#define __PRINTF_INFO_NO_BITFIELD
#include <printf.h>
 
#ifdef __STDIO_THREADSAFE
#include <stdio_ext.h>
#include <pthread.h>
#endif /* __STDIO_THREADSAFE */
 
#ifdef __UCLIBC_HAS_WCHAR__
#include <wchar.h>
#endif /* __UCLIBC_HAS_WCHAR__ */
 
/* Some older or broken gcc toolchains define LONG_LONG_MAX but not
* LLONG_MAX. Since LLONG_MAX is part of the standard, that's what
* we use. So complain if we do not have it but should.
*/
#if !defined(LLONG_MAX) && defined(LONG_LONG_MAX)
#error Apparently, LONG_LONG_MAX is defined but LLONG_MAX is not. You need to fix your toolchain headers to support the standard macros for (unsigned) long long.
#endif
 
/**********************************************************************/
/* These provide some control over printf's feature set */
 
/* This is undefined below depeding on uClibc's configuration. */
#define __STDIO_PRINTF_FLOAT 1
 
/* Now controlled by uClibc_stdio.h. */
/* #define __STDIO_PRINTF_M_SUPPORT */
 
 
/**********************************************************************/
 
#if defined(__UCLIBC__) && !defined(__UCLIBC_HAS_FLOATS__)
#undef __STDIO_PRINTF_FLOAT
#endif
 
#ifdef __BCC__
#undef __STDIO_PRINTF_FLOAT
#endif
 
#ifdef __STDIO_PRINTF_FLOAT
#include <float.h>
#include <bits/uClibc_fpmax.h>
#else /* __STDIO_PRINTF_FLOAT */
#undef L__fpmaxtostr
#endif /* __STDIO_PRINTF_FLOAT */
 
 
#undef __STDIO_HAS_VSNPRINTF
#if defined(__STDIO_BUFFERS) || defined(__USE_OLD_VFPRINTF__) || defined(__STDIO_GLIBC_CUSTOM_STREAMS)
#define __STDIO_HAS_VSNPRINTF 1
#endif
 
/**********************************************************************/
 
/* Now controlled by uClibc_stdio.h. */
/* #define __STDIO_GLIBC_CUSTOM_PRINTF */
 
/* TODO -- move these to a configuration section? */
#define MAX_FIELD_WIDTH 4095
 
#ifdef __UCLIBC_MJN3_ONLY__
#ifdef L_register_printf_function
/* emit only once */
#warning WISHLIST: Make MAX_USER_SPEC configurable?
#warning WISHLIST: Make MAX_ARGS_PER_SPEC configurable?
#endif
#endif /* __UCLIBC_MJN3_ONLY__ */
 
#ifdef __STDIO_GLIBC_CUSTOM_PRINTF
 
#define MAX_USER_SPEC 10
#define MAX_ARGS_PER_SPEC 5
 
#else /* __STDIO_GLIBC_CUSTOM_PRINTF */
 
#undef MAX_USER_SPEC
#define MAX_ARGS_PER_SPEC 1
 
#endif /* __STDIO_GLIBC_CUSTOM_PRINTF */
 
#if MAX_ARGS_PER_SPEC < 1
#error MAX_ARGS_PER_SPEC < 1!
#undef MAX_ARGS_PER_SPEC
#define MAX_ARGS_PER_SPEC 1
#endif
 
#if defined(NL_ARGMAX) && (NL_ARGMAX < 9)
#error NL_ARGMAX < 9!
#endif
 
#if defined(NL_ARGMAX) && (NL_ARGMAX >= (MAX_ARGS_PER_SPEC + 2))
#define MAX_ARGS NL_ARGMAX
#else
/* N for spec itself, plus 1 each for width and precision */
#define MAX_ARGS (MAX_ARGS_PER_SPEC + 2)
#endif
 
 
/**********************************************************************/
/* Deal with pre-C99 compilers. */
 
#ifndef va_copy
 
#ifdef __va_copy
#define va_copy(A,B) __va_copy(A,B)
#else
/* TODO -- maybe create a bits/vacopy.h for arch specific versions
* to ensure we get the right behavior? Either that or fall back
* on the portable (but costly in size) method of using a va_list *.
* That means a pointer derefs in the va_arg() invocations... */
#warning Neither va_copy (C99/SUSv3) or __va_copy is defined. Using a simple copy instead. But you should really check that this is appropriate...
/* the glibc manual suggests that this will usually suffice when
__va_copy doesn't exist. */
#define va_copy(A,B) A = B
#endif
 
#endif /* va_copy */
 
/**********************************************************************/
 
#define __PA_FLAG_INTMASK \
(__PA_FLAG_CHAR|PA_FLAG_SHORT|__PA_FLAG_INT|PA_FLAG_LONG|PA_FLAG_LONG_LONG)
 
#ifdef __STDIO_GLIBC_CUSTOM_PRINTF
extern printf_function _custom_printf_handler[MAX_USER_SPEC];
extern printf_arginfo_function *_custom_printf_arginfo[MAX_USER_SPEC];
extern char *_custom_printf_spec;
#endif /* __STDIO_GLIBC_CUSTOM_PRINTF */
 
/**********************************************************************/
 
#define SPEC_FLAGS " +0-#'I"
enum {
FLAG_SPACE = 0x01,
FLAG_PLUS = 0x02, /* must be 2 * FLAG_SPACE */
FLAG_ZERO = 0x04,
FLAG_MINUS = 0x08, /* must be 2 * FLAG_ZERO */
FLAG_HASH = 0x10,
FLAG_THOUSANDS = 0x20,
FLAG_I18N = 0x40, /* only works for d, i, u */
FLAG_WIDESTREAM = 0x80
};
 
/**********************************************************************/
 
/* float layout 01234567890123456789 TODO: B?*/
#define SPEC_CHARS "npxXoudifFeEgGaACScs"
enum {
CONV_n = 0,
CONV_p,
CONV_x, CONV_X, CONV_o, CONV_u, CONV_d, CONV_i,
CONV_f, CONV_F, CONV_e, CONV_E, CONV_g, CONV_G, CONV_a, CONV_A,
CONV_C, CONV_S, CONV_c, CONV_s,
#ifdef __STDIO_PRINTF_M_SUPPORT
CONV_m,
#endif
CONV_custom0 /* must be last */
};
 
/* p x X o u d i */
#define SPEC_BASE { 16, 16, 16, 8, 10, 10, 10 }
 
#define SPEC_RANGES { CONV_n, CONV_p, CONV_i, CONV_A, \
CONV_C, CONV_S, CONV_c, CONV_s, CONV_custom0 }
 
#define SPEC_OR_MASK { \
/* n */ (PA_FLAG_PTR|PA_INT), \
/* p */ PA_POINTER, \
/* oxXudi */ PA_INT, \
/* fFeEgGaA */ PA_DOUBLE, \
/* C */ PA_WCHAR, \
/* S */ PA_WSTRING, \
/* c */ PA_CHAR, \
/* s */ PA_STRING, \
}
 
#define SPEC_AND_MASK { \
/* n */ (PA_FLAG_PTR|__PA_INTMASK), \
/* p */ PA_POINTER, \
/* oxXudi */ (__PA_INTMASK), \
/* fFeEgGaA */ (PA_FLAG_LONG_DOUBLE|PA_DOUBLE), \
/* C */ (PA_WCHAR), \
/* S */ (PA_WSTRING), \
/* c */ (PA_CHAR), \
/* s */ (PA_STRING), \
}
 
/**********************************************************************/
/*
* In order to ease translation to what arginfo and _print_info._flags expect,
* we map: 0:int 1:char 2:longlong 4:long 8:short
* and then _flags |= (((q << 7) + q) & 0x701) and argtype |= (_flags & 0x701)
*/
 
/* TODO -- Fix the table below to take into account stdint.h. */
/* #ifndef LLONG_MAX */
/* #error fix QUAL_CHARS for no long long! Affects 'L', 'j', 'q', 'll'. */
/* #else */
/* #if LLONG_MAX != INTMAX_MAX */
/* #error fix QUAL_CHARS intmax_t entry 'j'! */
/* #endif */
/* #endif */
 
#ifdef PDS
#error PDS already defined!
#endif
#ifdef SS
#error SS already defined!
#endif
#ifdef IMS
#error IMS already defined!
#endif
 
#if PTRDIFF_MAX == INT_MAX
#define PDS 0
#elif PTRDIFF_MAX == LONG_MAX
#define PDS 4
#elif defined(LLONG_MAX) && (PTRDIFF_MAX == LLONG_MAX)
#define PDS 8
#else
#error fix QUAL_CHARS ptrdiff_t entry 't'!
#endif
 
#if SIZE_MAX == UINT_MAX
#define SS 0
#elif SIZE_MAX == ULONG_MAX
#define SS 4
#elif defined(LLONG_MAX) && (SIZE_MAX == ULLONG_MAX)
#define SS 8
#else
#error fix QUAL_CHARS size_t entries 'z', 'Z'!
#endif
 
#if INTMAX_MAX == INT_MAX
#define IMS 0
#elif INTMAX_MAX == LONG_MAX
#define IMS 4
#elif defined(LLONG_MAX) && (INTMAX_MAX == LLONG_MAX)
#define IMS 8
#else
#error fix QUAL_CHARS intmax_t entry 'j'!
#endif
 
#define QUAL_CHARS { \
/* j:(u)intmax_t z:(s)size_t t:ptrdiff_t \0:int */ \
/* q:long_long Z:(s)size_t */ \
'h', 'l', 'L', 'j', 'z', 't', 'q', 'Z', 0, \
2, 4, 8, IMS, SS, PDS, 8, SS, 0, /* TODO -- fix!!! */\
1, 8 \
}
 
/**********************************************************************/
 
#ifdef __STDIO_VA_ARG_PTR
#ifdef __BCC__
#define __va_arg_ptr(ap,type) (((type *)(ap += sizeof(type))) - 1)
#endif
 
#if 1
#ifdef __GNUC__
/* TODO -- need other than for 386 as well! */
 
#ifndef __va_rounded_size
#define __va_rounded_size(TYPE) \
(((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))
#endif
#define __va_arg_ptr(AP, TYPE) \
(AP = (va_list) ((char *) (AP) + __va_rounded_size (TYPE)), \
((void *) ((char *) (AP) - __va_rounded_size (TYPE))))
#endif
#endif
#endif /* __STDIO_VA_ARG_PTR */
 
#ifdef __va_arg_ptr
#define GET_VA_ARG(AP,F,TYPE,ARGS) (*(AP) = __va_arg_ptr(ARGS,TYPE))
#define GET_ARG_VALUE(AP,F,TYPE) (*((TYPE *)(*(AP))))
#else
typedef union {
wchar_t wc;
unsigned int u;
unsigned long ul;
#ifdef ULLONG_MAX
unsigned long long ull;
#endif
#ifdef __STDIO_PRINTF_FLOAT
double d;
long double ld;
#endif /* __STDIO_PRINTF_FLOAT */
void *p;
} argvalue_t;
 
#define GET_VA_ARG(AU,F,TYPE,ARGS) (AU->F = va_arg(ARGS,TYPE))
#define GET_ARG_VALUE(AU,F,TYPE) ((TYPE)((AU)->F))
#endif
 
typedef struct {
const char *fmtpos; /* TODO: move below struct?? */
struct printf_info info;
#ifdef NL_ARGMAX
int maxposarg; /* > 0 if args are positional, 0 if not, -1 if unknown */
#endif /* NL_ARGMAX */
int num_data_args; /* TODO: use sentinal??? */
unsigned int conv_num;
unsigned char argnumber[4]; /* width | prec | 1st data | unused */
int argtype[MAX_ARGS];
va_list arg;
#ifdef __va_arg_ptr
void *argptr[MAX_ARGS];
#else
/* if defined(NL_ARGMAX) || defined(__STDIO_GLIBC_CUSTOM_PRINTF) */
/* While this is wasteful of space in the case where pos args aren't
* enabled, it is also needed to support custom printf handlers. */
argvalue_t argvalue[MAX_ARGS];
#endif
} ppfs_t; /* parse printf format state */
 
/**********************************************************************/
 
/* TODO: fix printf to return 0 and set errno if format error. Standard says
only returns -1 if sets error indicator for the stream. */
 
#ifdef __STDIO_PRINTF_FLOAT
typedef void (__fp_outfunc_t)(FILE *fp, intptr_t type, intptr_t len,
intptr_t buf);
 
extern size_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info,
__fp_outfunc_t fp_outfunc);
#endif
 
extern int _ppfs_init(ppfs_t *ppfs, const char *fmt0); /* validates */
extern void _ppfs_prepargs(ppfs_t *ppfs, va_list arg); /* sets posargptrs */
extern void _ppfs_setargs(ppfs_t *ppfs); /* sets argptrs for current spec */
extern int _ppfs_parsespec(ppfs_t *ppfs); /* parses specifier */
 
extern void _store_inttype(void *dest, int desttype, uintmax_t val);
extern uintmax_t _load_inttype(int desttype, const void *src, int uflag);
 
/**********************************************************************/
#ifdef L_parse_printf_format
 
/* NOTE: This function differs from the glibc version in that parsing stops
* upon encountering an invalid conversion specifier. Since this is the way
* my printf functions work, I think it makes sense to do it that way here.
* Unfortunately, since glibc sets the return type as size_t, we have no way
* of returning that the template is illegal, other than returning 0.
*/
 
size_t parse_printf_format(register const char *template,
size_t n, register int *argtypes)
{
ppfs_t ppfs;
size_t i;
size_t count = 0;
 
if (_ppfs_init(&ppfs, template) >= 0) {
#ifdef NL_ARGMAX
if (ppfs.maxposarg > 0) { /* Using positional args. */
count = ppfs.maxposarg;
if (n > count) {
n = count;
}
for (i = 0 ; i < n ; i++) {
*argtypes++ = ppfs.argtype[i];
}
} else { /* Not using positional args. */
#endif /* NL_ARGMAX */
while (*template) {
if ((*template == '%') && (*++template != '%')) {
ppfs.fmtpos = template;
_ppfs_parsespec(&ppfs); /* Can't fail. */
template = ppfs.fmtpos; /* Update to one past spec end. */
if (ppfs.info.width == INT_MIN) {
++count;
if (n > 0) {
*argtypes++ = PA_INT;
--n;
}
}
if (ppfs.info.prec == INT_MIN) {
++count;
if (n > 0) {
*argtypes++ = PA_INT;
--n;
}
}
for (i = 0 ; i < ppfs.num_data_args ; i++) {
if ((ppfs.argtype[i]) != __PA_NOARG) {
++count;
if (n > 0) {
*argtypes++ = ppfs.argtype[i];
--n;
}
}
}
} else {
++template;
}
}
#ifdef NL_ARGMAX
}
#endif /* NL_ARGMAX */
}
 
return count;
}
 
#endif
/**********************************************************************/
#ifdef L__ppfs_init
 
int _ppfs_init(register ppfs_t *ppfs, const char *fmt0)
{
int r;
 
/* First, zero out everything... argnumber[], argtype[], argptr[] */
memset(ppfs, 0, sizeof(ppfs_t)); /* TODO: nonportable???? */
#ifdef NL_ARGMAX
--ppfs->maxposarg; /* set to -1 */
#endif /* NL_ARGMAX */
ppfs->fmtpos = fmt0;
#ifdef __UCLIBC_MJN3_ONLY__
#warning TODO: Make checking of the format string in C locale an option.
#endif
#ifdef __UCLIBC_HAS_LOCALE__
/* To support old programs, don't check mb validity if in C locale. */
if (((__UCLIBC_CURLOCALE_DATA).encoding) != __ctype_encoding_7_bit) {
/* ANSI/ISO C99 requires format string to be a valid multibyte string
* beginning and ending in its initial shift state. */
static const char invalid_mbs[] = "Invalid multibyte format string.";
mbstate_t mbstate;
const char *p;
mbstate.mask = 0; /* Initialize the mbstate. */
p = fmt0;
if (mbsrtowcs(NULL, &p, SIZE_MAX, &mbstate) == ((size_t)(-1))) {
ppfs->fmtpos = invalid_mbs;
return -1;
}
}
#endif /* __UCLIBC_HAS_LOCALE__ */
/* now set all argtypes to no-arg */
{
#if 1
/* TODO - use memset here since already "paid for"? */
register int *p = ppfs->argtype;
r = MAX_ARGS;
do {
*p++ = __PA_NOARG;
} while (--r);
#else
/* TODO -- get rid of this?? */
register char *p = (char *) ((MAX_ARGS-1) * sizeof(int));
 
do {
*((int *)(((char *)ppfs) + ((int)p) + offsetof(ppfs_t,argtype))) = __PA_NOARG;
p -= sizeof(int);
} while (p);
#endif
}
 
/*
* Run through the entire format string to validate it and initialize
* the positional arg numbers (if any).
*/
{
register const char *fmt = fmt0;
 
while (*fmt) {
if ((*fmt == '%') && (*++fmt != '%')) {
ppfs->fmtpos = fmt; /* back up to the '%' */
if ((r = _ppfs_parsespec(ppfs)) < 0) {
return -1;
}
fmt = ppfs->fmtpos; /* update to one past end of spec */
} else {
++fmt;
}
}
ppfs->fmtpos = fmt0; /* rewind */
}
 
#ifdef NL_MAX_ARG
/* If we have positional args, make sure we know all the types. */
{
register int *p = ppfs->argtype;
r = ppfs->maxposarg;
while (--r >= 0) {
if ( *p == __PA_NOARG ) { /* missing arg type!!! */
return -1;
}
++p;
}
}
#endif /* NL_MAX_ARG */
 
return 0;
}
#endif
/**********************************************************************/
#ifdef L__ppfs_prepargs
void _ppfs_prepargs(register ppfs_t *ppfs, va_list arg)
{
int i;
 
va_copy(ppfs->arg, arg);
 
#ifdef NL_ARGMAX
if ((i = ppfs->maxposarg) > 0) { /* init for positional args */
ppfs->num_data_args = i;
ppfs->info.width = ppfs->info.prec = ppfs->maxposarg = 0;
_ppfs_setargs(ppfs);
ppfs->maxposarg = i;
}
#endif /* NL_ARGMAX */
}
#endif
/**********************************************************************/
#ifdef L__ppfs_setargs
 
void _ppfs_setargs(register ppfs_t *ppfs)
{
#ifdef __va_arg_ptr
register void **p = ppfs->argptr;
#else
register argvalue_t *p = ppfs->argvalue;
#endif
int i;
 
#ifdef NL_ARGMAX
if (ppfs->maxposarg == 0) { /* initing for or no pos args */
#endif /* NL_ARGMAX */
if (ppfs->info.width == INT_MIN) {
ppfs->info.width =
#ifdef __va_arg_ptr
*(int *)
#endif
GET_VA_ARG(p,u,unsigned int,ppfs->arg);
}
if (ppfs->info.prec == INT_MIN) {
ppfs->info.prec =
#ifdef __va_arg_ptr
*(int *)
#endif
GET_VA_ARG(p,u,unsigned int,ppfs->arg);
}
i = 0;
while (i < ppfs->num_data_args) {
switch(ppfs->argtype[i++]) {
case (PA_INT|PA_FLAG_LONG_LONG):
#ifdef ULLONG_MAX
GET_VA_ARG(p,ull,unsigned long long,ppfs->arg);
break;
#endif
case (PA_INT|PA_FLAG_LONG):
#if ULONG_MAX != UINT_MAX
GET_VA_ARG(p,ul,unsigned long,ppfs->arg);
break;
#endif
case PA_CHAR: /* TODO - be careful */
/* ... users could use above and really want below!! */
case (PA_INT|__PA_FLAG_CHAR):/* TODO -- translate this!!! */
case (PA_INT|PA_FLAG_SHORT):
case PA_INT:
GET_VA_ARG(p,u,unsigned int,ppfs->arg);
break;
case PA_WCHAR: /* TODO -- assume int? */
/* we're assuming wchar_t is at least an int */
GET_VA_ARG(p,wc,wchar_t,ppfs->arg);
break;
#ifdef __STDIO_PRINTF_FLOAT
/* PA_FLOAT */
case PA_DOUBLE:
GET_VA_ARG(p,d,double,ppfs->arg);
break;
case (PA_DOUBLE|PA_FLAG_LONG_DOUBLE):
GET_VA_ARG(p,ld,long double,ppfs->arg);
break;
#else /* __STDIO_PRINTF_FLOAT */
case PA_DOUBLE:
case (PA_DOUBLE|PA_FLAG_LONG_DOUBLE):
assert(0);
continue;
#endif /* __STDIO_PRINTF_FLOAT */
default:
/* TODO -- really need to ensure this can't happen */
assert(ppfs->argtype[i-1] & PA_FLAG_PTR);
case PA_POINTER:
case PA_STRING:
case PA_WSTRING:
GET_VA_ARG(p,p,void *,ppfs->arg);
break;
case __PA_NOARG:
continue;
}
++p;
}
#ifdef NL_ARGMAX
} else {
if (ppfs->info.width == INT_MIN) {
ppfs->info.width
= (int) GET_ARG_VALUE(p + ppfs->argnumber[0] - 1,u,unsigned int);
}
if (ppfs->info.prec == INT_MIN) {
ppfs->info.prec
= (int) GET_ARG_VALUE(p + ppfs->argnumber[1] - 1,u,unsigned int);
}
}
#endif /* NL_ARGMAX */
 
/* Now we know the width and precision. */
if (ppfs->info.width < 0) {
ppfs->info.width = -ppfs->info.width;
PRINT_INFO_SET_FLAG(&(ppfs->info),left);
PRINT_INFO_CLR_FLAG(&(ppfs->info),space);
ppfs->info.pad = ' ';
}
#if 0
/* NOTE -- keep neg for now so float knows! */
if (ppfs->info.prec < 0) { /* spec says treat as omitted. */
/* so use default prec... 1 for everything but floats and strings. */
ppfs->info.prec = 1;
}
#endif
}
#endif
/**********************************************************************/
#ifdef L__ppfs_parsespec
 
/* Notes: argtype differs from glibc for the following:
* mine glibc
* lc PA_WCHAR PA_CHAR the standard says %lc means %C
* ls PA_WSTRING PA_STRING the standard says %ls means %S
* {*}n {*}|PA_FLAG_PTR PA_FLAG_PTR size of n can be qualified
*/
 
/* TODO: store positions of positional args */
 
/* TODO -- WARNING -- assumes aligned on integer boundaries!!! */
 
/* TODO -- disable if not using positional args!!! */
#define _OVERLAPPING_DIFFERENT_ARGS
 
/* TODO -- rethink this -- perhaps we should set to largest type??? */
 
#ifdef _OVERLAPPING_DIFFERENT_ARGS
 
#define PROMOTED_SIZE_OF(X) ((sizeof(X) + sizeof(int) - 1) / sizeof(X))
 
static const short int type_codes[] = {
__PA_NOARG, /* must be first entry */
PA_POINTER,
PA_STRING,
PA_WSTRING,
PA_CHAR,
PA_INT|PA_FLAG_SHORT,
PA_INT,
PA_INT|PA_FLAG_LONG,
PA_INT|PA_FLAG_LONG_LONG,
PA_WCHAR,
#ifdef __STDIO_PRINTF_FLOAT
/* PA_FLOAT, */
PA_DOUBLE,
PA_DOUBLE|PA_FLAG_LONG_DOUBLE,
#endif /* __STDIO_PRINTF_FLOAT */
};
 
static const unsigned char type_sizes[] = {
/* unknown type consumes no arg */
0, /* must be first entry */
PROMOTED_SIZE_OF(void *),
PROMOTED_SIZE_OF(char *),
PROMOTED_SIZE_OF(wchar_t *),
PROMOTED_SIZE_OF(char),
PROMOTED_SIZE_OF(short),
PROMOTED_SIZE_OF(int),
PROMOTED_SIZE_OF(long),
#ifdef ULLONG_MAX
PROMOTED_SIZE_OF(long long),
#else
PROMOTED_SIZE_OF(long), /* TODO -- is this correct? (above too) */
#endif
PROMOTED_SIZE_OF(wchar_t),
#ifdef __STDIO_PRINTF_FLOAT
/* PROMOTED_SIZE_OF(float), */
PROMOTED_SIZE_OF(double),
PROMOTED_SIZE_OF(long double),
#endif /* __STDIO_PRINTF_FLOAT */
};
 
static int _promoted_size(int argtype)
{
register const short int *p;
 
/* note -- since any unrecognized type is treated as a pointer */
p = type_codes + sizeof(type_codes)/sizeof(type_codes[0]);
do {
if (*--p == argtype) {
break;
}
} while (p > type_codes);
 
return type_sizes[(int)(p - type_codes)];
}
 
static int _is_equal_or_bigger_arg(int curtype, int newtype)
{
/* Quick test */
if (newtype == __PA_NOARG) {
return 0;
}
if ((curtype == __PA_NOARG) || (curtype == newtype)) {
return 1;
}
/* Ok... slot is already filled and types are different in name. */
/* So, compare promoted sizes of curtype and newtype args. */
return _promoted_size(curtype) <= _promoted_size(newtype);
}
 
#else
 
#define _is_equal_or_bigger_arg(C,N) (((C) == __PA_NOARG) || ((C) == (N)))
 
#endif
 
#ifdef __STDIO_GLIBC_CUSTOM_PRINTF
/* TODO - do this differently? */
static char _bss_custom_printf_spec[MAX_USER_SPEC]; /* 0-init'd for us. */
 
char *_custom_printf_spec = _bss_custom_printf_spec;
printf_arginfo_function *_custom_printf_arginfo[MAX_USER_SPEC];
printf_function _custom_printf_handler[MAX_USER_SPEC];
#endif /* __STDIO_GLIBC_CUSTOM_PRINTF */
 
extern int _ppfs_parsespec(ppfs_t *ppfs)
{
register const char *fmt;
register const char *p;
int preci;
int width;
int flags;
int dataargtype;
int i;
int dpoint;
#ifdef NL_ARGMAX
int maxposarg;
#endif /* NL_ARGMAX */
int p_m_spec_chars;
int n;
int argtype[MAX_ARGS_PER_SPEC+2];
int argnumber[3]; /* width, precision, 1st data arg */
static const char spec_flags[] = SPEC_FLAGS;
static const char spec_chars[] = SPEC_CHARS;/* TODO: b? */
static const char spec_ranges[] = SPEC_RANGES;
static const short spec_or_mask[] = SPEC_OR_MASK;
static const short spec_and_mask[] = SPEC_AND_MASK;
static const char qual_chars[] = QUAL_CHARS;
#ifdef __UCLIBC_HAS_WCHAR__
char buf[32];
#endif /* __UCLIBC_HAS_WCHAR__ */
 
/* WIDE note: we can test against '%' here since we don't allow */
/* WIDE note: other mappings of '%' in the wide char set. */
preci = -1;
argnumber[0] = 0;
argnumber[1] = 0;
argtype[0] = __PA_NOARG;
argtype[1] = __PA_NOARG;
#ifdef NL_ARGMAX
maxposarg = ppfs->maxposarg;
#endif /* NL_ARGMAX */
 
#ifdef __UCLIBC_HAS_WCHAR__
/* This is somewhat lame, but saves a lot of code. If we're dealing with
* a wide stream, that means the format is a wchar string. So, copy it
* char-by-char into a normal char buffer for processing. Make the buffer
* (buf) big enough so that any reasonable format specifier will fit.
* While there a legal specifiers that won't, the all involve duplicate
* flags or outrageous field widths/precisions. */
width = dpoint = 0;
if ((flags = ppfs->info._flags & FLAG_WIDESTREAM) == 0) {
fmt = ppfs->fmtpos;
} else {
fmt = buf + 1;
i = 0;
do {
if ((buf[i] = (char) (((wchar_t *) ppfs->fmtpos)[i-1]))
!= (((wchar_t *) ppfs->fmtpos)[i-1])
) {
return -1;
}
} while (buf[i++]);
buf[sizeof(buf)-1] = 0;
}
#else /* __UCLIBC_HAS_WCHAR__ */
width = flags = dpoint = 0;
fmt = ppfs->fmtpos;
#endif /* __UCLIBC_HAS_WCHAR__ */
 
assert(fmt[-1] == '%');
assert(fmt[0] != '%');
 
/* Process arg pos and/or flags and/or width and/or precision. */
width_precision:
p = fmt;
if (*fmt == '*') {
argtype[-dpoint] = PA_INT;
++fmt;
}
i = 0;
while (isdigit(*fmt)) {
if (i < MAX_FIELD_WIDTH) { /* Avoid overflow. */
i = (i * 10) + (*fmt - '0');
}
++fmt;
}
if (p[-1] == '%') { /* Check for a position. */
 
/* TODO: if val not in range, then error */
 
#ifdef NL_ARGMAX
if ((*fmt == '$') && (i > 0)) {/* Positional spec. */
++fmt;
if (maxposarg == 0) {
return -1;
}
if ((argnumber[2] = i) > maxposarg) {
maxposarg = i;
}
/* Now fall through to check flags. */
} else {
if (maxposarg > 0) {
#ifdef __STDIO_PRINTF_M_SUPPORT
#ifdef __UCLIBC_MJN3_ONLY__
#warning TODO: Support prec and width for %m when positional args used
/* Actually, positional arg processing will fail in general
* for specifiers that don't require an arg. */
#endif /* __UCLIBC_MJN3_ONLY__ */
if (*fmt == 'm') {
goto PREC_WIDTH;
}
#endif /* __STDIO_PRINTF_M_SUPPORT */
return -1;
}
maxposarg = 0; /* Possible redundant store, but cuts size. */
 
if ((fmt > p) && (*p != '0')) {
goto PREC_WIDTH;
}
 
fmt = p; /* Back up for possible '0's flag. */
/* Now fall through to check flags. */
}
#else /* NL_ARGMAX */
if (*fmt == '$') { /* Positional spec. */
return -1;
}
 
if ((fmt > p) && (*p != '0')) {
goto PREC_WIDTH;
}
 
fmt = p; /* Back up for possible '0's flag. */
/* Now fall through to check flags. */
#endif /* NL_ARGMAX */
 
restart_flags: /* Process flags. */
i = 1;
p = spec_flags;
do {
if (*fmt == *p++) {
++fmt;
flags |= i;
goto restart_flags;
}
i += i; /* Better than i <<= 1 for bcc */
} while (*p);
i = 0;
 
/* If '+' then ignore ' ', and if '-' then ignore '0'. */
/* Note: Need to ignore '0' when prec is an arg with val < 0, */
/* but that test needs to wait until the arg is retrieved. */
flags &= ~((flags & (FLAG_PLUS|FLAG_MINUS)) >> 1);
/* Note: Ignore '0' when prec is specified < 0 too (in printf). */
 
if (fmt[-1] != '%') { /* If we've done anything, loop for width. */
goto width_precision;
}
}
PREC_WIDTH:
if (*p == '*') { /* Prec or width takes an arg. */
#ifdef NL_ARGMAX
if (maxposarg) {
if ((*fmt++ != '$') || (i <= 0)) {
/* Using pos args and no $ or invalid arg number. */
return -1;
}
argnumber[-dpoint] = i;
} else
#endif /* NL_ARGMAX */
if (++p != fmt) {
/* Not using pos args but digits followed *. */
return -1;
}
i = INT_MIN;
}
 
if (!dpoint) {
width = i;
if (*fmt == '.') {
++fmt;
dpoint = -1; /* To use as default precison. */
goto width_precision;
}
} else {
preci = i;
}
 
/* Process qualifier. */
p = qual_chars;
do {
if (*fmt == *p) {
++fmt;
break;
}
} while (*++p);
if ((p - qual_chars < 2) && (*fmt == *p)) {
p += ((sizeof(qual_chars)-2) / 2);
++fmt;
}
dataargtype = ((int)(p[(sizeof(qual_chars)-2) / 2])) << 8;
 
/* Process conversion specifier. */
if (!*fmt) {
return -1;
}
 
p = spec_chars;
 
do {
if (*fmt == *p) {
p_m_spec_chars = p - spec_chars;
 
if ((p_m_spec_chars >= CONV_c)
&& (dataargtype & PA_FLAG_LONG)) {
p_m_spec_chars -= 2; /* lc -> C and ls -> S */
}
 
ppfs->conv_num = p_m_spec_chars;
p = spec_ranges-1;
while (p_m_spec_chars > *++p) {}
 
i = p - spec_ranges;
argtype[2] = (dataargtype | spec_or_mask[i]) & spec_and_mask[i];
p = spec_chars;
break;
}
} while(*++p);
 
ppfs->info.spec = *fmt;
ppfs->info.prec = preci;
ppfs->info.width = width;
ppfs->info.pad = ((flags & FLAG_ZERO) ? '0' : ' ');
ppfs->info._flags = (flags & ~FLAG_ZERO) | (dataargtype & __PA_INTMASK);
ppfs->num_data_args = 1;
 
if (!*p) {
#ifdef __STDIO_PRINTF_M_SUPPORT
if (*fmt == 'm') {
ppfs->conv_num = CONV_m;
ppfs->num_data_args = 0;
goto DONE;
}
#endif
#ifdef __STDIO_GLIBC_CUSTOM_PRINTF
 
/* Handle custom arg -- WARNING -- overwrites p!!! */
ppfs->conv_num = CONV_custom0;
p = _custom_printf_spec;
do {
if (*p == *fmt) {
if ((ppfs->num_data_args
= ((*_custom_printf_arginfo[(int)(p-_custom_printf_spec)])
(&(ppfs->info), MAX_ARGS_PER_SPEC, argtype+2)))
> MAX_ARGS_PER_SPEC) {
break; /* Error -- too many args! */
}
goto DONE;
}
} while (++p < (_custom_printf_spec + MAX_USER_SPEC));
#endif /* __STDIO_GLIBC_CUSTOM_PRINTF */
/* Otherwise error. */
return -1;
}
#if defined(__STDIO_GLIBC_CUSTOM_PRINTF) || defined(__STDIO_PRINTF_M_SUPPORT)
DONE:
#endif
 
#ifdef NL_ARGMAX
if (maxposarg > 0) {
i = 0;
do {
/* Update maxposarg and check that NL_ARGMAX is not exceeded. */
n = ((i <= 2)
? (ppfs->argnumber[i] = argnumber[i])
: argnumber[2] + (i-2));
if (n > maxposarg) {
if ((maxposarg = n) > NL_ARGMAX) {
return -1;
}
}
--n;
/* Record argtype with largest size (current, new). */
if (_is_equal_or_bigger_arg(ppfs->argtype[n], argtype[i])) {
ppfs->argtype[n] = argtype[i];
}
} while (++i < ppfs->num_data_args + 2);
} else {
#endif /* NL_ARGMAX */
ppfs->argnumber[2] = 1;
memcpy(ppfs->argtype, argtype + 2, ppfs->num_data_args * sizeof(int));
#ifdef NL_ARGMAX
}
 
ppfs->maxposarg = maxposarg;
#endif /* NL_ARGMAX */
 
#ifdef __UCLIBC_HAS_WCHAR__
if ((flags = ppfs->info._flags & FLAG_WIDESTREAM) == 0) {
ppfs->fmtpos = ++fmt;
} else {
ppfs->fmtpos = (const char *) (((const wchar_t *)(ppfs->fmtpos))
+ (fmt - buf) );
}
#else /* __UCLIBC_HAS_WCHAR__ */
ppfs->fmtpos = ++fmt;
#endif /* __UCLIBC_HAS_WCHAR__ */
 
return ppfs->num_data_args + 2;
}
 
#endif
/**********************************************************************/
#ifdef L_register_printf_function
 
#ifdef __STDIO_GLIBC_CUSTOM_PRINTF
 
int register_printf_function(int spec, printf_function handler,
printf_arginfo_function arginfo)
{
register char *r;
register char *p;
 
if (spec && (arginfo != NULL)) { /* TODO -- check if spec is valid char */
r = NULL;
p = _custom_printf_spec + MAX_USER_SPEC;
do {
--p;
if (!*p) {
r = p;
}
#ifdef __BCC__
else /* bcc generates less code with fall-through */
#endif
if (*p == spec) {
r = p;
p = _custom_printf_spec;
}
} while (p > _custom_printf_spec);
 
if (r) {
if (handler) {
*r = spec;
_custom_printf_handler[(int)(r - p)] = handler;
_custom_printf_arginfo[(int)(r - p)] = arginfo;
} else {
*r = 0;
}
return 0;
}
/* TODO -- if asked to unregister a non-existent spec, return what? */
}
return -1;
}
 
#endif
 
#endif
/**********************************************************************/
#ifdef L_vsnprintf
 
#ifdef __UCLIBC_MJN3_ONLY__
#warning WISHLIST: Implement vsnprintf for non-buffered and no custom stream case.
#endif /* __UCLIBC_MJN3_ONLY__ */
 
#ifdef __STDIO_BUFFERS
 
int vsnprintf(char *__restrict buf, size_t size,
const char * __restrict format, va_list arg)
{
FILE f;
int rv;
 
#ifdef __STDIO_GETC_MACRO
f.bufgetc =
#endif
f.bufpos = f.bufread = f.bufstart = buf;
 
if (size > SIZE_MAX - (size_t) buf) {
size = SIZE_MAX - (size_t) buf;
}
#ifdef __STDIO_PUTC_MACRO
f.bufputc =
#endif
f.bufend = buf + size;
 
#if 0 /* shouldn't be necessary */
/* #ifdef __STDIO_GLIBC_CUSTOM_STREAMS */
f.cookie = &(f.filedes);
f.gcs.read = 0;
f.gcs.write = 0;
f.gcs.seek = 0;
f.gcs.close = 0;
#endif
f.filedes = -2; /* for debugging */
f.modeflags = (__FLAG_NARROW|__FLAG_WRITEONLY|__FLAG_WRITING);
 
#ifdef __STDIO_MBSTATE
__INIT_MBSTATE(&(f.state));
#endif /* __STDIO_MBSTATE */
 
#ifdef __STDIO_THREADSAFE
f.user_locking = 0;
__stdio_init_mutex(&f.lock);
#endif
 
rv = vfprintf(&f, format, arg);
if (size) {
if (f.bufpos == f.bufend) {
--f.bufpos;
}
*f.bufpos = 0;
}
return rv;
}
 
#elif defined(__USE_OLD_VFPRINTF__)
 
typedef struct {
FILE f;
unsigned char *bufend; /* pointer to 1 past end of buffer */
unsigned char *bufpos;
} __FILE_vsnprintf;
 
int vsnprintf(char *__restrict buf, size_t size,
const char * __restrict format, va_list arg)
{
__FILE_vsnprintf f;
int rv;
 
f.bufpos = buf;
 
if (size > SIZE_MAX - (size_t) buf) {
size = SIZE_MAX - (size_t) buf;
}
f.bufend = buf + size;
 
#if 0 /* shouldn't be necessary */
/* #ifdef __STDIO_GLIBC_CUSTOM_STREAMS */
f.f.cookie = &(f.f.filedes);
f.f.gcs.read = 0;
f.f.gcs.write = 0;
f.f.gcs.seek = 0;
f.f.gcs.close = 0;
#endif
f.f.filedes = -2; /* for debugging */
f.f.modeflags = (__FLAG_NARROW|__FLAG_WRITEONLY|__FLAG_WRITING);
 
#ifdef __STDIO_MBSTATE
__INIT_MBSTATE(&(f.f.state));
#endif /* __STDIO_MBSTATE */
 
#ifdef __STDIO_THREADSAFE
f.f.user_locking = 0;
__stdio_init_mutex(&f.f.lock);
#endif
 
rv = vfprintf((FILE *) &f, format, arg);
if (size) {
if (f.bufpos == f.bufend) {
--f.bufpos;
}
*f.bufpos = 0;
}
return rv;
}
 
#elif defined(__STDIO_GLIBC_CUSTOM_STREAMS)
 
typedef struct {
size_t pos;
size_t len;
unsigned char *buf;
FILE *fp;
} __snpf_cookie;
 
#define COOKIE ((__snpf_cookie *) cookie)
 
static ssize_t snpf_write(register void *cookie, const char *buf,
size_t bufsize)
{
size_t count;
register char *p;
 
/* Note: bufsize < SSIZE_MAX because of _stdio_WRITE. */
 
if (COOKIE->len > COOKIE->pos) {
count = COOKIE->len - COOKIE->pos - 1; /* Leave space for nul. */
if (count > bufsize) {
count = bufsize;
}
 
p = COOKIE->buf + COOKIE->pos;
while (count) {
*p++ = *buf++;
--count;
}
*p = 0;
}
 
COOKIE->pos += bufsize;
 
return bufsize;
}
 
#undef COOKIE
 
int vsnprintf(char *__restrict buf, size_t size,
const char * __restrict format, va_list arg)
{
FILE f;
__snpf_cookie cookie;
int rv;
 
cookie.buf = buf;
cookie.len = size;
cookie.pos = 0;
cookie.fp = &f;
 
f.cookie = &cookie;
f.gcs.write = snpf_write;
f.gcs.read = NULL;
f.gcs.seek = NULL;
f.gcs.close = NULL;
 
f.filedes = -1; /* For debugging. */
f.modeflags = (__FLAG_NARROW|__FLAG_WRITEONLY|__FLAG_WRITING);
 
#ifdef __STDIO_MBSTATE
__INIT_MBSTATE(&(f.state));
#endif /* __STDIO_MBSTATE */
 
#ifdef __STDIO_THREADSAFE
f.user_locking = 0;
__stdio_init_mutex(&f.lock);
#endif
 
rv = vfprintf(&f, format, arg);
 
return rv;
}
 
#else
#warning Skipping vsnprintf since no buffering, no custom streams, and not old vfprintf!
#ifdef __STDIO_HAS_VSNPRINTF
#error WHOA! __STDIO_HAS_VSNPRINTF is defined!
#endif
#endif
 
#endif
/**********************************************************************/
#ifdef L_vdprintf
 
int vdprintf(int filedes, const char * __restrict format, va_list arg)
{
FILE f;
int rv;
#ifdef __STDIO_BUFFERS
char buf[64]; /* TODO: provide _optional_ buffering? */
 
#ifdef __STDIO_GETC_MACRO
f.bufgetc =
#endif
f.bufpos = f.bufread = f.bufstart = buf;
#ifdef __STDIO_PUTC_MACRO
f.bufputc =
#endif
f.bufend = buf + sizeof(buf);
#endif /* __STDIO_BUFFERS */
#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
f.cookie = &(f.filedes);
f.gcs.read = _cs_read;
f.gcs.write = _cs_write;
f.gcs.seek = 0; /* The internal seek func handles normals. */
f.gcs.close = _cs_close;
#endif
f.filedes = filedes;
f.modeflags = (__FLAG_NARROW|__FLAG_WRITEONLY|__FLAG_WRITING);
 
#ifdef __STDIO_MBSTATE
__INIT_MBSTATE(&(f.state));
#endif /* __STDIO_MBSTATE */
 
#ifdef __STDIO_THREADSAFE
f.user_locking = 0;
__stdio_init_mutex(&f.lock);
#endif
 
rv = vfprintf(&f, format, arg);
 
return fflush(&f) ? -1 : rv;
}
 
#endif
/**********************************************************************/
#ifdef L_vasprintf
 
#ifndef __STDIO_HAS_VSNPRINTF
#warning Skipping vasprintf since no vsnprintf!
#else
 
int vasprintf(char **__restrict buf, const char * __restrict format,
va_list arg)
{
/* TODO -- change the default implementation? */
#ifndef __STDIO_GLIBC_CUSTOM_STREAMS
/* This implementation actually calls the printf machinery twice, but only
* only does one malloc. This can be a problem though when custom printf
* specs or the %m specifier are involved because the results of the
* second call might be different from the first. */
va_list arg2;
int rv;
 
va_copy(arg2, arg);
rv = vsnprintf(NULL, 0, format, arg2);
va_end(arg2);
 
return (((rv >= 0) && ((*buf = malloc(++rv)) != NULL))
? vsnprintf(*buf, rv, format, arg)
: -1);
#else
FILE *f;
size_t size;
int rv;
 
/* TODO - do the memstream stuff inline here to avoid fclose and the openlist? */
if ((f = open_memstream(buf, &size)) == NULL) {
return -1;
}
rv = vfprintf(f, format, arg);
fclose(f);
if (rv < 0) {
free(*buf);
*buf = NULL;
return -1;
}
return rv;
#endif
}
#endif
#endif
/**********************************************************************/
#ifdef L_vprintf
int vprintf(const char * __restrict format, va_list arg)
{
return vfprintf(stdout, format, arg);
}
#endif
/**********************************************************************/
#ifdef L_vsprintf
 
#ifndef __STDIO_HAS_VSNPRINTF
#warning Skipping vsprintf since no vsnprintf!
#else
 
int vsprintf(char *__restrict buf, const char * __restrict format,
va_list arg)
{
return vsnprintf(buf, SIZE_MAX, format, arg);
}
 
#endif
#endif
/**********************************************************************/
#ifdef L_fprintf
 
int fprintf(FILE * __restrict stream, const char * __restrict format, ...)
{
va_list arg;
int rv;
 
va_start(arg, format);
rv = vfprintf(stream, format, arg);
va_end(arg);
 
return rv;
}
 
#endif
/**********************************************************************/
#ifdef L_snprintf
 
#ifndef __STDIO_HAS_VSNPRINTF
#warning Skipping snprintf since no vsnprintf!
#else
 
int snprintf(char *__restrict buf, size_t size,
const char * __restrict format, ...)
{
va_list arg;
int rv;
 
va_start(arg, format);
rv = vsnprintf(buf, size, format, arg);
va_end(arg);
return rv;
}
 
#endif
#endif
/**********************************************************************/
#ifdef L_dprintf
 
int dprintf(int filedes, const char * __restrict format, ...)
{
va_list arg;
int rv;
 
va_start(arg, format);
rv = vdprintf(filedes, format, arg);
va_end(arg);
 
return rv;
}
 
#endif
/**********************************************************************/
#ifdef L_asprintf
 
#ifndef __STDIO_HAS_VSNPRINTF
#warning Skipping asprintf and __asprintf since no vsnprintf!
#else
 
weak_alias(__asprintf,asprintf)
 
int __asprintf(char **__restrict buf, const char * __restrict format, ...)
{
va_list arg;
int rv;
 
va_start(arg, format);
rv = vasprintf(buf, format, arg);
va_end(arg);
 
return rv;
}
 
#endif
#endif
/**********************************************************************/
#ifdef L_printf
int printf(const char * __restrict format, ...)
{
va_list arg;
int rv;
 
va_start(arg, format);
rv = vfprintf(stdout, format, arg);
va_end(arg);
 
return rv;
}
#endif
/**********************************************************************/
#ifdef L_sprintf
 
#ifndef __STDIO_HAS_VSNPRINTF
#warning Skipping sprintf since no vsnprintf!
#else
 
int sprintf(char *__restrict buf, const char * __restrict format, ...)
{
va_list arg;
int rv;
 
va_start(arg, format);
rv = vsnprintf(buf, SIZE_MAX, format, arg);
va_end(arg);
 
return rv;
}
 
#endif
#endif
/**********************************************************************/
#ifdef L_vswprintf
 
#ifdef __STDIO_BUFFERS
int vswprintf(wchar_t *__restrict buf, size_t size,
const wchar_t * __restrict format, va_list arg)
{
FILE f;
int rv;
 
#ifdef __STDIO_GETC_MACRO
f.bufgetc =
#endif
f.bufpos = f.bufread = f.bufstart = (char *) buf;
 
/* if (size > SIZE_MAX - (size_t) buf) { */
/* size = SIZE_MAX - (size_t) buf; */
/* } */
#ifdef __STDIO_PUTC_MACRO
f.bufputc =
#endif
f.bufend = (char *)(buf + size);
 
#if 0 /* shouldn't be necessary */
/* #ifdef __STDIO_GLIBC_CUSTOM_STREAMS */
f.cookie = &(f.filedes);
f.gcs.read = 0;
f.gcs.write = 0;
f.gcs.seek = 0;
f.gcs.close = 0;
#endif
f.filedes = -3; /* for debugging */
f.modeflags = (__FLAG_WIDE|__FLAG_WRITEONLY|__FLAG_WRITING);
 
#ifdef __STDIO_MBSTATE
__INIT_MBSTATE(&(f.state));
#endif /* __STDIO_MBSTATE */
 
#ifdef __STDIO_THREADSAFE
f.user_locking = 0;
__stdio_init_mutex(&f.lock);
#endif
 
rv = vfwprintf(&f, format, arg);
 
/* NOTE: Return behaviour differs from snprintf... */
if (f.bufpos == f.bufend) {
rv = -1;
if (size) {
f.bufpos = (char *)(((wchar_t *) f.bufpos) - 1);
}
}
if (size) {
*((wchar_t *) f.bufpos) = 0;
}
return rv;
}
#else /* __STDIO_BUFFERS */
#warning Skipping vswprintf since no buffering!
#endif /* __STDIO_BUFFERS */
#endif
/**********************************************************************/
#ifdef L_swprintf
#ifdef __STDIO_BUFFERS
 
int swprintf(wchar_t *__restrict buf, size_t size,
const wchar_t * __restrict format, ...)
{
va_list arg;
int rv;
 
va_start(arg, format);
rv = vswprintf(buf, size, format, arg);
va_end(arg);
return rv;
}
 
#else /* __STDIO_BUFFERS */
#warning Skipping vsWprintf since no buffering!
#endif /* __STDIO_BUFFERS */
#endif
/**********************************************************************/
#ifdef L_fwprintf
 
int fwprintf(FILE * __restrict stream, const wchar_t * __restrict format, ...)
{
va_list arg;
int rv;
 
va_start(arg, format);
rv = vfwprintf(stream, format, arg);
va_end(arg);
 
return rv;
}
 
#endif
/**********************************************************************/
#ifdef L_vwprintf
int vwprintf(const wchar_t * __restrict format, va_list arg)
{
return vfwprintf(stdout, format, arg);
}
#endif
/**********************************************************************/
#ifdef L_wprintf
int wprintf(const wchar_t * __restrict format, ...)
{
va_list arg;
int rv;
 
va_start(arg, format);
rv = vfwprintf(stdout, format, arg);
va_end(arg);
 
return rv;
}
#endif
/**********************************************************************/
#ifdef L__fpmaxtostr
 
/* Copyright (C) 2000, 2001, 2003 Manuel Novoa III
*
* Function:
*
* size_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info,
* __fp_outfunc_t fp_outfunc);
*
* This is derived from the old _dtostr, whic I wrote for uClibc to provide
* floating point support for the printf functions. It handles +/- infinity,
* nan, and signed 0 assuming you have ieee arithmetic. It also now handles
* digit grouping (for the uClibc supported locales) and hexadecimal float
* notation. Finally, via the fp_outfunc parameter, it now supports wide
* output.
*
* Notes:
*
* At most DECIMAL_DIG significant digits are kept. Any trailing digits
* are treated as 0 as they are really just the results of rounding noise
* anyway. If you want to do better, use an arbitary precision arithmetic
* package. ;-)
*
* It should also be fairly portable, as no assumptions are made about the
* bit-layout of doubles. Of course, that does make it less efficient than
* it could be.
*
*/
 
/*****************************************************************************/
/* Don't change anything that follows unless you know what you're doing. */
/*****************************************************************************/
/* Fairly portable nan check. Bitwise for i386 generated larger code.
* If you have a better version, comment this out.
*/
#define isnan(x) ((x) != (x))
 
/* Without seminumerical functions to examine the sign bit, this is
* about the best we can do to test for '-0'.
*/
#define zeroisnegative(x) ((1./(x)) < 0)
 
/*****************************************************************************/
/* Don't change anything that follows peroid!!! ;-) */
/*****************************************************************************/
#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
#if FLT_RADIX != 2
#error FLT_RADIX != 2 is not currently supported
#endif
#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
 
#define NUM_HEX_DIGITS ((FPMAX_MANT_DIG + 3)/ 4)
 
/* WARNING: Adjust _fp_out_wide() below if this changes! */
/* With 32 bit ints, we can get 9 decimal digits per block. */
#define DIGITS_PER_BLOCK 9
#define HEX_DIGITS_PER_BLOCK 8
 
/* Maximum number of subcases to output double is...
* 0 - sign
* 1 - padding and initial digit
* 2 - digits left of the radix
* 3 - 0s left of the radix or radix
* 4 - radix or digits right of the radix
* 5 - 0s right of the radix
* 6 - exponent
* 7 - trailing space padding
* although not all cases may occur.
*/
#define MAX_CALLS 8
 
/*****************************************************************************/
 
#define NUM_DIGIT_BLOCKS ((DECIMAL_DIG+DIGITS_PER_BLOCK-1)/DIGITS_PER_BLOCK)
#define NUM_HEX_DIGIT_BLOCKS \
((NUM_HEX_DIGITS+HEX_DIGITS_PER_BLOCK-1)/HEX_DIGITS_PER_BLOCK)
 
/* WARNING: Adjust _fp_out_wide() below if this changes! */
 
/* extra space for '-', '.', 'e+###', and nul */
#define BUF_SIZE ( 3 + NUM_DIGIT_BLOCKS * DIGITS_PER_BLOCK )
 
/*****************************************************************************/
 
static const char fmt[] = "inf\0INF\0nan\0NAN\0.\0,";
 
#define INF_OFFSET 0 /* must be 1st */
#define NAN_OFFSET 8 /* must be 2nd.. see hex sign handling */
#define DECPT_OFFSET 16
#define THOUSEP_OFFSET 18
 
#define EMPTY_STRING_OFFSET 3
 
/*****************************************************************************/
#if FPMAX_MAX_10_EXP < -FPMAX_MIN_10_EXP
#error scaling code can not handle FPMAX_MAX_10_EXP < -FPMAX_MIN_10_EXP
#endif
 
static const __fpmax_t exp10_table[] =
{
1e1L, 1e2L, 1e4L, 1e8L, 1e16L, 1e32L, /* floats */
#if FPMAX_MAX_10_EXP < 32
#error unsupported FPMAX_MAX_10_EXP (< 32). ANSI/ISO C requires >= 37.
#endif
#if FPMAX_MAX_10_EXP >= 64
1e64L,
#endif
#if FPMAX_MAX_10_EXP >= 128
1e128L,
#endif
#if FPMAX_MAX_10_EXP >= 256
1e256L,
#endif
#if FPMAX_MAX_10_EXP >= 512
1e512L,
#endif
#if FPMAX_MAX_10_EXP >= 1024
1e1024L,
#endif
#if FPMAX_MAX_10_EXP >= 2048
1e2048L,
#endif
#if FPMAX_MAX_10_EXP >= 4096
1e4096L
#endif
#if FPMAX_MAX_10_EXP >= 8192
#error unsupported FPMAX_MAX_10_EXP. please increase table
#endif
};
 
#define EXP10_TABLE_SIZE (sizeof(exp10_table)/sizeof(exp10_table[0]))
#define EXP10_TABLE_MAX (1U<<(EXP10_TABLE_SIZE-1))
 
/*****************************************************************************/
#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
 
#if FLT_RADIX != 2
#error FLT_RADIX != 2 is not currently supported
#endif
 
#if FPMAX_MAX_EXP < -FPMAX_MIN_EXP
#error scaling code can not handle FPMAX_MAX_EXP < -FPMAX_MIN_EXP
#endif
 
static const __fpmax_t exp16_table[] = {
0x1.0p4L, 0x1.0p8L, 0x1.0p16L, 0x1.0p32L, 0x1.0p64L,
#if FPMAX_MAX_EXP >= 128
0x1.0p128L,
#endif
#if FPMAX_MAX_EXP >= 256
0x1.0p256L,
#endif
#if FPMAX_MAX_EXP >= 512
0x1.0p512L,
#endif
#if FPMAX_MAX_EXP >= 1024
0x1.0p1024L,
#endif
#if FPMAX_MAX_EXP >= 2048
0x1.0p2048L,
#endif
#if FPMAX_MAX_EXP >= 4096
0x1.0p4096L,
#endif
#if FPMAX_MAX_EXP >= 8192
0x1.0p8192L,
#endif
#if FPMAX_MAX_EXP >= 16384
0x1.0p16384L
#endif
#if FPMAX_MAX_EXP >= 32768
#error unsupported FPMAX_MAX_EXP. please increase table
#endif
};
 
#define EXP16_TABLE_SIZE (sizeof(exp16_table)/sizeof(exp16_table[0]))
#define EXP16_TABLE_MAX (1U<<(EXP16_TABLE_SIZE-1))
 
#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
/*****************************************************************************/
 
#define FPO_ZERO_PAD (0x80 | '0')
#define FPO_STR_WIDTH (0x80 | ' ');
#define FPO_STR_PREC 'p'
 
size_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info,
__fp_outfunc_t fp_outfunc)
{
#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
__fpmax_t lower_bnd;
__fpmax_t upper_bnd = 1e9;
#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
uint_fast32_t digit_block;
#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
uint_fast32_t base = 10;
const __fpmax_t *power_table;
int dpb = DIGITS_PER_BLOCK;
int ndb = NUM_DIGIT_BLOCKS;
int nd = DECIMAL_DIG;
int sufficient_precision = 0;
#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
int num_groups = 0;
int initial_group; /* This does not need to be initialized. */
int tslen; /* This does not need to be initialized. */
int nblk2; /* This does not need to be initialized. */
const char *ts; /* This does not need to be initialized. */
#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
int i, j;
int round, o_exp;
int exp, exp_neg;
int width, preci;
int cnt;
char *s;
char *e;
intptr_t pc_fwi[3*MAX_CALLS];
intptr_t *ppc;
intptr_t *ppc_last;
#ifdef __UCLIBC_MJN3_ONLY__
#warning TODO: The size of exp_buf[] should really be determined by the float constants.
#endif /* __UCLIBC_MJN3_ONLY__ */
char exp_buf[16];
char buf[BUF_SIZE];
char sign_str[6]; /* Last 2 are for 1st digit + nul. */
char o_mode;
char mode;
 
 
width = info->width;
preci = info->prec;
mode = info->spec;
 
*exp_buf = 'e';
if ((mode|0x20) == 'a') {
#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
*exp_buf = 'p';
if (preci < 0) {
preci = NUM_HEX_DIGITS;
sufficient_precision = 1;
}
#else
mode += ('g' - 'a');
#endif
}
 
if (preci < 0) {
preci = 6;
}
 
*sign_str = '\0';
if (PRINT_INFO_FLAG_VAL(info,showsign)) {
*sign_str = '+';
} else if (PRINT_INFO_FLAG_VAL(info,space)) {
*sign_str = ' ';
}
 
*(sign_str+1) = 0;
pc_fwi[5] = INF_OFFSET;
if (isnan(x)) { /* First, check for nan. */
pc_fwi[5] = NAN_OFFSET;
goto INF_NAN;
}
 
if (x == 0) { /* Handle 0 now to avoid false positive. */
#if 1
if (zeroisnegative(x)) { /* Handle 'signed' zero. */
*sign_str = '-';
}
#endif
exp = -1;
goto GENERATE_DIGITS;
}
 
if (x < 0) { /* Convert negatives to positives. */
*sign_str = '-';
x = -x;
}
 
if (__FPMAX_ZERO_OR_INF_CHECK(x)) { /* Inf since zero handled above. */
INF_NAN:
info->pad = ' ';
ppc = pc_fwi + 6;
pc_fwi[3] = FPO_STR_PREC;
pc_fwi[4] = 3;
if (mode < 'a') {
pc_fwi[5] += 4;
}
pc_fwi[5] = (intptr_t)(fmt + pc_fwi[5]);
goto EXIT_SPECIAL;
}
 
#ifdef __UCLIBC_MJN3_ONLY__
#warning TODO: Clean up defines when hexadecimal float notation is unsupported.
#endif /* __UCLIBC_MJN3_ONLY__ */
 
#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
 
if ((mode|0x20) == 'a') {
lower_bnd = 0x1.0p31L;
upper_bnd = 0x1.0p32L;
power_table = exp16_table;
exp = HEX_DIGITS_PER_BLOCK - 1;
i = EXP16_TABLE_SIZE;
j = EXP16_TABLE_MAX;
dpb = HEX_DIGITS_PER_BLOCK;
ndb = NUM_HEX_DIGIT_BLOCKS;
nd = NUM_HEX_DIGITS;
base = 16;
} else {
lower_bnd = 1e8;
/* upper_bnd = 1e9; */
power_table = exp10_table;
exp = DIGITS_PER_BLOCK - 1;
i = EXP10_TABLE_SIZE;
j = EXP10_TABLE_MAX;
/* dpb = DIGITS_PER_BLOCK; */
/* ndb = NUM_DIGIT_BLOCKS; */
/* base = 10; */
}
 
 
 
#else /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
 
#define lower_bnd 1e8
#define upper_bnd 1e9
#define power_table exp10_table
#define dpb DIGITS_PER_BLOCK
#define base 10
#define ndb NUM_DIGIT_BLOCKS
#define nd DECIMAL_DIG
 
exp = DIGITS_PER_BLOCK - 1;
i = EXP10_TABLE_SIZE;
j = EXP10_TABLE_MAX;
 
#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
 
exp_neg = 0;
if (x < lower_bnd) { /* Do we need to scale up or down? */
exp_neg = 1;
}
 
do {
--i;
if (exp_neg) {
if (x * power_table[i] < upper_bnd) {
x *= power_table[i];
exp -= j;
}
} else {
if (x / power_table[i] >= lower_bnd) {
x /= power_table[i];
exp += j;
}
}
j >>= 1;
} while (i);
if (x >= upper_bnd) { /* Handle bad rounding case. */
x /= power_table[0];
++exp;
}
assert(x < upper_bnd);
 
GENERATE_DIGITS:
s = buf + 2; /* Leave space for '\0' and '0'. */
i = 0;
do {
digit_block = (uint_fast32_t) x;
assert(digit_block < upper_bnd);
#ifdef __UCLIBC_MJN3_ONLY__
#warning CONSIDER: Can rounding be a problem?
#endif /* __UCLIBC_MJN3_ONLY__ */
x = (x - digit_block) * upper_bnd;
s += dpb;
j = 0;
do {
s[- ++j] = '0' + (digit_block % base);
digit_block /= base;
} while (j < dpb);
} while (++i < ndb);
 
/*************************************************************************/
 
if (mode < 'a') {
*exp_buf -= ('a' - 'A'); /* e->E and p->P */
mode += ('a' - 'A');
}
 
o_mode = mode;
if ((mode == 'g') && (preci > 0)){
--preci;
}
round = preci;
 
if (mode == 'f') {
round += exp;
if (round < -1) {
memset(buf, '0', DECIMAL_DIG); /* OK, since 'f' -> decimal case. */
exp = -1;
round = -1;
}
}
 
s = buf;
*s++ = 0; /* Terminator for rounding and 0-triming. */
*s = '0'; /* Space to round. */
 
i = 0;
e = s + nd + 1;
if (round < nd) {
e = s + round + 2;
if (*e >= '0' + (base/2)) { /* NOTE: We always round away from 0! */
i = 1;
}
}
 
do { /* Handle rounding and trim trailing 0s. */
*--e += i; /* Add the carry. */
} while ((*e == '0') || (*e > '0' - 1 + base));
 
#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
if ((mode|0x20) == 'a') {
char *q;
for (q = e ; *q ; --q) {
if (*q > '9') {
*q += (*exp_buf - ('p' - 'a') - '9' - 1);
}
}
 
if (e > s) {
exp *= 4; /* Change from base 16 to base 2. */
}
}
#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
 
o_exp = exp;
if (e <= s) { /* We carried into an extra digit. */
++o_exp;
e = s; /* Needed if all 0s. */
} else {
++s;
}
*++e = 0; /* Terminating nul char. */
 
if ((mode == 'g') && ((o_exp >= -4) && (o_exp <= round))) {
mode = 'f';
preci = round - o_exp;
}
 
exp = o_exp;
if (mode != 'f') {
o_exp = 0;
}
 
if (o_exp < 0) { /* Exponent is < 0, so */
*--s = '0'; /* fake the first 0 digit. */
}
 
pc_fwi[3] = FPO_ZERO_PAD;
pc_fwi[4] = 1;
pc_fwi[5] = (intptr_t)(sign_str + 4);
sign_str[4] = *s++;
sign_str[5] = 0;
ppc = pc_fwi + 6;
 
i = e - s; /* Total digits is 'i'. */
if (o_exp >= 0) {
#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
 
const char *p;
 
if (PRINT_INFO_FLAG_VAL(info,group)
&& *(p = __UCLIBC_CURLOCALE_DATA.grouping)
) {
int nblk1;
 
nblk2 = nblk1 = *p;
if (*++p) {
nblk2 = *p;
assert(!*++p);
}
 
if (o_exp >= nblk1) {
num_groups = (o_exp - nblk1) / nblk2 + 1;
initial_group = (o_exp - nblk1) % nblk2;
 
#ifdef __UCLIBC_HAS_WCHAR__
if (PRINT_INFO_FLAG_VAL(info,wide)) {
/* _fp_out_wide() will fix this up. */
ts = fmt + THOUSEP_OFFSET;
tslen = 1;
} else {
#endif /* __UCLIBC_HAS_WCHAR__ */
ts = __UCLIBC_CURLOCALE_DATA.thousands_sep;
tslen = __UCLIBC_CURLOCALE_DATA.thousands_sep_len;
#ifdef __UCLIBC_HAS_WCHAR__
}
#endif /* __UCLIBC_HAS_WCHAR__ */
 
width -= num_groups * tslen;
}
}
 
 
#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
ppc[0] = FPO_STR_PREC;
ppc[2] = (intptr_t)(s);
if (o_exp >= i) { /* all digit(s) left of decimal */
ppc[1] = i;
ppc += 3;
o_exp -= i;
i = 0;
if (o_exp>0) { /* have 0s left of decimal */
ppc[0] = FPO_ZERO_PAD;
ppc[1] = o_exp;
ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET);
ppc += 3;
}
} else if (o_exp > 0) { /* decimal between digits */
ppc[1] = o_exp;
ppc += 3;
s += o_exp;
i -= o_exp;
}
o_exp = -1;
}
 
if (PRINT_INFO_FLAG_VAL(info,alt)
|| (i)
|| ((o_mode != 'g')
#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
&& (o_mode != 'a')
#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
&& (preci > 0))
) {
ppc[0] = FPO_STR_PREC;
#ifdef __LOCALE_C_ONLY
ppc[1] = 1;
ppc[2] = (intptr_t)(fmt + DECPT_OFFSET);
#else /* __LOCALE_C_ONLY */
#ifdef __UCLIBC_HAS_WCHAR__
if (PRINT_INFO_FLAG_VAL(info,wide)) {
/* _fp_out_wide() will fix this up. */
ppc[1] = 1;
ppc[2] = (intptr_t)(fmt + DECPT_OFFSET);
} else {
#endif /* __UCLIBC_HAS_WCHAR__ */
ppc[1] = __UCLIBC_CURLOCALE_DATA.decimal_point_len;
ppc[2] = (intptr_t)(__UCLIBC_CURLOCALE_DATA.decimal_point);
#ifdef __UCLIBC_HAS_WCHAR__
}
#endif /* __UCLIBC_HAS_WCHAR__ */
#endif /* __LOCALE_C_ONLY */
ppc += 3;
}
 
if (++o_exp < 0) { /* Have 0s right of decimal. */
ppc[0] = FPO_ZERO_PAD;
ppc[1] = -o_exp;
ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET);
ppc += 3;
}
if (i) { /* Have digit(s) right of decimal. */
ppc[0] = FPO_STR_PREC;
ppc[1] = i;
ppc[2] = (intptr_t)(s);
ppc += 3;
}
 
if (((o_mode != 'g') || PRINT_INFO_FLAG_VAL(info,alt))
#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
&& !sufficient_precision
#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
) {
i -= o_exp;
if (i < preci) { /* Have 0s right of digits. */
i = preci - i;
ppc[0] = FPO_ZERO_PAD;
ppc[1] = i;
ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET);
ppc += 3;
}
}
 
/* Build exponent string. */
if (mode != 'f') {
char *p = exp_buf + sizeof(exp_buf);
char exp_char = *exp_buf;
char exp_sign = '+';
#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
int min_exp_dig_plus_2 = ((o_mode != 'a') ? (2+2) : (2+1));
#else /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
#define min_exp_dig_plus_2 (2+2)
#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
 
if (exp < 0) {
exp_sign = '-';
exp = -exp;
}
 
*--p = 0; /* nul-terminate */
j = 2; /* Count exp_char and exp_sign. */
do {
*--p = '0' + (exp % 10);
exp /= 10;
} while ((++j < min_exp_dig_plus_2) || exp); /* char+sign+mindigits */
*--p = exp_sign;
*--p = exp_char;
 
ppc[0] = FPO_STR_PREC;
ppc[1] = j;
ppc[2] = (intptr_t)(p);
ppc += 3;
}
 
EXIT_SPECIAL:
ppc_last = ppc;
ppc = pc_fwi + 4; /* Need width fields starting with second. */
do {
width -= *ppc;
ppc += 3;
} while (ppc < ppc_last);
 
ppc = pc_fwi;
ppc[0] = FPO_STR_WIDTH;
ppc[1] = i = ((*sign_str) != 0);
ppc[2] = (intptr_t) sign_str;
 
#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
if (((mode|0x20) == 'a') && (pc_fwi[3] >= 16)) { /* Hex sign handling. */
/* Hex and not inf or nan, so prefix with 0x. */
char *h = sign_str + i;
*h = '0';
*++h = 'x' - 'p' + *exp_buf;
*++h = 0;
ppc[1] = (i += 2);
}
#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
 
if ((width -= i) > 0) {
if (PRINT_INFO_FLAG_VAL(info,left)) { /* Left-justified. */
ppc_last[0] = FPO_STR_WIDTH;
ppc_last[1] = width;
ppc_last[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET);
ppc_last += 3;
} else if (info->pad == '0') { /* 0 padding */
ppc[4] += width; /* Pad second field. */
} else {
ppc[1] += width; /* Pad first (sign) field. */
}
}
 
cnt = 0;
 
do {
#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
 
if ((ppc == pc_fwi + 6) && num_groups) {
const char *gp = (const char *) ppc[2];
int len = ppc[1];
int blk = initial_group;
 
cnt += num_groups * tslen; /* Adjust count now for sep chars. */
 
/* printf("\n"); */
do {
if (!blk) { /* Initial group could be 0 digits long! */
blk = nblk2;
} else if (len >= blk) { /* Enough digits for a group. */
/* printf("norm: len=%d blk=%d \"%.*s\"\n", len, blk, blk, gp); */
fp_outfunc(fp, *ppc, blk, (intptr_t) gp);
assert(gp);
if (*gp) {
gp += blk;
}
len -= blk;
} else { /* Transition to 0s. */
/* printf("trans: len=%d blk=%d \"%.*s\"\n", len, blk, len, gp); */
if (len) {
/* printf("len\n"); */
fp_outfunc(fp, *ppc, len, (intptr_t) gp);
gp += len;
}
 
if (ppc[3] == FPO_ZERO_PAD) { /* Need to group 0s */
/* printf("zeropad\n"); */
cnt += ppc[1];
ppc += 3;
gp = (const char *) ppc[2];
blk -= len; /* blk > len, so blk still > 0. */
len = ppc[1];
continue; /* Don't decrement num_groups here. */
} else {
assert(num_groups == 0);
break;
}
}
 
if (num_groups <= 0) {
break;
}
--num_groups;
 
fp_outfunc(fp, FPO_STR_PREC, tslen, (intptr_t) ts);
blk = nblk2;
 
/* printf("num_groups=%d blk=%d\n", num_groups, blk); */
 
} while (1);
} else
 
#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
 
fp_outfunc(fp, *ppc, ppc[1], ppc[2]); /* NOTE: Remember 'else' above! */
 
cnt += ppc[1];
ppc += 3;
} while (ppc < ppc_last);
 
return cnt;
}
 
#endif
/**********************************************************************/
#ifdef L__store_inttype
 
/* Right now, we assume intmax_t is either long or long long */
 
#ifdef INTMAX_MAX
 
#ifdef LLONG_MAX
 
#if INTMAX_MAX > LLONG_MAX
#error INTMAX_MAX > LLONG_MAX! The printf code needs to be updated!
#endif
 
#elif INTMAX_MAX > LONG_MAX
 
#error No LLONG_MAX and INTMAX_MAX > LONG_MAX! The printf code needs to be updated!
 
#endif /* LLONG_MAX */
 
#endif /* INTMAX_MAX */
 
/* We assume int may be short or long, but short and long are different. */
 
void _store_inttype(register void *dest, int desttype, uintmax_t val)
{
if (desttype == __PA_FLAG_CHAR) { /* assume char not int */
*((unsigned char *) dest) = val;
return;
}
#if defined(LLONG_MAX) && (LONG_MAX != LLONG_MAX)
if (desttype == PA_FLAG_LONG_LONG) {
*((unsigned long long int *) dest) = val;
return;
}
#endif /* LLONG_MAX */
#if SHRT_MAX != INT_MAX
if (desttype == PA_FLAG_SHORT) {
*((unsigned short int *) dest) = val;
return;
}
#endif /* SHRT_MAX */
#if LONG_MAX != INT_MAX
if (desttype == PA_FLAG_LONG) {
*((unsigned long int *) dest) = val;
return;
}
#endif /* LONG_MAX */
 
*((unsigned int *) dest) = val;
}
 
#endif
/**********************************************************************/
#ifdef L__load_inttype
 
extern uintmax_t _load_inttype(int desttype, register const void *src,
int uflag)
{
if (uflag >= 0) { /* unsigned */
#if LONG_MAX != INT_MAX
if (desttype & (PA_FLAG_LONG|PA_FLAG_LONG_LONG)) {
#ifdef LLONG_MAX
if (desttype == PA_FLAG_LONG_LONG) {
return *((unsigned long long int *) src);
}
#endif
return *((unsigned long int *) src);
}
#else /* LONG_MAX != INT_MAX */
#ifdef LLONG_MAX
if (desttype & PA_FLAG_LONG_LONG) {
return *((unsigned long long int *) src);
}
#endif
#endif /* LONG_MAX != INT_MAX */
{
unsigned int x;
x = *((unsigned int *) src);
if (desttype == __PA_FLAG_CHAR) x = (unsigned char) x;
#if SHRT_MAX != INT_MAX
if (desttype == PA_FLAG_SHORT) x = (unsigned short int) x;
#endif
return x;
}
} else { /* signed */
#if LONG_MAX != INT_MAX
if (desttype & (PA_FLAG_LONG|PA_FLAG_LONG_LONG)) {
#ifdef LLONG_MAX
if (desttype == PA_FLAG_LONG_LONG) {
return *((long long int *) src);
}
#endif
return *((long int *) src);
}
#else /* LONG_MAX != INT_MAX */
#ifdef LLONG_MAX
if (desttype & PA_FLAG_LONG_LONG) {
return *((long long int *) src);
}
#endif
#endif /* LONG_MAX != INT_MAX */
{
int x;
x = *((int *) src);
if (desttype == __PA_FLAG_CHAR) x = (char) x;
#if SHRT_MAX != INT_MAX
if (desttype == PA_FLAG_SHORT) x = (short int) x;
#endif
return x;
}
}
}
 
#endif
/**********************************************************************/
#if defined(L_vfprintf) || defined(L_vfwprintf)
 
/* We only support ascii digits (or their USC equivalent codes) in
* precision and width settings in *printf (wide) format strings.
* In other words, we don't currently support glibc's 'I' flag.
* We do accept it, but it is currently ignored. */
 
static void _charpad(FILE * __restrict stream, int padchar, size_t numpad);
 
#ifdef L_vfprintf
 
#define VFPRINTF vfprintf
#define FMT_TYPE char
#define OUTNSTR _outnstr
#define STRLEN strlen
#define _PPFS_init _ppfs_init
#define OUTPUT(F,S) fputs(S,F)
#define _outnstr(stream, string, len) _stdio_fwrite(string, len, stream)
#define FP_OUT _fp_out_narrow
 
#ifdef __STDIO_PRINTF_FLOAT
 
static void _fp_out_narrow(FILE *fp, intptr_t type, intptr_t len, intptr_t buf)
{
if (type & 0x80) { /* Some type of padding needed. */
int buflen = strlen((const char *) buf);
if ((len -= buflen) > 0) {
_charpad(fp, (type & 0x7f), len);
}
len = buflen;
}
OUTNSTR(fp, (const char *) buf, len);
}
 
#endif /* __STDIO_PRINTF_FLOAT */
 
#else /* L_vfprintf */
 
#define VFPRINTF vfwprintf
#define FMT_TYPE wchar_t
#define OUTNSTR _outnwcs
#define STRLEN wcslen
#define _PPFS_init _ppwfs_init
#define OUTPUT(F,S) fputws(S,F)
#define _outnwcs(stream, wstring, len) _wstdio_fwrite(wstring, len, stream)
#define FP_OUT _fp_out_wide
 
static void _outnstr(FILE *stream, const char *s, size_t wclen)
{
/* NOTE!!! len here is the number of wchars we want to generate!!! */
wchar_t wbuf[64];
mbstate_t mbstate;
size_t todo, r;
 
mbstate.mask = 0;
todo = wclen;
while (todo) {
r = mbsrtowcs(wbuf, &s,
((todo <= sizeof(wbuf)/sizeof(wbuf[0]))
? todo
: sizeof(wbuf)/sizeof(wbuf[0])),
&mbstate);
assert(((ssize_t)r) > 0);
_outnwcs(stream, wbuf, r);
todo -= r;
}
}
 
#ifdef __STDIO_PRINTF_FLOAT
 
#ifdef __UCLIBC_MJN3_ONLY__
#warning TODO: Move defines from _fpmaxtostr. Put them in a common header.
#endif
 
/* The following defines are from _fpmaxtostr.*/
#define DIGITS_PER_BLOCK 9
#define NUM_DIGIT_BLOCKS ((DECIMAL_DIG+DIGITS_PER_BLOCK-1)/DIGITS_PER_BLOCK)
#define BUF_SIZE ( 3 + NUM_DIGIT_BLOCKS * DIGITS_PER_BLOCK )
 
static void _fp_out_wide(FILE *fp, intptr_t type, intptr_t len, intptr_t buf)
{
wchar_t wbuf[BUF_SIZE];
const char *s = (const char *) buf;
int i;
 
if (type & 0x80) { /* Some type of padding needed */
int buflen = strlen(s);
if ((len -= buflen) > 0) {
_charpad(fp, (type & 0x7f), len);
}
len = buflen;
}
 
if (len > 0) {
i = 0;
do {
#ifdef __LOCALE_C_ONLY
wbuf[i] = s[i];
#else /* __LOCALE_C_ONLY */
 
#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
if (s[i] == ',') {
wbuf[i] = __UCLIBC_CURLOCALE_DATA.thousands_sep_wc;
} else
#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
if (s[i] == '.') {
wbuf[i] = __UCLIBC_CURLOCALE_DATA.decimal_point_wc;
} else {
wbuf[i] = s[i];
}
#endif /* __LOCALE_C_ONLY */
 
} while (++i < len);
 
OUTNSTR(fp, wbuf, len);
}
}
 
#endif /* __STDIO_PRINTF_FLOAT */
 
static int _ppwfs_init(register ppfs_t *ppfs, const wchar_t *fmt0)
{
static const wchar_t invalid_wcs[] = L"Invalid wide format string.";
int r;
 
/* First, zero out everything... argnumber[], argtype[], argptr[] */
memset(ppfs, 0, sizeof(ppfs_t)); /* TODO: nonportable???? */
#ifdef NL_ARGMAX
--ppfs->maxposarg; /* set to -1 */
#endif /* NL_ARGMAX */
ppfs->fmtpos = (const char *) fmt0;
ppfs->info._flags = FLAG_WIDESTREAM;
 
{
mbstate_t mbstate;
const wchar_t *p;
mbstate.mask = 0; /* Initialize the mbstate. */
p = fmt0;
if (wcsrtombs(NULL, &p, SIZE_MAX, &mbstate) == ((size_t)(-1))) {
ppfs->fmtpos = (const char *) invalid_wcs;
return -1;
}
}
 
/* now set all argtypes to no-arg */
{
#if 1
/* TODO - use memset here since already "paid for"? */
register int *p = ppfs->argtype;
r = MAX_ARGS;
do {
*p++ = __PA_NOARG;
} while (--r);
#else
/* TODO -- get rid of this?? */
register char *p = (char *) ((MAX_ARGS-1) * sizeof(int));
 
do {
*((int *)(((char *)ppfs) + ((int)p) + offsetof(ppfs_t,argtype))) = __PA_NOARG;
p -= sizeof(int);
} while (p);
#endif
}
 
/*
* Run through the entire format string to validate it and initialize
* the positional arg numbers (if any).
*/
{
register const wchar_t *fmt = fmt0;
 
while (*fmt) {
if ((*fmt == '%') && (*++fmt != '%')) {
ppfs->fmtpos = (const char *) fmt; /* back up to the '%' */
if ((r = _ppfs_parsespec(ppfs)) < 0) {
return -1;
}
fmt = (const wchar_t *) ppfs->fmtpos; /* update to one past end of spec */
} else {
++fmt;
}
}
ppfs->fmtpos = (const char *) fmt0; /* rewind */
}
 
#ifdef NL_ARGMAX
/* If we have positional args, make sure we know all the types. */
{
register int *p = ppfs->argtype;
r = ppfs->maxposarg;
while (--r >= 0) {
if ( *p == __PA_NOARG ) { /* missing arg type!!! */
return -1;
}
++p;
}
}
#endif /* NL_ARGMAX */
 
return 0;
}
 
#endif /* L_vfprintf */
 
static void _charpad(FILE * __restrict stream, int padchar, size_t numpad)
{
/* TODO -- Use a buffer to cut down on function calls... */
FMT_TYPE pad[1];
 
*pad = padchar;
while (numpad) {
OUTNSTR(stream, pad, 1);
--numpad;
}
}
 
/* TODO -- Dynamically allocate work space to accomodate stack-poor archs? */
static int _do_one_spec(FILE * __restrict stream,
register ppfs_t *ppfs, int *count)
{
static const char spec_base[] = SPEC_BASE;
#ifdef L_vfprintf
static const char prefix[] = "+\0-\0 \0000x\0000X";
/* 0 2 4 6 9 11*/
#else /* L_vfprintf */
static const wchar_t prefix[] = L"+\0-\0 \0000x\0000X";
#endif /* L_vfprintf */
enum {
PREFIX_PLUS = 0,
PREFIX_MINUS = 2,
PREFIX_SPACE = 4,
PREFIX_LWR_X = 6,
PREFIX_UPR_X = 9,
PREFIX_NONE = 11
};
 
#ifdef __va_arg_ptr
const void * const *argptr;
#else
const void * argptr[MAX_ARGS_PER_SPEC];
#endif
int *argtype;
#ifdef __UCLIBC_HAS_WCHAR__
const wchar_t *ws = NULL;
mbstate_t mbstate;
#endif /* __UCLIBC_HAS_WCHAR__ */
size_t slen;
#ifdef L_vfprintf
#define SLEN slen
#else
size_t SLEN;
wchar_t wbuf[2];
#endif
int base;
int numpad;
int alphacase;
int numfill = 0; /* TODO: fix */
int prefix_num = PREFIX_NONE;
char padchar = ' ';
#ifdef __UCLIBC_MJN3_ONLY__
#warning TODO: Determine appropriate buf size.
#endif /* __UCLIBC_MJN3_ONLY__ */
/* TODO: buf needs to be big enough for any possible error return strings
* and also for any locale-grouped long long integer strings generated.
* This should be large enough for any of the current archs/locales, but
* eventually this should be handled robustly. */
char buf[128];
 
#ifdef NDEBUG
_ppfs_parsespec(ppfs);
#else
if (_ppfs_parsespec(ppfs) < 0) { /* TODO: just for debugging */
abort();
}
#endif
_ppfs_setargs(ppfs);
 
argtype = ppfs->argtype + ppfs->argnumber[2] - 1;
/* Deal with the argptr vs argvalue issue. */
#ifdef __va_arg_ptr
argptr = (const void * const *) ppfs->argptr;
#ifdef NL_ARGMAX
if (ppfs->maxposarg > 0) { /* Using positional args... */
argptr += ppfs->argnumber[2] - 1;
}
#endif /* NL_ARGMAX */
#else
/* Need to build a local copy... */
{
register argvalue_t *p = ppfs->argvalue;
int i;
#ifdef NL_ARGMAX
if (ppfs->maxposarg > 0) { /* Using positional args... */
p += ppfs->argnumber[2] - 1;
}
#endif /* NL_ARGMAX */
for (i = 0 ; i < ppfs->num_data_args ; i++ ) {
argptr[i] = (void *) p++;
}
}
#endif
{
register char *s = NULL; /* TODO: Should s be unsigned char * ? */
 
if (ppfs->conv_num == CONV_n) {
_store_inttype(*(void **)*argptr,
ppfs->info._flags & __PA_INTMASK,
(intmax_t) (*count));
return 0;
}
if (ppfs->conv_num <= CONV_i) { /* pointer or (un)signed int */
alphacase = __UIM_LOWER;
 
#ifdef __UCLIBC_MJN3_ONLY__
#ifdef L_vfprintf
#warning CONSIDER: Should we ignore these flags if stub locale? What about custom specs?
#endif
#endif /* __UCLIBC_MJN3_ONLY__ */
if ((base = spec_base[(int)(ppfs->conv_num - CONV_p)]) == 10) {
if (PRINT_INFO_FLAG_VAL(&(ppfs->info),group)) {
alphacase = __UIM_GROUP;
}
if (PRINT_INFO_FLAG_VAL(&(ppfs->info),i18n)) {
alphacase |= 0x80;
}
}
 
if (ppfs->conv_num <= CONV_u) { /* pointer or unsigned int */
if (ppfs->conv_num == CONV_X) {
alphacase = __UIM_UPPER;
}
if (ppfs->conv_num == CONV_p) { /* pointer */
prefix_num = PREFIX_LWR_X;
} else { /* unsigned int */
}
} else { /* signed int */
base = -base;
}
if (ppfs->info.prec < 0) { /* Ignore '0' flag if prec specified. */
padchar = ppfs->info.pad;
}
#ifdef __UCLIBC_MJN3_ONLY__
#ifdef L_vfprintf
#warning CONSIDER: If using outdigits and/or grouping, how should we interpret precision?
#endif
#endif /* __UCLIBC_MJN3_ONLY__ */
s = _uintmaxtostr(buf + sizeof(buf) - 1,
(uintmax_t)
_load_inttype(*argtype & __PA_INTMASK,
*argptr, base), base, alphacase);
if (ppfs->conv_num > CONV_u) { /* signed int */
if (*s == '-') {
PRINT_INFO_SET_FLAG(&(ppfs->info),showsign);
++s; /* handle '-' in the prefix string */
prefix_num = PREFIX_MINUS;
} else if (PRINT_INFO_FLAG_VAL(&(ppfs->info),showsign)) {
prefix_num = PREFIX_PLUS;
} else if (PRINT_INFO_FLAG_VAL(&(ppfs->info),space)) {
prefix_num = PREFIX_SPACE;
}
}
slen = (char *)(buf + sizeof(buf) - 1) - s;
#ifdef L_vfwprintf
{
const char *q = s;
mbstate.mask = 0; /* Initialize the mbstate. */
SLEN = mbsrtowcs(NULL, &q, 0, &mbstate);
}
#endif
numfill = ((ppfs->info.prec < 0) ? 1 : ppfs->info.prec);
if (PRINT_INFO_FLAG_VAL(&(ppfs->info),alt)) {
if (ppfs->conv_num <= CONV_x) { /* x or p */
prefix_num = PREFIX_LWR_X;
}
if (ppfs->conv_num == CONV_X) {
prefix_num = PREFIX_UPR_X;
}
if ((ppfs->conv_num == CONV_o) && (numfill <= SLEN)) {
numfill = ((*s == '0') ? 1 : SLEN + 1);
}
}
if (*s == '0') {
if (prefix_num >= PREFIX_LWR_X) {
prefix_num = PREFIX_NONE;
}
if (ppfs->conv_num == CONV_p) {/* null pointer */
s = "(nil)";
#ifdef L_vfwprintf
SLEN =
#endif
slen = 5;
numfill = 0;
} else if (numfill == 0) { /* if precision 0, no output */
#ifdef L_vfwprintf
SLEN =
#endif
slen = 0;
}
}
numfill = ((numfill > SLEN) ? numfill - SLEN : 0);
} else if (ppfs->conv_num <= CONV_A) { /* floating point */
#ifdef __STDIO_PRINTF_FLOAT
*count +=
_fpmaxtostr(stream,
(__fpmax_t)
(PRINT_INFO_FLAG_VAL(&(ppfs->info),is_long_double)
? *(long double *) *argptr
: (long double) (* (double *) *argptr)),
&ppfs->info, FP_OUT );
return 0;
#else /* __STDIO_PRINTF_FLOAT */
return -1; /* TODO -- try to continue? */
#endif /* __STDIO_PRINTF_FLOAT */
} else if (ppfs->conv_num <= CONV_S) { /* wide char or string */
#ifdef L_vfprintf
 
#ifdef __UCLIBC_HAS_WCHAR__
mbstate.mask = 0; /* Initialize the mbstate. */
if (ppfs->conv_num == CONV_S) { /* wide string */
if (!(ws = *((const wchar_t **) *argptr))) {
goto NULL_STRING;
}
/* We use an awful uClibc-specific hack here, passing
* (char*) &ws as the conversion destination. This signals
* uClibc's wcsrtombs that we want a "restricted" length
* such that the mbs fits in a buffer of the specified
* size with no partial conversions. */
if ((slen = wcsrtombs((char *) &ws, &ws, /* Use awful hack! */
((ppfs->info.prec >= 0)
? ppfs->info.prec
: SIZE_MAX), &mbstate))
== ((size_t)-1)
) {
return -1; /* EILSEQ */
}
} else { /* wide char */
s = buf;
slen = wcrtomb(s, (*((const wchar_t *) *argptr)), &mbstate);
if (slen == ((size_t)-1)) {
return -1; /* EILSEQ */
}
s[slen] = 0; /* TODO - Is this necessary? */
}
#else /* __UCLIBC_HAS_WCHAR__ */
return -1;
#endif /* __UCLIBC_HAS_WCHAR__ */
} else if (ppfs->conv_num <= CONV_s) { /* char or string */
if (ppfs->conv_num == CONV_s) { /* string */
s = *((char **) (*argptr));
if (s) {
#ifdef __STDIO_PRINTF_M_SUPPORT
SET_STRING_LEN:
#endif
slen = strnlen(s, ((ppfs->info.prec >= 0)
? ppfs->info.prec : SIZE_MAX));
} else {
#ifdef __UCLIBC_HAS_WCHAR__
NULL_STRING:
#endif
s = "(null)";
slen = 6;
}
} else { /* char */
s = buf;
*s = (unsigned char)(*((const int *) *argptr));
s[1] = 0;
slen = 1;
}
 
#else /* L_vfprintf */
 
if (ppfs->conv_num == CONV_S) { /* wide string */
ws = *((wchar_t **) (*argptr));
if (!ws) {
goto NULL_STRING;
}
SLEN = wcsnlen(ws, ((ppfs->info.prec >= 0)
? ppfs->info.prec : SIZE_MAX));
} else { /* wide char */
*wbuf = (wchar_t)(*((const wint_t *) *argptr));
CHAR_CASE:
ws = wbuf;
wbuf[1] = 0;
SLEN = 1;
}
 
} else if (ppfs->conv_num <= CONV_s) { /* char or string */
 
if (ppfs->conv_num == CONV_s) { /* string */
#ifdef __UCLIBC_MJN3_ONLY__
#warning TODO: Fix %s for vfwprintf... output upto illegal sequence?
#endif /* __UCLIBC_MJN3_ONLY__ */
s = *((char **) (*argptr));
if (s) {
#ifdef __STDIO_PRINTF_M_SUPPORT
SET_STRING_LEN:
#endif
/* We use an awful uClibc-specific hack here, passing
* (wchar_t*) &mbstate as the conversion destination.
* This signals uClibc's mbsrtowcs that we want a
* "restricted" length such that the mbs fits in a buffer
* of the specified size with no partial conversions. */
{
const char *q = s;
mbstate.mask = 0; /* Initialize the mbstate. */
SLEN = mbsrtowcs((wchar_t *) &mbstate, &q,
((ppfs->info.prec >= 0)
? ppfs->info.prec : SIZE_MAX),
&mbstate);
}
if (SLEN == ((size_t)(-1))) {
return -1; /* EILSEQ */
}
} else {
NULL_STRING:
s = "(null)";
SLEN = slen = 6;
}
} else { /* char */
*wbuf = btowc( (unsigned char)(*((const int *) *argptr)) );
goto CHAR_CASE;
}
 
#endif /* L_vfprintf */
 
#ifdef __STDIO_PRINTF_M_SUPPORT
} else if (ppfs->conv_num == CONV_m) {
s = _glibc_strerror_r(errno, buf, sizeof(buf));
goto SET_STRING_LEN;
#endif
} else {
#ifdef __STDIO_GLIBC_CUSTOM_PRINTF
assert(ppfs->conv_num == CONV_custom0);
 
s = _custom_printf_spec;
do {
if (*s == ppfs->info.spec) {
int rv;
/* TODO -- check return value for sanity? */
rv = (*_custom_printf_handler
[(int)(s-_custom_printf_spec)])
(stream, &ppfs->info, argptr);
if (rv < 0) {
return -1;
}
*count += rv;
return 0;
}
} while (++s < (_custom_printf_spec + MAX_USER_SPEC));
#endif /* __STDIO_GLIBC_CUSTOM_PRINTF */
assert(0);
return -1;
}
 
#ifdef __UCLIBC_MJN3_ONLY__
#ifdef L_vfprintf
#warning CONSIDER: If using outdigits and/or grouping, how should we pad?
#endif
#endif /* __UCLIBC_MJN3_ONLY__ */
{
size_t t;
 
t = SLEN + numfill;
if (prefix_num != PREFIX_NONE) {
t += ((prefix_num < PREFIX_LWR_X) ? 1 : 2);
}
numpad = ((ppfs->info.width > t) ? (ppfs->info.width - t) : 0);
*count += t + numpad;
}
if (padchar == '0') { /* TODO: check this */
numfill += numpad;
numpad = 0;
}
 
/* Now handle the output itself. */
if (!PRINT_INFO_FLAG_VAL(&(ppfs->info),left)) {
_charpad(stream, ' ', numpad);
numpad = 0;
}
OUTPUT(stream, prefix + prefix_num);
_charpad(stream, '0', numfill);
 
#ifdef L_vfprintf
 
#ifdef __UCLIBC_HAS_WCHAR__
if (!ws) {
assert(s);
_outnstr(stream, s, slen);
} else { /* wide string */
size_t t;
mbstate.mask = 0; /* Initialize the mbstate. */
while (slen) {
t = (slen <= sizeof(buf)) ? slen : sizeof(buf);
t = wcsrtombs(buf, &ws, t, &mbstate);
assert (t != ((size_t)(-1)));
_outnstr(stream, buf, t);
slen -= t;
}
}
#else /* __UCLIBC_HAS_WCHAR__ */
_outnstr(stream, s, slen);
#endif /* __UCLIBC_HAS_WCHAR__ */
 
#else /* L_vfprintf */
 
if (!ws) {
assert(s);
_outnstr(stream, s, SLEN);
} else {
_outnwcs(stream, ws, SLEN);
}
 
#endif /* L_vfprintf */
_charpad(stream, ' ', numpad);
}
 
return 0;
}
 
int VFPRINTF (FILE * __restrict stream,
register const FMT_TYPE * __restrict format,
va_list arg)
{
ppfs_t ppfs;
int count, r;
register const FMT_TYPE *s;
 
__STDIO_THREADLOCK(stream);
 
count = 0;
s = format;
 
#if defined(L_vfprintf) && defined(__UCLIBC_HAS_WCHAR__)
/* Sigh... I forgot that by calling _stdio_fwrite, vfprintf doesn't
* always check the stream's orientation. This is just a temporary
* fix until I rewrite the stdio core work routines. */
if (stream->modeflags & __FLAG_WIDE) {
stream->modeflags |= __FLAG_ERROR;
count = -1;
goto DONE;
}
stream->modeflags |= __FLAG_NARROW;
#endif
 
if (_PPFS_init(&ppfs, format) < 0) { /* Bad format string. */
OUTNSTR(stream, (const FMT_TYPE *) ppfs.fmtpos,
STRLEN((const FMT_TYPE *)(ppfs.fmtpos)));
#if defined(L_vfprintf) && !defined(NDEBUG)
fprintf(stderr,"\nIMbS: \"%s\"\n\n", format);
#endif
count = -1;
} else {
_ppfs_prepargs(&ppfs, arg); /* This did a va_copy!!! */
 
do {
while (*format && (*format != '%')) {
++format;
}
 
if (format-s) { /* output any literal text in format string */
if ( (r = OUTNSTR(stream, s, format-s)) < 0) {
count = -1;
break;
}
count += r;
}
 
if (!*format) { /* we're done */
break;
}
if (format[1] != '%') { /* if we get here, *format == '%' */
/* TODO: _do_one_spec needs to know what the output funcs are!!! */
ppfs.fmtpos = (const char *)(++format);
/* TODO: check -- should only fail on stream error */
if ( (r = _do_one_spec(stream, &ppfs, &count)) < 0) {
count = -1;
break;
}
s = format = (const FMT_TYPE *) ppfs.fmtpos;
} else { /* %% means literal %, so start new string */
s = ++format;
++format;
}
} while (1);
 
va_end(ppfs.arg); /* Need to clean up after va_copy! */
}
 
#if defined(L_vfprintf) && defined(__UCLIBC_HAS_WCHAR__)
DONE:
#endif
 
__STDIO_THREADUNLOCK(stream);
 
return count;
}
#endif
/**********************************************************************/
/ctermid.c
0,0 → 1,36
/* Copyright (C) 1991, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
 
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
 
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
 
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
 
#include <stdio.h>
#include <string.h>
 
 
/* Return the name of the controlling terminal.
If S is not NULL, the name is copied into it (it should be at
least L_ctermid bytes long), otherwise a static buffer is used. */
char *
ctermid (s)
char *s;
{
static char name[L_ctermid];
 
if (s == NULL)
s = name;
 
return strcpy (s, "/dev/tty");
}
/getline.c
0,0 → 1,34
/* vi: set sw=4 ts=4: */
/* getline for uClibc
*
* Copyright (C) 2000 by Lineo, inc. and Erik Andersen
* Copyright (C) 2000,2001 by Erik Andersen <andersen@uclibc.org>
* Written by Erik Andersen <andersen@uclibc.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Library General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
* for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
 
#include <stddef.h>
#include <sys/types.h>
#define __USE_GNU
#include <stdio.h>
 
/* Basically getdelim() with the delimiter hard wired to '\n' */
ssize_t getline(char **linebuf, size_t *n, FILE *file)
{
return (getdelim (linebuf, n, '\n', file));
}
 
/getdelim.c
0,0 → 1,81
/* vi: set sw=4 ts=4: */
/* getdelim for uClibc
*
* Copyright (C) 2000 by Lineo, inc. and Erik Andersen
* Copyright (C) 2000,2001 by Erik Andersen <andersen@uclibc.org>
* Written by Erik Andersen <andersen@uclibc.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Library General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
* for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
 
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
 
 
/* Read up to (and including) a TERMINATOR from STREAM into *LINEPTR
(and null-terminate it). *LINEPTR is a pointer returned from malloc (or
NULL), pointing to *N characters of space. It is realloc'd as
necessary. Returns the number of characters read (not including the
null delimiter), or -1 on error or EOF. */
ssize_t getdelim(char **linebuf, size_t *linebufsz, int delimiter, FILE *file)
{
static const int GROWBY = 80; /* how large we will grow strings by */
 
int ch;
int idx = 0;
 
if (file == NULL || linebuf==NULL || linebufsz == NULL) {
__set_errno(EINVAL);
return -1;
}
 
if (*linebuf == NULL || *linebufsz < 2) {
*linebuf = malloc(GROWBY);
if (!*linebuf) {
__set_errno(ENOMEM);
return -1;
}
*linebufsz += GROWBY;
}
 
while (1) {
ch = fgetc(file);
if (ch == EOF)
break;
/* grow the line buffer as necessary */
while (idx > *linebufsz-2) {
*linebuf = realloc(*linebuf, *linebufsz += GROWBY);
if (!*linebuf) {
__set_errno(ENOMEM);
return -1;
}
}
(*linebuf)[idx++] = (char)ch;
if ((char)ch == delimiter)
break;
}
 
if (idx != 0)
(*linebuf)[idx] = 0;
else if ( ch == EOF )
return -1;
return idx;
}
 
/scanf.c
0,0 → 1,2165
/* Copyright (C) 2002, 2003 Manuel Novoa III
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
 
/* Aug 1, 2003
* New *scanf implementation with lots of bug fixes and *wscanf support.
* Also now optionally supports hexadecimal float notation, positional
* args, and glibc locale-specific digit grouping. Should now be
* standards compliant.
*
* Aug 18, 2003
* Bug fix: scanf %lc,%ls,%l[ would always set mb_fail on eof or error,
* even when just starting a new mb char.
* Bug fix: wscanf would incorrectly unget in certain situations.
*
* Sep 5, 2003
* Bug fix: store flag wasn't respected if no positional args.
* Implement vs{n}scanf for the non-buffered stdio no-wchar case.
*
* Sep 13, 2003
* Bug fix: Fix a problem reported by Atsushi Nemoto <anemo@mba.ocn.ne.jp>
* for environments where long and long long are the same.
*
* Sep 21, 2003
* Ugh... EOF handling by scanf was completely broken. :-( Regretably,
* I got my mind fixed in one mode and didn't comply with the standards.
* Things should be fixed now, but comparision testing is difficult when
* glibc's scanf is broken and they stubbornly refuse to even acknowledge
* that it is... even when confronted by specific examples from the C99
* standards and from an official C standard defect report.
*/
 
 
#define _ISOC99_SOURCE /* for LLONG_MAX primarily... */
#define _GNU_SOURCE
#define _STDIO_UTILITY
#include <features.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <stdarg.h>
#include <stdint.h>
#include <errno.h>
#include <printf.h>
 
#ifdef __UCLIBC_HAS_WCHAR__
#include <bits/uClibc_uwchar.h>
#include <wchar.h>
#include <wctype.h>
#endif /* __UCLIBC_HAS_WCHAR__ */
 
#include <langinfo.h>
#include <locale.h>
 
#include <assert.h>
#include <limits.h>
 
#ifdef __STDIO_THREADSAFE
#include <stdio_ext.h>
#include <pthread.h>
#endif /* __STDIO_THREADSAFE */
 
#ifdef __UCLIBC_HAS_FLOATS__
#include <float.h>
#include <bits/uClibc_fpmax.h>
#endif /* __UCLIBC_HAS_FLOATS__ */
 
#ifdef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__
#ifdef L_vfscanf
/* only emit this once */
#warning Forcing undef of __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ until implemented!
#endif
#undef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__
#endif
 
#undef __STDIO_HAS_VSSCANF
#if defined(__STDIO_BUFFERS) || !defined(__UCLIBC_HAS_WCHAR__) || defined(__STDIO_GLIBC_CUSTOM_STREAMS)
#define __STDIO_HAS_VSSCANF 1
 
#if !defined(__STDIO_BUFFERS) && !defined(__UCLIBC_HAS_WCHAR__)
typedef struct {
FILE f;
unsigned char *bufread; /* pointer to 1 past end of buffer */
unsigned char *bufpos;
} __FILE_vsscanf;
#endif
 
#endif
 
extern void _store_inttype(void *dest, int desttype, uintmax_t val);
 
#if defined(ULLONG_MAX) && (LLONG_MAX > LONG_MAX)
 
extern unsigned long long
_stdlib_strto_ll(register const char * __restrict str,
char ** __restrict endptr, int base, int sflag);
#if (ULLONG_MAX == UINTMAX_MAX)
#define STRTOUIM(s,e,b,sf) _stdlib_strto_ll(s,e,b,sf)
#endif
 
#else /* defined(ULLONG_MAX) && (LLONG_MAX > LONG_MAX) */
 
extern unsigned long
_stdlib_strto_l(register const char * __restrict str,
char ** __restrict endptr, int base, int sflag);
 
#if (ULONG_MAX == UINTMAX_MAX)
#define STRTOUIM(s,e,b,sf) _stdlib_strto_l(s,e,b,sf)
#endif
 
#endif /* defined(ULLONG_MAX) && (LLONG_MAX > LONG_MAX) */
 
#ifndef STRTOUIM
#error STRTOUIM conversion function is undefined!
#endif
 
/**********************************************************************/
 
/* The standards require EOF < 0. */
#if EOF >= CHAR_MIN
#define __isdigit_char_or_EOF(C) __isdigit_char((C))
#else
#define __isdigit_char_or_EOF(C) __isdigit_int((C))
#endif
 
/**********************************************************************/
#ifdef L_fscanf
 
int fscanf(FILE * __restrict stream, const char * __restrict format, ...)
{
va_list arg;
int rv;
 
va_start(arg, format);
rv = vfscanf(stream, format, arg);
va_end(arg);
 
return rv;
}
 
#endif
/**********************************************************************/
#ifdef L_scanf
 
int scanf(const char * __restrict format, ...)
{
va_list arg;
int rv;
 
va_start(arg, format);
rv = vfscanf(stdin, format, arg);
va_end(arg);
 
return rv;
}
 
#endif
/**********************************************************************/
#ifdef L_sscanf
 
#ifdef __STDIO_HAS_VSSCANF
 
int sscanf(const char * __restrict str, const char * __restrict format, ...)
{
va_list arg;
int rv;
 
va_start(arg, format);
rv = vsscanf(str, format, arg);
va_end(arg);
 
return rv;
}
 
#else /* __STDIO_HAS_VSSCANF */
#warning Skipping sscanf since no vsscanf!
#endif /* __STDIO_HAS_VSSCANF */
 
#endif
/**********************************************************************/
#ifdef L_vscanf
 
int vscanf(const char * __restrict format, va_list arg)
{
return vfscanf(stdin, format, arg);
}
 
#endif
/**********************************************************************/
#ifdef L_vsscanf
 
#ifdef __UCLIBC_MJN3_ONLY__
#warning WISHLIST: Implement vsscanf for non-buffered and no custom stream case.
#endif /* __UCLIBC_MJN3_ONLY__ */
 
#ifdef __STDIO_BUFFERS
 
int vsscanf(__const char *sp, __const char *fmt, va_list ap)
{
FILE string[1];
 
string->filedes = -2;
string->modeflags = (__FLAG_NARROW|__FLAG_READONLY);
string->bufstart = string->bufpos = (unsigned char *) ((void *) sp);
#ifdef __STDIO_GETC_MACRO
string->bufgetc =
#endif /* __STDIO_GETC_MACRO */
string->bufread = string->bufstart + strlen(sp);
 
#ifdef __STDIO_MBSTATE
__INIT_MBSTATE(&(string->state));
#endif /* __STDIO_MBSTATE */
 
#ifdef __STDIO_THREADSAFE
string->user_locking = 0;
__stdio_init_mutex(&string->lock);
#endif
 
return vfscanf(string, fmt, ap);
}
 
#elif !defined(__UCLIBC_HAS_WCHAR__)
 
int vsscanf(__const char *sp, __const char *fmt, va_list ap)
{
__FILE_vsscanf string[1];
 
string->f.filedes = -2;
string->f.modeflags = (__FLAG_NARROW|__FLAG_READONLY);
string->bufpos = (unsigned char *) ((void *) sp);
string->bufread = string->bufpos + strlen(sp);
 
#ifdef __STDIO_MBSTATE
#error __STDIO_MBSTATE is defined!
#endif /* __STDIO_MBSTATE */
 
#ifdef __STDIO_THREADSAFE
string->user_locking = 0;
__stdio_init_mutex(&string->f.lock);
#endif
 
return vfscanf(&string->f, fmt, ap);
}
 
#elif defined(__STDIO_GLIBC_CUSTOM_STREAMS)
 
int vsscanf(__const char *sp, __const char *fmt, va_list ap)
{
FILE *f;
int rv;
 
if ((f = fmemopen((char *)sp, strlen(sp), "r")) == NULL) {
return -1;
}
rv = vfscanf(f, fmt, ap);
fclose(f);
 
return rv;
}
 
#else
#warning Skipping vsscanf since no buffering, no custom streams, and wchar enabled!
#ifdef __STDIO_HAS_VSSCANF
#error WHOA! __STDIO_HAS_VSSCANF is defined!
#endif
#endif
 
#endif
/**********************************************************************/
#ifdef L_fwscanf
 
int fwscanf(FILE * __restrict stream, const wchar_t * __restrict format, ...)
{
va_list arg;
int rv;
 
va_start(arg, format);
rv = vfwscanf(stream, format, arg);
va_end(arg);
 
return rv;
}
 
#endif
/**********************************************************************/
#ifdef L_wscanf
 
int wscanf(const wchar_t * __restrict format, ...)
{
va_list arg;
int rv;
 
va_start(arg, format);
rv = vfwscanf(stdin, format, arg);
va_end(arg);
 
return rv;
}
 
#endif
/**********************************************************************/
#ifdef L_swscanf
 
#ifdef __STDIO_BUFFERS
 
int swscanf(const wchar_t * __restrict str, const wchar_t * __restrict format,
...)
{
va_list arg;
int rv;
 
va_start(arg, format);
rv = vswscanf(str, format, arg);
va_end(arg);
 
return rv;
}
#else /* __STDIO_BUFFERS */
#warning Skipping swscanf since no buffering!
#endif /* __STDIO_BUFFERS */
 
#endif
/**********************************************************************/
#ifdef L_vwscanf
 
int vwscanf(const wchar_t * __restrict format, va_list arg)
{
return vfwscanf(stdin, format, arg);
}
 
#endif
/**********************************************************************/
#ifdef L_vswscanf
 
#ifdef __STDIO_BUFFERS
 
int vswscanf(const wchar_t * __restrict str, const wchar_t * __restrict format,
va_list arg)
{
FILE f;
 
f.filedes = -3; /* FAKE STREAM TO SUPPORT *wscanf! */
f.modeflags = (__FLAG_WIDE|__FLAG_READONLY|__FLAG_READING);
f.bufpos = (char *) str;
f.bufend = (char *)(str + wcslen(str));
f.ungot_width[0] = 0;
#ifdef __STDIO_THREADSAFE
f.user_locking = 0;
__stdio_init_mutex(&f.lock);
#endif
 
 
return vfwscanf(&f, format, arg);
}
#else /* __STDIO_BUFFERS */
#warning Skipping vswscanf since no buffering!
#endif /* __STDIO_BUFFERS */
 
#endif
/**********************************************************************/
/**********************************************************************/
 
 
 
/* float layout 0123456789012345678901 repeat n for "l[" */
#define SPEC_CHARS "npxXoudifFeEgGaACSncs["
/* npxXoudif eEgG CS cs[ */
 
/* NOTE: Ordering is important! In particular, CONV_LEFTBRACKET
* must immediately precede CONV_c. */
 
enum {
CONV_n = 0,
CONV_p,
CONV_x, CONV_X, CONV_o, CONV_u, CONV_d, CONV_i,
CONV_f, CONV_F, CONV_e, CONV_E, CONV_g, CONV_G, CONV_a, CONV_A,
CONV_C, CONV_S, CONV_LEFTBRACKET, CONV_c, CONV_s, CONV_leftbracket,
CONV_percent, CONV_whitespace /* not in SPEC_* and no flags */
};
 
#ifdef __UCLIBC_HAS_FLOATS__
#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
/* p x X o u d i f F e E g G a A */
#define SPEC_BASE { 16, 16, 16, 8, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
#else
/* p x X o u d i f F e E g G a A */
#define SPEC_BASE { 16, 16, 16, 8, 10, 10, 0, 10, 10, 10, 10, 10, 10, 10, 10 }
#endif
#else /* __UCLIBC_HAS_FLOATS__ */
/* p x X o u d i f F e E g G a A */
#define SPEC_BASE { 16, 16, 16, 8, 10, 10, 0 }
#endif /* __UCLIBC_HAS_FLOATS__ */
 
#ifdef __UCLIBC_MJN3_ONLY__
#ifdef L_vfscanf
/* emit once */
#warning CONSIDER: Add a '0' flag to eat 0 padding when grouping?
#endif
#endif /* __UCLIBC_MJN3_ONLY__ */
 
#define SPEC_FLAGS "*'I"
 
enum {
FLAG_SURPRESS = 0x10, /* MUST BE 1ST!! See DO_FLAGS. */
FLAG_THOUSANDS = 0x20,
FLAG_I18N = 0x40, /* only works for d, i, u */
FLAG_MALLOC = 0x80, /* only works for s, S, and [ (and l[)*/
};
 
 
#define SPEC_RANGES { CONV_n, CONV_p, CONV_i, CONV_A, \
CONV_C, CONV_LEFTBRACKET, \
CONV_c, CONV_leftbracket }
 
/* Note: We treat L and ll as synonymous... for ints and floats. */
 
#define SPEC_ALLOWED_FLAGS { \
/* n */ (0x0f|FLAG_SURPRESS), \
/* p */ ( 0|FLAG_SURPRESS), \
/* oxXudi */ (0x0f|FLAG_SURPRESS|FLAG_THOUSANDS|FLAG_I18N), \
/* fFeEgGaA */ (0x0c|FLAG_SURPRESS|FLAG_THOUSANDS|FLAG_I18N), \
/* C */ ( 0|FLAG_SURPRESS), \
/* S and l[ */ ( 0|FLAG_SURPRESS|FLAG_MALLOC), \
/* c */ (0x04|FLAG_SURPRESS), \
/* s and [ */ (0x04|FLAG_SURPRESS|FLAG_MALLOC), \
}
 
 
/**********************************************************************/
/*
* In order to ease translation to what arginfo and _print_info._flags expect,
* we map: 0:int 1:char 2:longlong 4:long 8:short
* and then _flags |= (((q << 7) + q) & 0x701) and argtype |= (_flags & 0x701)
*/
 
/* TODO -- Fix the table below to take into account stdint.h. */
/* #ifndef LLONG_MAX */
/* #error fix QUAL_CHARS for no long long! Affects 'L', 'j', 'q', 'll'. */
/* #else */
/* #if LLONG_MAX != INTMAX_MAX */
/* #error fix QUAL_CHARS intmax_t entry 'j'! */
/* #endif */
/* #endif */
 
#ifdef PDS
#error PDS already defined!
#endif
#ifdef SS
#error SS already defined!
#endif
#ifdef IMS
#error IMS already defined!
#endif
 
#if PTRDIFF_MAX == INT_MAX
#define PDS 0
#elif PTRDIFF_MAX == LONG_MAX
#define PDS 4
#elif defined(LLONG_MAX) && (PTRDIFF_MAX == LLONG_MAX)
#define PDS 8
#else
#error fix QUAL_CHARS ptrdiff_t entry 't'!
#endif
 
#if SIZE_MAX == UINT_MAX
#define SS 0
#elif SIZE_MAX == ULONG_MAX
#define SS 4
#elif defined(LLONG_MAX) && (SIZE_MAX == ULLONG_MAX)
#define SS 8
#else
#error fix QUAL_CHARS size_t entries 'z', 'Z'!
#endif
 
#if INTMAX_MAX == INT_MAX
#define IMS 0
#elif INTMAX_MAX == LONG_MAX
#define IMS 4
#elif defined(LLONG_MAX) && (INTMAX_MAX == LLONG_MAX)
#define IMS 8
#else
#error fix QUAL_CHARS ptrdiff_t entry 't'!
#endif
 
#define QUAL_CHARS { \
/* j:(u)intmax_t z:(s)size_t t:ptrdiff_t \0:int q:long_long */ \
'h', 'l', 'L', 'j', 'z', 't', 'q', 0, \
2, 4, 8, IMS, SS, PDS, 8, 0, /* TODO -- fix!!! */\
1, 8 }
 
 
/**********************************************************************/
 
#ifdef L_vfwscanf
#if WINT_MIN > EOF
#error Unfortunately, we currently need wint_t to be able to store EOF. Sorry.
#endif
#define W_EOF WEOF
#define Wint wint_t
#define Wchar wchar_t
#define Wuchar __uwchar_t
#define ISSPACE(C) iswspace((C))
#define VFSCANF vfwscanf
#define GETC(SC) (SC)->sc_getc((SC))
#else
typedef unsigned char __uchar_t;
#define W_EOF EOF
#define Wint int
#define Wchar char
#define Wuchar __uchar_t
#define ISSPACE(C) isspace((C))
#define VFSCANF vfscanf
#ifdef __UCLIBC_HAS_WCHAR__
#define GETC(SC) (SC)->sc_getc((SC))
#else /* __UCLIBC_HAS_WCHAR__ */
#define GETC(SC) getc_unlocked((SC)->fp)
#endif /* __UCLIBC_HAS_WCHAR__ */
#endif
 
struct scan_cookie {
Wint cc;
Wint ungot_char;
FILE *fp;
int nread;
int width;
 
#ifdef __UCLIBC_HAS_WCHAR__
wchar_t app_ungot; /* Match FILE struct member type. */
unsigned char ungot_wchar_width;
#else /* __UCLIBC_HAS_WCHAR__ */
unsigned char app_ungot; /* Match FILE struct member type. */
#endif /* __UCLIBC_HAS_WCHAR__ */
 
char ungot_flag;
 
#ifdef __UCLIBC_HAS_WCHAR__
char ungot_wflag; /* vfwscanf */
char mb_fail; /* vfscanf */
mbstate_t mbstate; /* vfscanf */
wint_t wc;
wint_t ungot_wchar; /* to support __scan_getc */
int (*sc_getc)(struct scan_cookie *);
#endif /* __UCLIBC_HAS_WCHAR__ */
 
#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
const char *grouping;
const unsigned char *thousands_sep;
int tslen;
#ifdef __UCLIBC_HAS_WCHAR__
wchar_t thousands_sep_wc;
#endif /* __UCLIBC_HAS_WCHAR__ */
#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
 
#ifdef __UCLIBC_HAS_FLOATS__
const unsigned char *decpt;
int decpt_len;
#ifdef __UCLIBC_HAS_WCHAR__
wchar_t decpt_wc;
#endif /* __UCLIBC_HAS_WCHAR__ */
const unsigned char *fake_decpt;
#endif /* __UCLIBC_HAS_FLOATS__ */
 
};
 
typedef struct {
#if defined(NL_ARGMAX) && (NL_ARGMAX > 0)
#if NL_ARGMAX > 10
#warning NL_ARGMAX > 10, and space is allocated on the stack for positional args.
#endif
void *pos_args[NL_ARGMAX];
int num_pos_args; /* Must start at -1. */
int cur_pos_arg;
#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */
void *cur_ptr;
const unsigned char *fmt;
int cnt, dataargtype, conv_num, max_width;
unsigned char store, flags;
} psfs_t; /* parse scanf format state */
 
 
/**********************************************************************/
/**********************************************************************/
 
extern void __init_scan_cookie(register struct scan_cookie *sc,
register FILE *fp);
extern int __scan_getc(register struct scan_cookie *sc);
extern void __scan_ungetc(register struct scan_cookie *sc);
 
#ifdef __UCLIBC_HAS_FLOATS__
extern int __scan_strtold(long double *ld, struct scan_cookie *sc);
#endif /* __UCLIBC_HAS_FLOATS__ */
 
extern int __psfs_parse_spec(psfs_t *psfs);
extern int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc);
 
/**********************************************************************/
#ifdef L___scan_cookie
 
#ifdef __UCLIBC_MJN3_ONLY__
#warning TODO: Remove dependence on decpt_str and fake_decpt in stub locale mode.
#endif
#ifndef __UCLIBC_HAS_LOCALE__
static const char decpt_str[] = ".";
#endif
 
void __init_scan_cookie(register struct scan_cookie *sc,
register FILE *fp)
{
sc->fp = fp;
sc->nread = 0;
sc->ungot_flag = 0;
sc->app_ungot = ((fp->modeflags & __MASK_UNGOT) ? fp->ungot[1] : 0);
#ifdef __UCLIBC_HAS_WCHAR__
sc->ungot_wflag = 0; /* vfwscanf */
sc->mb_fail = 0;
#endif /* __UCLIBC_HAS_WCHAR__ */
 
#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
if (*(sc->grouping = __UCLIBC_CURLOCALE_DATA.grouping)) {
sc->thousands_sep = __UCLIBC_CURLOCALE_DATA.thousands_sep;
sc->tslen = __UCLIBC_CURLOCALE_DATA.thousands_sep_len;
#ifdef __UCLIBC_HAS_WCHAR__
sc->thousands_sep_wc = __UCLIBC_CURLOCALE_DATA.thousands_sep_wc;
#endif /* __UCLIBC_HAS_WCHAR__ */
}
#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
 
#ifdef __UCLIBC_HAS_FLOATS__
#ifdef __UCLIBC_HAS_LOCALE__
sc->decpt = __UCLIBC_CURLOCALE_DATA.decimal_point;
sc->decpt_len = __UCLIBC_CURLOCALE_DATA.decimal_point_len;
#else /* __UCLIBC_HAS_LOCALE__ */
sc->fake_decpt = sc->decpt = decpt_str;
sc->decpt_len = 1;
#endif /* __UCLIBC_HAS_LOCALE__ */
#ifdef __UCLIBC_HAS_WCHAR__
#ifdef __UCLIBC_HAS_LOCALE__
sc->decpt_wc = __UCLIBC_CURLOCALE_DATA.decimal_point_wc;
#else
sc->decpt_wc = '.';
#endif
#endif /* __UCLIBC_HAS_WCHAR__ */
#endif /* __UCLIBC_HAS_FLOATS__ */
 
}
 
int __scan_getc(register struct scan_cookie *sc)
{
int c;
 
#ifdef __UCLIBC_HAS_WCHAR__
assert(!sc->mb_fail);
#endif /* __UCLIBC_HAS_WCHAR__ */
 
sc->cc = EOF;
 
if (--sc->width < 0) {
sc->ungot_flag |= 2;
return -1;
}
 
if (sc->ungot_flag == 0) {
#if !defined(__STDIO_BUFFERS) && !defined(__UCLIBC_HAS_WCHAR__)
if (sc->fp->filedes != -2) {
c = GETC(sc);
} else {
__FILE_vsscanf *fv = (__FILE_vsscanf *)(sc->fp);
if (fv->bufpos < fv->bufread) {
c = *fv->bufpos++;
} else {
c = EOF;
sc->fp->modeflags |= __FLAG_EOF;
}
}
if (c == EOF) {
sc->ungot_flag |= 2;
return -1;
}
#else
if ((c = GETC(sc)) == EOF) {
sc->ungot_flag |= 2;
return -1;
}
#endif
sc->ungot_char = c;
} else {
assert(sc->ungot_flag == 1);
sc->ungot_flag = 0;
}
 
++sc->nread;
return sc->cc = sc->ungot_char;
}
 
void __scan_ungetc(register struct scan_cookie *sc)
{
++sc->width;
if (sc->ungot_flag == 2) { /* last was EOF */
sc->ungot_flag = 0;
sc->cc = sc->ungot_char;
} else if (sc->ungot_flag == 0) {
sc->ungot_flag = 1;
--sc->nread;
} else {
assert(0);
}
}
 
#endif
/**********************************************************************/
#ifdef L___psfs_parse_spec
 
#ifdef SPEC_FLAGS
static const unsigned char spec_flags[] = SPEC_FLAGS;
#endif /* SPEC_FLAGS */
static const unsigned char spec_chars[] = SPEC_CHARS;
static const unsigned char qual_chars[] = QUAL_CHARS;
static const unsigned char spec_ranges[] = SPEC_RANGES;
static const unsigned short spec_allowed[] = SPEC_ALLOWED_FLAGS;
 
int __psfs_parse_spec(register psfs_t *psfs)
{
const unsigned char *p;
const unsigned char *fmt0 = psfs->fmt;
int i;
#ifdef SPEC_FLAGS
int j;
#endif
#if defined(NL_ARGMAX) && (NL_ARGMAX > 0)
unsigned char fail = 0;
 
i = 0; /* Do this here to avoid a warning. */
 
if (!__isdigit_char(*psfs->fmt)) { /* Not a positional arg. */
fail = 1;
goto DO_FLAGS;
}
 
/* parse the positional arg (or width) value */
do {
if (i <= ((INT_MAX - 9)/10)) {
i = (i * 10) + (*psfs->fmt++ - '0');
}
} while (__isdigit_char(*psfs->fmt));
 
if (*psfs->fmt != '$') { /* This is a max field width. */
if (psfs->num_pos_args >= 0) { /* Already saw a pos arg! */
goto ERROR_EINVAL;
}
psfs->max_width = i;
psfs->num_pos_args = -2;
goto DO_QUALIFIER;
}
++psfs->fmt; /* Advance past '$'. */
#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */
 
#if defined(SPEC_FLAGS) || (defined(NL_ARGMAX) && (NL_ARGMAX > 0))
DO_FLAGS:
#endif /* defined(SPEC_FLAGS) || (defined(NL_ARGMAX) && (NL_ARGMAX > 0)) */
#ifdef SPEC_FLAGS
p = spec_flags;
j = FLAG_SURPRESS;
do {
if (*p == *psfs->fmt) {
++psfs->fmt;
psfs->flags |= j;
goto DO_FLAGS;
}
j += j;
} while (*++p);
 
if (psfs->flags & FLAG_SURPRESS) { /* Suppress assignment. */
psfs->store = 0;
goto DO_WIDTH;
}
#else /* SPEC_FLAGS */
if (*psfs->fmt == '*') { /* Suppress assignment. */
++psfs->fmt;
psfs->store = 0;
goto DO_WIDTH;
}
#endif /* SPEC_FLAGS */
 
 
#if defined(NL_ARGMAX) && (NL_ARGMAX > 0)
if (fail) {
/* Must be a non-positional arg */
if (psfs->num_pos_args >= 0) { /* Already saw a pos arg! */
goto ERROR_EINVAL;
}
psfs->num_pos_args = -2;
} else {
if ((psfs->num_pos_args == -2) || (((unsigned int)(--i)) >= NL_ARGMAX)) {
/* Already saw a non-pos arg or (0-based) num too large. */
goto ERROR_EINVAL;
}
psfs->cur_pos_arg = i;
}
#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */
 
DO_WIDTH:
for (i = 0 ; __isdigit_char(*psfs->fmt) ; ) {
if (i <= ((INT_MAX - 9)/10)) {
i = (i * 10) + (*psfs->fmt++ - '0');
psfs->max_width = i;
}
}
 
#if defined(NL_ARGMAX) && (NL_ARGMAX > 0)
DO_QUALIFIER:
#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */
p = qual_chars;
do {
if (*psfs->fmt == *p) {
++psfs->fmt;
break;
}
} while (*++p);
if ((p - qual_chars < 2) && (*psfs->fmt == *p)) {
p += ((sizeof(qual_chars)-2) / 2);
++psfs->fmt;
}
psfs->dataargtype = ((int)(p[(sizeof(qual_chars)-2) / 2])) << 8;
 
#ifdef __UCLIBC_MJN3_ONLY__
#warning CONSIDER: Should we validate that psfs->max_width > 0 in __psfs_parse_spec()? It would avoid whitespace consumption...
#warning CONSIDER: Should INT_MAX be a valid width (%c/%C)? See __psfs_parse_spec().
#endif /* __UCLIBC_MJN3_ONLY__ */
 
p = spec_chars;
do {
if (*psfs->fmt == *p) {
int p_m_spec_chars = p - spec_chars;
 
#ifdef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__
#error implement gnu a flag
if ((*p == 'a')
&& ((psfs->fmt[1] == '[') || ((psfs->fmt[1]|0x20) == 's'))
) { /* Assumes ascii for 's' and 'S' test. */
psfs->flags |= FLAG_MALLOC;
++psfs->fmt;
++p;
continue; /* The related conversions follow 'a'. */
}
#endif /* __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ */
 
for (p = spec_ranges; p_m_spec_chars > *p ; ++p) {}
if (((psfs->dataargtype >> 8) | psfs->flags)
& ~spec_allowed[(int)(p - spec_ranges)]
) {
goto ERROR_EINVAL;
}
 
if ((p_m_spec_chars >= CONV_c)
&& (psfs->dataargtype & PA_FLAG_LONG)) {
p_m_spec_chars -= 3; /* lc -> C, ls -> S, l[ -> ?? */
}
 
psfs->conv_num = p_m_spec_chars;
return psfs->fmt - fmt0;
}
if (!*++p) {
ERROR_EINVAL:
__set_errno(EINVAL);
return -1;
}
} while(1);
 
assert(0);
}
 
#endif
/**********************************************************************/
#if defined(L_vfscanf) || defined(L_vfwscanf)
 
#ifdef __UCLIBC_HAS_WCHAR__
#ifdef L_vfscanf
static int sc_getc(register struct scan_cookie *sc)
{
return (getc_unlocked)(sc->fp); /* Disable the macro. */
}
 
static int scan_getwc(register struct scan_cookie *sc)
{
size_t r;
int width;
wchar_t wc[1];
char b[1];
 
if (--sc->width < 0) {
sc->ungot_flag |= 2;
return -1;
}
 
width = sc->width; /* Preserve width. */
sc->width = INT_MAX; /* MB_CUR_MAX can invoke a function. */
 
assert(!sc->mb_fail);
 
r = (size_t)(-3);
while (__scan_getc(sc) >= 0) {
*b = sc->cc;
 
r = mbrtowc(wc, b, 1, &sc->mbstate);
if (((ssize_t) r) >= 0) { /* Successful completion of a wc. */
sc->wc = *wc;
goto SUCCESS;
} else if (r == ((size_t) -2)) {
/* Potentially valid but incomplete. */
continue;
}
break;
}
 
if (r == ((size_t)(-3))) { /* EOF or ERROR on first read */
sc->wc = WEOF;
r = (size_t)(-1);
} else {
/* If we reach here, either r == ((size_t)-1) and
* mbrtowc set errno to EILSEQ, or r == ((size_t)-2)
* and stream is in an error state or at EOF with a
* partially complete wchar. */
__set_errno(EILSEQ); /* In case of incomplete conversion. */
sc->mb_fail = 1;
}
SUCCESS:
sc->width = width; /* Restore width. */
 
return (int)((ssize_t) r);
}
 
#endif /* L_vfscanf */
 
#ifdef L_vfwscanf
 
/* This gets called by __scan_getc. __scan_getc is called by vfwscanf
* when the next wide char is expected to be valid ascii (digits).
*/
static int sc_getc(register struct scan_cookie *sc)
{
wint_t wc;
 
if (sc->fp->filedes == -3) {
if (sc->fp->bufpos < sc->fp->bufend) {
wc = *((wchar_t *)(sc->fp->bufpos));
sc->fp->bufpos += sizeof(wchar_t);
} else {
sc->fp->modeflags |= __FLAG_EOF;
return EOF;
}
} else if ((wc = fgetwc_unlocked(sc->fp)) == WEOF) {
return EOF;
}
 
sc->ungot_wflag = 1;
sc->ungot_wchar = wc;
sc->ungot_wchar_width = sc->fp->ungot_width[0];
 
#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
if (wc == sc->thousands_sep_wc) {
wc = ',';
} else
#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
#ifdef __UCLIBC_HAS_FLOATS__
if (wc == sc->decpt_wc) {
wc = '.';
} else
#endif /* __UCLIBC_HAS_FLOATS__ */
if (!__isascii(wc)) {
wc = '?';
}
sc->wc = sc->ungot_char = wc;
 
return (int) wc;
}
 
static int scan_getwc(register struct scan_cookie *sc)
{
wint_t wc;
 
sc->wc = WEOF;
 
if (--sc->width < 0) {
sc->ungot_flag |= 2;
return -1;
}
 
if (sc->ungot_flag == 0) {
 
if (sc->fp->filedes == -3) {
if (sc->fp->bufpos < sc->fp->bufend) {
wc = *((wchar_t *)(sc->fp->bufpos));
sc->fp->bufpos += sizeof(wchar_t);
} else {
sc->ungot_flag |= 2;
return -1;
}
} else if ((wc = fgetwc_unlocked(sc->fp)) == WEOF) {
sc->ungot_flag |= 2;
return -1;
}
sc->ungot_wflag = 1;
sc->ungot_char = wc;
sc->ungot_wchar_width = sc->fp->ungot_width[0];
} else {
assert(sc->ungot_flag == 1);
sc->ungot_flag = 0;
}
 
++sc->nread;
sc->wc = sc->ungot_char;
 
return 0;
}
 
 
#endif /* L_vfwscanf */
#endif /* __UCLIBC_HAS_WCHAR__ */
 
static __inline void kill_scan_cookie(register struct scan_cookie *sc)
{
#ifdef L_vfscanf
 
if (sc->ungot_flag & 1) {
#if !defined(__STDIO_BUFFERS) && !defined(__UCLIBC_HAS_WCHAR__)
if (sc->fp->filedes != -2) {
ungetc(sc->ungot_char, sc->fp);
}
#else
ungetc(sc->ungot_char, sc->fp);
#endif
/* Deal with distiction between user and scanf ungots. */
if (sc->nread == 0) { /* Only one char was read... app ungot? */
sc->fp->ungot[1] = sc->app_ungot; /* restore ungot state. */
} else {
sc->fp->ungot[1] = 0;
}
}
 
#else
 
if ((sc->ungot_flag & 1) && (sc->ungot_wflag & 1)
&& (sc->fp->filedes != -3) && (sc->fp->state.mask == 0)
) {
ungetwc(sc->ungot_char, sc->fp);
/* Deal with distiction between user and scanf ungots. */
if (sc->nread == 0) { /* Only one char was read... app ungot? */
sc->fp->ungot[1] = sc->app_ungot; /* restore ungot state. */
} else {
sc->fp->ungot[1] = 0;
}
sc->fp->ungot_width[1] = sc->ungot_wchar_width;
}
 
#endif
}
 
#ifdef L_vfwscanf
#ifdef __UCLIBC_HAS_FLOATS__
static const char fake_decpt_str[] = ".";
#endif
#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
static const char fake_thousands_sep_str[] = ",";
#endif
#endif /* L_vfwscanf */
 
 
int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg)
{
const Wuchar *fmt;
unsigned char *b;
 
 
#ifdef L_vfwscanf
wchar_t wbuf[1];
wchar_t *wb;
#endif /* L_vfwscanf */
 
#ifdef __UCLIBC_HAS_WCHAR__
mbstate_t mbstate;
#endif /* __UCLIBC_HAS_WCHAR__ */
 
struct scan_cookie sc;
psfs_t psfs;
 
int i;
 
#warning fix MAX_DIGITS. we do not do binary, so...!
#define MAX_DIGITS 65 /* Allow one leading 0. */
unsigned char buf[MAX_DIGITS+2];
#ifdef L_vfscanf
unsigned char scanset[UCHAR_MAX + 1];
unsigned char invert; /* Careful! Meaning changes. */
#endif /* L_vfscanf */
unsigned char fail;
unsigned char zero_conversions = 1;
 
#ifdef __UCLIBC_MJN3_ONLY__
#warning TODO: Make checking of the format string in C locale an option.
#endif
/* To support old programs, don't check mb validity if in C locale. */
#if defined(__UCLIBC_HAS_LOCALE__) && !defined(L_vfwscanf)
/* ANSI/ISO C99 requires format string to be a valid multibyte string
* beginning and ending in its initial shift state. */
if (((__UCLIBC_CURLOCALE_DATA).encoding) != __ctype_encoding_7_bit) {
const char *p = format;
mbstate.mask = 0; /* Initialize the mbstate. */
if (mbsrtowcs(NULL, &p, SIZE_MAX, &mbstate) == ((size_t)(-1))) {
__set_errno(EINVAL); /* Format string is invalid. */
return 0;
}
}
#endif /* defined(__UCLIBC_HAS_LOCALE__) && !defined(L_vfwscanf) */
 
#if defined(NL_ARGMAX) && (NL_ARGMAX > 0)
psfs.num_pos_args = -1; /* Must start at -1. */
/* Initialize positional arg ptrs to NULL. */
memset(psfs.pos_args, 0, sizeof(psfs.pos_args));
#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */
 
__STDIO_THREADLOCK(fp);
 
__init_scan_cookie(&sc,fp);
#ifdef __UCLIBC_HAS_WCHAR__
sc.sc_getc = sc_getc;
sc.ungot_wchar_width = sc.fp->ungot_width[1];
 
#ifdef L_vfwscanf
 
#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
if (*sc.grouping) {
sc.thousands_sep = fake_thousands_sep_str;
sc.tslen = 1;
}
#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
 
#ifdef __UCLIBC_HAS_FLOATS__
sc.fake_decpt = fake_decpt_str;
#endif /* __UCLIBC_HAS_FLOATS__ */
 
#else /* L_vfwscanf */
 
#ifdef __UCLIBC_HAS_FLOATS__
sc.fake_decpt = sc.decpt;
#endif /* __UCLIBC_HAS_FLOATS__ */
 
#endif /* L_vfwscanf */
 
#endif /* __UCLIBC_HAS_WCHAR__ */
psfs.cnt = 0;
 
/* Note: If we ever wanted to support non-nice codesets, we
* would really need to do a mb->wc conversion here in the
* vfscanf case. Related changes would have to be made in
* the code that follows... basicly wherever fmt appears. */
for (fmt = (const Wuchar *) format ; *fmt ; /* ++fmt */) {
 
psfs.store = 1;
psfs.flags = 0;
#ifndef NDEBUG
psfs.cur_ptr = NULL; /* Debugging aid. */
#endif /* NDEBUG */
 
 
sc.ungot_flag &= 1; /* Clear (possible fake) EOF. */
sc.width = psfs.max_width = INT_MAX;
 
/* Note: According to the standards, vfscanf does use isspace
* here. So, if we did a mb->wc conversion, we would have to do
* something like
* ((((__uwchar_t)wc) < UCHAR_MAX) && isspace(wc))
* because wc might not be in the allowed domain. */
if (ISSPACE(*fmt)) {
do {
++fmt;
} while (ISSPACE(*fmt));
--fmt;
psfs.conv_num = CONV_whitespace;
goto DO_WHITESPACE;
}
 
if (*fmt == '%') { /* Conversion specification. */
if (*++fmt == '%') { /* Remember, '%' eats whitespace too. */
/* Note: The standard says no conversion occurs.
* So do not reset zero_conversions flag. */
psfs.conv_num = CONV_percent;
goto DO_CONVERSION;
}
 
 
#ifdef L_vfscanf
psfs.fmt = fmt;
#else /* L_vfscanf */
{
const __uwchar_t *wf = fmt;
psfs.fmt = b = buf;
 
while (*wf && __isascii(*wf) && (b < buf + sizeof(buf) - 1)) {
*b++ = *wf++;
}
#ifdef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__
#error this is wrong... we need to ched in __psfs_parse_spec instead since this checks last char in buffer and conversion my have stopped before it.
if ((*b == 'a') && ((*wf == '[') || ((*wf|0x20) == 's'))) {
goto DONE; /* Spec was excessively long. */
}
#endif /* __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ */
*b = 0;
if (b == buf) { /* Bad conversion specifier! */
goto DONE;
}
}
#endif /* L_vfscanf */
if ((i = __psfs_parse_spec(&psfs)) < 0) { /* Bad conversion specifier! */
goto DONE;
}
fmt += i;
 
if (psfs.store) {
#if defined(NL_ARGMAX) && (NL_ARGMAX > 0)
if (psfs.num_pos_args == -2) {
psfs.cur_ptr = va_arg(arg, void *);
} else {
while (psfs.cur_pos_arg > psfs.num_pos_args) {
psfs.pos_args[++psfs.num_pos_args] = va_arg(arg, void *);
}
psfs.cur_ptr = psfs.pos_args[psfs.cur_pos_arg];
}
#else /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */
psfs.cur_ptr = va_arg(arg, void *);
#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */
}
 
DO_CONVERSION:
/* First, consume white-space if not n, c, [, C, or l[. */
if ((((1L << CONV_n)|(1L << CONV_C)|(1L << CONV_c)
|(1L << CONV_LEFTBRACKET)|(1L << CONV_leftbracket))
& (1L << psfs.conv_num)) == 0
) {
DO_WHITESPACE:
while ((__scan_getc(&sc) >= 0)
#ifdef L_vfscanf
&& isspace(sc.cc)
#else /* L_vfscanf */
&& iswspace(sc.wc)
#endif /* L_vfscanf */
) {}
__scan_ungetc(&sc);
if (psfs.conv_num == CONV_whitespace) {
goto NEXT_FMT;
}
}
 
sc.width = psfs.max_width; /* Now limit the max width. */
 
if (sc.width == 0) { /* 0 width is forbidden. */
goto DONE;
}
 
 
if (psfs.conv_num == CONV_percent) {
goto MATCH_CHAR;
}
 
if (psfs.conv_num == CONV_n) {
#ifdef __UCLIBC_MJN3_ONLY__
#warning Should %n count as a conversion as far as EOF return value?
#endif
/* zero_conversions = 0; */
if (psfs.store) {
_store_inttype(psfs.cur_ptr, psfs.dataargtype,
(uintmax_t) sc.nread);
}
goto NEXT_FMT;
}
 
if (psfs.conv_num <= CONV_A) { /* pointer, integer, or float spec */
int r = __psfs_do_numeric(&psfs, &sc);
#ifndef L_vfscanf
if (sc.ungot_wflag == 1) { /* fix up '?', '.', and ',' hacks */
sc.cc = sc.ungot_char = sc.ungot_wchar;
}
#endif
if (r != -1) { /* Either success or a matching failure. */
zero_conversions = 0;
}
if (r < 0) {
goto DONE;
}
goto NEXT_FMT;
}
 
/* Do string conversions here since they are not common code. */
 
 
#ifdef L_vfscanf
 
if
#ifdef __UCLIBC_HAS_WCHAR__
(psfs.conv_num >= CONV_LEFTBRACKET)
#else /* __UCLIBC_HAS_WCHAR__ */
(psfs.conv_num >= CONV_c)
#endif /* __UCLIBC_HAS_WCHAR__ */
{
b = (psfs.store ? ((unsigned char *) psfs.cur_ptr) : buf);
fail = 1;
 
if (psfs.conv_num == CONV_c) {
if (sc.width == INT_MAX) {
sc.width = 1;
}
 
while (__scan_getc(&sc) >= 0) {
zero_conversions = 0;
*b = sc.cc;
b += psfs.store;
}
__scan_ungetc(&sc);
if (sc.width > 0) { /* Failed to read all required. */
goto DONE;
}
psfs.cnt += psfs.store;
goto NEXT_FMT;
}
 
if (psfs.conv_num == CONV_s) {
/* Yes, believe it or not, a %s conversion can store nuls. */
while ((__scan_getc(&sc) >= 0) && !isspace(sc.cc)) {
zero_conversions = 0;
*b = sc.cc;
b += psfs.store;
fail = 0;
}
} else {
#ifdef __UCLIBC_HAS_WCHAR__
assert((psfs.conv_num == CONV_LEFTBRACKET) || \
(psfs.conv_num == CONV_leftbracket));
#else /* __UCLIBC_HAS_WCHAR__ */
assert((psfs.conv_num == CONV_leftbracket));
#endif /* __UCLIBC_HAS_WCHAR__ */
 
invert = 0;
 
if (*++fmt == '^') {
++fmt;
invert = 1;
}
memset(scanset, invert, sizeof(scanset));
invert = 1-invert;
 
if (*fmt == ']') {
scanset[(int)(']')] = invert;
++fmt;
}
 
while (*fmt != ']') {
if (!*fmt) { /* No closing ']'. */
goto DONE;
}
if ((*fmt == '-') && (fmt[1] != ']')
&& (fmt[-1] < fmt[1]) /* sorted? */
) { /* range */
++fmt;
i = fmt[-2];
/* Note: scanset[i] should already have been done
* in the previous iteration. */
do {
scanset[++i] = invert;
} while (i < *fmt);
/* Safe to fall through, and a bit smaller. */
}
/* literal char */
scanset[(int) *fmt] = invert;
++fmt;
}
 
#ifdef __UCLIBC_HAS_WCHAR__
if (psfs.conv_num == CONV_LEFTBRACKET) {
goto DO_LEFTBRACKET;
}
#endif /* __UCLIBC_HAS_WCHAR__ */
 
 
while (__scan_getc(&sc) >= 0) {
zero_conversions = 0;
if (!scanset[sc.cc]) {
break;
}
*b = sc.cc;
b += psfs.store;
fail = 0;
}
}
/* Common tail for processing of %s and %[. */
 
__scan_ungetc(&sc);
if (fail) { /* nothing stored! */
goto DONE;
}
*b = 0; /* Nul-terminate string. */
psfs.cnt += psfs.store;
goto NEXT_FMT;
}
 
#ifdef __UCLIBC_HAS_WCHAR__
DO_LEFTBRACKET: /* Need to do common wide init. */
if (psfs.conv_num >= CONV_C) {
wchar_t wbuf[1];
wchar_t *wb;
 
sc.mbstate.mask = 0;
 
wb = (psfs.store ? ((wchar_t *) psfs.cur_ptr) : wbuf);
fail = 1;
if (psfs.conv_num == CONV_C) {
if (sc.width == INT_MAX) {
sc.width = 1;
}
 
while (scan_getwc(&sc) >= 0) {
zero_conversions = 0;
assert(sc.width >= 0);
*wb = sc.wc;
wb += psfs.store;
}
 
__scan_ungetc(&sc);
if (sc.width > 0) { /* Failed to read all required. */
goto DONE;
}
psfs.cnt += psfs.store;
goto NEXT_FMT;
}
 
 
if (psfs.conv_num == CONV_S) {
/* Yes, believe it or not, a %s conversion can store nuls. */
while (scan_getwc(&sc) >= 0) {
zero_conversions = 0;
if ((((__uwchar_t)(sc.wc)) <= UCHAR_MAX) && isspace(sc.wc)) {
break;
}
*wb = sc.wc;
wb += psfs.store;
fail = 0;
}
} else {
assert(psfs.conv_num == CONV_LEFTBRACKET);
 
while (scan_getwc(&sc) >= 0) {
zero_conversions = 0;
if (((__uwchar_t) sc.wc) <= UCHAR_MAX) {
if (!scanset[sc.wc]) {
break;
}
} else if (invert) {
break;
}
*wb = sc.wc;
wb += psfs.store;
fail = 0;
}
}
/* Common tail for processing of %ls and %l[. */
 
__scan_ungetc(&sc);
if (fail || sc.mb_fail) { /* Nothing stored or mb error. */
goto DONE;
}
*wb = 0; /* Nul-terminate string. */
psfs.cnt += psfs.store;
goto NEXT_FMT;
 
}
 
#endif /* __UCLIBC_HAS_WCHAR__ */
#else /* L_vfscanf */
 
if (psfs.conv_num >= CONV_C) {
b = buf;
wb = wbuf;
if (psfs.conv_num >= CONV_c) {
mbstate.mask = 0; /* Initialize the mbstate. */
if (psfs.store) {
b = (unsigned char *) psfs.cur_ptr;
}
} else {
if (psfs.store) {
wb = (wchar_t *) psfs.cur_ptr;
}
}
fail = 1;
 
 
if ((psfs.conv_num == CONV_C) || (psfs.conv_num == CONV_c)) {
if (sc.width == INT_MAX) {
sc.width = 1;
}
 
while (scan_getwc(&sc) >= 0) {
zero_conversions = 0;
if (psfs.conv_num == CONV_C) {
*wb = sc.wc;
wb += psfs.store;
} else {
i = wcrtomb(b, sc.wc, &mbstate);
if (i < 0) { /* Conversion failure. */
goto DONE_DO_UNGET;
}
if (psfs.store) {
b += i;
}
}
}
__scan_ungetc(&sc);
if (sc.width > 0) { /* Failed to read all required. */
goto DONE;
}
psfs.cnt += psfs.store;
goto NEXT_FMT;
}
 
if ((psfs.conv_num == CONV_S) || (psfs.conv_num == CONV_s)) {
/* Yes, believe it or not, a %s conversion can store nuls. */
while (scan_getwc(&sc) >= 0) {
zero_conversions = 0;
if (iswspace(sc.wc)) {
break;
}
if (psfs.conv_num == CONV_S) {
*wb = sc.wc;
wb += psfs.store;
} else {
i = wcrtomb(b, sc.wc, &mbstate);
if (i < 0) { /* Conversion failure. */
goto DONE_DO_UNGET;
}
if (psfs.store) {
b += i;
}
}
fail = 0;
}
} else {
const wchar_t *sss;
const wchar_t *ssp;
unsigned char invert = 0;
 
assert((psfs.conv_num == CONV_LEFTBRACKET)
|| (psfs.conv_num == CONV_leftbracket));
 
if (*++fmt == '^') {
++fmt;
invert = 1;
}
sss = (const wchar_t *) fmt;
if (*fmt == ']') {
++fmt;
}
while (*fmt != ']') {
if (!*fmt) { /* No closing ']'. */
goto DONE;
}
if ((*fmt == '-') && (fmt[1] != ']')
&& (fmt[-1] < fmt[1]) /* sorted? */
) { /* range */
++fmt;
}
++fmt;
}
/* Ok... a valid scanset spec. */
 
while (scan_getwc(&sc) >= 0) {
zero_conversions = 0;
ssp = sss;
do { /* We know sss < fmt. */
if (*ssp == '-') { /* possible range... */
/* Note: We accept a-c-e (ordered) as
* equivalent to a-e. */
if (ssp > sss) {
if ((++ssp < (const wchar_t *) fmt)
&& (ssp[-2] < *ssp) /* sorted? */
) { /* yes */
if ((sc.wc >= ssp[-2])
&& (sc.wc <= *ssp)) {
break;
}
continue; /* not in range */
}
--ssp; /* oops... '-' at end, so back up */
}
/* false alarm... a literal '-' */
}
if (sc.wc == *ssp) { /* Matched literal char. */
break;
}
} while (++ssp < (const wchar_t *) fmt);
 
if ((ssp == (const wchar_t *) fmt) ^ invert) {
/* no match and not inverting
* or match and inverting */
break;
}
if (psfs.conv_num == CONV_LEFTBRACKET) {
*wb = sc.wc;
wb += psfs.store;
} else {
i = wcrtomb(b, sc.wc, &mbstate);
if (i < 0) { /* Conversion failure. */
goto DONE_DO_UNGET;
}
if (psfs.store) {
b += i;
}
}
fail = 0;
}
}
/* Common tail for processing of %s and %[. */
 
__scan_ungetc(&sc);
if (fail) { /* nothing stored! */
goto DONE;
}
*wb = 0; /* Nul-terminate string. */
*b = 0;
psfs.cnt += psfs.store;
goto NEXT_FMT;
}
 
#endif /* L_vfscanf */
 
assert(0);
goto DONE;
} /* conversion specification */
 
MATCH_CHAR:
if (__scan_getc(&sc) != *fmt) {
#ifdef L_vfwscanf
DONE_DO_UNGET:
#endif /* L_vfwscanf */
__scan_ungetc(&sc);
goto DONE;
}
 
NEXT_FMT:
++fmt;
if (__FERROR(fp)) {
break;
}
}
 
DONE:
if (__FERROR(fp) || (*fmt && zero_conversions && __FEOF(fp))) {
psfs.cnt = EOF; /* Yes, vfwscanf also returns EOF. */
}
 
kill_scan_cookie(&sc);
 
__STDIO_THREADUNLOCK(fp);
 
return psfs.cnt;
}
#endif
/**********************************************************************/
#ifdef L___psfs_do_numeric
 
static const unsigned char spec_base[] = SPEC_BASE;
static const unsigned char nil_string[] = "(nil)";
 
int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc)
{
unsigned char *b;
const unsigned char *p;
 
#ifdef __UCLIBC_HAS_FLOATS__
int exp_adjust = 0;
#endif
#warning fix MAX_DIGITS. we do not do binary, so...!
#define MAX_DIGITS 65 /* Allow one leading 0. */
#warning fix buf!
unsigned char buf[MAX_DIGITS+2+ 100];
unsigned char usflag, base;
unsigned char nonzero = 0;
unsigned char seendigit = 0;
 
#warning what should be returned for an invalid conversion specifier?
#ifndef __UCLIBC_HAS_FLOATS__
if (psfs->conv_num > CONV_i) { /* floating point */
goto DONE;
}
#endif
 
base = spec_base[psfs->conv_num - CONV_p];
usflag = (psfs->conv_num <= CONV_u); /* (1)0 if (un)signed */
b = buf;
 
 
if (psfs->conv_num == CONV_p) { /* Pointer */
p = nil_string;
do {
if ((__scan_getc(sc) < 0) || (*p != sc->cc)) {
__scan_ungetc(sc);
if (p > nil_string) {
/* We matched at least the '(' so even if we
* are at eof, we can not match a pointer. */
return -2; /* Matching failure */
}
break;
}
if (!*++p) { /* Matched (nil), so no unget necessary. */
if (psfs->store) {
++psfs->cnt;
_store_inttype(psfs->cur_ptr, psfs->dataargtype,
(uintmax_t) NULL);
}
return 0;
}
} while (1);
 
#ifdef __UCLIBC_MJN3_ONLY__
#warning CONSIDER: Should we require a 0x prefix and disallow +/- for pointer %p?
#endif /* __UCLIBC_MJN3_ONLY__ */
}
 
__scan_getc(sc);
if (sc->cc < 0) {
return -1; /* Input failure (nothing read yet). */
}
 
if ((sc->cc == '+') || (sc->cc == '-')) { /* Handle leading sign.*/
*b++ = sc->cc;
__scan_getc(sc);
}
 
if ((base & 0xef) == 0) { /* 0xef is ~16, so 16 or 0. */
if (sc->cc == '0') { /* Possibly set base and handle prefix. */
__scan_getc(sc);
if ((sc->cc|0x20) == 'x') { /* Assumes ascii.. x or X. */
if (__scan_getc(sc) < 0) {
/* Either EOF or error (including wc outside char range).
* If EOF or error, this is a matching failure (we read 0x).
* If wc outside char range, this is also a matching failure.
* Hence, we do an unget (although not really necessary here
* and fail. */
goto DONE_DO_UNGET; /* matching failure */
}
base = 16; /* Base 16 for sure now. */
#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
/* The prefix is required for hexadecimal floats. */
*b++ = '0';
*b++ = 'x';
#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */
} else { /* oops... back up */
__scan_ungetc(sc);
sc->cc = '0'; /* NASTY HACK! */
 
base = (base >> 1) + 8; /* 0->8, 16->16. no 'if' */
#ifdef __UCLIBC_HAS_FLOATS__
if (psfs->conv_num > CONV_i) { /* floating point */
base = 10;
}
#endif
}
} else if (!base) {
base = 10;
}
}
 
/***************** digit grouping **********************/
#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
 
if ((psfs->flags & FLAG_THOUSANDS) && (base == 10)
&& *(p = sc->grouping)
) {
 
int nblk1, nblk2, nbmax, lastblock, pass, i;
 
 
#ifdef __UCLIBC_MJN3_ONLY__
#warning CONSIDER: Should we initalize the grouping blocks in __init_scan_cookie()?
#endif /* __UCLIBC_MJN3_ONLY__ */
nbmax = nblk2 = nblk1 = *p;
if (*++p) {
nblk2 = *p;
if (nbmax < nblk2) {
nbmax = nblk2;
}
assert(!p[1]);
}
 
/* Note: for printf, if 0 and \' flags appear then
* grouping is done before 0-padding. Should we
* strip leading 0's first? Or add a 0 flag? */
 
/* For vfwscanf, sc_getc translates, so the value of sc->cc is
* either EOF or a char. */
 
if (!__isdigit_char_or_EOF(sc->cc)) { /* No starting digit! */
#ifdef __UCLIBC_HAS_FLOATS__
if (psfs->conv_num > CONV_i) { /* floating point */
goto NO_STARTING_DIGIT;
}
#endif
goto DONE_DO_UNGET;
}
 
if (sc->cc == '0') {
seendigit = 1;
*b++ = '0'; /* Store the first 0. */
#ifdef __UCLIBC_MJN3_ONLY__
#warning CONSIDER: Should leading 0s be skipped before digit grouping? (printf 0 pad)
#endif /* __UCLIBC_MJN3_ONLY__ */
#if 0
do { /* But ignore all subsequent 0s. */
__scan_getc(sc);
} while (sc->cc == '0');
#endif
}
pass = 0;
lastblock = 0;
do {
i = 0;
while (__isdigit_char_or_EOF(sc->cc)) {
seendigit = 1;
if (i == nbmax) { /* too many digits for a block */
#ifdef __UCLIBC_HAS_SCANF_LENIENT_DIGIT_GROUPING__
if (!pass) { /* treat as nongrouped */
if (nonzero) {
goto DO_NO_GROUP;
}
goto DO_TRIM_LEADING_ZEROS;
}
#endif
if (nbmax > nblk1) {
goto DONE_DO_UNGET; /* matching failure */
}
goto DONE_GROUPING_DO_UNGET; /* nbmax == nblk1 */
}
++i;
 
if (nonzero || (sc->cc != '0')) {
if (b < buf + MAX_DIGITS) {
*b++ = sc->cc;
nonzero = 1;
#ifdef __UCLIBC_HAS_FLOATS__
} else {
++exp_adjust;
#endif
}
}
 
__scan_getc(sc);
}
 
if (i) { /* we saw digits digits */
if ((i == nblk2) || ((i < nblk2) && !pass)) {
/* (possible) outer grp */
p = sc->thousands_sep;
if (*p == sc->cc) { /* first byte matches... */
/* so check if grouping mb char */
/* Since 1st matched, either match or fail now
* unless EOF (yuk) */
__scan_getc(sc);
MBG_LOOP:
if (!*++p) { /* is a grouping mb char */
lastblock = i;
++pass;
continue;
}
if (*p == sc->cc) {
__scan_getc(sc);
goto MBG_LOOP;
}
/* bad grouping mb char! */
__scan_ungetc(sc);
if ((sc->cc >= 0) || (p > sc->thousands_sep + 1)) {
#ifdef __UCLIBC_HAS_FLOATS__
/* We failed to match a thousep mb char, and
* we've read too much to recover. But if
* this is a floating point conversion and
* the initial portion of the decpt mb char
* matches, then we may still be able to
* recover. */
int k = p - sc->thousands_sep - 1;
 
if ((psfs->conv_num > CONV_i) /* float conversion */
&& (!pass || (i == nblk1)) /* possible last */
&& !memcmp(sc->thousands_sep, sc->fake_decpt, k)
/* and prefix matched, so could be decpt */
) {
__scan_getc(sc);
p = sc->fake_decpt + k;
do {
if (!*++p) {
strcpy(b, sc->decpt);
b += sc->decpt_len;
goto GOT_DECPT;
}
if (*p != sc->cc) {
__scan_ungetc(sc);
break; /* failed */
}
__scan_getc(sc);
} while (1);
}
#endif /* __UCLIBC_HAS_FLOATS__ */
goto DONE;
}
/* was EOF and 1st, so recoverable. */
}
}
if ((i == nblk1) || ((i < nblk1) && !pass)) {
/* got an inner group */
goto DONE_GROUPING_DO_UNGET;
}
goto DONE_DO_UNGET; /* Matching failure. */
} /* i != 0 */
 
assert(pass);
 
goto DONE_DO_UNGET;
} while (1);
 
assert(0); /* Should never get here. */
}
 
#endif /***************** digit grouping **********************/
 
/* Not grouping so first trim all but one leading 0. */
#ifdef __UCLIBC_HAS_SCANF_LENIENT_DIGIT_GROUPING__
DO_TRIM_LEADING_ZEROS:
#endif /* __UCLIBC_HAS_SCANF_LENIENT_DIGIT_GROUPING__ */
if (sc->cc == '0') {
seendigit = 1;
*b++ = '0'; /* Store the first 0. */
do { /* But ignore all subsequent 0s. */
__scan_getc(sc);
} while (sc->cc == '0');
}
 
#ifdef __UCLIBC_HAS_SCANF_LENIENT_DIGIT_GROUPING__
DO_NO_GROUP:
#endif /* __UCLIBC_HAS_SCANF_LENIENT_DIGIT_GROUPING__ */
/* At this point, we're ready to start reading digits. */
 
#define valid_digit(cc,base) (isxdigit(cc) && ((base == 16) || (cc - '0' < base)))
 
while (valid_digit(sc->cc,base)) { /* Now for significant digits.*/
if (b - buf < MAX_DIGITS) {
nonzero = seendigit = 1; /* Set nonzero too 0s trimmed above. */
*b++ = sc->cc;
#ifdef __UCLIBC_HAS_FLOATS__
} else {
++exp_adjust;
#endif
}
__scan_getc(sc);
}
 
#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
DONE_GROUPING_DO_UNGET:
#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */
if (psfs->conv_num <= CONV_i) { /* integer conversion */
__scan_ungetc(sc);
*b = 0; /* null-terminate */
if (!seendigit) {
goto DONE; /* No digits! */
}
if (psfs->store) {
if (*buf == '-') {
usflag = 0;
}
++psfs->cnt;
_store_inttype(psfs->cur_ptr, psfs->dataargtype,
(uintmax_t) STRTOUIM(buf, NULL, base, 1-usflag));
}
return 0;
}
 
#ifdef __UCLIBC_HAS_FLOATS__
 
/* At this point, we have everything left of the decimal point or exponent. */
#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
NO_STARTING_DIGIT:
#endif
p = sc->fake_decpt;
do {
if (!*p) {
strcpy(b, sc->decpt);
b += sc->decpt_len;
break;
}
if (*p != sc->cc) {
if (p > sc->fake_decpt) {
goto DONE_DO_UNGET; /* matching failure (read some of decpt) */
}
goto DO_DIGIT_CHECK;
}
++p;
__scan_getc(sc);
} while (1);
 
#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__
GOT_DECPT:
#endif
if (!nonzero) {
if (sc->cc == '0') {
assert(exp_adjust == 0);
*b++ = '0';
++exp_adjust;
seendigit = 1;
do {
--exp_adjust;
__scan_getc(sc);
} while (sc->cc == '0');
}
}
 
while (valid_digit(sc->cc,base)) { /* Process fractional digits.*/
if (b - buf < MAX_DIGITS) {
seendigit = 1;
*b++ = sc->cc;
}
__scan_getc(sc);
}
 
DO_DIGIT_CHECK:
/* Hmm... no decimal point. */
if (!seendigit) {
static const unsigned char nan_inf_str[] = "an\0nfinity";
 
if (base == 16) { /* We had a prefix, but no digits! */
goto DONE_DO_UNGET; /* matching failure */
}
 
/* Avoid tolower problems for INFINITY in the tr_TR locale. (yuk)*/
#undef TOLOWER
#define TOLOWER(C) ((C)|0x20)
 
switch (TOLOWER(sc->cc)) {
case 'i':
p = nan_inf_str + 3;
break;
case 'n':
p = nan_inf_str;
break;
default:
/* No digits and not inf or nan. */
goto DONE_DO_UNGET;
}
 
*b++ = sc->cc;
 
do {
__scan_getc(sc);
if (TOLOWER(sc->cc) == *p) {
*b++ = sc->cc;
++p;
continue;
}
if (!*p || (p == nan_inf_str + 5)) { /* match nan/infinity or inf */
goto GOT_FLOAT;
}
/* Unrecoverable. Even if on 1st char, we had no digits. */
goto DONE_DO_UNGET;
} while (1);
}
 
/* If we get here, we had some digits. */
 
if (
#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__
((base == 16) && (((sc->cc)|0x20) == 'p')) ||
#endif
(((sc->cc)|0x20) == 'e')
) { /* Process an exponent. */
*b++ = sc->cc;
 
__scan_getc(sc);
if (sc->cc < 0) {
goto DONE_DO_UNGET; /* matching failure.. no exponent digits */
}
 
if ((sc->cc == '+') || (sc->cc == '-')) { /* Signed exponent? */
*b++ = sc->cc;
__scan_getc(sc);
}
 
#warning fix MAX_EXP_DIGITS!
#define MAX_EXP_DIGITS 20
assert(seendigit);
seendigit = 0;
nonzero = 0;
if (sc->cc == '0') {
seendigit = 1;
*b++ = '0';
do {
__scan_getc(sc);
} while (sc->cc == '0');
}
 
while (__isdigit_char_or_EOF(sc->cc)) { /* Exponent digits (base 10).*/
if (seendigit < MAX_EXP_DIGITS) {
++seendigit;
*b++ = sc->cc;
}
__scan_getc(sc);
}
if (!seendigit) { /* No digits. Unrecoverable. */
goto DONE_DO_UNGET;
}
}
 
 
GOT_FLOAT:
*b = 0;
{
__fpmax_t x;
char *e;
x = __strtofpmax(buf, &e, exp_adjust);
assert(!*e);
if (psfs->store) {
if (psfs->dataargtype & PA_FLAG_LONG_LONG) {
*((long double *)psfs->cur_ptr) = (long double) x;
} else if (psfs->dataargtype & PA_FLAG_LONG) {
*((double *)psfs->cur_ptr) = (double) x;
} else {
*((float *)psfs->cur_ptr) = (float) x;
}
++psfs->cnt;
}
__scan_ungetc(sc);
return 0;
}
#endif /* __UCLIBC_HAS_FLOATS__ */
 
DONE_DO_UNGET:
__scan_ungetc(sc);
DONE:
return -2; /* Matching failure. */
 
}
#endif
/**********************************************************************/
/Makefile
0,0 → 1,111
# Makefile for uClibc
#
# Copyright (C) 2000 by Lineo, inc.
# Copyright (C) 2000,2001 Erik Andersen <andersen@uclibc.org>
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU Library General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option) any
# later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more
# details.
#
# You should have received a copy of the GNU Library General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Derived in part from the Linux-8086 C library, the GNU C Library, and several
# other sundry sources. Files within this library are copyright by their
# respective copyright holders.
 
TOPDIR=../../
include $(TOPDIR)Rules.mak
 
# Note: The *64.o objects are empty when compiled without large file support.
#
 
# Note: Use the libpthreads of: flockfile.o ftrylockfile.o funlockfile.o
# Also, maybe move __fsetlocking.o as well?
 
MSRC = stdio.c
MOBJ = fclose.o fflush.o fopen.o freopen.o perror.o remove.o \
setbuf.o setvbuf.o fgetc.o fgets.o fputc.o fputs.o \
getc.o getchar.o gets.o putc.o putchar.o puts.o \
ungetc.o fread.o fwrite.o fgetpos.o fseek.o fsetpos.o ftell.o \
rewind.o clearerr.o feof.o ferror.o \
fileno.o fdopen.o getw.o putw.o setbuffer.o setlinebuf.o fcloseall.o \
fopen64.o freopen64.o ftello64.o fseeko64.o fsetpos64.o fgetpos64.o \
__fbufsize.o __freading.o __fwriting.o __freadable.o __fwritable.o \
__flbf.o __fpurge.o __fpending.o _flushlbf.o \
fopencookie.o fmemopen.o open_memstream.o \
__fsetlocking.o flockfile.o ftrylockfile.o funlockfile.o \
_stdio_fopen.o _stdio_fread.o _stdio_fwrite.o _stdio_adjpos.o \
_stdio_lseek.o _stdio_init.o \
_stdio_fsfopen.o _stdio_fdout.o _uintmaxtostr.o _stdio_strerror_r.o \
getdelim.o getline.o ctermid.o
 
MSRC2= printf.c
MOBJ2= vsnprintf.o vdprintf.o vasprintf.o vprintf.o vsprintf.o \
fprintf.o snprintf.o dprintf.o asprintf.o printf.o sprintf.o \
_store_inttype.o _load_inttype.o
 
MSRC3=scanf.c
MOBJ3=scanf.o sscanf.o fscanf.o vscanf.o vsscanf.o vfscanf.o \
__scan_cookie.o __psfs_parse_spec.o __psfs_do_numeric.o
 
ifeq ($(UCLIBC_HAS_WCHAR),y)
MOBJ += _wstdio_fwrite.o
MOBJ2 += fwprintf.o wprintf.o swprintf.o vwprintf.o vswprintf.o \
vfwprintf.o
MOBJ3 += wscanf.o swscanf.o fwscanf.o vwscanf.o vswscanf.o vfwscanf.o
endif
 
ifneq ($(USE_OLD_VFPRINTF),y)
MOBJ2 += _ppfs_init.o _ppfs_prepargs.o _ppfs_setargs.o \
_ppfs_parsespec.o vfprintf.o \
register_printf_function.o parse_printf_format.o
endif
 
ifeq ($(UCLIBC_HAS_FLOATS),y)
MOBJ2 += _fpmaxtostr.o
endif
 
CSRC=popen.c tmpfile.c tmpnam.c tmpnam_r.c tempnam.c
ifeq ($(USE_OLD_VFPRINTF),y)
CSRC += old_vfprintf.c
endif
COBJS=$(patsubst %.c,%.o, $(CSRC))
 
OBJS=$(MOBJ) $(MOBJ2) $(MOBJ3) $(COBJS)
 
all: $(OBJS) $(LIBC)
 
$(LIBC): ar-target
 
ar-target: $(OBJS)
$(AR) $(ARFLAGS) $(LIBC) $(OBJS)
 
$(MOBJ): $(MSRC)
$(CC) $(CFLAGS) -DL_$* $< -c -o $*.o
$(STRIPTOOL) -x -R .note -R .comment $*.o
 
$(MOBJ2): $(MSRC2)
$(CC) $(CFLAGS) -DL_$* $< -c -o $*.o
$(STRIPTOOL) -x -R .note -R .comment $*.o
 
$(MOBJ3): $(MSRC3)
$(CC) $(CFLAGS) -DL_$* $< -c -o $*.o
$(STRIPTOOL) -x -R .note -R .comment $*.o
 
$(COBJS): %.o : %.c
$(CC) $(CFLAGS) -c $< -o $@
$(STRIPTOOL) -x -R .note -R .comment $*.o
 
$(OBJ): Makefile
 
clean:
$(RM) *.[oa] *~ core
 
/tmpnam.c
0,0 → 1,48
/* Copyright (C) 1991, 1993, 1996, 1997, 1998 Free Software Foundation, Inc.
This file is part of the GNU C Library.
 
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
 
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
 
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
 
#include <stdio.h>
#include <string.h>
#include "../misc/internals/tempname.h"
 
static char tmpnam_buffer[L_tmpnam];
 
/* Generate a unique filename in P_tmpdir.
This function is *not* thread safe when S == NULL!
*/
char * tmpnam (char *s)
{
/* By using two buffers we manage to be thread safe in the case
where S != NULL. */
char tmpbuf[L_tmpnam];
 
/* In the following call we use the buffer pointed to by S if
non-NULL although we don't know the size. But we limit the size
to L_tmpnam characters in any case. */
if (__path_search (s ? : tmpbuf, L_tmpnam, NULL, NULL, 0))
return NULL;
 
if (__gen_tempname (s ? : tmpbuf, __GT_NOCREATE))
return NULL;
 
if (s == NULL)
return (char *) memcpy (tmpnam_buffer, tmpbuf, L_tmpnam);
 
return s;
}
 
/old_vfprintf.c
0,0 → 1,699
/*
* This file based on printf.c from 'Dlibs' on the atari ST (RdeBath)
*
*
* Dale Schumacher 399 Beacon Ave.
* (alias: Dalnefre') St. Paul, MN 55104
* dal@syntel.UUCP United States of America
* "It's not reality that's important, but how you perceive things."
*/
 
/* Altered to use stdarg, made the core function vfnprintf.
* Hooked into the stdio package using 'inside information'
* Altered sizeof() assumptions, now assumes all integers except chars
* will be either
* sizeof(xxx) == sizeof(long) or sizeof(xxx) == sizeof(short)
*
* -RDB
*/
 
/*
* Manuel Novoa III Dec 2000
*
* The previous vfnprintf routine was almost completely rewritten with the
* goal of fixing some shortcomings and reducing object size.
*
* The summary of changes:
*
* Converted print conversion specification parsing from one big switch
* to a method using string tables. This new method verifies that the
* conversion flags, field width, precision, qualifier, and specifier
* appear in the correct order. Many questionable specifications were
* accepted by the previous code. This new method also resulted in a
* substantial reduction in object size of about 330 bytes (20%) from
* the old version (1627 bytes) on i386, even with the following
* improvements.
*
* Implemented %n specifier as required by the standards.
* Implemented proper handling of precision for int types.
* Implemented # for hex and pointer, fixed error for octal rep of 0.
* Implemented return of -1 on stream error.
*
* Added optional support for the GNU extension %m which prints the string
* corresponding the errno.
*
* Added optional support for long long ints and unsigned long long ints
* using the conversion qualifiers "ll", "L", or "q" (like glibc).
*
* Added optional support for doubles in a very limited form. None of
* the formating options are obeyed. The string returned by __dtostr
* is printed directly.
*
* Converted to use my (un)signed long (long) to string routines, which are
* smaller than the previous functions and don't require static buffers.
*
* Other Modifications:
* Modified sprintf, snprintf, vsprintf, vsnprintf to share on fake-file.
*/
 
/*
* Manuel Novoa III Jan 2001
*
* Removed fake file from *s*printf functions because of possible problems
* if called recursively. Instead, have sprintf, snprintf, and vsprintf
* call vsnprintf which allocates a fake file on the stack.
* Removed WANT_FPUTC option. Always use standard putc macro to avoid
* problems with the fake file used by the *s*printf functions.
* Fixed bug parsing flags -- did not restart scan.
* Added function asprintf.
* Fixed 0-pad prefixing bug.
* Converted sizeof(int) == sizeof(long) tests to compile time vs run time.
* This saves 112 bytes of code on i386.
* Fixed precision bug -- when negative set to default.
* Added function fnprintf to support __dtostr.
* Added floating point support for doubles. Yeah!
*
*
* May 2001 Fixes from Johan Adolfsson (johan.adolfsson@axis.com)
* 1) printf("%c",0) returned 0 instead of 1.
* 2) unrolled loop in asprintf to reduce size and remove compile warning.
*
*
* June 2001
* 1) fix %p so that "0x" is prepended to outputed hex val
* 2) fix %p so that "(nil)" is output for (void *)0 to match glibc
*
* Sep 5, 2003
* Convert to new floating point conversion routine.
* Fix qualifier handling on integer and %n conversions.
* Add support for vsnprintf when in non-buffered/no-wchar configuration.
*
*/
 
/*****************************************************************************/
/* OPTIONS */
/*****************************************************************************/
/* The optional support for long longs and doubles comes in two forms.
*
* 1) Normal (or partial for doubles) output support. Set to 1 to turn on.
* Adds about 130 bytes for doubles, about 220 bytes for long longs,
* and about 275 for both to the base code size of 1163 on i386.
*/
 
/* These are now set in uClibc_config.h based on Config. */
/*
#define __UCLIBC_HAS_FLOATS__ 1
*/
 
/* 2) An error message is inserted into the stream, an arg of the
* appropriate size is removed from the arglist, and processing
* continues. This is adds less code and may be useful in some
* cases. Set to 1 to turn on. Adds about 50 bytes for doubles,
* about 140 bytes for long longs, and about 175 bytes for both
* to the base code size of 1163 on i386.
*/
 
#define WANT_FLOAT_ERROR 0
 
/*
* Set to support GNU extension of %m to print string corresponding to errno.
*
* Warning: This adds about 50 bytes (i386) to the code but it also pulls in
* strerror and the corresponding string table which together are about 3.8k.
*/
 
/* Now controlled by uClibc_stdio.h and set below. */
/* #define WANT_GNU_ERRNO 0 */
 
/**************************************************************************/
 
#define _ISOC99_SOURCE /* for ULLONG primarily... */
#define _GNU_SOURCE /* for strnlen */
#define _STDIO_UTILITY
#include <stdio.h>
#include <stdarg.h>
#include <limits.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
 
#define __PRINTF_INFO_NO_BITFIELD
#include <printf.h>
 
#ifdef __STDIO_THREADSAFE
#include <pthread.h>
#endif /* __STDIO_THREADSAFE */
 
/* #undef __UCLIBC_HAS_FLOATS__ */
/* #undef WANT_FLOAT_ERROR */
/* #define WANT_FLOAT_ERROR 1 */
 
/* #define __isdigit(c) (((unsigned int)(c - '0')) < 10) */
 
#ifdef __STDIO_PRINTF_M_SUPPORT
#define WANT_GNU_ERRNO 1
#else
#define WANT_GNU_ERRNO 0
#endif
 
#undef PUTC
#undef OUTNSTR
#undef _outnstr
 
#ifdef __STDIO_BUFFERS
 
#define PUTC(C,F) putc_unlocked((C),(F))
#define OUTNSTR _outnstr
#define _outnstr(stream, string, len) _stdio_fwrite(string, len, stream)
 
#else /* __STDIO_BUFFERS */
 
typedef struct {
FILE f;
unsigned char *bufend; /* pointer to 1 past end of buffer */
unsigned char *bufpos;
} __FILE_vsnprintf;
 
#ifdef __UCLIBC_HAS_FLOATS__
static void _outnstr(FILE *stream, const unsigned char *s, size_t n)
{
__FILE_vsnprintf *f = (__FILE_vsnprintf *) stream;
 
if (f->f.filedes != -2) {
_stdio_fwrite(s, n, &f->f);
} else {
if (f->bufend > f->bufpos) {
size_t r = f->bufend - f->bufpos;
if (r > n) {
r = n;
}
memcpy(f->bufpos, s, r);
f->bufpos += r;
}
}
}
#endif
 
static void putc_unlocked_sprintf(int c, __FILE_vsnprintf *f)
{
if (f->f.filedes != -2) {
putc_unlocked(c, &f->f);
} else if (f->bufpos < f->bufend) {
*f->bufpos++ = c;
}
}
 
 
#define PUTC(C,F) putc_unlocked_sprintf((C),(__FILE_vsnprintf *)(F))
#define OUTNSTR _outnstr
 
#endif /* __STDIO_BUFFERS */
 
#ifdef __UCLIBC_HAS_FLOATS__
#include <float.h>
#include <bits/uClibc_fpmax.h>
 
typedef void (__fp_outfunc_t)(FILE *fp, intptr_t type, intptr_t len,
intptr_t buf);
 
extern size_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info,
__fp_outfunc_t fp_outfunc);
 
static void _charpad(FILE * __restrict stream, int padchar, size_t numpad)
{
/* TODO -- Use a buffer to cut down on function calls... */
char pad[1];
 
*pad = padchar;
while (numpad) {
OUTNSTR(stream, pad, 1);
--numpad;
}
}
 
static void _fp_out_narrow(FILE *fp, intptr_t type, intptr_t len, intptr_t buf)
{
if (type & 0x80) { /* Some type of padding needed. */
int buflen = strlen((const char *) buf);
if ((len -= buflen) > 0) {
_charpad(fp, (type & 0x7f), len);
}
len = buflen;
}
OUTNSTR(fp, (const char *) buf, len);
}
 
#endif
 
 
enum {
FLAG_PLUS = 0,
FLAG_MINUS_LJUSTIFY,
FLAG_HASH,
FLAG_0_PAD,
FLAG_SPACE,
};
 
/* layout 01234 */
static const char spec[] = "+-#0 ";
 
/**********************************************************************/
 
extern void _store_inttype(void *dest, int desttype, uintmax_t val);
extern uintmax_t _load_inttype(int desttype, const void *src, int uflag);
 
/*
* In order to ease translation to what arginfo and _print_info._flags expect,
* we map: 0:int 1:char 2:longlong 4:long 8:short
* and then _flags |= (((q << 7) + q) & 0x701) and argtype |= (_flags & 0x701)
*/
 
#ifdef PDS
#error PDS already defined!
#endif
#ifdef SS
#error SS already defined!
#endif
#ifdef IMS
#error IMS already defined!
#endif
 
#if PTRDIFF_MAX == INT_MAX
#define PDS 0
#elif PTRDIFF_MAX == LONG_MAX
#define PDS 4
#elif defined(LLONG_MAX) && (PTRDIFF_MAX == LLONG_MAX)
#define PDS 8
#else
#error fix QUAL_CHARS ptrdiff_t entry 't'!
#endif
 
#if SIZE_MAX == UINT_MAX
#define SS 0
#elif SIZE_MAX == ULONG_MAX
#define SS 4
#elif defined(LLONG_MAX) && (SIZE_MAX == ULLONG_MAX)
#define SS 8
#else
#error fix QUAL_CHARS size_t entries 'z', 'Z'!
#endif
 
#if INTMAX_MAX == INT_MAX
#define IMS 0
#elif INTMAX_MAX == LONG_MAX
#define IMS 4
#elif defined(LLONG_MAX) && (INTMAX_MAX == LLONG_MAX)
#define IMS 8
#else
#error fix QUAL_CHARS intmax_t entry 'j'!
#endif
 
#define QUAL_CHARS { \
/* j:(u)intmax_t z:(s)size_t t:ptrdiff_t \0:int */ \
/* q:long_long Z:(s)size_t */ \
'h', 'l', 'L', 'j', 'z', 't', 'q', 'Z', 0, \
2, 4, 8, IMS, SS, PDS, 8, SS, 0, /* TODO -- fix!!! */\
1, 8 \
}
 
static const char qual_chars[] = QUAL_CHARS;
 
/* static const char qual[] = "hlLq"; */
/**********************************************************************/
 
#if !defined(__UCLIBC_HAS_FLOATS__) && WANT_FLOAT_ERROR
static const char dbl_err[] = "<DOUBLE>";
#endif
 
#if defined(__UCLIBC_HAS_FLOATS__) || WANT_FLOAT_ERROR
/* layout 012345678901234567 */
static const char u_spec[] = "%nbopxXudicsfgGeEaA";
#else
/* layout 0123456789012 */
static const char u_spec[] = "%nbopxXudics";
#endif
 
/* WARNING: u_spec and u_radix need to stay in agreement!!! */
/* u_radix[i] <-> u_spec[i+2] for unsigned entries only */
static const char u_radix[] = "\x02\x08\x10\x10\x10\x0a";
 
int vfprintf(FILE * __restrict op, register const char * __restrict fmt,
va_list ap)
{
union {
#ifdef LLONG_MAX
long long ll;
#endif
#if LONG_MAX != INT_MAX
long l;
#endif
int i;
} intarg;
int i, cnt, dataargtype, len;
const void *argptr; /* This does not need to be initialized. */
register char *p;
const char *fmt0;
int preci, width;
#define upcase i
int radix, dpoint /*, upcase*/;
char tmp[65]; /* TODO - determing needed size from headers */
char flag[sizeof(spec)];
 
__STDIO_THREADLOCK(op);
 
cnt = 0;
 
while (*fmt) {
if (*fmt == '%') {
fmt0 = fmt; /* save our position in case of bad format */
++fmt;
width = -1; /* min field width */
preci = -5; /* max string width or mininum digits */
radix = 10; /* number base */
dpoint = 0; /* found decimal point */
 
/* init flags */
for (p =(char *) spec ; *p ; p++) {
flag[p-spec] = '\0';
}
flag[FLAG_0_PAD] = ' ';
 
/* process optional flags */
for (p = (char *)spec ; *p ; ) {
if (*fmt == *p) {
flag[p-spec] = *fmt++;
p = (char *)spec; /* restart scan */
} else {
p++;
}
}
if (!flag[FLAG_PLUS]) {
flag[FLAG_PLUS] = flag[FLAG_SPACE];
}
 
/* process optional width and precision */
do {
if (*fmt == '.') {
++fmt;
dpoint = 1;
}
if (*fmt == '*') { /* parameter width value */
++fmt;
i = va_arg(ap, int);
} else {
for ( i = 0 ; (*fmt >= '0') && (*fmt <= '9') ; ++fmt ) {
i = (i * 10) + (*fmt - '0');
}
}
 
if (dpoint) {
preci = i;
if (i<0) {
preci = -5;
}
} else {
width = i;
if (i<0) {
width = -i;
flag[FLAG_MINUS_LJUSTIFY] = 1;
}
}
} while ((*fmt == '.') && !dpoint );
 
/* process optional qualifier */
p = (char *) qual_chars;
do {
if (*fmt == *p) {
++fmt;
break;
}
} while (*++p);
if ((p - qual_chars < 2) && (*fmt == *p)) {
p += ((sizeof(qual_chars)-2) / 2);
++fmt;
}
dataargtype = ((int)(p[(sizeof(qual_chars)-2) / 2])) << 8;
 
#if WANT_GNU_ERRNO
if (*fmt == 'm') {
flag[FLAG_PLUS] = '\0';
flag[FLAG_0_PAD] = ' ';
p = _glibc_strerror_r(errno, tmp, sizeof(tmp));
goto print;
}
#endif
 
/* process format specifier */
for (p = (char *) u_spec ; *p ; p++) {
if (*fmt != *p) continue;
if (p-u_spec < 1) { /* print a % */
goto charout;
}
if (p-u_spec < 2) { /* store output count in int ptr */
_store_inttype(va_arg(ap, void *),
dataargtype,
(intmax_t) (cnt));
goto nextfmt;
}
 
if (p-u_spec < 10) {
if (*p == 'p') {
#if INTPTR_MAX == INT_MAX
dataargtype = 0;
#else
#error Fix dataargtype for pointers!
#endif
}
 
switch(dataargtype) {
case (PA_INT|PA_FLAG_LONG_LONG):
#ifdef LLONG_MAX
intarg.ll = va_arg(ap, long long);
argptr = &intarg.ll;
break;
#endif
case (PA_INT|PA_FLAG_LONG):
#if LONG_MAX != INT_MAX
intarg.l = va_arg(ap, long);
argptr = &intarg.l;
break;
#endif
default:
intarg.i = va_arg(ap, int);
argptr = &intarg.i;
break;
}
}
 
if (p-u_spec < 8) { /* unsigned conversion */
radix = u_radix[p-u_spec-2];
upcase = ((*p == 'x') ? __UIM_LOWER : __UIM_UPPER);
if (*p == 'p') {
upcase = __UIM_LOWER;
flag[FLAG_HASH] = 'p';
}
p = _uintmaxtostr(tmp + sizeof(tmp) - 1,
(uintmax_t)
_load_inttype(dataargtype, argptr, radix),
radix, upcase);
 
flag[FLAG_PLUS] = '\0'; /* meaningless for unsigned */
if (*p != '0') { /* non-zero */
if (flag[FLAG_HASH]) {
if (radix == 8) {
*--p = '0'; /* add leadding zero */
} else if (radix != 10) { /* either 2 or 16 */
flag[FLAG_PLUS] = '0';
*--p = 'b';
if (radix == 16) {
*p = 'x';
if (*fmt == 'X') {
*p = 'X';
}
}
}
}
} else if (flag[FLAG_HASH] == 'p') { /* null pointer */
p = "(nil)";
}
} else if (p-u_spec < 10) { /* signed conversion */
p = _uintmaxtostr(tmp + sizeof(tmp) - 1,
(uintmax_t)
_load_inttype(dataargtype, argptr, -radix),
-radix, upcase);
 
} else if (p-u_spec < 12) { /* character or string */
flag[FLAG_PLUS] = '\0';
flag[FLAG_0_PAD] = ' ';
if (*p == 'c') { /* character */
p = tmp;
*p = va_arg(ap, int);
/* This takes care of the "%c",0 case */
len = 1;
goto print_len_set;
} else { /* string */
p = va_arg(ap, char *);
if (!p) {
p = "(null)";
preci = 6;
} else {
if (preci < 0) {
preci = INT_MAX;
}
}
len = strnlen(p, preci);
goto print_len_set;
}
#if defined(__UCLIBC_HAS_FLOATS__) || WANT_FLOAT_ERROR
} else if (p-u_spec < 27) { /* floating point */
#endif /* defined(__UCLIBC_HAS_FLOATS__) || WANT_FLOAT_ERROR */
#if defined(__UCLIBC_HAS_FLOATS__)
struct printf_info info;
if (preci < 0) {
preci = 6;
}
info.width = width;
info.prec = preci;
info.spec = *fmt;
info.pad = flag[FLAG_0_PAD];
info._flags = 0;
if (flag[FLAG_PLUS] == '+') {
PRINT_INFO_SET_FLAG(&info,showsign);
} else if (flag[FLAG_PLUS] == ' ') {
PRINT_INFO_SET_FLAG(&info,space);
}
if (flag[FLAG_HASH]) {
PRINT_INFO_SET_FLAG(&info,alt);
}
if (flag[FLAG_MINUS_LJUSTIFY]) {
PRINT_INFO_SET_FLAG(&info,left);
}
#if 1
cnt += _fpmaxtostr(op,
(__fpmax_t)
((dataargtype == (8 << 8))
? va_arg(ap, long double)
: (long double) va_arg(ap, double)),
&info, _fp_out_narrow);
#else
cnt += _fpmaxtostr(op,
(__fpmax_t)
((lval > 1)
? va_arg(ap, long double)
: (long double) va_arg(ap, double)),
&info, _fp_out_narrow);
#endif
goto nextfmt;
#elif WANT_FLOAT_ERROR
(void) ((lval > 1) ? va_arg(ap, long double)
: va_arg(ap, double)); /* carry on */
p = (char *) dbl_err;
#endif /* defined(__UCLIBC_HAS_FLOATS__) */
}
 
#if WANT_GNU_ERRNO
print:
#endif
{ /* this used to be printfield */
/* cheaper than strlen call */
/* for ( len = 0 ; p[len] ; len++ ) { } */
len = strnlen(p, SIZE_MAX);
print_len_set:
if ((*p == '-')
#if WANT_GNU_ERRNO
&& (*fmt != 'm')
#endif
&& (*fmt != 's')) {
flag[FLAG_PLUS] = *p++;
--len;
}
if (flag[FLAG_PLUS]) {
++len;
++preci;
if (flag[FLAG_PLUS] == '0') { /* base 16 */
++preci; /* account for x or X */
}
}
 
if (preci >= 0) {
if ((*fmt == 's')
#if WANT_GNU_ERRNO
|| (*fmt == 'm')
#endif
) {
if (len > preci) {
len = preci;
} else {
preci = len;
}
}
preci -= len;
if (preci < 0) {
preci = 0;
}
width -= preci;
}
 
width -= len;
if (width < 0) {
width = 0;
}
 
if (preci < 0) {
preci = 0;
if (!flag[FLAG_MINUS_LJUSTIFY]
/* && flag[FLAG_PLUS] */
&& (flag[FLAG_0_PAD] == '0')) {
preci = width;
width = 0;
}
}
 
while (width + len + preci) {
unsigned char ch;
/* right padding || left padding */
if ((!len && !preci)
|| (width && !flag[FLAG_MINUS_LJUSTIFY])) {
ch = ' ';
--width;
} else if (flag[FLAG_PLUS]) {
ch = flag[FLAG_PLUS]; /* sign */
if (flag[FLAG_PLUS]=='0') { /* base 16 case */
flag[FLAG_PLUS] = *p++; /* get the x|X */
} else {
flag[FLAG_PLUS] = '\0';
}
--len;
} else if (preci) {
ch = '0';
--preci;
} else {
ch = *p++; /* main field */
--len;
}
++cnt;
PUTC(ch, op);
}
}
goto nextfmt;
}
 
fmt = fmt0; /* this was an illegal format */
}
 
charout:
++cnt;
PUTC(*fmt, op); /* normal char out */
 
nextfmt:
++fmt;
}
 
i = (__FERROR(op)) ? -1 : cnt;
 
__STDIO_THREADUNLOCK(op);
 
return i;
}
/.indent.pro
0,0 → 1,33
--blank-lines-after-declarations
--blank-lines-after-procedures
--break-before-boolean-operator
--no-blank-lines-after-commas
--braces-on-if-line
--braces-on-struct-decl-line
--comment-indentation25
--declaration-comment-column25
--no-comment-delimiters-on-blank-lines
--cuddle-else
--continuation-indentation4
--case-indentation0
--else-endif-column33
--space-after-cast
--line-comments-indentation0
--declaration-indentation1
--dont-format-first-column-comments
--dont-format-comments
--honour-newlines
--indent-level4
/* changed from 0 to 4 */
--parameter-indentation4
--line-length78 /* changed from 75 */
--continue-at-parentheses
--no-space-after-function-call-names
--dont-break-procedure-type
--dont-star-comments
--leave-optional-blank-lines
--dont-space-special-semicolon
--tab-size4
/* additions by Mark */
--case-brace-indentation0
--leave-preprocessor-space
/tmpnam_r.c
0,0 → 1,35
/* Copyright (C) 1991, 1993, 1996, 1997, 1998 Free Software Foundation, Inc.
This file is part of the GNU C Library.
 
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
 
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
 
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
 
#include <stdio.h>
#include "../misc/internals/tempname.h"
 
/* Generate a unique filename in P_tmpdir. If S is NULL return NULL.
This makes this function thread safe. */
char * tmpnam_r (char *s)
{
if (s == NULL)
return NULL;
 
if (__path_search (s, L_tmpnam, NULL, NULL, 0))
return NULL;
if (__gen_tempname (s, __GT_NOCREATE))
return NULL;
 
return s;
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.