| Dmitriy Ivanov | 623b0d0 | 2014-05-14 23:11:05 -0700 | [diff] [blame] | 1 | /*	$OpenBSD: findfp.c,v 1.15 2013/12/17 16:33:27 deraadt Exp $ */ | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 2 | /*- | 
 | 3 |  * Copyright (c) 1990, 1993 | 
 | 4 |  *	The Regents of the University of California.  All rights reserved. | 
 | 5 |  * | 
 | 6 |  * This code is derived from software contributed to Berkeley by | 
 | 7 |  * Chris Torek. | 
 | 8 |  * | 
 | 9 |  * Redistribution and use in source and binary forms, with or without | 
 | 10 |  * modification, are permitted provided that the following conditions | 
 | 11 |  * are met: | 
 | 12 |  * 1. Redistributions of source code must retain the above copyright | 
 | 13 |  *    notice, this list of conditions and the following disclaimer. | 
 | 14 |  * 2. Redistributions in binary form must reproduce the above copyright | 
 | 15 |  *    notice, this list of conditions and the following disclaimer in the | 
 | 16 |  *    documentation and/or other materials provided with the distribution. | 
 | 17 |  * 3. Neither the name of the University nor the names of its contributors | 
 | 18 |  *    may be used to endorse or promote products derived from this software | 
 | 19 |  *    without specific prior written permission. | 
 | 20 |  * | 
 | 21 |  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 
 | 22 |  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
 | 23 |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
 | 24 |  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 
 | 25 |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
 | 26 |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 
 | 27 |  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 
 | 28 |  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
 | 29 |  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 
 | 30 |  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
 | 31 |  * SUCH DAMAGE. | 
 | 32 |  */ | 
 | 33 |  | 
| Elliott Hughes | 53cf348 | 2016-08-09 13:06:41 -0700 | [diff] [blame] | 34 | #define __BIONIC_NO_STDIO_FORTIFY | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 35 | #include <stdio.h> | 
| Elliott Hughes | 021335e | 2016-01-19 16:28:15 -0800 | [diff] [blame] | 36 |  | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 37 | #include <errno.h> | 
| Elliott Hughes | 021335e | 2016-01-19 16:28:15 -0800 | [diff] [blame] | 38 | #include <fcntl.h> | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 39 | #include <limits.h> | 
| Elliott Hughes | 20788ae | 2016-06-09 15:16:32 -0700 | [diff] [blame] | 40 | #include <paths.h> | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 41 | #include <stdlib.h> | 
 | 42 | #include <string.h> | 
| Elliott Hughes | 021335e | 2016-01-19 16:28:15 -0800 | [diff] [blame] | 43 | #include <sys/param.h> | 
| Elliott Hughes | 023c307 | 2016-01-22 15:04:51 -0800 | [diff] [blame] | 44 | #include <sys/stat.h> | 
| Elliott Hughes | 021335e | 2016-01-19 16:28:15 -0800 | [diff] [blame] | 45 | #include <unistd.h> | 
 | 46 |  | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 47 | #include "local.h" | 
 | 48 | #include "glue.h" | 
| Elliott Hughes | fb3873d | 2016-08-10 11:07:54 -0700 | [diff] [blame] | 49 | #include "private/bionic_fortify.h" | 
| Elliott Hughes | 023c307 | 2016-01-22 15:04:51 -0800 | [diff] [blame] | 50 | #include "private/ErrnoRestorer.h" | 
| Elliott Hughes | 6a03abc | 2014-11-03 12:32:17 -0800 | [diff] [blame] | 51 | #include "private/thread_private.h" | 
 | 52 |  | 
 | 53 | #define ALIGNBYTES (sizeof(uintptr_t) - 1) | 
 | 54 | #define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) &~ ALIGNBYTES) | 
| Calin Juravle | c20de90 | 2014-03-20 15:21:32 +0000 | [diff] [blame] | 55 |  | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 56 | #define	NDYNAMIC 10		/* add ten more whenever necessary */ | 
 | 57 |  | 
| Elliott Hughes | 70715da | 2016-08-01 16:35:17 -0700 | [diff] [blame] | 58 | #define PRINTF_IMPL(expr) \ | 
 | 59 |     va_list ap; \ | 
 | 60 |     va_start(ap, fmt); \ | 
 | 61 |     int result = (expr); \ | 
 | 62 |     va_end(ap); \ | 
 | 63 |     return result; | 
 | 64 |  | 
| Elliott Hughes | 023c307 | 2016-01-22 15:04:51 -0800 | [diff] [blame] | 65 | #define std(flags, file) \ | 
 | 66 |     {0,0,0,flags,file,{0,0},0,__sF+file,__sclose,__sread,nullptr,__swrite, \ | 
 | 67 |     {(unsigned char *)(__sFext+file), 0},nullptr,0,{0},{0},{0,0},0,0} | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 68 |  | 
| Kenny Root | f582340 | 2011-02-12 07:13:44 -0800 | [diff] [blame] | 69 | _THREAD_PRIVATE_MUTEX(__sfp_mutex); | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 70 |  | 
| Elliott Hughes | 29ee639 | 2015-12-07 11:07:15 -0800 | [diff] [blame] | 71 | // TODO: when we no longer have to support both clang and GCC, we can simplify all this. | 
 | 72 | #define SBUF_INIT {0,0} | 
 | 73 | #if defined(__LP64__) | 
 | 74 | #define MBSTATE_T_INIT {{0},{0}} | 
 | 75 | #else | 
 | 76 | #define MBSTATE_T_INIT {{0}} | 
 | 77 | #endif | 
 | 78 | #define WCHAR_IO_DATA_INIT {MBSTATE_T_INIT,MBSTATE_T_INIT,{0},0,0} | 
 | 79 |  | 
| Elliott Hughes | bb46afd | 2015-12-04 18:03:12 -0800 | [diff] [blame] | 80 | static struct __sfileext __sFext[3] = { | 
| Elliott Hughes | 023c307 | 2016-01-22 15:04:51 -0800 | [diff] [blame] | 81 |   { SBUF_INIT, WCHAR_IO_DATA_INIT, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, false, __sseek64 }, | 
 | 82 |   { SBUF_INIT, WCHAR_IO_DATA_INIT, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, false, __sseek64 }, | 
 | 83 |   { SBUF_INIT, WCHAR_IO_DATA_INIT, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, false, __sseek64 }, | 
| Elliott Hughes | bb46afd | 2015-12-04 18:03:12 -0800 | [diff] [blame] | 84 | }; | 
| Elliott Hughes | f0141df | 2015-10-12 12:44:23 -0700 | [diff] [blame] | 85 |  | 
 | 86 | // __sF is exported for backwards compatibility. Until M, we didn't have symbols | 
 | 87 | // for stdin/stdout/stderr; they were macros accessing __sF. | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 88 | FILE __sF[3] = { | 
| Elliott Hughes | bb46afd | 2015-12-04 18:03:12 -0800 | [diff] [blame] | 89 |   std(__SRD, STDIN_FILENO), | 
 | 90 |   std(__SWR, STDOUT_FILENO), | 
 | 91 |   std(__SWR|__SNBF, STDERR_FILENO), | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 92 | }; | 
