853
+ − 1 /* DDE client for XEmacs.
+ − 2 Copyright (C) 2002 Alastair J. Houghton
+ − 3
+ − 4 This file is part of XEmacs.
+ − 5
+ − 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
+ − 9 later version.
+ − 10
+ − 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
+ − 14 for more details.
+ − 15
+ − 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. */
+ − 20
+ − 21 /* Synched up with: Not in FSF. */
+ − 22
+ − 23 /* -- Includes -------------------------------------------------------------- */
+ − 24
+ − 25 #include <windows.h>
+ − 26 #ifdef HAVE_CONFIG_H
+ − 27 # include <config.h>
+ − 28 #endif
+ − 29 #include <stdlib.h>
+ − 30 #include <stdio.h>
+ − 31 #include <ctype.h>
+ − 32 #include <errno.h>
+ − 33
+ − 34 static void error (const char* s1, const char* s2);
+ − 35 static void fatal (const char *s1, const char *s2);
+ − 36 static void * xmalloc (size_t size);
+ − 37 static char * getNextArg (const char **ptr, unsigned *len);
+ − 38
+ − 39 /* -- Post-Include Defines -------------------------------------------------- */
+ − 40
+ − 41 /* Timeouts & delays */
+ − 42 #define CONNECT_DELAY 500 /* ms */
+ − 43 #define TRANSACTION_TIMEOUT 5000 /* ms */
+ − 44 #define MAX_INPUT_IDLE_WAIT INFINITE /* ms */
+ − 45
+ − 46 /* DDE Strings */
+ − 47 #define SERVICE_NAME "XEmacs"
+ − 48 #define TOPIC_NAME "System"
+ − 49 #define COMMAND_FORMAT "[open(\"%s%s\")]"
+ − 50
+ − 51 /* XEmacs program name */
+ − 52 #define PROGRAM_TO_RUN "xemacs.exe"
+ − 53
+ − 54 /* -- Constants ------------------------------------------------------------- */
+ − 55
+ − 56 /* -- Global Variables ------------------------------------------------------ */
+ − 57
+ − 58 HINSTANCE hInstance;
+ − 59 DWORD idInst = 0;
+ − 60
+ − 61 /* -- Function Declarations ------------------------------------------------- */
+ − 62
+ − 63 HDDEDATA CALLBACK ddeCallback (UINT uType, UINT uFmt, HCONV hconv,
+ − 64 HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
+ − 65 DWORD dwData1, DWORD dwData2);
+ − 66
+ − 67 int WINAPI WinMain (HINSTANCE hInst,
+ − 68 HINSTANCE hPrev,
+ − 69 LPSTR lpCmdLine,
+ − 70 int nCmdShow);
+ − 71
+ − 72 static HCONV openConversation (void);
+ − 73 static void closeConversation (HCONV hConv);
+ − 74 static int doFile (HCONV hConv, LPSTR lpszFileName1, LPSTR lpszFileName2);
+ − 75 static int parseCommandLine (HCONV hConv, LPSTR lpszCommandLine);
+ − 76
+ − 77 /* -- Function Definitions -------------------------------------------------- */
+ − 78
+ − 79 /*
+ − 80 * Name : ddeCallback
+ − 81 * Function: Gets called by DDEML.
+ − 82 *
+ − 83 */
+ − 84
+ − 85 HDDEDATA CALLBACK
+ − 86 ddeCallback (UINT uType, UINT uFmt, HCONV hconv,
+ − 87 HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
+ − 88 DWORD dwData1, DWORD dwData2)
+ − 89 {
+ − 90 return (HDDEDATA) NULL;
+ − 91 }
+ − 92
+ − 93 /*
+ − 94 * Name : WinMain
+ − 95 * Function: The program's entry point function.
+ − 96 *
+ − 97 */
+ − 98
+ − 99 int WINAPI
+ − 100 WinMain (HINSTANCE hInst,
+ − 101 HINSTANCE hPrev,
+ − 102 LPSTR lpCmdLine,
+ − 103 int nCmdShow)
+ − 104 {
+ − 105 HCONV hConv;
+ − 106 int ret = 0;
+ − 107 UINT uiRet;
+ − 108
+ − 109 /* Initialise the DDEML library */
+ − 110 uiRet = DdeInitialize (&idInst,
+ − 111 (PFNCALLBACK) ddeCallback,
+ − 112 APPCMD_CLIENTONLY
+ − 113 |CBF_FAIL_ALLSVRXACTIONS,
+ − 114 0);
+ − 115
+ − 116 if (uiRet != DMLERR_NO_ERROR)
+ − 117 {
+ − 118 MessageBox (NULL, "Could not initialise DDE management library.",
+ − 119 "winclient", MB_ICONEXCLAMATION | MB_OK);
+ − 120
+ − 121 return 1;
+ − 122 }
+ − 123
+ − 124 /* Open a conversation */
+ − 125 hConv = openConversation ();
+ − 126
+ − 127 if (hConv)
+ − 128 {
+ − 129 /* OK. Next, we need to parse the command line. */
+ − 130 ret = parseCommandLine (hConv, lpCmdLine);
+ − 131
+ − 132 /* Close the conversation */
+ − 133 closeConversation (hConv);
+ − 134 }
+ − 135
+ − 136 DdeUninitialize (idInst);
+ − 137
+ − 138 return ret;
+ − 139 }
+ − 140
+ − 141 /*
+ − 142 * Name : openConversation
+ − 143 * Function: Start a conversation.
+ − 144 *
+ − 145 */
+ − 146
+ − 147 static HCONV
+ − 148 openConversation (void)
+ − 149 {
+ − 150 HSZ hszService = NULL, hszTopic = NULL;
+ − 151 HCONV hConv = NULL;
+ − 152
+ − 153 /* Get the application (service) name */
+ − 154 hszService = DdeCreateStringHandle (idInst,
+ − 155 SERVICE_NAME,
+ − 156 CP_WINANSI);
+ − 157
+ − 158 if (!hszService)
+ − 159 {
+ − 160 MessageBox (NULL, "Could not create string handle for service.",
+ − 161 "winclient", MB_ICONEXCLAMATION | MB_OK);
+ − 162
+ − 163 goto error;
+ − 164 }
+ − 165
+ − 166 /* Get the topic name */
+ − 167 hszTopic = DdeCreateStringHandle (idInst,
+ − 168 TOPIC_NAME,
+ − 169 CP_WINANSI);
+ − 170
+ − 171 if (!hszTopic)
+ − 172 {
+ − 173 MessageBox (NULL, "Could not create string handle for topic.",
+ − 174 "winclient", MB_ICONEXCLAMATION | MB_OK);
+ − 175
+ − 176 goto error;
+ − 177 }
+ − 178
+ − 179 /* Try to connect */
+ − 180 hConv = DdeConnect (idInst, hszService, hszTopic, NULL);
+ − 181
+ − 182 if (!hConv)
+ − 183 {
+ − 184 STARTUPINFO sti;
+ − 185 PROCESS_INFORMATION pi;
+ − 186 int n;
+ − 187
+ − 188 /* Try to start the program */
+ − 189 ZeroMemory (&sti, sizeof (sti));
+ − 190 sti.cb = sizeof (sti);
+ − 191 if (!CreateProcess (NULL, PROGRAM_TO_RUN, NULL, NULL, FALSE, 0,
+ − 192 NULL, NULL, &sti, &pi))
+ − 193 {
+ − 194 MessageBox (NULL, "Could not start process.",
+ − 195 "winclient", MB_ICONEXCLAMATION | MB_OK);
+ − 196
+ − 197 goto error;
+ − 198 }
+ − 199
+ − 200 /* Wait for the process to enter an idle state */
+ − 201 WaitForInputIdle (pi.hProcess, MAX_INPUT_IDLE_WAIT);
+ − 202
+ − 203 /* Close the handles */
+ − 204 CloseHandle (pi.hThread);
+ − 205 CloseHandle (pi.hProcess);
+ − 206
+ − 207 /* Try to connect */
+ − 208 for (n = 0; n < 5; n++)
+ − 209 {
+ − 210 Sleep (CONNECT_DELAY);
+ − 211
+ − 212 hConv = DdeConnect (idInst, hszService, hszTopic, NULL);
+ − 213
+ − 214 if (hConv)
+ − 215 break;
+ − 216 }
+ − 217
+ − 218 if (!hConv)
+ − 219 {
+ − 220 /* Still couldn't connect. */
+ − 221 MessageBox (NULL, "Could not connect to DDE server.",
+ − 222 "winclient", MB_ICONEXCLAMATION | MB_OK);
+ − 223
+ − 224 goto error;
+ − 225 }
+ − 226 }
+ − 227
+ − 228 /* Release the string handles */
+ − 229 DdeFreeStringHandle (idInst, hszService);
+ − 230 DdeFreeStringHandle (idInst, hszTopic);
+ − 231
+ − 232 return hConv;
+ − 233
+ − 234 error:
+ − 235 if (hConv)
+ − 236 DdeDisconnect (hConv);
+ − 237 if (hszService)
+ − 238 DdeFreeStringHandle (idInst, hszService);
+ − 239 if (hszTopic)
+ − 240 DdeFreeStringHandle (idInst, hszTopic);
+ − 241
+ − 242 return NULL;
+ − 243 }
+ − 244
+ − 245 /*
+ − 246 * Name : closeConversation
+ − 247 * Function: Close a conversation.
+ − 248 *
+ − 249 */
+ − 250
+ − 251 static void
+ − 252 closeConversation (HCONV hConv)
+ − 253 {
+ − 254 /* Shut down */
+ − 255 DdeDisconnect (hConv);
+ − 256 }
+ − 257
+ − 258 /*
+ − 259 * Name : doFile
+ − 260 * Function: Process a file.
+ − 261 *
+ − 262 */
+ − 263
+ − 264 int
+ − 265 doFile (HCONV hConv, LPSTR lpszFileName1, LPSTR lpszFileName2)
+ − 266 {
+ − 267 char *buf = NULL;
+ − 268 unsigned len;
+ − 269
+ − 270 /* Calculate the buffer length */
+ − 271 len = strlen (lpszFileName1) + strlen (lpszFileName2)
+ − 272 + strlen (COMMAND_FORMAT);
+ − 273
+ − 274 /* Allocate a buffer */
+ − 275 buf = (char *) xmalloc (len);
+ − 276
+ − 277 if (!buf)
+ − 278 {
+ − 279 MessageBox (NULL, "Not enough memory.",
+ − 280 "winclient", MB_ICONEXCLAMATION | MB_OK);
+ − 281
+ − 282 return 1;
+ − 283 }
+ − 284
+ − 285 /* Build the command */
+ − 286 len = wsprintf (buf, COMMAND_FORMAT, lpszFileName1, lpszFileName2);
+ − 287
+ − 288 len++;
+ − 289
+ − 290 /* OK. We're connected. Send the message. */
+ − 291 DdeClientTransaction (buf, len, hConv, NULL,
+ − 292 0, XTYP_EXECUTE, TRANSACTION_TIMEOUT, NULL);
+ − 293
+ − 294 free (buf);
+ − 295
+ − 296 return 0;
+ − 297 }
+ − 298
+ − 299 /*
+ − 300 * Name : getNextArg
+ − 301 * Function: Retrieve the next command line argument.
+ − 302 *
+ − 303 */
+ − 304
+ − 305 static char *
+ − 306 getNextArg (const char **ptr, unsigned *len)
+ − 307 {
+ − 308 int in_quotes = 0, quit = 0, all_in_quotes = 0;
+ − 309 const char *p = *ptr, *start;
+ − 310 char *buf = NULL;
+ − 311 unsigned length = 0;
+ − 312
+ − 313 /* Skip whitespace */
+ − 314 while (*p && isspace (*p))
+ − 315 p++;
+ − 316
+ − 317 /* If this is the end, return NULL */
+ − 318 if (!*p)
+ − 319 return NULL;
+ − 320
+ − 321 /* Remember where we are */
+ − 322 start = p;
+ − 323
+ − 324 /* Find the next whitespace character outside quotes */
+ − 325 if (*p == '"')
+ − 326 all_in_quotes = 1;
+ − 327
+ − 328 while (*p && !quit)
+ − 329 {
+ − 330 switch (*p)
+ − 331 {
+ − 332 case '"':
+ − 333 in_quotes = 1 - in_quotes;
+ − 334 p++;
+ − 335 break;
+ − 336
+ − 337 case '\\':
+ − 338 if (!in_quotes)
+ − 339 all_in_quotes = 0;
+ − 340
+ − 341 p++;
+ − 342
+ − 343 if (!*p)
+ − 344 break;
+ − 345
+ − 346 p++;
+ − 347 break;
+ − 348
+ − 349 default:
+ − 350 if (isspace (*p) && !in_quotes)
+ − 351 quit = 1;
+ − 352 else if (!in_quotes)
+ − 353 all_in_quotes = 0;
+ − 354
+ − 355 if (!quit)
+ − 356 p++;
+ − 357 }
+ − 358 }
+ − 359
+ − 360 /* Work out the length */
+ − 361 length = p - start;
+ − 362
+ − 363 /* Strip quotes if the argument is completely quoted */
+ − 364 if (all_in_quotes)
+ − 365 {
+ − 366 start++;
+ − 367 length -= 2;
+ − 368 }
+ − 369
+ − 370 /* Copy */
+ − 371 buf = (char *) xmalloc (length + 1);
+ − 372
+ − 373 if (!buf)
+ − 374 return NULL;
+ − 375
+ − 376 strncpy (buf, start, length);
+ − 377 buf[length] = '\0';
+ − 378
+ − 379 /* Return the pointer and length */
+ − 380 *ptr = p;
+ − 381 *len = length;
+ − 382
+ − 383 return buf;
+ − 384 }
+ − 385
+ − 386 /*
+ − 387 * Name : parseCommandLine
+ − 388 * Function: Process the command line. This program accepts a list of strings
+ − 389 * : (which may contain wildcards) representing filenames.
+ − 390 *
+ − 391 */
+ − 392
+ − 393 int
+ − 394 parseCommandLine (HCONV hConv, LPSTR lpszCommandLine)
+ − 395 {
+ − 396 char *fullpath, *filepart;
+ − 397 char *arg;
+ − 398 unsigned len, pathlen;
+ − 399 int ret = 0;
+ − 400 HANDLE hFindFile = NULL;
+ − 401 WIN32_FIND_DATA wfd;
+ − 402
+ − 403 /* Retrieve arguments */
+ − 404 while ((arg = getNextArg ((const char**)&lpszCommandLine, &len)) != NULL)
+ − 405 {
+ − 406 /* First find the canonical path name */
+ − 407 fullpath = filepart = NULL;
+ − 408 pathlen = GetFullPathName (arg, 0, fullpath, &filepart);
+ − 409
+ − 410 fullpath = (char *) xmalloc (pathlen);
+ − 411
+ − 412 if (!fullpath)
+ − 413 {
+ − 414 MessageBox (NULL, "Not enough memory.", "winclient",
+ − 415 MB_ICONEXCLAMATION | MB_OK);
+ − 416
+ − 417 ret = 1;
+ − 418 free (arg);
+ − 419
+ − 420 break;
+ − 421 }
+ − 422
+ − 423 GetFullPathName (arg, pathlen, fullpath, &filepart);
+ − 424
+ − 425 /* Find the first matching file */
+ − 426 hFindFile = FindFirstFile (arg, &wfd);
+ − 427
+ − 428 if (hFindFile == INVALID_HANDLE_VALUE)
+ − 429 ret = doFile (hConv, fullpath, "");
+ − 430 else
+ − 431 {
+ − 432 /* Chop off the file part from the full path name */
+ − 433 if (filepart)
+ − 434 *filepart = '\0';
+ − 435
+ − 436 /* For each matching file */
+ − 437 do
+ − 438 {
+ − 439 /* Process it */
+ − 440 ret = doFile (hConv, fullpath, wfd.cFileName);
+ − 441
+ − 442 if (ret)
+ − 443 break;
+ − 444 }
+ − 445 while (FindNextFile (hFindFile, &wfd));
+ − 446
+ − 447 FindClose (hFindFile);
+ − 448 }
+ − 449
+ − 450 /* Release the path name buffers */
+ − 451 free (fullpath);
+ − 452 free (arg);
+ − 453
+ − 454 if (ret)
+ − 455 break;
+ − 456 }
+ − 457
+ − 458 return ret;
+ − 459 }
+ − 460
+ − 461 static void
+ − 462 fatal (const char *s1, const char *s2)
+ − 463 {
+ − 464 error (s1, s2);
+ − 465 exit (1);
+ − 466 }
+ − 467
+ − 468 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
+ − 469 static void
+ − 470 error (const char* s1, const char* s2)
+ − 471 {
+ − 472 fprintf (stderr, "winclient: ");
+ − 473 fprintf (stderr, s1, s2);
+ − 474 fprintf (stderr, "\n");
+ − 475 }
+ − 476
+ − 477 /* Like malloc but get fatal error if memory is exhausted. */
+ − 478
+ − 479 static void *
+ − 480 xmalloc (size_t size)
+ − 481 {
+ − 482 void *result = malloc (size);
+ − 483 if (result == NULL)
+ − 484 fatal ("virtual memory exhausted", (char *) 0);
+ − 485 return result;
+ − 486 }