/*  Copyright (C) 2002     Manuel Novoa III
 *  Header for 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! */

#ifndef _STDIO_H
#error Always include <stdio.h> rather than <bits/uClibc_stdio.h>
#endif

/**********************************************************************/
#ifdef __UCLIBC__

#ifdef __UCLIBC_HAS_THREADS__
#define __STDIO_THREADSAFE
#endif

#ifdef __UCLIBC_HAVE_LFS__
#define __STDIO_LARGE_FILES
#endif /* __UCLIBC_HAVE_LFS__ */

#ifdef __UCLIBC_HAS_WCHAR__
#define __STDIO_WIDE
#endif

/* Make sure defines related to large files are consistent. */
#ifdef _LIBC

#ifdef __UCLIBC_HAVE_LFS__
#undef __USE_LARGEFILE
#undef __USE_LARGEFILE64
#undef __USE_FILE_OFFSET64
/* if we're actually building uClibc with large file support, only define... */
#define __USE_LARGEFILE64	1
#endif /* __UCLIBC_HAVE_LFS__ */

#else  /* not _LIBC */

#ifndef __UCLIBC_HAVE_LFS__
#if defined(__LARGEFILE64_SOURCE) || defined(__USE_LARGEFILE64) \
    || defined(__USE_FILE_OFFSET64)
#error Sorry... uClibc was built without large file support!
#endif
#endif /* __UCLIBC_HAVE_LFS__ */

#endif /* _LIBC */

#endif /* __UCLIBC__ */
/**********************************************************************/
/* These are the stdio configuration options.  Keep them here until
   uClibc's configuration process gets reworked. */

#ifdef __STDIO_WIDE
#define __need_wchar_t
#include <stddef.h>
/* Note: we don't really need mbstate for 8-bit locales.  We do for UTF-8.
 * For now, always use it. */
#define __STDIO_MBSTATE
#define __need_mbstate_t
#include <wchar.h>
#endif

#define __STDIO_BUFFERS
#define __STDIO_GETC_MACRO
#define __STDIO_PUTC_MACRO

/* For uClibc, these are currently handled above. */
/*  #define __STDIO_LARGE_FILES */
/*  #define __STDIO_THREADSAFE */

/* L mode extension for fopen. */
#define __STDIO_FOPEN_LARGEFILE_MODE

/* size of builtin buf -- only tested with 0 */
#define _STDIO_BUILTIN_BUF_SIZE		0

/* TODO - enable features based on __STDIO_GLIBC_FEATURES */

/*  #define __STDIO_GLIBC_FEATURES */
#define __STDIO_AUTO_RW_TRANSITION
#define __STDIO_FOPEN_EXCLUSIVE_MODE
#define __STDIO_PRINTF_M_SPEC
#define __STDIO_GLIBC_CUSTOM_STREAMS


/* ANSI/ISO mandate at least 256. */
#define _STDIO_BUFSIZ			256

/* Currently unimplemented/untested */
/* #define __STDIO_FLEXIBLE_SETVBUF */

/**********************************************************************/
/* TODO -- posix or gnu -- belongs in limits.h and >= 9 for sus */
/* NOTE: for us it is currently _always_ 9 */
/*#define NL_ARGMAX			9*/

/**********************************************************************/

/* These are consistency checks on the different options */

#ifndef __STDIO_BUFFERS
#undef __STDIO_GETC_MACRO
#undef __STDIO_PUTC_MACRO
#endif

#ifdef __BCC__
#undef __STDIO_LARGE_FILES
#endif

#ifndef __STDIO_LARGE_FILES
#undef __STDIO_FOPEN_LARGEFILE_MODE
#endif

/**********************************************************************/

#ifdef __STDIO_THREADSAFE
/* Need this for pthread_mutex_t. */
#include <bits/pthreadtypes.h>

#define __STDIO_THREADLOCK(STREAM) \
	if ((STREAM)->user_locking == 0) { \
		pthread_mutex_lock(&(STREAM)->lock); \
	}

#define __STDIO_THREADUNLOCK(STREAM) \
	if ((STREAM)->user_locking == 0) { \
		pthread_mutex_unlock(&(STREAM)->lock); \
	}

#define __STDIO_THREADTRYLOCK(STREAM) \
	if ((STREAM)->user_locking == 0) { \
		pthread_mutex_trylock(&(STREAM)->lock); \
	}

#else  /* __STDIO_THREADSAFE */

#define __STDIO_THREADLOCK(STREAM)
#define __STDIO_THREADUNLOCK(STREAM)
#define __STDIO_THREADTRYLOCK(STREAM)

#endif /* __STDIO_THREADSAFE */

