Put tclresolver working files into version control.
authorPat Thoyts <patthoyts@users.sourceforge.net>
Thu, 26 Aug 2004 09:47:48 +0000 (09:47 +0000)
committerPat Thoyts <patthoyts@users.sourceforge.net>
Thu, 26 Aug 2004 09:47:48 +0000 (09:47 +0000)
Makefile [new file with mode: 0644]
demo/demo.tcl [new file with mode: 0644]
library/resolver.tcl [new file with mode: 0644]
resolvchk.c [new file with mode: 0644]
tclresolver2.c [new file with mode: 0644]
tclresolver3.c [new file with mode: 0644]
w32cat.c [new file with mode: 0644]
win/resolver.c [new file with mode: 0644]
win/tclresolver.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..6df5a7d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,49 @@
+
+CC     =cl -nologo
+CFLAGS =-Ox -W3 -MD
+LD     =link -nologo
+LDFLAGS=-dll
+
+DEFS   =-DUSE_TCL_STUBS
+INC    =-Ic:\opt\tcl\include
+LIBS   =c:\opt\tcl\lib\tclstub84.lib ws2_32.lib
+
+all: resolver.exe resolvchk.exe tclresolver.dll tclresolver2.dll tclresolver3.dll
+
+resolver.exe: resolver.c
+       $(CC) $(CFLAGS) -o $@ $**
+
+resolvchk.exe: resolvchk.c
+       $(CC) $(CFLAGS) -o $@ $**
+
+tclresolver.dll: tclresolver.obj
+       $(LD) $(LDFLAGS) -out:$@ $(LIBS) $**
+
+tclresolver2.dll: tclresolver2.obj
+       $(LD) $(LDFLAGS) -out:$@ $(LIBS) $**
+
+tclresolver3.dll: tclresolver3.obj
+       $(LD) $(LDFLAGS) -out:$@ $(LIBS) $**
+
+.c.obj::
+       $(CC) $(CFLAGS) $(DEFS) $(INC) -c @<<
+$<
+<<
+
+clean:
+       @if exist tclresolver.obj del tclresolver.obj
+       @if exist tclresolver2.obj del tclresolver2.obj
+       @if exist tclresolver3.obj del tclresolver3.obj
+
+realclean: clean
+       @if exist tclresolver.dll del tclresolver.dll
+       @if exist tclresolver.exp del tclresolver.exp
+       @if exist tclresolver.lib del tclresolver.lib
+       @if exist tclresolver2.dll del tclresolver2.dll
+       @if exist tclresolver2.exp del tclresolver2.exp
+       @if exist tclresolver2.lib del tclresolver2.lib
+       @if exist tclresolver3.dll del tclresolver3.dll
+       @if exist tclresolver3.exp del tclresolver3.exp
+       @if exist tclresolver3.lib del tclresolver3.lib
+       @if exist resolver.exe del resolver.exe
+       @if exist resolvchk.exe del resolvchk.exe
diff --git a/demo/demo.tcl b/demo/demo.tcl
new file mode 100644 (file)
index 0000000..57b6093
--- /dev/null
@@ -0,0 +1,80 @@
+if {[file exists tclresolver3.dll]} {
+    load tclresolver3.dll Tclresolver
+} else {
+    package require Tclresolver
+}
+
+set hostname [info hostname]
+set uid 0
+
+proc every {aidname n args} {
+    upvar #0 $aidname aid
+    catch $args
+    set aid [after $n [eval list every $aidname $n $args]]
+}
+
+proc display {s} {
+    if {[info exists ::tk_library]} {
+        .t insert end $s
+        .t see end
+    } else {
+        puts -nonewline $s ; flush stdout
+    }
+}
+
+proc lookup {} {
+    global hostname
+    set aidname ::aid[incr ::uid]
+    upvar $aidname aid
+    every $aidname 25 display .
+    set ips [resolve $hostname]
+    after cancel $aid
+    unset aid
+    display "\n$ips\n"
+}
+    
+proc gui {} {
+    wm title . "Tclresolver [package provide Tclresolver]"
+
+    frame .f
+    label .l -text Hostname -underline 0
+    entry .e -textvariable ::hostname
+    button .h -text Lookup -width -12 -underline 0 -command lookup \
+        -default active
+    button .x -text Exit   -width -12 -underline 1 -command {destroy .}
+    text .t -yscrollcommand {.s set} -height 10
+    scrollbar .s -command {.t yview}
+
+    pack .l -in .f -side left
+    pack .e -in .f -side left -expand 1 -fill x -padx 4
+    pack .h .x -in .f -side left
+
+    grid .t .s -sticky news
+    grid .f  - -sticky news
+    grid rowconfigure . 0 -weight 1
+    grid columnconfigure . 0 -weight 1
+
+    bind . <Alt-h> {focus .e}
+    bind . <Alt-l> {focus .h}
+    bind . <Alt-x> {focus .x}
+    bind . <Return> {.h invoke}
+    bind . <Escape> {.x invoke}
+    if {[string equal [tk windowingsystem] "win32"]} {
+        bind . <Control-o> {console show}
+    }
+
+    tkwait visibility .t
+    after 100 {set ::wait 1} ; tkwait variable ::wait
+    focus .e
+    tkwait window .
+    return
+}
+
+
+if {[package provide Tk] != {}} {
+    gui
+} else {
+    puts "tclresolver [package provide Tclresolver]"
+    set hostname [lindex $argv 0]
+    lookup
+}
diff --git a/library/resolver.tcl b/library/resolver.tcl
new file mode 100644 (file)
index 0000000..9628903
--- /dev/null
@@ -0,0 +1,84 @@
+# resolver.tcl - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sf.net>
+#
+#
+#
+# -------------------------------------------------------------------------
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+# -------------------------------------------------------------------------
+# $Id$
+
+namespace eval ::resolver {
+    variable version 1.0.0
+    variable channel
+    namespace export gethostbyname
+}
+
+# -------------------------------------------------------------------------
+
+proc ::resolver::Read {} {
+    variable done
+    variable channel
+    if {[eof $channel]} {
+        fileevent $channel readable {}
+        return
+    }
+    set done [gets $channel]
+}
+
+proc ::resolver::Write {host} {
+    variable channel
+    fileevent $channel writable {}
+    puts $channel $host
+    flush $channel
+}
+
+proc ::resolver::Init {} {
+    variable done
+    variable channel
+    set cmd [auto_execok resolver]
+    set channel [open |$cmd w+]
+    fconfigure $channel -buffering line -blocking 1
+}
+
+proc ::resolver::Shutdown {} {
+    variable channel
+    catch {close $channel} msg
+}
+
+proc ::resolver::gethostbyname {host} {
+    variable done
+    variable channel
+    set done {}
+    fileevent $channel readable [list [namespace origin Read]]
+    fileevent $channel writable [list [namespace origin Write] $host]
+    vwait [namespace current]::done
+    return $done
+}
+
+# package initialization - we will create the child resolver process on loading
+# this package.
+#
+namespace eval ::resolver {
+    if {![info exists channel]} {
+        Init
+    }
+}
+
+# -------------------------------------------------------------------------
+
+package provide resolver $::resolver::version
+
+# -------------------------------------------------------------------------
+
+if {! $tcl_interactive} {
+    foreach host {binky aol.com www.microsoft.com slashdot.org} {
+        set a [resolver::gethostbyname $host]
+        foreach h $a {
+            puts [list $host $h]
+        }
+    }
+    
+    # We don't need to do this explicitly. It'll close when the app exits.
+    # Resolver::Shutdown
+}
diff --git a/resolvchk.c b/resolvchk.c
new file mode 100644 (file)
index 0000000..ce8db7e
--- /dev/null
@@ -0,0 +1,233 @@
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <tchar.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define BUFSIZE 4096
+
+typedef struct {
+    HANDLE hReady;
+    HANDLE hDone;
+    HANDLE hIn;
+    HANDLE hOut;
+    LPBYTE pData;
+    BOOL   bRunning;
+} LookupInfo;
+
+static LPCTSTR LookupHostname(LookupInfo * pLookupInfo, LPCTSTR szHostname);
+static DWORD WINAPI Reader(LPVOID clientData);
+static BOOL CreateChildProcess(HANDLE hChildStdoutWr, HANDLE hChildStdinRd);
+static void ErrorExit(LPTSTR);
+static void Win32Error(FILE *fp, LPCTSTR szMessage, HRESULT hr);
+
+DWORD
+_tmain(int argc, LPCTSTR argv[]) 
+{
+    HANDLE hChildStdinWr, hChildStdinWrDup, hChildStdoutRd, hChildStdoutRdDup;
+    HANDLE hChildStdinRd, hChildStdoutWr;
+    LookupInfo lookupinfo;
+    HANDLE hReaderThread = INVALID_HANDLE_VALUE;
+    DWORD idReaderThread = 0;
+    SECURITY_ATTRIBUTES saAttr; 
+    BOOL fSuccess;
+    LPCTSTR szAddress;
+    int n;
+
+    if (argc < 2) {
+        ErrorExit(_T("usage: resolvchk hostname ?hostname ...?"));
+    }
+
+    // Set the bInheritHandle flag so pipe handles are inherited.
+    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+    saAttr.bInheritHandle = TRUE;
+    saAttr.lpSecurityDescriptor = NULL;
+
+    // Create a pipe for the child process's STDOUT.
+    if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
+        ErrorExit("Stdout pipe creation failed\n");
+
+    // Create noninheritable read handle and close the inheritable read
+    // handle.
+    fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
+                               GetCurrentProcess(), &hChildStdoutRdDup,
+                               0, FALSE,
+                               DUPLICATE_SAME_ACCESS);
+    if( !fSuccess )
+        ErrorExit("DuplicateHandle failed");
+    CloseHandle(hChildStdoutRd);
+
+    // Create a pipe for the child process's STDIN.
+    if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))
+        ErrorExit("Stdin pipe creation failed\n");
+
+    // Duplicate the write handle to the pipe so it is not inherited.
+    fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
+                               GetCurrentProcess(), &hChildStdinWrDup,
+                               0, FALSE,                  // not inherited
+                               DUPLICATE_SAME_ACCESS);
+    if (! fSuccess)
+        ErrorExit("DuplicateHandle failed");
+
+    CloseHandle(hChildStdinWr);
+
+    // Create an event for lookup synch.
+    lookupinfo.hReady = CreateEvent(NULL, FALSE, FALSE, NULL);
+    lookupinfo.hDone = CreateEvent(NULL, FALSE, FALSE, NULL);
+    lookupinfo.hIn = hChildStdoutRdDup;
+    lookupinfo.hOut = hChildStdinWrDup;
+    lookupinfo.bRunning = TRUE;
+    lookupinfo.pData = (LPBYTE)malloc(BUFSIZE);
+
+    // Create reader thread.
+    hReaderThread = CreateThread(NULL, 0, Reader, &lookupinfo,
+                                 0, &idReaderThread);
+
+    // Now create the child process.
+    fSuccess = CreateChildProcess(hChildStdoutWr, hChildStdinRd);
+    if (! fSuccess)
+        ErrorExit("Create process failed");
+
+    // Perform the address lookup.
+    for (n = 1; n < argc; n++) {
+        szAddress = LookupHostname(&lookupinfo, argv[n]);
+        _ftprintf(stdout, _T("'%s'\n"), szAddress);
+    }
+
+    lookupinfo.bRunning = FALSE;
+    free((LPBYTE)lookupinfo.pData);
+
+    // Close the pipe handle so the child process stops reading and exits.
+    if (! CloseHandle(hChildStdinWrDup))
+        ErrorExit("Close pipe failed");
+    if (!CloseHandle(hChildStdoutWr))
+        ErrorExit("CloseHandle failed");
+
+    return 0;
+}
+
+static LPCTSTR
+LookupHostname(LookupInfo *pLookupInfo, LPCTSTR szHostname)
+{
+    DWORD dwWrote;
+    BOOL br;
+    
+    ZeroMemory(pLookupInfo->pData, BUFSIZE);
+    lstrcpy((LPTSTR)pLookupInfo->pData, szHostname);
+    lstrcat((LPTSTR)pLookupInfo->pData, _T("\n"));
+    br = WriteFile(pLookupInfo->hOut, pLookupInfo->pData,
+                   lstrlen((LPTSTR)pLookupInfo->pData) * sizeof(TCHAR),
+                   &dwWrote, NULL);
+    if (br)
+        br = FlushFileBuffers(pLookupInfo->hOut);
+    if (br) {
+        ZeroMemory(pLookupInfo->pData, BUFSIZE);
+        SetEvent(pLookupInfo->hReady);
+        WaitForSingleObject(pLookupInfo->hDone, INFINITE);
+    }
+    if (!br) {
+        Win32Error(stderr, _T("lookup hostname"), GetLastError());
+    }
+    return (LPCTSTR)pLookupInfo->pData;
+}
+
+static DWORD WINAPI
+Reader(LPVOID clientData)
+{
+    LookupInfo *pLookupInfo = (LookupInfo *)clientData;
+    DWORD dwErr = 0, dwRead = 0;
+    BOOL br;
+
+    while (pLookupInfo->bRunning) {
+        dwErr = WaitForSingleObject(pLookupInfo->hReady, 500);
+        if (dwErr == WAIT_OBJECT_0) {
+            br = ReadFile(pLookupInfo->hIn, pLookupInfo->pData,
+                          BUFSIZE, &dwRead, NULL);
+
+            if (dwRead > 0) {
+                LPTSTR p = (LPTSTR)pLookupInfo->pData;
+                for (p = p + lstrlen(p) - 1;
+                     p > pLookupInfo->pData && _istspace(*p);
+                     p--)
+                    ;
+                *++p = 0;
+            }
+            SetEvent(pLookupInfo->hDone);
+        } else {
+            Win32Error(stderr, _T("reader wait"), dwErr);
+        }
+    }
+    return 0;
+}
+
+static BOOL
+CreateChildProcess(HANDLE hChildStdoutWr, HANDLE hChildStdinRd) 
+{
+    PROCESS_INFORMATION piProcInfo;
+    STARTUPINFO siStartInfo;
+    BOOL bFuncRetn = FALSE;
+
+    // Set up members of the PROCESS_INFORMATION structure.
+    ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
+
+    // Set up members of the STARTUPINFO structure.
+    ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
+    siStartInfo.cb = sizeof(STARTUPINFO);
+    siStartInfo.hStdError = hChildStdoutWr;
+    siStartInfo.hStdOutput = hChildStdoutWr;
+    siStartInfo.hStdInput = hChildStdinRd;
+    siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
+    // Create the child process.
+    bFuncRetn = CreateProcess(NULL,
+                              "resolver",    // command line 
+                              NULL,          // process security attributes 
+                              NULL,          // primary thread security attributes 
+                              TRUE,          // handles are inherited 
+                              0,             // creation flags 
+                              NULL,          // use parent's environment 
+                              NULL,          // use parent's current directory 
+                              &siStartInfo,  // STARTUPINFO pointer 
+                              &piProcInfo);  // receives PROCESS_INFORMATION 
+   
+    if (bFuncRetn == 0) {
+        ErrorExit("CreateProcess failed");
+        return FALSE;
+    } else {
+        CloseHandle(piProcInfo.hProcess);
+        CloseHandle(piProcInfo.hThread);
+        return bFuncRetn;
+    }
+}
+static void
+ErrorExit (LPTSTR lpszMessage)
+{
+    _ftprintf(stderr, _T("%s\n"), lpszMessage);
+    ExitProcess(0);
+}
+
+static void
+Win32Error(FILE *fp, LPCTSTR szMessage, HRESULT hr)
+{
+    LPTSTR lpBuffer = NULL;
+    DWORD  dwLen = 0;
+    
+    dwLen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER 
+                          | FORMAT_MESSAGE_FROM_SYSTEM,
+                          NULL, (DWORD)hr, LANG_NEUTRAL,
+                          (LPTSTR)&lpBuffer, 0, NULL);
+    if (dwLen < 1) {
+        dwLen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER 
+                              | FORMAT_MESSAGE_FROM_STRING
+                              | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+                              _T("code 0x%1!08X!%n"), 0, LANG_NEUTRAL,
+                              (LPTSTR)&lpBuffer, 0, (va_list *)&hr);
+    }
+    _ftprintf(fp, _T("%s"), szMessage);
+    if (dwLen > 0) {
+        _ftprintf(fp, _T(": "));
+        _ftprintf(fp, lpBuffer);
+    }
+    LocalFree((HLOCAL)lpBuffer);
+}
diff --git a/tclresolver2.c b/tclresolver2.c
new file mode 100644 (file)
index 0000000..6684408
--- /dev/null
@@ -0,0 +1,319 @@
+/* tclresolver.c - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sf.net>
+ *
+ *
+ *
+ * $Id$
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h> 
+#include <tchar.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#include <tcl.h>
+
+#if _MSC_VER >= 1000
+#pragma comment(lib, "ws2_32")
+#endif
+
+#define BUFSIZE 4096
+#undef TCL_STORAGE_CLASS
+#define TCL_STORAGE_CLASS DLLEXPORT
+
+static const char ASSOC_KEY[] = "tclresolver_key";
+
+EXTERN int Tclresolver_Init(Tcl_Interp *interp);
+EXTERN int Tclresolver_Unload(Tcl_Interp *interp);
+EXTERN int TclGetHostByName(LPSOCKADDR_IN saddrPtr, 
+                            const char *hostname, int port);
+
+typedef struct {
+    HANDLE hReader;
+    DWORD  idReader;
+    HANDLE hReady;
+    HANDLE hDone;
+    LPBYTE pData;
+    BOOL   bRunning;
+    Tcl_ThreadId tclid;
+    Tcl_Command command;
+} LOOKUPINFO, *LPLOOKUPINFO;
+
+typedef struct {
+    Tcl_Event header;
+    LPLOOKUPINFO info;
+} RESOLVEREVENT, *LPRESOLVEREVENT;
+
+static Tcl_ObjCmdProc TclresolverObjCmd;
+static Tcl_ExitProc Terminate;
+static int Initialize(Tcl_Interp *interp, LPLOOKUPINFO pLookupInfo);
+static LPCTSTR LookupHostname(LPLOOKUPINFO pLookupInfo, LPCTSTR szHostname);
+static DWORD WINAPI Reader(LPVOID clientData);
+static void QueueEvent(LPLOOKUPINFO pLookupInfo);
+static int  ResolverEventProc(Tcl_Event *evPtr, int flags);
+static int GetAddress(char *query);
+static Tcl_Obj *Win32Error(const char * szPrefix, HRESULT hr);
+
+/* ---------------------------------------------------------------------- */
+
+int
+Tclresolver_Init(Tcl_Interp *interp)
+{
+    int initialized = 0;
+    LPLOOKUPINFO pLookupInfo = NULL;
+
+#ifdef USE_TCL_STUBS
+    if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
+        return TCL_ERROR;
+    }
+#endif
+    
+    pLookupInfo = (LPLOOKUPINFO)Tcl_Alloc(sizeof(LOOKUPINFO));
+    if (pLookupInfo == NULL) {
+        Tcl_SetResult(interp, "out of memory", TCL_STATIC);
+        return TCL_ERROR;
+    }
+    ZeroMemory(pLookupInfo, sizeof(LOOKUPINFO));
+
+    if (Initialize(interp, pLookupInfo) != TCL_OK) {
+        Tcl_Free((char*)pLookupInfo);
+        return TCL_ERROR;
+    }
+    pLookupInfo->tclid = Tcl_GetCurrentThread();
+    pLookupInfo->command = Tcl_CreateObjCommand(interp, "resolve", 
+                                                TclresolverObjCmd,
+                                                (ClientData)pLookupInfo,
+                                                (Tcl_CmdDeleteProc *)NULL);
+    Tcl_CreateExitHandler(Terminate, (ClientData)pLookupInfo);
+    Tcl_SetAssocData(interp, ASSOC_KEY, NULL, (ClientData)pLookupInfo);
+    return Tcl_PkgProvide(interp, "Tclresolver", "1.2.0");
+}
+
+EXTERN int
+Tclresolver_SafeInit(Tcl_Interp *interp)
+{
+    return Tclresolver_Init(interp);
+}
+
+int 
+Tclresolver_Unload(Tcl_Interp *interp)
+{
+    LPLOOKUPINFO pLookupInfo = NULL;
+    pLookupInfo = (LPLOOKUPINFO)Tcl_GetAssocData(interp, ASSOC_KEY, NULL);
+    Tcl_DeleteCommandFromToken(interp, pLookupInfo->command);
+    Terminate((ClientData)pLookupInfo);
+    Tcl_SetAssocData(interp, ASSOC_KEY, NULL, (ClientData)NULL);    
+    return TCL_OK;
+}
+
+static int
+TclresolverObjCmd(ClientData clientData, Tcl_Interp *interp, 
+                  int objc, Tcl_Obj *CONST objv[])
+{
+    LPLOOKUPINFO pLookupInfo = (LPLOOKUPINFO)clientData;
+    Tcl_Obj *resObj = NULL;
+    if (objc != 2) {
+        Tcl_WrongNumArgs(interp, objc, objv, "hostname");
+        return TCL_ERROR;
+    }
+    resObj = Tcl_NewStringObj(LookupHostname(pLookupInfo,
+                                             Tcl_GetString(objv[1])), -1);
+    Tcl_SetObjResult(interp, resObj);
+    return TCL_OK;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int
+Initialize(Tcl_Interp *interp, LPLOOKUPINFO pLookupInfo) 
+{
+    WSADATA wsd;
+    
+    OutputDebugString(_T("Initialize\n"));
+    
+    /* Initialize windows sockets. */
+    if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
+       Tcl_Obj *errObj = Win32Error("WSAStartup error", GetLastError());
+        Tcl_SetObjResult(interp, errObj);
+        return TCL_ERROR;
+    }
+    
+    if (LOBYTE(wsd.wVersion) != 2 ||
+        HIBYTE(wsd.wVersion) != 2) {
+        Tcl_SetResult(interp,
+           "error: failed to load a compatible winsock version", TCL_STATIC);
+       WSACleanup();
+        return TCL_ERROR;
+    }
+
+    /* Create synchronization events */
+    pLookupInfo->hReady = CreateEvent(NULL, FALSE, FALSE, NULL);
+    pLookupInfo->hDone  = CreateEvent(NULL, FALSE, FALSE, NULL);
+    pLookupInfo->bRunning = TRUE;
+    pLookupInfo->pData = (LPBYTE)Tcl_Alloc(BUFSIZE);
+
+    /* Create the reader thread */
+    pLookupInfo->hReader = CreateThread(NULL, 0, Reader, pLookupInfo,
+                                        0, &pLookupInfo->idReader);
+
+    return TCL_OK;
+}
+
+static void
+Terminate(ClientData clientData)
+{
+    LPLOOKUPINFO pLookupInfo = (LPLOOKUPINFO)clientData;
+    OutputDebugString(_T("Terminate\n"));
+    pLookupInfo->bRunning = FALSE;
+    Tcl_DeleteExitHandler(Terminate, clientData);
+    WaitForSingleObject(pLookupInfo->hReader, 400);
+    CloseHandle(pLookupInfo->hReader);
+    CloseHandle(pLookupInfo->hReady);
+    CloseHandle(pLookupInfo->hDone);
+    Tcl_Free(pLookupInfo->pData);
+    Tcl_Free((LPBYTE)pLookupInfo);
+    WSACleanup();
+}
+
+static LPCTSTR
+LookupHostname(LPLOOKUPINFO pLookupInfo, LPCTSTR szHostname)
+{
+    DWORD dwWait;
+    
+    /* copy the hostname into out buffer */
+    ZeroMemory(pLookupInfo->pData, BUFSIZE);
+    lstrcpy((LPTSTR)pLookupInfo->pData, szHostname);
+    
+    /* notify the reader thread that it should begin reading */
+    SetEvent(pLookupInfo->hReady);
+
+    /* Run a tcl event loop while we wait for the answer */
+    while (1) {
+        Tcl_DoOneEvent(0); /* when tk loaded */
+        //Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT);
+        OutputDebugString("done one event\n");
+        dwWait = WaitForSingleObject(pLookupInfo->hDone, 0);
+        if (dwWait == WAIT_OBJECT_0)
+            break;
+    }
+
+    return (LPCTSTR)pLookupInfo->pData;
+}
+
+static DWORD WINAPI
+Reader(LPVOID clientData)
+{
+    LPLOOKUPINFO pLookupInfo = (LPLOOKUPINFO)clientData;
+    DWORD dwErr = 0, dwRead = 0;
+
+    while (pLookupInfo->bRunning) {
+        dwErr = WaitForSingleObject(pLookupInfo->hReady, 500);
+        if (dwErr == WAIT_OBJECT_0) {
+           GetAddress((char *)pLookupInfo->pData);
+            QueueEvent(pLookupInfo);
+        }
+    }
+    return 0;
+}
+
+static void
+QueueEvent(LPLOOKUPINFO pLookupInfo)
+{
+    LPRESOLVEREVENT evPtr;
+    OutputDebugString(_T("QueueEvent\n"));
+    evPtr = (LPRESOLVEREVENT)Tcl_Alloc(sizeof(RESOLVEREVENT));
+    evPtr->header.proc = ResolverEventProc;
+    evPtr->header.nextPtr = NULL;
+    evPtr->info = pLookupInfo;
+    Tcl_ThreadQueueEvent(pLookupInfo->tclid, 
+                         (Tcl_Event *)evPtr, TCL_QUEUE_TAIL);
+}
+
+static int
+ResolverEventProc(Tcl_Event *evPtr, int flags)
+{
+    LPRESOLVEREVENT revPtr = (LPRESOLVEREVENT)evPtr;
+    OutputDebugString(_T("ResolverEventProc\n"));
+    SetEvent(revPtr->info->hDone);
+    return 1;
+}
+
+static Tcl_Obj *
+Win32Error(const char * szPrefix, HRESULT hr)
+{
+    Tcl_Obj *msgObj = NULL;
+    char * lpBuffer = NULL;
+    DWORD  dwLen = 0;
+    
+    dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER 
+                          | FORMAT_MESSAGE_FROM_SYSTEM,
+                          NULL, (DWORD)hr, LANG_NEUTRAL,
+                          (LPTSTR)&lpBuffer, 0, NULL);
+    if (dwLen < 1) {
+        dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER 
+                              | FORMAT_MESSAGE_FROM_STRING
+                              | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+                              "code 0x%1!08X!%n", 0, LANG_NEUTRAL,
+                              (LPTSTR)&lpBuffer, 0, (va_list *)&hr);
+    }
+
+    msgObj = Tcl_NewStringObj(szPrefix, -1);
+    if (dwLen > 0) {
+       char *p = lpBuffer + dwLen - 1;        /* remove cr-lf at end */
+       for ( ; p && *p && isspace(*p); p--)
+           ;
+       *++p = 0;
+       Tcl_AppendToObj(msgObj, ": ", 2);
+       Tcl_AppendToObj(msgObj, lpBuffer, -1);
+    }
+    LocalFree((HLOCAL)lpBuffer);
+    return msgObj;
+}
+
+static int
+GetAddress(char *query)
+{
+    struct addrinfo hints = {0};
+    struct addrinfo *res = NULL;
+    char *hostname, *p, *q;
+    int r = 0, nc = 0;
+    
+    /* trim whitespace */
+    for (p = query; *p && isspace(*p); p++)
+       ;
+    for (q = p + strlen(p) - 1; q > p && *q && isspace(*q); q--)
+       ;
+    *++q = 0;
+    hostname = p;
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = PF_UNSPEC;
+    hints.ai_flags = AI_CANONNAME;
+    hints.ai_socktype = SOCK_STREAM;
+    r = getaddrinfo(hostname, "", &hints, &res);
+    *query = 0;
+
+    if (r != 0) {
+       Tcl_Obj *o = Win32Error("error", WSAGetLastError());
+       strcpy(query, Tcl_GetString(o));
+       Tcl_DecrRefCount(o);
+    } else {
+       struct addrinfo *resPtr = res;
+       for (resPtr = res; resPtr != NULL; resPtr = resPtr->ai_next) {
+           char name[NI_MAXHOST];
+
+           getnameinfo(resPtr->ai_addr, resPtr->ai_addrlen, 
+                       name, NI_MAXHOST,
+                       NULL, 0,
+                       NI_NUMERICHOST | NI_NUMERICSERV);
+
+           if (resPtr != res)
+               strcat(query, " ");
+           strcat(query, name);
+       }
+       freeaddrinfo(res);
+    }
+    return r;
+}
+    
+
diff --git a/tclresolver3.c b/tclresolver3.c
new file mode 100644 (file)
index 0000000..d465b5b
--- /dev/null
@@ -0,0 +1,336 @@
+/* tclresolver.c - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sf.net>
+ *
+ * Non-blocking name resolution.
+ *
+ * $Id$
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h> 
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#include <tcl.h>
+
+#if _MSC_VER >= 1000
+#pragma comment(lib, "ws2_32")
+#pragma comment(lib, "user32")
+#endif
+
+#define BUFSIZE 4096
+#define XWM_RESOLVE WM_APP + 1
+
+#undef TCL_STORAGE_CLASS
+#define TCL_STORAGE_CLASS DLLEXPORT
+
+static const char ASSOC_KEY[] = "tclresolver_key";
+
+EXTERN int Tclresolver_Init(Tcl_Interp *interp);
+EXTERN int Tclresolver_Unload(Tcl_Interp *interp);
+EXTERN int TclGetHostByName(LPSOCKADDR_IN saddrPtr, 
+                            const char *hostname, int port);
+
+typedef struct {
+    HANDLE hReader;
+    DWORD  idReader;
+    Tcl_ThreadId tclid;
+    Tcl_Command command;
+} PKGINFO, *LPPKGINFO;
+
+typedef struct {
+    HANDLE lock;
+    LONG   cookie;
+    char   data[BUFSIZE];
+} QUERYINFO, *LPQUERYINFO;
+
+typedef struct {
+    Tcl_Event header;
+    LPQUERYINFO query;
+} RESOLVEREVENT, *LPRESOLVEREVENT;
+
+static Tcl_ObjCmdProc TclresolverObjCmd;
+static Tcl_ExitProc Terminate;
+static int Initialize(Tcl_Interp *interp, LPPKGINFO pkgPtr);
+static Tcl_Obj *LookupHostname(LPPKGINFO pkgPtr, const char *hostname);
+static DWORD WINAPI Reader(LPVOID clientData);
+static void QueueEvent(Tcl_ThreadId id, LPQUERYINFO pQuery);
+static int  ResolverEventProc(Tcl_Event *evPtr, int flags);
+static int GetAddress(char *query);
+static Tcl_Obj *Win32Error(const char * szPrefix, HRESULT hr);
+
+/* ---------------------------------------------------------------------- */
+
+int
+Tclresolver_Init(Tcl_Interp *interp)
+{
+    int initialized = 0;
+    LPPKGINFO pkgPtr = NULL;
+
+#ifdef USE_TCL_STUBS
+    if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
+        return TCL_ERROR;
+    }
+#endif
+    
+    pkgPtr = (LPPKGINFO)Tcl_Alloc(sizeof(PKGINFO));
+    if (pkgPtr == NULL) {
+        Tcl_SetResult(interp, "out of memory", TCL_STATIC);
+        return TCL_ERROR;
+    }
+    ZeroMemory(pkgPtr, sizeof(PKGINFO));
+
+    if (Initialize(interp, pkgPtr) != TCL_OK) {
+        Tcl_Free((char*)pkgPtr);
+        return TCL_ERROR;
+    }
+    pkgPtr->tclid = Tcl_GetCurrentThread();
+    pkgPtr->command = Tcl_CreateObjCommand(interp, "resolve", 
+                                                TclresolverObjCmd,
+                                                (ClientData)pkgPtr,
+                                                (Tcl_CmdDeleteProc *)NULL);
+    Tcl_CreateExitHandler(Terminate, (ClientData)pkgPtr);
+    Tcl_SetAssocData(interp, ASSOC_KEY, NULL, (ClientData)pkgPtr);
+    return Tcl_PkgProvide(interp, "Tclresolver", "1.3.0");
+}
+
+EXTERN int
+Tclresolver_SafeInit(Tcl_Interp *interp)
+{
+    return Tclresolver_Init(interp);
+}
+
+int 
+Tclresolver_Unload(Tcl_Interp *interp)
+{
+    LPPKGINFO pkgPtr = NULL;
+    pkgPtr = (LPPKGINFO)Tcl_GetAssocData(interp, ASSOC_KEY, NULL);
+    Tcl_DeleteCommandFromToken(interp, pkgPtr->command);
+    Terminate((ClientData)pkgPtr);
+    Tcl_SetAssocData(interp, ASSOC_KEY, NULL, (ClientData)NULL);    
+    return TCL_OK;
+}
+
+static int
+TclresolverObjCmd(ClientData clientData, Tcl_Interp *interp, 
+                  int objc, Tcl_Obj *CONST objv[])
+{
+    LPPKGINFO pkgPtr = (LPPKGINFO)clientData;
+    Tcl_Obj *resObj = NULL;
+    if (objc != 2) {
+        Tcl_WrongNumArgs(interp, objc, objv, "hostname");
+        return TCL_ERROR;
+    }
+    resObj = LookupHostname(pkgPtr, Tcl_GetString(objv[1]));
+    Tcl_SetObjResult(interp, resObj);
+    return TCL_OK;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int
+Initialize(Tcl_Interp *interp, LPPKGINFO pkgPtr) 
+{
+    WSADATA wsd;
+    
+    OutputDebugStringA("Initialize\n");
+    
+    /* Initialize windows sockets. */
+    if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
+       Tcl_Obj *errObj = Win32Error("WSAStartup error", GetLastError());
+        Tcl_SetObjResult(interp, errObj);
+        return TCL_ERROR;
+    }
+    
+    if (LOBYTE(wsd.wVersion) != 2 ||
+        HIBYTE(wsd.wVersion) != 2) {
+        Tcl_SetResult(interp,
+           "error: failed to load a compatible winsock version", TCL_STATIC);
+       WSACleanup();
+        return TCL_ERROR;
+    }
+
+    /* Create the reader thread */
+    pkgPtr->hReader = CreateThread(NULL, 0, Reader, pkgPtr,
+                                        0, &pkgPtr->idReader);
+
+    return TCL_OK;
+}
+
+static void
+Terminate(ClientData clientData)
+{
+    LPPKGINFO pkgPtr = (LPPKGINFO)clientData;
+    OutputDebugStringA("Terminate\n");
+    PostThreadMessage(pkgPtr->idReader, WM_QUIT, 0, 0L);
+    Tcl_DeleteExitHandler(Terminate, clientData);
+    WaitForSingleObject(pkgPtr->hReader, 400);
+    CloseHandle(pkgPtr->hReader);
+    Tcl_Free((LPBYTE)pkgPtr);
+    WSACleanup();
+}
+
+static Tcl_Obj *
+LookupHostname(LPPKGINFO pkgPtr, const char * hostname)
+{
+    Tcl_Obj *resObj = NULL;
+    DWORD dwWait;
+    LPQUERYINFO queryPtr;
+    static LONG cookie;
+    char sz[128];
+
+    /* copy the hostname into out buffer */
+    queryPtr = (LPQUERYINFO)Tcl_Alloc(sizeof(QUERYINFO));
+    memset(queryPtr, 0, sizeof(QUERYINFO));
+    queryPtr->lock = CreateEvent(NULL, FALSE, FALSE, NULL);
+    queryPtr->cookie = InterlockedIncrement(&cookie);
+    strcpy(queryPtr->data, hostname);
+
+    sprintf(sz, "query %d begin\n", queryPtr->cookie);
+    OutputDebugString(sz);
+    
+    /* notify the reader thread that it should begin reading */
+    PostThreadMessage(pkgPtr->idReader, XWM_RESOLVE, 0, (LPARAM)queryPtr);
+
+    /* Run a tcl event loop while we wait for the answer */
+    while (1) {
+        Tcl_DoOneEvent(0); /* when tk loaded */
+        //Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT);
+        dwWait = WaitForSingleObject(queryPtr->lock, 0);
+        if (dwWait == WAIT_OBJECT_0) {
+           sprintf(sz, "query %d complete\n", queryPtr->cookie);
+           OutputDebugString(sz);
+            break;
+       }
+    }
+
+    resObj = Tcl_NewStringObj(queryPtr->data, -1);
+    CloseHandle(queryPtr->lock);
+    Tcl_Free((char *)queryPtr);
+    return resObj;
+}
+
+static DWORD WINAPI
+Reader(LPVOID clientData)
+{
+    LPPKGINFO pkgPtr = (LPPKGINFO)clientData;
+    LPQUERYINFO queryPtr = NULL;
+    MSG msg;
+    DWORD dwErr = 0, dwRead = 0;
+    
+    while(GetMessage(&msg, 0, 0, 0)) {
+       if (msg.hwnd == NULL) {
+           switch (msg.message) {
+               case XWM_RESOLVE:
+                   queryPtr = (LPQUERYINFO)msg.lParam;
+                   GetAddress(queryPtr->data);
+                   QueueEvent(pkgPtr->tclid, queryPtr);
+                   break;
+           }
+       }
+       DispatchMessage(&msg);
+    }
+
+    OutputDebugStringA("Reader exiting.\n");
+    return 0;
+}
+
+static void
+QueueEvent(Tcl_ThreadId id, LPQUERYINFO queryPtr)
+{
+    LPRESOLVEREVENT evPtr;
+    OutputDebugStringA("QueueEvent\n");
+    evPtr = (LPRESOLVEREVENT)Tcl_Alloc(sizeof(RESOLVEREVENT));
+    evPtr->header.proc = ResolverEventProc;
+    evPtr->header.nextPtr = NULL;
+    evPtr->query = queryPtr;
+    Tcl_ThreadQueueEvent(id, (Tcl_Event *)evPtr, TCL_QUEUE_TAIL);
+}
+
+static int
+ResolverEventProc(Tcl_Event *evPtr, int flags)
+{
+    LPRESOLVEREVENT revPtr = (LPRESOLVEREVENT)evPtr;
+    OutputDebugStringA("ResolverEventProc\n");
+    SetEvent(revPtr->query->lock);
+    return 1;
+}
+
+static Tcl_Obj *
+Win32Error(const char * szPrefix, HRESULT hr)
+{
+    Tcl_Obj *msgObj = NULL;
+    char * lpBuffer = NULL;
+    DWORD  dwLen = 0;
+    
+    dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER 
+                          | FORMAT_MESSAGE_FROM_SYSTEM,
+                          NULL, (DWORD)hr, LANG_NEUTRAL,
+                          (LPTSTR)&lpBuffer, 0, NULL);
+    if (dwLen < 1) {
+        dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER 
+                              | FORMAT_MESSAGE_FROM_STRING
+                              | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+                              "code 0x%1!08X!%n", 0, LANG_NEUTRAL,
+                              (LPTSTR)&lpBuffer, 0, (va_list *)&hr);
+    }
+
+    msgObj = Tcl_NewStringObj(szPrefix, -1);
+    if (dwLen > 0) {
+       char *p = lpBuffer + dwLen - 1;        /* remove cr-lf at end */
+       for ( ; p && *p && isspace(*p); p--)
+           ;
+       *++p = 0;
+       Tcl_AppendToObj(msgObj, ": ", 2);
+       Tcl_AppendToObj(msgObj, lpBuffer, -1);
+    }
+    LocalFree((HLOCAL)lpBuffer);
+    return msgObj;
+}
+
+static int
+GetAddress(char *query)
+{
+    struct addrinfo hints = {0};
+    struct addrinfo *res = NULL;
+    char *hostname, *p, *q;
+    int r = 0, nc = 0;
+    
+    /* trim whitespace */
+    for (p = query; *p && isspace(*p); p++)
+       ;
+    for (q = p + strlen(p) - 1; q > p && *q && isspace(*q); q--)
+       ;
+    *++q = 0;
+    hostname = p;
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = PF_INET; /* use PF_UNSPEC if we want IPv6 too */
+    hints.ai_flags = AI_CANONNAME;
+    hints.ai_socktype = SOCK_STREAM;
+    r = getaddrinfo(hostname, "", &hints, &res);
+    *query = 0;
+
+    if (r != 0) {
+       Tcl_Obj *o = Win32Error("error", WSAGetLastError());
+       strcpy(query, Tcl_GetString(o));
+       Tcl_DecrRefCount(o);
+    } else {
+       struct addrinfo *resPtr = res;
+       for (resPtr = res; resPtr != NULL; resPtr = resPtr->ai_next) {
+           char name[NI_MAXHOST];
+
+           getnameinfo(resPtr->ai_addr, resPtr->ai_addrlen, 
+                       name, NI_MAXHOST,
+                       NULL, 0,
+                       NI_NUMERICHOST | NI_NUMERICSERV);
+
+           if (resPtr != res)
+               strcat(query, " ");
+           strcat(query, name);
+       }
+       freeaddrinfo(res);
+    }
+    return r;
+}
+    
+
diff --git a/w32cat.c b/w32cat.c
new file mode 100644 (file)
index 0000000..560d15a
--- /dev/null
+++ b/w32cat.c
@@ -0,0 +1,93 @@
+/* w32cat.c - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sourceforge.net>
+ *
+ * This illustrates a line-oriented cat program. Each line read from the
+ * console is echoed to the output.
+ *
+ * NB: only reads from the CONSOLE though. Not piped input.
+ *
+ * $Id$
+ */
+
+#if defined(_UNICODE) && !defined(UNICODE)
+#define UNICODE
+#endif
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <tchar.h>
+
+void Win32Error(HANDLE hFile, LPCTSTR sz, HRESULT hr);
+
+int
+_tmain(int argc, LPCTSTR argv[])
+{
+    HANDLE hStdin, hStdout, hStderr;
+    DWORD dwMode;
+
+    if ((hStderr = GetStdHandle(STD_ERROR_HANDLE)) == INVALID_HANDLE_VALUE) {
+        return -2;
+    }
+
+    if ((hStdout = GetStdHandle(STD_OUTPUT_HANDLE)) == INVALID_HANDLE_VALUE) {
+        Win32Error(hStderr, _T("get stdout handle"), GetLastError());
+        return -1;
+    }
+
+    if ((hStdin = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE) {
+        Win32Error(hStderr, _T("get stdin handle"), GetLastError());
+        return -1;
+    }
+
+    /* 
+     * By default the console input is processed line oriented so we
+     * don't need to do anything special.
+     */
+
+    GetConsoleMode(hStdin, &dwMode);
+    dwMode &= ~ENABLE_LINE_INPUT;
+    SetConsoleMode(hStdin, dwMode);
+    GetConsoleMode(hStdout, &dwMode);
+    dwMode &= ~ENABLE_LINE_INPUT;
+    SetConsoleMode(hStdout, dwMode);
+
+    while (1) {
+        DWORD cbRead = 0;
+        TCHAR buffer[1024];
+
+        if (! ReadFile(hStdin, buffer, 1023, &cbRead, NULL))
+            break;
+        if (CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE,
+                          _T("quit"), 4, buffer, 4) == CSTR_EQUAL)
+            break;
+        
+        WriteFile(hStdout, buffer, cbRead, NULL, NULL);
+    }
+
+    return 0;
+}
+
+void
+Win32Error(HANDLE hFile, LPCTSTR sz, HRESULT hr)
+{
+    LPTSTR lpBuffer = NULL;
+    DWORD dwLen = 0;
+
+    dwLen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER 
+                          | FORMAT_MESSAGE_FROM_SYSTEM,
+                          NULL, (DWORD)hr, LANG_NEUTRAL,
+                          (LPTSTR)&lpBuffer, 0, NULL);
+    if (dwLen < 1) {
+        HRESULT ahr[1] = {hr};
+        dwLen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER 
+                              | FORMAT_MESSAGE_FROM_STRING
+                              | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+                              _T("code: 0x%1!08X!%n"), 0, LANG_NEUTRAL,
+                              (LPTSTR)&lpBuffer, 0, (va_list *)ahr);
+    }
+    WriteConsole(hFile, sz, lstrlen(sz), NULL, NULL);
+    if (dwLen > 0) {
+        WriteConsole(hFile, _T(": "), 3, NULL, NULL);
+        WriteConsole(hFile, lpBuffer, dwLen, NULL, NULL);
+    }
+    LocalFree((HLOCAL)lpBuffer);
+}
diff --git a/win/resolver.c b/win/resolver.c
new file mode 100644 (file)
index 0000000..e4e35b2
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ *
+ */
+#if defined(_UNICODE) && !defined(UNICODE)
+#define UNICODE
+#endif
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <tchar.h>
+#include <stdio.h>
+
+#if _MSC_VER >= 1000
+#pragma comment(lib, "ws2_32")
+#endif
+
+void Win32Error(LPCTSTR sz, HRESULT hr);
+LPSTR W2A(LPCWSTR wstr);
+
+int
+_tmain(int argc, LPCTSTR argv[])
+{
+    WSADATA wsd;
+    struct addrinfo hints = {0};
+    struct addrinfo *res = NULL;
+    const char * hostname;
+    DWORD dwCookie = 0;
+    int  r = 0;
+
+    if (argc < 2) {
+        _ftprintf(stderr, _T("usage: resolver <hostname>\n"));
+        return -1;
+    }
+
+    if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
+        Win32Error(_T("WSAStartup failed"), GetLastError());
+        return -1;
+    }
+
+    if (LOBYTE(wsd.wVersion) != 2 ||
+        HIBYTE(wsd.wVersion) != 2) {
+        _ftprintf(stderr, 
+                  _T("error: failed to load a compatible winsock version\n"));
+        WSACleanup();
+        return -1;
+    }
+    
+#ifdef UNICODE
+    hostname = W2A(argv[1]);
+#else
+    hostname = argv[1];
+#endif
+    
+    dwCookie = GetCurrentProcessId();
+    ZeroMemory(&hints, sizeof(hints));
+    hints.ai_family = PF_UNSPEC;
+
+    r = getaddrinfo(hostname, "", &hints, &res);
+
+#ifdef UNICODE
+    HeapFree(GetProcessHeap(), 0, (LPVOID)hostname);
+#endif
+
+    if (r != 0) {
+        Win32Error(_T("getaddrinfo"), WSAGetLastError());
+    } else {
+        struct addrinfo *p = res;
+        printf("%d", dwCookie);
+        while (p != NULL) {
+            char name[NI_MAXHOST];
+            getnameinfo(p->ai_addr, p->ai_addrlen, 
+                        name, NI_MAXHOST,
+                        NULL, 0,
+                        NI_NUMERICHOST | NI_NUMERICSERV);
+            printf(" %s", name);
+            p = p->ai_next;
+        }
+        printf("\n");
+        freeaddrinfo(res);
+    }
+
+    WSACleanup();
+    return 0;
+}
+
+LPSTR
+W2A(LPCWSTR wstr)
+{
+    LPSTR str = NULL;
+    int slen = 0;
+
+    if (wstr == NULL)
+        return NULL;
+
+    slen = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL);
+    str = HeapAlloc(GetProcessHeap(), 0, slen * sizeof(CHAR));
+    if (str != NULL)
+        slen = WideCharToMultiByte(CP_ACP, 0, wstr, slen, str, slen, NULL, NULL);
+    return str;
+}
+
+void
+Win32Error(LPCTSTR sz, HRESULT hr)
+{
+    LPTSTR lpBuffer = NULL;
+    TCHAR  sBuffer[30];
+    
+    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+                  NULL, (DWORD)hr, LANG_NEUTRAL,
+                  (LPTSTR)&lpBuffer, 0, NULL);
+    
+    _ftprintf(stderr, _T("%s: "), sz);
+    if (lpBuffer == NULL) {
+        lpBuffer = sBuffer;
+        _ftprintf(stderr, _T("code: %08lX\n"), hr);
+    } else {
+        _ftprintf(stderr, lpBuffer);
+    }
+}
diff --git a/win/tclresolver.c b/win/tclresolver.c
new file mode 100644 (file)
index 0000000..bcd143f
--- /dev/null
@@ -0,0 +1,375 @@
+/* tclresolver.c - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sf.net>
+ *
+ *
+ *
+ * $Id$
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h> 
+#include <tchar.h>
+#include <winsock2.h>
+
+#include <tcl.h>
+
+#define BUFSIZE 4096
+#undef TCL_STORAGE_CLASS
+#define TCL_STORAGE_CLASS DLLEXPORT
+
+static const char ASSOC_KEY[] = "tclresolver_key";
+
+EXTERN int Tclresolver_Init(Tcl_Interp *interp);
+EXTERN int Tclresolver_Unload(Tcl_Interp *interp);
+EXTERN int TclGetHostByName(LPSOCKADDR_IN saddrPtr, 
+                            const char *hostname, int port);
+
+typedef struct {
+    HANDLE hReader;
+    DWORD  idReader;
+    HANDLE hReady;
+    HANDLE hDone;
+    HANDLE hIn;
+    HANDLE hOut;
+    LPBYTE pData;
+    BOOL   bRunning;
+    Tcl_ThreadId tclid;
+    Tcl_Command command;
+} LOOKUPINFO, *LPLOOKUPINFO;
+
+typedef struct {
+    Tcl_Event header;
+    LPLOOKUPINFO info;
+} RESOLVEREVENT, *LPRESOLVEREVENT;
+
+//static int TclResolverObjCmd(ClientData clientData, Tcl_Interp *interp,
+//                             int objc, Tcl_Obj *CONST objv[]);
+static Tcl_ObjCmdProc TclresolverObjCmd;
+static Tcl_ExitProc Terminate;
+static int Initialize(Tcl_Interp *interp, LPLOOKUPINFO pLookupInfo);
+static BOOL CreateChildProcess(HANDLE hChildStdoutWr, HANDLE hChildStdinRd);
+static LPCTSTR LookupHostname(LPLOOKUPINFO pLookupInfo, LPCTSTR szHostname);
+static DWORD WINAPI Reader(LPVOID clientData);
+static void QueueEvent(LPLOOKUPINFO pLookupInfo);
+static int  ResolverEventProc(Tcl_Event *evPtr, int flags);
+
+
+/* ---------------------------------------------------------------------- */
+
+int
+Tclresolver_Init(Tcl_Interp *interp)
+{
+    int initialized = 0;
+    LPLOOKUPINFO pLookupInfo = NULL;
+
+#ifdef USE_TCL_STUBS
+    if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
+        return TCL_ERROR;
+    }
+#endif
+    
+    pLookupInfo = (LPLOOKUPINFO)Tcl_Alloc(sizeof(LOOKUPINFO));
+    if (pLookupInfo == NULL) {
+        Tcl_SetResult(interp, "out of memory", TCL_STATIC);
+        return TCL_ERROR;
+    }
+    ZeroMemory(pLookupInfo, sizeof(LOOKUPINFO));
+
+    if (Initialize(interp, pLookupInfo) != TCL_OK) {
+        Tcl_Free((char*)pLookupInfo);
+        //Tcl_SetResult(interp, "failed to initialize package", TCL_STATIC);
+        return TCL_ERROR;
+    }
+    pLookupInfo->tclid = Tcl_GetCurrentThread();
+    pLookupInfo->command = Tcl_CreateObjCommand(interp, "resolve", 
+                                                TclresolverObjCmd,
+                                                (ClientData)pLookupInfo,
+                                                (Tcl_CmdDeleteProc *)NULL);
+    Tcl_CreateExitHandler(Terminate, (ClientData)pLookupInfo);
+    Tcl_SetAssocData(interp, ASSOC_KEY, NULL, (ClientData)pLookupInfo);
+    return Tcl_PkgProvide(interp, "Tclresolver", "1.0.0");
+}
+
+EXTERN int
+Tclresolver_SafeInit(Tcl_Interp *interp)
+{
+    return Tclresolver_Init(interp);
+}
+
+int 
+Tclresolver_Unload(Tcl_Interp *interp)
+{
+    LPLOOKUPINFO pLookupInfo = NULL;
+    pLookupInfo = (LPLOOKUPINFO)Tcl_GetAssocData(interp, ASSOC_KEY, NULL);
+    Tcl_DeleteCommandFromToken(interp, pLookupInfo->command);
+    Terminate((ClientData)pLookupInfo);
+    Tcl_SetAssocData(interp, ASSOC_KEY, NULL, (ClientData)NULL);    
+    return TCL_OK;
+}
+
+static int
+TclresolverObjCmd(ClientData clientData, Tcl_Interp *interp, 
+                  int objc, Tcl_Obj *CONST objv[])
+{
+    LPLOOKUPINFO pLookupInfo = (LPLOOKUPINFO)clientData;
+    Tcl_Obj *resObj = NULL;
+    if (objc != 2) {
+        Tcl_WrongNumArgs(interp, objc, objv, "hostname");
+        return TCL_ERROR;
+    }
+    resObj = Tcl_NewStringObj(LookupHostname(pLookupInfo,
+                                             Tcl_GetString(objv[1])), -1);
+    Tcl_SetObjResult(interp, resObj);
+    return TCL_OK;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int
+Initialize(Tcl_Interp *interp, LPLOOKUPINFO pLookupInfo) 
+{ 
+    SECURITY_ATTRIBUTES saAttr;
+    BOOL fSuccess;
+    HANDLE hChildStdinRd, hChildStdinWr, hChildStdoutRd, hChildStdoutWr;
+    HANDLE hChildStdoutRdDup, hChildStdinWrDup;
+
+    // Set the bInheritHandle flag so pipe handles are inherited.
+    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+    saAttr.bInheritHandle = TRUE;
+    saAttr.lpSecurityDescriptor = NULL;
+    // Create a pipe for the child process's STDOUT.
+    if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
+        Tcl_SetResult(interp, "Stdout pipe creation failed", TCL_STATIC); 
+        return TCL_ERROR;
+    }
+    // Create noninheritable read handle and close the inheritable read 
+    // handle.
+    fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
+                               GetCurrentProcess(), &hChildStdoutRdDup , 0,
+                               FALSE,
+                               DUPLICATE_SAME_ACCESS);
+    if( !fSuccess ) {
+        Tcl_SetResult(interp,  "DuplicateHandle failed", TCL_STATIC);
+        return TCL_ERROR;
+    }
+    CloseHandle(hChildStdoutRd);
+
+    // Create a pipe for the child process's STDIN.
+    if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
+        Tcl_SetResult(interp, "Stdin pipe creation failed", TCL_STATIC);
+        return TCL_ERROR;
+    }
+    // Duplicate the write handle to the pipe so it is not inherited.
+    fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr, 
+                               GetCurrentProcess(), &hChildStdinWrDup, 0, 
+                               FALSE,                  // not inherited 
+                               DUPLICATE_SAME_ACCESS); 
+    if (! fSuccess) {
+        Tcl_SetResult(interp, "DuplicateHandle failed", TCL_STATIC);
+        return TCL_ERROR;
+    }
+    CloseHandle(hChildStdinWr); 
+
+    /* Create synchronization events */
+    pLookupInfo->hReady = CreateEvent(NULL, FALSE, FALSE, NULL);
+    pLookupInfo->hDone  = CreateEvent(NULL, FALSE, FALSE, NULL);
+    pLookupInfo->hIn = hChildStdoutRdDup;
+    pLookupInfo->hOut = hChildStdinWrDup;
+    pLookupInfo->bRunning = TRUE;
+    pLookupInfo->pData = (LPBYTE)Tcl_Alloc(BUFSIZE);
+
+    /* Create the reader thread */
+    pLookupInfo->hReader = CreateThread(NULL, 0, Reader, pLookupInfo,
+                                        0, &pLookupInfo->idReader);
+
+    /* Now create the child process. */
+    fSuccess = CreateChildProcess(hChildStdoutWr, hChildStdinRd);
+    if (! fSuccess) {
+        Tcl_SetResult(interp, "Create process failed", TCL_STATIC);
+        return TCL_ERROR;
+    }
+    
+    return TCL_OK;
+}
+
+static void
+Terminate(ClientData clientData)
+{
+    LPLOOKUPINFO pLookupInfo = (LPLOOKUPINFO)clientData;
+    OutputDebugString("Pkgdeleteproc\n");
+    pLookupInfo->bRunning = FALSE;
+    WaitForSingleObject(pLookupInfo->hReader, 400);
+    CloseHandle(pLookupInfo->hReader);
+    CloseHandle(pLookupInfo->hReady);
+    CloseHandle(pLookupInfo->hDone);
+    CloseHandle(pLookupInfo->hIn);
+    CloseHandle(pLookupInfo->hOut);
+    Tcl_Free(pLookupInfo->pData);
+    Tcl_Free((LPBYTE)pLookupInfo);
+}
+
+static LPCTSTR
+LookupHostname(LPLOOKUPINFO pLookupInfo, LPCTSTR szHostname)
+{
+    DWORD dwWrote, dwWait;
+    BOOL br;
+    
+    ZeroMemory(pLookupInfo->pData, BUFSIZE);
+    lstrcpy((LPTSTR)pLookupInfo->pData, szHostname);
+    lstrcat((LPTSTR)pLookupInfo->pData, _T("\n"));
+    br = WriteFile(pLookupInfo->hOut, pLookupInfo->pData,
+                   lstrlen((LPTSTR)pLookupInfo->pData) * sizeof(TCHAR),
+                   &dwWrote, NULL);
+    if (br)
+        br = FlushFileBuffers(pLookupInfo->hOut);
+    if (br) {
+        /* clear out the buffer ready for reading */
+        ZeroMemory(pLookupInfo->pData, BUFSIZE);
+        /* notify the reader thread that it should begin reading */
+        SetEvent(pLookupInfo->hReady);
+#ifdef USE_WIN32_EVENTS
+        /* Doing this will block Tcl event processing */
+        dwWait = WaitForSingleObject(pLookupInfo->hDone, INFINITE);
+#else
+        /* Run a tcl event loop while we wait for the answer */
+        while (1) {
+            Tcl_DoOneEvent(0); /* when tk loaded */
+            //Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT);
+            OutputDebugString("done one event\n");
+            dwWait = WaitForSingleObject(pLookupInfo->hDone, 0);
+            if (dwWait == WAIT_OBJECT_0)
+                break;
+        }
+#endif
+    }
+    if (!br) {
+        return _T("");
+    }
+    return (LPCTSTR)pLookupInfo->pData;
+}
+static BOOL
+CreateChildProcess(HANDLE hChildStdoutWr, HANDLE hChildStdinRd) 
+{ 
+    PROCESS_INFORMATION piProcInfo; 
+    STARTUPINFO siStartInfo;
+    BOOL bFuncRetn = FALSE; 
+    /* Set up members of the PROCESS_INFORMATION structure.*/
+    ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
+    /* Set up members of the STARTUPINFO structure.*/
+    ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
+    siStartInfo.cb = sizeof(STARTUPINFO); 
+    siStartInfo.hStdError = hChildStdoutWr;
+    siStartInfo.hStdOutput = hChildStdoutWr;
+    siStartInfo.hStdInput = hChildStdinRd;
+    siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
+    /* Create the child process.*/
+    bFuncRetn = CreateProcess(NULL, 
+                              "resolver",    // command line 
+                              NULL,          // process security attributes 
+                              NULL,          // primary thread security attributes 
+                              TRUE,          // handles are inherited 
+                              DETACHED_PROCESS, // no console
+                              NULL,          // use parent's environment 
+                              NULL,          // use parent's current directory 
+                              &siStartInfo,  // STARTUPINFO pointer 
+                              &piProcInfo);  // receives PROCESS_INFORMATION 
+   
+    if (bFuncRetn == 0) 
+        return bFuncRetn; /*ErrorExit("CreateProcess failed");*/
+    else 
+    {
+        CloseHandle(piProcInfo.hProcess);
+        CloseHandle(piProcInfo.hThread);
+        return bFuncRetn;
+    }
+}
+
+static DWORD WINAPI
+Reader(LPVOID clientData)
+{
+    LPLOOKUPINFO pLookupInfo = (LPLOOKUPINFO)clientData;
+    DWORD dwErr = 0, dwRead = 0;
+    BOOL br;
+
+    while (pLookupInfo->bRunning) {
+        dwErr = WaitForSingleObject(pLookupInfo->hReady, 500);
+        if (dwErr == WAIT_OBJECT_0) {
+            br = ReadFile(pLookupInfo->hIn, pLookupInfo->pData,
+                          BUFSIZE, &dwRead, NULL);
+
+            if (dwRead > 0) {
+                LPTSTR p = (LPTSTR)pLookupInfo->pData;
+                for (p = p + lstrlen(p) - 1;
+                     p > pLookupInfo->pData && _istspace(*p);
+                     p--)
+                    ;
+                *++p = 0;
+            }
+#ifdef USE_WIN32_EVENTS
+            SetEvent(pLookupInfo->hDone);
+#else
+            QueueEvent(pLookupInfo);
+#endif
+        }
+    }
+    return 0;
+}
+
+static void
+QueueEvent(LPLOOKUPINFO pLookupInfo)
+{
+    LPRESOLVEREVENT evPtr;
+    OutputDebugString("queueevent\n");
+    evPtr = (LPRESOLVEREVENT)Tcl_Alloc(sizeof(RESOLVEREVENT));
+    evPtr->header.proc = ResolverEventProc;
+    evPtr->header.nextPtr = NULL;
+    evPtr->info = pLookupInfo;
+    Tcl_ThreadQueueEvent(pLookupInfo->tclid, 
+                         (Tcl_Event *)evPtr, TCL_QUEUE_TAIL);
+}
+
+static int
+ResolverEventProc(Tcl_Event *evPtr, int flags)
+{
+    LPRESOLVEREVENT revPtr = (LPRESOLVEREVENT)evPtr;
+    OutputDebugString("resolvereventproc\n");
+    SetEvent(revPtr->info->hDone);
+    return 1;
+}
+
+#if 0
+int
+TclGetHostByName(LPSOCKADDR_IN saddrPtr, const char *hostname, int port)
+{
+    struct in_addr addr;
+
+    ZeroMemory(saddrPtr, sizeof(SOCKADDR_IN));
+    saddrPtr->sin_family = AF_INET;
+    saddrPtr->sin_port = htons((unsigned short)port);
+    /* if no hostname then the answer is any host */
+    if (hostname == NULL || *hostname == 0) {
+        addr.s_addr = INADDR_ANY;
+    } else {
+        /* see if we have been given an ip number already */
+        addr.s_addr = inet_addr(hostname);
+        if (addr.s_addr == INADDR_NONE) {
+            /* Perform the address lookup via our subprocess */
+            addr.s_addr = inet_addr(LookupHostname(hostname));
+            if (addr.s_addr == INADDR_NONE) {
+#ifdef EHOSTUNREACH
+                Tcl_SetErrno(EHOSTUNREACH);
+#endif
+                return 0; /* error */
+            }
+        }
+    }
+    saddrPtr->sin_addr.s_addr = addr.s_addr;
+    return 1;
+}
+#endif