/*
 * tnmWinInit.c --
 *
 *	This file contains the Windows specific entry point.
 *
 * Copyright (c) 1996-1997 University of Twente.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "tnmInt.h"
#include "tnmPort.h"

#include <arpa/nameser.h>
#include <resolv.h>

#if defined(__WIN32__)
#   define WIN32_LEAN_AND_MEAN
#   include <windows.h>
#   undef WIN32_LEAN_AND_MEAN

/*
 * VC++ has an alternate entry point called DllMain, so we need to rename
 * our entry point.
 */

#   if defined(_MSC_VER)
#	define EXPORT(a,b) __declspec(dllexport) a b
#	define DllEntryPoint DllMain
#   else
#	if defined(__BORLANDC__)
#	    define EXPORT(a,b) a _export b
#	else
#	    define EXPORT(a,b) a b
#	endif
#   endif
#else
#   define EXPORT(a,b) a b
#endif

/*
 * Forward declarations for procedures defined later in this file:
 */

static void
FixPath				_ANSI_ARGS_((char *path));

static char*
GetRegValue			_ANSI_ARGS_((char *path, char *attribute));

EXTERN EXPORT(int,Tnm_Init)	_ANSI_ARGS_((Tcl_Interp *interp));

EXTERN EXPORT(int,Tnm_SafeInit)	_ANSI_ARGS_((Tcl_Interp *interp));


/*
 *----------------------------------------------------------------------
 *
 * DllEntryPoint --
 *
 *	This wrapper function is used by Windows to invoke the
 *	initialization code for the DLL.  If we are compiling
 *	with Visual C++, this routine will be renamed to DllMain.
 *	routine.
 *
 * Results:
 *	Returns TRUE;
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

#ifdef __WIN32__
BOOL APIENTRY
DllEntryPoint(hInst, reason, reserved)
    HINSTANCE hInst;		/* Library instance handle. */
    DWORD reason;		/* Reason this function is being called. */
    LPVOID reserved;		/* Not used. */
{
    return TRUE;
}
#endif

/*
 *----------------------------------------------------------------------
 *
 * FixPath --
 *
 *	This procedure converts a Windows path into the Tcl 
 *	representation by converting "\\" to "/".
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The path is modified.
 *
 *----------------------------------------------------------------------
 */