/* This file may eventually have two personalities:
   1) core stuff (similar to glibc's libio.h)
   2) extern inlines (for glibc's bits/stdio.h)
   Right now, only (1) is implemented. */

#define _STDIO_IOFBF 0	/* Fully buffered.  */
#define _STDIO_IOLBF 1	/* Line buffered.  */
#define _STDIO_IONBF 2	/* No buffering.  */

typedef struct {
	__off_t __pos;
#ifdef __STDIO_MBSTATE
  __mbstate_t __mbstate;
#endif
} __stdio_fpos_t;

#ifdef __STDIO_LARGE_FILES
typedef struct {
	__off64_t __pos;
#ifdef __STDIO_MBSTATE
  __mbstate_t __mbstate;
#endif
} __stdio_fpos64_t;
#endif


/**********************************************************************/
#ifdef __STDIO_LARGE_FILES
typedef __off64_t __offmax_t;	/* TODO -- rename this? */
#else
typedef __off_t __offmax_t;		/* TODO -- rename this? */
#endif

/**********************************************************************/

#ifdef __STDIO_GLIBC_CUSTOM_STREAMS

typedef __ssize_t __io_read_fn(void *cookie,
							   char *buf, size_t bufsize);
typedef __ssize_t __io_write_fn(void *cookie,
								const char *buf, size_t bufsize);
/* NOTE: GLIBC difference!!! -- fopencookie seek function
 * For glibc, the type of pos is always (__off64_t *) but in our case
 * it is type (__off_t *) when the lib is built without large file support.
 */
typedef int __io_seek_fn(void *cookie,
						 __offmax_t *pos, int whence);
typedef int __io_close_fn(void *cookie);

typedef struct {
	__io_read_fn *read;
	__io_write_fn *write;
	__io_seek_fn *seek;
	__io_close_fn *close;
} _IO_cookie_io_functions_t;

#if defined(_LIBC) || defined(_GNU_SOURCE)
typedef __io_read_fn cookie_read_function_t;
typedef __io_write_fn cookie_write_function_t;
typedef __io_seek_fn cookie_seek_function_t;
typedef __io_close_fn cookie_close_function_t;

typedef _IO_cookie_io_functions_t cookie_io_functions_t;
#endif /* _GNU_SOURCE */

#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */

/*
 * ungot scheme:
 * 0 0   none
 * 0 1   one user (unused ungot is 1) or one scanf (unused ungot is 0)
 * 1 0   must be scanf[0] and user[1]
 * 1 1   illegal -- could be used to signal safe for setbuf
 */

#ifdef __UCLIBC__
#define __stdio_file_struct _UC_FILE
#endif

struct __stdio_file_struct {
	unsigned short modeflags;
	/* There could be a hole here, but modeflags is used most.*/
#ifdef __STDIO_WIDE
	/* TOOD - ungot_width could be combined with ungot.  But what about hole? */
	unsigned char ungot_width[2];
	wchar_t ungot[2];
#else  /* __STDIO_WIDE */
	unsigned char ungot[2];
#endif /* __STDIO_WIDE */
	int filedes;
#if defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS)
	struct __stdio_file_struct *nextopen;
#endif /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */
#ifdef __STDIO_BUFFERS
	unsigned char *bufstart;	/* pointer to buffer */
	unsigned char *bufend;		/* pointer to 1 past end of buffer */
	unsigned char *bufpos;
	unsigned char *bufread;		/* pointer to 1 past last buffered read char. */
#ifdef __STDIO_GETC_MACRO
	unsigned char *bufgetc;		/* 1 past last readable by getc */
#endif /* __STDIO_GETC_MACRO */
#ifdef __STDIO_PUTC_MACRO
	unsigned char *bufputc;		/* 1 past last writeable by putc */
#endif /* __STDIO_PUTC_MACRO */
#endif /* __STDIO_BUFFERS */
#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
	void *cookie;
	_IO_cookie_io_functions_t gcs;
#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */
#ifdef __STDIO_MBSTATE
	__mbstate_t state;
#endif
#ifdef __STDIO_THREADSAFE
	int user_locking;
	pthread_mutex_t lock;
#endif
/* Everything after this is unimplemented... and may be trashed. */
#if __STDIO_BUILTIN_BUF_SIZE > 0
	unsigned char builtinbuf[__STDIO_BUILTIN_BUF_SIZE];
#endif /* __STDIO_BUILTIN_BUF_SIZE > 0 */
};


/***********************************************************************/

#define __MASK_UNGOT    	(0x0002|0x0001)
#define __MASK_UNGOT1    	0x0001
#define __MASK_UNGOT2    	0x0002
#define __FLAG_EOF			0x0004	/* EOF reached? */
#define __FLAG_ERROR		0x0008	/* stream in error state? */
#define __FLAG_WRITEONLY  	0x0010	/* unreadable */
#define __FLAG_READONLY  	0x0020	/* unwriteable */
#define __FLAG_FREEFILE		0x0040	/* free FILE struct after use */
#define __FLAG_NARROW       0x0080

