11573Srgrimes/*-
21573Srgrimes * Copyright (c) 1990, 1993
31573Srgrimes *	The Regents of the University of California.  All rights reserved.
41573Srgrimes *
51573Srgrimes * This code is derived from software contributed to Berkeley by
61573Srgrimes * Chris Torek.
71573Srgrimes *
81573Srgrimes * Redistribution and use in source and binary forms, with or without
91573Srgrimes * modification, are permitted provided that the following conditions
101573Srgrimes * are met:
111573Srgrimes * 1. Redistributions of source code must retain the above copyright
121573Srgrimes *    notice, this list of conditions and the following disclaimer.
131573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141573Srgrimes *    notice, this list of conditions and the following disclaimer in the
151573Srgrimes *    documentation and/or other materials provided with the distribution.
16249808Semaste * 3. Neither the name of the University nor the names of its contributors
171573Srgrimes *    may be used to endorse or promote products derived from this software
181573Srgrimes *    without specific prior written permission.
191573Srgrimes *
201573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231573Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301573Srgrimes * SUCH DAMAGE.
311573Srgrimes */
321573Srgrimes
331573Srgrimes#if defined(LIBC_SCCS) && !defined(lint)
341573Srgrimesstatic char sccsid[] = "@(#)findfp.c	8.2 (Berkeley) 1/4/94";
351573Srgrimes#endif /* LIBC_SCCS and not lint */
3692986Sobrien#include <sys/cdefs.h>
3792986Sobrien__FBSDID("$FreeBSD: releng/11.0/lib/libc/stdio/findfp.c 292902 2015-12-30 03:36:22Z imp $");
381573Srgrimes
391573Srgrimes#include <sys/param.h>
4071579Sdeischen#include <machine/atomic.h>
411573Srgrimes#include <unistd.h>
421573Srgrimes#include <stdio.h>
431573Srgrimes#include <stdlib.h>
44292809Simp#include <stdint.h>
451573Srgrimes#include <string.h>
4653459Sdt
4753459Sdt#include <spinlock.h>
4853459Sdt
4971579Sdeischen#include "libc_private.h"
501573Srgrimes#include "local.h"
511573Srgrimes#include "glue.h"
521573Srgrimes
531573Srgrimesint	__sdidinit;
541573Srgrimes
551573Srgrimes#define	NDYNAMIC 10		/* add ten more whenever necessary */
561573Srgrimes
57189249Sdas#define	std(flags, file) {		\
58189249Sdas	._flags = (flags),		\
59189249Sdas	._file = (file),		\
60189249Sdas	._cookie = __sF + (file),	\
61189249Sdas	._close = __sclose,		\
62189249Sdas	._read = __sread,		\
63189249Sdas	._seek = __sseek,		\
64189249Sdas	._write = __swrite,		\
65205021Sjhb	._fl_mutex = PTHREAD_MUTEX_INITIALIZER, \
66189249Sdas}
671573Srgrimes				/* the usual - (stdin + stdout + stderr) */
681573Srgrimesstatic FILE usual[FOPEN_MAX - 3];
6971579Sdeischenstatic struct glue uglue = { NULL, FOPEN_MAX - 3, usual };
701573Srgrimes
71178287Sjhbstatic FILE __sF[3] = {
7272529Simp	std(__SRD, STDIN_FILENO),
7372529Simp	std(__SWR, STDOUT_FILENO),
7472529Simp	std(__SWR|__SNBF, STDERR_FILENO)
7572529Simp};
7672529Simp
7781600SpeterFILE *__stdinp = &__sF[0];
7881600SpeterFILE *__stdoutp = &__sF[1];
7981600SpeterFILE *__stderrp = &__sF[2];
8072732Speter
8172529Simpstruct glue __sglue = { &uglue, 3, __sF };
8271579Sdeischenstatic struct glue *lastglue = &uglue;
831573Srgrimes
8492905Sobrienstatic struct glue *	moreglue(int);
8516586Sjraynard
86234657Skibspinlock_t __stdio_thread_lock = _SPINLOCK_INITIALIZER;
8753459Sdt
8871579Sdeischen#if NOT_YET
8972373Sdeischen#define	SET_GLUE_PTR(ptr, val)	atomic_set_rel_ptr(&(ptr), (uintptr_t)(val))
9071579Sdeischen#else
9171579Sdeischen#define	SET_GLUE_PTR(ptr, val)	ptr = val
9271579Sdeischen#endif
9371579Sdeischen
941573Srgrimesstatic struct glue *
95249810Semastemoreglue(int n)
961573Srgrimes{
9771579Sdeischen	struct glue *g;
98205021Sjhb	static FILE empty = { ._fl_mutex = PTHREAD_MUTEX_INITIALIZER };
9971579Sdeischen	FILE *p;
100292809Simp	size_t align;
1011573Srgrimes
102292902Simp	align = __alignof__(FILE);
103292809Simp	g = (struct glue *)malloc(sizeof(*g) + align + n * sizeof(FILE));
1041573Srgrimes	if (g == NULL)
1051573Srgrimes		return (NULL);
106292809Simp	p = (FILE *)roundup((uintptr_t)(g + 1), align);
1071573Srgrimes	g->next = NULL;
1081573Srgrimes	g->niobs = n;
1091573Srgrimes	g->iobs = p;
110178287Sjhb	while (--n >= 0)
111178287Sjhb		*p++ = empty;
1121573Srgrimes	return (g);
1131573Srgrimes}
1141573Srgrimes
1151573Srgrimes/*
1161573Srgrimes * Find a free FILE for fopen et al.
1171573Srgrimes */
1181573SrgrimesFILE *
119288033Srodrigc__sfp(void)
1201573Srgrimes{
12171579Sdeischen	FILE	*fp;
12271579Sdeischen	int	n;
12371579Sdeischen	struct glue *g;
1241573Srgrimes
1251573Srgrimes	if (!__sdidinit)
1261573Srgrimes		__sinit();
12771579Sdeischen	/*
12871579Sdeischen	 * The list must be locked because a FILE may be updated.
12971579Sdeischen	 */
130234657Skib	STDIO_THREAD_LOCK();
13171579Sdeischen	for (g = &__sglue; g != NULL; g = g->next) {
1321573Srgrimes		for (fp = g->iobs, n = g->niobs; --n >= 0; fp++)
1331573Srgrimes			if (fp->_flags == 0)
1341573Srgrimes				goto found;
1351573Srgrimes	}
136234657Skib	STDIO_THREAD_UNLOCK();	/* don't hold lock while malloc()ing. */
13772128Ssobomax	if ((g = moreglue(NDYNAMIC)) == NULL)
13871579Sdeischen		return (NULL);
139234657Skib	STDIO_THREAD_LOCK();	/* reacquire the lock */
14071579Sdeischen	SET_GLUE_PTR(lastglue->next, g); /* atomically append glue to list */
14171579Sdeischen	lastglue = g;		/* not atomic; only accessed when locked */
14271579Sdeischen	fp = g->iobs;
1431573Srgrimesfound:
1441573Srgrimes	fp->_flags = 1;		/* reserve this slot; caller sets real flags */
145234657Skib	STDIO_THREAD_UNLOCK();
1461573Srgrimes	fp->_p = NULL;		/* no current pointer */
1471573Srgrimes	fp->_w = 0;		/* nothing to read or write */
1481573Srgrimes	fp->_r = 0;
1491573Srgrimes	fp->_bf._base = NULL;	/* no buffer */
1501573Srgrimes	fp->_bf._size = 0;
1511573Srgrimes	fp->_lbfsize = 0;	/* not line buffered */
1521573Srgrimes	fp->_file = -1;		/* no file */
1531573Srgrimes/*	fp->_cookie = <any>; */	/* caller sets cookie, _read/_write etc */
1541573Srgrimes	fp->_ub._base = NULL;	/* no ungetc buffer */
1551573Srgrimes	fp->_ub._size = 0;
1561573Srgrimes	fp->_lb._base = NULL;	/* no line buffer */
1571573Srgrimes	fp->_lb._size = 0;
158205021Sjhb/*	fp->_fl_mutex = NULL; */ /* once set always set (reused) */
159178287Sjhb	fp->_orientation = 0;
160178287Sjhb	memset(&fp->_mbstate, 0, sizeof(mbstate_t));
161290110Sache	fp->_flags2 = 0;
1621573Srgrimes	return (fp);
1631573Srgrimes}
1641573Srgrimes
1651573Srgrimes/*
1661573Srgrimes * XXX.  Force immediate allocation of internal memory.  Not used by stdio,
1671573Srgrimes * but documented historically for certain applications.  Bad applications.
1681573Srgrimes */
16911667Sphk__warn_references(f_prealloc,
17080541Ssheldonh	"warning: this program uses f_prealloc(), which is not recommended.");
171288033Srodrigcvoid f_prealloc(void);
17211667Sphk
17311667Sphkvoid
174200150Sedf_prealloc(void)
1751573Srgrimes{
17671579Sdeischen	struct glue *g;
1771573Srgrimes	int n;
1781573Srgrimes
1791573Srgrimes	n = getdtablesize() - FOPEN_MAX + 20;		/* 20 for slop. */
18071579Sdeischen	/*
18171579Sdeischen	 * It should be safe to walk the list without locking it;
18271579Sdeischen	 * new nodes are only added to the end and none are ever
18371579Sdeischen	 * removed.
18471579Sdeischen	 */
1851573Srgrimes	for (g = &__sglue; (n -= g->niobs) > 0 && g->next; g = g->next)
1861573Srgrimes		/* void */;
18771579Sdeischen	if ((n > 0) && ((g = moreglue(n)) != NULL)) {
188234657Skib		STDIO_THREAD_LOCK();
18971579Sdeischen		SET_GLUE_PTR(lastglue->next, g);
19071579Sdeischen		lastglue = g;
191234657Skib		STDIO_THREAD_UNLOCK();
19271579Sdeischen	}
1931573Srgrimes}
1941573Srgrimes
1951573Srgrimes/*
1961573Srgrimes * exit() calls _cleanup() through *__cleanup, set whenever we
1971573Srgrimes * open or buffer a file.  This chicanery is done so that programs
1981573Srgrimes * that do not use stdio need not link it all in.
1991573Srgrimes *
2001573Srgrimes * The name `_cleanup' is, alas, fairly well known outside stdio.
2011573Srgrimes */
2021573Srgrimesvoid
203288033Srodrigc_cleanup(void)
2041573Srgrimes{
2051573Srgrimes	/* (void) _fwalk(fclose); */
2061573Srgrimes	(void) _fwalk(__sflush);		/* `cheating' */
2071573Srgrimes}
2081573Srgrimes
2091573Srgrimes/*
2101573Srgrimes * __sinit() is called whenever stdio's internal variables must be set up.
2111573Srgrimes */
2121573Srgrimesvoid
213288033Srodrigc__sinit(void)
2141573Srgrimes{
21572550Simp
216178287Sjhb	/* Make sure we clean up on exit. */
217178287Sjhb	__cleanup = _cleanup;		/* conservative */
218178287Sjhb	__sdidinit = 1;
2191573Srgrimes}
220