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 struct glue_with_file {
132   struct _glue glue;
133   FILE file;
134 };
135 
136 static struct _glue *
sfmoreglue(int n)137 sfmoreglue (int n)
138 {
139   struct glue_with_file *g;
140 
141   g = (struct glue_with_file *)
142     malloc (sizeof (*g) + (n - 1) * sizeof (FILE));
143   if (g == NULL)
144     return NULL;
145   g->glue._next = NULL;
146   g->glue._niobs = n;
147   g->glue._iobs = &g->file;
148   memset (&g->file, 0, n * sizeof (FILE));
149   return &g->glue;
150 }
151 
152 static void
stdio_exit_handler(void)153 stdio_exit_handler (void)
154 {
155   (void) _fwalk_sglue (CLEANUP_FILE, &__sglue);
156 }
157 
158 static void
global_stdio_init(void)159 global_stdio_init (void)
160 {
161   if (__stdio_exit_handler == NULL) {
162     __stdio_exit_handler = stdio_exit_handler;
163     stdin_init (&__sf[0]);
164     stdout_init (&__sf[1]);
165     stderr_init (&__sf[2]);
166   }
167 }
168 
169 /*
170  * Find a free FILE for fopen et al.
171  */
172 
173 FILE *
__sfp(void)174 __sfp (void)
175 {
176   FILE *fp;
177   int n;
178   struct _glue *g;
179 
180   _newlib_sfp_lock_start ();
181   global_stdio_init ();
182 
183   for (g = &__sglue;; g = g->_next)
184     {
185       for (fp = g->_iobs, n = g->_niobs; --n >= 0; fp++)
186 	if (fp->_flags == 0)
187 	  goto found;
188       if (g->_next == NULL &&
189 	  (g->_next = sfmoreglue (NDYNAMIC)) == NULL)
190 	break;
191     }
192   _newlib_sfp_lock_exit ();
193   _REENT_ERRNO(d) = ENOMEM;
194   return NULL;
195 
196 found:
197   fp->_file = -1;		/* no file */
198   fp->_flags = 1;		/* reserve this slot; caller sets real flags */
199   fp->_flags2 = 0;
200 #ifndef __SINGLE_THREAD__
201   __lock_init_recursive (fp->_lock);
202 #endif
203   _newlib_sfp_lock_end ();
204 
205   fp->_p = NULL;		/* no current pointer */
206   fp->_w = 0;			/* nothing to read or write */
207   fp->_r = 0;
208   fp->_bf._base = NULL;		/* no buffer */
209   fp->_bf._size = 0;
210   fp->_lbfsize = 0;		/* not line buffered */
211   memset (&fp->_mbstate, 0, sizeof (_mbstate_t));
212   /* fp->_cookie = <any>; */	/* caller sets cookie, _read/_write etc */
213   fp->_ub._base = NULL;		/* no ungetc buffer */
214   fp->_ub._size = 0;
215   fp->_lb._base = NULL;		/* no line buffer */
216   fp->_lb._size = 0;
217 
218   return fp;
219 }
220 
221 /*
222  * exit() calls _cleanup() through *__cleanup, set whenever we
223  * open or buffer a file.  This chicanery is done so that programs
224  * that do not use stdio need not link it all in.
225  *
226  * The name `_cleanup' is, alas, fairly well known outside stdio.
227  */
228 
229 static void
cleanup_stdio(void)230 cleanup_stdio (void)
231 {
232   if (_REENT_STDIN(ptr) != &__sf[0])
233     CLEANUP_FILE (_REENT_STDIN(ptr));
234   if (_REENT_STDOUT(ptr) != &__sf[1])
235     CLEANUP_FILE (_REENT_STDOUT(ptr));
236   if (_REENT_STDERR(ptr) != &__sf[2])
237     CLEANUP_FILE (_REENT_STDERR(ptr));
238 }
239 
240 /*
241  * __sinit() is called whenever stdio's internal variables must be set up.
242  */
243 
244 void
__sinit(void)245 __sinit (void)
246 {
247   __sfp_lock_acquire ();
248 
249   if (_REENT_CLEANUP(s))
250     {
251       __sfp_lock_release ();
252       return;
253     }
254 
255   /* make sure we clean up on exit */
256   _REENT_CLEANUP(s) = cleanup_stdio;	/* conservative */
257 
258   global_stdio_init ();
259   __sfp_lock_release ();
260 }
261 
262 #ifndef __SINGLE_THREAD__
263 
264 
265 /* Walkable file locking routine.  */
266 static int
__fp_lock(FILE * fp)267 __fp_lock (FILE * fp)
268 {
269   if (!(fp->_flags2 & __SNLK))
270     _flockfile (fp);
271 
272   return 0;
273 }
274 
275 /* Walkable file unlocking routine.  */
276 static int
__fp_unlock(FILE * fp)277 __fp_unlock (FILE * fp)
278 {
279   if (!(fp->_flags2 & __SNLK))
280     _funlockfile (fp);
281 
282   return 0;
283 }
284 
285 void
__fp_lock_all(void)286 __fp_lock_all (void)
287 {
288   __sfp_lock_acquire ();
289   (void) _fwalk_sglue (__fp_lock, &__sglue);
290 }
291 
292 void
__fp_unlock_all(void)293 __fp_unlock_all (void)
294 {
295   (void) _fwalk_sglue (__fp_unlock, &__sglue);
296   __sfp_lock_release ();
297 }
298 #endif
299