#define __FLAG_FBF          0		/* convenience value */
#define __FLAG_LBF          0x0100
#define __FLAG_NBF          0x0200
#define __MASK_BUFMODE      0x0300

#define __FLAG_APPEND       0x0400
#define __FLAG_WIDE			0x0800

#define __FLAG_READING		0x1000
#define __FLAG_WRITING		0x2000

#define __FLAG_FREEBUF		0x4000	/* free buffer after use */
#define __FLAG_LARGEFILE    0x8000

/**********************************************************************/

#ifdef __STDIO_GLIBC_CUSTOM_STREAMS
extern __ssize_t _cs_read(void *cookie, char *buf, size_t bufsize);
extern __ssize_t _cs_write(void *cookie, const char *buf, size_t bufsize);
extern int _cs_seek(void *cookie, __offmax_t *pos, int whence);
extern int _cs_close(void *cookie);
#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */

/**********************************************************************/

#ifdef __STDIO_MBSTATE
#define __COPY_MBSTATE(dest,src)  ((dest)->mask = (src)->mask, (dest)->wc = (src)->wc)
#define __INIT_MBSTATE(dest) ((dest)->mask = 0)
#else
#define __COPY_MBSTATE(dest,src)
#define __INIT_MBSTATE(dest)
#endif

/**********************************************************************/

/* TODO -- thread safety issues */
#define __CLEARERR(stream) \
	((stream)->modeflags &= ~(__FLAG_EOF|__FLAG_ERROR), (void)0)
#define __FEOF(stream)		((stream)->modeflags & __FLAG_EOF)
#define __FERROR(stream)	((stream)->modeflags & __FLAG_ERROR)

#define __FEOF_OR_FERROR(stream) \
	((stream)->modeflags & (__FLAG_EOF|__FLAG_ERROR))


/* TODO: check this
 * If we want to implement the getc and putc macros, we need to take
 * into account wide streams.  So... would need two additional variables
 * if we have wide streams (bufread and bufwrite), and one otherwise
 * (bufwrite).  getc would be effective for FBF streams.  It isn't for
 * LBF streams because other LBF streams need to be flushed.  putc
 * thouch is only effective for FBF streams.  Of course, to support
 * threads, we have to use functions.
 */

#ifdef __STDIO_GETC_MACRO
#define __GETC(stream)		( ((stream)->bufpos < (stream)->bufgetc) \
							? (*(stream)->bufpos++) \
							: fgetc_unlocked(stream) )
#else  /* __STDIO_GETC_MACRO */
#define __GETC(stream)		fgetc_unlocked(stream)
#endif /* __STDIO_GETC_MACRO */

#ifdef __STDIO_PUTC_MACRO
#define __PUTC(c, stream)	( ((stream)->bufpos < (stream)->bufputc) \
							? (*(stream)->bufpos++) = (c) \
							: fputc_unlocked((c),(stream)) )
#else  /* __STDIO_PUTC_MACRO */
#define __PUTC(c, stream)	fputc_unlocked(c, stream);
#endif /* __STDIO_PUTC_MACRO */


#if 0
/* TODO: disabled for now */
/* Masking macros for the above _are_ allowed by the standard. */
#define clearerr(stream)	__CLEARERR(stream)
#define feof(stream)		__FEOF(stream)
#define ferror(stream)		__FERROR(stream)
#endif

#if 0
/* TODO -- what about custom streams!!! */
/* Only use the macro below if you know fp is a valid FILE for a valid fd. */
#define __fileno(fp)	((fp)->filedes)
#endif

/**********************************************************************
 * PROTOTYPES OF INTERNAL FUNCTIONS
 **********************************************************************/

extern FILE *_stdio_openlist;

#ifdef __STDIO_THREADSAFE
extern pthread_mutex_t _stdio_openlist_lock;
extern void __stdio_init_mutex(pthread_mutex_t *m);
#endif

extern int _stdio_adjpos(FILE * __restrict stream, __offmax_t * pos);
extern int _stdio_lseek(FILE *stream, __offmax_t *pos, int whence);
/* TODO: beware of signals with _stdio_fwrite!!!! */
extern size_t _stdio_fwrite(const unsigned char *buffer, size_t bytes,
							  FILE *stream);
extern size_t _stdio_fread(unsigned char *buffer, size_t bytes,
							 FILE *stream);

extern FILE *_stdio_fopen(const char * __restrict filename,
							const char * __restrict mode,
							FILE * __restrict stream, int filedes);

