OSDN Git Service

Please enter the commit message for your changes. Lines starting
[eos/base.git] / util / src / TclTk / blt2.5 / generic / bltWinDde.c
diff --git a/util/src/TclTk/blt2.5/generic/bltWinDde.c b/util/src/TclTk/blt2.5/generic/bltWinDde.c
new file mode 100644 (file)
index 0000000..9d24737
--- /dev/null
@@ -0,0 +1,1329 @@
+
+/* 
+ * bltWinDde.c --
+ *
+ *     This file provides procedures that implement the "send"
+ *     command, allowing commands to be passed from interpreter
+ *     to interpreter.
+ *
+ * Copyright (c) 1997 by Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id: bltWinDde.c,v 1.1.1.1 2009/05/09 16:27:07 pcmacdon Exp $
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_DDE
+
+#include <ddeml.h>
+
+/* 
+ * The following structure is used to keep track of the interpreters
+ * registered by this process.
+ */
+
+typedef struct RegisteredInterp {
+    struct RegisteredInterp *nextPtr;
+                               /* The next interp this application knows
+                                * about. */
+    Tcl_Interp *interp;                /* The interpreter attached to this name. */
+    char name[1];              /* Interpreter's name. Malloc-ed as
+                                * part of the structure. */
+} RegisteredInterp;
+
+/*
+ * Used to keep track of conversations.
+ */
+
+typedef struct Conversation {
+    struct Conversation *nextPtr;
+                               /* The next conversation in the list. */
+    RegisteredInterp *riPtr;   /* The info we know about the conversation. */
+    HCONV hConv;               /* The DDE handle for this conversation. */
+    Tcl_Obj *returnPackagePtr; /* The result package for this conversation. */
+
+} Conversation;
+
+static Conversation *conversations; /* A list of conversations currently
+                                    * being processed. */
+static RegisteredInterp *interps; /* List of all interpreters registered
+                                  * in the current process. */
+static HSZ globalService;              
+static DWORD instance;         /* The application instance handle given
+                                * to us by DdeInitialize. */ 
+static int isServer;
+
+#define TCL_DDE_VERSION                "1.2"
+#define TCL_DDE_PACKAGE_NAME   "dde"
+#define TCL_DDE_SERVICE_NAME   "TclEval"
+
+/*
+ * Forward declarations for procedures defined later in this file.
+ */
+
+static Tcl_Obj *ExecuteRemoteObject _ANSI_ARGS_((Tcl_Interp *interp,
+       Tcl_Obj *objPtr));
+static int MakeConnection _ANSI_ARGS_((Tcl_Interp *interp, char *name, 
+       HCONV *convPtr));
+static HDDEDATA CALLBACK ServerProc _ANSI_ARGS_((UINT uType, UINT uFmt, 
+       HCONV hConv, HSZ topic, HSZ item, HDDEDATA hData, DWORD dwData1, 
+       DWORD dwData2));
+
+static Tcl_ExitProc ExitProc;
+static Tcl_CmdDeleteProc DeleteProc;
+static void SetError _ANSI_ARGS_((Tcl_Interp *interp));
+
+static Tcl_ObjCmdProc DdeObjCmd;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Initialize --
+ *
+ *     Initialize the global DDE instance.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Registers the DDE server proc.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+Initialize(void)
+{
+    int nameFound = 0;
+
+    /*
+     * See if the application is already registered; if so, remove its
+     * current name from the registry. The deletion of the command
+     * will take care of disposing of this entry.
+     */
+
+    if (interps != NULL) {
+       nameFound = 1;
+    }
+
+    /*
+     * Make sure that the DDE server is there. This is done only once,
+     * add an exit handler tear it down.
+     */
+
+    if (instance == 0) {
+       if (instance == 0) {
+           unsigned int flags;
+
+           flags = (CBF_SKIP_REGISTRATIONS | CBF_SKIP_UNREGISTRATIONS | 
+                   CBF_FAIL_POKES);
+           if (DdeInitialize(&instance, ServerProc, flags, 0) 
+               != DMLERR_NO_ERROR) {
+               instance = 0;
+           }
+       }
+    }
+    if ((globalService == 0) && (nameFound != 0)) {
+       if ((globalService == 0) && (nameFound != 0)) {
+           isServer = TRUE;
+           Tcl_CreateExitHandler(ExitProc, NULL);
+           globalService = DdeCreateStringHandle(instance, 
+               TCL_DDE_SERVICE_NAME, 0);
+           DdeNameService(instance, globalService, 0L, DNS_REGISTER);
+       } else {
+           isServer = FALSE;
+       }
+    }
+}    
+
+/*
+ *--------------------------------------------------------------
+ *
+ * SetServerName --
+ *
+ *     This procedure is called to associate an ASCII name with a Dde
+ *     server.  If the interpreter has already been named, the
+ *     name replaces the old one.
+ *
+ * Results:
+ *     The return value is the name actually given to the interp.
+ *     This will normally be the same as name, but if name was already
+ *     in use for a Dde Server then a name of the form "name #2" will
+ *     be chosen,  with a high enough number to make the name unique.
+ *
+ * Side effects:
+ *     Registration info is saved, thereby allowing the "send" command
+ *     to be used later to invoke commands in the application.  In
+ *     addition, the "send" command is created in the application's
+ *     interpreter.  The registration will be removed automatically
+ *     if the interpreter is deleted or the "send" command is removed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static char *
+SetServerName(
+    Tcl_Interp *interp,
+    char *name                 /* The name that will be used to
+                                * refer to the interpreter in later
+                                * "send" commands.  Must be globally
+                                * unique. */
+    )
+{
+    int suffix, offset;
+    RegisteredInterp *riPtr, *prevPtr;
+    Tcl_DString dString;
+
+    /*
+     * See if the application is already registered; if so, remove its
+     * current name from the registry. The deletion of the command
+     * will take care of disposing of this entry.
+     */
+
+    for (riPtr = interps, prevPtr = NULL; riPtr != NULL; 
+           prevPtr = riPtr, riPtr = riPtr->nextPtr) {
+       if (riPtr->interp == interp) {
+           if (name != NULL) {
+               if (prevPtr == NULL) {
+                   interps = interps->nextPtr;
+               } else {
+                   prevPtr->nextPtr = riPtr->nextPtr;
+               }
+               break;
+           } else {
+               /*
+                * the name was NULL, so the caller is asking for
+                * the name of the current interp.
+                */
+
+               return riPtr->name;
+           }
+       }
+    }
+
+    if (name == NULL) {
+       /*
+        * the name was NULL, so the caller is asking for
+        * the name of the current interp, but it doesn't
+        * have a name.
+        */
+
+       return "";
+    }
+    
+    /*
+     * Pick a name to use for the application.  Use "name" if it's not
+     * already in use.  Otherwise add a suffix such as " #2", trying
+     * larger and larger numbers until we eventually find one that is
+     * unique.
+     */
+
+    suffix = 1;
+    offset = 0;
+    Tcl_DStringInit(&dString);
+
+    /*
+     * We have found a unique name. Now add it to the registry.
+     */
+
+    riPtr = Blt_Malloc(sizeof(RegisteredInterp) + strlen(name));
+    riPtr->interp = interp;
+    riPtr->nextPtr = interps;
+    interps = riPtr;
+    strcpy(riPtr->name, name);
+
+    Tcl_CreateObjCommand(interp, "dde", DdeObjCmd, riPtr, DeleteProc);
+    if (Tcl_IsSafe(interp)) {
+       Tcl_HideCommand(interp, "dde", "dde");
+    }
+    Tcl_DStringFree(&dString);
+
+    /*
+     * re-initialize with the new name
+     */
+    Initialize();
+    
+    return riPtr->name;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DeleteProc
+ *
+ *     This procedure is called when the command "dde" is destroyed.
+ *
+ * Results:
+ *     none
+ *
+ * Side effects:
+ *     The interpreter given by riPtr is unregistered.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+DeleteProc(clientData)
+    ClientData clientData;     /* The interp we are deleting passed
+                                * as ClientData. */
+{
+    RegisteredInterp *riPtr = clientData;
+    RegisteredInterp *searchPtr, *prevPtr;
+
+    for (searchPtr = interps, prevPtr = NULL;
+           (searchPtr != NULL) && (searchPtr != riPtr);
+           prevPtr = searchPtr, searchPtr = searchPtr->nextPtr) {
+       /*
+        * Empty loop body.
+        */
+    }
+
+    if (searchPtr != NULL) {
+       if (prevPtr == NULL) {
+           interps = interps->nextPtr;
+       } else {
+           prevPtr->nextPtr = searchPtr->nextPtr;
+       }
+    }
+    Tcl_EventuallyFree(clientData, TCL_DYNAMIC);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ExecuteRemoteObject --
+ *
+ *     Takes the package delivered by DDE and executes it in
+ *     the server's interpreter.
+ *
+ * Results:
+ *     A list Tcl_Obj * that describes what happened. The first
+ *     element is the numerical return code (TCL_ERROR, etc.).
+ *     The second element is the result of the script. If the
+ *     return result was TCL_ERROR, then the third element
+ *     will be the value of the global "errorCode", and the
+ *     fourth will be the value of the global "errorInfo".
+ *     The return result will have a refCount of 0.
+ *
+ * Side effects:
+ *     A Tcl script is run, which can cause all kinds of other
+ *     things to happen.
+ *
+ *--------------------------------------------------------------
+ */
+
+static Tcl_Obj *
+ExecuteRemoteObject(
+    Tcl_Interp *interp,                /* Remote interpreter. */
+    Tcl_Obj *objPtr)           /* The object to execute. */
+{
+    Tcl_Obj *listObjPtr;
+    int result;
+
+    result = Tcl_GlobalEval(interp, Tcl_GetString(objPtr));
+    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+    Tcl_ListObjAppendElement(NULL, listObjPtr, Tcl_NewIntObj(result));
+    Tcl_ListObjAppendElement(NULL, listObjPtr, Tcl_GetObjResult(interp));
+    if (result == TCL_ERROR) {
+       char *value;
+       Tcl_Obj *objPtr;
+
+       value = Tcl_GetVar2(interp, "errorCode", NULL, TCL_GLOBAL_ONLY);
+       objPtr = Tcl_NewStringObj(value, -1);
+       Tcl_ListObjAppendElement(NULL, listObjPtr, objPtr);
+       value = Tcl_GetVar2(interp, "errorInfo", NULL, TCL_GLOBAL_ONLY);
+       objPtr = Tcl_NewStringObj(value, -1);
+        Tcl_ListObjAppendElement(NULL, listObjPtr, objPtr);
+    }
+    return listObjPtr;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ServerProc --
+ *
+ *     Handles all transactions for this server. Can handle
+ *     execute, request, and connect protocols. Dde will
+ *     call this routine when a client attempts to run a dde
+ *     command using this server.
+ *
+ * Results:
+ *     A DDE Handle with the result of the dde command.
+ *
+ * Side effects:
+ *     Depending on which command is executed, arbitrary
+ *     Tcl scripts can be run.
+ *
+ *--------------------------------------------------------------
+ */
+
+static HDDEDATA CALLBACK
+ServerProc (
+    UINT uType,                        /* The type of DDE transaction we
+                                * are performing. */
+    UINT uFmt,                 /* The format that data is sent or
+                                * received. */
+    HCONV hConv,               /* The conversation associated with the 
+                                * current transaction. */
+    HSZ topic,                 /* A string handle. Transaction-type 
+                                * dependent. */
+    HSZ item,                  /* A string handle. Transaction-type 
+                                * dependent. */
+    HDDEDATA hData,            /* DDE data. Transaction-type dependent. */
+    DWORD dwData1,             /* Transaction-dependent data. */
+    DWORD dwData2)             /* Transaction-dependent data. */
+{
+    Tcl_DString dString;
+    char *utilString;
+    Tcl_Obj *objPtr;
+    HDDEDATA code = NULL;
+    RegisteredInterp *riPtr;
+    Conversation *convPtr, *prevConvPtr;
+
+    switch(uType) {
+    case XTYP_CONNECT:
+       {
+           int length;
+
+           /*
+            * Dde is trying to initialize a conversation with us. Check
+            * and make sure we have a valid topic.
+            */
+
+           length = DdeQueryString(instance, topic, NULL, 0, 0);
+           Tcl_DStringInit(&dString);
+           Tcl_DStringSetLength(&dString, length);
+           utilString = Tcl_DStringValue(&dString);
+           DdeQueryString(instance, topic, utilString, length + 1, 
+                          CP_WINANSI);
+
+           for (riPtr = interps; riPtr != NULL; riPtr = riPtr->nextPtr) {
+               if (strcasecmp(utilString, riPtr->name) == 0) {
+                   Tcl_DStringFree(&dString);
+                   return (HDDEDATA) TRUE;
+               }
+           }
+
+           Tcl_DStringFree(&dString);
+           return (HDDEDATA) FALSE;
+       }
+    case XTYP_CONNECT_CONFIRM: 
+       {
+           DWORD length;
+
+           /*
+            * Dde has decided that we can connect, so it gives us a 
+            * conversation handle. We need to keep track of it
+            * so we know which execution result to return in an
+            * XTYP_REQUEST.
+            */
+
+           length = DdeQueryString(instance, topic, NULL, 0, 0);
+           Tcl_DStringInit(&dString);
+           Tcl_DStringSetLength(&dString, length);
+           utilString = Tcl_DStringValue(&dString);
+           DdeQueryString(instance, topic, utilString, length + 1, 
+                   CP_WINANSI);
+           for (riPtr = interps; riPtr != NULL; riPtr = riPtr->nextPtr) {
+               if (strcasecmp(riPtr->name, utilString) == 0) {
+                   convPtr = Blt_Malloc(sizeof(Conversation));
+                   convPtr->nextPtr = conversations;
+                   convPtr->returnPackagePtr = NULL;
+                   convPtr->hConv = hConv;
+                   convPtr->riPtr = riPtr;
+                   conversations = convPtr;
+                   break;
+               }
+           }
+           Tcl_DStringFree(&dString);
+           return (HDDEDATA) TRUE;
+       }
+    case XTYP_DISCONNECT:
+       {
+           /*
+            * The client has disconnected from our server. Forget this
+            * conversation.
+            */
+
+           for (convPtr = conversations, prevConvPtr = NULL;
+                   convPtr != NULL; 
+                   prevConvPtr = convPtr, convPtr = convPtr->nextPtr) {
+               if (hConv == convPtr->hConv) {
+                   if (prevConvPtr == NULL) {
+                       conversations = convPtr->nextPtr;
+                   } else {
+                       prevConvPtr->nextPtr = convPtr->nextPtr;
+                   }
+                   if (convPtr->returnPackagePtr != NULL) {
+                       Tcl_DecrRefCount(convPtr->returnPackagePtr);
+                   }
+                   Blt_Free(convPtr);
+                   break;
+               }
+           }
+           return (HDDEDATA) TRUE;
+       }
+    case XTYP_REQUEST:
+       {
+           int length;
+
+           /*
+            * This could be either a request for a value of a Tcl variable,
+            * or it could be the send command requesting the results of the
+            * last execute.
+            */
+
+           if (uFmt != CF_TEXT) {
+               return (HDDEDATA) FALSE;
+           }
+
+           code = (HDDEDATA) FALSE;
+           for (convPtr = conversations; (convPtr != NULL)
+                   && (convPtr->hConv != hConv); convPtr = convPtr->nextPtr) {
+               /*
+                * Empty loop body.
+                */
+           }
+
+           if (convPtr != NULL) {
+               length = DdeQueryString(instance, item, NULL, 0, CP_WINANSI);
+               Tcl_DStringInit(&dString);
+               Tcl_DStringSetLength(&dString, length);
+               utilString = Tcl_DStringValue(&dString);
+               DdeQueryString(instance, item, utilString, length + 1, 
+                       CP_WINANSI);
+               if (strcasecmp(utilString, "$TCLEVAL$EXECUTE$RESULT") == 0) {
+                   char *value;
+
+                   value = Tcl_GetStringFromObj(convPtr->returnPackagePtr, 
+                                &length);
+                   code = DdeCreateDataHandle(instance, value, length+1, 0, 
+                       item, CF_TEXT, 0);
+               } else {
+                   char *value;
+
+                   value = Tcl_GetVar2(convPtr->riPtr->interp, utilString, 
+                               NULL, TCL_GLOBAL_ONLY);
+                   if (value != NULL) {
+                       length = strlen(value);
+                       code = DdeCreateDataHandle(instance, value, length+1,
+                               0, item, CF_TEXT, 0);
+                   } else {
+                       code = NULL;
+                   }
+               }
+               Tcl_DStringFree(&dString);
+           }
+           return code;
+       }
+    case XTYP_EXECUTE: 
+       {
+           DWORD length;
+           /*
+            * Execute this script. The results will be saved into
+            * a list object which will be retreived later. See
+            * ExecuteRemoteObject.
+            */
+
+           Tcl_Obj *returnPackagePtr;
+
+           for (convPtr = conversations; (convPtr != NULL)
+                   && (convPtr->hConv != hConv); convPtr = convPtr->nextPtr) {
+               /*
+                * Empty loop body.
+                */
+
+           }
+
+           if (convPtr == NULL) {
+               return (HDDEDATA) DDE_FNOTPROCESSED;
+           }
+
+           utilString = (char *) DdeAccessData(hData, &length);
+           objPtr = Tcl_NewStringObj(utilString, -1);
+           Tcl_IncrRefCount(objPtr);
+           DdeUnaccessData(hData);
+           if (convPtr->returnPackagePtr != NULL) {
+               Tcl_DecrRefCount(convPtr->returnPackagePtr);
+           }
+           convPtr->returnPackagePtr = NULL;
+           returnPackagePtr = ExecuteRemoteObject(convPtr->riPtr->interp, 
+               objPtr);
+           for (convPtr = conversations; (convPtr != NULL)
+                   && (convPtr->hConv != hConv); convPtr = convPtr->nextPtr) {
+               /*
+                * Empty loop body.
+                */
+
+           }
+           if (convPtr != NULL) {
+               Tcl_IncrRefCount(returnPackagePtr);
+               convPtr->returnPackagePtr = returnPackagePtr;
+           }
+           Tcl_DecrRefCount(objPtr);
+           if (returnPackagePtr == NULL) {
+               return (HDDEDATA) DDE_FNOTPROCESSED;
+           } else {
+               return (HDDEDATA) DDE_FACK;
+           }
+       }
+    case XTYP_WILDCONNECT: 
+       {
+           DWORD length;
+
+           /*
+            * Dde wants a list of services and topics that we support.
+            */
+
+           HSZPAIR *returnPtr;
+           int i;
+           int numItems;
+
+           for (i = 0, riPtr = interps; riPtr != NULL;
+                   i++, riPtr = riPtr->nextPtr) {
+               /*
+                * Empty loop body.
+                */
+
+           }
+
+           numItems = i;
+           code = DdeCreateDataHandle(instance, NULL, 
+               (numItems + 1) * sizeof(HSZPAIR), 0, 0, 0, 0);
+           returnPtr = (HSZPAIR *) DdeAccessData(code, &length);
+           for (i = 0, riPtr = interps; i < numItems; 
+                   i++, riPtr = riPtr->nextPtr) {
+               returnPtr[i].hszSvc = DdeCreateStringHandle(
+                        instance, "TclEval", CP_WINANSI);
+               returnPtr[i].hszTopic = DdeCreateStringHandle(
+                        instance, riPtr->name, CP_WINANSI);
+           }
+           returnPtr[i].hszSvc = NULL;
+           returnPtr[i].hszTopic = NULL;
+           DdeUnaccessData(code);
+           return code;
+       }
+    }
+    return NULL;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ExitProc --
+ *
+ *     Gets rid of our DDE server when we go away.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     The DDE server is deleted.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+ExitProc(
+    ClientData clientData)         /* Not used in this handler. */
+{
+    DdeNameService(instance, NULL, 0, DNS_UNREGISTER);
+    DdeUninitialize(instance);
+    instance = 0;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * MakeConnection --
+ *
+ *     This procedure is a utility used to connect to a DDE
+ *     server when given a server name and a topic name.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *     
+ *
+ * Side effects:
+ *     Passes back a conversation through ddeConvPtr
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+MakeConnection(
+    Tcl_Interp *interp,                /* Used to report errors. */
+    char *name,                        /* The connection to use. */
+    HCONV *convPtr)
+{
+    HSZ topic, service;
+    HCONV conv;
+    
+    service = DdeCreateStringHandle(instance, "TclEval", 0);
+    topic = DdeCreateStringHandle(instance, name, 0);
+
+    conv = DdeConnect(instance, service, topic, NULL);
+    DdeFreeStringHandle(instance, service);
+    DdeFreeStringHandle(instance, topic);
+
+    if (conv == NULL) {
+       if (interp != NULL) {
+           Tcl_AppendResult(interp, "no registered server named \"", name, 
+               "\"", (char *) NULL);
+       }
+       return TCL_ERROR;
+    }
+
+    *convPtr = conv;
+    return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * SetError --
+ *
+ *     Sets the interp result to a cogent error message
+ *     describing the last DDE error.
+ *
+ * Results:
+ *     None.
+ *     
+ *
+ * Side effects:
+ *     The interp's result object is changed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+SetError(
+    Tcl_Interp *interp)            /* The interp to put the message in.*/
+{
+    int err;
+    char *mesg;
+
+    err = DdeGetLastError(instance);
+    switch (err) {
+       case DMLERR_DATAACKTIMEOUT:
+       case DMLERR_EXECACKTIMEOUT:
+       case DMLERR_POKEACKTIMEOUT:
+           mesg = "remote interpreter did not respond";
+           break;
+
+       case DMLERR_BUSY:
+           mesg = "remote server is busy";
+           break;
+
+       case DMLERR_NOTPROCESSED:
+           mesg = "remote server cannot handle this command";
+           break;
+
+       default:
+           mesg = "dde command failed";
+           break;
+    }
+    Tcl_SetResult(interp, mesg, TCL_VOLATILE);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DdeObjCmd --
+ *
+ *     This procedure is invoked to process the "dde" Tcl command.
+ *     See the user documentation for details on what it does.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+DdeObjCmd(
+    ClientData clientData,     /* Used only for deletion */
+    Tcl_Interp *interp,                /* The interp we are sending from */
+    int objc,                  /* Number of arguments */
+    Tcl_Obj *CONST objv[])     /* The arguments */
+{
+    enum {
+       DDE_SERVERNAME,
+       DDE_EXECUTE,
+       DDE_POKE,
+       DDE_REQUEST,
+       DDE_SERVICES,
+       DDE_EVAL
+    };
+
+    static char *commands[] = {
+       "servername", "execute", "poke", "request", "services", "eval", 
+       (char *) NULL
+    };
+    static char *options[] = {
+       "-async", (char *) NULL
+    };
+    int index, argIndex;
+    int async = 0, binary = 0;
+    int result = TCL_OK;
+    HSZ service = NULL;
+    HSZ topic = NULL;
+    HSZ item = NULL;
+    HDDEDATA data = NULL;
+    HDDEDATA itemData = NULL;
+    HCONV hConv = NULL;
+    HSZ cookie = 0;
+    char *serviceName, *topicName, *itemString, *dataString;
+    char *string;
+    int firstArg, length, dataLength;
+    HDDEDATA code;
+    RegisteredInterp *riPtr;
+    Tcl_Interp *sendInterp;
+    Tcl_Obj *objPtr;
+
+    /*
+     * Initialize DDE server/client
+     */
+    
+    if (objc < 2) {
+       Tcl_WrongNumArgs(interp, 1, objv, 
+               "?-async? serviceName topicName value");
+       return TCL_ERROR;
+    }
+
+    if (Tcl_GetIndexFromObj(interp, objv[1], commands, "command", 0,
+           &index) != TCL_OK) {
+       return TCL_ERROR;
+    }
+
+    serviceName = NULL;                /* Suppress compiler warning. */
+    firstArg = 1;
+    switch (index) {
+    case DDE_SERVERNAME:
+       if ((objc != 3) && (objc != 2)) {
+           Tcl_WrongNumArgs(interp, 1, objv, "servername ?serverName?");
+           return TCL_ERROR;
+       }
+       firstArg = (objc - 1);
+       break;
+       
+    case DDE_EXECUTE:
+       if ((objc < 5) || (objc > 6)) {
+           Tcl_WrongNumArgs(interp, 1, objv, 
+                       "execute ?-async? serviceName topicName value");
+           return TCL_ERROR;
+       }
+       if (Tcl_GetIndexFromObj(NULL, objv[2], options, "option", 0,
+                               &argIndex) != TCL_OK) {
+           if (objc != 5) {
+               Tcl_WrongNumArgs(interp, 1, objv,
+                       "execute ?-async? serviceName topicName value");
+               return TCL_ERROR;
+           }
+           async = 0;
+           firstArg = 2;
+       } else {
+           if (objc != 6) {
+               Tcl_WrongNumArgs(interp, 1, objv,
+                        "execute ?-async? serviceName topicName value");
+               return TCL_ERROR;
+           }
+           async = 1;
+           firstArg = 3;
+       }
+       break;
+    case DDE_POKE:
+       if (objc != 6) {
+           Tcl_WrongNumArgs(interp, 1, objv,
+                       "poke serviceName topicName item value");
+           return TCL_ERROR;
+       }
+       firstArg = 2;
+       break;
+
+    case DDE_REQUEST:
+       if (objc != 5) {
+           Tcl_WrongNumArgs(interp, 1, objv,
+                            "request serviceName topicName value");
+           return TCL_ERROR;
+       }
+       binary = 0;
+       firstArg = 2;
+       break;
+
+    case DDE_SERVICES:
+       if (objc != 4) {
+           Tcl_WrongNumArgs(interp, 1, objv,
+                            "services serviceName topicName");
+           return TCL_ERROR;
+       }
+       firstArg = 2;
+       break;
+
+    case DDE_EVAL:
+       if (objc < 4) {
+           Tcl_WrongNumArgs(interp, 1, objv, 
+                            "eval ?-async? serviceName args");
+           return TCL_ERROR;
+       }
+       if (Tcl_GetIndexFromObj(NULL, objv[2], options, "option", 0,
+                               &argIndex) != TCL_OK) {
+           if (objc < 4) {
+               Tcl_WrongNumArgs(interp, 1, objv,
+                                "eval ?-async? serviceName args");
+               return TCL_ERROR;
+           }
+           async = 0;
+           firstArg = 2;
+       } else {
+           if (objc < 5) {
+               Tcl_WrongNumArgs(interp, 1, objv,
+                                "eval ?-async? serviceName args");
+               return TCL_ERROR;
+           }
+           async = 1;
+           firstArg = 3;
+       }
+       break;
+    }
+
+    Initialize();
+
+    if (firstArg != 1) {
+       serviceName = Tcl_GetStringFromObj(objv[firstArg], &length);
+    } else {
+       length = 0;
+    }
+
+    if (length == 0) {
+       serviceName = NULL;
+    } else if ((index != DDE_SERVERNAME) && (index != DDE_EVAL)) {
+       service = DdeCreateStringHandle(instance, serviceName,
+               CP_WINANSI);
+    }
+
+    if ((index != DDE_SERVERNAME) &&(index != DDE_EVAL)) {
+       topicName = Tcl_GetStringFromObj(objv[firstArg + 1], &length);
+       if (length == 0) {
+           topicName = NULL;
+       } else {
+           topic = DdeCreateStringHandle(instance, topicName, CP_WINANSI);
+       }
+    }
+
+    switch (index) {
+    case DDE_SERVERNAME: 
+       serviceName = SetServerName(interp, serviceName);
+       if (serviceName != NULL) {
+           Tcl_SetStringObj(Tcl_GetObjResult(interp),
+                            serviceName, -1);
+       } else {
+           Tcl_ResetResult(interp);
+       }
+       break;
+
+    case DDE_EXECUTE: 
+       {
+           dataString = Tcl_GetStringFromObj(objv[firstArg + 2], &dataLength);
+           if (dataLength == 0) {
+               Tcl_SetStringObj(Tcl_GetObjResult(interp),
+                                "cannot execute null data", -1);
+               result = TCL_ERROR;
+               break;
+           }
+           hConv = DdeConnect(instance, service, topic, NULL);
+           DdeFreeStringHandle(instance, service);
+           DdeFreeStringHandle(instance, topic);
+
+           if (hConv == NULL) {
+               SetError(interp);
+               result = TCL_ERROR;
+               break;
+           }
+
+           data = DdeCreateDataHandle(instance, dataString, dataLength + 1, 
+               0, 0, CF_TEXT, 0);
+           if (data != NULL) {
+               if (async) {
+                   DWORD status;
+
+                   DdeClientTransaction((LPBYTE) data, 0xFFFFFFFF, hConv, 0, 
+                           CF_TEXT, XTYP_EXECUTE, TIMEOUT_ASYNC, &status);
+                   DdeAbandonTransaction(instance, hConv, status);
+               } else {
+                   code = DdeClientTransaction((LPBYTE) data, 0xFFFFFFFF,
+                           hConv, 0, CF_TEXT, XTYP_EXECUTE, 30000, NULL);
+                   if (code == 0) {
+                       SetError(interp);
+                       result = TCL_ERROR;
+                   }
+               }
+               DdeFreeDataHandle(data);
+           } else {
+               SetError(interp);
+               result = TCL_ERROR;
+           }
+           break;
+       }
+    case DDE_REQUEST: 
+       {
+           itemString = Tcl_GetStringFromObj(objv[firstArg + 2], &length);
+           if (length == 0) {
+               Tcl_SetStringObj(Tcl_GetObjResult(interp),
+                       "cannot request value of null data", -1);
+               return TCL_ERROR;
+           }
+           hConv = DdeConnect(instance, service, topic, NULL);
+           DdeFreeStringHandle(instance, service);
+           DdeFreeStringHandle(instance, topic);
+           
+           if (hConv == NULL) {
+               SetError(interp);
+               result = TCL_ERROR;
+           } else {
+               item = DdeCreateStringHandle(instance, itemString, CP_WINANSI);
+               if (item != NULL) {
+                   data = DdeClientTransaction(NULL, 0, hConv, item, CF_TEXT,
+                       XTYP_REQUEST, 5000, NULL);
+                   if (data == NULL) {
+                       SetError(interp);
+                       result = TCL_ERROR;
+                   } else {
+                       Tcl_Obj *objPtr;
+                       DWORD dataLength;
+
+                       dataString = DdeAccessData(data, &dataLength);
+                       objPtr = Tcl_NewStringObj(dataString, -1);
+                       DdeUnaccessData(data);
+                       DdeFreeDataHandle(data);
+                       Tcl_SetObjResult(interp, objPtr);
+                   }
+               } else {
+                   SetError(interp);
+                   result = TCL_ERROR;
+               }
+           }
+
+           break;
+       }
+    case DDE_POKE: 
+       {
+           itemString = Tcl_GetStringFromObj(objv[firstArg + 2], &length);
+           if (length == 0) {
+               Tcl_SetStringObj(Tcl_GetObjResult(interp),
+                       "cannot have a null item", -1);
+               return TCL_ERROR;
+           }
+           dataString = Tcl_GetStringFromObj(objv[firstArg + 3], &length);
+           
+           hConv = DdeConnect(instance, service, topic, NULL);
+           DdeFreeStringHandle(instance, service);
+           DdeFreeStringHandle(instance, topic);
+
+           if (hConv == NULL) {
+               SetError(interp);
+               result = TCL_ERROR;
+           } else {
+               item = DdeCreateStringHandle(instance, itemString,
+                       CP_WINANSI);
+               if (item != NULL) {
+                   data = DdeClientTransaction(dataString,length+1,
+                           hConv, item, CF_TEXT, XTYP_POKE, 5000, NULL);
+                   if (data == NULL) {
+                       SetError(interp);
+                       result = TCL_ERROR;
+                   }
+               } else {
+                   SetError(interp);
+                   result = TCL_ERROR;
+               }
+           }
+           break;
+       }
+
+    case DDE_SERVICES: 
+       {
+           HCONVLIST hConvList;
+           CONVINFO convInfo;
+           Tcl_Obj *convListObjPtr, *elementObjPtr;
+           Tcl_DString dString;
+           char *name;
+           
+           convInfo.cb = sizeof(CONVINFO);
+           hConvList = DdeConnectList(instance, service, 
+                    topic, 0, NULL);
+           DdeFreeStringHandle(instance, service);
+           DdeFreeStringHandle(instance, topic);
+           hConv = 0;
+           convListObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+           Tcl_DStringInit(&dString);
+
+           while (hConv = DdeQueryNextServer(hConvList, hConv), hConv != 0) {
+               elementObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+               DdeQueryConvInfo(hConv, QID_SYNC, &convInfo);
+               length = DdeQueryString(instance, 
+                        convInfo.hszSvcPartner, NULL, 0, CP_WINANSI);
+               Tcl_DStringSetLength(&dString, length);
+               name = Tcl_DStringValue(&dString);
+               DdeQueryString(instance, convInfo.hszSvcPartner, 
+                        name, length + 1, CP_WINANSI);
+               Tcl_ListObjAppendElement(interp, elementObjPtr,
+                       Tcl_NewStringObj(name, length));
+               length = DdeQueryString(instance, convInfo.hszTopic,
+                       NULL, 0, CP_WINANSI);
+               Tcl_DStringSetLength(&dString, length);
+               name = Tcl_DStringValue(&dString);
+               DdeQueryString(instance, convInfo.hszTopic, name,
+                       length + 1, CP_WINANSI);
+               Tcl_ListObjAppendElement(interp, elementObjPtr,
+                       Tcl_NewStringObj(name, length));
+               Tcl_ListObjAppendElement(interp, convListObjPtr,
+                       elementObjPtr);
+           }
+           DdeDisconnectList(hConvList);
+           Tcl_SetObjResult(interp, convListObjPtr);
+           Tcl_DStringFree(&dString);
+           break;
+       }
+    case DDE_EVAL: 
+       {
+           objc -= (async + 3);
+           ((Tcl_Obj **) objv) += (async + 3);
+
+            /*
+            * See if the target interpreter is local.  If so, execute
+            * the command directly without going through the DDE
+            * server.  Don't exchange objects between interps.  The
+            * target interp could compile an object, producing a
+            * bytecode structure that refers to other objects owned
+            * by the target interp.  If the target interp is then
+            * deleted, the bytecode structure would be referring to
+            * deallocated objects.
+            */
+           
+           for (riPtr = interps; riPtr != NULL;
+                riPtr = riPtr->nextPtr) {
+               if (strcasecmp(serviceName, riPtr->name) == 0) {
+                   break;
+               }
+           }
+
+           if (riPtr != NULL) {
+               /*
+                * This command is to a local interp. No need to go through
+                * the server.
+                */
+               
+               Tcl_Preserve(riPtr);
+               sendInterp = riPtr->interp;
+               Tcl_Preserve(sendInterp);
+               
+               /*
+                * Don't exchange objects between interps.  The target interp
+                * would compile an object, producing a bytecode structure that
+                * refers to other objects owned by the target interp.  If the
+                * target interp is then deleted, the bytecode structure would
+                * be referring to deallocated objects.
+                */
+
+               if (objc == 1) {
+                   result = Tcl_GlobalEval(sendInterp,Tcl_GetString(objv[0]));
+               } else {
+                   objPtr = Tcl_ConcatObj(objc, objv);
+                   Tcl_IncrRefCount(objPtr);
+                   result = Tcl_GlobalEval(sendInterp, Tcl_GetString(objPtr));
+                   Tcl_DecrRefCount(objPtr);
+               }
+               if (interp != sendInterp) {
+                   if (result == TCL_ERROR) {
+                       char *value;
+                       /*
+                        * An error occurred, so transfer error information
+                        * from the destination interpreter back to our
+                        * interpreter.
+                        */
+                       
+                       Tcl_ResetResult(interp);
+                       value = Tcl_GetVar2(sendInterp, "errorInfo", NULL, 
+                               TCL_GLOBAL_ONLY);
+                       Tcl_AddObjErrorInfo(interp, value, length);
+                       
+                       value = Tcl_GetVar2(sendInterp, "errorCode", NULL,
+                               TCL_GLOBAL_ONLY);
+                       Tcl_SetErrorCode(interp, value, (char *)NULL);
+                   }
+                   Tcl_SetObjResult(interp, Tcl_GetObjResult(sendInterp));
+               }
+               Tcl_Release(riPtr);
+               Tcl_Release(sendInterp);
+           } else {
+               /*
+                * This is a non-local request. Send the script to the server
+                * and poll it for a result.
+                */
+               
+               if (MakeConnection(interp, serviceName, &hConv) != TCL_OK) {
+                   goto error;
+               }
+               
+               objPtr = Tcl_ConcatObj(objc, objv);
+               string = Tcl_GetStringFromObj(objPtr, &length);
+               itemData = DdeCreateDataHandle(instance, string,
+                       length+1, 0, 0, CF_TEXT, 0);
+               
+               if (async) {
+                   DWORD status;
+
+                   data = DdeClientTransaction((LPBYTE) itemData, 0xFFFFFFFF,
+                       hConv, 0, CF_TEXT, XTYP_EXECUTE, TIMEOUT_ASYNC,
+                       &status);
+                   DdeAbandonTransaction(instance, hConv, status);
+               } else {
+                   data = DdeClientTransaction((LPBYTE) itemData,
+                           0xFFFFFFFF, hConv, 0,
+                           CF_TEXT, XTYP_EXECUTE, 30000, NULL);
+                   if (data != 0) {
+                       
+                       cookie = DdeCreateStringHandle(instance, 
+                               "$TCLEVAL$EXECUTE$RESULT", CP_WINANSI);
+                       data = DdeClientTransaction(NULL, 0, hConv,
+                               cookie, CF_TEXT, XTYP_REQUEST, 30000, NULL);
+                   }
+               }
+
+               Tcl_DecrRefCount(objPtr);
+               
+               if (data == 0) {
+                   SetError(interp);
+                   goto errorNoResult;
+               }
+               
+               if (async == 0) {
+                   Tcl_Obj *resultPtr;
+                   
+                   /*
+                    * The return handle has a two or four element list in
+                    * it. The first element is the return code (TCL_OK,
+                    * TCL_ERROR, etc.). The second is the result of the
+                    * script. If the return code is TCL_ERROR, then the third
+                    * element is the value of the variable "errorCode", and
+                    * the fourth is the value of the variable "errorInfo".
+                    */
+                   
+                   resultPtr = Tcl_NewObj();
+                   length = DdeGetData(data, NULL, 0, 0);
+                   Tcl_SetObjLength(resultPtr, length);
+                   string = Tcl_GetString(resultPtr);
+                   DdeGetData(data, string, length, 0);
+                   Tcl_SetObjLength(resultPtr, strlen(string));
+                   
+                   if (Tcl_ListObjIndex(NULL, resultPtr, 0, &objPtr)
+                           != TCL_OK) {
+                       Tcl_DecrRefCount(resultPtr);
+                       goto error;
+                   }
+                   if (Tcl_GetIntFromObj(NULL, objPtr, &result) != TCL_OK) {
+                       Tcl_DecrRefCount(resultPtr);
+                       goto error;
+                   }
+                   if (result == TCL_ERROR) {
+                       Tcl_ResetResult(interp);
+
+                       if (Tcl_ListObjIndex(NULL, resultPtr, 3, &objPtr)
+                               != TCL_OK) {
+                           Tcl_DecrRefCount(resultPtr);
+                           goto error;
+                       }
+                       length = -1;
+                       string = Tcl_GetStringFromObj(objPtr, &length);
+                       Tcl_AddObjErrorInfo(interp, string, length);
+                       
+                       Tcl_ListObjIndex(NULL, resultPtr, 2, &objPtr);
+                       Tcl_SetObjErrorCode(interp, objPtr);
+                   }
+                   if (Tcl_ListObjIndex(NULL, resultPtr, 1, &objPtr)
+                           != TCL_OK) {
+                       Tcl_DecrRefCount(resultPtr);
+                       goto error;
+                   }
+                   Tcl_SetObjResult(interp, objPtr);
+                   Tcl_DecrRefCount(resultPtr);
+               }
+           }
+       }
+    }
+    if (cookie != NULL) {
+       DdeFreeStringHandle(instance, cookie);
+    }
+    if (item != NULL) {
+       DdeFreeStringHandle(instance, item);
+    }
+    if (itemData != NULL) {
+       DdeFreeDataHandle(itemData);
+    }
+    if (data != NULL) {
+       DdeFreeDataHandle(data);
+    }
+    if (hConv != NULL) {
+       DdeDisconnect(hConv);
+    }
+    return result;
+
+    error:
+    Tcl_SetStringObj(Tcl_GetObjResult(interp), 
+            "invalid data returned from server", -1);
+
+    errorNoResult:
+    if (cookie != NULL) {
+       DdeFreeStringHandle(instance, cookie);
+    }
+    if (item != NULL) {
+       DdeFreeStringHandle(instance, item);
+    }
+    if (itemData != NULL) {
+       DdeFreeDataHandle(itemData);
+    }
+    if (data != NULL) {
+       DdeFreeDataHandle(data);
+    }
+    if (hConv != NULL) {
+       DdeDisconnect(hConv);
+    }
+    return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DdeInit --
+ *
+ *     This procedure initializes the dde command.
+ *
+ * Results:
+ *     A standard Tcl result.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Blt_DdeInit(interp)
+    Tcl_Interp *interp;
+{
+    Tcl_CreateObjCommand(interp, "dde", DdeObjCmd, NULL, NULL);
+    conversations = NULL;
+    interps = NULL;
+    Tcl_CreateExitHandler(ExitProc, NULL);
+    return Tcl_PkgProvide(interp, TCL_DDE_PACKAGE_NAME, TCL_DDE_VERSION);
+}
+
+#endif /* NO_DDE */