findfp.c revision 72472
1214152Sed/*- 2214152Sed * Copyright (c) 1990, 1993 3214152Sed * The Regents of the University of California. All rights reserved. 4214152Sed * 5222656Sed * This code is derived from software contributed to Berkeley by 6222656Sed * Chris Torek. 7214152Sed * 8214152Sed * Redistribution and use in source and binary forms, with or without 9214152Sed * modification, are permitted provided that the following conditions 10214152Sed * are met: 11214152Sed * 1. Redistributions of source code must retain the above copyright 12214152Sed * notice, this list of conditions and the following disclaimer. 13214152Sed * 2. Redistributions in binary form must reproduce the above copyright 14214152Sed * notice, this list of conditions and the following disclaimer in the 15214152Sed * documentation and/or other materials provided with the distribution. 16214152Sed * 3. All advertising materials mentioning features or use of this software 17214152Sed * must display the following acknowledgement: 18222656Sed * This product includes software developed by the University of 19214152Sed * California, Berkeley and its contributors. 20214152Sed * 4. Neither the name of the University nor the names of its contributors 21214152Sed * may be used to endorse or promote products derived from this software 22214152Sed * without specific prior written permission. 23222656Sed * 24222656Sed * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25214152Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26214152Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27214152Sed * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28214152Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29214152Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30214152Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31214152Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32214152Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33214152Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34214152Sed * SUCH DAMAGE. 35214152Sed */ 36214152Sed 37214152Sed#if defined(LIBC_SCCS) && !defined(lint) 38214152Sed#if 0 39214152Sedstatic char sccsid[] = "@(#)findfp.c 8.2 (Berkeley) 1/4/94"; 40214152Sed#endif 41214152Sedstatic const char rcsid[] = 42214152Sed "$FreeBSD: head/lib/libc/stdio/findfp.c 72472 2001-02-14 05:00:20Z peter $"; 43214152Sed#endif /* LIBC_SCCS and not lint */ 44214152Sed 45214152Sed#include <sys/param.h> 46214152Sed#include <machine/atomic.h> 47214152Sed#include <unistd.h> 48214152Sed#include <stdio.h> 49214152Sed#include <stdlib.h> 50214152Sed#include <string.h> 51214152Sed 52214152Sed#include <spinlock.h> 53214152Sed 54214152Sed#include "libc_private.h" 55214152Sed#include "local.h" 56214152Sed#include "glue.h" 57214152Sed 58214152Sedint __sdidinit; 59214152Sed 60214152Sed#define NDYNAMIC 10 /* add ten more whenever necessary */ 61214152Sed 62214152Sed#define std(handle, flags, file) \ 63214152SedFILE handle = {0,0,0,flags,file,{0},0,&handle,__sclose,__sread,__sseek,__swrite} 64214152Sed/* p r w flags file _bf z cookie close read seek write */ 65214152Sed 66214152Sed /* the usual - (stdin + stdout + stderr) */ 67214152Sedstatic FILE usual[FOPEN_MAX - 3]; 68214152Sedstatic struct glue uglue = { NULL, FOPEN_MAX - 3, usual }; 69214152Sed 70214152Sedstd(__stdin, __SRD, STDIN_FILENO); 71214152Sedstd(__stdout, __SWR, STDOUT_FILENO); 72214152Sedstd(__stderr, __SWR|__SNBF, STDERR_FILENO); 73214152Sed 74214152Sedstatic struct glue sglue2 = { &uglue, 1, &__stderr }; 75214152Sedstatic struct glue sglue1 = { &sglue2, 1, &__stdout }; 76214152Sedstruct glue __sglue = { &sglue1, 1, &__stdin }; 77214152Sedstatic struct glue *lastglue = &uglue; 78214152Sed 79214152Sedstatic struct glue * moreglue __P((int)); 80214152Sed 81214152Sedstatic spinlock_t thread_lock = _SPINLOCK_INITIALIZER; 82214152Sed#define THREAD_LOCK() if (__isthreaded) _SPINLOCK(&thread_lock) 83214152Sed#define THREAD_UNLOCK() if (__isthreaded) _SPINUNLOCK(&thread_lock) 84214152Sed 85214152Sed#if NOT_YET 86214152Sed#define SET_GLUE_PTR(ptr, val) atomic_set_rel_ptr(&(ptr), (uintptr_t)(val)) 87214152Sed#else 88214152Sed#define SET_GLUE_PTR(ptr, val) ptr = val 89214152Sed#endif 90214152Sed 91214152Sedstatic struct glue * 92214152Sedmoreglue(n) 93214152Sed int n; 94214152Sed{ 95214152Sed struct glue *g; 96214152Sed FILE *p; 97214152Sed static FILE empty; 98214152Sed 99214152Sed g = (struct glue *)malloc(sizeof(*g) + ALIGNBYTES + n * sizeof(FILE)); 100214152Sed if (g == NULL) 101214152Sed return (NULL); 102214152Sed p = (FILE *)ALIGN(g + 1); 103214152Sed g->next = NULL; 104214152Sed g->niobs = n; 105214152Sed g->iobs = p; 106214152Sed while (--n >= 0) 107214152Sed *p++ = empty; 108214152Sed return (g); 109214152Sed} 110214152Sed 111214152Sed/* 112214152Sed * Find a free FILE for fopen et al. 113214152Sed */ 114214152SedFILE * 115214152Sed__sfp() 116214152Sed{ 117214152Sed FILE *fp; 118214152Sed int n; 119214152Sed struct glue *g; 120214152Sed 121214152Sed if (!__sdidinit) 122214152Sed __sinit(); 123214152Sed /* 124214152Sed * The list must be locked because a FILE may be updated. 125214152Sed */ 126214152Sed THREAD_LOCK(); 127214152Sed for (g = &__sglue; g != NULL; g = g->next) { 128214152Sed for (fp = g->iobs, n = g->niobs; --n >= 0; fp++) 129214152Sed if (fp->_flags == 0) 130214152Sed goto found; 131214152Sed } 132214152Sed THREAD_UNLOCK(); /* don't hold lock while malloc()ing. */ 133214152Sed if ((g = moreglue(NDYNAMIC)) == NULL) 134214152Sed return (NULL); 135214152Sed THREAD_LOCK(); /* reacquire the lock */ 136214152Sed SET_GLUE_PTR(lastglue->next, g); /* atomically append glue to list */ 137214152Sed lastglue = g; /* not atomic; only accessed when locked */ 138214152Sed fp = g->iobs; 139214152Sedfound: 140214152Sed fp->_flags = 1; /* reserve this slot; caller sets real flags */ 141214152Sed THREAD_UNLOCK(); 142214152Sed fp->_p = NULL; /* no current pointer */ 143214152Sed fp->_w = 0; /* nothing to read or write */ 144214152Sed fp->_r = 0; 145214152Sed fp->_bf._base = NULL; /* no buffer */ 146214152Sed fp->_bf._size = 0; 147214152Sed fp->_lbfsize = 0; /* not line buffered */ 148214152Sed fp->_file = -1; /* no file */ 149214152Sed/* fp->_cookie = <any>; */ /* caller sets cookie, _read/_write etc */ 150214152Sed fp->_ub._base = NULL; /* no ungetc buffer */ 151214152Sed fp->_ub._size = 0; 152214152Sed fp->_lb._base = NULL; /* no line buffer */ 153214152Sed fp->_lb._size = 0; 154214152Sed/* fp->_lock = NULL; */ /* once set always set (reused) */ 155214152Sed return (fp); 156214152Sed} 157214152Sed 158214152Sed/* 159214152Sed * XXX. Force immediate allocation of internal memory. Not used by stdio, 160214152Sed * but documented historically for certain applications. Bad applications. 161214152Sed */ 162214152Sed__warn_references(f_prealloc, 163214152Sed "warning: this program uses f_prealloc(), which is stupid."); 164214152Sed 165214152Sedvoid 166214152Sedf_prealloc() 167214152Sed{ 168214152Sed struct glue *g; 169214152Sed int n; 170214152Sed 171214152Sed n = getdtablesize() - FOPEN_MAX + 20; /* 20 for slop. */ 172214152Sed /* 173214152Sed * It should be safe to walk the list without locking it; 174214152Sed * new nodes are only added to the end and none are ever 175214152Sed * removed. 176214152Sed */ 177214152Sed for (g = &__sglue; (n -= g->niobs) > 0 && g->next; g = g->next) 178214152Sed /* void */; 179214152Sed if ((n > 0) && ((g = moreglue(n)) != NULL)) { 180214152Sed THREAD_LOCK(); 181214152Sed SET_GLUE_PTR(lastglue->next, g); 182214152Sed lastglue = g; 183214152Sed THREAD_UNLOCK(); 184214152Sed } 185214152Sed} 186 187/* 188 * exit() calls _cleanup() through *__cleanup, set whenever we 189 * open or buffer a file. This chicanery is done so that programs 190 * that do not use stdio need not link it all in. 191 * 192 * The name `_cleanup' is, alas, fairly well known outside stdio. 193 */ 194void 195_cleanup() 196{ 197 /* (void) _fwalk(fclose); */ 198 (void) _fwalk(__sflush); /* `cheating' */ 199} 200 201/* 202 * __sinit() is called whenever stdio's internal variables must be set up. 203 */ 204void 205__sinit() 206{ 207 /* Make sure we clean up on exit. */ 208 __cleanup = _cleanup; /* conservative */ 209 __sdidinit = 1; 210} 211