1 /*
2  * Copyright (c) 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * and/or other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 /* No user fns here.  Pesch 15apr92. */
18 
19 #define _DEFAULT_SOURCE
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <errno.h>
23 #include <string.h>
24 #include <fcntl.h>
25 #include <sys/lock.h>
26 #include "local.h"
27 
28 void (*__stdio_exit_handler) (void);
29 
30 __FILE __sf[3];
31 
32 struct _glue __sglue = {NULL, 3, &__sf[0]};
33 
34 #ifdef _REENT_THREAD_LOCAL
35 NEWLIB_THREAD_LOCAL __FILE *_tls_stdin = &__sf[0];
36 NEWLIB_THREAD_LOCAL __FILE *_tls_stdout = &__sf[1];
37 NEWLIB_THREAD_LOCAL __FILE *_tls_stderr = &__sf[2];
38 NEWLIB_THREAD_LOCAL void (*_tls_cleanup)(void);
39 #endif
40 
41 #ifdef _STDIO_BSD_SEMANTICS
42   /* BSD and Glibc systems only flush streams which have been written to
43      at exit time.  Calling flush rather than close for speed, as on
44      the aforementioned systems. */
45 #define CLEANUP_FILE __sflushw_r
46 #else
47   /* Otherwise close files and flush read streams, too.
48      Note we call flush directly if "--enable-lite-exit" is in effect.  */
49 #ifdef _LITE_EXIT
50 #define CLEANUP_FILE fflush
51 #else
52 #define CLEANUP_FILE fclose
53 #endif
54 #endif
55 
56 #if (defined (__OPTIMIZE_SIZE__) || defined (PREFER_SIZE_OVER_SPEED))
57 _NOINLINE_STATIC void
58 #else
59 static void
60 #endif
std(FILE * ptr,int flags,int file)61 std (FILE *ptr,
62             int flags,
63             int file)
64 {
65   ptr->_p = 0;
66   ptr->_r = 0;
67   ptr->_w = 0;
68   ptr->_flags = flags;
69   ptr->_flags2 = 0;
70   ptr->_file = file;
71   ptr->_bf._base = 0;
72   ptr->_bf._size = 0;
73   ptr->_lbfsize = 0;
74   memset (&ptr->_mbstate, 0, sizeof (_mbstate_t));
75   ptr->_cookie = ptr;
76   ptr->_read = __sread;
77 #ifndef __LARGE64_FILES
78   ptr->_write = __swrite;
79 #else /* __LARGE64_FILES */
80   ptr->_write = __swrite64;
81   ptr->_seek64 = __sseek64;
82   ptr->_flags |= __SL64;
83 #endif /* __LARGE64_FILES */
84   ptr->_seek = __sseek;
85 #ifdef _STDIO_CLOSE_PER_REENT_STD_STREAMS
86   ptr->_close = __sclose;
87 #else /* _STDIO_CLOSE_STD_STREAMS */
88   ptr->_close = NULL;
89 #endif /* _STDIO_CLOSE_STD_STREAMS */
90 #ifndef __SINGLE_THREAD__
91   if (ptr == &__sf[0] || ptr == &__sf[1] || ptr == &__sf[2])
92     __lock_init_recursive (ptr->_lock);
93 #endif
94 #ifdef __SCLE
95   if (__stextmode (ptr->_file))
96     ptr->_flags |= __SCLE;
97 #endif
98 }
99 
100 static inline void
stdin_init(FILE * ptr)101 stdin_init(FILE *ptr)
102 {
103   std (ptr,  __SRD, 0);
104 }
105 
106 static inline void
stdout_init(FILE * ptr)107 stdout_init(FILE *ptr)
108 {
109   /* On platforms that have true file system I/O, we can verify
110      whether stdout is an interactive terminal or not, as part of
111      __smakebuf on first use of the stream.  For all other platforms,
112      we will default to line buffered mode here.  Technically, POSIX
113      requires both stdin and stdout to be line-buffered, but tradition
114      leaves stdin alone on systems without fcntl.  */
115 #ifdef _HAVE_FCNTL
116   std (ptr, __SWR, 1);
117 #else
118   std (ptr, __SWR | __SLBF, 1);
119 #endif
120 }
121 
122 static inline void
stderr_init(FILE * ptr)123 stderr_init(FILE *ptr)
124 {
125   /* POSIX requires stderr to be opened for reading and writing, even
126      when the underlying fd 2 is write-only.  */
127   std (ptr, __SRW | __SNBF, 2);
128 }
129 
130 #ifdef __GNUC__
131 #pragma GCC diagnostic ignored "-Wpragmas"
132 #pragma GCC diagnostic ignored "-Wunknown-warning-option"
133 #pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak"
134 #endif
135 
136 struct glue_with_files {
137   struct _glue glue;
138   FILE file[];
139 };
140 
141 static struct _glue *
sfmoreglue(int n)142 sfmoreglue (int n)
143 {
144   struct glue_with_files *g;
145 
146   g = (struct glue_with_files *)
147     malloc (sizeof (*g) + n * sizeof (FILE));
148   if (g == NULL)
149     return NULL;
150   g->glue._next = NULL;
151   g->glue._niobs = n;
152   g->glue._iobs = g->file;
153   memset (g->file, 0, n * sizeof (FILE));
154   return &g->glue;
155 }
156 
157 static void
stdio_exit_handler(void)158 stdio_exit_handler (void)
159 {
160   (void) _fwalk_sglue (CLEANUP_FILE, &__sglue);
161 }
162 
163 static void
global_stdio_init(void)164 global_stdio_init (void)
165 {
166   if (__stdio_exit_handler == NULL) {
167     __stdio_exit_handler = stdio_exit_handler;
168     stdin_init (&__sf[0]);
169     stdout_init (&__sf[1]);
170     stderr_init (&__sf[2]);
171   }
172 }
173 
174 /*
175  * Find a free FILE for fopen et al.
176  */
177 
178 FILE *
__sfp(void)179 __sfp (void)
180 {
181   FILE *fp;
182   int n;
183   struct _glue *g;
184 
185   _newlib_sfp_lock_start ();
186   global_stdio_init ();
187 
188   for (g = &__sglue;; g = g->_next)
189     {
190       for (fp = g->_iobs, n = g->_niobs; --n >= 0; fp++)
191 	if (fp->_flags == 0)
192 	  goto found;
193       if (g->_next == NULL &&
194 	  (g->_next = sfmoreglue (NDYNAMIC)) == NULL)
195 	break;
196     }
197   _newlib_sfp_lock_exit ();
198   _REENT_ERRNO(d) = ENOMEM;
199   return NULL;
200 
201 found:
202   fp->_file = -1;		/* no file */
203   fp->_flags = 1;		/* reserve this slot; caller sets real flags */
204   fp->_flags2 = 0;
205 #ifndef __SINGLE_THREAD__
206   __lock_init_recursive (fp->_lock);
207 #endif
208   _newlib_sfp_lock_end ();
209 
210   fp->_p = NULL;		/* no current pointer */
211   fp->_w = 0;			/* nothing to read or write */
212   fp->_r = 0;
213   fp->_bf._base = NULL;		/* no buffer */
214   fp->_bf._size = 0;
215   fp->_lbfsize = 0;		/* not line buffered */
216   memset (&fp->_mbstate, 0, sizeof (_mbstate_t));
217   /* fp->_cookie = <any>; */	/* caller sets cookie, _read/_write etc */
218   fp->_ub._base = NULL;		/* no ungetc buffer */
219   fp->_ub._size = 0;
220   fp->_lb._base = NULL;		/* no line buffer */
221   fp->_lb._size = 0;
222 
223   return fp;
224 }
225 
226 /*
227  * exit() calls _cleanup() through *__cleanup, set whenever we
228  * open or buffer a file.  This chicanery is done so that programs
229  * that do not use stdio need not link it all in.
230  *
231  * The name `_cleanup' is, alas, fairly well known outside stdio.
232  */
233 
234 static void
cleanup_stdio(void)235 cleanup_stdio (void)
236 {
237   if (_REENT_STDIN(ptr) != &__sf[0])
238     CLEANUP_FILE (_REENT_STDIN(ptr));
239   if (_REENT_STDOUT(ptr) != &__sf[1])
240     CLEANUP_FILE (_REENT_STDOUT(ptr));
241   if (_REENT_STDERR(ptr) != &__sf[2])
242     CLEANUP_FILE (_REENT_STDERR(ptr));
243 }
244 
245 /*
246  * __sinit() is called whenever stdio's internal variables must be set up.
247  */
248 
249 void
__sinit(void)250 __sinit (void)
251 {
252   __sfp_lock_acquire ();
253 
254   if (_REENT_CLEANUP(s))
255     {
256       __sfp_lock_release ();
257       return;
258     }
259 
260   /* make sure we clean up on exit */
261   _REENT_CLEANUP(s) = cleanup_stdio;	/* conservative */
262 
263   global_stdio_init ();
264   __sfp_lock_release ();
265 }
266