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_RETRIES 10
47 #define CONNECT_DELAY 500 /* ms */
48 #define TRANSACTION_TIMEOUT 5000 /* ms */
49 #define MAX_INPUT_IDLE_WAIT INFINITE /* ms */
52 #define SERVICE_NAME "XEmacs"
53 #define TOPIC_NAME "System"
54 #define COMMAND_FORMAT "[open(\"%s%s\")]"
56 /* XEmacs program name */
57 #define PROGRAM_TO_RUN "xemacs.exe"
59 /* -- Constants ------------------------------------------------------------- */
61 /* -- Global Variables ------------------------------------------------------ */
66 /* -- Function Declarations ------------------------------------------------- */
68 HDDEDATA CALLBACK ddeCallback (UINT uType, UINT uFmt, HCONV hconv,
69 HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
70 DWORD dwData1, DWORD dwData2);
72 int WINAPI WinMain (HINSTANCE hInst,
77 static HCONV openConversation (void);
78 static void closeConversation (HCONV hConv);
79 static int doFile (HCONV hConv, LPSTR lpszFileName1, LPSTR lpszFileName2);
80 static int parseCommandLine (HCONV hConv, LPSTR lpszCommandLine);
82 /* -- Function Definitions -------------------------------------------------- */
86 * Function: Gets called by DDEML.
91 ddeCallback (UINT uType, UINT uFmt, HCONV hconv,
92 HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
93 DWORD dwData1, DWORD dwData2)
95 return (HDDEDATA) NULL;
100 * Function: The program's entry point function.
105 WinMain (HINSTANCE hInst,
114 /* Initialise the DDEML library */
115 uiRet = DdeInitialize (&idInst,
116 (PFNCALLBACK) ddeCallback,
118 |CBF_FAIL_ALLSVRXACTIONS,
121 if (uiRet != DMLERR_NO_ERROR)
123 MessageBox (NULL, "Could not initialise DDE management library.",
124 "winclient", MB_ICONEXCLAMATION | MB_OK);
129 /* Open a conversation */
130 hConv = openConversation ();
134 /* OK. Next, we need to parse the command line. */
135 ret = parseCommandLine (hConv, lpCmdLine);
137 /* Close the conversation */
138 closeConversation (hConv);
141 DdeUninitialize (idInst);
147 * Name : openConversation
148 * Function: Start a conversation.
153 openConversation (void)
155 HSZ hszService = NULL, hszTopic = NULL;
158 /* Get the application (service) name */
159 hszService = DdeCreateStringHandle (idInst,
165 MessageBox (NULL, "Could not create string handle for service.",
166 "winclient", MB_ICONEXCLAMATION | MB_OK);
171 /* Get the topic name */
172 hszTopic = DdeCreateStringHandle (idInst,
178 MessageBox (NULL, "Could not create string handle for topic.",
179 "winclient", MB_ICONEXCLAMATION | MB_OK);
185 hConv = DdeConnect (idInst, hszService, hszTopic, NULL);
190 PROCESS_INFORMATION pi;
193 /* Try to start the program */
194 ZeroMemory (&sti, sizeof (sti));
195 sti.cb = sizeof (sti);
196 if (!CreateProcess (NULL, PROGRAM_TO_RUN, NULL, NULL, FALSE, 0,
197 NULL, NULL, &sti, &pi))
199 MessageBox (NULL, "Could not start process.",
200 "winclient", MB_ICONEXCLAMATION | MB_OK);
205 /* Wait for the process to enter an idle state */
206 WaitForInputIdle (pi.hProcess, MAX_INPUT_IDLE_WAIT);
208 /* Close the handles */
209 CloseHandle (pi.hThread);
210 CloseHandle (pi.hProcess);
213 for (n = 0; n < CONNECT_RETRIES; n++)
215 Sleep (CONNECT_DELAY);
217 hConv = DdeConnect (idInst, hszService, hszTopic, NULL);
225 /* Still couldn't connect. */
226 MessageBox (NULL, "Could not connect to DDE server.",
227 "winclient", MB_ICONEXCLAMATION | MB_OK);
233 /* Release the string handles */
234 DdeFreeStringHandle (idInst, hszService);
235 DdeFreeStringHandle (idInst, hszTopic);
241 DdeDisconnect (hConv);
243 DdeFreeStringHandle (idInst, hszService);
245 DdeFreeStringHandle (idInst, hszTopic);
251 * Name : closeConversation
252 * Function: Close a conversation.
257 closeConversation (HCONV hConv)
260 DdeDisconnect (hConv);
265 * Function: Process a file.
270 doFile (HCONV hConv, LPSTR lpszFileName1, LPSTR lpszFileName2)
275 /* Calculate the buffer length */
276 len = strlen (lpszFileName1) + strlen (lpszFileName2)
277 + strlen (COMMAND_FORMAT);
279 /* Allocate a buffer */
280 buf = (char *) xmalloc (len);
284 MessageBox (NULL, "Not enough memory.",
285 "winclient", MB_ICONEXCLAMATION | MB_OK);
290 /* Build the command */
291 len = wsprintf (buf, COMMAND_FORMAT, lpszFileName1, lpszFileName2);
295 /* OK. We're connected. Send the message. */
296 DdeClientTransaction (buf, len, hConv, NULL,
297 0, XTYP_EXECUTE, TRANSACTION_TIMEOUT, NULL);
306 * Function: Retrieve the next command line argument.
311 getNextArg (const char **ptr, unsigned *len)
313 int in_quotes = 0, quit = 0, all_in_quotes = 0;
314 const char *p = *ptr, *start;
318 /* Skip whitespace */
319 while (*p && isspace (*p))
322 /* If this is the end, return NULL */
326 /* Remember where we are */
329 /* Find the next whitespace character outside quotes */
338 in_quotes = 1 - in_quotes;
355 if (isspace (*p) && !in_quotes)
365 /* Work out the length */
368 /* Strip quotes if the argument is completely quoted */
376 buf = (char *) xmalloc (length + 1);
381 strncpy (buf, start, length);
384 /* Return the pointer and length */
392 * Name : parseCommandLine
393 * Function: Process the command line. This program accepts a list of strings
394 * : (which may contain wildcards) representing filenames.
399 parseCommandLine (HCONV hConv, LPSTR lpszCommandLine)
401 char *fullpath, *filepart;
403 unsigned len, pathlen;
405 HANDLE hFindFile = NULL;
408 /* Retrieve arguments */
409 while ((arg = getNextArg ((const char**)&lpszCommandLine, &len)) != NULL)
411 /* First find the canonical path name */
412 fullpath = filepart = NULL;
413 pathlen = GetFullPathName (arg, 0, fullpath, &filepart);
415 fullpath = (char *) xmalloc (pathlen);
419 MessageBox (NULL, "Not enough memory.", "winclient",
420 MB_ICONEXCLAMATION | MB_OK);
428 GetFullPathName (arg, pathlen, fullpath, &filepart);
430 /* Find the first matching file */
431 hFindFile = FindFirstFile (arg, &wfd);
433 if (hFindFile == INVALID_HANDLE_VALUE)
434 ret = doFile (hConv, fullpath, "");
437 /* Chop off the file part from the full path name */
441 /* For each matching file */
445 ret = doFile (hConv, fullpath, wfd.cFileName);
450 while (FindNextFile (hFindFile, &wfd));
452 FindClose (hFindFile);
455 /* Release the path name buffers */
467 fatal (const char *s1, const char *s2)
473 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
475 error (const char* s1, const char* s2)
477 fprintf (stderr, "winclient: ");
478 fprintf (stderr, s1, s2);
479 fprintf (stderr, "\n");
482 /* Like malloc but get fatal error if memory is exhausted. */
485 xmalloc (size_t size)
487 void *result = malloc (size);
489 fatal ("virtual memory exhausted", (char *) 0);