| Elliott Hughes | f0141df | 2015-10-12 12:44:23 -0700 | [diff] [blame] | 93 |  | 
| Elliott Hughes | 168667c | 2014-11-14 14:42:59 -0800 | [diff] [blame] | 94 | FILE* stdin = &__sF[0]; | 
 | 95 | FILE* stdout = &__sF[1]; | 
 | 96 | FILE* stderr = &__sF[2]; | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 97 |  | 
| Elliott Hughes | bb46afd | 2015-12-04 18:03:12 -0800 | [diff] [blame] | 98 | struct glue __sglue = { NULL, 3, __sF }; | 
 | 99 | static struct glue* lastglue = &__sglue; | 
 | 100 |  | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 101 | class ScopedFileLock { | 
 | 102 |  public: | 
| Chih-Hung Hsieh | 62e3a07 | 2016-05-03 12:08:05 -0700 | [diff] [blame] | 103 |   explicit ScopedFileLock(FILE* fp) : fp_(fp) { | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 104 |     FLOCKFILE(fp_); | 
 | 105 |   } | 
 | 106 |   ~ScopedFileLock() { | 
 | 107 |     FUNLOCKFILE(fp_); | 
 | 108 |   } | 
 | 109 |  | 
 | 110 |  private: | 
 | 111 |   FILE* fp_; | 
 | 112 | }; | 
 | 113 |  | 
| Elliott Hughes | 021335e | 2016-01-19 16:28:15 -0800 | [diff] [blame] | 114 | static glue* moreglue(int n) { | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 115 |   static FILE empty; | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 116 |  | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 117 |   char* data = new char[sizeof(glue) + ALIGNBYTES + n * sizeof(FILE) + n * sizeof(__sfileext)]; | 
 | 118 |   if (data == nullptr) return nullptr; | 
| Elliott Hughes | 021335e | 2016-01-19 16:28:15 -0800 | [diff] [blame] | 119 |  | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 120 |   glue* g = reinterpret_cast<glue*>(data); | 
 | 121 |   FILE* p = reinterpret_cast<FILE*>(ALIGN(data + sizeof(*g))); | 
 | 122 |   __sfileext* pext = reinterpret_cast<__sfileext*>(ALIGN(data + sizeof(*g)) + n * sizeof(FILE)); | 
 | 123 |   g->next = NULL; | 
 | 124 |   g->niobs = n; | 
 | 125 |   g->iobs = p; | 
 | 126 |   while (--n >= 0) { | 
 | 127 |     *p = empty; | 
 | 128 |     _FILEEXT_SETUP(p, pext); | 
 | 129 |     p++; | 
 | 130 |     pext++; | 
 | 131 |   } | 
 | 132 |   return g; | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 133 | } | 
 | 134 |  | 
 | 135 | /* | 
 | 136 |  * Find a free FILE for fopen et al. | 
 | 137 |  */ | 
| Elliott Hughes | 021335e | 2016-01-19 16:28:15 -0800 | [diff] [blame] | 138 | FILE* __sfp(void) { | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 139 | 	FILE *fp; | 
 | 140 | 	int n; | 
 | 141 | 	struct glue *g; | 
 | 142 |  | 
| Kenny Root | f582340 | 2011-02-12 07:13:44 -0800 | [diff] [blame] | 143 | 	_THREAD_PRIVATE_MUTEX_LOCK(__sfp_mutex); | 
 | 144 | 	for (g = &__sglue; g != NULL; g = g->next) { | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 145 | 		for (fp = g->iobs, n = g->niobs; --n >= 0; fp++) | 
 | 146 | 			if (fp->_flags == 0) | 
 | 147 | 				goto found; | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 148 | 	} | 
| Kenny Root | f582340 | 2011-02-12 07:13:44 -0800 | [diff] [blame] | 149 |  | 
 | 150 | 	/* release lock while mallocing */ | 
 | 151 | 	_THREAD_PRIVATE_MUTEX_UNLOCK(__sfp_mutex); | 
 | 152 | 	if ((g = moreglue(NDYNAMIC)) == NULL) | 
 | 153 | 		return (NULL); | 
 | 154 | 	_THREAD_PRIVATE_MUTEX_LOCK(__sfp_mutex); | 
 | 155 | 	lastglue->next = g; | 
 | 156 | 	lastglue = g; | 
 | 157 | 	fp = g->iobs; | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 158 | found: | 
 | 159 | 	fp->_flags = 1;		/* reserve this slot; caller sets real flags */ | 
| Kenny Root | f582340 | 2011-02-12 07:13:44 -0800 | [diff] [blame] | 160 | 	_THREAD_PRIVATE_MUTEX_UNLOCK(__sfp_mutex); | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 161 | 	fp->_p = NULL;		/* no current pointer */ | 
 | 162 | 	fp->_w = 0;		/* nothing to read or write */ | 
 | 163 | 	fp->_r = 0; | 
 | 164 | 	fp->_bf._base = NULL;	/* no buffer */ | 
 | 165 | 	fp->_bf._size = 0; | 
 | 166 | 	fp->_lbfsize = 0;	/* not line buffered */ | 
 | 167 | 	fp->_file = -1;		/* no file */ | 
| Elliott Hughes | 023c307 | 2016-01-22 15:04:51 -0800 | [diff] [blame] | 168 |  | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 169 | 	fp->_lb._base = NULL;	/* no line buffer */ | 
 | 170 | 	fp->_lb._size = 0; | 
 | 171 | 	_FILEEXT_INIT(fp); | 
| Elliott Hughes | 023c307 | 2016-01-22 15:04:51 -0800 | [diff] [blame] | 172 |  | 
 | 173 | 	// Caller sets cookie, _read/_write etc. | 
 | 174 | 	// We explicitly clear _seek and _seek64 to prevent subtle bugs. | 
 | 175 | 	fp->_seek = nullptr; | 
 | 176 | 	_EXT(fp)->_seek64 = nullptr; | 
 | 177 |  | 
 | 178 | 	return fp; | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 179 | } | 
 | 180 |  | 
