#define _POSIX_C_SOURCE 200809L #define _GNU_SOURCE #define _BSD_SOURCE #define _MBSTATE_T_DEFINED_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef DEFFILEMODE #define DEFFILEMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) #define _SIZE_T_DEFINED_ #define _OFF_T_DEFINED_ #define __va_list va_list /* $OpenBSD: stdio.h,v 1.55 2022/01/05 20:57:27 millert Exp $ */ /* $NetBSD: stdio.h,v 1.18 1996/04/25 18:29:21 jtc Exp $ */ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Chris Torek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)stdio.h 5.17 (Berkeley) 6/3/91 */ #ifndef _STDIO_H_ #define _STDIO_H_ #if __BSD_VISIBLE || __POSIX_VISIBLE || __XPG_VISIBLE #endif #ifndef _SIZE_T_DEFINED_ #define _SIZE_T_DEFINED_ typedef __size_t size_t; #endif #ifndef _OFF_T_DEFINED_ #define _OFF_T_DEFINED_ typedef __off_t off_t; #endif #define _FSTDIO /* Define for new stdio with functions. */ typedef off_t fpos_t; /* stdio file position type */ /* * NB: to fit things in six character monocase externals, the stdio * code uses the prefix `__s' for stdio objects, typically followed * by a three-character attempt at a mnemonic. */ /* stdio buffers */ struct __sbuf { unsigned char *_base; int _size; }; /* * stdio state variables. * * The following always hold: * * if (_flags&(__SLBF|__SWR)) == (__SLBF|__SWR), * _lbfsize is -_bf._size, else _lbfsize is 0 * if _flags&__SRD, _w is 0 * if _flags&__SWR, _r is 0 * * This ensures that the getc and putc macros (or inline functions) never * try to write or read from a file that is in `read' or `write' mode. * (Moreover, they can, and do, automatically switch from read mode to * write mode, and back, on "r+" and "w+" files.) * * _lbfsize is used only to make the inline line-buffered output stream * code as compact as possible. * * _ub, _up, and _ur are used when ungetc() pushes back more characters * than fit in the current _bf, or when ungetc() pushes back a character * that does not match the previous one in _bf. When this happens, * _ub._base becomes non-nil (i.e., a stream has ungetc() data iff * _ub._base!=NULL) and _up and _ur save the current values of _p and _r. */ typedef struct __sFILE { unsigned char *_p; /* current position in (some) buffer */ int _r; /* read space left for getc() */ int _w; /* write space left for putc() */ short _flags; /* flags, below; this FILE is free if 0 */ short _file; /* fileno, if Unix descriptor, else -1 */ struct __sbuf _bf; /* the buffer (at least 1 byte, if !NULL) */ int _lbfsize; /* 0 or -_bf._size, for inline putc */ /* operations */ void *_cookie; /* cookie passed to io functions */ int (*_close)(void *); int (*_read)(void *, char *, int); fpos_t (*_seek)(void *, fpos_t, int); int (*_write)(void *, const char *, int); /* extension data, to avoid further ABI breakage */ struct __sbuf _ext; /* data for long sequences of ungetc() */ unsigned char *_up; /* saved _p when _p is doing ungetc data */ int _ur; /* saved _r when _r is counting ungetc data */ /* tricks to meet minimum requirements even when malloc() fails */ unsigned char _ubuf[3]; /* guarantee an ungetc() buffer */ unsigned char _nbuf[1]; /* guarantee a getc() buffer */ /* separate buffer for fgetln() when line crosses buffer boundary */ struct __sbuf _lb; /* buffer for fgetln() */ /* Unix stdio files get aligned to block boundaries on fseek() */ int _blksize; /* stat.st_blksize (may be != _bf._size) */ fpos_t _offset; /* current lseek offset */ } FILE; extern FILE __sF[]; #define __SLBF 0x0001 /* line buffered */ #define __SNBF 0x0002 /* unbuffered */ #define __SRD 0x0004 /* OK to read */ #define __SWR 0x0008 /* OK to write */ /* RD and WR are never simultaneously asserted */ #define __SRW 0x0010 /* open for reading & writing */ #define __SEOF 0x0020 /* found EOF */ #define __SERR 0x0040 /* found error */ #define __SMBF 0x0080 /* _buf is from malloc */ #define __SAPP 0x0100 /* fdopen()ed in append mode */ #define __SSTR 0x0200 /* this is an sprintf/snprintf string */ #define __SOPT 0x0400 /* do fseek() optimisation */ #define __SNPT 0x0800 /* do not do fseek() optimisation */ #define __SOFF 0x1000 /* set iff _offset is in fact correct */ #define __SMOD 0x2000 /* true => fgetln modified _p text */ #define __SALC 0x4000 /* allocate string space dynamically */ #define __SIGN 0x8000 /* ignore this file in _fwalk */ /* * The following three definitions are for ANSI C, which took them * from System V, which brilliantly took internal interface macros and * made them official arguments to setvbuf(), without renaming them. * Hence, these ugly _IOxxx names are *supposed* to appear in user code. * * Although numbered as their counterparts above, the implementation * does not rely on this. */ #define _IOFBF 0 /* setvbuf should set fully buffered */ #define _IOLBF 1 /* setvbuf should set line buffered */ #define _IONBF 2 /* setvbuf should set unbuffered */ #define BUFSIZ 1024 /* size of buffer used by setbuf */ #define EOF (-1) /* * FOPEN_MAX is a minimum maximum, and should be the number of descriptors * that the kernel can provide without allocation of a resource that can * fail without the process sleeping. Do not use this for anything. */ #define FOPEN_MAX 20 /* must be <= OPEN_MAX */ #define FILENAME_MAX 1024 /* must be <= PATH_MAX */ /* System V/ANSI C; this is the wrong way to do this, do *not* use these. */ #if __BSD_VISIBLE || __XPG_VISIBLE #define P_tmpdir "/tmp/" #endif #define L_tmpnam 1024 /* XXX must be == PATH_MAX */ #define TMP_MAX 0x7fffffff /* more, but don't overflow int */ #ifndef SEEK_SET #define SEEK_SET 0 /* set file offset to offset */ #endif #ifndef SEEK_CUR #define SEEK_CUR 1 /* set file offset to current plus offset */ #endif #ifndef SEEK_END #define SEEK_END 2 /* set file offset to EOF plus offset */ #endif #define stdin (&__sF[0]) #define stdout (&__sF[1]) #define stderr (&__sF[2]) /* * Functions defined in ANSI C standard. */ void clearerr(FILE *); #if __POSIX_VISIBLE >= 200809 int dprintf(int, const char * __restrict, ...) __attribute__((__format__ (printf, 2, 3))) __attribute__((__nonnull__ (2))); #endif int fclose(FILE *); int feof(FILE *); int ferror(FILE *); int fflush(FILE *); int fgetc(FILE *); int fgetpos(FILE *, fpos_t *); char *fgets(char *, int, FILE *) __attribute__((__bounded__ (__string__,1,2))); FILE *fopen(const char *, const char *); int fprintf(FILE *, const char *, ...); int fputc(int, FILE *); int fputs(const char *, FILE *); size_t fread(void *, size_t, size_t, FILE *) __attribute__((__bounded__ (__size__,1,3,2))); FILE *freopen(const char *, const char *, FILE *); int fscanf(FILE *, const char *, ...); int fseek(FILE *, long, int); int fseeko(FILE *, off_t, int); int fsetpos(FILE *, const fpos_t *); long ftell(FILE *); off_t ftello(FILE *); size_t fwrite(const void *, size_t, size_t, FILE *) __attribute__((__bounded__ (__size__,1,3,2))); int getc(FILE *); int getchar(void); #if __POSIX_VISIBLE >= 200809 ssize_t getdelim(char ** __restrict, size_t * __restrict, int, FILE * __restrict); ssize_t getline(char ** __restrict, size_t * __restrict, FILE * __restrict); #endif #if __BSD_VISIBLE && !defined(__SYS_ERRLIST) #define __SYS_ERRLIST extern int sys_nerr; /* perror(3) external variables */ extern char *sys_errlist[]; #endif void perror(const char *); int printf(const char *, ...); int putc(int, FILE *); int putchar(int); int puts(const char *); int remove(const char *); int rename(const char *, const char *); #if __POSIX_VISIBLE >= 200809 int renameat(int, const char *, int, const char *); #endif void rewind(FILE *); int scanf(const char *, ...); void setbuf(FILE *, char *); int setvbuf(FILE *, char *, int, size_t); int sprintf(char *, const char *, ...); int sscanf(const char *, const char *, ...); FILE *tmpfile(void); char *tmpnam(char *); int ungetc(int, FILE *); int vfprintf(FILE *, const char *, __va_list); int vprintf(const char *, __va_list); int vsprintf(char *, const char *, __va_list); #if __POSIX_VISIBLE >= 200809 int vdprintf(int, const char * __restrict, __va_list) __attribute__((__format__ (printf, 2, 0))) __attribute__((__nonnull__ (2))); #endif #if __ISO_C_VISIBLE >= 1999 || __XPG_VISIBLE >= 500 || __BSD_VISIBLE int snprintf(char *, size_t, const char *, ...) __attribute__((__format__ (printf, 3, 4))) __attribute__((__nonnull__ (3))) __attribute__((__bounded__ (__string__,1,2))); int vsnprintf(char *, size_t, const char *, __va_list) __attribute__((__format__ (printf, 3, 0))) __attribute__((__nonnull__ (3))) __attribute__((__bounded__(__string__,1,2))); #endif /* __ISO_C_VISIBLE >= 1999 || __XPG_VISIBLE >= 500 || __BSD_VISIBLE */ #if __ISO_C_VISIBLE >= 1999 || __BSD_VISIBLE int vfscanf(FILE *, const char *, __va_list) __attribute__((__format__ (scanf, 2, 0))) __attribute__((__nonnull__ (2))); int vscanf(const char *, __va_list) __attribute__((__format__ (scanf, 1, 0))) __attribute__((__nonnull__ (1))); int vsscanf(const char *, const char *, __va_list) __attribute__((__format__ (scanf, 2, 0))) __attribute__((__nonnull__ (2))); #endif /* __ISO_C_VISIBLE >= 1999 || __BSD_VISIBLE */ /* * Functions defined in POSIX 1003.1. */ #if __BSD_VISIBLE || __POSIX_VISIBLE || __XPG_VISIBLE #define L_ctermid 1024 /* size for ctermid(); PATH_MAX */ char *ctermid(char *); FILE *fdopen(int, const char *); int fileno(FILE *); #if __POSIX_VISIBLE >= 199209 int pclose(FILE *); FILE *popen(const char *, const char *); #endif #if __POSIX_VISIBLE >= 199506 void flockfile(FILE *); int ftrylockfile(FILE *); void funlockfile(FILE *); /* * These are normally used through macros as defined below, but POSIX * requires functions as well. */ int getc_unlocked(FILE *); int getchar_unlocked(void); int putc_unlocked(int, FILE *); int putchar_unlocked(int); #endif /* __POSIX_VISIBLE >= 199506 */ #if __POSIX_VISIBLE >= 200809 FILE *fmemopen(void *, size_t, const char *); FILE *open_memstream(char **, size_t *); #endif /* __POSIX_VISIBLE >= 200809 */ #if __XPG_VISIBLE char *tempnam(const char *, const char *); #endif #endif /* __BSD_VISIBLE || __POSIX_VISIBLE || __XPG_VISIBLE */ /* * Routines that are purely local. */ #if __BSD_VISIBLE int asprintf(char **, const char *, ...) __attribute__((__format__ (printf, 2, 3))) __attribute__((__nonnull__ (2))); char *fgetln(FILE *, size_t *); int fpurge(FILE *); int getw(FILE *); int putw(int, FILE *); void setbuffer(FILE *, char *, int); int setlinebuf(FILE *); int vasprintf(char **, const char *, __va_list) __attribute__((__format__ (printf, 2, 0))) __attribute__((__nonnull__ (2))); /* * Stdio function-access interface. */ FILE *funopen(const void *, int (*)(void *, char *, int), int (*)(void *, const char *, int), off_t (*)(void *, off_t, int), int (*)(void *)); #define fropen(cookie, fn) funopen(cookie, fn, 0, 0, 0) #define fwopen(cookie, fn) funopen(cookie, 0, fn, 0, 0) #endif /* __BSD_VISIBLE */ /* * Functions internal to the implementation. */ int __srget(FILE *); int __swbuf(int, FILE *); /* * The __sfoo macros are here so that we can * define function versions in the C library. */ #define __sgetc(p) (--(p)->_r < 0 ? __srget(p) : (int)(*(p)->_p++)) static __inline int __sputc(int _c, FILE *_p) { if (--_p->_w >= 0 || (_p->_w >= _p->_lbfsize && (char)_c != '\n')) return (*_p->_p++ = _c); else return (__swbuf(_c, _p)); } #define __sfeof(p) (((p)->_flags & __SEOF) != 0) #define __sferror(p) (((p)->_flags & __SERR) != 0) #define __sclearerr(p) ((void)((p)->_flags &= ~(__SERR|__SEOF))) #define __sfileno(p) ((p)->_file) extern int __isthreaded; #define feof(p) (!__isthreaded ? __sfeof(p) : (feof)(p)) #define ferror(p) (!__isthreaded ? __sferror(p) : (ferror)(p)) #define clearerr(p) (!__isthreaded ? __sclearerr(p) : (clearerr)(p)) #if __POSIX_VISIBLE #define fileno(p) (!__isthreaded ? __sfileno(p) : (fileno)(p)) #endif #define getc(fp) (!__isthreaded ? __sgetc(fp) : (getc)(fp)) #if __BSD_VISIBLE /* * The macro implementations of putc and putc_unlocked are not * fully POSIX compliant; they do not set errno on failure */ #define putc(x, fp) (!__isthreaded ? __sputc(x, fp) : (putc)(x, fp)) #endif /* __BSD_VISIBLE */ #if __POSIX_VISIBLE >= 199506 #define getc_unlocked(fp) __sgetc(fp) /* * The macro implementations of putc and putc_unlocked are not * fully POSIX compliant; they do not set errno on failure */ #if __BSD_VISIBLE #define putc_unlocked(x, fp) __sputc(x, fp) #endif /* __BSD_VISIBLE */ #endif /* __POSIX_VISIBLE >= 199506 */ #define getchar() getc(stdin) #define putchar(x) putc(x, stdout) #define getchar_unlocked() getc_unlocked(stdin) #define putchar_unlocked(c) putc_unlocked(c, stdout) #endif /* _STDIO_H_ */ #undef clearerr #undef feof #undef ferror #undef fileno #undef getc_unlocked #undef putc_unlocked #undef getchar_unlocked #undef putchar_unlocked #undef __warn_references #define __warn_references(...) typedef int wint_t; typedef struct __mbstate_t { unsigned __opaque1, __opaque2; } mbstate_t; typedef unsigned long u_long; typedef unsigned char u_char; void *recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size); int _fake_arc_state = 0; #define arc4random_buf(buf, nbytes) memset( buf, _fake_arc_state++, nbytes ) int __isthreaded = 0; /* $OpenBSD: wcio.h,v 1.2 2013/04/17 17:40:35 tedu Exp $ */ /* $NetBSD: wcio.h,v 1.3 2003/01/18 11:30:00 thorpej Exp $ */ /*- * Copyright (c)2001 Citrus Project, * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Citrus$ */ #ifndef _WCIO_H_ #define _WCIO_H_ /* minimal requirement of SUSv2 */ #define WCIO_UNGETWC_BUFSIZE 1 struct wchar_io_data { mbstate_t wcio_mbstate_in; mbstate_t wcio_mbstate_out; wchar_t wcio_ungetwc_buf[WCIO_UNGETWC_BUFSIZE]; size_t wcio_ungetwc_inbuf; int wcio_mode; /* orientation */ }; #define WCIO_GET(fp) \ (_EXT(fp) ? &(_EXT(fp)->_wcio) : (struct wchar_io_data *)0) #define _SET_ORIENTATION(fp, mode) \ do {\ struct wchar_io_data *_wcio = WCIO_GET(fp); \ if (_wcio && _wcio->wcio_mode == 0) \ _wcio->wcio_mode = (mode);\ } while (0) /* * WCIO_FREE should be called by fclose */ #define WCIO_FREE(fp) \ do {\ struct wchar_io_data *_wcio = WCIO_GET(fp); \ if (_wcio) { \ _wcio->wcio_mode = 0;\ _wcio->wcio_ungetwc_inbuf = 0;\ } \ } while (0) #define WCIO_FREEUB(fp) \ do {\ struct wchar_io_data *_wcio = WCIO_GET(fp); \ if (_wcio) { \ _wcio->wcio_ungetwc_inbuf = 0;\ } \ } while (0) #define WCIO_INIT(fp) \ memset(&(_EXT(fp)->_wcio), 0, sizeof(struct wchar_io_data)) #endif /*_WCIO_H_*/ /* $OpenBSD: fileext.h,v 1.2 2005/06/17 20:40:32 espie Exp $ */ /* $NetBSD: fileext.h,v 1.5 2003/07/18 21:46:41 nathanw Exp $ */ /*- * Copyright (c)2001 Citrus Project, * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Citrus$ */ /* * file extension */ struct __sfileext { struct __sbuf _ub; /* ungetc buffer */ struct wchar_io_data _wcio; /* wide char io status */ }; #define _EXT(fp) ((struct __sfileext *)((fp)->_ext._base)) #define _UB(fp) _EXT(fp)->_ub #define _FILEEXT_INIT(fp) \ do { \ _UB(fp)._base = NULL; \ _UB(fp)._size = 0; \ WCIO_INIT(fp); \ } while (0) #define _FILEEXT_SETUP(f, fext) \ do { \ (f)->_ext._base = (unsigned char *)(fext); \ _FILEEXT_INIT(f); \ } while (0) /* $OpenBSD: floatio.h,v 1.5 2015/09/14 12:49:33 guenther Exp $ */ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Chris Torek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Floating point scanf/printf (input/output) definitions. */ /* 11-bit exponent (VAX G floating point) is 308 decimal digits */ #define MAXEXP 308 /* 128 bit fraction takes up 39 decimal digits; max reasonable precision */ #define MAXFRACT 39 /* * MAXEXPDIG is the maximum number of decimal digits needed to store a * floating point exponent in the largest supported format. It should * be ceil(log10(LDBL_MAX_10_EXP)) or, if hexadecimal floating point * conversions are supported, ceil(log10(LDBL_MAX_EXP)). But since it * is presently never greater than 5 in practice, we fudge it. */ #define MAXEXPDIG 6 #if LDBL_MAX_EXP > 999999 #error "floating point buffers too small" #endif /* $OpenBSD: glue.h,v 1.5 2015/08/27 04:37:09 guenther Exp $ */ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Chris Torek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * The first few FILEs are statically allocated; others are dynamically * allocated and linked in via this glue structure. */ struct glue { struct glue *next; int niobs; FILE *iobs; }; extern struct glue __sglue; /* $OpenBSD: local.h,v 1.25 2016/05/23 00:21:48 guenther Exp $ */ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Chris Torek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Information local to this implementation of stdio, * in particular, macros and private variables. */ void _cleanup(void); int _fwalk(int (*)(FILE *)); int __sflush(FILE *); int __sflush_locked(FILE *); FILE *__sfp(void); int __srefill(FILE *); int __sread(void *, char *, int); int __swrite(void *, const char *, int); fpos_t __sseek(void *, fpos_t, int); int __sclose(void *); void __sinit(void); void __smakebuf(FILE *); int __swhatbuf(FILE *, size_t *, int *); int __swsetup(FILE *); int __sflags(const char *, int *); wint_t __fgetwc_unlock(FILE *); wint_t __ungetwc(wint_t, FILE *); int __vfprintf(FILE *, const char *, __va_list); int __svfscanf(FILE * __restrict, const char * __restrict, __va_list); int __vfwprintf(FILE * __restrict, const wchar_t * __restrict, __va_list); int __vfwscanf(FILE * __restrict, const wchar_t * __restrict, __va_list); extern int __sdidinit; /* * Return true if the given FILE cannot be written now. */ #define cantwrite(fp) \ ((((fp)->_flags & __SWR) == 0 || (fp)->_bf._base == NULL) && \ __swsetup(fp)) /* * Test whether the given stdio file has an active ungetc buffer; * release such a buffer, without restoring ordinary unread data. */ #define HASUB(fp) (_UB(fp)._base != NULL) #define FREEUB(fp) { \ if (_UB(fp)._base != (fp)->_ubuf) \ free(_UB(fp)._base); \ _UB(fp)._base = NULL; \ } /* * test for an fgetln() buffer. */ #define HASLB(fp) ((fp)->_lb._base != NULL) #define FREELB(fp) { \ free((char *)(fp)->_lb._base); \ (fp)->_lb._base = NULL; \ } #define FLOCKFILE(fp) \ do { \ if (_thread_cb.tc_flockfile != NULL) \ _thread_cb.tc_flockfile(fp); \ } while (0) #define FUNLOCKFILE(fp) \ do { \ if (_thread_cb.tc_funlockfile != NULL) \ _thread_cb.tc_funlockfile(fp); \ } while (0) /* $OpenBSD: fvwrite.h,v 1.7 2015/08/27 04:37:09 guenther Exp $ */ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Chris Torek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * I/O descriptors for __sfvwrite(). */ struct __siov { void *iov_base; size_t iov_len; }; struct __suio { struct __siov *uio_iov; int uio_iovcnt; int uio_resid; }; extern int __sfvwrite(FILE *, struct __suio *); wint_t __fputwc_unlock(wchar_t wc, FILE *fp); #undef FLOCKFILE #undef FUNLOCKFILE #undef FLOATING_POINT struct syslog_data {}; #define SYSLOG_DATA_INIT {} #define syslog_r(...) #define LONG 0x00001 /* l: long or double */ #define SHORT 0x00004 /* h: short */ #define SHORTSHORT 0x00008 /* hh: 8 bit integer */ #define LLONG 0x00010 /* ll: long long (+ deprecated q: quad) */ #define POINTER 0x00020 /* p: void * (as hex) */ #define NOSKIP 0x00200 /* [ or c: do not skip blanks */ #define SUPPRESS 0x00400 /* *: suppress assignment */ #define UNSIGNED 0x00800 /* %[oupxX] conversions */ #define CT_CHAR 0 /* %c conversion */ #define CT_CCL 1 /* %[...] conversion */ #define CT_INT 3 /* integer, i.e., strtoimax or strtoumax */ #define CT_STRING 2 /* %s conversion */ #define CT_FLOAT 4 /* floating, i.e., strtod */ #define SIGNOK 0x01000 /* +/- is (still) legal */ #define HAVESIGN 0x02000 /* sign detected */ #define NDIGITS 0x04000 /* no digits detected */ #define DPTOK 0x08000 /* (float) decimal point is still legal */ #define EXPOK 0x10000 /* (float) exponent (e+3, etc) still legal */ #define PFXOK 0x08000 /* 0x prefix is (still) legal */ #define NZDIGITS 0x10000 /* no zero digits detected */ #define __progname "__progname" #define sendsyslog(x, y, z) #define ALIGNBYTES 7 #define ALIGN(p) (((unsigned long)(p) + ALIGNBYTES) &~ALIGNBYTES) #define MINBUF 128 //#define BUFSIZ 128 #define TEMPCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" #define NUM_CHARS (sizeof(TEMPCHARS) - 1) #define MKOTEMP_FLAGS (O_APPEND | O_CLOEXEC | O_DSYNC | O_RSYNC | O_SYNC) #define MIN_X 6 #define MKTEMP_NAME 0 #define MKTEMP_FILE 1 #define MKTEMP_DIR 2 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #define FLOCKFILE(p) #define FUNLOCKFILE(p) #define _MUTEX_LOCK(m) #define _MUTEX_UNLOCK(m) #define __atexit_register_cleanup(r) #define MUL_NO_OVERFLOW (1UL << (sizeof(size_t) * 4)) #define POS_ERR (-(fpos_t)1) #define EOF (-1) void * recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size) { size_t oldsize, newsize; void *newptr; if (ptr == NULL) return calloc(newnmemb, size); if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && newnmemb > 0 && SIZE_MAX / newnmemb < size) { errno = ENOMEM; return NULL; } newsize = newnmemb * size; if ((oldnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && oldnmemb > 0 && SIZE_MAX / oldnmemb < size) { errno = EINVAL; return NULL; } oldsize = oldnmemb * size; /* * Don't bother too much if we're shrinking just a bit, * we do not shrink for series of small steps, oh well. */ if (newsize <= oldsize) { size_t d = oldsize - newsize; if (d < oldsize / 2 && d < (size_t)getpagesize()) { memset((char *)ptr + newsize, 0, d); return ptr; } } newptr = malloc(newsize); if (newptr == NULL) return NULL; if (newsize > oldsize) { memcpy(newptr, ptr, oldsize); memset((char *)newptr + oldsize, 0, newsize - oldsize); } else memcpy(newptr, ptr, newsize); explicit_bzero(ptr, oldsize); free(ptr); return newptr; } size_t strlcpy(char *dst, const char *src, size_t dsize) { const char *osrc = src; size_t nleft = dsize; /* Copy as many bytes as will fit. */ if (nleft != 0) { while (--nleft != 0) { if ((*dst++ = *src++) == '\0') break; } } /* Not enough room in dst, add NUL and traverse rest of src. */ if (nleft == 0) { if (dsize != 0) *dst = '\0'; /* NUL-terminate dst */ while (*src++) ; } return(src - osrc - 1); /* count does not include NUL */ } size_t strlcat(char *dst, const char *src, size_t dsize) { const char *odst = dst; const char *osrc = src; size_t n = dsize; size_t dlen; /* Find the end of dst and adjust bytes left but don't go past end. */ while (n-- != 0 && *dst != '\0') dst++; dlen = dst - odst; n = dsize - dlen; if (n-- == 0) return(dlen + strlen(src)); while (*src != '\0') { if (n != 0) { *dst++ = *src; n--; } src++; } *dst = '\0'; return(dlen + (src - osrc)); /* count does not include NUL */ } size_t strnlen(const char *str, size_t maxlen) { const char *cp; for (cp = str; maxlen != 0 && *cp != '\0'; cp++, maxlen--) ; return (size_t)(cp - str); } char * strndup(const char *str, size_t maxlen) { char *copy; size_t len; len = strnlen(str, maxlen); copy = malloc(len + 1); if (copy != NULL) { (void)memcpy(copy, str, len); copy[len] = '\0'; } return copy; } char * strdup(const char *str) { size_t siz; char *copy; siz = strlen(str) + 1; if ((copy = malloc(siz)) == NULL) return(NULL); (void)memcpy(copy, str, siz); return(copy); } __attribute__((noreturn)) void verr(int eval, const char *fmt, va_list ap) { int sverrno; sverrno = errno; (void)fprintf(stderr, "%s: ", __progname); if (fmt != NULL) { (void)vfprintf(stderr, fmt, ap); (void)fprintf(stderr, ": "); } (void)fprintf(stderr, "%s\n", strerror(sverrno)); exit(eval); } __attribute__((noreturn)) void verrx(int eval, const char *fmt, va_list ap) { (void)fprintf(stderr, "%s: ", __progname); if (fmt != NULL) (void)vfprintf(stderr, fmt, ap); (void)fprintf(stderr, "\n"); exit(eval); } __attribute__((noreturn)) void verrc(int eval, int code, const char *fmt, va_list ap) { (void)fprintf(stderr, "%s: ", __progname); if (fmt != NULL) { (void)vfprintf(stderr, fmt, ap); (void)fprintf(stderr, ": "); } (void)fprintf(stderr, "%s\n", strerror(code)); exit(eval); } __attribute__((noreturn)) void err(int eval, const char *fmt, ...) { va_list ap; va_start(ap, fmt); verr(eval, fmt, ap); va_end(ap); } __attribute__((noreturn)) void errc(int eval, int code, const char *fmt, ...) { va_list ap; va_start(ap, fmt); verrc(eval, code, fmt, ap); va_end(ap); } __attribute__((noreturn)) void errx(int eval, const char *fmt, ...) { va_list ap; va_start(ap, fmt); verrx(eval, fmt, ap); va_end(ap); } void vwarn(const char *fmt, va_list ap) { int sverrno; sverrno = errno; (void)fprintf(stderr, "%s: ", __progname); if (fmt != NULL) { (void)vfprintf(stderr, fmt, ap); (void)fprintf(stderr, ": "); } (void)fprintf(stderr, "%s\n", strerror(sverrno)); } void vwarnx(const char *fmt, va_list ap) { (void)fprintf(stderr, "%s: ", __progname); if (fmt != NULL) (void)vfprintf(stderr, fmt, ap); (void)fprintf(stderr, "\n"); } void warn(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vwarn(fmt, ap); va_end(ap); } void warnx(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vwarnx(fmt, ap); va_end(ap); } void clearerr(FILE *fp) { FLOCKFILE(fp); __sclearerr(fp); FUNLOCKFILE(fp); } static int __dwrite(void *cookie, const char *buf, int n) { int *fdp = cookie; return (write(*fdp, buf, n)); } int vdprintf(int fd, const char * __restrict fmt, va_list ap) { FILE f; struct __sfileext fext; unsigned char buf[BUFSIZ]; int ret; _FILEEXT_SETUP(&f, &fext); f._p = buf; f._w = sizeof(buf); f._flags = __SWR; f._file = -1; f._bf._base = buf; f._bf._size = sizeof(buf); f._cookie = &fd; f._write = __dwrite; if ((ret = __vfprintf(&f, fmt, ap)) < 0) return ret; return __sflush(&f) ? EOF : ret; } int dprintf(int fd, const char * __restrict fmt, ...) { va_list ap; int ret; va_start(ap, fmt); ret = vdprintf(fd, fmt, ap); va_end(ap); return ret; } int fclose(FILE *fp) { int r; if (fp->_flags == 0) { /* not open! */ errno = EBADF; return (EOF); } FLOCKFILE(fp); WCIO_FREE(fp); r = fp->_flags & __SWR ? __sflush(fp) : 0; if (fp->_close != NULL && (*fp->_close)(fp->_cookie) < 0) r = EOF; if (fp->_flags & __SMBF) free((char *)fp->_bf._base); if (HASUB(fp)) FREEUB(fp); if (HASLB(fp)) FREELB(fp); fp->_r = fp->_w = 0; /* Mess up if reaccessed. */ fp->_flags = 0; /* Release this FILE for reuse. */ FUNLOCKFILE(fp); return (r); } FILE * fdopen(int fd, const char *mode) { FILE *fp; int flags, oflags, fdflags, tmp; /* _file is only a short */ if (fd > SHRT_MAX) { errno = EMFILE; return (NULL); } if ((flags = __sflags(mode, &oflags)) == 0) return (NULL); /* Make sure the mode the user wants is a subset of the actual mode. */ if ((fdflags = fcntl(fd, F_GETFL)) == -1) return (NULL); tmp = fdflags & O_ACCMODE; if (tmp != O_RDWR && (tmp != (oflags & O_ACCMODE))) { errno = EINVAL; return (NULL); } if ((fp = __sfp()) == NULL) return (NULL); fp->_flags = flags; /* * If opened for appending, but underlying descriptor does not have * O_APPEND bit set, assert __SAPP so that __swrite() will lseek to * end before each write. */ if ((oflags & O_APPEND) && !(fdflags & O_APPEND)) fp->_flags |= __SAPP; /* * If close-on-exec was requested, then turn it on if not already */ if ((oflags & O_CLOEXEC) && !((tmp = fcntl(fd, F_GETFD)) & FD_CLOEXEC)) fcntl(fd, F_SETFD, tmp | FD_CLOEXEC); fp->_file = fd; fp->_cookie = fp; fp->_read = __sread; fp->_write = __swrite; fp->_seek = __sseek; fp->_close = __sclose; return (fp); } int feof(FILE *fp) { int ret; FLOCKFILE(fp); ret = __sfeof(fp); FUNLOCKFILE(fp); return (ret); } int ferror(FILE *fp) { int ret; FLOCKFILE(fp); ret = __sferror(fp); FUNLOCKFILE(fp); return (ret); } int fflush(FILE *fp) { int r; if (fp == NULL) return (_fwalk(__sflush_locked)); FLOCKFILE(fp); if ((fp->_flags & (__SWR | __SRW)) == 0) { errno = EBADF; r = EOF; } else r = __sflush(fp); FUNLOCKFILE(fp); return (r); } int __sflush(FILE *fp) { unsigned char *p; int n, t; t = fp->_flags; if ((t & __SWR) == 0) return (0); if ((p = fp->_bf._base) == NULL) return (0); n = fp->_p - p; /* write this much */ /* * Set these immediately to avoid problems with longjmp and to allow * exchange buffering (via setvbuf) in user write function. */ fp->_p = p; fp->_w = t & (__SLBF|__SNBF) ? 0 : fp->_bf._size; for (; n > 0; n -= t, p += t) { t = (*fp->_write)(fp->_cookie, (char *)p, n); if (t <= 0) { fp->_flags |= __SERR; return (EOF); } } return (0); } int __sflush_locked(FILE *fp) { int r; FLOCKFILE(fp); r = __sflush(fp); FUNLOCKFILE(fp); return (r); } int fgetc(FILE *fp) { return (getc(fp)); } static int __slbexpand(FILE *fp, size_t newsize) { void *p; if (fp->_lb._size >= newsize) return (0); if ((p = recallocarray(fp->_lb._base, fp->_lb._size, newsize, 1)) == NULL) return (-1); fp->_lb._base = p; fp->_lb._size = newsize; return (0); } /* * Get an input line. The returned pointer often (but not always) * points into a stdio buffer. Fgetline does not alter the text of * the returned line (which is thus not a C string because it will * not necessarily end with '\0'), but does allow callers to modify * it if they wish. Thus, we set __SMOD in case the caller does. */ char * fgetln(FILE *fp, size_t *lenp) { unsigned char *p; char *ret; size_t len; size_t off; FLOCKFILE(fp); _SET_ORIENTATION(fp, -1); /* make sure there is input */ if (fp->_r <= 0 && __srefill(fp)) goto error; /* look for a newline in the input */ if ((p = memchr(fp->_p, '\n', fp->_r)) != NULL) { /* * Found one. Flag buffer as modified to keep fseek from * `optimising' a backward seek, in case the user stomps on * the text. */ p++; /* advance over it */ ret = (char *)fp->_p; *lenp = len = p - fp->_p; fp->_flags |= __SMOD; fp->_r -= len; fp->_p = p; FUNLOCKFILE(fp); return (ret); } /* * We have to copy the current buffered data to the line buffer. * As a bonus, though, we can leave off the __SMOD. * * OPTIMISTIC is length that we (optimistically) expect will * accommodate the `rest' of the string, on each trip through the * loop below. */ #define OPTIMISTIC 80 for (len = fp->_r, off = 0;; len += fp->_r) { size_t diff; /* * Make sure there is room for more bytes. Copy data from * file buffer to line buffer, refill file and look for * newline. The loop stops only when we find a newline. */ if (__slbexpand(fp, len + OPTIMISTIC)) goto error; (void)memcpy(fp->_lb._base + off, fp->_p, len - off); off = len; if (__srefill(fp)) { if (fp->_flags & __SEOF) break; goto error; } if ((p = memchr(fp->_p, '\n', fp->_r)) == NULL) continue; /* got it: finish up the line (like code above) */ p++; diff = p - fp->_p; len += diff; if (__slbexpand(fp, len)) goto error; (void)memcpy(fp->_lb._base + off, fp->_p, diff); fp->_r -= diff; fp->_p = p; break; } *lenp = len; ret = (char *)fp->_lb._base; FUNLOCKFILE(fp); return (ret); error: FUNLOCKFILE(fp); *lenp = 0; return (NULL); } int fgetpos(FILE *fp, fpos_t *pos) { return((*pos = ftello(fp)) == (fpos_t)-1); } char * fgets(char *buf, int n, FILE *fp) { size_t len; char *s; unsigned char *p, *t; if (n <= 0) { /* sanity check */ errno = EINVAL; return (NULL); } FLOCKFILE(fp); _SET_ORIENTATION(fp, -1); s = buf; n--; /* leave space for NUL */ while (n != 0) { /* * If the buffer is empty, refill it. */ if (fp->_r <= 0) { if (__srefill(fp)) { /* EOF/error: stop with partial or no line */ if (s == buf) { FUNLOCKFILE(fp); return (NULL); } break; } } len = fp->_r; p = fp->_p; /* * Scan through at most n bytes of the current buffer, * looking for '\n'. If found, copy up to and including * newline, and stop. Otherwise, copy entire chunk * and loop. */ if (len > n) len = n; t = memchr(p, '\n', len); if (t != NULL) { len = ++t - p; fp->_r -= len; fp->_p = t; (void)memcpy(s, p, len); s[len] = '\0'; FUNLOCKFILE(fp); return (buf); } fp->_r -= len; fp->_p += len; (void)memcpy(s, p, len); s += len; n -= len; } *s = '\0'; FUNLOCKFILE(fp); return (buf); } int fileno(FILE *fp) { int ret; FLOCKFILE(fp); ret = __sfileno(fp); FUNLOCKFILE(fp); return (ret); } int __sdidinit; #define NDYNAMIC 10 /* add ten more whenever necessary */ #define std(flags, file) \ {0,0,0,flags,file,{0},0,__sF+file,__sclose,__sread,__sseek,__swrite, \ {(unsigned char *)(__sFext+file), 0}} /* p r w flags file _bf z cookie close read seek write ext */ /* the usual - (stdin + stdout + stderr) */ static FILE usual[FOPEN_MAX - 3]; static struct __sfileext usualext[FOPEN_MAX - 3]; static struct glue uglue = { 0, FOPEN_MAX - 3, usual }; static struct glue *lastglue = &uglue; static void *sfp_mutex; static struct __sfileext __sFext[3]; FILE __sF[3] = { std(__SRD, STDIN_FILENO), /* stdin */ std(__SWR, STDOUT_FILENO), /* stdout */ std(__SWR|__SNBF, STDERR_FILENO) /* stderr */ }; struct glue __sglue = { &uglue, 3, __sF }; static struct glue * moreglue(int n) { struct glue *g; FILE *p; struct __sfileext *pext; static FILE empty; char *data; data = malloc(sizeof(*g) + ALIGNBYTES + n * sizeof(FILE) + n * sizeof(struct __sfileext)); if (data == NULL) return (NULL); g = (struct glue *)data; p = (FILE *)ALIGN(data + sizeof(*g)); pext = (struct __sfileext *) (ALIGN(data + sizeof(*g)) + n * sizeof(FILE)); g->next = NULL; g->niobs = n; g->iobs = p; while (--n >= 0) { *p = empty; _FILEEXT_SETUP(p, pext); p++; pext++; } return (g); } /* * Find a free FILE for fopen et al. */ FILE * __sfp(void) { FILE *fp; int n; struct glue *g; if (!__sdidinit) __sinit(); _MUTEX_LOCK(&sfp_mutex); for (g = &__sglue; g != NULL; g = g->next) { for (fp = g->iobs, n = g->niobs; --n >= 0; fp++) if (fp->_flags == 0) goto found; } /* release lock while mallocing */ _MUTEX_UNLOCK(&sfp_mutex); if ((g = moreglue(NDYNAMIC)) == NULL) return (NULL); _MUTEX_LOCK(&sfp_mutex); lastglue->next = g; lastglue = g; fp = g->iobs; found: fp->_flags = 1; /* reserve this slot; caller sets real flags */ _MUTEX_UNLOCK(&sfp_mutex); fp->_p = NULL; /* no current pointer */ fp->_w = 0; /* nothing to read or write */ fp->_r = 0; fp->_bf._base = NULL; /* no buffer */ fp->_bf._size = 0; fp->_lbfsize = 0; /* not line buffered */ fp->_file = -1; /* no file */ /* fp->_cookie = ; */ /* caller sets cookie, _read/_write etc */ fp->_lb._base = NULL; /* no line buffer */ fp->_lb._size = 0; _FILEEXT_INIT(fp); return (fp); } /* * exit() calls _cleanup() through the callback registered * with __atexit_register_cleanup(), set whenever we open or buffer a * file. This chicanery is done so that programs that do not use stdio * need not link it all in. * * The name `_cleanup' is, alas, fairly well known outside stdio. */ void _cleanup(void) { /* (void) _fwalk(fclose); */ (void) _fwalk(__sflush); /* `cheating' */ } /* * __sinit() is called whenever stdio's internal variables must be set up. */ void __sinit(void) { static void *sinit_mutex; int i; _MUTEX_LOCK(&sinit_mutex); if (__sdidinit) goto out; /* bail out if caller lost the race */ for (i = 0; i < FOPEN_MAX - 3; i++) { _FILEEXT_SETUP(usual+i, usualext+i); } /* make sure we clean up on exit */ __atexit_register_cleanup(_cleanup); /* conservative */ __sdidinit = 1; out: _MUTEX_UNLOCK(&sinit_mutex); } int __sflags(const char *mode, int *optr) { int ret, m, o; switch (*mode++) { case 'r': /* open for reading */ ret = __SRD; m = O_RDONLY; o = 0; break; case 'w': /* open for writing */ ret = __SWR; m = O_WRONLY; o = O_CREAT | O_TRUNC; break; case 'a': /* open for appending */ ret = __SWR; m = O_WRONLY; o = O_CREAT | O_APPEND; break; default: /* illegal mode */ errno = EINVAL; return (0); } while (*mode != '\0') switch (*mode++) { case 'b': break; case '+': ret = __SRW; m = O_RDWR; break; case 'e': o |= O_CLOEXEC; break; case 'x': if (o & O_CREAT) o |= O_EXCL; break; default: /* * Lots of software passes other extension mode * letters, like Window's 't' */ #if 0 errno = EINVAL; return (0); #else break; #endif } *optr = m | o; return (ret); } FILE * fopen(const char *file, const char *mode) { FILE *fp; int f; int flags, oflags; if ((flags = __sflags(mode, &oflags)) == 0) return (NULL); if ((fp = __sfp()) == NULL) return (NULL); if ((f = open(file, oflags, DEFFILEMODE)) == -1) { fp->_flags = 0; /* release */ return (NULL); } /* _file is only a short */ if (f > SHRT_MAX) { fp->_flags = 0; /* release */ close(f); errno = EMFILE; return (NULL); } fp->_file = f; fp->_flags = flags; fp->_cookie = fp; fp->_read = __sread; fp->_write = __swrite; fp->_seek = __sseek; fp->_close = __sclose; /* * When opening in append mode, even though we use O_APPEND, * we need to seek to the end so that ftell() gets the right * answer. If the user then alters the seek pointer, or * the file extends, this will fail, but there is not much * we can do about this. (We could set __SAPP and check in * fseek and ftell.) */ if (oflags & O_APPEND) (void) __sseek(fp, 0, SEEK_END); return (fp); } union arg { int intarg; unsigned int uintarg; long longarg; unsigned long ulongarg; long long longlongarg; unsigned long long ulonglongarg; ptrdiff_t ptrdiffarg; size_t sizearg; ssize_t ssizearg; intmax_t intmaxarg; uintmax_t uintmaxarg; void *pvoidarg; char *pchararg; signed char *pschararg; short *pshortarg; int *pintarg; long *plongarg; long long *plonglongarg; ptrdiff_t *pptrdiffarg; ssize_t *pssizearg; intmax_t *pintmaxarg; #ifdef FLOATING_POINT double doublearg; long double longdoublearg; #endif #ifdef PRINTF_WIDE_CHAR wint_t wintarg; wchar_t *pwchararg; #endif }; static int __find_arguments(const char *fmt0, va_list ap, union arg **argtable, size_t *argtablesiz); static int __grow_type_table(unsigned char **typetable, int *tablesize); /* * Flush out all the vectors defined by the given uio, * then reset it so that it can be reused. */ static int __sprint(FILE *fp, struct __suio *uio) { int err; if (uio->uio_resid == 0) { uio->uio_iovcnt = 0; return (0); } err = __sfvwrite(fp, uio); uio->uio_resid = 0; uio->uio_iovcnt = 0; return (err); } /* * Helper function for `fprintf to unbuffered unix file': creates a * temporary buffer. We only work on write-only files; this avoids * worries about ungetc buffers and so forth. */ static int __sbprintf(FILE *fp, const char *fmt, va_list ap) { int ret; FILE fake; struct __sfileext fakeext; unsigned char buf[BUFSIZ]; _FILEEXT_SETUP(&fake, &fakeext); /* copy the important variables */ fake._flags = fp->_flags & ~__SNBF; fake._file = fp->_file; fake._cookie = fp->_cookie; fake._write = fp->_write; /* set up the buffer */ fake._bf._base = fake._p = buf; fake._bf._size = fake._w = sizeof(buf); fake._lbfsize = 0; /* not actually used, but Just In Case */ /* do the work, then copy any error status */ ret = __vfprintf(&fake, fmt, ap); if (ret >= 0 && __sflush(&fake)) ret = EOF; if (fake._flags & __SERR) fp->_flags |= __SERR; return (ret); } #ifdef PRINTF_WIDE_CHAR /* * Convert a wide character string argument for the %ls format to a multibyte * string representation. If not -1, prec specifies the maximum number of * bytes to output, and also means that we can't assume that the wide char * string is null-terminated. */ static char * __wcsconv(wchar_t *wcsarg, int prec) { mbstate_t mbs; char buf[MB_LEN_MAX]; wchar_t *p; char *convbuf; size_t clen, nbytes; /* Allocate space for the maximum number of bytes we could output. */ if (prec < 0) { memset(&mbs, 0, sizeof(mbs)); p = wcsarg; nbytes = wcsrtombs(NULL, (const wchar_t **)&p, 0, &mbs); if (nbytes == (size_t)-1) return (NULL); } else { /* * Optimisation: if the output precision is small enough, * just allocate enough memory for the maximum instead of * scanning the string. */ if (prec < 128) nbytes = prec; else { nbytes = 0; p = wcsarg; memset(&mbs, 0, sizeof(mbs)); for (;;) { clen = wcrtomb(buf, *p++, &mbs); if (clen == 0 || clen == (size_t)-1 || nbytes + clen > (size_t)prec) break; nbytes += clen; } if (clen == (size_t)-1) return (NULL); } } if ((convbuf = malloc(nbytes + 1)) == NULL) return (NULL); /* Fill the output buffer. */ p = wcsarg; memset(&mbs, 0, sizeof(mbs)); if ((nbytes = wcsrtombs(convbuf, (const wchar_t **)&p, nbytes, &mbs)) == (size_t)-1) { free(convbuf); return (NULL); } convbuf[nbytes] = '\0'; return (convbuf); } #endif #ifdef FLOATING_POINT #include #include #include #include "floatio.h" #include "gdtoa.h" #define DEFPREC 6 static int exponent(char *, int, int); #endif /* FLOATING_POINT */ /* * The size of the buffer we use as scratch space for integer * conversions, among other things. Technically, we would need the * most space for base 10 conversions with thousands' grouping * characters between each pair of digits. 100 bytes is a * conservative overestimate even for a 128-bit uintmax_t. */ #define BUF 100 #define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */ /* * Macros for converting digits to letters and vice versa */ #define to_digit(c) ((c) - '0') #define is_digit(c) ((unsigned)to_digit(c) <= 9) #define to_char(n) ((n) + '0') /* * Flags used during conversion. */ #define ALT 0x0001 /* alternate form */ #define LADJUST 0x0004 /* left adjustment */ #define LONGDBL 0x0008 /* long double */ #define LONGINT 0x0010 /* long integer */ #define LLONGINT 0x0020 /* long long integer */ #define SHORTINT 0x0040 /* short integer */ #define ZEROPAD 0x0080 /* zero (as opposed to blank) pad */ #define FPT 0x0100 /* Floating point number */ #define PTRINT 0x0200 /* (unsigned) ptrdiff_t */ #define SIZEINT 0x0400 /* (signed) size_t */ #define CHARINT 0x0800 /* 8 bit integer */ #define MAXINT 0x1000 /* largest integer size (intmax_t) */ int vfprintf(FILE *fp, const char *fmt0, __va_list ap) { int ret; FLOCKFILE(fp); ret = __vfprintf(fp, fmt0, ap); FUNLOCKFILE(fp); return (ret); } int __vfprintf(FILE *fp, const char *fmt0, __va_list ap) { char *fmt; /* format string */ int ch; /* character from fmt */ int n, n2; /* handy integers (short term usage) */ char *cp; /* handy char pointer (short term usage) */ struct __siov *iovp; /* for PRINT macro */ int flags; /* flags as above */ int ret; /* return value accumulator */ int width; /* width from format (%8d), or 0 */ int prec; /* precision from format; <0 for N/A */ char sign; /* sign prefix (' ', '+', '-', or \0) */ #ifdef FLOATING_POINT /* * We can decompose the printed representation of floating * point numbers into several parts, some of which may be empty: * * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ * A B ---C--- D E F * * A: 'sign' holds this value if present; '\0' otherwise * B: ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal * C: cp points to the string MMMNNN. Leading and trailing * zeros are not in the string and must be added. * D: expchar holds this character; '\0' if no exponent, e.g. %f * F: at least two digits for decimal, at least one digit for hex */ char *decimal_point = NULL; int signflag; /* true if float is negative */ union { /* floating point arguments %[aAeEfFgG] */ double dbl; long double ldbl; } fparg; int expt; /* integer value of exponent */ char expchar; /* exponent character: [eEpP\0] */ char *dtoaend; /* pointer to end of converted digits */ int expsize; /* character count for expstr */ int lead; /* sig figs before decimal or group sep */ int ndig; /* actual number of digits returned by dtoa */ char expstr[MAXEXPDIG+2]; /* buffer for exponent string: e+ZZZ */ char *dtoaresult = NULL; #endif uintmax_t _umax; /* integer arguments %[diouxX] */ enum { OCT, DEC, HEX } base; /* base for %[diouxX] conversion */ int dprec; /* a copy of prec if %[diouxX], 0 otherwise */ int realsz; /* field size expanded by dprec */ int size; /* size of converted field or string */ const char *xdigs; /* digits for %[xX] conversion */ #define NIOV 8 struct __suio uio; /* output information: summary */ struct __siov iov[NIOV];/* ... and individual io vectors */ char buf[BUF]; /* buffer with space for digits of uintmax_t */ char ox[2]; /* space for 0x; ox[1] is either x, X, or \0 */ union arg *argtable; /* args, built due to positional arg */ union arg statargtable[STATIC_ARG_TBL_SIZE]; size_t argtablesiz; int nextarg; /* 1-based argument index */ va_list orgap; /* original argument pointer */ #ifdef PRINTF_WIDE_CHAR char *convbuf; /* buffer for wide to multi-byte conversion */ #endif /* * Choose PADSIZE to trade efficiency vs. size. If larger printf * fields occur frequently, increase PADSIZE and make the initialisers * below longer. */ #define PADSIZE 16 /* pad chunk size */ static char blanks[PADSIZE] = {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; static char zeroes[PADSIZE] = {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; static const char xdigs_lower[16] = "0123456789abcdef"; static const char xdigs_upper[16] = "0123456789ABCDEF"; /* * BEWARE, these `goto error' on error, and PAD uses `n'. */ #define PRINT(ptr, len) do { \ iovp->iov_base = (ptr); \ iovp->iov_len = (len); \ uio.uio_resid += (len); \ iovp++; \ if (++uio.uio_iovcnt >= NIOV) { \ if (__sprint(fp, &uio)) \ goto error; \ iovp = iov; \ } \ } while (0) #define PAD(howmany, with) do { \ if ((n = (howmany)) > 0) { \ while (n > PADSIZE) { \ PRINT(with, PADSIZE); \ n -= PADSIZE; \ } \ PRINT(with, n); \ } \ } while (0) #define PRINTANDPAD(p, ep, len, with) do { \ n2 = (ep) - (p); \ if (n2 > (len)) \ n2 = (len); \ if (n2 > 0) \ PRINT((p), n2); \ PAD((len) - (n2 > 0 ? n2 : 0), (with)); \ } while(0) #define FLUSH() do { \ if (uio.uio_resid && __sprint(fp, &uio)) \ goto error; \ uio.uio_iovcnt = 0; \ iovp = iov; \ } while (0) /* * To extend shorts properly, we need both signed and unsigned * argument extraction methods. */ #define SARG() \ ((intmax_t)(flags&MAXINT ? GETARG(intmax_t) : \ flags&LLONGINT ? GETARG(long long) : \ flags&LONGINT ? GETARG(long) : \ flags&PTRINT ? GETARG(ptrdiff_t) : \ flags&SIZEINT ? GETARG(ssize_t) : \ flags&SHORTINT ? (short)GETARG(int) : \ flags&CHARINT ? (signed char)GETARG(int) : \ GETARG(int))) #define UARG() \ ((uintmax_t)(flags&MAXINT ? GETARG(uintmax_t) : \ flags&LLONGINT ? GETARG(unsigned long long) : \ flags&LONGINT ? GETARG(unsigned long) : \ flags&PTRINT ? (uintptr_t)GETARG(ptrdiff_t) : /* XXX */ \ flags&SIZEINT ? GETARG(size_t) : \ flags&SHORTINT ? (unsigned short)GETARG(int) : \ flags&CHARINT ? (unsigned char)GETARG(int) : \ GETARG(unsigned int))) /* * Append a digit to a value and check for overflow. */ #define APPEND_DIGIT(val, dig) do { \ if ((val) > INT_MAX / 10) \ goto overflow; \ (val) *= 10; \ if ((val) > INT_MAX - to_digit((dig))) \ goto overflow; \ (val) += to_digit((dig)); \ } while (0) /* * Get * arguments, including the form *nn$. Preserve the nextarg * that the argument can be gotten once the type is determined. */ #define GETASTER(val) \ n2 = 0; \ cp = fmt; \ while (is_digit(*cp)) { \ APPEND_DIGIT(n2, *cp); \ cp++; \ } \ if (*cp == '$') { \ int hold = nextarg; \ if (argtable == NULL) { \ argtable = statargtable; \ if (__find_arguments(fmt0, orgap, &argtable, \ &argtablesiz) == -1) { \ ret = -1; \ goto error; \ } \ } \ nextarg = n2; \ val = GETARG(int); \ nextarg = hold; \ fmt = ++cp; \ } else { \ val = GETARG(int); \ } /* * Get the argument indexed by nextarg. If the argument table is * built, use it to get the argument. If its not, get the next * argument (and arguments must be gotten sequentially). */ #define GETARG(type) \ ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \ (nextarg++, va_arg(ap, type))) _SET_ORIENTATION(fp, -1); /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */ if (cantwrite(fp)) { errno = EBADF; return (EOF); } /* optimise fprintf(stderr) (and other unbuffered Unix files) */ if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && fp->_file >= 0) return (__sbprintf(fp, fmt0, ap)); fmt = (char *)fmt0; argtable = NULL; nextarg = 1; va_copy(orgap, ap); uio.uio_iov = iovp = iov; uio.uio_resid = 0; uio.uio_iovcnt = 0; ret = 0; #ifdef PRINTF_WIDE_CHAR convbuf = NULL; #endif /* * Scan the format for conversions (`%' character). */ for (;;) { for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) continue; if (fmt != cp) { ptrdiff_t m = fmt - cp; if (m < 0 || m > INT_MAX - ret) goto overflow; PRINT(cp, m); ret += m; } if (ch == '\0') goto done; fmt++; /* skip over '%' */ flags = 0; dprec = 0; width = 0; prec = -1; sign = '\0'; ox[1] = '\0'; rflag: ch = *fmt++; reswitch: switch (ch) { case ' ': /* * ``If the space and + flags both appear, the space * flag will be ignored.'' * -- ANSI X3J11 */ if (!sign) sign = ' '; goto rflag; case '#': flags |= ALT; goto rflag; case '\'': /* grouping not implemented */ goto rflag; case '*': /* * ``A negative field width argument is taken as a * - flag followed by a positive field width.'' * -- ANSI X3J11 * They don't exclude field widths read from args. */ GETASTER(width); if (width >= 0) goto rflag; if (width == INT_MIN) goto overflow; width = -width; /* FALLTHROUGH */ case '-': flags |= LADJUST; goto rflag; case '+': sign = '+'; goto rflag; case '.': if ((ch = *fmt++) == '*') { GETASTER(n); prec = n < 0 ? -1 : n; goto rflag; } n = 0; while (is_digit(ch)) { APPEND_DIGIT(n, ch); ch = *fmt++; } if (ch == '$') { nextarg = n; if (argtable == NULL) { argtable = statargtable; if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) { ret = -1; goto error; } } goto rflag; } prec = n; goto reswitch; case '0': /* * ``Note that 0 is taken as a flag, not as the * beginning of a field width.'' * -- ANSI X3J11 */ flags |= ZEROPAD; goto rflag; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = 0; do { APPEND_DIGIT(n, ch); ch = *fmt++; } while (is_digit(ch)); if (ch == '$') { nextarg = n; if (argtable == NULL) { argtable = statargtable; if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) { ret = -1; goto error; } } goto rflag; } width = n; goto reswitch; #ifdef FLOATING_POINT case 'L': flags |= LONGDBL; goto rflag; #endif case 'h': if (*fmt == 'h') { fmt++; flags |= CHARINT; } else { flags |= SHORTINT; } goto rflag; case 'j': flags |= MAXINT; goto rflag; case 'l': if (*fmt == 'l') { fmt++; flags |= LLONGINT; } else { flags |= LONGINT; } goto rflag; case 'q': flags |= LLONGINT; goto rflag; case 't': flags |= PTRINT; goto rflag; case 'z': flags |= SIZEINT; goto rflag; case 'c': #ifdef PRINTF_WIDE_CHAR if (flags & LONGINT) { mbstate_t mbs; size_t mbseqlen; memset(&mbs, 0, sizeof(mbs)); mbseqlen = wcrtomb(buf, (wchar_t)GETARG(wint_t), &mbs); if (mbseqlen == (size_t)-1) { ret = -1; goto error; } cp = buf; size = (int)mbseqlen; } else { #endif *(cp = buf) = GETARG(int); size = 1; #ifdef PRINTF_WIDE_CHAR } #endif sign = '\0'; break; case 'D': flags |= LONGINT; /*FALLTHROUGH*/ case 'd': case 'i': _umax = SARG(); if ((intmax_t)_umax < 0) { _umax = -_umax; sign = '-'; } base = DEC; goto number; #ifdef FLOATING_POINT case 'a': case 'A': if (ch == 'a') { ox[1] = 'x'; xdigs = xdigs_lower; expchar = 'p'; } else { ox[1] = 'X'; xdigs = xdigs_upper; expchar = 'P'; } if (prec >= 0) prec++; if (dtoaresult) __freedtoa(dtoaresult); if (flags & LONGDBL) { fparg.ldbl = GETARG(long double); dtoaresult = cp = __hldtoa(fparg.ldbl, xdigs, prec, &expt, &signflag, &dtoaend); if (dtoaresult == NULL) { errno = ENOMEM; goto error; } } else { fparg.dbl = GETARG(double); dtoaresult = cp = __hdtoa(fparg.dbl, xdigs, prec, &expt, &signflag, &dtoaend); if (dtoaresult == NULL) { errno = ENOMEM; goto error; } } if (prec < 0) prec = dtoaend - cp; if (expt == INT_MAX) ox[1] = '\0'; goto fp_common; case 'e': case 'E': expchar = ch; if (prec < 0) /* account for digit before decpt */ prec = DEFPREC + 1; else prec++; goto fp_begin; case 'f': case 'F': expchar = '\0'; goto fp_begin; case 'g': case 'G': expchar = ch - ('g' - 'e'); if (prec == 0) prec = 1; fp_begin: if (prec < 0) prec = DEFPREC; if (dtoaresult) __freedtoa(dtoaresult); if (flags & LONGDBL) { fparg.ldbl = GETARG(long double); dtoaresult = cp = __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend); if (dtoaresult == NULL) { errno = ENOMEM; goto error; } } else { fparg.dbl = GETARG(double); dtoaresult = cp = __dtoa(fparg.dbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend); if (dtoaresult == NULL) { errno = ENOMEM; goto error; } if (expt == 9999) expt = INT_MAX; } fp_common: if (signflag) sign = '-'; if (expt == INT_MAX) { /* inf or nan */ if (*cp == 'N') cp = (ch >= 'a') ? "nan" : "NAN"; else cp = (ch >= 'a') ? "inf" : "INF"; size = 3; flags &= ~ZEROPAD; break; } flags |= FPT; ndig = dtoaend - cp; if (ch == 'g' || ch == 'G') { if (expt > -4 && expt <= prec) { /* Make %[gG] smell like %[fF] */ expchar = '\0'; if (flags & ALT) prec -= expt; else prec = ndig - expt; if (prec < 0) prec = 0; } else { /* * Make %[gG] smell like %[eE], but * trim trailing zeroes if no # flag. */ if (!(flags & ALT)) prec = ndig; } } if (expchar) { expsize = exponent(expstr, expt - 1, expchar); size = expsize + prec; if (prec > 1 || flags & ALT) ++size; } else { /* space for digits before decimal point */ if (expt > 0) size = expt; else /* "0" */ size = 1; /* space for decimal pt and following digits */ if (prec || flags & ALT) size += prec + 1; lead = expt; } break; #endif /* FLOATING_POINT */ case 'n': { static const char n_msg[] = ": *printf used %n, aborting: "; char buf[1024], *p; /* <10> is LOG_CRIT */ strlcpy(buf, "<10>", sizeof buf); /* Make sure progname does not fill the whole buffer */ strlcat(buf, __progname, sizeof(buf) - sizeof n_msg); strlcat(buf, n_msg, sizeof buf); strlcat(buf, fmt0, sizeof buf); if ((p = strchr(buf, '\n'))) *p = '\0'; sendsyslog(buf, strlen(buf), LOG_CONS); abort(); break; } case 'O': flags |= LONGINT; /*FALLTHROUGH*/ case 'o': _umax = UARG(); base = OCT; goto nosign; case 'p': /* * ``The argument shall be a pointer to void. The * value of the pointer is converted to a sequence * of printable characters, in an implementation- * defined manner.'' * -- ANSI X3J11 */ _umax = (u_long)GETARG(void *); base = HEX; xdigs = xdigs_lower; ox[1] = 'x'; goto nosign; case 's': { size_t len; #ifdef PRINTF_WIDE_CHAR if (flags & LONGINT) { wchar_t *wcp; free(convbuf); convbuf = NULL; if ((wcp = GETARG(wchar_t *)) == NULL) { struct syslog_data sdata = SYSLOG_DATA_INIT; int save_errno = errno; syslog_r(LOG_CRIT | LOG_CONS, &sdata, "vfprintf %%ls NULL in \"%s\"", fmt0); errno = save_errno; cp = "(null)"; } else { convbuf = __wcsconv(wcp, prec); if (convbuf == NULL) { ret = -1; goto error; } cp = convbuf; } } else #endif /* PRINTF_WIDE_CHAR */ if ((cp = GETARG(char *)) == NULL) { struct syslog_data sdata = SYSLOG_DATA_INIT; int save_errno = errno; syslog_r(LOG_CRIT | LOG_CONS, &sdata, "vfprintf %%s NULL in \"%s\"", fmt0); errno = save_errno; cp = "(null)"; } len = prec >= 0 ? strnlen(cp, prec) : strlen(cp); if (len > INT_MAX) goto overflow; size = (int)len; sign = '\0'; } break; case 'U': flags |= LONGINT; /*FALLTHROUGH*/ case 'u': _umax = UARG(); base = DEC; goto nosign; case 'X': xdigs = xdigs_upper; goto hex; case 'x': xdigs = xdigs_lower; hex: _umax = UARG(); base = HEX; /* leading 0x/X only if non-zero */ if (flags & ALT && _umax != 0) ox[1] = ch; /* unsigned conversions */ nosign: sign = '\0'; /* * ``... diouXx conversions ... if a precision is * specified, the 0 flag will be ignored.'' * -- ANSI X3J11 */ number: if ((dprec = prec) >= 0) flags &= ~ZEROPAD; /* * ``The result of converting a zero value with an * explicit precision of zero is no characters.'' * -- ANSI X3J11 */ cp = buf + BUF; if (_umax != 0 || prec != 0) { /* * Unsigned mod is hard, and unsigned mod * by a constant is easier than that by * a variable; hence this switch. */ switch (base) { case OCT: do { *--cp = to_char(_umax & 7); _umax >>= 3; } while (_umax); /* handle octal leading 0 */ if (flags & ALT && *cp != '0') *--cp = '0'; break; case DEC: /* many numbers are 1 digit */ while (_umax >= 10) { *--cp = to_char(_umax % 10); _umax /= 10; } *--cp = to_char(_umax); break; case HEX: do { *--cp = xdigs[_umax & 15]; _umax >>= 4; } while (_umax); break; default: cp = "bug in vfprintf: bad base"; size = strlen(cp); goto skipsize; } } size = buf + BUF - cp; if (size > BUF) /* should never happen */ abort(); skipsize: break; default: /* "%?" prints ?, unless ? is NUL */ if (ch == '\0') goto done; /* pretend it was %c with argument ch */ cp = buf; *cp = ch; size = 1; sign = '\0'; break; } /* * All reasonable formats wind up here. At this point, `cp' * points to a string which (if not flags&LADJUST) should be * padded out to `width' places. If flags&ZEROPAD, it should * first be prefixed by any sign or other prefix; otherwise, * it should be blank padded before the prefix is emitted. * After any left-hand padding and prefixing, emit zeroes * required by a decimal %[diouxX] precision, then print the * string proper, then emit zeroes required by any leftover * floating precision; finally, if LADJUST, pad with blanks. * * Compute actual size, so we know how much to pad. * size excludes decimal prec; realsz includes it. */ realsz = dprec > size ? dprec : size; if (sign) realsz++; if (ox[1]) realsz+= 2; /* right-adjusting blank padding */ if ((flags & (LADJUST|ZEROPAD)) == 0) PAD(width - realsz, blanks); /* prefix */ if (sign) PRINT(&sign, 1); if (ox[1]) { /* ox[1] is either x, X, or \0 */ ox[0] = '0'; PRINT(ox, 2); } /* right-adjusting zero padding */ if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) PAD(width - realsz, zeroes); /* leading zeroes from decimal precision */ PAD(dprec - size, zeroes); /* the string or number proper */ #ifdef FLOATING_POINT if ((flags & FPT) == 0) { PRINT(cp, size); } else { /* glue together f_p fragments */ if (decimal_point == NULL) decimal_point = nl_langinfo(RADIXCHAR); if (!expchar) { /* %[fF] or sufficiently short %[gG] */ if (expt <= 0) { PRINT(zeroes, 1); if (prec || flags & ALT) PRINT(decimal_point, 1); PAD(-expt, zeroes); /* already handled initial 0's */ prec += expt; } else { PRINTANDPAD(cp, dtoaend, lead, zeroes); cp += lead; if (prec || flags & ALT) PRINT(decimal_point, 1); } PRINTANDPAD(cp, dtoaend, prec, zeroes); } else { /* %[eE] or sufficiently long %[gG] */ if (prec > 1 || flags & ALT) { buf[0] = *cp++; buf[1] = *decimal_point; PRINT(buf, 2); PRINT(cp, ndig-1); PAD(prec - ndig, zeroes); } else { /* XeYYY */ PRINT(cp, 1); } PRINT(expstr, expsize); } } #else PRINT(cp, size); #endif /* left-adjusting padding (always blank) */ if (flags & LADJUST) PAD(width - realsz, blanks); /* finally, adjust ret */ if (width < realsz) width = realsz; if (width > INT_MAX - ret) goto overflow; ret += width; FLUSH(); /* copy out the I/O vectors */ } done: FLUSH(); error: va_end(orgap); if (__sferror(fp)) ret = -1; goto finish; overflow: errno = EOVERFLOW; ret = -1; finish: #ifdef PRINTF_WIDE_CHAR free(convbuf); #endif #ifdef FLOATING_POINT if (dtoaresult) __freedtoa(dtoaresult); #endif if (argtable != NULL && argtable != statargtable) { munmap(argtable, argtablesiz); argtable = NULL; } return (ret); } /* * Type ids for argument type table. */ #define T_UNUSED 0 #define T_SHORT 1 #define T_U_SHORT 2 #define TP_SHORT 3 #define T_INT 4 #define T_U_INT 5 #define TP_INT 6 #define T_LONG 7 #define T_U_LONG 8 #define TP_LONG 9 #define T_LLONG 10 #define T_U_LLONG 11 #define TP_LLONG 12 #define T_DOUBLE 13 #define T_LONG_DOUBLE 14 #define TP_CHAR 15 #define TP_VOID 16 #define T_PTRINT 17 #define TP_PTRINT 18 #define T_SIZEINT 19 #define T_SSIZEINT 20 #define TP_SSIZEINT 21 #define T_MAXINT 22 #define T_MAXUINT 23 #define TP_MAXINT 24 #define T_CHAR 25 #define T_U_CHAR 26 #define T_WINT 27 #define TP_WCHAR 28 /* * Find all arguments when a positional parameter is encountered. Returns a * table, indexed by argument number, of pointers to each arguments. The * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries. * It will be replaced with a mmap-ed one if it overflows (malloc cannot be * used since we are attempting to make snprintf thread safe, and alloca is * problematic since we have nested functions..) */ static int __find_arguments(const char *fmt0, va_list ap, union arg **argtable, size_t *argtablesiz) { char *fmt; /* format string */ int ch; /* character from fmt */ int n, n2; /* handy integer (short term usage) */ char *cp; /* handy char pointer (short term usage) */ int flags; /* flags as above */ unsigned char *typetable; /* table of types */ unsigned char stattypetable[STATIC_ARG_TBL_SIZE]; int tablesize; /* current size of type table */ int tablemax; /* largest used index in table */ int nextarg; /* 1-based argument index */ int ret = 0; /* return value */ /* * Add an argument type to the table, expanding if necessary. */ #define ADDTYPE(type) \ ((nextarg >= tablesize) ? \ __grow_type_table(&typetable, &tablesize) : 0, \ (nextarg > tablemax) ? tablemax = nextarg : 0, \ typetable[nextarg++] = type) #define ADDSARG() \ ((flags&MAXINT) ? ADDTYPE(T_MAXINT) : \ ((flags&PTRINT) ? ADDTYPE(T_PTRINT) : \ ((flags&SIZEINT) ? ADDTYPE(T_SSIZEINT) : \ ((flags&LLONGINT) ? ADDTYPE(T_LLONG) : \ ((flags&LONGINT) ? ADDTYPE(T_LONG) : \ ((flags&SHORTINT) ? ADDTYPE(T_SHORT) : \ ((flags&CHARINT) ? ADDTYPE(T_CHAR) : ADDTYPE(T_INT)))))))) #define ADDUARG() \ ((flags&MAXINT) ? ADDTYPE(T_MAXUINT) : \ ((flags&PTRINT) ? ADDTYPE(T_PTRINT) : \ ((flags&SIZEINT) ? ADDTYPE(T_SIZEINT) : \ ((flags&LLONGINT) ? ADDTYPE(T_U_LLONG) : \ ((flags&LONGINT) ? ADDTYPE(T_U_LONG) : \ ((flags&SHORTINT) ? ADDTYPE(T_U_SHORT) : \ ((flags&CHARINT) ? ADDTYPE(T_U_CHAR) : ADDTYPE(T_U_INT)))))))) /* * Add * arguments to the type array. */ #define ADDASTER() \ n2 = 0; \ cp = fmt; \ while (is_digit(*cp)) { \ APPEND_DIGIT(n2, *cp); \ cp++; \ } \ if (*cp == '$') { \ int hold = nextarg; \ nextarg = n2; \ ADDTYPE(T_INT); \ nextarg = hold; \ fmt = ++cp; \ } else { \ ADDTYPE(T_INT); \ } fmt = (char *)fmt0; typetable = stattypetable; tablesize = STATIC_ARG_TBL_SIZE; tablemax = 0; nextarg = 1; memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE); /* * Scan the format for conversions (`%' character). */ for (;;) { for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) continue; if (ch == '\0') goto done; fmt++; /* skip over '%' */ flags = 0; rflag: ch = *fmt++; reswitch: switch (ch) { case ' ': case '#': case '\'': goto rflag; case '*': ADDASTER(); goto rflag; case '-': case '+': goto rflag; case '.': if ((ch = *fmt++) == '*') { ADDASTER(); goto rflag; } while (is_digit(ch)) { ch = *fmt++; } goto reswitch; case '0': goto rflag; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = 0; do { APPEND_DIGIT(n ,ch); ch = *fmt++; } while (is_digit(ch)); if (ch == '$') { nextarg = n; goto rflag; } goto reswitch; #ifdef FLOATING_POINT case 'L': flags |= LONGDBL; goto rflag; #endif case 'h': if (*fmt == 'h') { fmt++; flags |= CHARINT; } else { flags |= SHORTINT; } goto rflag; case 'j': flags |= MAXINT; goto rflag; case 'l': if (*fmt == 'l') { fmt++; flags |= LLONGINT; } else { flags |= LONGINT; } goto rflag; case 'q': flags |= LLONGINT; goto rflag; case 't': flags |= PTRINT; goto rflag; case 'z': flags |= SIZEINT; goto rflag; case 'c': #ifdef PRINTF_WIDE_CHAR if (flags & LONGINT) ADDTYPE(T_WINT); else #endif ADDTYPE(T_INT); break; case 'D': flags |= LONGINT; /*FALLTHROUGH*/ case 'd': case 'i': ADDSARG(); break; #ifdef FLOATING_POINT case 'a': case 'A': case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': if (flags & LONGDBL) ADDTYPE(T_LONG_DOUBLE); else ADDTYPE(T_DOUBLE); break; #endif /* FLOATING_POINT */ case 'n': if (flags & LLONGINT) ADDTYPE(TP_LLONG); else if (flags & LONGINT) ADDTYPE(TP_LONG); else if (flags & SHORTINT) ADDTYPE(TP_SHORT); else if (flags & PTRINT) ADDTYPE(TP_PTRINT); else if (flags & SIZEINT) ADDTYPE(TP_SSIZEINT); else if (flags & MAXINT) ADDTYPE(TP_MAXINT); else ADDTYPE(TP_INT); continue; /* no output */ case 'O': flags |= LONGINT; /*FALLTHROUGH*/ case 'o': ADDUARG(); break; case 'p': ADDTYPE(TP_VOID); break; case 's': #ifdef PRINTF_WIDE_CHAR if (flags & LONGINT) ADDTYPE(TP_WCHAR); else #endif ADDTYPE(TP_CHAR); break; case 'U': flags |= LONGINT; /*FALLTHROUGH*/ case 'u': case 'X': case 'x': ADDUARG(); break; default: /* "%?" prints ?, unless ? is NUL */ if (ch == '\0') goto done; break; } } done: /* * Build the argument table. */ if (tablemax >= STATIC_ARG_TBL_SIZE) { *argtablesiz = sizeof(union arg) * (tablemax + 1); *argtable = mmap(NULL, *argtablesiz, PROT_WRITE|PROT_READ, MAP_ANON|MAP_PRIVATE, -1, 0); if (*argtable == MAP_FAILED) return (-1); } for (n = 1; n <= tablemax; n++) { switch (typetable[n]) { case T_UNUSED: case T_CHAR: case T_U_CHAR: case T_SHORT: case T_U_SHORT: case T_INT: (*argtable)[n].intarg = va_arg(ap, int); break; case TP_SHORT: (*argtable)[n].pshortarg = va_arg(ap, short *); break; case T_U_INT: (*argtable)[n].uintarg = va_arg(ap, unsigned int); break; case TP_INT: (*argtable)[n].pintarg = va_arg(ap, int *); break; case T_LONG: (*argtable)[n].longarg = va_arg(ap, long); break; case T_U_LONG: (*argtable)[n].ulongarg = va_arg(ap, unsigned long); break; case TP_LONG: (*argtable)[n].plongarg = va_arg(ap, long *); break; case T_LLONG: (*argtable)[n].longlongarg = va_arg(ap, long long); break; case T_U_LLONG: (*argtable)[n].ulonglongarg = va_arg(ap, unsigned long long); break; case TP_LLONG: (*argtable)[n].plonglongarg = va_arg(ap, long long *); break; #ifdef FLOATING_POINT case T_DOUBLE: (*argtable)[n].doublearg = va_arg(ap, double); break; case T_LONG_DOUBLE: (*argtable)[n].longdoublearg = va_arg(ap, long double); break; #endif case TP_CHAR: (*argtable)[n].pchararg = va_arg(ap, char *); break; case TP_VOID: (*argtable)[n].pvoidarg = va_arg(ap, void *); break; case T_PTRINT: (*argtable)[n].ptrdiffarg = va_arg(ap, ptrdiff_t); break; case TP_PTRINT: (*argtable)[n].pptrdiffarg = va_arg(ap, ptrdiff_t *); break; case T_SIZEINT: (*argtable)[n].sizearg = va_arg(ap, size_t); break; case T_SSIZEINT: (*argtable)[n].ssizearg = va_arg(ap, ssize_t); break; case TP_SSIZEINT: (*argtable)[n].pssizearg = va_arg(ap, ssize_t *); break; case T_MAXINT: (*argtable)[n].intmaxarg = va_arg(ap, intmax_t); break; case T_MAXUINT: (*argtable)[n].uintmaxarg = va_arg(ap, uintmax_t); break; case TP_MAXINT: (*argtable)[n].pintmaxarg = va_arg(ap, intmax_t *); break; #ifdef PRINTF_WIDE_CHAR case T_WINT: (*argtable)[n].wintarg = va_arg(ap, wint_t); break; case TP_WCHAR: (*argtable)[n].pwchararg = va_arg(ap, wchar_t *); break; #endif } } goto finish; overflow: errno = EOVERFLOW; ret = -1; finish: if (typetable != NULL && typetable != stattypetable) { munmap(typetable, *argtablesiz); typetable = NULL; } return (ret); } /* * Increase the size of the type table. */ static int __grow_type_table(unsigned char **typetable, int *tablesize) { unsigned char *oldtable = *typetable; int newsize = *tablesize * 2; if (newsize < getpagesize()) newsize = getpagesize(); if (*tablesize == STATIC_ARG_TBL_SIZE) { *typetable = mmap(NULL, newsize, PROT_WRITE|PROT_READ, MAP_ANON|MAP_PRIVATE, -1, 0); if (*typetable == MAP_FAILED) return (-1); bcopy(oldtable, *typetable, *tablesize); } else { unsigned char *new = mmap(NULL, newsize, PROT_WRITE|PROT_READ, MAP_ANON|MAP_PRIVATE, -1, 0); if (new == MAP_FAILED) return (-1); memmove(new, *typetable, *tablesize); munmap(*typetable, *tablesize); *typetable = new; } memset(*typetable + *tablesize, T_UNUSED, (newsize - *tablesize)); *tablesize = newsize; return (0); } #ifdef FLOATING_POINT static int exponent(char *p0, int exp, int fmtch) { char *p, *t; char expbuf[MAXEXPDIG]; p = p0; *p++ = fmtch; if (exp < 0) { exp = -exp; *p++ = '-'; } else *p++ = '+'; t = expbuf + MAXEXPDIG; if (exp > 9) { do { *--t = to_char(exp % 10); } while ((exp /= 10) > 9); *--t = to_char(exp); for (; t < expbuf + MAXEXPDIG; *p++ = *t++) /* nothing */; } else { /* * Exponents for decimal floating point conversions * (%[eEgG]) must be at least two characters long, * whereas exponents for hexadecimal conversions can * be only one character long. */ if (fmtch == 'e' || fmtch == 'E') *p++ = '0'; *p++ = to_char(exp); } return (p - p0); } #endif /* FLOATING_POINT */ int fprintf(FILE *fp, const char *fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = vfprintf(fp, fmt, ap); va_end(ap); return (ret); } int fpurge(FILE *fp) { FLOCKFILE(fp); if (!fp->_flags) { FUNLOCKFILE(fp); errno = EBADF; return(EOF); } if (HASUB(fp)) FREEUB(fp); WCIO_FREE(fp); fp->_p = fp->_bf._base; fp->_r = 0; fp->_w = fp->_flags & (__SLBF|__SNBF) ? 0 : fp->_bf._size; FUNLOCKFILE(fp); return (0); } int fputc(int c, FILE *fp) { return (putc(c, fp)); } int fputs(const char *s, FILE *fp) { struct __suio uio; struct __siov iov; int ret; iov.iov_base = (void *)s; iov.iov_len = uio.uio_resid = strlen(s); uio.uio_iov = &iov; uio.uio_iovcnt = 1; FLOCKFILE(fp); _SET_ORIENTATION(fp, -1); ret = __sfvwrite(fp, &uio); FUNLOCKFILE(fp); return (ret); } size_t fread(void *buf, size_t size, size_t count, FILE *fp) { size_t resid; char *p; int r; size_t total; /* * Extension: Catch integer overflow */ if ((size >= MUL_NO_OVERFLOW || count >= MUL_NO_OVERFLOW) && size > 0 && SIZE_MAX / size < count) { errno = EOVERFLOW; fp->_flags |= __SERR; return (0); } /* * ANSI and SUSv2 require a return value of 0 if size or count are 0. */ if ((resid = count * size) == 0) return (0); FLOCKFILE(fp); _SET_ORIENTATION(fp, -1); if (fp->_r < 0) fp->_r = 0; total = resid; p = buf; /* * If we're unbuffered we know that the buffer in fp is empty so * we can read directly into buf. This is much faster than a * series of one byte reads into fp->_nbuf. */ if ((fp->_flags & __SNBF) != 0 && buf != NULL) { while (resid > 0) { /* set up the buffer */ fp->_bf._base = fp->_p = p; fp->_bf._size = resid; if (__srefill(fp)) { /* no more input: return partial result */ count = (total - resid) / size; break; } p += fp->_r; resid -= fp->_r; } /* restore the old buffer (see __smakebuf) */ fp->_bf._base = fp->_p = fp->_nbuf; fp->_bf._size = 1; fp->_r = 0; FUNLOCKFILE(fp); return (count); } while (resid > (r = fp->_r)) { (void)memcpy(p, fp->_p, r); fp->_p += r; /* fp->_r = 0 ... done in __srefill */ p += r; resid -= r; if (__srefill(fp)) { /* no more input: return partial result */ FUNLOCKFILE(fp); return ((total - resid) / size); } } (void)memcpy(p, fp->_p, resid); fp->_r -= resid; fp->_p += resid; FUNLOCKFILE(fp); return (count); } FILE * freopen(const char *file, const char *mode, FILE *fp) { int f; int flags, isopen, oflags, sverrno, wantfd; if ((flags = __sflags(mode, &oflags)) == 0) { (void) fclose(fp); return (NULL); } if (!__sdidinit) __sinit(); FLOCKFILE(fp); /* * There are actually programs that depend on being able to "freopen" * descriptors that weren't originally open. Keep this from breaking. * Remember whether the stream was open to begin with, and which file * descriptor (if any) was associated with it. If it was attached to * a descriptor, defer closing it; freopen("/dev/stdin", "r", stdin) * should work. This is unnecessary if it was not a Unix file. */ if (fp->_flags == 0) { fp->_flags = __SEOF; /* hold on to it */ isopen = 0; wantfd = -1; } else { /* flush the stream; ANSI doesn't require this. */ if (fp->_flags & __SWR) (void) __sflush(fp); /* if close is NULL, closing is a no-op, hence pointless */ isopen = fp->_close != NULL; if ((wantfd = fp->_file) < 0 && isopen) { (void) (*fp->_close)(fp->_cookie); isopen = 0; } } /* Get a new descriptor to refer to the new file. */ f = open(file, oflags, DEFFILEMODE); if (f == -1 && isopen) { /* If out of fd's close the old one and try again. */ if (errno == ENFILE || errno == EMFILE) { (void) (*fp->_close)(fp->_cookie); isopen = 0; f = open(file, oflags, DEFFILEMODE); } } sverrno = errno; /* * Finish closing fp. Even if the open succeeded above, we cannot * keep fp->_base: it may be the wrong size. This loses the effect * of any setbuffer calls, but stdio has always done this before. */ if (isopen && f != wantfd) (void) (*fp->_close)(fp->_cookie); if (fp->_flags & __SMBF) free((char *)fp->_bf._base); fp->_w = 0; fp->_r = 0; fp->_p = NULL; fp->_bf._base = NULL; fp->_bf._size = 0; fp->_lbfsize = 0; if (HASUB(fp)) FREEUB(fp); _UB(fp)._size = 0; WCIO_FREE(fp); if (HASLB(fp)) FREELB(fp); fp->_lb._size = 0; if (f == -1) { /* did not get it after all */ fp->_flags = 0; /* set it free */ FUNLOCKFILE(fp); errno = sverrno; /* restore in case _close clobbered */ return (NULL); } /* * If reopening something that was open before on a real file, try * to maintain the descriptor. Various C library routines (perror) * assume stderr is always fd STDERR_FILENO, even if being freopen'd. */ if (wantfd >= 0 && f != wantfd) { if (dup3(f, wantfd, oflags & O_CLOEXEC) >= 0) { (void) close(f); f = wantfd; } } /* _file is only a short */ if (f > SHRT_MAX) { fp->_flags = 0; /* set it free */ FUNLOCKFILE(fp); errno = EMFILE; return (NULL); } fp->_flags = flags; fp->_file = f; fp->_cookie = fp; fp->_read = __sread; fp->_write = __swrite; fp->_seek = __sseek; fp->_close = __sclose; /* * When opening in append mode, even though we use O_APPEND, * we need to seek to the end so that ftell() gets the right * answer. If the user then alters the seek pointer, or * the file extends, this will fail, but there is not much * we can do about this. (We could set __SAPP and check in * fseek and ftell.) */ if (oflags & O_APPEND) (void) __sseek(fp, 0, SEEK_END); FUNLOCKFILE(fp); return (fp); } static u_char *__sccl(char *, u_char *); /* * Internal, unlocked version of vfscanf */ int __svfscanf(FILE *fp, const char *fmt0, __va_list ap) { u_char *fmt = (u_char *)fmt0; int c; /* character from format, or conversion */ size_t width; /* field width, or 0 */ char *p; /* points into all kinds of strings */ int n; /* handy integer */ int flags; /* flags as defined above */ char *p0; /* saves original value of p when necessary */ int nassigned; /* number of fields assigned */ int nread; /* number of characters consumed from fp */ int base; /* base argument to strtoimax/strtouimax */ char ccltab[256]; /* character class table for %[...] */ char buf[BUF]; /* buffer for numeric conversions */ #ifdef SCANF_WIDE_CHAR wchar_t *wcp; /* handy wide character pointer */ size_t nconv; /* length of multibyte sequence converted */ mbstate_t mbs; #endif /* `basefix' is used to avoid `if' tests in the integer scanner */ static short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; _SET_ORIENTATION(fp, -1); nassigned = 0; nread = 0; base = 0; /* XXX just to keep gcc happy */ for (;;) { c = *fmt++; if (c == 0) return (nassigned); if (isspace(c)) { while ((fp->_r > 0 || __srefill(fp) == 0) && isspace(*fp->_p)) nread++, fp->_r--, fp->_p++; continue; } if (c != '%') goto literal; width = 0; flags = 0; /* * switch on the format. continue if done; * break once format type is derived. */ again: c = *fmt++; switch (c) { case '%': literal: if (fp->_r <= 0 && __srefill(fp)) goto input_failure; if (*fp->_p != c) goto match_failure; fp->_r--, fp->_p++; nread++; continue; case '*': flags |= SUPPRESS; goto again; case 'j': flags |= MAXINT; goto again; case 'L': flags |= LONGDBL; goto again; case 'h': if (*fmt == 'h') { fmt++; flags |= SHORTSHORT; } else { flags |= SHORT; } goto again; case 'l': if (*fmt == 'l') { fmt++; flags |= LLONG; } else { flags |= LONG; } goto again; case 'q': flags |= LLONG; /* deprecated */ goto again; case 't': flags |= PTRINT; goto again; case 'z': flags |= SIZEINT; goto again; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': width = width * 10 + c - '0'; goto again; /* * Conversions. * Those marked `compat' are for 4.[123]BSD compatibility. * * (According to ANSI, E and X formats are supposed * to the same as e and x. Sorry about that.) */ case 'D': /* compat */ flags |= LONG; /* FALLTHROUGH */ case 'd': c = CT_INT; base = 10; break; case 'i': c = CT_INT; base = 0; break; case 'O': /* compat */ flags |= LONG; /* FALLTHROUGH */ case 'o': c = CT_INT; flags |= UNSIGNED; base = 8; break; case 'u': c = CT_INT; flags |= UNSIGNED; base = 10; break; case 'X': case 'x': flags |= PFXOK; /* enable 0x prefixing */ c = CT_INT; flags |= UNSIGNED; base = 16; break; #ifdef FLOATING_POINT case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': case 'a': case 'A': c = CT_FLOAT; break; #endif case 's': c = CT_STRING; break; case '[': fmt = __sccl(ccltab, fmt); flags |= NOSKIP; c = CT_CCL; break; case 'c': flags |= NOSKIP; c = CT_CHAR; break; case 'p': /* pointer format is like hex */ flags |= POINTER | PFXOK; c = CT_INT; flags |= UNSIGNED; base = 16; break; case 'n': if (flags & SUPPRESS) continue; if (flags & SHORTSHORT) *va_arg(ap, signed char *) = nread; else if (flags & SHORT) *va_arg(ap, short *) = nread; else if (flags & LONG) *va_arg(ap, long *) = nread; else if (flags & SIZEINT) *va_arg(ap, ssize_t *) = nread; else if (flags & PTRINT) *va_arg(ap, ptrdiff_t *) = nread; else if (flags & LLONG) *va_arg(ap, long long *) = nread; else if (flags & MAXINT) *va_arg(ap, intmax_t *) = nread; else *va_arg(ap, int *) = nread; continue; /* * Disgusting backwards compatibility hacks. XXX */ case '\0': /* compat */ return (EOF); default: /* compat */ if (isupper(c)) flags |= LONG; c = CT_INT; base = 10; break; } /* * We have a conversion that requires input. */ if (fp->_r <= 0 && __srefill(fp)) goto input_failure; /* * Consume leading white space, except for formats * that suppress this. */ if ((flags & NOSKIP) == 0) { while (isspace(*fp->_p)) { nread++; if (--fp->_r > 0) fp->_p++; else if (__srefill(fp)) goto input_failure; } /* * Note that there is at least one character in * the buffer, so conversions that do not set NOSKIP * ca no longer result in an input failure. */ } /* * Do the conversion. */ switch (c) { case CT_CHAR: /* scan arbitrary characters (sets NOSKIP) */ if (width == 0) width = 1; #ifdef SCANF_WIDE_CHAR if (flags & LONG) { if ((flags & SUPPRESS) == 0) wcp = va_arg(ap, wchar_t *); else wcp = NULL; n = 0; while (width != 0) { if (n == MB_CUR_MAX) { fp->_flags |= __SERR; goto input_failure; } buf[n++] = *fp->_p; fp->_p++; fp->_r--; bzero(&mbs, sizeof(mbs)); nconv = mbrtowc(wcp, buf, n, &mbs); if (nconv == (size_t)-1) { fp->_flags |= __SERR; goto input_failure; } if (nconv == 0 && !(flags & SUPPRESS)) *wcp = L'\0'; if (nconv != (size_t)-2) { nread += n; width--; if (!(flags & SUPPRESS)) wcp++; n = 0; } if (fp->_r <= 0 && __srefill(fp)) { if (n != 0) { fp->_flags |= __SERR; goto input_failure; } break; } } if (!(flags & SUPPRESS)) nassigned++; } else #endif /* SCANF_WIDE_CHAR */ if (flags & SUPPRESS) { size_t sum = 0; for (;;) { if ((n = fp->_r) < width) { sum += n; width -= n; fp->_p += n; if (__srefill(fp)) { if (sum == 0) goto input_failure; break; } } else { sum += width; fp->_r -= width; fp->_p += width; break; } } nread += sum; } else { size_t r = fread(va_arg(ap, char *), 1, width, fp); if (r == 0) goto input_failure; nread += r; nassigned++; } break; case CT_CCL: /* scan a (nonempty) character class (sets NOSKIP) */ if (width == 0) width = (size_t)~0; /* `infinity' */ #ifdef SCANF_WIDE_CHAR /* take only those things in the class */ if (flags & LONG) { wchar_t twc; int nchars; if ((flags & SUPPRESS) == 0) wcp = va_arg(ap, wchar_t *); else wcp = &twc; n = 0; nchars = 0; while (width != 0) { if (n == MB_CUR_MAX) { fp->_flags |= __SERR; goto input_failure; } buf[n++] = *fp->_p; fp->_p++; fp->_r--; bzero(&mbs, sizeof(mbs)); nconv = mbrtowc(wcp, buf, n, &mbs); if (nconv == (size_t)-1) { fp->_flags |= __SERR; goto input_failure; } if (nconv == 0) *wcp = L'\0'; if (nconv != (size_t)-2) { if (wctob(*wcp) != EOF && !ccltab[wctob(*wcp)]) { while (n != 0) { n--; ungetc(buf[n], fp); } break; } nread += n; width--; if (!(flags & SUPPRESS)) wcp++; nchars++; n = 0; } if (fp->_r <= 0 && __srefill(fp)) { if (n != 0) { fp->_flags |= __SERR; goto input_failure; } break; } } if (n != 0) { fp->_flags |= __SERR; goto input_failure; } n = nchars; if (n == 0) goto match_failure; if (!(flags & SUPPRESS)) { *wcp = L'\0'; nassigned++; } } else #endif /* SCANF_WIDE_CHAR */ /* take only those things in the class */ if (flags & SUPPRESS) { n = 0; while (ccltab[*fp->_p]) { n++, fp->_r--, fp->_p++; if (--width == 0) break; if (fp->_r <= 0 && __srefill(fp)) { if (n == 0) goto input_failure; break; } } if (n == 0) goto match_failure; } else { p0 = p = va_arg(ap, char *); while (ccltab[*fp->_p]) { fp->_r--; *p++ = *fp->_p++; if (--width == 0) break; if (fp->_r <= 0 && __srefill(fp)) { if (p == p0) goto input_failure; break; } } n = p - p0; if (n == 0) goto match_failure; *p = '\0'; nassigned++; } nread += n; break; case CT_STRING: /* like CCL, but zero-length string OK, & no NOSKIP */ if (width == 0) width = (size_t)~0; #ifdef SCANF_WIDE_CHAR if (flags & LONG) { wchar_t twc; if ((flags & SUPPRESS) == 0) wcp = va_arg(ap, wchar_t *); else wcp = &twc; n = 0; while (!isspace(*fp->_p) && width != 0) { if (n == MB_CUR_MAX) { fp->_flags |= __SERR; goto input_failure; } buf[n++] = *fp->_p; fp->_p++; fp->_r--; bzero(&mbs, sizeof(mbs)); nconv = mbrtowc(wcp, buf, n, &mbs); if (nconv == (size_t)-1) { fp->_flags |= __SERR; goto input_failure; } if (nconv == 0) *wcp = L'\0'; if (nconv != (size_t)-2) { if (iswspace(*wcp)) { while (n != 0) { n--; ungetc(buf[n], fp); } break; } nread += n; width--; if (!(flags & SUPPRESS)) wcp++; n = 0; } if (fp->_r <= 0 && __srefill(fp)) { if (n != 0) { fp->_flags |= __SERR; goto input_failure; } break; } } if (!(flags & SUPPRESS)) { *wcp = L'\0'; nassigned++; } } else #endif /* SCANF_WIDE_CHAR */ if (flags & SUPPRESS) { n = 0; while (!isspace(*fp->_p)) { n++, fp->_r--, fp->_p++; if (--width == 0) break; if (fp->_r <= 0 && __srefill(fp)) break; } nread += n; } else { p0 = p = va_arg(ap, char *); while (!isspace(*fp->_p)) { fp->_r--; *p++ = *fp->_p++; if (--width == 0) break; if (fp->_r <= 0 && __srefill(fp)) break; } *p = '\0'; nread += p - p0; nassigned++; } continue; case CT_INT: /* scan an integer as if by strtoimax/strtoumax */ #ifdef hardway if (width == 0 || width > sizeof(buf) - 1) width = sizeof(buf) - 1; #else /* size_t is unsigned, hence this optimisation */ if (--width > sizeof(buf) - 2) width = sizeof(buf) - 2; width++; #endif flags |= SIGNOK | NDIGITS | NZDIGITS; for (p = buf; width; width--) { c = *fp->_p; /* * Switch on the character; `goto ok' * if we accept it as a part of number. */ switch (c) { /* * The digit 0 is always legal, but is * special. For %i conversions, if no * digits (zero or nonzero) have been * scanned (only signs), we will have * base==0. In that case, we should set * it to 8 and enable 0x prefixing. * Also, if we have not scanned zero digits * before this, do not turn off prefixing * (someone else will turn it off if we * have scanned any nonzero digits). */ case '0': if (base == 0) { base = 8; flags |= PFXOK; } if (flags & NZDIGITS) flags &= ~(SIGNOK|NZDIGITS|NDIGITS); else flags &= ~(SIGNOK|PFXOK|NDIGITS); goto ok; /* 1 through 7 always legal */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': base = basefix[base]; flags &= ~(SIGNOK | PFXOK | NDIGITS); goto ok; /* digits 8 and 9 ok iff decimal or hex */ case '8': case '9': base = basefix[base]; if (base <= 8) break; /* not legal here */ flags &= ~(SIGNOK | PFXOK | NDIGITS); goto ok; /* letters ok iff hex */ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': /* no need to fix base here */ if (base <= 10) break; /* not legal here */ flags &= ~(SIGNOK | PFXOK | NDIGITS); goto ok; /* sign ok only as first character */ case '+': case '-': if (flags & SIGNOK) { flags &= ~SIGNOK; flags |= HAVESIGN; goto ok; } break; /* * x ok iff flag still set and 2nd char (or * 3rd char if we have a sign). */ case 'x': case 'X': if ((flags & PFXOK) && p == buf + 1 + !!(flags & HAVESIGN)) { base = 16; /* if %i */ flags &= ~PFXOK; goto ok; } break; } /* * If we got here, c is not a legal character * for a number. Stop accumulating digits. */ break; ok: /* * c is legal: store it and look at the next. */ *p++ = c; if (--fp->_r > 0) fp->_p++; else if (__srefill(fp)) break; /* EOF */ } /* * If we had only a sign, it is no good; push * back the sign. If the number ends in `x', * it was [sign] '0' 'x', so push back the x * and treat it as [sign] '0'. */ if (flags & NDIGITS) { if (p > buf) (void) ungetc(*(u_char *)--p, fp); goto match_failure; } c = ((u_char *)p)[-1]; if (c == 'x' || c == 'X') { --p; (void) ungetc(c, fp); } if ((flags & SUPPRESS) == 0) { uintmax_t res; *p = '\0'; if (flags & UNSIGNED) res = strtoumax(buf, NULL, base); else res = strtoimax(buf, NULL, base); if (flags & POINTER) *va_arg(ap, void **) = (void *)(uintptr_t)res; else if (flags & MAXINT) *va_arg(ap, intmax_t *) = res; else if (flags & LLONG) *va_arg(ap, long long *) = res; else if (flags & SIZEINT) *va_arg(ap, ssize_t *) = res; else if (flags & PTRINT) *va_arg(ap, ptrdiff_t *) = res; else if (flags & LONG) *va_arg(ap, long *) = res; else if (flags & SHORT) *va_arg(ap, short *) = res; else if (flags & SHORTSHORT) *va_arg(ap, signed char *) = res; else *va_arg(ap, int *) = res; nassigned++; } nread += p - buf; break; #ifdef FLOATING_POINT case CT_FLOAT: /* scan a floating point number as if by strtod */ #ifdef hardway if (width == 0 || width > sizeof(buf) - 1) width = sizeof(buf) - 1; #else /* size_t is unsigned, hence this optimisation */ if (--width > sizeof(buf) - 2) width = sizeof(buf) - 2; width++; #endif flags |= SIGNOK | NDIGITS | DPTOK | EXPOK; for (p = buf; width; width--) { c = *fp->_p; /* * This code mimicks the integer conversion * code, but is much simpler. */ switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': flags &= ~(SIGNOK | NDIGITS); goto fok; case '+': case '-': if (flags & SIGNOK) { flags &= ~SIGNOK; goto fok; } break; case '.': if (flags & DPTOK) { flags &= ~(SIGNOK | DPTOK); goto fok; } break; case 'e': case 'E': /* no exponent without some digits */ if ((flags&(NDIGITS|EXPOK)) == EXPOK) { flags = (flags & ~(EXPOK|DPTOK)) | SIGNOK | NDIGITS; goto fok; } break; } break; fok: *p++ = c; if (--fp->_r > 0) fp->_p++; else if (__srefill(fp)) break; /* EOF */ } /* * If no digits, might be missing exponent digits * (just give back the exponent) or might be missing * regular digits, but had sign and/or decimal point. */ if (flags & NDIGITS) { if (flags & EXPOK) { /* no digits at all */ while (p > buf) ungetc(*(u_char *)--p, fp); goto match_failure; } /* just a bad exponent (e and maybe sign) */ c = *(u_char *)--p; if (c != 'e' && c != 'E') { (void) ungetc(c, fp);/* sign */ c = *(u_char *)--p; } (void) ungetc(c, fp); } if ((flags & SUPPRESS) == 0) { *p = '\0'; if (flags & LONGDBL) { long double res = strtold(buf, (char **)NULL); *va_arg(ap, long double *) = res; } else if (flags & LONG) { double res = strtod(buf, (char **)NULL); *va_arg(ap, double *) = res; } else { float res = strtof(buf, (char **)NULL); *va_arg(ap, float *) = res; } nassigned++; } nread += p - buf; break; #endif /* FLOATING_POINT */ } } input_failure: if (nassigned == 0) nassigned = -1; match_failure: return (nassigned); } /* * Fill in the given table from the scanset at the given format * (just after `['). Return a pointer to the character past the * closing `]'. The table has a 1 wherever characters should be * considered part of the scanset. */ static u_char * __sccl(char *tab, u_char *fmt) { int c, n, v; /* first `clear' the whole table */ c = *fmt++; /* first char hat => negated scanset */ if (c == '^') { v = 1; /* default => accept */ c = *fmt++; /* get new first char */ } else v = 0; /* default => reject */ /* should probably use memset here */ for (n = 0; n < 256; n++) tab[n] = v; if (c == 0) return (fmt - 1);/* format ended before closing ] */ /* * Now set the entries corresponding to the actual scanset * to the opposite of the above. * * The first character may be ']' (or '-') without being special; * the last character may be '-'. */ v = 1 - v; for (;;) { tab[c] = v; /* take character c */ doswitch: n = *fmt++; /* and examine the next */ switch (n) { case 0: /* format ended too soon */ return (fmt - 1); case '-': /* * A scanset of the form * [01+-] * is defined as `the digit 0, the digit 1, * the character +, the character -', but * the effect of a scanset such as * [a-zA-Z0-9] * is implementation defined. The V7 Unix * scanf treats `a-z' as `the letters a through * z', but treats `a-a' as `the letter a, the * character -, and the letter a'. * * For compatibility, the `-' is not considerd * to define a range if the character following * it is either a close bracket (required by ANSI) * or is not numerically greater than the character * we just stored in the table (c). */ n = *fmt; if (n == ']' || n < c) { c = '-'; break; /* resume the for(;;) */ } fmt++; do { /* fill in the range */ tab[++c] = v; } while (c < n); #if 1 /* XXX another disgusting compatibility hack */ /* * Alas, the V7 Unix scanf also treats formats * such as [a-c-e] as `the letters a through e'. * This too is permitted by the standard.... */ goto doswitch; #else c = *fmt++; if (c == 0) return (fmt - 1); if (c == ']') return (fmt); #endif break; case ']': /* end of scanset */ return (fmt); default: /* just another character */ c = n; break; } } /* NOTREACHED */ } int vfscanf(FILE *fp, const char *fmt0, __va_list ap) { int r; FLOCKFILE(fp); r = __svfscanf(fp, fmt0, ap); FUNLOCKFILE(fp); return (r); } int fscanf(FILE *fp, const char *fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = vfscanf(fp, fmt, ap); va_end(ap); return (ret); } int fseeko(FILE *fp, off_t offset, int whence) { fpos_t (*seekfn)(void *, fpos_t, int); fpos_t target, curoff; size_t n; struct stat st; int havepos; /* make sure stdio is set up */ if (!__sdidinit) __sinit(); /* * Have to be able to seek. */ if ((seekfn = fp->_seek) == NULL) { errno = ESPIPE; /* historic practice */ return (EOF); } /* * Change any SEEK_CUR to SEEK_SET, and check `whence' argument. * After this, whence is either SEEK_SET or SEEK_END. */ FLOCKFILE(fp); switch (whence) { case SEEK_CUR: /* * In order to seek relative to the current stream offset, * we have to first find the current stream offset a la * ftell (see ftell for details). */ __sflush(fp); /* may adjust seek offset on append stream */ if (fp->_flags & __SOFF) curoff = fp->_offset; else { curoff = (*seekfn)(fp->_cookie, (fpos_t)0, SEEK_CUR); if (curoff == (fpos_t)-1) { FUNLOCKFILE(fp); return (EOF); } } if (fp->_flags & __SRD) { curoff -= fp->_r; if (HASUB(fp)) curoff -= fp->_ur; } else if (fp->_flags & __SWR && fp->_p != NULL) curoff += fp->_p - fp->_bf._base; offset += curoff; whence = SEEK_SET; havepos = 1; break; case SEEK_SET: case SEEK_END: curoff = 0; /* XXX just to keep gcc quiet */ havepos = 0; break; default: FUNLOCKFILE(fp); errno = EINVAL; return (EOF); } /* * Can only optimise if: * reading (and not reading-and-writing); * not unbuffered; and * this is a `regular' Unix file (and hence seekfn==__sseek). * We must check __NBF first, because it is possible to have __NBF * and __SOPT both set. */ if (fp->_bf._base == NULL) __smakebuf(fp); if (fp->_flags & (__SWR | __SRW | __SNBF | __SNPT)) goto dumb; if ((fp->_flags & __SOPT) == 0) { if (seekfn != __sseek || fp->_file < 0 || fstat(fp->_file, &st) == -1 || (st.st_mode & S_IFMT) != S_IFREG) { fp->_flags |= __SNPT; goto dumb; } fp->_blksize = st.st_blksize; fp->_flags |= __SOPT; } /* * We are reading; we can try to optimise. * Figure out where we are going and where we are now. */ if (whence == SEEK_SET) target = offset; else { if (fstat(fp->_file, &st) == -1) goto dumb; target = st.st_size + offset; } if (!havepos) { if (fp->_flags & __SOFF) curoff = fp->_offset; else { curoff = (*seekfn)(fp->_cookie, (fpos_t)0, SEEK_CUR); if (curoff == POS_ERR) goto dumb; } curoff -= fp->_r; if (HASUB(fp)) curoff -= fp->_ur; } /* * Compute the number of bytes in the input buffer (pretending * that any ungetc() input has been discarded). Adjust current * offset backwards by this count so that it represents the * file offset for the first byte in the current input buffer. */ if (HASUB(fp)) { curoff += fp->_r; /* kill off ungetc */ n = fp->_up - fp->_bf._base; curoff -= n; n += fp->_ur; } else { n = fp->_p - fp->_bf._base; curoff -= n; n += fp->_r; } /* * If the target offset is within the current buffer, * simply adjust the pointers, clear EOF, undo ungetc(), * and return. (If the buffer was modified, we have to * skip this; see fgetln.c.) */ if ((fp->_flags & __SMOD) == 0 && target >= curoff && target < curoff + n) { int o = target - curoff; fp->_p = fp->_bf._base + o; fp->_r = n - o; if (HASUB(fp)) FREEUB(fp); fp->_flags &= ~__SEOF; FUNLOCKFILE(fp); return (0); } /* * The place we want to get to is not within the current buffer, * but we can still be kind to the kernel copyout mechanism. * By aligning the file offset to a block boundary, we can let * the kernel use the VM hardware to map pages instead of * copying bytes laboriously. Using a block boundary also * ensures that we only read one block, rather than two. */ curoff = target & ~(fp->_blksize - 1); if ((*seekfn)(fp->_cookie, curoff, SEEK_SET) == POS_ERR) goto dumb; fp->_r = 0; fp->_p = fp->_bf._base; if (HASUB(fp)) FREEUB(fp); fp->_flags &= ~__SEOF; n = target - curoff; if (n) { if (__srefill(fp) || fp->_r < n) goto dumb; fp->_p += n; fp->_r -= n; } FUNLOCKFILE(fp); return (0); /* * We get here if we cannot optimise the seek ... just * do it. Allow the seek function to change fp->_bf._base. */ dumb: if (__sflush(fp) || (*seekfn)(fp->_cookie, (fpos_t)offset, whence) == POS_ERR) { FUNLOCKFILE(fp); return (EOF); } /* success: clear EOF indicator and discard ungetc() data */ if (HASUB(fp)) FREEUB(fp); fp->_p = fp->_bf._base; fp->_r = 0; /* fp->_w = 0; */ /* unnecessary (I think...) */ fp->_flags &= ~__SEOF; FUNLOCKFILE(fp); return (0); } int fseek(FILE *fp, long offset, int whence) { return (fseeko(fp, offset, whence)); } int fsetpos(FILE *iop, const fpos_t *pos) { return (fseeko(iop, *pos, SEEK_SET)); } off_t ftello(FILE *fp) { fpos_t pos; if (fp->_seek == NULL) { errno = ESPIPE; /* historic practice */ pos = -1; goto out; } /* * Find offset of underlying I/O object, then * adjust for buffered bytes. */ FLOCKFILE(fp); __sflush(fp); /* may adjust seek offset on append stream */ if (fp->_flags & __SOFF) pos = fp->_offset; else { pos = (*fp->_seek)(fp->_cookie, (fpos_t)0, SEEK_CUR); if (pos == -1) goto out; } if (fp->_flags & __SRD) { /* * Reading. Any unread characters (including * those from ungetc) cause the position to be * smaller than that in the underlying object. */ pos -= fp->_r; if (HASUB(fp)) pos -= fp->_ur; } else if (fp->_flags & __SWR && fp->_p != NULL) { /* * Writing. Any buffered characters cause the * position to be greater than that in the * underlying object. */ pos += fp->_p - fp->_bf._base; } out: FUNLOCKFILE(fp); return (pos); } long ftell(FILE *fp) { off_t offset = ftello(fp); if (offset > LONG_MAX) { errno = EOVERFLOW; return (-1); } return ((long)offset); } FILE * funopen(const void *cookie, int (*readfn)(void *, char *, int), int (*writefn)(void *, const char *, int), off_t (*seekfn)(void *, off_t, int), int (*closefn)(void *)) { FILE *fp; int flags; if (readfn == NULL) { if (writefn == NULL) { /* illegal */ errno = EINVAL; return (NULL); } else flags = __SWR; /* write only */ } else { if (writefn == NULL) flags = __SRD; /* read only */ else flags = __SRW; /* read-write */ } if ((fp = __sfp()) == NULL) return (NULL); fp->_flags = flags; fp->_file = -1; fp->_cookie = (void *)cookie; /* SAFE: cookie not modified */ fp->_read = readfn; fp->_write = writefn; fp->_seek = seekfn; fp->_close = closefn; return (fp); } int getc_unlocked(FILE *fp) { return (__sgetc(fp)); } /* * A subroutine version of the macro getc. */ #undef getc int getc(FILE *fp) { int c; FLOCKFILE(fp); c = __sgetc(fp); FUNLOCKFILE(fp); return (c); } int getchar_unlocked(void) { return (getc_unlocked(stdin)); } /* * A subroutine version of the macro getchar. */ #undef getchar int getchar(void) { return (getc(stdin)); } ssize_t getdelim(char **__restrict buf, size_t *__restrict buflen, int sep, FILE *__restrict fp) { unsigned char *p; size_t len, newlen, off; char *newb; FLOCKFILE(fp); if (buf == NULL || buflen == NULL) { errno = EINVAL; goto error; } /* If buf is NULL, we have to assume a size of zero */ if (*buf == NULL) *buflen = 0; _SET_ORIENTATION(fp, -1); off = 0; do { /* If the input buffer is empty, refill it */ if (fp->_r <= 0 && __srefill(fp)) { if (__sferror(fp)) goto error; /* No error, so EOF. */ break; } /* Scan through looking for the separator */ p = memchr(fp->_p, sep, fp->_r); if (p == NULL) len = fp->_r; else len = (p - fp->_p) + 1; /* Ensure we can handle it */ if (off > SSIZE_MAX || len + 1 > SSIZE_MAX - off) { errno = EOVERFLOW; goto error; } newlen = off + len + 1; /* reserve space for NUL terminator */ if (newlen > *buflen) { if (newlen < MINBUF) newlen = MINBUF; #define powerof2(x) ((((x)-1)&(x))==0) if (!powerof2(newlen)) { /* Grow the buffer to the next power of 2 */ newlen--; newlen |= newlen >> 1; newlen |= newlen >> 2; newlen |= newlen >> 4; newlen |= newlen >> 8; newlen |= newlen >> 16; #if SIZE_MAX > 0xffffffffU newlen |= newlen >> 32; #endif newlen++; } newb = recallocarray(*buf, *buflen, newlen, 1); if (newb == NULL) goto error; *buf = newb; *buflen = newlen; } (void)memcpy((*buf + off), fp->_p, len); /* Safe, len is never greater than what fp->_r can fit. */ fp->_r -= (int)len; fp->_p += (int)len; off += len; } while (p == NULL); FUNLOCKFILE(fp); /* POSIX demands we return -1 on EOF. */ if (off == 0) return -1; if (*buf != NULL) *(*buf + off) = '\0'; return off; error: fp->_flags |= __SERR; FUNLOCKFILE(fp); return -1; } ssize_t getline(char **__restrict buf, size_t *__restrict buflen, FILE *__restrict fp) { return getdelim(buf, buflen, '\n', fp); } void __smakebuf(FILE *fp) { void *p; int flags; size_t size; int couldbetty; if (fp->_flags & __SNBF) { fp->_bf._base = fp->_p = fp->_nbuf; fp->_bf._size = 1; return; } flags = __swhatbuf(fp, &size, &couldbetty); if ((p = malloc(size)) == NULL) { fp->_flags |= __SNBF; fp->_bf._base = fp->_p = fp->_nbuf; fp->_bf._size = 1; return; } flags |= __SMBF; fp->_bf._base = fp->_p = p; fp->_bf._size = size; if (couldbetty && isatty(fp->_file)) flags |= __SLBF; fp->_flags |= flags; } /* * Internal routine to determine `proper' buffering for a file. */ int __swhatbuf(FILE *fp, size_t *bufsize, int *couldbetty) { struct stat st; if (fp->_file < 0 || fstat(fp->_file, &st) == -1) { *couldbetty = 0; *bufsize = BUFSIZ; return (__SNPT); } /* could be a tty iff it is a character device */ *couldbetty = S_ISCHR(st.st_mode); if (st.st_blksize == 0) { *bufsize = BUFSIZ; return (__SNPT); } /* * Optimise fseek() only if it is a regular file. (The test for * __sseek is mainly paranoia.) It is safe to set _blksize * unconditionally; it will only be used if __SOPT is also set. */ *bufsize = st.st_blksize; fp->_blksize = st.st_blksize; return ((st.st_mode & S_IFMT) == S_IFREG && fp->_seek == __sseek ? __SOPT : __SNPT); } static int mktemp_internal(char *path, int slen, int mode, int flags) { char *start, *cp, *ep; const char tempchars[] = TEMPCHARS; unsigned int tries; struct stat sb; size_t len; int fd; len = strlen(path); if (len < MIN_X || slen < 0 || (size_t)slen > len - MIN_X) { errno = EINVAL; return(-1); } ep = path + len - slen; for (start = ep; start > path && start[-1] == 'X'; start--) ; if (ep - start < MIN_X) { errno = EINVAL; return(-1); } if (flags & ~MKOTEMP_FLAGS) { errno = EINVAL; return(-1); } flags |= O_CREAT | O_EXCL | O_RDWR; tries = INT_MAX; do { cp = start; do { unsigned short rbuf[16]; unsigned int i; /* * Avoid lots of arc4random() calls by using * a buffer sized for up to 16 Xs at a time. */ arc4random_buf(rbuf, sizeof(rbuf)); for (i = 0; i < nitems(rbuf) && cp != ep; i++) *cp++ = tempchars[rbuf[i] % NUM_CHARS]; } while (cp != ep); switch (mode) { case MKTEMP_NAME: if (lstat(path, &sb) != 0) return(errno == ENOENT ? 0 : -1); break; case MKTEMP_FILE: fd = open(path, flags, S_IRUSR|S_IWUSR); if (fd != -1 || errno != EEXIST) return(fd); break; case MKTEMP_DIR: if (mkdir(path, S_IRUSR|S_IWUSR|S_IXUSR) == 0) return(0); if (errno != EEXIST) return(-1); break; } } while (--tries); errno = EEXIST; return(-1); } char * _mktemp(char *path) { if (mktemp_internal(path, 0, MKTEMP_NAME, 0) == -1) return(NULL); return(path); } __warn_references(mktemp, "mktemp() possibly used unsafely; consider using mkstemp()"); char * mktemp(char *path) { return(_mktemp(path)); } int mkostemps(char *path, int slen, int flags) { return(mktemp_internal(path, slen, MKTEMP_FILE, flags)); } int mkstemp(char *path) { return(mktemp_internal(path, 0, MKTEMP_FILE, 0)); } int mkostemp(char *path, int flags) { return(mktemp_internal(path, 0, MKTEMP_FILE, flags)); } int mkstemps(char *path, int slen) { return(mktemp_internal(path, slen, MKTEMP_FILE, 0)); } char * mkdtemp(char *path) { int error; error = mktemp_internal(path, 0, MKTEMP_DIR, 0); return(error ? NULL : path); } int printf(const char *fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = vfprintf(stdout, fmt, ap); va_end(ap); return (ret); } int putc_unlocked(int c, FILE *fp) { if (cantwrite(fp)) { errno = EBADF; return (EOF); } _SET_ORIENTATION(fp, -1); return (__sputc(c, fp)); } /* * A subroutine version of the macro putc. */ #undef putc int putc(int c, FILE *fp) { int ret; FLOCKFILE(fp); ret = putc_unlocked(c, fp); FUNLOCKFILE(fp); return (ret); } int putchar_unlocked(int c) { FILE *so = stdout; return (putc_unlocked(c,so)); } #undef putchar /* * A subroutine version of the macro putchar */ int putchar(int c) { FILE *so = stdout; return (putc(c, so)); } int puts(const char *s) { size_t c = strlen(s); struct __suio uio; struct __siov iov[2]; int ret; iov[0].iov_base = (void *)s; iov[0].iov_len = c; iov[1].iov_base = "\n"; iov[1].iov_len = 1; uio.uio_resid = c + 1; uio.uio_iov = &iov[0]; uio.uio_iovcnt = 2; FLOCKFILE(stdout); _SET_ORIENTATION(stdout, -1); ret = __sfvwrite(stdout, &uio); FUNLOCKFILE(stdout); return (ret ? EOF : '\n'); } static int lflush(FILE *fp) { if ((fp->_flags & (__SLBF|__SWR)) == (__SLBF|__SWR)) return (__sflush_locked(fp)); /* ignored... */ return (0); } /* * Refill a stdio buffer. * Return EOF on eof or error, 0 otherwise. */ int __srefill(FILE *fp) { /* make sure stdio is set up */ if (!__sdidinit) __sinit(); fp->_r = 0; /* largely a convenience for callers */ /* SysV does not make this test; take it out for compatibility */ if (fp->_flags & __SEOF) return (EOF); /* if not already reading, have to be reading and writing */ if ((fp->_flags & __SRD) == 0) { if ((fp->_flags & __SRW) == 0) { errno = EBADF; fp->_flags |= __SERR; return (EOF); } /* switch to reading */ if (fp->_flags & __SWR) { if (__sflush(fp)) return (EOF); fp->_flags &= ~__SWR; fp->_w = 0; fp->_lbfsize = 0; } fp->_flags |= __SRD; } else { /* * We were reading. If there is an ungetc buffer, * we must have been reading from that. Drop it, * restoring the previous buffer (if any). If there * is anything in that buffer, return. */ if (HASUB(fp)) { FREEUB(fp); if ((fp->_r = fp->_ur) != 0) { fp->_p = fp->_up; return (0); } } } if (fp->_bf._base == NULL) __smakebuf(fp); /* * Before reading from a line buffered or unbuffered file, * flush all line buffered output files, per the ANSI C * standard. */ if (fp->_flags & (__SLBF|__SNBF)) { /* Ignore this file in _fwalk to avoid potential deadlock. */ fp->_flags |= __SIGN; (void) _fwalk(lflush); fp->_flags &= ~__SIGN; /* Now flush this file without locking it. */ if ((fp->_flags & (__SLBF|__SWR)) == (__SLBF|__SWR)) __sflush(fp); } fp->_p = fp->_bf._base; fp->_r = (*fp->_read)(fp->_cookie, (char *)fp->_p, fp->_bf._size); fp->_flags &= ~__SMOD; /* buffer contents are again pristine */ if (fp->_r <= 0) { if (fp->_r == 0) fp->_flags |= __SEOF; else { fp->_r = 0; fp->_flags |= __SERR; } return (EOF); } return (0); } int remove(const char *file) { struct stat st; if (lstat(file, &st) == -1) return (-1); if (S_ISDIR(st.st_mode)) return (rmdir(file)); return (unlink(file)); } int __srget(FILE *fp) { _SET_ORIENTATION(fp, -1); if (__srefill(fp) == 0) { fp->_r--; return (*fp->_p++); } return (EOF); } int scanf(const char *fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = vfscanf(stdin, fmt, ap); va_end(ap); return (ret); } void setbuf(FILE *fp, char *buf) { (void) setvbuf(fp, buf, buf ? _IOFBF : _IONBF, BUFSIZ); } void setbuffer(FILE *fp, char *buf, int size) { (void)setvbuf(fp, buf, buf ? _IOFBF : _IONBF, size); } /* * set line buffering */ int setlinebuf(FILE *fp) { return (setvbuf(fp, NULL, _IOLBF, 0)); } int setvbuf(FILE *fp, char *buf, int mode, size_t size) { int ret, flags; size_t iosize; int ttyflag; /* * Verify arguments. The `int' limit on `size' is due to this * particular implementation. Note, buf and size are ignored * when setting _IONBF. */ if (mode != _IONBF) if ((mode != _IOFBF && mode != _IOLBF) || (int)size < 0) return (EOF); /* * Write current buffer, if any. Discard unread input (including * ungetc data), cancel line buffering, and free old buffer if * malloc()ed. We also clear any eof condition, as if this were * a seek. */ FLOCKFILE(fp); ret = 0; (void)__sflush(fp); if (HASUB(fp)) FREEUB(fp); WCIO_FREE(fp); fp->_r = fp->_lbfsize = 0; flags = fp->_flags; if (flags & __SMBF) free(fp->_bf._base); flags &= ~(__SLBF | __SNBF | __SMBF | __SOPT | __SNPT | __SEOF); /* If setting unbuffered mode, skip all the hard work. */ if (mode == _IONBF) goto nbf; /* * Find optimal I/O size for seek optimization. This also returns * a `tty flag' to suggest that we check isatty(fd), but we do not * care since our caller told us how to buffer. */ flags |= __swhatbuf(fp, &iosize, &ttyflag); if (size == 0) { buf = NULL; /* force local allocation */ size = iosize; } /* Allocate buffer if needed. */ if (buf == NULL) { if ((buf = malloc(size)) == NULL) { /* * Unable to honor user's request. We will return * failure, but try again with file system size. */ ret = EOF; if (size != iosize) { size = iosize; buf = malloc(size); } } if (buf == NULL) { /* No luck; switch to unbuffered I/O. */ nbf: fp->_flags = flags | __SNBF; fp->_w = 0; fp->_bf._base = fp->_p = fp->_nbuf; fp->_bf._size = 1; FUNLOCKFILE(fp); return (ret); } flags |= __SMBF; } /* * We're committed to buffering from here, so make sure we've * registered to flush buffers on exit. */ if (!__sdidinit) __sinit(); /* * Kill any seek optimization if the buffer is not the * right size. * * SHOULD WE ALLOW MULTIPLES HERE (i.e., ok iff (size % iosize) == 0)? */ if (size != iosize) flags |= __SNPT; /* * Fix up the FILE fields, and set __cleanup for output flush on * exit (since we are buffered in some way). */ if (mode == _IOLBF) flags |= __SLBF; fp->_flags = flags; fp->_bf._base = fp->_p = (unsigned char *)buf; fp->_bf._size = size; /* fp->_lbfsize is still 0 */ if (flags & __SWR) { /* * Begin or continue writing: see __swsetup(). Note * that __SNBF is impossible (it was handled earlier). */ if (flags & __SLBF) { fp->_w = 0; fp->_lbfsize = -fp->_bf._size; } else fp->_w = size; } else { /* begin/continue reading, or stay in intermediate state */ fp->_w = 0; } FUNLOCKFILE(fp); return (ret); } int __sread(void *cookie, char *buf, int n) { FILE *fp = cookie; int ret; ret = read(fp->_file, buf, n); /* if the read succeeded, update the current offset */ if (ret >= 0) fp->_offset += ret; else fp->_flags &= ~__SOFF; /* paranoia */ return (ret); } int __swrite(void *cookie, const char *buf, int n) { FILE *fp = cookie; if (fp->_flags & __SAPP) (void) lseek(fp->_file, 0, SEEK_END); fp->_flags &= ~__SOFF; /* in case FAPPEND mode is set */ return (write(fp->_file, buf, n)); } fpos_t __sseek(void *cookie, fpos_t offset, int whence) { FILE *fp = cookie; off_t ret; ret = lseek(fp->_file, offset, whence); if (ret == -1) fp->_flags &= ~__SOFF; else { fp->_flags |= __SOFF; fp->_offset = ret; } return (ret); } int __sclose(void *cookie) { return (close(((FILE *)cookie)->_file)); } FILE * tmpfile(void) { sigset_t set, oset; FILE *fp; int fd, sverrno; char buf[] = _PATH_TMP "tmp.XXXXXXXXXX"; sigfillset(&set); (void)sigprocmask(SIG_BLOCK, &set, &oset); fd = mkstemp(buf); if (fd != -1) (void)unlink(buf); (void)sigprocmask(SIG_SETMASK, &oset, NULL); if (fd == -1) return (NULL); if ((fp = fdopen(fd, "w+")) == NULL) { sverrno = errno; (void)close(fd); errno = sverrno; return (NULL); } return (fp); } static int __submore(FILE *); /* * Expand the ungetc buffer `in place'. That is, adjust fp->_p when * the buffer moves, so that it points the same distance from the end, * and move the bytes in the buffer around as necessary so that they * are all at the end (stack-style). */ static int __submore(FILE *fp) { int i; unsigned char *p; if (_UB(fp)._base == fp->_ubuf) { /* * Get a new buffer (rather than expanding the old one). */ if ((p = malloc(BUFSIZ)) == NULL) return (EOF); _UB(fp)._base = p; _UB(fp)._size = BUFSIZ; p += BUFSIZ - sizeof(fp->_ubuf); for (i = sizeof(fp->_ubuf); --i >= 0;) p[i] = fp->_ubuf[i]; fp->_p = p; return (0); } i = _UB(fp)._size; p = reallocarray(_UB(fp)._base, i, 2); if (p == NULL) return (EOF); /* no overlap (hence can use memcpy) because we doubled the size */ (void)memcpy(p + i, p, i); fp->_p = p + i; _UB(fp)._base = p; _UB(fp)._size = i * 2; return (0); } int ungetc(int c, FILE *fp) { if (c == EOF) return (EOF); if (!__sdidinit) __sinit(); FLOCKFILE(fp); _SET_ORIENTATION(fp, -1); if ((fp->_flags & __SRD) == 0) { /* * Not already reading: no good unless reading-and-writing. * Otherwise, flush any current write stuff. */ if ((fp->_flags & __SRW) == 0) { error: FUNLOCKFILE(fp); return (EOF); } if (fp->_flags & __SWR) { if (__sflush(fp)) goto error; fp->_flags &= ~__SWR; fp->_w = 0; fp->_lbfsize = 0; } fp->_flags |= __SRD; } c = (unsigned char)c; /* * If we are in the middle of ungetc'ing, just continue. * This may require expanding the current ungetc buffer. */ if (HASUB(fp)) { if (fp->_r >= _UB(fp)._size && __submore(fp)) goto error; *--fp->_p = c; inc_ret: fp->_r++; FUNLOCKFILE(fp); return (c); } fp->_flags &= ~__SEOF; /* * If we can handle this by simply backing up, do so, * but never replace the original character. * (This makes sscanf() work when scanning `const' data.) */ if (fp->_bf._base != NULL && fp->_p > fp->_bf._base && fp->_p[-1] == c) { fp->_p--; goto inc_ret; } /* * Create an ungetc buffer. * Initially, we will use the `reserve' buffer. */ fp->_ur = fp->_r; fp->_up = fp->_p; _UB(fp)._base = fp->_ubuf; _UB(fp)._size = sizeof(fp->_ubuf); fp->_ubuf[sizeof(fp->_ubuf) - 1] = c; fp->_p = &fp->_ubuf[sizeof(fp->_ubuf) - 1]; fp->_r = 1; FUNLOCKFILE(fp); return (c); } int vprintf(const char *fmt, __va_list ap) { return (vfprintf(stdout, fmt, ap)); } int vscanf(const char *fmt, __va_list ap) { return (vfscanf(stdin, fmt, ap)); } int vsprintf(char *str, const char *fmt, __va_list ap) { int ret; FILE f; struct __sfileext fext; _FILEEXT_SETUP(&f, &fext); f._file = -1; f._flags = __SWR | __SSTR; f._bf._base = f._p = (unsigned char *)str; f._bf._size = f._w = INT_MAX; ret = __vfprintf(&f, fmt, ap); *f._p = '\0'; return (ret); } static int eofread(void *cookie, char *buf, int len) { return (0); } int vsscanf(const char *str, const char *fmt, __va_list ap) { FILE f; struct __sfileext fext; _FILEEXT_SETUP(&f, &fext); f._flags = __SRD; f._bf._base = f._p = (unsigned char *)str; f._bf._size = f._r = strlen(str); f._read = eofread; f._lb._base = NULL; return (__svfscanf(&f, fmt, ap)); } void rewind(FILE *fp) { fseeko(fp, 0, SEEK_SET); clearerr(fp); errno = 0; /* not required, but seems reasonable */ } size_t fwrite(const void *buf, size_t size, size_t count, FILE *fp) { size_t n; struct __suio uio; struct __siov iov; int ret; /* * Extension: Catch integer overflow */ if ((size >= MUL_NO_OVERFLOW || count >= MUL_NO_OVERFLOW) && size > 0 && SIZE_MAX / size < count) { errno = EOVERFLOW; fp->_flags |= __SERR; return (0); } /* * ANSI and SUSv2 require a return value of 0 if size or count are 0. */ if ((n = count * size) == 0) return (0); iov.iov_base = (void *)buf; uio.uio_resid = iov.iov_len = n; uio.uio_iov = &iov; uio.uio_iovcnt = 1; /* * The usual case is success (__sfvwrite returns 0); * skip the divide if this happens, since divides are * generally slow and since this occurs whenever size==0. */ FLOCKFILE(fp); _SET_ORIENTATION(fp, -1); ret = __sfvwrite(fp, &uio); FUNLOCKFILE(fp); if (ret == 0) return (count); return ((n - uio.uio_resid) / size); } int _fwalk(int (*function)(FILE *)) { FILE *fp; int n, ret; struct glue *g; ret = 0; for (g = &__sglue; g != NULL; g = g->next) for (fp = g->iobs, n = g->niobs; --n >= 0; fp++) { if ((fp->_flags != 0) && ((fp->_flags & __SIGN) == 0)) ret |= (*function)(fp); } return (ret); } int __sfvwrite(FILE *fp, struct __suio *uio) { size_t len; char *p; struct __siov *iov; int w, s; char *nl; int nlknown, nldist; if ((len = uio->uio_resid) == 0) return (0); /* make sure we can write */ if (cantwrite(fp)) { errno = EBADF; return (EOF); } #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define COPY(n) (void)memcpy(fp->_p, p, n) iov = uio->uio_iov; p = iov->iov_base; len = iov->iov_len; iov++; #define GETIOV(extra_work) \ while (len == 0) { \ extra_work; \ p = iov->iov_base; \ len = iov->iov_len; \ iov++; \ } if (fp->_flags & __SNBF) { /* * Unbuffered: write up to BUFSIZ bytes at a time. */ do { GETIOV(;); w = (*fp->_write)(fp->_cookie, p, MIN(len, BUFSIZ)); if (w <= 0) goto err; p += w; len -= w; } while ((uio->uio_resid -= w) != 0); } else if ((fp->_flags & __SLBF) == 0) { /* * Fully buffered: fill partially full buffer, if any, * and then flush. If there is no partial buffer, write * one _bf._size byte chunk directly (without copying). * * String output is a special case: write as many bytes * as fit, but pretend we wrote everything. This makes * snprintf() return the number of bytes needed, rather * than the number used, and avoids its write function * (so that the write function can be invalid). */ do { GETIOV(;); if ((fp->_flags & (__SALC | __SSTR)) == (__SALC | __SSTR) && fp->_w < len) { size_t blen = fp->_p - fp->_bf._base; int pgmsk = getpagesize() - 1; unsigned char *_base; int _size; /* Round up to nearest page. */ _size = ((blen + len + 1 + pgmsk) & ~pgmsk) - 1; _base = recallocarray(fp->_bf._base, fp->_bf._size + 1, _size + 1, 1); if (_base == NULL) goto err; fp->_w += _size - fp->_bf._size; fp->_bf._base = _base; fp->_bf._size = _size; fp->_p = _base + blen; } w = fp->_w; if (fp->_flags & __SSTR) { if (len < w) w = len; COPY(w); /* copy MIN(fp->_w,len), */ fp->_w -= w; fp->_p += w; w = len; /* but pretend copied all */ } else if (fp->_p > fp->_bf._base && len > w) { /* fill and flush */ COPY(w); /* fp->_w -= w; */ /* unneeded */ fp->_p += w; if (__sflush(fp)) goto err; } else if (len >= (w = fp->_bf._size)) { /* write directly */ w = (*fp->_write)(fp->_cookie, p, w); if (w <= 0) goto err; } else { /* fill and done */ w = len; COPY(w); fp->_w -= w; fp->_p += w; } p += w; len -= w; } while ((uio->uio_resid -= w) != 0); } else { /* * Line buffered: like fully buffered, but we * must check for newlines. Compute the distance * to the first newline (including the newline), * or `infinity' if there is none, then pretend * that the amount to write is MIN(len,nldist). */ nlknown = 0; nldist = 0; /* XXX just to keep gcc happy */ do { GETIOV(nlknown = 0); if (!nlknown) { nl = memchr(p, '\n', len); nldist = nl ? nl + 1 - p : len + 1; nlknown = 1; } s = MIN(len, nldist); w = fp->_w + fp->_bf._size; if (fp->_p > fp->_bf._base && s > w) { COPY(w); /* fp->_w -= w; */ fp->_p += w; if (__sflush(fp)) goto err; } else if (s >= (w = fp->_bf._size)) { w = (*fp->_write)(fp->_cookie, p, w); if (w <= 0) goto err; } else { w = s; COPY(w); fp->_w -= w; fp->_p += w; } if ((nldist -= w) == 0) { /* copied the newline: flush and forget */ if (__sflush(fp)) goto err; nlknown = 0; } p += w; len -= w; } while ((uio->uio_resid -= w) != 0); } return (0); err: fp->_flags |= __SERR; return (EOF); } int __swbuf(int c, FILE *fp) { int n; _SET_ORIENTATION(fp, -1); /* * In case we cannot write, or longjmp takes us out early, * make sure _w is 0 (if fully- or un-buffered) or -_bf._size * (if line buffered) so that we will get called again. * If we did not do this, a sufficient number of putc() * calls might wrap _w from negative to positive. */ fp->_w = fp->_lbfsize; if (cantwrite(fp)) { errno = EBADF; return (EOF); } c = (unsigned char)c; /* * If it is completely full, flush it out. Then, in any case, * stuff c into the buffer. If this causes the buffer to fill * completely, or if c is '\n' and the file is line buffered, * flush it (perhaps a second time). The second flush will always * happen on unbuffered streams, where _bf._size==1; __sflush() * guarantees that putc() will always call wbuf() by setting _w * to 0, so we need not do anything else. */ n = fp->_p - fp->_bf._base; if (n >= fp->_bf._size) { if (__sflush(fp)) return (EOF); n = 0; } fp->_w--; *fp->_p++ = c; if (++n == fp->_bf._size || (fp->_flags & __SLBF && c == '\n')) if (__sflush(fp)) return (EOF); return (c); } int __swsetup(FILE *fp) { /* make sure stdio is set up */ if (!__sdidinit) __sinit(); /* * If we are not writing, we had better be reading and writing. */ if ((fp->_flags & __SWR) == 0) { if ((fp->_flags & __SRW) == 0) return (EOF); if (fp->_flags & __SRD) { /* clobber any ungetc data */ if (HASUB(fp)) FREEUB(fp); fp->_flags &= ~(__SRD|__SEOF); fp->_r = 0; fp->_p = fp->_bf._base; } fp->_flags |= __SWR; } /* * Make a buffer if necessary, then set _w. */ if (fp->_bf._base == NULL) { if ((fp->_flags & (__SSTR | __SALC)) == __SSTR) return (EOF); __smakebuf(fp); } if (fp->_flags & __SLBF) { /* * It is line buffered, so make _lbfsize be -_bufsize * for the putc() macro. We will change _lbfsize back * to 0 whenever we turn off __SWR. */ fp->_w = 0; fp->_lbfsize = -fp->_bf._size; } else fp->_w = fp->_flags & __SNBF ? 0 : fp->_bf._size; return (0); } #undef stdin #undef stdout #undef stderr FILE *stdin = __sF + 0; FILE *stdout = __sF + 1; FILE *stderr = __sF + 2;