//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/***
*stream.c - find a stream not in use
*
*
*Purpose:
*   defines _getstream() - find a stream not in use
*
*******************************************************************************/


#include <cruntime.h>
#include <windows.h>
#include <malloc.h>
#include <file2.h>
#include <internal.h>
#include <stdio.h>
#include <mtdll.h>
#include <dbgint.h>

/***
*FILEX *_getstream() - find a stream not in use
*
*Purpose:
*   Find a stream not in use and make it available to caller. Intended
*   for use inside library only
*
*Entry:
*   None. Scans __piob[]
*
*Exit:
*   Returns a pointer to a free stream, or NULL if all are in use.  A
*   stream becomes allocated if the caller decided to use it by setting
*   any r, w, r/w mode.
*
*   [Multi-thread note: If a free stream is found, it is returned in a
*   LOCKED state.  It is the caller's responsibility to unlock the stream.]
*
*Exceptions:
*
*******************************************************************************/

FILEX * __cdecl _getstream (
    void
    )
{
    REG2 FILEX *retval = NULL;
    REG1 int i;

    /* Get the iob[] scan lock */
    EnterCriticalSection(&csIobScanLock); // protects the __piob table

    /*
     * Loop through the __piob table looking for a free stream, or the
     * first NULL entry.
     */
    for ( i = 0 ; i < _nstream ; i++ ) 
    {

        if ( __piob[i] != NULL ) 
        {
            //if the stream is not inuse, return it.
            if ( !inuse(__piob[i] ) ) 
            {
                _lock_str2(i, __piob[i]);

                if ( inuse(__piob[i] ) ) 
                {
                    _unlock_str2(i, __piob[i]);
                    continue;
                }
                retval = __piob[i];
                break;
            }
        }
        else 
        {
            // allocate a new _FILEX, set _piob[i] to it and return a pointer to it.
            if (__piob[i] = _malloc_crt(sizeof(_FILEX)))
            {
                memset(__piob[i], 0, sizeof(_FILEX));
                InitializeCriticalSection( &(__piob[i]->lock) );
                __piob[i]->fd = i;
                EnterCriticalSection( &(__piob[i]->lock) );
                retval = __piob[i];
            }
            break;
        }
    }

    // if the __piob array is full, grow it
    if(i==_nstream)
    {
        FILEX** newpiob;
        if(newpiob = _realloc_crt(__piob, (_nstream+NSTREAM_INCREMENT)*sizeof(FILEX*)))
        {
            __piob=newpiob;
            _nstream += NSTREAM_INCREMENT;

            // zero the newly allocated part of the __piob or we'll get GPFs on flsall & other scans
            // __piob[i] now points to the first newly allocated slot
            memset(&(__piob[i]), 0, NSTREAM_INCREMENT*sizeof(FILEX*));
            
            // i now indexes the first free element
            // allocate a new _FILEX, set _piob[i] to it and return a pointer to it.
            if (__piob[i] = _malloc_crt(sizeof(_FILEX)))
            {
                memset(__piob[i], 0, sizeof(_FILEX));
                InitializeCriticalSection( &(__piob[i]->lock) );
                __piob[i]->fd = i;
                EnterCriticalSection( &(__piob[i]->lock) );
                retval = __piob[i];
            }
        }
    }     

    // Initialize the return stream.
    if ( retval != NULL ) 
    {
        retval->_flag = retval->_cnt = 0;
        retval->_ptr = retval->_base = NULL;
        retval->osfhnd = INVALID_HANDLE_VALUE;
        retval->peekch = '\n';
        retval->osfile = 0;
    }

    LeaveCriticalSection(&csIobScanLock);

    return(retval);
}
