diff options
Diffstat (limited to 'libc')
-rw-r--r-- | libc/stdio/printf.c | 2033 | ||||
-rw-r--r-- | libc/stdio/stdio.c | 3171 |
2 files changed, 5204 insertions, 0 deletions
diff --git a/libc/stdio/printf.c b/libc/stdio/printf.c new file mode 100644 index 000000000..ad4da445b --- /dev/null +++ b/libc/stdio/printf.c @@ -0,0 +1,2033 @@ +/* Copyright (C) 2002 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! */ + +#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> + +#define __PRINTF_INFO_NO_BITFIELD +#include <printf.h> + +#ifdef __STDIO_THREADSAFE +#include <pthread.h> +#endif /* __STDIO_THREADSAFE */ + +/**********************************************************************/ + +#define __STDIO_GLIBC_CUSTOM_PRINTF + +/* TODO -- move these to a configuration section? */ +#define MAX_FIELD_WIDTH 4095 +#define MAX_USER_SPEC 10 +#define MAX_POS_ARGS 10 + +/* TODO - fix the defs below */ +#define MAX_ARGS_PER_SPEC (MAX_POS_ARGS-2) + +#if MAX_ARGS_PER_SPEC + 2 > MAX_POS_ARGS +#define MAX_ARGS MAX_ARGS_PER_SPEC + 2 +#else +#define MAX_ARGS MAX_POS_ARGS +#endif + +/**********************************************************************/ + +#define __PA_FLAG_INTMASK \ + (__PA_FLAG_CHAR|PA_FLAG_SHORT|__PA_FLAG_INT|PA_FLAG_LONG|PA_FLAG_LONG_LONG) + +extern printf_function *_custom_printf_handler[MAX_USER_SPEC]; +extern printf_arginfo_function *_custom_printf_arginfo[MAX_USER_SPEC]; +extern char *_custom_printf_spec; + +/**********************************************************************/ + +#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 */ +}; + +/**********************************************************************/ + +/* 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, + CONV_m, + 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. */ +#if PTRDIFF_MAX != INT_MAX +#error fix QUAL_CHARS ptrdiff_t entry 't'! +#endif +#if SIZE_MAX != UINT_MAX +#error fix QUAL_CHARS size_t entries 'z', 'Z'! +#endif +#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 + +#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, 8, 0, 0, 8, 0, 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 +#ifndef __BCC__ + double d; + long double ld; +#endif + 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; + int maxposarg; /* > 0 if args are positional, 0 if not, -1 if unknown */ + 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 + /* 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. */ + +/* TODO -- __isdigit() macro */ +#define __isdigit(c) (((unsigned int)(c - '0')) < 10) + +extern size_t _dtostr(FILE * fp, long double x, struct printf_info *info); + +#define _outnstr(stream, string, len) _stdio_fwrite(s, len, stream) /* TODO */ + +extern int _do_one_spec(FILE * __restrict stream, ppfs_t *ppfs, int *count); + +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 */ + +/**********************************************************************/ +#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, int *argtypes) +{ + ppfs_t ppfs; + size_t i; + size_t count = 0; + + if (_ppfs_init(&ppfs, template) >= 0) { + 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. */ + 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; + } + } + } + } + + return count; +} + +#endif +/**********************************************************************/ +#ifdef L_vfprintf + +/* 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. */ + +/* TODO -- Implement grouping flag... on hold (locale support). */ + +int vfprintf(FILE * __restrict stream, register const char * __restrict format, + va_list arg) +{ + ppfs_t ppfs; + int count, r; + register const char *s; + + __STDIO_THREADLOCK(stream); + + count = 0; + s = format; + + if (_ppfs_init(&ppfs, format) < 0) { /* Bad format string. */ + _outnstr(stream, format, strlen(format)); + count = -1; + } else { + _ppfs_prepargs(&ppfs, arg); + + 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 = ++format; + /* TODO: check -- should only fail on stream error */ + if ( (r = _do_one_spec(stream, &ppfs, &count)) < 0) { + count = -1; + break; + } + s = format = ppfs.fmtpos; + } else { /* %% means literal %, so start new string */ + s = ++format; + ++format; + } + } while (1); + } + + __STDIO_THREADUNLOCK(stream); + + 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???? */ + --ppfs->maxposarg; /* set to -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 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 */ + } + + /* 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; + } + } + + return 0; +} +#endif +/**********************************************************************/ +#ifdef L__ppfs_prepargs +void _ppfs_prepargs(register ppfs_t *ppfs, va_list arg) +{ + int i; + + __va_copy(ppfs->arg, arg); + + 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 +/**********************************************************************/ +#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; + + if (ppfs->maxposarg == 0) { /* initing for or no pos args */ + 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; +#ifndef __BCC__ + /* 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 + case PA_DOUBLE: + case (PA_DOUBLE|PA_FLAG_LONG_DOUBLE): + assert(0); +#endif + 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; + } + } 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); + } + } + + /* 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, +#ifndef __BCC__ + /* PA_FLOAT, */ + PA_DOUBLE, + PA_DOUBLE|PA_FLAG_LONG_DOUBLE, +#endif +}; + +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), +#ifndef __BCC__ + /* PROMOTED_SIZE_OF(float), */ + PROMOTED_SIZE_OF(double), + PROMOTED_SIZE_OF(long double), +#endif +}; + +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 + +/* 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]; + +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; + int maxposarg; + int p_m_spec_chars; + int n; + int argtype[MAX_ARGS_PER_SPEC+2]; + int argnumber[3]; /* width, precision, 1st data arg */ + unsigned int conv_num; + 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; + + /* WIDE note: we can test against '%' here since we don't allow */ + /* WIDE note: other mappings of '%' in the wide char set. */ + preci = -1; + width = flags = dpoint = 0; + argnumber[0] = 0; + argnumber[1] = 0; + argtype[0] = __PA_NOARG; + argtype[1] = __PA_NOARG; + maxposarg = ppfs->maxposarg; + fmt = ppfs->fmtpos; + + 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 */ + + 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) { + 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. */ + } + + 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. */ + if (maxposarg) { + if ((*fmt++ != '$') || (i <= 0)) { + /* Using pos args and no $ or invalid arg number. */ + return -1; + } + argnumber[-dpoint] = i; + } else 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 */ + } + + 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_GLIBC_CUSTOM_PRINTF + /* TODO -- gnu %m support build option. */ + if (*fmt == 'm') { + conv_num = CONV_m; + ppfs->num_data_args = 0; + goto DONE; + } + + /* Handle custom arg -- WARNING -- overwrites p!!! */ + 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; + } + +#ifdef __STDIO_GLIBC_CUSTOM_PRINTF + DONE: +#endif + + 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 { + ppfs->argnumber[2] = 1; + memcpy(ppfs->argtype, argtype + 2, ppfs->num_data_args * sizeof(int)); + } + + ppfs->maxposarg = maxposarg; + ppfs->fmtpos = ++fmt; + ppfs->conv_num = conv_num; + + return ppfs->num_data_args + 2; +} + +#endif +/**********************************************************************/ +#ifdef L_register_printf_function + +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 +/**********************************************************************/ +#ifdef L__do_one_spec + +printf_function *_custom_printf_handler[MAX_USER_SPEC]; + +extern void _store_inttype(void *dest, int desttype, uintmax_t val); +extern uintmax_t _load_inttype(int desttype, const void *src, int uflag); +extern void _charpad(FILE * __restrict stream, char padchar, size_t numpad); +extern void output(FILE * __restrict stream, const char *s); + +#define output(F,S) fputs(S,F) + +#undef putc +void _charpad(FILE * __restrict stream, char padchar, size_t numpad) +{ + /* TODO -- Use a buffer to cut down on function calls... */ + char pad[1]; + + *pad = padchar; + while (numpad) { + _stdio_fwrite(pad, 1, stream); + --numpad; + } +} + +/* TODO -- Dynamically allocate work space to accomodate stack-poor archs? */ + +int _do_one_spec(FILE * __restrict stream, register ppfs_t *ppfs, int *count) +{ + static const char spec_base[] = SPEC_BASE; + static const char prefix[] = "+\0-\0 \0000x\0000X"; + /* 0 2 4 6 9 11*/ + 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; + size_t slen; + int base; + int numpad; + int numfill = 0; /* TODO: fix */ + int prefix_num = PREFIX_NONE; + char padchar = ' '; + char buf[64]; + +#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; + if (ppfs->maxposarg > 0) { /* Using positional args... */ + argptr += ppfs->argnumber[2] - 1; + } +#else + /* Need to build a local copy... */ + { + register argvalue_t *p = ppfs->argvalue; + int i; + if (ppfs->maxposarg > 0) { /* Using positional args... */ + p += ppfs->argnumber[2] - 1; + } + for (i = 0 ; i < ppfs->num_data_args ; i++ ) { + argptr[i] = (void *) p++; + } + } +#endif + { + register char *s; + + 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 */ + base = spec_base[(int)(ppfs->conv_num - CONV_p)]; + if (ppfs->conv_num <= CONV_u) { /* pointer or unsigned int */ + 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; + } + s = _uintmaxtostr(buf + sizeof(buf) - 1, + (uintmax_t) + _load_inttype(*argtype & __PA_INTMASK, + *argptr, base), base, + ((ppfs->conv_num == CONV_X) + ? __UIM_UPPER : __UIM_LOWER)); + 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; + 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)"; + slen = 5; + numfill = 0; + } else if (numfill == 0) { /* if precision 0, no output */ + slen = 0; + } + } + numfill = ((numfill > slen) ? numfill - slen : 0); + } else if (ppfs->conv_num <= CONV_A) { /* floating point */ + *count += _dtostr(stream, + (PRINT_INFO_FLAG_VAL(&(ppfs->info),is_long_double) + ? *(long double *) *argptr + : (long double) (* (double *) *argptr)), + &ppfs->info); + return 0; + } else if (ppfs->conv_num <= CONV_S) { /* wide char or string */ +#if 1 + return -1; /* TODO -- wide */ +#else + if (ppfs->conv_num == CONV_S) { /* wide string */ + + } else { /* wide char */ + + } +#endif + } else if (ppfs->conv_num <= CONV_s) { /* char or string */ + if (ppfs->conv_num == CONV_s) { /* string */ + s = *((char **) (*argptr)); + if (s) { + SET_STRING_LEN: + slen = strnlen(s, ((ppfs->info.prec >= 0) + ? ppfs->info.prec : SIZE_MAX)); + } else { + s = "(null)"; + slen = 6; + } + } else { /* char */ + s = (char *) buf; + *s = *((const char *) *argptr); + s[1] = 0; + slen = 1; + } + } else if (ppfs->conv_num == CONV_m) { + s = strerror(errno); + goto SET_STRING_LEN; + } else { + 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)); + assert(0); + return -1; + } + + { + 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); + _outnstr(stream, s, slen); + _charpad(stream, ' ', numpad); + } + + return 0; +} +#endif +/**********************************************************************/ +#ifdef L_vsnprintf +#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.bufrpos = f.bufwpos = 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); + + rv = vfprintf(&f, format, arg); + if (size) { + if (f.bufwpos == f.bufend) { + --f.bufwpos; + } + *f.bufwpos = 0; + } + return rv; +} +#else /* __STDIO_BUFFERS */ +#ifdef __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(void *cookie, const char *buf, size_t bufsize) +{ + size_t count; + 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); + + rv = vfprintf(&f, format, arg); + + return rv; +} + +#else /* __STDIO_GLIBC_CUSTOM_STREAMS */ +#warning skipping vsnprintf since no buffering and no custom streams! +#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */ +#endif /* __STDIO_BUFFERS */ +#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.bufrpos = f.bufwpos = 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); + + rv = vfprintf(&f, format, arg); + + return fflush(&f) ? -1 : rv; +} +#endif +/**********************************************************************/ +#ifdef L_vasprintf + +#if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS) +#warning skipping vasprintf since no buffering and no custom streams! +#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. */ + + int rv = vsnprintf(NULL, 0, format, arg); + + 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 + +#if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS) +#warning skipping vsprintf since no buffering and no custom streams! +#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 + +#if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS) +#warning skipping snprintf since no buffering and no custom streams! +#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 + +#if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS) +#warning skipping asprintf and __asprintf since no buffering and no custom streams! +#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 + +#if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS) +#warning skipping sprintf since no buffering and no custom streams! +#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__dtostr +/* + * Copyright (C) 2000, 2001 Manuel Novoa III + * + * Function: size_t _dtostr(FILE *fp, long double x, struct printf_info *info) + * + * This was written for uClibc to provide floating point support for + * the printf functions. It handles +/- infinity and nan on i386. + * + * Notes: + * + * At most MAX_DIGITS 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 not assumptions are made about the + * bit-layout of doubles. + * + * It should be too difficult to convert this to handle long doubles on i386. + * For information, see the comments below. + * + * TODO: + * long double and/or float version? (note: for float can trim code some). + * + * Decrease the size. This is really much bigger than I'd like. + */ + +/*****************************************************************************/ +/* Don't change anything that follows unless you know what you're doing. */ +/*****************************************************************************/ + +/* + * Configuration for the scaling power table. Ignoring denormals, you + * should have 2**EXP_TABLE_SIZE >= LDBL_MAX_EXP >= 2**(EXP_TABLE_SIZE-1). + * The minimum for standard C is 6. For IEEE 8bit doubles, 9 suffices. + * For long doubles on i386, use 13. + */ +#define EXP_TABLE_SIZE 13 + +/* + * Set this to the maximum number of digits you want converted. + * Conversion is done in blocks of DIGITS_PER_BLOCK (9 by default) digits. + * (20) 17 digits suffices to uniquely determine a (long) double on i386. + */ +#define MAX_DIGITS 20 + +/* + * Set this to the smallest integer type capable of storing a pointer. + */ +#define INT_OR_PTR int + +/* + * This is really only used to check for infinities. The macro produces + * smaller code for i386 and, since this is tested before any floating point + * calculations, it doesn't appear to suffer from the excess precision problem + * caused by the FPU that strtod had. If it causes problems, call the function + * and compile zoicheck.c with -ffloat-store. + */ +#define _zero_or_inf_check(x) ( x == (x/4) ) + +/* + * Fairly portable nan check. Bitwise for i386 generated larger code. + * If you have a better version, comment this out. + */ +#define isnan(x) (x != x) + +/*****************************************************************************/ +/* Don't change anything that follows peroid!!! ;-) */ +/*****************************************************************************/ + +#include <float.h> + +/*****************************************************************************/ + +/* + * Set things up for the scaling power table. + */ + +#if EXP_TABLE_SIZE < 6 +#error EXP_TABLE_SIZE should be at least 6 to comply with standards +#endif + +#define EXP_TABLE_MAX (1U<<(EXP_TABLE_SIZE-1)) + +/* + * Only bother checking if this is too small. + */ + +#if LDBL_MAX_10_EXP/2 > EXP_TABLE_MAX +#error larger EXP_TABLE_SIZE needed +#endif + +/* + * With 32 bit ints, we can get 9 digits per block. + */ +#define DIGITS_PER_BLOCK 9 + +#if UINT_MAX >= 4294967295UL +#define DIGIT_BLOCK_TYPE int +#define DB_FMT "%.*d" +#elif LONG_MAX >= 4294967295UL +#define DIGIT_BLOCK_TYPE long +#define DB_FMT "%.*ld" +#else +#error need at least 32 bit longs +#endif + +/* Maximum number of calls to fnprintf to output double. */ +#define MAX_CALLS 8 + +/*****************************************************************************/ + +#define NUM_DIGIT_BLOCKS ((MAX_DIGITS+DIGITS_PER_BLOCK-1)/DIGITS_PER_BLOCK) + +/* extra space for '-', '.', 'e+###', and nul */ +#define BUF_SIZE ( 3 + NUM_DIGIT_BLOCKS * DIGITS_PER_BLOCK ) +/*****************************************************************************/ + +static const char *fmts[] = { + "%0*d", "%.*s", ".", "inf", "INF", "nan", "NAN", "%*s" +}; + +/*****************************************************************************/ + +size_t _dtostr(FILE * fp, long double x, struct printf_info *info) +{ + long double exp_table[EXP_TABLE_SIZE]; + long double p10; + DIGIT_BLOCK_TYPE digit_block; /* int of at least 32 bits */ + int i, j; + int round, o_exp; + int exp, exp_neg; + int width, preci; + char *s; + char *e; + char buf[BUF_SIZE]; + INT_OR_PTR pc_fwi[2*MAX_CALLS]; + INT_OR_PTR *ppc; + char exp_buf[8]; + char drvr[8]; + char *pdrvr; + int npc; + int cnt; + char sign_str[2]; + char o_mode; + char mode; + + /* check that INT_OR_PTR is sufficiently large */ + assert( sizeof(INT_OR_PTR) == sizeof(char *) ); + + width = info->width; + preci = info->prec; + mode = info->spec; + if (mode == 'a') { + mode = 'g'; /* TODO -- fix */ + } + if (mode == 'A') { + mode = 'G'; /* TODO -- fix */ + } + + 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 = flag[FLAG_PLUS]; */ + *(sign_str+1) = 0; + if (isnan(x)) { /* nan check */ + pdrvr = drvr + 1; + *pdrvr++ = 5 + (mode < 'a'); + pc_fwi[2] = 3; + info->pad = ' '; +/* flag[FLAG_0_PAD] = 0; */ + goto EXIT_SPECIAL; + } + + if (x == 0) { /* handle 0 now to avoid false positive */ + exp = -1; + goto GENERATE_DIGITS; + } + + if (x < 0) { /* convert negatives to positives */ + *sign_str = '-'; + x = -x; + } + + if (_zero_or_inf_check(x)) { /* must be inf since zero handled above */ + pdrvr = drvr + 1; + *pdrvr++ = 3 + + (mode < 'a'); + pc_fwi[2] = 3; + info->pad = ' '; +/* flag[FLAG_0_PAD] = 0; */ + goto EXIT_SPECIAL; + } + + /* need to build the scaling table */ + for (i = 0, p10 = 10 ; i < EXP_TABLE_SIZE ; i++) { + exp_table[i] = p10; + p10 *= p10; + } + + exp_neg = 0; + if (x < 1e8) { /* do we need to scale up or down? */ + exp_neg = 1; + } + + exp = DIGITS_PER_BLOCK - 1; + + i = EXP_TABLE_SIZE; + j = EXP_TABLE_MAX; + while ( i-- ) { /* scale x such that 1e8 <= x < 1e9 */ + if (exp_neg) { + if (x * exp_table[i] < 1e9) { + x *= exp_table[i]; + exp -= j; + } + } else { + if (x / exp_table[i] >= 1e8) { + x /= exp_table[i]; + exp += j; + } + } + j >>= 1; + } + if (x >= 1e9) { /* handle bad rounding case */ + x /= 10; + ++exp; + } + assert(x < 1e9); + + GENERATE_DIGITS: + s = buf + 2; /* leave space for '\0' and '0' */ +#if 1 +#define ONE_E_NINE 1000000000L +#else +#define ONE_E_NINE 1e9 +#endif + for (i = 0 ; i < NUM_DIGIT_BLOCKS ; ++i ) { + digit_block = (DIGIT_BLOCK_TYPE) x; + x = (x - digit_block) * ONE_E_NINE; + s += sprintf(s, DB_FMT, DIGITS_PER_BLOCK, digit_block); + } + + /*************************************************************************/ + + *exp_buf = 'e'; + if (mode < 'a') { + *exp_buf = 'E'; + mode += ('a' - 'A'); + } + + o_mode = mode; + + round = preci; + + if ((mode == 'g') && (round > 0)){ + --round; + } + + if (mode == 'f') { + round += exp; + } + + s = buf; + *s++ = 0; /* terminator for rounding and 0-triming */ + *s = '0'; /* space to round */ + + i = 0; + e = s + MAX_DIGITS + 1; + if (round < MAX_DIGITS) { + e = s + round + 2; + if (*e >= '5') { + i = 1; + } + } + + do { /* handle rounding and trim trailing 0s */ + *--e += i; /* add the carry */ + } while ((*e == '0') || (*e > '9')); + + o_exp = exp; + if (e <= s) { /* we carried into extra digit */ + ++o_exp; + e = s; /* needed if all 0s */ + } else { + ++s; + } + *++e = 0; /* ending nul char */ + + if ((mode == 'g') && ((o_exp >= -4) && (o_exp <= round))) { + mode = 'f'; + } + + exp = o_exp; + if (mode != 'f') { + o_exp = 0; + } + + if (o_exp < 0) { + *--s = '0'; /* fake the first digit */ + } + + pdrvr = drvr+1; + ppc = pc_fwi+2; + + *pdrvr++ = 0; + *ppc++ = 1; + *ppc++ = (INT_OR_PTR)(*s++ - '0'); + + i = e - s; /* total digits */ + if (o_exp >= 0) { + if (o_exp >= i) { /* all digit(s) left of decimal */ + *pdrvr++ = 1; + *ppc++ = i; + *ppc++ = (INT_OR_PTR)(s); + o_exp -= i; + i = 0; + if (o_exp>0) { /* have 0s left of decimal */ + *pdrvr++ = 0; + *ppc++ = o_exp; + *ppc++ = 0; + } + } else if (o_exp > 0) { /* decimal between digits */ + *pdrvr++ = 1; + *ppc++ = o_exp; + *ppc++ = (INT_OR_PTR)(s); + s += o_exp; + i -= o_exp; + } + o_exp = -1; + } + + if (PRINT_INFO_FLAG_VAL(info,alt) +/* flag[FLAG_HASH] */ + || (i) || ((o_mode != 'g') && (preci > 0))) { + *pdrvr++ = 2; /* need decimal */ + *ppc++ = 1; /* needed for width calc */ + ppc++; + } + + if (++o_exp < 0) { /* have 0s right of decimal */ + *pdrvr++ = 0; + *ppc++ = -o_exp; + *ppc++ = 0; + } + if (i) { /* have digit(s) right of decimal */ + *pdrvr++ = 1; + *ppc++ = i; + *ppc++ = (INT_OR_PTR)(s); + } + + if (o_mode != 'g') { + i -= o_exp; + if (i < preci) { /* have 0s right of digits */ + i = preci - i; + *pdrvr++ = 0; + *ppc++ = i; + *ppc++ = 0; + } + } + + /* build exponent string */ + if (mode != 'f') { + *pdrvr++ = 1; + *ppc++ = sprintf(exp_buf,"%c%+.2d", *exp_buf, exp); + *ppc++ = (INT_OR_PTR) exp_buf; + } + + EXIT_SPECIAL: + npc = pdrvr - drvr; + ppc = pc_fwi + 2; + for (i=1 ; i< npc ; i++) { + width -= *(ppc++); + ppc++; + } + i = 0; + if (*sign_str) { + i = 1; + } + width -= i; + if (width <= 0) { + width = 0; + } else { + if (PRINT_INFO_FLAG_VAL(info,left)) { /* padding on right */ +/* flag[FLAG_MINUS_LJUSTIFY] */ + ++npc; + *pdrvr++ = 7; + *ppc = width; + *++ppc = (INT_OR_PTR)(""); + width = 0; + } else if (info->pad == '0') { /* 0 padding */ +/* (flag[FLAG_0_PAD] == '0') */ + pc_fwi[2] += width; + width = 0; + } + } + *drvr = 7; + ppc = pc_fwi; + *ppc++ = width + i; + *ppc = (INT_OR_PTR) sign_str; + + pdrvr = drvr; + ppc = pc_fwi; + cnt = 0; + for (i=0 ; i<npc ; i++) { +#if 1 + fprintf(fp, fmts[(int)(*pdrvr++)], (INT_OR_PTR)(*(ppc)), + (INT_OR_PTR)(*(ppc+1))); +#else + j = fprintf(fp, fmts[(int)(*pdrvr++)], (INT_OR_PTR)(*(ppc)), + (INT_OR_PTR)(*(ppc+1))); + assert(j == *ppc); +#endif +/* if (size > *ppc) { */ +/* size -= *ppc; */ +/* } */ + cnt += *ppc; /* to avoid problems if j == -1 */ + ppc += 2; + } + + return cnt; +} +#endif +/**********************************************************************/ +#ifdef L__store_inttype +/* TODO -- right now, assumes intmax_t is either long or long long */ + +/* 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, 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 (destsize == 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 +/**********************************************************************/ diff --git a/libc/stdio/stdio.c b/libc/stdio/stdio.c new file mode 100644 index 000000000..01e0e35e9 --- /dev/null +++ b/libc/stdio/stdio.c @@ -0,0 +1,3171 @@ +/* Copyright (C) 2002 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! */ + +#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> + +/**********************************************************************/ +/* 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 + +/**********************************************************************/ + +/* TODO -- make this the default except for bcc with it's broken preproc? */ +#ifdef __UCLIBC__ +#define _stdin stdin +#define _stdout stdout +#define _stderr stderr +#endif /* __UCLIBC__ */ + +/**********************************************************************/ + +#ifndef __STDIO_THREADSAFE + +#define UNLOCKED_STREAM(RETURNTYPE,NAME,PARAMS,ARGS,STREAM) \ +strong_alias(NAME,NAME##_unlocked) \ +RETURNTYPE NAME PARAMS + +#define UNLOCKED(RETURNTYPE,NAME,PARAMS,ARGS) \ + UNLOCKED_STREAM(RETURNTYPE,NAME,PARAMS,ARGS,stream) + +#define UNLOCKED_VOID_RETURN(NAME,PARAMS,ARGS) \ +strong_alias(NAME,NAME##_unlocked) \ +void NAME PARAMS + +#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_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_THREADSAFE \ +} /* TODO: mbstate and builtin buf */ + +#ifdef __STDIO_MBSTATE_DATA +extern void _init_mbstate(mbstate_t *dest); + +#define __COMMA_CLEAN_MBSTATE , 0 +#define __COPY_MBSTATE(dest,src) memcpy(dest, src, sizeof(mbstate_t)) +#define __INIT_MBSTATE(dest) _init_mbstate(dest) +#else +#define __COMMA_CLEAN_MBSTATE +#define __COPY_MBSTATE(dest,src) +#define __INIT_MBSTATE(dest) +#endif + +#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 (register FILE *stream) +{ + int aw[1]; + +#ifdef __STDIO_WIDE + + return (fread((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, register FILE *stream) +{ + int aw[1]; + + *aw = w; /* In case 'w' is in a register... */ + +#ifdef __STDIO_WIDE + + return (fwrite((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->cookie == &(stream->filedes)) && (stream->filedes >= 0)) + ? stream->filedes + : (__set_errno(EBADF), -1) ); +#else /* __STDIO_GLIBC_CUSTOM_STREAMS */ + return (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 -- replace by intptr_t?? */ + + 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 +/**********************************************************************/ +/* 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) BUFSIZ); +#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(void *cookie, register char *buf, size_t bufsize) +{ + size_t count = COOKIE->len - COOKIE->pos; + + /* Note: bufsize < SSIZE_MAX because of _stdio_READ. */ + + 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(void *cookie, register 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... */ + 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(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(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; + __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_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(void *cookie, const char *buf, size_t bufsize) +{ + 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) { + 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; + } + + return bufsize; +} + +static int oms_seek(void *cookie, __offmax_t *pos, int whence) +{ + __offmax_t p = *pos; + 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) +{ + __oms_cookie *cookie; + 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. */ + +/* TODO: rewrite _stdio_fopen() to avoid the fopencookie() kludge. */ + +/* 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; + int fd; + + if ((stream = _stdio_fopen("/dev/null", mode, NULL, -1)) != NULL) { + fd = stream->filedes; + stream->filedes = -1; + close(fd); + 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, + cookie_io_functions_t *io_functions) +{ + FILE *stream; + + if ((stream = _stdio_fopen("/dev/null", mode, NULL, -1)) != NULL) { + int fd = stream->filedes; + stream->filedes = -1; + close(fd); + 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->bufwpos = stream->bufrpos = 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 TODO -- implement __fpending for wide streams! */ +#else /* __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->bufwpos - stream->bufstart); +#else /* __STDIO_BUFFERS */ + return 0; +#endif /* __STDIO_BUFFERS */ +} +#endif /* __STDIO_WIDE */ + +#endif +/**********************************************************************/ +#ifdef L__flushlbf + +/* No reentrancy issues. */ + +void _flushlbf(void) +{ +#ifdef __STDIO_BUFFERS + fflush((FILE *) &_stdio_openlist); /* Uses an implementation hack!!! */ +#else /* __STDIO_BUFFERS */ + /* Nothing to do. */ +#endif /* __STDIO_BUFFERS */ +} + +#endif +/**********************************************************************/ +#ifdef L___fsetlocking + +/* No (serious) reentrancy issues -- return value could be incorrect. */ +/* TODO -- fix race */ + +int __fsetlocking(FILE *stream, int locking_mode) +{ + int old_mode; + + assert((FSETLOCKING_QUERY == 0) && (FSETLOCKING_INTERNAL == 1) + && (FSETLOCKING_BYCALLER == 2)); + + assert(((unsigned int) locking_mode) <= 2); + + /* Note: don't even bother locking here... */ + + old_mode = stream->user_locking; + + 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) + ? FSETLOCKING_BYCALLER + : FSETLOCKING_INTERNAL); + } + + return old_mode; +} + +#endif +/**********************************************************************/ +#ifdef L_flockfile + +void flockfile(FILE *stream) +{ + pthread_mutex_lock(&stream->lock); +} + +#endif +/**********************************************************************/ +#ifdef L_ftrylockfile + +int ftrylockfile(FILE *stream) +{ + return pthread_mutex_trylock(&stream->lock); +} + +#endif +/**********************************************************************/ +#ifdef L_funlockfile + +void funlockfile(FILE *stream) +{ + pthread_mutex_unlock(&stream->lock); +} + +#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 +/* + * ANSI/ISO p. 370: The file positioning indicator is unspecified after + * a successful call to ungetwc. + * 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 ungot0_bytes. 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 */ + +#ifdef __STDIO_WIDE + /* Assumed narrow stream so correct if wide. */ + if (cor && (stream->modeflags & __FLAG_WIDE)) { + cor = cor - 1 + stream->ungot_width[0]; + if ((stream->ungot_width[0] == 0) /* don't know byte count or */ + || ((stream->modeflags & __MASK_UNGOT) > 1)) { /* app case */ + return -1; + } + } +#endif /* __STDIO_WIDE */ +#ifdef __STDIO_BUFFERS + if (stream->modeflags & __FLAG_WRITING) { + cor -= (stream->bufwpos - stream->bufstart); /* pending writes */ + } + if (stream->modeflags & __FLAG_READING) { + cor += (stream->bufwpos - stream->bufrpos); /* 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(FILE *stream, __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(FILE *stream, void *buf, size_t bufsize) +{ + ssize_t rv; + + if (bufsize == 0) { + 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(). */ + 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. */ + fflush((FILE *) &_stdio_openlist); /* Uses an implementation hack!!! */ + } + +#ifdef __STDIO_AUTO_RW_TRANSITION + if (stream->modeflags & __FLAG_WRITING) { + /* TODO -- return if error? Test glibc behavior with a custom r/w. */ + fflush(stream); + } +#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->bufrpos < stream->bufwpos)) { + --bytes; + *p++ = *stream->bufrpos++; + } + + if (bytes > 0) { + ssize_t len; + + /* The buffer is exhausted, but we still need chars. */ + stream->bufrpos = stream->bufwpos = stream->bufstart; + + if (bytes <= stream->bufend - stream->bufwpos) { + /* We have sufficient space in the buffer. */ + len = _stdio_READ(stream, stream->bufwpos, + stream->bufend - stream->bufwpos); + if (len > 0) { + stream->bufwpos += 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))) { + stream->bufgetc = stream->bufwpos; /* Enable getc macro. */ + } +#endif + + __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(). */ + 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(FILE *stream, const void *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(). */ + errno = EBADF; + return 0; + } + +#ifdef __STDIO_AUTO_RW_TRANSITION + /* If we were reading, deal with ungots and buffered chars. */ + if ((stream->modeflags & (__FLAG_EOF|__FLAG_WRITING|__FLAG_WRITEONLY)) + == 0) { + /* If appending, we might as well seek to end to save a seek. */ + fseek(stream, 0L, + ((stream->modeflags & __FLAG_APPEND) ? SEEK_END : SEEK_CUR)); + /* TODO: set EOF in fseek when appropriate? */ + } +#endif + + /* We need to disable putc and getc macros in case of error */ +#if defined(__STDIO_AUTO_RW_TRANSITION) \ + || defined(__STDIO_PUTC_MACRO) || defined(__STDIO_GETC_MACRO) +#ifdef __STDIO_AUTO_RW_TRANSITION + stream->bufrpos = /* TODO -- necessary? */ +#endif /* __STDIO_AUTO_RW_TRANSITION */ +#ifdef __STDIO_PUTC_MACRO + stream->bufputc = +#endif /* __STDIO_GETC_MACRO */ +#ifdef __STDIO_GETC_MACRO + stream->bufgetc = +#endif /* __STDIO_GETC_MACRO */ + stream->bufstart; +#endif /* not using ansi restrictions ; or using putc and/or 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; + + if (!buffer) { /* fflush the stream */ + FFLUSH: + { + size_t count = stream->bufwpos - stream->bufstart; + p = stream->bufstart; + + if (stream->filedes == -2) { /* TODO -- document this hack! */ + stream->modeflags |= __FLAG_WRITING; + return (!buffer) ? 0 : ((buffer - buf0) + bytes); + } + + { + size_t rv = _stdio_WRITE(stream, p, count); + p += rv; + count -= rv; + } + + stream->bufwpos = stream->bufstart; + while (count) { + *stream->bufwpos++ = *p++; + --count; + } + + if (!buffer) { /* fflush case... */ + __stdio_validate_FILE(stream); /* debugging only */ + return stream->bufwpos - stream->bufstart; + } + } + } + +#if 1 + /* TODO: If the stream is buffered, we may be able to omit. */ + if ((stream->bufwpos == 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->bufwpos; + p = buffer; + + if (count > bytes) { + count = bytes; + } + bytes -= count; + + while (count) { + *stream->bufwpos++ = *buffer++; + --count; + } + + 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))) { + /* 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(). */ + 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->bufrpos); + assert(stream->bufrpos <= stream->bufwpos); + assert(stream->bufwpos <= stream->bufend); + assert(stream->bufwpos <= 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->bufwpos); + 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) + 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? */ + +#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 = FSETLOCKING_BYCALLER; + __stdio_init_mutex(&ptr->lock); + } +#endif /* __STDIO_THREADSAFE */ + + /* TODO -- set an alarm and flush each file "by hand"? to avoid blocking? */ + + /* Now flush all streams. */ + fflush(NULL); + + /* 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->bufwpos = stream->bufrpos = 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 + /* stdin and stdout uses line buffering when connected to a tty. */ + _stdio_streams[0].modeflags ^= isatty(0) * __FLAG_LBF; + _stdio_streams[1].modeflags ^= isatty(1) * __FLAG_LBF; +#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(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 = NULL; /* To aid debugging... */ +#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) { + 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 = NULL; /* To aid debugging... */ +#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) { + 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) +#ifdef __STDIO_BUFFERS + && (stream != (FILE *) &_stdio_openlist) +#endif /* __STDIO_BUFFERS */ + ) { + __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. */ + } + } else if (stream->modeflags & (__FLAG_READONLY|__FLAG_READING)) { + /* TODO - __FLAG_READING too? check glibc behavior */ + /* According to info, glibc returns an error when the file is opened + * in read-only mode. + * ANSI/ISO says behavior in this case is undefined but also says you + * shouldn't flush a stream you were reading from. + */ + stream->modeflags |= __FLAG_ERROR; /* TODO - check glibc behavior */ + __set_errno(EBADF); + rv = -1; + } + +#ifndef NDEBUG + if ((stream != NULL) && (stream != (FILE *) &_stdio_openlist)) { + __stdio_validate_FILE(stream); /* debugging only */ + } +#endif + return rv; + +#else /* __STDIO_BUFFERS --------------------------------------- */ + + __stdio_validate_FILE(stream); /* debugging only */ + /* TODO -- check glibc behavior regarding error indicator */ + return (stream->modeflags & (__FLAG_READONLY|__FLAG_READING) + ? ((stream->modeflags |= __FLAG_ERROR), __set_errno(EBADF), EOF) + : 0 ); + +#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 || O_APPEND != __FLAG_APPEND || O_LARGEFILE != __FLAG_LARGEFILE +#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 */ + if (stream) { /* If this is freopen, free the stream. */ + goto FREE_STREAM; + } + 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_GNU_FEATURE) || defined(__STDIO_FOPEN_LARGEFILE_MODE) + while (*mode) { /* ignore everything else except ... */ +#ifdef __STDIO_FOPEN_EXCLUSIVE_MODE + if (*mode++ == 'x') { /* open exclusive -- glibc extension */ + open_mode |= O_EXCL; + } +#endif /* __STDIO_FOPEN_EXCLUSIVE_MODE */ +#ifdef __STDIO_FOPEN_LARGEFILE_MODE + if (*mode++ == 'F') { /* open large file */ + open_mode |= O_LARGEFILE; + } +#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 for append mode agreement, as well as + * largefile agreement if applicable. + */ + int i = (open_mode & (O_ACCMODE|O_APPEND|O_LARGEFILE)) + 1; + + if ((i & (((int) filename) + 1)) != i) { + __set_errno(EINVAL); + filedes = -1; + } + stream->filedes = filedes; + } else { +#ifdef __STDIO_LARGE_FILES + if (filedes < -1) { + open_mode |= __FLAG_LARGEFILE; + } +#endif /* __STDIO_LARGE_FILES */ + stream->filedes = open(filename, open_mode, 0666); + } + + if (stream->filedes < 0) { + FREE_STREAM: +#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 + stream->modeflags |= (isatty(stream->filedes) * __FLAG_LBF) + | ((((open_mode & O_ACCMODE) + 1) ^ 0x03) * __FLAG_WRITEONLY) + | (open_mode & (O_APPEND|O_LARGEFILE)); + + +#ifdef __STDIO_GETC_MACRO + stream->bufgetc = +#endif +#ifdef __STDIO_PUTC_MACRO + stream->bufputc = +#endif + stream->bufwpos = stream->bufrpos = stream->bufstart; + +#else /* __STDIO_BUFFERS */ + stream->modeflags |= + ((((open_mode & O_ACCMODE) + 1) ^ 0x03) * __FLAG_WRITEONLY) + | (open_mode & (O_APPEND|O_LARGEFILE)); +#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_THREADSAFE + __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 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; + 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, -1); + + __STDIO_THREADUNLOCK(stream); + + return fp; +} +#endif +/**********************************************************************/ +#ifdef L_freopen64 + +/* Reentrant. */ + +/* TODO -- is it collecting the common work (40 bytes) into a function? */ +FILE *freopen64(const char * __restrict filename, const char * __restrict mode, + register FILE * __restrict stream) +{ + unsigned short dynmode; + 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; + } + + 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. */ + allocated_buf_flag = __FLAG_FREEBUF; + /* If size == 0, create a (hopefully) bogus non-null pointer... */ + if (!(buf = ((size > 0) ? malloc(size) : ((char *)NULL) + 1))) { + 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->bufwpos = stream->bufrpos = 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(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; + + p = s; + while ((n > 1) && ((c = getc(stream)) != EOF) && ((*p++ = c) != '\n')) { + --n; + } + if (p == s) { + /* TODO -- should we set errno? */ +/* if (n <= 0) { */ +/* errno = EINVAL; */ +/* } */ + return NULL; + } + *p = 0; + return s; +} + +#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(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(s, n, (size_t) 1, stream) > 0) ? n : EOF; + +#else /* __STDIO_WIDE */ + + return (_stdio_fwrite(s, n, stream) == n) ? n : EOF; + +#endif /* __STDIO_WIDE */ +} + +#endif +/**********************************************************************/ +#ifdef L_getc +#undef getc + +/* 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)(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 + +/* 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(s,stream) + 1; + if ( +#if 1 + fputc('\n',stream) +#else + fputs("\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 there's been an error, or c == EOF, or ungot slots + * already filled, then return EOF */ + 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 + /* TODO: shouldn't allow writing??? */ + if (stream->modeflags & __FLAG_WRITING) { + fflush(stream); /* Commit any write-buffered chars. */ + } +#endif /* __STDIO_BUFFERS */ + + /* Clear EOF and WRITING flags, and set READING FLAG */ + stream->modeflags &= ~(__FLAG_EOF|__FLAG_WRITING); + 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; + + __STDIO_THREADLOCK(stream); + + retval = ((pos != NULL) && ((pos->__pos = ftello64(stream)) >= 0)) + ? ( +#ifdef __STDIO_MBSTATE_DATA + __COPY_MBSTATE(&(pos->__mbstate), &(stream->mbstate)), +#endif /* __STDIO_MBSTATE_DATA */ + 0) + : (__set_errno(EINVAL), -1); + + __STDIO_THREADUNLOCK(stream); + + 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(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->bufwpos = stream->bufrpos = stream->bufstart; +#endif /* __STDIO_BUFFERS */ + + stream->modeflags &= + ~(__FLAG_READING|__FLAG_WRITING|__FLAG_EOF|__MASK_UNGOT); + +#ifdef __STDIO_MBSTATE_DATA + /* TODO: don't clear state if don't move? */ + __INIT_MBSTATE(&(stream->mbstate)); +#endif /* __STDIO_MBSTATE_DATA */ + __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_MBSTATE_DATA +#error unimplemented and non-reentrant besides! + { + int retval; + if ((retval = fseeko64(stream, pos->__pos, SEEK_SET)) == 0) { + __COPY_MBSTATE(&(stream->mbstate), &(pos->__mbstate)); + } + return retval; + } +#else /* __STDIO_MBSTATE_DATA */ + return fseeko64(stream, pos->__pos, SEEK_SET); +#endif /* __STDIO_MBSTATE_DATA */ +} + +#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 + +/* TODO -- not allowed to interfere with static storage of strerror(). */ + +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 + /* TODO: use strerror_r instead? */ + fprintf(_stderr, "%s%s%s\n", s, sep, strerror(errno)); +#endif +#else + /* Note: Assumes stderr not closed or buffered. */ + __STDIO_THREADLOCK(stderr); + _stdio_fdout(STDERR_FILENO, s, sep, strerror(errno)); + __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; + 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 (UINTMAX_MAX > UINT_MAX) && ((UINTMAX_MAX/UINT_MAX) - 2 <= UINT_MAX) +#define INTERNAL_DIV_MOD +#endif + +char *_uintmaxtostr(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 + + 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))); + + *bufend = '\0'; + +#ifndef INTERNAL_DIV_MOD + do { + digit = uval % base; + uval /= base; + + *--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 { + rh = high % base; + high /= base; + digit = (low % base) + (L * rh); + low = (low / base) + (H * rh) + (digit / base); + digit %= base; + + *--bufend = ( (digit < 10) ? digit + '0' : digit + alphacase ); + } while (low | high); + +#endif /******************************************************/ + + if (negative) { + *--bufend = '-'; + } + + return bufend; +} +#undef INTERNAL_DIV_MOD + +#endif |