| Elliott Hughes | 021335e | 2016-01-19 16:28:15 -0800 | [diff] [blame] | 181 | extern "C" __LIBC_HIDDEN__ void __libc_stdio_cleanup(void) { | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 182 |   // Equivalent to fflush(nullptr), but without all the locking since we're shutting down anyway. | 
 | 183 |   _fwalk(__sflush); | 
| The Android Open Source Project | 1dc9e47 | 2009-03-03 19:28:35 -0800 | [diff] [blame] | 184 | } | 
| Elliott Hughes | 923f165 | 2016-01-19 15:46:05 -0800 | [diff] [blame] | 185 |  | 
| Elliott Hughes | 023c307 | 2016-01-22 15:04:51 -0800 | [diff] [blame] | 186 | static FILE* __fopen(int fd, int flags) { | 
 | 187 | #if !defined(__LP64__) | 
 | 188 |   if (fd > SHRT_MAX) { | 
 | 189 |     errno = EMFILE; | 
 | 190 |     return nullptr; | 
 | 191 |   } | 
 | 192 | #endif | 
 | 193 |  | 
 | 194 |   FILE* fp = __sfp(); | 
 | 195 |   if (fp != nullptr) { | 
 | 196 |     fp->_file = fd; | 
 | 197 |     fp->_flags = flags; | 
 | 198 |     fp->_cookie = fp; | 
 | 199 |     fp->_read = __sread; | 
 | 200 |     fp->_write = __swrite; | 
 | 201 |     fp->_close = __sclose; | 
 | 202 |     _EXT(fp)->_seek64 = __sseek64; | 
 | 203 |   } | 
 | 204 |   return fp; | 
 | 205 | } | 
 | 206 |  | 
 | 207 | FILE* fopen(const char* file, const char* mode) { | 
 | 208 |   int oflags; | 
 | 209 |   int flags = __sflags(mode, &oflags); | 
 | 210 |   if (flags == 0) return nullptr; | 
 | 211 |  | 
 | 212 |   int fd = open(file, oflags, DEFFILEMODE); | 
 | 213 |   if (fd == -1) { | 
 | 214 |     return nullptr; | 
 | 215 |   } | 
 | 216 |  | 
 | 217 |   FILE* fp = __fopen(fd, flags); | 
 | 218 |   if (fp == nullptr) { | 
 | 219 |     ErrnoRestorer errno_restorer; | 
 | 220 |     close(fd); | 
 | 221 |     return nullptr; | 
 | 222 |   } | 
 | 223 |  | 
 | 224 |   // When opening in append mode, even though we use O_APPEND, | 
 | 225 |   // we need to seek to the end so that ftell() gets the right | 
 | 226 |   // answer.  If the user then alters the seek pointer, or | 
 | 227 |   // the file extends, this will fail, but there is not much | 
 | 228 |   // we can do about this.  (We could set __SAPP and check in | 
 | 229 |   // fseek and ftell.) | 
 | 230 |   // TODO: check in __sseek instead. | 
 | 231 |   if (oflags & O_APPEND) __sseek64(fp, 0, SEEK_END); | 
 | 232 |  | 
 | 233 |   return fp; | 
 | 234 | } | 
| Elliott Hughes | f226ee5 | 2016-02-03 11:24:28 -0800 | [diff] [blame] | 235 | __strong_alias(fopen64, fopen); | 
| Elliott Hughes | 023c307 | 2016-01-22 15:04:51 -0800 | [diff] [blame] | 236 |  | 
 | 237 | FILE* fdopen(int fd, const char* mode) { | 
 | 238 |   int oflags; | 
 | 239 |   int flags = __sflags(mode, &oflags); | 
 | 240 |   if (flags == 0) return nullptr; | 
 | 241 |  | 
 | 242 |   // Make sure the mode the user wants is a subset of the actual mode. | 
 | 243 |   int fdflags = fcntl(fd, F_GETFL, 0); | 
 | 244 |   if (fdflags < 0) return nullptr; | 
 | 245 |   int tmp = fdflags & O_ACCMODE; | 
 | 246 |   if (tmp != O_RDWR && (tmp != (oflags & O_ACCMODE))) { | 
 | 247 |     errno = EINVAL; | 
 | 248 |     return nullptr; | 
 | 249 |   } | 
 | 250 |  | 
 | 251 |   // If opened for appending, but underlying descriptor does not have | 
 | 252 |   // O_APPEND bit set, assert __SAPP so that __swrite() will lseek to | 
 | 253 |   // end before each write. | 
 | 254 |   // TODO: use fcntl(2) to set O_APPEND instead. | 
 | 255 |   if ((oflags & O_APPEND) && !(fdflags & O_APPEND)) flags |= __SAPP; | 
 | 256 |  | 
 | 257 |   // If close-on-exec was requested, then turn it on if not already. | 
 | 258 |   if ((oflags & O_CLOEXEC) && !((tmp = fcntl(fd, F_GETFD)) & FD_CLOEXEC)) { | 
 | 259 |     fcntl(fd, F_SETFD, tmp | FD_CLOEXEC); | 
 | 260 |   } | 
 | 261 |  | 
 | 262 |   return __fopen(fd, flags); | 
 | 263 | } | 
 | 264 |  | 
 | 265 | // Re-direct an existing, open (probably) file to some other file. | 
 | 266 | // ANSI is written such that the original file gets closed if at | 
 | 267 | // all possible, no matter what. | 
 | 268 | // TODO: rewrite this mess completely. | 
 | 269 | FILE* freopen(const char* file, const char* mode, FILE* fp) { | 
 | 270 |   int oflags; | 
 | 271 |   int flags = __sflags(mode, &oflags); | 
 | 272 |   if (flags == 0) { | 
 | 273 |     fclose(fp); | 
 | 274 |     return nullptr; | 
 | 275 |   } | 
 | 276 |  | 
 | 277 |   ScopedFileLock sfl(fp); | 
 | 278 |  | 
 | 279 |   // There are actually programs that depend on being able to "freopen" | 
 | 280 |   // descriptors that weren't originally open.  Keep this from breaking. | 
 | 281 |   // Remember whether the stream was open to begin with, and which file | 
 | 282 |   // descriptor (if any) was associated with it.  If it was attached to | 
 | 283 |   // a descriptor, defer closing it; freopen("/dev/stdin", "r", stdin) | 
 | 284 |   // should work.  This is unnecessary if it was not a Unix file. | 
 | 285 |   int isopen, wantfd; | 
 | 286 |   if (fp->_flags == 0) { | 
 | 287 |     fp->_flags = __SEOF; // Hold on to it. | 
 | 288 |     isopen = 0; | 
 | 289 |     wantfd = -1; | 
 | 290 |   } else { | 
 | 291 |     // Flush the stream; ANSI doesn't require this. | 
 | 292 |     if (fp->_flags & __SWR) __sflush(fp); | 
 | 293 |  | 
 | 294 |     // If close is NULL, closing is a no-op, hence pointless. | 
 | 295 |     isopen = fp->_close != NULL; | 
 | 296 |     if ((wantfd = fp->_file) < 0 && isopen) { | 
 | 297 |         (*fp->_close)(fp->_cookie); | 
 | 298 |         isopen = 0; | 
 | 299 |     } | 
 | 300 |   } | 
 | 301 |  | 
 | 302 |   // Get a new descriptor to refer to the new file. | 
 | 303 |   int fd = open(file, oflags, DEFFILEMODE); | 
 | 304 |   if (fd < 0 && isopen) { | 
 | 305 |     // If out of fd's close the old one and try again. | 
 | 306 |     if (errno == ENFILE || errno == EMFILE) { | 
 | 307 |       (*fp->_close)(fp->_cookie); | 
 | 308 |       isopen = 0; | 
 | 309 |       fd = open(file, oflags, DEFFILEMODE); | 
 | 310 |     } | 
 | 311 |   } | 
 | 312 |  | 
 | 313 |   int sverrno = errno; | 
 | 314 |  | 
 | 315 |   // Finish closing fp.  Even if the open succeeded above, we cannot | 
 | 316 |   // keep fp->_base: it may be the wrong size.  This loses the effect | 
 | 317 |   // of any setbuffer calls, but stdio has always done this before. | 
 | 318 |   if (isopen && fd != wantfd) (*fp->_close)(fp->_cookie); | 
 | 319 |   if (fp->_flags & __SMBF) free(fp->_bf._base); | 
 | 320 |   fp->_w = 0; | 
 | 321 |   fp->_r = 0; | 
 | 322 |   fp->_p = NULL; | 
 | 323 |   fp->_bf._base = NULL; | 
 | 324 |   fp->_bf._size = 0; | 
 | 325 |   fp->_lbfsize = 0; | 
 | 326 |   if (HASUB(fp)) FREEUB(fp); | 
 | 327 |   _UB(fp)._size = 0; | 
 | 328 |   WCIO_FREE(fp); | 
 | 329 |   if (HASLB(fp)) FREELB(fp); | 
 | 330 |   fp->_lb._size = 0; | 
 | 331 |  | 
 | 332 |   if (fd < 0) { // Did not get it after all. | 
 | 333 |     fp->_flags = 0; // Release. | 
 | 334 |     errno = sverrno; // Restore errno in case _close clobbered it. | 
 | 335 |     return nullptr; | 
 | 336 |   } | 
 | 337 |  | 
 | 338 |   // If reopening something that was open before on a real file, try | 
 | 339 |   // to maintain the descriptor.  Various C library routines (perror) | 
 | 340 |   // assume stderr is always fd STDERR_FILENO, even if being freopen'd. | 
 | 341 |   if (wantfd >= 0 && fd != wantfd) { | 
 | 342 |     if (dup3(fd, wantfd, oflags & O_CLOEXEC) >= 0) { | 
 | 343 |       close(fd); | 
 | 344 |       fd = wantfd; | 
 | 345 |     } | 
 | 346 |   } | 
 | 347 |  | 
 | 348 |   // _file is only a short. | 
 | 349 |   if (fd > SHRT_MAX) { | 
 | 350 |       fp->_flags = 0; // Release. | 
 | 351 |       errno = EMFILE; | 
 | 352 |       return nullptr; | 
 | 353 |   } | 
 | 354 |  | 
 | 355 |   fp->_flags = flags; | 
 | 356 |   fp->_file = fd; | 
 | 357 |   fp->_cookie = fp; | 
 | 358 |   fp->_read = __sread; | 
 | 359 |   fp->_write = __swrite; | 
 | 360 |   fp->_close = __sclose; | 
 | 361 |   _EXT(fp)->_seek64 = __sseek64; | 
 | 362 |  | 
 | 363 |   // When opening in append mode, even though we use O_APPEND, | 
 | 364 |   // we need to seek to the end so that ftell() gets the right | 
 | 365 |   // answer.  If the user then alters the seek pointer, or | 
 | 366 |   // the file extends, this will fail, but there is not much | 
 | 367 |   // we can do about this.  (We could set __SAPP and check in | 
 | 368 |   // fseek and ftell.) | 
 | 369 |   if (oflags & O_APPEND) __sseek64(fp, 0, SEEK_END); | 
 | 370 |   return fp; | 
 | 371 | } | 
