findfp.c revision 178287
1254219Scy/*- 2254219Scy * Copyright (c) 1990, 1993 3254219Scy * The Regents of the University of California. All rights reserved. 4254219Scy * 5254219Scy * This code is derived from software contributed to Berkeley by 6254219Scy * Chris Torek. 7254219Scy * 8254219Scy * Redistribution and use in source and binary forms, with or without 9254219Scy * modification, are permitted provided that the following conditions 10254219Scy * are met: 11254219Scy * 1. Redistributions of source code must retain the above copyright 12254219Scy * notice, this list of conditions and the following disclaimer. 13254219Scy * 2. Redistributions in binary form must reproduce the above copyright 14254219Scy * notice, this list of conditions and the following disclaimer in the 15254219Scy * documentation and/or other materials provided with the distribution. 16254219Scy * 4. Neither the name of the University nor the names of its contributors 17254219Scy * may be used to endorse or promote products derived from this software 18254219Scy * without specific prior written permission. 19254219Scy * 20254219Scy * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21254219Scy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22254219Scy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23254219Scy * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24254219Scy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25254219Scy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26254219Scy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27254219Scy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28254219Scy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29254219Scy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30254219Scy * SUCH DAMAGE. 31254219Scy */ 32254219Scy 33254219Scy#if defined(LIBC_SCCS) && !defined(lint) 34254219Scystatic char sccsid[] = "@(#)findfp.c 8.2 (Berkeley) 1/4/94"; 35254219Scy#endif /* LIBC_SCCS and not lint */ 36254219Scy#include <sys/cdefs.h> 37254219Scy__FBSDID("$FreeBSD: head/lib/libc/stdio/findfp.c 178287 2008-04-17 22:17:54Z jhb $"); 38254219Scy 39254219Scy#include <sys/param.h> 40254219Scy#include <machine/atomic.h> 41254219Scy#include <unistd.h> 42254219Scy#include <stdio.h> 43254219Scy#include <stdlib.h> 44254219Scy#include <string.h> 45254219Scy 46254219Scy#include <spinlock.h> 47254219Scy 48254219Scy#include "libc_private.h" 49254219Scy#include "local.h" 50254219Scy#include "glue.h" 51254219Scy 52254219Scyint __sdidinit; 53254219Scy 54254219Scy#define NDYNAMIC 10 /* add ten more whenever necessary */ 55254219Scy 56254219Scy#define std(flags, file) \ 57254219Scy {0,0,0,flags,file,{0},0,__sF+file,__sclose,__sread,__sseek,__swrite} 58254219Scy /* p r w flags file _bf z cookie close read seek write */ 59254219Scy 60254219Scy /* the usual - (stdin + stdout + stderr) */ 61254219Scystatic FILE usual[FOPEN_MAX - 3]; 62254219Scystatic struct glue uglue = { NULL, FOPEN_MAX - 3, usual }; 63254219Scy 64254219Scystatic FILE __sF[3] = { 65254219Scy std(__SRD, STDIN_FILENO), 66254219Scy std(__SWR, STDOUT_FILENO), 67254219Scy std(__SWR|__SNBF, STDERR_FILENO) 68254219Scy}; 69254219Scy 70254219ScyFILE *__stdinp = &__sF[0]; 71254219ScyFILE *__stdoutp = &__sF[1]; 72254219ScyFILE *__stderrp = &__sF[2]; 73254219Scy 74254219Scystruct glue __sglue = { &uglue, 3, __sF }; 75254219Scystatic struct glue *lastglue = &uglue; 76254219Scy 77254219Scystatic struct glue * moreglue(int); 78254219Scy 79254219Scystatic spinlock_t thread_lock = _SPINLOCK_INITIALIZER; 80254219Scy#define THREAD_LOCK() if (__isthreaded) _SPINLOCK(&thread_lock) 81254219Scy#define THREAD_UNLOCK() if (__isthreaded) _SPINUNLOCK(&thread_lock) 82254219Scy 83254219Scy#if NOT_YET 84254219Scy#define SET_GLUE_PTR(ptr, val) atomic_set_rel_ptr(&(ptr), (uintptr_t)(val)) 85254219Scy#else 86254219Scy#define SET_GLUE_PTR(ptr, val) ptr = val 87254219Scy#endif 88254219Scy 89254219Scystatic struct glue * 90254219Scymoreglue(n) 91254219Scy int n; 92254219Scy{ 93254219Scy struct glue *g; 94254219Scy static FILE empty; 95254219Scy FILE *p; 96254219Scy 97254219Scy g = (struct glue *)malloc(sizeof(*g) + ALIGNBYTES + n * sizeof(FILE)); 98254219Scy if (g == NULL) 99254219Scy return (NULL); 100254219Scy p = (FILE *)ALIGN(g + 1); 101254219Scy g->next = NULL; 102254219Scy g->niobs = n; 103254219Scy g->iobs = p; 104254219Scy while (--n >= 0) 105254219Scy *p++ = empty; 106254219Scy return (g); 107254219Scy} 108254219Scy 109254219Scy/* 110254219Scy * Find a free FILE for fopen et al. 111254219Scy */ 112254219ScyFILE * 113254219Scy__sfp() 114254219Scy{ 115254219Scy FILE *fp; 116254219Scy int n; 117254219Scy struct glue *g; 118254219Scy 119254219Scy if (!__sdidinit) 120254219Scy __sinit(); 121254219Scy /* 122254219Scy * The list must be locked because a FILE may be updated. 123254219Scy */ 124254219Scy THREAD_LOCK(); 125254219Scy for (g = &__sglue; g != NULL; g = g->next) { 126254219Scy for (fp = g->iobs, n = g->niobs; --n >= 0; fp++) 127254219Scy if (fp->_flags == 0) 128254219Scy goto found; 129254219Scy } 130254219Scy THREAD_UNLOCK(); /* don't hold lock while malloc()ing. */ 131254219Scy if ((g = moreglue(NDYNAMIC)) == NULL) 132254219Scy return (NULL); 133254219Scy THREAD_LOCK(); /* reacquire the lock */ 134254219Scy SET_GLUE_PTR(lastglue->next, g); /* atomically append glue to list */ 135254219Scy lastglue = g; /* not atomic; only accessed when locked */ 136254219Scy fp = g->iobs; 137254219Scyfound: 138254219Scy fp->_flags = 1; /* reserve this slot; caller sets real flags */ 139254219Scy THREAD_UNLOCK(); 140254219Scy fp->_p = NULL; /* no current pointer */ 141254219Scy fp->_w = 0; /* nothing to read or write */ 142254219Scy fp->_r = 0; 143254219Scy fp->_bf._base = NULL; /* no buffer */ 144254219Scy fp->_bf._size = 0; 145254219Scy fp->_lbfsize = 0; /* not line buffered */ 146254219Scy fp->_file = -1; /* no file */ 147254219Scy/* fp->_cookie = <any>; */ /* caller sets cookie, _read/_write etc */ 148254219Scy fp->_ub._base = NULL; /* no ungetc buffer */ 149254219Scy fp->_ub._size = 0; 150254219Scy fp->_lb._base = NULL; /* no line buffer */ 151254219Scy fp->_lb._size = 0; 152254219Scy/* fp->_lock = NULL; */ /* once set always set (reused) */ 153254219Scy fp->_orientation = 0; 154254219Scy memset(&fp->_mbstate, 0, sizeof(mbstate_t)); 155254219Scy return (fp); 156254219Scy} 157254219Scy 158254219Scy/* 159254219Scy * XXX. Force immediate allocation of internal memory. Not used by stdio, 160254219Scy * but documented historically for certain applications. Bad applications. 161254219Scy */ 162254219Scy__warn_references(f_prealloc, 163254219Scy "warning: this program uses f_prealloc(), which is not recommended."); 164254219Scy 165254219Scyvoid 166254219Scyf_prealloc() 167254219Scy{ 168254219Scy struct glue *g; 169254219Scy int n; 170254219Scy 171254219Scy n = getdtablesize() - FOPEN_MAX + 20; /* 20 for slop. */ 172254219Scy /* 173254219Scy * It should be safe to walk the list without locking it; 174254219Scy * new nodes are only added to the end and none are ever 175254219Scy * removed. 176254219Scy */ 177254219Scy for (g = &__sglue; (n -= g->niobs) > 0 && g->next; g = g->next) 178254219Scy /* void */; 179254219Scy if ((n > 0) && ((g = moreglue(n)) != NULL)) { 180254219Scy THREAD_LOCK(); 181254219Scy SET_GLUE_PTR(lastglue->next, g); 182254219Scy lastglue = g; 183254219Scy THREAD_UNLOCK(); 184268563Scy } 185254219Scy} 186254219Scy 187254219Scy/* 188254219Scy * exit() calls _cleanup() through *__cleanup, set whenever we 189254219Scy * open or buffer a file. This chicanery is done so that programs 190254219Scy * that do not use stdio need not link it all in. 191254219Scy * 192254219Scy * The name `_cleanup' is, alas, fairly well known outside stdio. 193254219Scy */ 194254219Scyvoid 195254219Scy_cleanup() 196254219Scy{ 197254219Scy /* (void) _fwalk(fclose); */ 198254219Scy (void) _fwalk(__sflush); /* `cheating' */ 199254219Scy} 200254219Scy 201254219Scy/* 202254219Scy * __sinit() is called whenever stdio's internal variables must be set up. 203254219Scy */ 204254219Scyvoid 205254219Scy__sinit() 206254219Scy{ 207254219Scy 208254219Scy /* Make sure we clean up on exit. */ 209254219Scy __cleanup = _cleanup; /* conservative */ 210254219Scy __sdidinit = 1; 211254219Scy} 212254219Scy