extern FILE *_stdio_fsfopen(const char * __restrict filename,
							const char * __restrict mode,
							register FILE * __restrict stream);

extern void _stdio_init(void);
extern void _stdio_term(void);

#ifndef NDEBUG
extern void __stdio_validate_FILE(FILE *stream);
#else
#define __stdio_validate_FILE(stream)		((void)0)
#endif

/**********************************************************************
 * UTILITY functions
 **********************************************************************/
#ifdef _STDIO_UTILITY

#include <features.h>
#include <limits.h>
#include <stdint.h>

#if INTMAX_MAX <= 2147483647L
#define __UIM_BUFLEN			12 /* 10 digits + 1 nul + 1 sign */
#elif INTMAX_MAX <= 9223372036854775807LL
#define __UIM_BUFLEN			22 /* 20 digits + 1 nul + 1 sign */
#else
#error unknown number of digits for intmax_t!
#endif

#ifdef LLONG_MAX				/* --------------- */
#if LLONG_MAX <= 2147483647L
#define __UIM_BUFLEN_LLONG		12 /* 10 digits + 1 nul + 1 sign */
#elif LLONG_MAX <= 9223372036854775807LL
#define __UIM_BUFLEN_LLONG		22 /* 20 digits + 1 nul + 1 sign */
#else
#error unknown number of digits for long long!
#endif
#endif /* ULLONG_MAX ----------------------------- */

#if LONG_MAX <= 2147483647L
#define __UIM_BUFLEN_LONG		12 /* 10 digits + 1 nul + 1 sign */
#elif LONG_MAX <= 9223372036854775807LL
#define __UIM_BUFLEN_LONG		22 /* 20 digits + 1 nul + 1 sign */
#else
#error unknown number of digits for long!
#endif

#if INT_MAX <= 32767
#define __UIM_BUFLEN_INT		7 /* 10 digits + 1 nul + 1 sign */
#elif INT_MAX <= 2147483647L
#define __UIM_BUFLEN_INT		12 /* 10 digits + 1 nul + 1 sign */
#else
#error unknown number of digits for int!
#endif

typedef enum {
	__UIM_DECIMAL = 0,
	__UIM_LOWER = 'a' - 10,
	__UIM_UPPER = 'A' - 10,
} __UIM_CASE;

/* Write a NULL-terminated list of "char *" args to file descriptor fd.
 * For an example of usage, see __assert.c.
 */
extern void _stdio_fdout(int fd, ...);

/* Convert the int val to a string in base abs(base).  val is treated as
 * an unsigned ??? int type if base > 0, and signed if base < 0.  This
 * is an internal function with _no_ error checking done unless assert()s
 * are enabled.
 *
 * Note: bufend is a pointer to the END of the buffer passed.
 * Call like this:
 *     char buf[SIZE], *p;
 *     p = _xltostr(buf + sizeof(buf) - 1, {unsigned int},  10, __UIM_DECIMAL)
 *     p = _xltostr(buf + sizeof(buf) - 1,          {int}, -10, __UIM_DECIMAL)
 *
 * WARNING: If base > 10, case _must_be_ either __UIM_LOWER or __UIM_UPPER
 *          for lower and upper case alphas respectively.
 * WARNING: If val is really a signed type, make sure base is negative!
 *          Otherwise, you could overflow your buffer.
 */
extern char *_uintmaxtostr(char * __restrict bufend, uintmax_t uval,
						   int base, __UIM_CASE alphacase);

/* TODO -- make this either a (possibly inline) function? */
#ifndef __BCC__
#define _int10tostr(bufend, intval) \
	_uintmaxtostr((bufend), (intval), -10, __UIM_DECIMAL)
#else  /* bcc doesn't do prototypes, we need to explicitly cast */
#define _int10tostr(bufend, intval) \
	_uintmaxtostr((bufend), (uintmax_t)(intval), -10, __UIM_DECIMAL)
#endif

#define __BUFLEN_INT10TOSTR		__UIM_BUFLEN_INT

#endif /* _STDIO_UTILITY */
/**********************************************************************/
/* uClibc translations */
/**********************************************************************/

/* TODO: note done above..  typedef struct __stdio_file_struct _UC_FILE; */
typedef __stdio_fpos_t		_UC_fpos_t;
#ifdef __STDIO_LARGE_FILES
typedef __stdio_fpos64_t	_UC_fpos64_t;
#endif

#define _UC_IOFBF		_STDIO_IOFBF /* Fully buffered.  */
#define _UC_IOLBF 		_STDIO_IOLBF /* Line buffered.  */
#define _UC_IONBF 		_STDIO_IONBF /* No buffering.  */

#define _UC_BUFSIZ		_STDIO_BUFSIZ