| Elliott Hughes | f226ee5 | 2016-02-03 11:24:28 -0800 | [diff] [blame] | 372 | __strong_alias(freopen64, freopen); | 
| Elliott Hughes | 023c307 | 2016-01-22 15:04:51 -0800 | [diff] [blame] | 373 |  | 
| Elliott Hughes | 923f165 | 2016-01-19 15:46:05 -0800 | [diff] [blame] | 374 | int fclose(FILE* fp) { | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 375 |   if (fp->_flags == 0) { | 
 | 376 |     // Already freed! | 
 | 377 |     errno = EBADF; | 
 | 378 |     return EOF; | 
 | 379 |   } | 
| Elliott Hughes | 923f165 | 2016-01-19 15:46:05 -0800 | [diff] [blame] | 380 |  | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 381 |   ScopedFileLock sfl(fp); | 
 | 382 |   WCIO_FREE(fp); | 
 | 383 |   int r = fp->_flags & __SWR ? __sflush(fp) : 0; | 
 | 384 |   if (fp->_close != NULL && (*fp->_close)(fp->_cookie) < 0) { | 
 | 385 |     r = EOF; | 
 | 386 |   } | 
 | 387 |   if (fp->_flags & __SMBF) free(fp->_bf._base); | 
 | 388 |   if (HASUB(fp)) FREEUB(fp); | 
 | 389 |   if (HASLB(fp)) FREELB(fp); | 
| Elliott Hughes | 923f165 | 2016-01-19 15:46:05 -0800 | [diff] [blame] | 390 |  | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 391 |   // Poison this FILE so accesses after fclose will be obvious. | 
 | 392 |   fp->_file = -1; | 
 | 393 |   fp->_r = fp->_w = 0; | 
| Elliott Hughes | 923f165 | 2016-01-19 15:46:05 -0800 | [diff] [blame] | 394 |  | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 395 |   // Release this FILE for reuse. | 
 | 396 |   fp->_flags = 0; | 
 | 397 |   return r; | 
| Elliott Hughes | 923f165 | 2016-01-19 15:46:05 -0800 | [diff] [blame] | 398 | } | 
 | 399 |  | 
| Elliott Hughes | cceaf06 | 2016-07-29 16:31:52 -0700 | [diff] [blame] | 400 | int fileno_unlocked(FILE* fp) { | 
 | 401 |   int fd = fp->_file; | 
 | 402 |   if (fd == -1) { | 
 | 403 |     errno = EBADF; | 
 | 404 |     return -1; | 
 | 405 |   } | 
 | 406 |   return fd; | 
 | 407 | } | 
 | 408 |  | 
| Elliott Hughes | 923f165 | 2016-01-19 15:46:05 -0800 | [diff] [blame] | 409 | int fileno(FILE* fp) { | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 410 |   ScopedFileLock sfl(fp); | 
 | 411 |   return fileno_unlocked(fp); | 
| Elliott Hughes | 923f165 | 2016-01-19 15:46:05 -0800 | [diff] [blame] | 412 | } | 
| Elliott Hughes | 021335e | 2016-01-19 16:28:15 -0800 | [diff] [blame] | 413 |  | 
| Elliott Hughes | cceaf06 | 2016-07-29 16:31:52 -0700 | [diff] [blame] | 414 | void clearerr_unlocked(FILE* fp) { | 
 | 415 |   return __sclearerr(fp); | 
 | 416 | } | 
 | 417 |  | 
 | 418 | void clearerr(FILE* fp) { | 
 | 419 |   ScopedFileLock sfl(fp); | 
 | 420 |   clearerr_unlocked(fp); | 
 | 421 | } | 
 | 422 |  | 
 | 423 | int feof_unlocked(FILE* fp) { | 
| Elliott Hughes | 70715da | 2016-08-01 16:35:17 -0700 | [diff] [blame] | 424 |   return ((fp->_flags & __SEOF) != 0); | 
| Elliott Hughes | cceaf06 | 2016-07-29 16:31:52 -0700 | [diff] [blame] | 425 | } | 
 | 426 |  | 
 | 427 | int feof(FILE* fp) { | 
 | 428 |   ScopedFileLock sfl(fp); | 
 | 429 |   return feof_unlocked(fp); | 
 | 430 | } | 
 | 431 |  | 
 | 432 | int ferror_unlocked(FILE* fp) { | 
 | 433 |   return __sferror(fp); | 
 | 434 | } | 
 | 435 |  | 
 | 436 | int ferror(FILE* fp) { | 
 | 437 |   ScopedFileLock sfl(fp); | 
 | 438 |   return ferror_unlocked(fp); | 
 | 439 | } | 
 | 440 |  | 
