xref: /original-bsd/lib/libc/stdio/freopen.c (revision c8876cb1)
1 /*-
2  * Copyright (c) 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Chris Torek.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #if defined(LIBC_SCCS) && !defined(lint)
12 static char sccsid[] = "@(#)freopen.c	5.4 (Berkeley) 02/01/91";
13 #endif /* LIBC_SCCS and not lint */
14 
15 #include <sys/types.h>
16 #include <sys/file.h>
17 #include <sys/stat.h>
18 #include <errno.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include "local.h"
22 
23 /*
24  * Re-direct an existing, open (probably) file to some other file.
25  * ANSI is written such that the original file gets closed if at
26  * all possible, no matter what.
27  */
28 FILE *
29 freopen(file, mode, fp)
30 	char *file, *mode;
31 	register FILE *fp;
32 {
33 	register int f;
34 	int flags, isopen, oflags, sverrno, wantfd;
35 
36 	if ((flags = __sflags(mode, &oflags)) == 0) {
37 		(void) fclose(fp);
38 		return (NULL);
39 	}
40 
41 	if (!__sdidinit)
42 		__sinit();
43 
44 	/*
45 	 * There are actually programs that depend on being able to "freopen"
46 	 * descriptors that weren't originally open.  Keep this from breaking.
47 	 * Remember whether the stream was open to begin with, and which file
48 	 * descriptor (if any) was associated with it.  If it was attached to
49 	 * a descriptor, defer closing it; freopen("/dev/stdin", "r", stdin)
50 	 * should work.  This is unnecessary if it was not a Unix file.
51 	 */
52 	if (fp->_flags == 0) {
53 		fp->_flags = __SEOF;	/* hold on to it */
54 		isopen = 0;
55 		wantfd = -1;
56 	} else {
57 		/* flush the stream; ANSI doesn't require this. */
58 		if (fp->_flags & __SWR)
59 			(void) __sflush(fp);
60 		/* if close is NULL, closing is a no-op, hence pointless */
61 		isopen = fp->_close != NULL;
62 		if ((wantfd = fp->_file) < 0 && isopen) {
63 			(void) (*fp->_close)(fp->_cookie);
64 			isopen = 0;
65 		}
66 	}
67 
68 	/* Get a new descriptor to refer to the new file. */
69 	f = open(file, oflags, DEFFILEMODE);
70 	if (f < 0 && isopen) {
71 		/* If out of fd's close the old one and try again. */
72 		if (errno == ENFILE || errno == EMFILE) {
73 			(void) (*fp->_close)(fp->_cookie);
74 			isopen = 0;
75 			f = open(file, oflags, DEFFILEMODE);
76 		}
77 	}
78 	sverrno = errno;
79 
80 	/*
81 	 * Finish closing fp.  Even if the open succeeded above, we cannot
82 	 * keep fp->_base: it may be the wrong size.  This loses the effect
83 	 * of any setbuffer calls, but stdio has always done this before.
84 	 */
85 	if (isopen)
86 		(void) (*fp->_close)(fp->_cookie);
87 	if (fp->_flags & __SMBF)
88 		free((char *)fp->_bf._base);
89 	fp->_w = 0;
90 	fp->_r = 0;
91 	fp->_p = NULL;
92 	fp->_bf._base = NULL;
93 	fp->_bf._size = 0;
94 	fp->_lbfsize = 0;
95 	if (HASUB(fp))
96 		FREEUB(fp);
97 	fp->_ub._size = 0;
98 	if (HASLB(fp))
99 		FREELB(fp);
100 	fp->_lb._size = 0;
101 
102 	if (f < 0) {			/* did not get it after all */
103 		fp->_flags = 0;		/* set it free */
104 		errno = sverrno;	/* restore in case _close clobbered */
105 		return (NULL);
106 	}
107 
108 	/*
109 	 * If reopening something that was open before on a real file, try
110 	 * to maintain the descriptor.  Various C library routines (perror)
111 	 * assume stderr is always fd STDERR_FILENO, even if being freopen'd.
112 	 */
113 	if (wantfd >= 0 && f != wantfd) {
114 		if (dup2(f, wantfd) >= 0) {
115 			(void) close(f);
116 			f = wantfd;
117 		}
118 	}
119 
120 	fp->_flags = flags;
121 	fp->_file = f;
122 	fp->_cookie = fp;
123 	fp->_read = __sread;
124 	fp->_write = __swrite;
125 	fp->_seek = __sseek;
126 	fp->_close = __sclose;
127 	return (fp);
128 }
129