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