1/*	$NetBSD: findfp.c,v 1.27 2012/03/15 18:22:30 christos 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[] = "@(#)findfp.c	8.2 (Berkeley) 1/4/94";
39#else
40__RCSID("$NetBSD: findfp.c,v 1.27 2012/03/15 18:22:30 christos Exp $");
41#endif
42#endif /* LIBC_SCCS and not lint */
43
44#include "namespace.h"
45#include <sys/param.h>
46#include <unistd.h>
47#include <stdio.h>
48#include <errno.h>
49#include <stdlib.h>
50#include <string.h>
51#include "reentrant.h"
52#include "local.h"
53#include "glue.h"
54
55int	__sdidinit;
56
57#define	NDYNAMIC 10		/* add ten more whenever necessary */
58
59#define	std(flags, file) { \
60	._p = NULL, \
61	._r = 0, \
62	._w = 0, \
63	._flags = (flags), \
64	._file = (file),  \
65	._bf = { ._base = NULL, ._size = 0 }, \
66	._lbfsize = 0,  \
67	._cookie = __sF + (file), \
68	._close = __sclose, \
69	._read = __sread, \
70	._seek = __sseek, \
71	._write = __swrite, \
72	._ext = { ._base = (void *)(__sFext + (file)), ._size = 0 }, \
73	._up = NULL, \
74        ._ur = 0, \
75	._ubuf = { [0] = '\0', [1] = '\0', [2] = '\0' }, \
76	._nbuf = { [0] = '\0' }, \
77	._flush = NULL, \
78	._lb_unused = { '\0' }, \
79	._blksize = 0, \
80	._offset = (off_t)0, \
81}
82
83				/* the usual - (stdin + stdout + stderr) */
84static FILE usual[FOPEN_MAX - 3];
85static struct __sfileext usualext[FOPEN_MAX - 3];
86static struct glue uglue = { 0, FOPEN_MAX - 3, usual };
87
88#if defined(_REENTRANT) && !defined(__lint__) /* XXX lint is busted */
89#define	STDEXT { ._lock = MUTEX_INITIALIZER, ._lockcond = COND_INITIALIZER }
90struct __sfileext __sFext[3] = { STDEXT,
91				 STDEXT,
92				 STDEXT};
93#else
94struct __sfileext __sFext[3];
95#endif
96
97FILE __sF[3] = {
98	std(__SRD, STDIN_FILENO),		/* stdin */
99	std(__SWR, STDOUT_FILENO),		/* stdout */
100	std(__SWR|__SNBF, STDERR_FILENO)	/* stderr */
101};
102struct glue __sglue = { &uglue, 3, __sF };
103
104void f_prealloc(void);
105
106#ifdef _REENTRANT
107rwlock_t __sfp_lock = RWLOCK_INITIALIZER;
108#endif
109
110static struct glue *
111moreglue(int n)
112{
113	struct glue *g;
114	FILE *p;
115	struct __sfileext *pext;
116	static FILE empty;
117
118	g = malloc(sizeof(*g) + ALIGNBYTES + n * sizeof(FILE)
119	    + n * sizeof(struct __sfileext));
120	if (g == NULL)
121		return NULL;
122	p = (FILE *)ALIGN((u_long)(g + 1));
123	g->next = NULL;
124	g->niobs = n;
125	g->iobs = p;
126	pext = (void *)(p + n);
127	while (--n >= 0) {
128		*p = empty;
129		_FILEEXT_SETUP(p, pext);
130		p++;
131		pext++;
132	}
133	return g;
134}
135
136void
137__sfpinit(FILE *fp)
138{
139	fp->_flags = 1;		/* reserve this slot; caller sets real flags */
140	fp->_p = NULL;		/* no current pointer */
141	fp->_w = 0;		/* nothing to read or write */
142	fp->_r = 0;
143	fp->_bf._base = NULL;	/* no buffer */
144	fp->_bf._size = 0;
145	fp->_lbfsize = 0;	/* not line buffered */
146	fp->_file = -1;		/* no file */
147/*	fp->_cookie = <any>; */	/* caller sets cookie, _read/_write etc */
148	fp->_flush = NULL;	/* default flush */
149	_UB(fp)._base = NULL;	/* no ungetc buffer */
150	_UB(fp)._size = 0;
151	memset(WCIO_GET(fp), 0, sizeof(struct wchar_io_data));
152}
153
154/*
155 * Find a free FILE for fopen et al.
156 */
157FILE *
158__sfp(void)
159{
160	FILE *fp;
161	int n;
162	struct glue *g;
163
164	if (!__sdidinit)
165		__sinit();
166
167	rwlock_wrlock(&__sfp_lock);
168	for (g = &__sglue;; g = g->next) {
169		for (fp = g->iobs, n = g->niobs; --n >= 0; fp++)
170			if (fp->_flags == 0)
171				goto found;
172		if (g->next == NULL && (g->next = moreglue(NDYNAMIC)) == NULL)
173			break;
174	}
175	rwlock_unlock(&__sfp_lock);
176	return NULL;
177found:
178	__sfpinit(fp);
179	rwlock_unlock(&__sfp_lock);
180	return fp;
181}
182
183/*
184 * XXX.  Force immediate allocation of internal memory.  Not used by stdio,
185 * but documented historically for certain applications.  Bad applications.
186 */
187void
188f_prealloc(void)
189{
190	struct glue *g;
191	int n;
192
193	n = (int)sysconf(_SC_OPEN_MAX) - FOPEN_MAX + 20; /* 20 for slop. */
194	for (g = &__sglue; (n -= g->niobs) > 0 && g->next; g = g->next)
195		continue;
196	if (n > 0)
197		g->next = moreglue(n);
198}
199
200/*
201 * exit() calls _cleanup() through *__cleanup, set whenever we
202 * open or buffer a file.  This chicanery is done so that programs
203 * that do not use stdio need not link it all in.
204 *
205 * The name `_cleanup' is, alas, fairly well known outside stdio.
206 */
207void
208_cleanup(void)
209{
210	/* (void) _fwalk(fclose); */
211	(void) fflush(NULL);			/* `cheating' */
212}
213
214/*
215 * __sinit() is called whenever stdio's internal variables must be set up.
216 */
217void
218__sinit(void)
219{
220	int i;
221
222	for (i = 0; i < FOPEN_MAX - 3; i++)
223		_FILEEXT_SETUP(&usual[i], &usualext[i]);
224
225	/* make sure we clean up on exit */
226	__cleanup = _cleanup;		/* conservative */
227	__sdidinit = 1;
228}
229