comparison lib-src/winclient.c @ 819:6504113e7c2d

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