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 <_ansi.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <fcntl.h>
26 #include <sys/lock.h>
27 #include "local.h"
28 
29 void (*__stdio_exit_handler) (void);
30 
31 __FILE __sf[3];
32 
33 struct _glue __sglue = {NULL, 3, &__sf[0]};
34 
35 #ifdef _REENT_THREAD_LOCAL
36 NEWLIB_THREAD_LOCAL __FILE *_tls_stdin = &__sf[0];
37 NEWLIB_THREAD_LOCAL __FILE *_tls_stdout = &__sf[1];
38 NEWLIB_THREAD_LOCAL __FILE *_tls_stderr = &__sf[2];
39 NEWLIB_THREAD_LOCAL void (*_tls_cleanup)(void);
40 #endif
41 
42 #ifdef _STDIO_BSD_SEMANTICS
43   /* BSD and Glibc systems only flush streams which have been written to
44      at exit time.  Calling flush rather than close for speed, as on
45      the aforementioned systems. */
46 #define CLEANUP_FILE __sflushw_r
47 #else
48   /* Otherwise close files and flush read streams, too.
49      Note we call flush directly if "--enable-lite-exit" is in effect.  */
50 #ifdef _LITE_EXIT
51 #define CLEANUP_FILE fflush
52 #else
53 #define CLEANUP_FILE fclose
54 #endif
55 #endif
56 
57 #if (defined (__OPTIMIZE_SIZE__) || defined (PREFER_SIZE_OVER_SPEED))
58 _NOINLINE_STATIC void
59 #else
60 static void
61 #endif
std(FILE * ptr,int flags,int file)62 std (FILE *ptr,
63             int flags,
64             int file)
65 {
66   ptr->_p = 0;
67   ptr->_r = 0;
68   ptr->_w = 0;
69   ptr->_flags = flags;
70   ptr->_flags2 = 0;
71   ptr->_file = file;
72   ptr->_bf._base = 0;
73   ptr->_bf._size = 0;
74   ptr->_lbfsize = 0;
75   memset (&ptr->_mbstate, 0, sizeof (_mbstate_t));
76   ptr->_cookie = ptr;
77   ptr->_read = __sread;
78 #ifndef __LARGE64_FILES
79   ptr->_write = __swrite;
80 #else /* __LARGE64_FILES */
81   ptr->_write = __swrite64;
82   ptr->_seek64 = __sseek64;
83   ptr->_flags |= __SL64;
84 #endif /* __LARGE64_FILES */
85   ptr->_seek = __sseek;
86 #ifdef _STDIO_CLOSE_PER_REENT_STD_STREAMS
87   ptr->_close = __sclose;
88 #else /* _STDIO_CLOSE_STD_STREAMS */
89   ptr->_close = NULL;
90 #endif /* _STDIO_CLOSE_STD_STREAMS */
91 #ifndef __SINGLE_THREAD__
92   if (ptr == &__sf[0] || ptr == &__sf[1] || ptr == &__sf[2])
93     __lock_init_recursive (ptr->_lock);
94 #endif
95 #ifdef __SCLE
96   if (__stextmode (ptr->_file))
97     ptr->_flags |= __SCLE;
98 #endif
99 }
100 
101 static inline void
stdin_init(FILE * ptr)102 stdin_init(FILE *ptr)
103 {
104   std (ptr,  __SRD, 0);
105 }
106 
107 static inline void
stdout_init(FILE * ptr)108 stdout_init(FILE *ptr)
109 {
110   /* On platforms that have true file system I/O, we can verify
111      whether stdout is an interactive terminal or not, as part of
112      __smakebuf on first use of the stream.  For all other platforms,
113      we will default to line buffered mode here.  Technically, POSIX
114      requires both stdin and stdout to be line-buffered, but tradition
115      leaves stdin alone on systems without fcntl.  */
116 #ifdef _HAVE_FCNTL
117   std (ptr, __SWR, 1);
118 #else
119   std (ptr, __SWR | __SLBF, 1);
120 #endif
121 }
122 
123 static inline void
stderr_init(FILE * ptr)124 stderr_init(FILE *ptr)
125 {
126   /* POSIX requires stderr to be opened for reading and writing, even
127      when the underlying fd 2 is write-only.  */
128   std (ptr, __SRW | __SNBF, 2);
129 }
130 
131 #ifdef __GNUC__
132 #pragma GCC diagnostic ignored "-Wpragmas"
133 #pragma GCC diagnostic ignored "-Wunknown-warning-option"
134 #pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak"
135 #endif
136 
137 struct glue_with_files {
138   struct _glue glue;
139   FILE file[];
140 };
141 
142 static struct _glue *
sfmoreglue(int n)143 sfmoreglue (int n)
144 {
145   struct glue_with_files *g;
146 
147   g = (struct glue_with_files *)
148     malloc (sizeof (*g) + n * sizeof (FILE));
149   if (g == NULL)
150     return NULL;
151   g->glue._next = NULL;
152   g->glue._niobs = n;
153   g->glue._iobs = g->file;
154   memset (g->file, 0, n * sizeof (FILE));
155   return &g->glue;
156 }
157 
158 static void
stdio_exit_handler(void)159 stdio_exit_handler (void)
160 {
161   (void) _fwalk_sglue (CLEANUP_FILE, &__sglue);
162 }
163 
164 static void
global_stdio_init(void)165 global_stdio_init (void)
166 {
167   if (__stdio_exit_handler == NULL) {
168     __stdio_exit_handler = stdio_exit_handler;
169     stdin_init (&__sf[0]);
170     stdout_init (&__sf[1]);
171     stderr_init (&__sf[2]);
172   }
173 }
174 
175 /*
176  * Find a free FILE for fopen et al.
177  */
178 
179 FILE *
__sfp(void)180 __sfp (void)
181 {
182   FILE *fp;
183   int n;
184   struct _glue *g;
185 
186   _newlib_sfp_lock_start ();
187   global_stdio_init ();
188 
189   for (g = &__sglue;; g = g->_next)
190     {
191       for (fp = g->_iobs, n = g->_niobs; --n >= 0; fp++)
192 	if (fp->_flags == 0)
193 	  goto found;
194       if (g->_next == NULL &&
195 	  (g->_next = sfmoreglue (NDYNAMIC)) == NULL)
196 	break;
197     }
198   _newlib_sfp_lock_exit ();
199   _REENT_ERRNO(d) = ENOMEM;
200   return NULL;
201 
202 found:
203   fp->_file = -1;		/* no file */
204   fp->_flags = 1;		/* reserve this slot; caller sets real flags */
205   fp->_flags2 = 0;
206 #ifndef __SINGLE_THREAD__
207   __lock_init_recursive (fp->_lock);
208 #endif
209   _newlib_sfp_lock_end ();
210 
211   fp->_p = NULL;		/* no current pointer */
212   fp->_w = 0;			/* nothing to read or write */
213   fp->_r = 0;
214   fp->_bf._base = NULL;		/* no buffer */
215   fp->_bf._size = 0;
216   fp->_lbfsize = 0;		/* not line buffered */
217   memset (&fp->_mbstate, 0, sizeof (_mbstate_t));
218   /* fp->_cookie = <any>; */	/* caller sets cookie, _read/_write etc */
219   fp->_ub._base = NULL;		/* no ungetc buffer */
220   fp->_ub._size = 0;
221   fp->_lb._base = NULL;		/* no line buffer */
222   fp->_lb._size = 0;
223 
224   return fp;
225 }
226 
227 /*
228  * exit() calls _cleanup() through *__cleanup, set whenever we
229  * open or buffer a file.  This chicanery is done so that programs
230  * that do not use stdio need not link it all in.
231  *
232  * The name `_cleanup' is, alas, fairly well known outside stdio.
233  */
234 
235 static void
cleanup_stdio(void)236 cleanup_stdio (void)
237 {
238   if (_REENT_STDIN(ptr) != &__sf[0])
239     CLEANUP_FILE (_REENT_STDIN(ptr));
240   if (_REENT_STDOUT(ptr) != &__sf[1])
241     CLEANUP_FILE (_REENT_STDOUT(ptr));
242   if (_REENT_STDERR(ptr) != &__sf[2])
243     CLEANUP_FILE (_REENT_STDERR(ptr));
244 }
245 
246 /*
247  * __sinit() is called whenever stdio's internal variables must be set up.
248  */
249 
250 void
__sinit(void)251 __sinit (void)
252 {
253   __sfp_lock_acquire ();
254 
255   if (_REENT_CLEANUP(s))
256     {
257       __sfp_lock_release ();
258       return;
259     }
260 
261   /* make sure we clean up on exit */
262   _REENT_CLEANUP(s) = cleanup_stdio;	/* conservative */
263 
264   global_stdio_init ();
265   __sfp_lock_release ();
266 }
267 
268 #ifndef __SINGLE_THREAD__
269 
270 
271 /* Walkable file locking routine.  */
272 static int
__fp_lock(FILE * fp)273 __fp_lock (FILE * fp)
274 {
275   if (!(fp->_flags2 & __SNLK))
276     _flockfile (fp);
277 
278   return 0;
279 }
280 
281 /* Walkable file unlocking routine.  */
282 static int
__fp_unlock(FILE * fp)283 __fp_unlock (FILE * fp)
284 {
285   if (!(fp->_flags2 & __SNLK))
286     _funlockfile (fp);
287 
288   return 0;
289 }
290 
291 void
__fp_lock_all(void)292 __fp_lock_all (void)
293 {
294   __sfp_lock_acquire ();
295   (void) _fwalk_sglue (__fp_lock, &__sglue);
296 }
297 
298 void
__fp_unlock_all(void)299 __fp_unlock_all (void)
300 {
301   (void) _fwalk_sglue (__fp_unlock, &__sglue);
302   __sfp_lock_release ();
303 }
304 #endif
305