1 /* DDE client for XEmacs.
\r
2 Copyright (C) 2002 Alastair J. Houghton
\r
4 This file is part of XEmacs.
\r
6 XEmacs is free software; you can redistribute it and/or modify it
\r
7 under the terms of the GNU General Public License as published by the
\r
8 Free Software Foundation; either version 2, or (at your option) any
\r
11 XEmacs is distributed in the hope that it will be useful, but WITHOUT
\r
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
\r
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
\r
16 You should have received a copy of the GNU General Public License
\r
17 along with XEmacs; see the file COPYING. If not, write to
\r
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
\r
19 Boston, MA 02111-1307, USA. */
\r
21 /* Synched up with: Not in FSF. */
\r
23 /* -- Pre-Include Defines --------------------------------------------------- */
\r
27 /* -- Includes -------------------------------------------------------------- */
\r
29 #include <windows.h>
\r
30 #ifdef HAVE_CONFIG_H
\r
31 # include <config.h>
\r
38 static void error (const char* s1, const char* s2);
\r
39 static void fatal (const char *s1, const char *s2);
\r
40 static void * xmalloc (size_t size);
\r
41 static char * getNextArg (const char **ptr, unsigned *len);
\r
43 /* -- Post-Include Defines -------------------------------------------------- */
\r
45 /* Timeouts & delays */
\r
46 #define CONNECT_DELAY 500 /* ms */
\r
47 #define TRANSACTION_TIMEOUT 5000 /* ms */
\r
48 #define MAX_INPUT_IDLE_WAIT INFINITE /* ms */
\r
51 #define SERVICE_NAME "XEmacs"
\r
52 #define TOPIC_NAME "System"
\r
53 #define COMMAND_FORMAT "[open(\"%s%s\")]"
\r
55 /* XEmacs program name */
\r
56 #define PROGRAM_TO_RUN "xemacs.exe"
\r
58 /* -- Constants ------------------------------------------------------------- */
\r
60 /* -- Global Variables ------------------------------------------------------ */
\r
62 HINSTANCE hInstance;
\r
65 /* -- Function Declarations ------------------------------------------------- */
\r
67 HDDEDATA CALLBACK ddeCallback (UINT uType, UINT uFmt, HCONV hconv,
\r
68 HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
\r
69 DWORD dwData1, DWORD dwData2);
\r
71 int WINAPI WinMain (HINSTANCE hInst,
\r
76 static HCONV openConversation (void);
\r
77 static void closeConversation (HCONV hConv);
\r
78 static int doFile (HCONV hConv, LPSTR lpszFileName1, LPSTR lpszFileName2);
\r
79 static int parseCommandLine (HCONV hConv, LPSTR lpszCommandLine);
\r
81 /* -- Function Definitions -------------------------------------------------- */
\r
84 * Name : ddeCallback
\r
85 * Function: Gets called by DDEML.
\r
90 ddeCallback (UINT uType, UINT uFmt, HCONV hconv,
\r
91 HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
\r
92 DWORD dwData1, DWORD dwData2)
\r
94 return (HDDEDATA) NULL;
\r
99 * Function: The program's entry point function.
\r
104 WinMain (HINSTANCE hInst,
\r
113 /* Initialise the DDEML library */
\r
114 uiRet = DdeInitialize (&idInst,
\r
115 (PFNCALLBACK) ddeCallback,
\r
117 |CBF_FAIL_ALLSVRXACTIONS,
\r
120 if (uiRet != DMLERR_NO_ERROR)
\r
122 MessageBox (NULL, "Could not initialise DDE management library.",
\r
123 "winclient", MB_ICONEXCLAMATION | MB_OK);
\r
128 /* Open a conversation */
\r
129 hConv = openConversation ();
\r
133 /* OK. Next, we need to parse the command line. */
\r
134 ret = parseCommandLine (hConv, lpCmdLine);
\r
136 /* Close the conversation */
\r
137 closeConversation (hConv);
\r
140 DdeUninitialize (idInst);
\r
146 * Name : openConversation
\r
147 * Function: Start a conversation.
\r
152 openConversation (void)
\r
154 HSZ hszService = NULL, hszTopic = NULL;
\r
155 HCONV hConv = NULL;
\r
157 /* Get the application (service) name */
\r
158 hszService = DdeCreateStringHandle (idInst,
\r
164 MessageBox (NULL, "Could not create string handle for service.",
\r
165 "winclient", MB_ICONEXCLAMATION | MB_OK);
\r
170 /* Get the topic name */
\r
171 hszTopic = DdeCreateStringHandle (idInst,
\r
177 MessageBox (NULL, "Could not create string handle for topic.",
\r
178 "winclient", MB_ICONEXCLAMATION | MB_OK);
\r
183 /* Try to connect */
\r
184 hConv = DdeConnect (idInst, hszService, hszTopic, NULL);
\r
189 PROCESS_INFORMATION pi;
\r
192 /* Try to start the program */
\r
193 ZeroMemory (&sti, sizeof (sti));
\r
194 sti.cb = sizeof (sti);
\r
195 if (!CreateProcess (NULL, PROGRAM_TO_RUN, NULL, NULL, FALSE, 0,
\r
196 NULL, NULL, &sti, &pi))
\r
198 MessageBox (NULL, "Could not start process.",
\r
199 "winclient", MB_ICONEXCLAMATION | MB_OK);
\r
204 /* Wait for the process to enter an idle state */
\r
205 WaitForInputIdle (pi.hProcess, MAX_INPUT_IDLE_WAIT);
\r
207 /* Close the handles */
\r
208 CloseHandle (pi.hThread);
\r
209 CloseHandle (pi.hProcess);
\r
211 /* Try to connect */
\r
212 for (n = 0; n < 5; n++)
\r
214 Sleep (CONNECT_DELAY);
\r
216 hConv = DdeConnect (idInst, hszService, hszTopic, NULL);
\r
224 /* Still couldn't connect. */
\r
225 MessageBox (NULL, "Could not connect to DDE server.",
\r
226 "winclient", MB_ICONEXCLAMATION | MB_OK);
\r
232 /* Release the string handles */
\r
233 DdeFreeStringHandle (idInst, hszService);
\r
234 DdeFreeStringHandle (idInst, hszTopic);
\r
240 DdeDisconnect (hConv);
\r
242 DdeFreeStringHandle (idInst, hszService);
\r
244 DdeFreeStringHandle (idInst, hszTopic);
\r
250 * Name : closeConversation
\r
251 * Function: Close a conversation.
\r
256 closeConversation (HCONV hConv)
\r
259 DdeDisconnect (hConv);
\r
264 * Function: Process a file.
\r
269 doFile (HCONV hConv, LPSTR lpszFileName1, LPSTR lpszFileName2)
\r
274 /* Calculate the buffer length */
\r
275 len = strlen (lpszFileName1) + strlen (lpszFileName2)
\r
276 + strlen (COMMAND_FORMAT);
\r
278 /* Allocate a buffer */
\r
279 buf = (char *) xmalloc (len);
\r
283 MessageBox (NULL, "Not enough memory.",
\r
284 "winclient", MB_ICONEXCLAMATION | MB_OK);
\r
289 /* Build the command */
\r
290 len = wsprintf (buf, COMMAND_FORMAT, lpszFileName1, lpszFileName2);
\r
294 /* OK. We're connected. Send the message. */
\r
295 DdeClientTransaction (buf, len, hConv, NULL,
\r
296 0, XTYP_EXECUTE, TRANSACTION_TIMEOUT, NULL);
\r
304 * Name : getNextArg
\r
305 * Function: Retrieve the next command line argument.
\r
310 getNextArg (const char **ptr, unsigned *len)
\r
312 int in_quotes = 0, quit = 0, all_in_quotes = 0;
\r
313 const char *p = *ptr, *start;
\r
315 unsigned length = 0;
\r
317 /* Skip whitespace */
\r
318 while (*p && isspace (*p))
\r
321 /* If this is the end, return NULL */
\r
325 /* Remember where we are */
\r
328 /* Find the next whitespace character outside quotes */
\r
332 while (*p && !quit)
\r
337 in_quotes = 1 - in_quotes;
\r
354 if (isspace (*p) && !in_quotes)
\r
356 else if (!in_quotes)
\r
364 /* Work out the length */
\r
365 length = p - start;
\r
367 /* Strip quotes if the argument is completely quoted */
\r
375 buf = (char *) xmalloc (length + 1);
\r
380 strncpy (buf, start, length);
\r
381 buf[length] = '\0';
\r
383 /* Return the pointer and length */
\r
391 * Name : parseCommandLine
\r
392 * Function: Process the command line. This program accepts a list of strings
\r
393 * : (which may contain wildcards) representing filenames.
\r
398 parseCommandLine (HCONV hConv, LPSTR lpszCommandLine)
\r
400 char *fullpath, *filepart;
\r
402 unsigned len, pathlen;
\r
404 HANDLE hFindFile = NULL;
\r
405 WIN32_FIND_DATA wfd;
\r
407 /* Retrieve arguments */
\r
408 while ((arg = getNextArg ((const char**)&lpszCommandLine, &len)) != NULL)
\r
410 /* First find the canonical path name */
\r
411 fullpath = filepart = NULL;
\r
412 pathlen = GetFullPathName (arg, 0, fullpath, &filepart);
\r
414 fullpath = (char *) xmalloc (pathlen);
\r
418 MessageBox (NULL, "Not enough memory.", "winclient",
\r
419 MB_ICONEXCLAMATION | MB_OK);
\r
427 GetFullPathName (arg, pathlen, fullpath, &filepart);
\r
429 /* Find the first matching file */
\r
430 hFindFile = FindFirstFile (arg, &wfd);
\r
432 if (hFindFile == INVALID_HANDLE_VALUE)
\r
433 ret = doFile (hConv, fullpath, "");
\r
436 /* Chop off the file part from the full path name */
\r
440 /* For each matching file */
\r
444 ret = doFile (hConv, fullpath, wfd.cFileName);
\r
449 while (FindNextFile (hFindFile, &wfd));
\r
451 FindClose (hFindFile);
\r
454 /* Release the path name buffers */
\r
466 fatal (const char *s1, const char *s2)
\r
472 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
\r
474 error (const char* s1, const char* s2)
\r
476 fprintf (stderr, "winclient: ");
\r
477 fprintf (stderr, s1, s2);
\r
478 fprintf (stderr, "\n");
\r
481 /* Like malloc but get fatal error if memory is exhausted. */
\r
484 xmalloc (size_t size)
\r
486 void *result = malloc (size);
\r
487 if (result == NULL)
\r
488 fatal ("virtual memory exhausted", (char *) 0);
\r