static void
FixPath(path)
    char *path;
{
    if (path) {
	for (; *path; path++) {
	    if (*path == '\\') {
		*path = '/';
	    }
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * GetRegValue --
 *
 *	This procedure retrieves a value from the Windows registry.
 *
 * Results:
 *	A pointer to the string containing the value or NULL if the
 *	value can't be obtained. The string points to static memory
 *	and is overwritten by every call to this procedure.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static char *
GetRegValue(path, attribute)
    char *path;
    char *attribute;
{
    int code;
    HKEY key;
    DWORD size;
    static char *value = NULL;

    if (value) {
	ckfree(value);
	value = NULL;
    }

    code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, path, 0, KEY_READ, &key);
    if (code != ERROR_SUCCESS) {
	return NULL;
    }

    code = RegQueryValueEx(key, attribute, NULL, NULL, NULL, &size);
    if (code != ERROR_SUCCESS) {
	return NULL;
    }

    value = ckalloc(size);
    code = RegQueryValueEx(key, attribute, NULL, NULL, value, &size);
    if (code != ERROR_SUCCESS) {
	ckfree(value);
	value = NULL;
    }

    return value;
}

/*
 *----------------------------------------------------------------------
 *
 * TnmInitPath --
 *
 *	This procedure is called to determine the installation path
 *	of the Tnm extension on this system. It uses the Windows
 *	registry to retrieve the the toplevel Tnm directory name.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Initializes the global Tcl variable tnm(library).
 *
 *----------------------------------------------------------------------
 */

void
TnmInitPath(interp)
    Tcl_Interp *interp;
{
    char *p, *path, *tclVersion, *tkVersion, *tclRoot;
    Tcl_DString ds;

    path = GetRegValue("Software\\Scotty\\Tnm\\" TNM_VERSION, "Library");
    if (! path) {
	path = getenv("TNM_LIBRARY");
        if (! path) {
            path = TNMLIB;
        }
    }
    FixPath(path);
    Tcl_SetVar2(interp, "tnm", "library", path, TCL_GLOBAL_ONLY);

    /*
     * Also initialize the tkined(library) variable which is used
     * by tnmIned.c to locate Tkined specific files. This should
     * be removed once Tkined gets integrated into Tnm.
     */

    path = GetRegValue("Software\\Scotty\\Tkined\\" TKI_VERSION, "Library");
    if (! path) {
	path = getenv("TKINED_LIBRARY");
	if (! path) {
	    path = TKINEDLIB;
	}
    }
    FixPath(path);
    Tcl_SetVar2(interp, "tkined", "library", path, TCL_GLOBAL_ONLY);

    /*
     * Locate tclsh and wish so that we can start additional
     * scripts with the correct path name.
     */

    Tcl_DStringInit(&ds);
    tclVersion = Tcl_GetVar(interp, "tcl_version", TCL_GLOBAL_ONLY);
    tkVersion = Tcl_GetVar(interp, "tk_version", TCL_GLOBAL_ONLY);

    if (tclVersion) {
	Tcl_DStringAppend(&ds, "Software\\Sun\\Tcl\\", -1);
	Tcl_DStringAppend(&ds, tclVersion, -1);
	tclRoot = GetRegValue(Tcl_DStringValue(&ds), "Root");
	Tcl_DStringFree(&ds);
    }

    if (tclVersion && tclRoot) {
	Tcl_DStringAppend(&ds, tclRoot, -1);
	Tcl_DStringAppend(&ds, "\\bin\\tclsh", -1);
	for (p = tclVersion; *p; p++) {
	    if (isdigit(*p)) Tcl_DStringAppend(&ds, p, 1);
	}
	Tcl_DStringAppend(&ds, ".exe", -1);
	FixPath(Tcl_DStringValue(&ds));
	if (access(Tcl_DStringValue(&ds), R_OK | X_OK) == 0) {
	    Tcl_SetVar2(interp, "tnm", "tclsh",
			Tcl_DStringValue(&ds), TCL_GLOBAL_ONLY);
	}
	Tcl_DStringFree(&ds);
    }

    if (tkVersion && tclRoot) {
	Tcl_DStringAppend(&ds, tclRoot, -1);
	Tcl_DStringAppend(&ds, "\\bin\\wish", -1);
	for (p = tkVersion; *p; p++) {
	    if (isdigit(*p)) Tcl_DStringAppend(&ds, p, 1);
	}
	Tcl_DStringAppend(&ds, ".exe", -1);
	FixPath(Tcl_DStringValue(&ds));
	if (access(Tcl_DStringValue(&ds), R_OK | X_OK) == 0) {
	    Tcl_SetVar2(interp, "tnm", "wish",
			Tcl_DStringValue(&ds), TCL_GLOBAL_ONLY);
	}
	Tcl_DStringFree(&ds);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TnmInitDns --
 *
 *	This procedure is called to initialize the DNS resolver and to
 *	save the domain name in the global tnm(domain) Tcl variable.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void
TnmInitDns(interp)
    Tcl_Interp *interp;
{
    char domain[MAXDNAME], *p;
    char *dir = "System\\CurrentControlSet\\Services\\TcpIP\\Parameters";
    char *nameServer = GetRegValue(dir, "NameServer");
    char *domainName = GetRegValue(dir, "Domain");

    /*
     * Start by getting the list of name servers and the domain name
     * from the Windows registry.
     */

    if (nameServer == NULL || domainName == NULL) {
	dir = "System\\CurrentControlSet\\Services\\VxD\\MSTCP";
	if (nameServer == NULL) {
	    nameServer = GetRegValue(dir, "NameServer");
	}
	if (domainName == NULL) {
	    domainName = GetRegValue(dir, "Domain");
	}
    }

    res_init();
    _res.options |= RES_RECURSE | RES_DNSRCH | RES_DEFNAMES | RES_AAONLY;
    
    /*
     * Set the list of default DNS servers. Note, this should be
     * done automatically by the resolver but it is obviously
     * not on Windows machines.
     */

    if (! nameServer) {
	TnmSetIPAddress((Tcl_Interp *) NULL, "127.0.0.1", 
			&_res.nsaddr_list[0]);
	_res.nscount = 1;
    } else {
	int i = 0;
	char *s = strtok(nameServer, ",");
	while (s && i < MAXNS) {
	    TnmSetIPAddress((Tcl_Interp *) NULL, s, &_res.nsaddr_list[i]);
	    s = strtok(NULL, ",");
	    i++;
	}
	_res.nscount = i;
    }

    /*
     * Set the default domain name for the resolver, if not done
     * automatically.
     */
    
    if (! _res.defdname[0] && domainName && strlen(domainName) < MAXDNAME) {
	strcpy(_res.defdname, domainName);
    }

    /*
     * Remove the any trailing dots or white spaces and save the
     * result in tnm(domain).
     */

    strcpy(domain, _res.defdname);
    p = domain + strlen(domain) - 1;
    while ((*p == '.' || isspace(*p)) && p > domain) {
	*p-- = '\0';
    }
    Tcl_SetVar2(interp, "tnm", "domain", domain, TCL_GLOBAL_ONLY);
}

/*
 *----------------------------------------------------------------------
 *
 * Tnm_Init --
 *
 *	This procedure is the Windows entry point for trusted Tcl
 *	interpreters.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	Tcl variables are created.
 *
 *----------------------------------------------------------------------
 */

EXPORT(int,Tnm_Init)(interp)
    Tcl_Interp *interp;
{
    return TnmInit(interp, 0);
}

/*
 *----------------------------------------------------------------------
 *
 * Tnm_SafeInit --
 *
 *	This procedure is the Windows entry point for safe Tcl
 *	interpreters.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	Tcl variables are created.
 *
 *----------------------------------------------------------------------
 */

EXPORT(int,Tnm_SafeInit)(interp)
    Tcl_Interp *interp;
{
    return TnmInit(interp, 1);
}