| Elliott Hughes | 021335e | 2016-01-19 16:28:15 -0800 | [diff] [blame] | 441 | int __sread(void* cookie, char* buf, int n) { | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 442 |   FILE* fp = reinterpret_cast<FILE*>(cookie); | 
 | 443 |   return TEMP_FAILURE_RETRY(read(fp->_file, buf, n)); | 
| Elliott Hughes | 021335e | 2016-01-19 16:28:15 -0800 | [diff] [blame] | 444 | } | 
 | 445 |  | 
 | 446 | int __swrite(void* cookie, const char* buf, int n) { | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 447 |   FILE* fp = reinterpret_cast<FILE*>(cookie); | 
 | 448 |   if (fp->_flags & __SAPP) { | 
 | 449 |     // The FILE* is in append mode, but the underlying fd doesn't have O_APPEND set. | 
 | 450 |     // We need to seek manually. | 
| Elliott Hughes | 023c307 | 2016-01-22 15:04:51 -0800 | [diff] [blame] | 451 |     // TODO: use fcntl(2) to set O_APPEND in fdopen(3) instead? | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 452 |     TEMP_FAILURE_RETRY(lseek64(fp->_file, 0, SEEK_END)); | 
 | 453 |   } | 
 | 454 |   return TEMP_FAILURE_RETRY(write(fp->_file, buf, n)); | 
| Elliott Hughes | 021335e | 2016-01-19 16:28:15 -0800 | [diff] [blame] | 455 | } | 
 | 456 |  | 
| Elliott Hughes | 021335e | 2016-01-19 16:28:15 -0800 | [diff] [blame] | 457 | fpos_t __sseek(void* cookie, fpos_t offset, int whence) { | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 458 |   FILE* fp = reinterpret_cast<FILE*>(cookie); | 
 | 459 |   return TEMP_FAILURE_RETRY(lseek(fp->_file, offset, whence)); | 
| Elliott Hughes | 021335e | 2016-01-19 16:28:15 -0800 | [diff] [blame] | 460 | } | 
 | 461 |  | 
| Elliott Hughes | 023c307 | 2016-01-22 15:04:51 -0800 | [diff] [blame] | 462 | off64_t __sseek64(void* cookie, off64_t offset, int whence) { | 
 | 463 |   FILE* fp = reinterpret_cast<FILE*>(cookie); | 
 | 464 |   return TEMP_FAILURE_RETRY(lseek64(fp->_file, offset, whence)); | 
 | 465 | } | 
 | 466 |  | 
| Elliott Hughes | 021335e | 2016-01-19 16:28:15 -0800 | [diff] [blame] | 467 | int __sclose(void* cookie) { | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 468 |   FILE* fp = reinterpret_cast<FILE*>(cookie); | 
 | 469 |   return close(fp->_file); | 
 | 470 | } | 
 | 471 |  | 
| Elliott Hughes | 023c307 | 2016-01-22 15:04:51 -0800 | [diff] [blame] | 472 | static off64_t __seek_unlocked(FILE* fp, off64_t offset, int whence) { | 
 | 473 |   // Use `_seek64` if set, but fall back to `_seek`. | 
 | 474 |   if (_EXT(fp)->_seek64 != nullptr) { | 
 | 475 |     return (*_EXT(fp)->_seek64)(fp->_cookie, offset, whence); | 
 | 476 |   } else if (fp->_seek != nullptr) { | 
| Elliott Hughes | 955426e | 2016-01-26 18:25:52 -0800 | [diff] [blame] | 477 |     off64_t result = (*fp->_seek)(fp->_cookie, offset, whence); | 
 | 478 | #if !defined(__LP64__) | 
 | 479 |     // Avoid sign extension if off64_t is larger than off_t. | 
 | 480 |     if (result != -1) result &= 0xffffffff; | 
 | 481 | #endif | 
 | 482 |     return result; | 
| Elliott Hughes | 023c307 | 2016-01-22 15:04:51 -0800 | [diff] [blame] | 483 |   } else { | 
 | 484 |     errno = ESPIPE; | 
 | 485 |     return -1; | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 486 |   } | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 487 | } | 
 | 488 |  | 
| Elliott Hughes | 9677fab | 2016-01-25 15:50:59 -0800 | [diff] [blame] | 489 | static off64_t __ftello64_unlocked(FILE* fp) { | 
| Elliott Hughes | 023c307 | 2016-01-22 15:04:51 -0800 | [diff] [blame] | 490 |   // Find offset of underlying I/O object, then adjust for buffered bytes. | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 491 |   __sflush(fp);  // May adjust seek offset on append stream. | 
| Elliott Hughes | 9677fab | 2016-01-25 15:50:59 -0800 | [diff] [blame] | 492 |   off64_t result = __seek_unlocked(fp, 0, SEEK_CUR); | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 493 |   if (result == -1) { | 
 | 494 |     return -1; | 
 | 495 |   } | 
 | 496 |  | 
 | 497 |   if (fp->_flags & __SRD) { | 
 | 498 |     // Reading.  Any unread characters (including | 
 | 499 |     // those from ungetc) cause the position to be | 
 | 500 |     // smaller than that in the underlying object. | 
 | 501 |     result -= fp->_r; | 
 | 502 |     if (HASUB(fp)) result -= fp->_ur; | 
 | 503 |   } else if (fp->_flags & __SWR && fp->_p != NULL) { | 
 | 504 |     // Writing.  Any buffered characters cause the | 
 | 505 |     // position to be greater than that in the | 
 | 506 |     // underlying object. | 
 | 507 |     result += fp->_p - fp->_bf._base; | 
 | 508 |   } | 
 | 509 |   return result; | 
 | 510 | } | 
 | 511 |  | 
