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