Mercurial > hg > xemacs-beta
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 } |