| Elliott Hughes | 9677fab | 2016-01-25 15:50:59 -0800 | [diff] [blame] | 512 | int __fseeko64(FILE* fp, off64_t offset, int whence, int off_t_bits) { | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 513 |   ScopedFileLock sfl(fp); | 
 | 514 |  | 
| Elliott Hughes | 023c307 | 2016-01-22 15:04:51 -0800 | [diff] [blame] | 515 |   // Change any SEEK_CUR to SEEK_SET, and check `whence` argument. | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 516 |   // After this, whence is either SEEK_SET or SEEK_END. | 
 | 517 |   if (whence == SEEK_CUR) { | 
| Elliott Hughes | 9677fab | 2016-01-25 15:50:59 -0800 | [diff] [blame] | 518 |     fpos64_t current_offset = __ftello64_unlocked(fp); | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 519 |     if (current_offset == -1) { | 
| Elliott Hughes | 9677fab | 2016-01-25 15:50:59 -0800 | [diff] [blame] | 520 |       return -1; | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 521 |     } | 
 | 522 |     offset += current_offset; | 
 | 523 |     whence = SEEK_SET; | 
 | 524 |   } else if (whence != SEEK_SET && whence != SEEK_END) { | 
 | 525 |     errno = EINVAL; | 
| Elliott Hughes | 9677fab | 2016-01-25 15:50:59 -0800 | [diff] [blame] | 526 |     return -1; | 
 | 527 |   } | 
 | 528 |  | 
 | 529 |   // If our caller has a 32-bit interface, refuse to go past a 32-bit file offset. | 
 | 530 |   if (off_t_bits == 32 && offset > LONG_MAX) { | 
 | 531 |     errno = EOVERFLOW; | 
 | 532 |     return -1; | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 533 |   } | 
 | 534 |  | 
 | 535 |   if (fp->_bf._base == NULL) __smakebuf(fp); | 
 | 536 |  | 
 | 537 |   // Flush unwritten data and attempt the seek. | 
| Elliott Hughes | 023c307 | 2016-01-22 15:04:51 -0800 | [diff] [blame] | 538 |   if (__sflush(fp) || __seek_unlocked(fp, offset, whence) == -1) { | 
| Elliott Hughes | 9677fab | 2016-01-25 15:50:59 -0800 | [diff] [blame] | 539 |     return -1; | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 540 |   } | 
 | 541 |  | 
 | 542 |   // Success: clear EOF indicator and discard ungetc() data. | 
 | 543 |   if (HASUB(fp)) FREEUB(fp); | 
 | 544 |   fp->_p = fp->_bf._base; | 
 | 545 |   fp->_r = 0; | 
 | 546 |   /* fp->_w = 0; */	/* unnecessary (I think...) */ | 
 | 547 |   fp->_flags &= ~__SEOF; | 
 | 548 |   return 0; | 
 | 549 | } | 
 | 550 |  | 
| Elliott Hughes | 9677fab | 2016-01-25 15:50:59 -0800 | [diff] [blame] | 551 | int fseeko(FILE* fp, off_t offset, int whence) { | 
 | 552 |   static_assert(sizeof(off_t) == sizeof(long), "sizeof(off_t) != sizeof(long)"); | 
 | 553 |   return __fseeko64(fp, offset, whence, 8*sizeof(off_t)); | 
 | 554 | } | 
 | 555 | __strong_alias(fseek, fseeko); | 
 | 556 |  | 
 | 557 | int fseeko64(FILE* fp, off64_t offset, int whence) { | 
 | 558 |   return __fseeko64(fp, offset, whence, 8*sizeof(off_t)); | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 559 | } | 
 | 560 |  | 
| Elliott Hughes | 9677fab | 2016-01-25 15:50:59 -0800 | [diff] [blame] | 561 | int fsetpos(FILE* fp, const fpos_t* pos) { | 
 | 562 |   return fseeko(fp, *pos, SEEK_SET); | 
 | 563 | } | 
 | 564 |  | 
 | 565 | int fsetpos64(FILE* fp, const fpos64_t* pos) { | 
 | 566 |   return fseeko64(fp, *pos, SEEK_SET); | 
 | 567 | } | 
 | 568 |  | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 569 | off_t ftello(FILE* fp) { | 
| Elliott Hughes | 9677fab | 2016-01-25 15:50:59 -0800 | [diff] [blame] | 570 |   static_assert(sizeof(off_t) == sizeof(long), "sizeof(off_t) != sizeof(long)"); | 
 | 571 |   off64_t result = ftello64(fp); | 
 | 572 |   if (result > LONG_MAX) { | 
| Elliott Hughes | 2704bd1 | 2016-01-20 17:14:53 -0800 | [diff] [blame] | 573 |     errno = EOVERFLOW; | 
 | 574 |     return -1; | 
 | 575 |   } | 
| Elliott Hughes | 9677fab | 2016-01-25 15:50:59 -0800 | [diff] [blame] | 576 |   return result; | 
 | 577 | } | 
 | 578 | __strong_alias(ftell, ftello); | 
 | 579 |  | 
 | 580 | off64_t ftello64(FILE* fp) { | 
 | 581 |   ScopedFileLock sfl(fp); | 
 | 582 |   return __ftello64_unlocked(fp); | 
| Elliott Hughes | 021335e | 2016-01-19 16:28:15 -0800 | [diff] [blame] | 583 | } | 
| Elliott Hughes | 023c307 | 2016-01-22 15:04:51 -0800 | [diff] [blame] | 584 |  | 
| Elliott Hughes | 023c307 | 2016-01-22 15:04:51 -0800 | [diff] [blame] | 585 | int fgetpos(FILE* fp, fpos_t* pos) { | 
 | 586 |   *pos = ftello(fp); | 
| Elliott Hughes | 955426e | 2016-01-26 18:25:52 -0800 | [diff] [blame] | 587 |   return (*pos == -1) ? -1 : 0; | 
| Elliott Hughes | 023c307 | 2016-01-22 15:04:51 -0800 | [diff] [blame] | 588 | } | 
 | 589 |  | 
| Elliott Hughes | 9677fab | 2016-01-25 15:50:59 -0800 | [diff] [blame] | 590 | int fgetpos64(FILE* fp, fpos64_t* pos) { | 
 | 591 |   *pos = ftello64(fp); | 
| Elliott Hughes | 955426e | 2016-01-26 18:25:52 -0800 | [diff] [blame] | 592 |   return (*pos == -1) ? -1 : 0; | 
| Elliott Hughes | 023c307 | 2016-01-22 15:04:51 -0800 | [diff] [blame] | 593 | } | 
| Elliott Hughes | 03e65eb | 2016-01-26 14:13:04 -0800 | [diff] [blame] | 594 |  | 
 | 595 | static FILE* __funopen(const void* cookie, | 
 | 596 |                        int (*read_fn)(void*, char*, int), | 
 | 597 |                        int (*write_fn)(void*, const char*, int), | 
 | 598 |                        int (*close_fn)(void*)) { | 
 | 599 |   if (read_fn == nullptr && write_fn == nullptr) { | 
 | 600 |     errno = EINVAL; | 
 | 601 |     return nullptr; | 
 | 602 |   } | 
 | 603 |  | 
 | 604 |   FILE* fp = __sfp(); | 
 | 605 |   if (fp == nullptr) return nullptr; | 
 | 606 |  | 
 | 607 |   if (read_fn != nullptr && write_fn != nullptr) { | 
 | 608 |     fp->_flags = __SRW; | 
 | 609 |   } else if (read_fn != nullptr) { | 
 | 610 |     fp->_flags = __SRD; | 
 | 611 |   } else if (write_fn != nullptr) { | 
 | 612 |     fp->_flags = __SWR; | 
 | 613 |   } | 
 | 614 |  | 
 | 615 |   fp->_file = -1; | 
 | 616 |   fp->_cookie = const_cast<void*>(cookie); // The funopen(3) API is incoherent. | 
 | 617 |   fp->_read = read_fn; | 
 | 618 |   fp->_write = write_fn; | 
 | 619 |   fp->_close = close_fn; | 
 | 620 |  | 
 | 621 |   return fp; | 
 | 622 | } | 
 | 623 |  | 
 | 624 | FILE* funopen(const void* cookie, | 
 | 625 |               int (*read_fn)(void*, char*, int), | 
 | 626 |               int (*write_fn)(void*, const char*, int), | 
 | 627 |               fpos_t (*seek_fn)(void*, fpos_t, int), | 
 | 628 |               int (*close_fn)(void*)) { | 
 | 629 |   FILE* fp = __funopen(cookie, read_fn, write_fn, close_fn); | 
 | 630 |   if (fp != nullptr) { | 
 | 631 |     fp->_seek = seek_fn; | 
 | 632 |   } | 
 | 633 |   return fp; | 
 | 634 | } | 
 | 635 |  | 
 | 636 | FILE* funopen64(const void* cookie, | 
 | 637 |                 int (*read_fn)(void*, char*, int), | 
 | 638 |                 int (*write_fn)(void*, const char*, int), | 
 | 639 |                 fpos64_t (*seek_fn)(void*, fpos64_t, int), | 
 | 640 |                 int (*close_fn)(void*)) { | 
 | 641 |   FILE* fp = __funopen(cookie, read_fn, write_fn, close_fn); | 
 | 642 |   if (fp != nullptr) { | 
 | 643 |     _EXT(fp)->_seek64 = seek_fn; | 
 | 644 |   } | 
 | 645 |   return fp; | 
 | 646 | } | 
