1/* $NetBSD: freopen.c,v 1.16 2010/01/11 20:39:29 joerg Exp $ */ 2 3/*- 4 * Copyright (c) 1990, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Chris Torek. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35#include <sys/cdefs.h> 36#if defined(LIBC_SCCS) && !defined(lint) 37#if 0 38static char sccsid[] = "@(#)freopen.c 8.1 (Berkeley) 6/4/93"; 39#else 40__RCSID("$NetBSD: freopen.c,v 1.16 2010/01/11 20:39:29 joerg Exp $"); 41#endif 42#endif /* LIBC_SCCS and not lint */ 43 44#include <sys/types.h> 45#include <sys/stat.h> 46 47#include <assert.h> 48#include <errno.h> 49#include <fcntl.h> 50#include <unistd.h> 51#include <stdio.h> 52#include <stdlib.h> 53#include <wchar.h> 54#include <limits.h> 55#include "reentrant.h" 56#include "local.h" 57 58/* 59 * Re-direct an existing, open (probably) file to some other file. 60 * ANSI is written such that the original file gets closed if at 61 * all possible, no matter what. 62 */ 63FILE * 64freopen(file, mode, fp) 65 const char *file, *mode; 66 FILE *fp; 67{ 68 int f; 69 int flags, isopen, oflags, sverrno, wantfd; 70 71 _DIAGASSERT(file != NULL); 72 _DIAGASSERT(mode != NULL); 73 _DIAGASSERT(fp != NULL); 74 75 if ((flags = __sflags(mode, &oflags)) == 0) { 76 (void) fclose(fp); 77 return (NULL); 78 } 79 80 if (!__sdidinit) 81 __sinit(); 82 83 /* 84 * There are actually programs that depend on being able to "freopen" 85 * descriptors that weren't originally open. Keep this from breaking. 86 * Remember whether the stream was open to begin with, and which file 87 * descriptor (if any) was associated with it. If it was attached to 88 * a descriptor, defer closing it; freopen("/dev/stdin", "r", stdin) 89 * should work. This is unnecessary if it was not a Unix file. 90 */ 91 if (fp->_flags == 0) { 92 fp->_flags = __SEOF; /* hold on to it */ 93 isopen = 0; 94 wantfd = -1; 95 } else { 96 /* flush the stream; ANSI doesn't require this. */ 97 if (fp->_flags & __SWR) 98 (void) __sflush(fp); 99 /* if close is NULL, closing is a no-op, hence pointless */ 100 isopen = fp->_close != NULL; 101 if ((wantfd = __sfileno(fp)) == -1 && isopen) { 102 (void) (*fp->_close)(fp->_cookie); 103 isopen = 0; 104 } 105 } 106 107 /* Get a new descriptor to refer to the new file. */ 108 f = open(file, oflags, DEFFILEMODE); 109 if (f < 0 && isopen) { 110 /* If out of fd's close the old one and try again. */ 111 if (errno == ENFILE || errno == EMFILE) { 112 (void) (*fp->_close)(fp->_cookie); 113 isopen = 0; 114 f = open(file, oflags, DEFFILEMODE); 115 } 116 } 117 sverrno = errno; 118 119 /* 120 * Finish closing fp. Even if the open succeeded above, we cannot 121 * keep fp->_base: it may be the wrong size. This loses the effect 122 * of any setbuffer calls, but stdio has always done this before. 123 */ 124 if (isopen && f != wantfd) 125 (void) (*fp->_close)(fp->_cookie); 126 if (fp->_flags & __SMBF) 127 free((char *)fp->_bf._base); 128 fp->_w = 0; 129 fp->_r = 0; 130 fp->_p = NULL; 131 fp->_bf._base = NULL; 132 fp->_bf._size = 0; 133 fp->_lbfsize = 0; 134 if (HASUB(fp)) 135 FREEUB(fp); 136 WCIO_FREE(fp); 137 _UB(fp)._size = 0; 138 FREELB(fp); 139 140 if (f < 0) { /* did not get it after all */ 141 fp->_flags = 0; /* set it free */ 142 errno = sverrno; /* restore in case _close clobbered */ 143 return (NULL); 144 } 145 146 if (oflags & O_NONBLOCK) { 147 struct stat st; 148 if (fstat(f, &st) == -1) { 149 sverrno = errno; 150 (void)close(f); 151 errno = sverrno; 152 return (NULL); 153 } 154 if (!S_ISREG(st.st_mode)) { 155 (void)close(f); 156 errno = EFTYPE; 157 return (NULL); 158 } 159 } 160 161 /* 162 * If reopening something that was open before on a real file, try 163 * to maintain the descriptor. Various C library routines (perror) 164 * assume stderr is always fd STDERR_FILENO, even if being freopen'd. 165 */ 166 if (wantfd >= 0 && f != wantfd) { 167 if (dup2(f, wantfd) >= 0) { 168 (void) close(f); 169 f = wantfd; 170 } 171 } 172 173 /* 174 * File descriptors are a full int, but _file is only a short. 175 * If we get a valid file descriptor that is greater or equal to 176 * USHRT_MAX, then the fd will get sign-extended into an 177 * invalid file descriptor. Handle this case by failing the 178 * open. (We treat the short as unsigned, and special-case -1). 179 */ 180 if (f >= USHRT_MAX) { 181 (void)close(f); 182 errno = EMFILE; 183 return NULL; 184 } 185 186 fp->_flags = flags; 187 fp->_file = f; 188 fp->_cookie = fp; 189 fp->_read = __sread; 190 fp->_write = __swrite; 191 fp->_seek = __sseek; 192 fp->_close = __sclose; 193 194 /* 195 * When reopening in append mode, even though we use O_APPEND, 196 * we need to seek to the end so that ftell() gets the right 197 * answer. If the user then alters the seek pointer, or 198 * the file extends, this will fail, but there is not much 199 * we can do about this. (We could set __SAPP and check in 200 * fseek and ftell.) 201 */ 202 if (oflags & O_APPEND) 203 (void) __sseek((void *)fp, (off_t)0, SEEK_END); 204 return (fp); 205} 206