/* 
 * tclFHandle.c --
 *
 *	This file contains functions for manipulating Tcl file handles.
 *
 * Copyright (c) 1995 Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * SCCS: @(#) tclFHandle.c 1.9 96/07/01 15:41:26
 */

#include "tcl.h"
#include "tclInt.h"
#include "tclPort.h"

/*
 * The FileHashKey structure is used to associate the OS file handle and type
 * with the corresponding notifier data in a FileHandle.
 */

typedef struct FileHashKey {
    int type;			/* File handle type. */
    ClientData osHandle;	/* Platform specific OS file handle. */
} FileHashKey;

typedef struct FileHandle {
    FileHashKey key;		/* Hash key for a given file. */
    ClientData data;		/* Platform specific notifier data. */
    Tcl_FileFreeProc *proc;	/* Callback to invoke when file is freed. */
} FileHandle;

/*
 * Static variables used in this file:
 */

static Tcl_HashTable fileTable;	/* Hash table containing file handles. */
static int initialized = 0;	/* 1 if this module has been initialized. */

/*
 * Static procedures used in this file:
 */

static void 		FileExitProc _ANSI_ARGS_((ClientData clientData));

/*
 *----------------------------------------------------------------------
 *
 * Tcl_GetFile --
 *
 *	This function retrieves the file handle associated with a
 *	platform specific file handle of the given type.  It creates
 *	a new file handle if needed.
 *
 * Results:
 *	Returns the file handle associated with the file descriptor.
 *
 * Side effects:
 *	Initializes the file handle table if necessary.
 *
 *----------------------------------------------------------------------
 */

Tcl_File
Tcl_GetFile(osHandle, type)
    ClientData osHandle;	/* Platform specific file handle. */
    int type;			/* Type of file handle. */
{
    FileHashKey key;
    Tcl_HashEntry *entryPtr;
    int new;

    if (!initialized) {
	Tcl_InitHashTable(&fileTable, sizeof(FileHashKey)/sizeof(int));
	Tcl_CreateExitHandler(FileExitProc, 0);
	initialized = 1;
    }
    key.osHandle = osHandle;
    key.type = type;
    entryPtr = Tcl_CreateHashEntry(&fileTable, (char *) &key, &new);
    if (new) {
	FileHandle *newHandlePtr;
	newHandlePtr = (FileHandle *) ckalloc(sizeof(FileHandle));
	newHandlePtr->key = key;
	newHandlePtr->data = NULL;
	newHandlePtr->proc = NULL;
	Tcl_SetHashValue(entryPtr, newHandlePtr);
    }
    
    return (Tcl_File) Tcl_GetHashValue(entryPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_FreeFile --
 *
 *	Deallocates an entry in the file handle table.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void
Tcl_FreeFile(handle)
    Tcl_File handle;
{
    Tcl_HashEntry *entryPtr;
    FileHandle *handlePtr = (FileHandle *) handle;
    
    /*
     * Invoke free procedure, then delete the handle.
     */

    if (handlePtr->proc) {
	(*handlePtr->proc)(handlePtr->data);
    }

    /*
     * Tcl_File structures may be freed as a result of running the
     * channel table exit handler. The file table is freed by the file
     * table exit handler, which may run before the channel table exit
     * handler. The file table exit handler sets the "initialized"
     * variable back to zero, so that the Tcl_FreeFile (when invoked
     * from the channel table exit handler) can notice that the file
     * table has already been destroyed. Otherwise, accessing a
     * deleted hash table would cause a panic.
     */
     
    if (initialized) {
        entryPtr = Tcl_FindHashEntry(&fileTable, (char *) &handlePtr->key);
        if (entryPtr) {
            Tcl_DeleteHashEntry(entryPtr);
        }
    }
    ckfree((char *) handlePtr);
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_GetFileInfo --
 *
 *	This function retrieves the platform specific file data and
 *	type from the file handle.
 *
 * Results:
 *	If typePtr is not NULL, sets *typePtr to the type of the file.
 *	Returns the platform specific file data.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

ClientData
Tcl_GetFileInfo(handle, typePtr)
    Tcl_File handle;
    int *typePtr;
{
    FileHandle *handlePtr = (FileHandle *) handle;

    if (typePtr) {
	*typePtr = handlePtr->key.type;
    }
    return handlePtr->key.osHandle;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_SetNotifierData --
 *
 *	This function is used by the notifier to associate platform
 *	specific notifier information and a deletion procedure with
 *	a file handle.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Updates the data and delProc slots in the file handle.
 *
 *----------------------------------------------------------------------
 */

void
Tcl_SetNotifierData(handle, proc, data)
    Tcl_File handle;
    Tcl_FileFreeProc *proc;
    ClientData data;
{
    FileHandle *handlePtr = (FileHandle *) handle;
    handlePtr->proc = proc;
    handlePtr->data = data;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_GetNotifierData --
 *
 *	This function is used by the notifier to retrieve the platform
 *	specific notifier information associated with a file handle.
 *
 * Results:
 *	Returns the data stored in a file handle by a previous call to
 *	Tcl_SetNotifierData, and places a pointer to the free proc
 *	in the location referred to by procPtr.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

ClientData
Tcl_GetNotifierData(handle, procPtr)
    Tcl_File handle;
    Tcl_FileFreeProc **procPtr;
{
    FileHandle *handlePtr = (FileHandle *) handle;
    if (procPtr != NULL) {
	*procPtr = handlePtr->proc;
    }
    return handlePtr->data;
}

/*
 *----------------------------------------------------------------------
 *
 * FileExitProc --
 *
 *	This function an exit handler that frees any memory allocated
 *	for the file handle table.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Cleans up the file handle table.
 *
 *----------------------------------------------------------------------
 */

static void
FileExitProc(clientData)
    ClientData clientData;	/* Not used. */
{
    Tcl_DeleteHashTable(&fileTable);
    initialized = 0;
}