| Elliott Hughes | 20788ae | 2016-06-09 15:16:32 -0700 | [diff] [blame] | 647 |  | 
| Elliott Hughes | 53cf348 | 2016-08-09 13:06:41 -0700 | [diff] [blame] | 648 | int asprintf(char** s, const char* fmt, ...) { | 
 | 649 |   PRINTF_IMPL(vasprintf(s, fmt, ap)); | 
 | 650 | } | 
 | 651 |  | 
| Elliott Hughes | 20788ae | 2016-06-09 15:16:32 -0700 | [diff] [blame] | 652 | char* ctermid(char* s) { | 
 | 653 |   return s ? strcpy(s, _PATH_TTY) : const_cast<char*>(_PATH_TTY); | 
 | 654 | } | 
| Elliott Hughes | cceaf06 | 2016-07-29 16:31:52 -0700 | [diff] [blame] | 655 |  | 
| Elliott Hughes | 70715da | 2016-08-01 16:35:17 -0700 | [diff] [blame] | 656 | int dprintf(int fd, const char* fmt, ...) { | 
 | 657 |   PRINTF_IMPL(vdprintf(fd, fmt, ap)); | 
 | 658 | } | 
 | 659 |  | 
 | 660 | int fprintf(FILE* fp, const char* fmt, ...) { | 
 | 661 |   PRINTF_IMPL(vfprintf(fp, fmt, ap)); | 
 | 662 | } | 
 | 663 |  | 
| Elliott Hughes | cceaf06 | 2016-07-29 16:31:52 -0700 | [diff] [blame] | 664 | int fgetc(FILE* fp) { | 
 | 665 |   return getc(fp); | 
 | 666 | } | 
 | 667 |  | 
 | 668 | int fputc(int c, FILE* fp) { | 
 | 669 |   return putc(c, fp); | 
 | 670 | } | 
 | 671 |  | 
| Elliott Hughes | 70715da | 2016-08-01 16:35:17 -0700 | [diff] [blame] | 672 | int fscanf(FILE* fp, const char* fmt, ...) { | 
 | 673 |   PRINTF_IMPL(vfscanf(fp, fmt, ap)); | 
 | 674 | } | 
 | 675 |  | 
 | 676 | int fwprintf(FILE* fp, const wchar_t* fmt, ...) { | 
 | 677 |   PRINTF_IMPL(vfwprintf(fp, fmt, ap)); | 
 | 678 | } | 
 | 679 |  | 
 | 680 | int fwscanf(FILE* fp, const wchar_t* fmt, ...) { | 
 | 681 |   PRINTF_IMPL(vfwscanf(fp, fmt, ap)); | 
 | 682 | } | 
 | 683 |  | 
 | 684 | int getc(FILE* fp) { | 
 | 685 |   ScopedFileLock sfl(fp); | 
 | 686 |   return getc_unlocked(fp); | 
 | 687 | } | 
 | 688 |  | 
 | 689 | int getc_unlocked(FILE* fp) { | 
 | 690 |   return __sgetc(fp); | 
 | 691 | } | 
 | 692 |  | 
 | 693 | int getchar_unlocked() { | 
 | 694 |   return getc_unlocked(stdin); | 
 | 695 | } | 
 | 696 |  | 
 | 697 | int getchar() { | 
 | 698 |   return getc(stdin); | 
 | 699 | } | 
 | 700 |  | 
| Elliott Hughes | cceaf06 | 2016-07-29 16:31:52 -0700 | [diff] [blame] | 701 | ssize_t getline(char** buf, size_t* len, FILE* fp) { | 
 | 702 |   return getdelim(buf, len, '\n', fp); | 
 | 703 | } | 
 | 704 |  | 
 | 705 | wint_t getwc(FILE* fp) { | 
 | 706 |   return fgetwc(fp); | 
 | 707 | } | 
 | 708 |  | 
 | 709 | wint_t getwchar() { | 
 | 710 |   return fgetwc(stdin); | 
 | 711 | } | 
 | 712 |  | 
| Elliott Hughes | 70715da | 2016-08-01 16:35:17 -0700 | [diff] [blame] | 713 | int printf(const char* fmt, ...) { | 
 | 714 |   PRINTF_IMPL(vfprintf(stdout, fmt, ap)); | 
 | 715 | } | 
 | 716 |  | 
 | 717 | int putc(int c, FILE* fp) { | 
 | 718 |   ScopedFileLock sfl(fp); | 
 | 719 |   return putc_unlocked(c, fp); | 
 | 720 | } | 
 | 721 |  | 
 | 722 | int putc_unlocked(int c, FILE* fp) { | 
 | 723 |   if (cantwrite(fp)) { | 
 | 724 |     errno = EBADF; | 
 | 725 |     return EOF; | 
 | 726 |   } | 
 | 727 |   _SET_ORIENTATION(fp, -1); | 
 | 728 |   if (--fp->_w >= 0 || (fp->_w >= fp->_lbfsize && c != '\n')) { | 
 | 729 |     return (*fp->_p++ = c); | 
 | 730 |   } | 
 | 731 |   return (__swbuf(c, fp)); | 
 | 732 | } | 
 | 733 |  | 
 | 734 | int putchar(int c) { | 
 | 735 |   return putc(c, stdout); | 
 | 736 | } | 
 | 737 |  | 
 | 738 | int putchar_unlocked(int c) { | 
 | 739 |   return putc_unlocked(c, stdout); | 
 | 740 | } | 
 | 741 |  | 
| Elliott Hughes | cceaf06 | 2016-07-29 16:31:52 -0700 | [diff] [blame] | 742 | wint_t putwc(wchar_t wc, FILE* fp) { | 
 | 743 |   return fputwc(wc, fp); | 
 | 744 | } | 
 | 745 |  | 
 | 746 | wint_t putwchar(wchar_t wc) { | 
 | 747 |   return fputwc(wc, stdout); | 
 | 748 | } | 
 | 749 |  | 
| Elliott Hughes | d1f25a7 | 2016-08-05 15:53:03 -0700 | [diff] [blame] | 750 | int remove(const char* path) { | 
 | 751 |   if (unlink(path) != -1) return 0; | 
 | 752 |   if (errno != EISDIR) return -1; | 
 | 753 |   return rmdir(path); | 
 | 754 | } | 
 | 755 |  | 
| Elliott Hughes | cceaf06 | 2016-07-29 16:31:52 -0700 | [diff] [blame] | 756 | void rewind(FILE* fp) { | 
 | 757 |   ScopedFileLock sfl(fp); | 
 | 758 |   fseek(fp, 0, SEEK_SET); | 
 | 759 |   clearerr_unlocked(fp); | 
 | 760 | } | 
 | 761 |  | 
| Elliott Hughes | 70715da | 2016-08-01 16:35:17 -0700 | [diff] [blame] | 762 | int scanf(const char* fmt, ...) { | 
 | 763 |   PRINTF_IMPL(vfscanf(stdin, fmt, ap)); | 
 | 764 | } | 
 | 765 |  | 
| Elliott Hughes | cceaf06 | 2016-07-29 16:31:52 -0700 | [diff] [blame] | 766 | void setbuf(FILE* fp, char* buf) { | 
 | 767 |   setbuffer(fp, buf, BUFSIZ); | 
 | 768 | } | 
 | 769 |  | 
 | 770 | void setbuffer(FILE* fp, char* buf, int size) { | 
 | 771 |   setvbuf(fp, buf, buf ? _IOFBF : _IONBF, size); | 
 | 772 | } | 
 | 773 |  | 
 | 774 | int setlinebuf(FILE* fp) { | 
 | 775 |   return setvbuf(fp, nullptr, _IOLBF, 0); | 
 | 776 | } | 
 | 777 |  | 
| Elliott Hughes | 53cf348 | 2016-08-09 13:06:41 -0700 | [diff] [blame] | 778 | int snprintf(char* s, size_t n, const char* fmt, ...) { | 
 | 779 |   PRINTF_IMPL(vsnprintf(s, n, fmt, ap)); | 
 | 780 | } | 
 | 781 |  | 
 | 782 | int sprintf(char* s, const char* fmt, ...) { | 
| Elliott Hughes | fb3873d | 2016-08-10 11:07:54 -0700 | [diff] [blame] | 783 |   PRINTF_IMPL(vsprintf(s, fmt, ap)); | 
| Elliott Hughes | 53cf348 | 2016-08-09 13:06:41 -0700 | [diff] [blame] | 784 | } | 
 | 785 |  | 
 | 786 | int sscanf(const char* s, const char* fmt, ...) { | 
 | 787 |   PRINTF_IMPL(vsscanf(s, fmt, ap)); | 
 | 788 | } | 
 | 789 |  | 
| Elliott Hughes | 70715da | 2016-08-01 16:35:17 -0700 | [diff] [blame] | 790 | int swprintf(wchar_t* s, size_t n, const wchar_t* fmt, ...) { | 
 | 791 |   PRINTF_IMPL(vswprintf(s, n, fmt, ap)); | 
 | 792 | } | 
 | 793 |  | 
 | 794 | int swscanf(const wchar_t* s, const wchar_t* fmt, ...) { | 
 | 795 |   PRINTF_IMPL(vswscanf(s, fmt, ap)); | 
 | 796 | } | 
 | 797 |  | 
| Elliott Hughes | cceaf06 | 2016-07-29 16:31:52 -0700 | [diff] [blame] | 798 | int vprintf(const char* fmt, va_list ap) { | 
 | 799 |   return vfprintf(stdout, fmt, ap); | 
 | 800 | } | 
 | 801 |  | 
 | 802 | int vscanf(const char* fmt, va_list ap) { | 
 | 803 |   return vfscanf(stdin, fmt, ap); | 
 | 804 | } | 
 | 805 |  | 
| Elliott Hughes | fb3873d | 2016-08-10 11:07:54 -0700 | [diff] [blame] | 806 | int vsnprintf(char* s, size_t n, const char* fmt, va_list ap) { | 
 | 807 |   // stdio internals use int rather than size_t. | 
 | 808 |   static_assert(INT_MAX <= SSIZE_MAX, "SSIZE_MAX too large to fit in int"); | 
 | 809 |  | 
 | 810 |   __check_count("vsnprintf", "size", n); | 
 | 811 |  | 
 | 812 |   // Stdio internals do not deal correctly with zero length buffer. | 
 | 813 |   char dummy; | 
 | 814 |   if (n == 0) { | 
 | 815 |     s = &dummy; | 
 | 816 |     n = 1; | 
 | 817 |   } | 
 | 818 |  | 
 | 819 |   FILE f; | 
 | 820 |   __sfileext fext; | 
 | 821 |   _FILEEXT_SETUP(&f, &fext); | 
 | 822 |   f._file = -1; | 
 | 823 |   f._flags = __SWR | __SSTR; | 
 | 824 |   f._bf._base = f._p = reinterpret_cast<unsigned char*>(s); | 
 | 825 |   f._bf._size = f._w = n - 1; | 
 | 826 |  | 
 | 827 |   int result = __vfprintf(&f, fmt, ap); | 
 | 828 |   *f._p = '\0'; | 
 | 829 |   return result; | 
 | 830 | } | 
 | 831 |  | 
| Elliott Hughes | 53cf348 | 2016-08-09 13:06:41 -0700 | [diff] [blame] | 832 | int vsprintf(char* s, const char* fmt, va_list ap) { | 
| Elliott Hughes | fb3873d | 2016-08-10 11:07:54 -0700 | [diff] [blame] | 833 |   return vsnprintf(s, SSIZE_MAX, fmt, ap); | 
| Elliott Hughes | 53cf348 | 2016-08-09 13:06:41 -0700 | [diff] [blame] | 834 | } | 
 | 835 |  | 
| Elliott Hughes | cceaf06 | 2016-07-29 16:31:52 -0700 | [diff] [blame] | 836 | int vwprintf(const wchar_t* fmt, va_list ap) { | 
 | 837 |   return vfwprintf(stdout, fmt, ap); | 
 | 838 | } | 
 | 839 |  | 
 | 840 | int vwscanf(const wchar_t* fmt, va_list ap) { | 
 | 841 |   return vfwscanf(stdin, fmt, ap); | 
 | 842 | } | 
| Elliott Hughes | 70715da | 2016-08-01 16:35:17 -0700 | [diff] [blame] | 843 |  | 
 | 844 | int wprintf(const wchar_t* fmt, ...) { | 
 | 845 |   PRINTF_IMPL(vfwprintf(stdout, fmt, ap)); | 
 | 846 | } | 
 | 847 |  | 
 | 848 | int wscanf(const wchar_t* fmt, ...) { | 
 | 849 |   PRINTF_IMPL(vfwscanf(stdin, fmt, ap)); | 
 | 850 | } |