Mercurial > hg > xemacs-beta
annotate lib-src/winclient.c @ 5926:da02ba75e50a cygwin
merge heads 3
author | Henry Thompson <ht@markup.co.uk> |
---|---|
date | Fri, 27 Feb 2015 17:48:44 +0000 |
parents | 68639fb08af8 |
children |
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 | |
5402
308d34e9f07d
Changed bulk of GPLv2 or later files identified by script
Mats Lidell <matsl@xemacs.org>
parents:
5030
diff
changeset
|
6 XEmacs is free software: you can redistribute it and/or modify it |
308d34e9f07d
Changed bulk of GPLv2 or later files identified by script
Mats Lidell <matsl@xemacs.org>
parents:
5030
diff
changeset
|
7 under the terms of the GNU General Public License as published by the |
308d34e9f07d
Changed bulk of GPLv2 or later files identified by script
Mats Lidell <matsl@xemacs.org>
parents:
5030
diff
changeset
|
8 Free Software Foundation, either version 3 of the License, or (at your |
308d34e9f07d
Changed bulk of GPLv2 or later files identified by script
Mats Lidell <matsl@xemacs.org>
parents:
5030
diff
changeset
|
9 option) any later version. |
853 | 10 |
5402
308d34e9f07d
Changed bulk of GPLv2 or later files identified by script
Mats Lidell <matsl@xemacs.org>
parents:
5030
diff
changeset
|
11 XEmacs is distributed in the hope that it will be useful, but WITHOUT |
308d34e9f07d
Changed bulk of GPLv2 or later files identified by script
Mats Lidell <matsl@xemacs.org>
parents:
5030
diff
changeset
|
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
308d34e9f07d
Changed bulk of GPLv2 or later files identified by script
Mats Lidell <matsl@xemacs.org>
parents:
5030
diff
changeset
|
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
308d34e9f07d
Changed bulk of GPLv2 or later files identified by script
Mats Lidell <matsl@xemacs.org>
parents:
5030
diff
changeset
|
14 for more details. |
853 | 15 |
5402
308d34e9f07d
Changed bulk of GPLv2 or later files identified by script
Mats Lidell <matsl@xemacs.org>
parents:
5030
diff
changeset
|
16 You should have received a copy of the GNU General Public License |
308d34e9f07d
Changed bulk of GPLv2 or later files identified by script
Mats Lidell <matsl@xemacs.org>
parents:
5030
diff
changeset
|
17 along with XEmacs. If not, see <http://www.gnu.org/licenses/>. */ |
853 | 18 |
19 /* Synched up with: Not in FSF. */ | |
20 | |
21 /* -- Includes -------------------------------------------------------------- */ | |
22 | |
23 #ifdef HAVE_CONFIG_H | |
24 # include <config.h> | |
25 #endif | |
2993 | 26 #include <windows.h> |
27 #include <ddeml.h> | |
853 | 28 #include <stdlib.h> |
29 #include <stdio.h> | |
30 #include <ctype.h> | |
31 #include <errno.h> | |
32 | |
5030 | 33 #ifdef __CYGWIN__ |
34 #include <stdlib.h> | |
35 #include <unistd.h> | |
36 #endif | |
37 | |
853 | 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 */ | |
5030 | 46 #define CONNECT_RETRIES 20 |
853 | 47 #define CONNECT_DELAY 500 /* ms */ |
48 #define TRANSACTION_TIMEOUT 5000 /* ms */ | |
49 #define MAX_INPUT_IDLE_WAIT INFINITE /* ms */ | |
50 | |
51 /* DDE Strings */ | |
52 #define SERVICE_NAME "XEmacs" | |
53 #define TOPIC_NAME "System" | |
54 #define COMMAND_FORMAT "[open(\"%s%s\")]" | |
55 | |
56 /* XEmacs program name */ | |
5030 | 57 #define GENERIC_PROGRAM EMACS_PROGNAME ".exe" |
58 #define VERSIONED_PROGRAM EMACS_PROGNAME "-" EMACS_VERSION ".exe" | |
853 | 59 |
60 /* -- Constants ------------------------------------------------------------- */ | |
61 | |
62 /* -- Global Variables ------------------------------------------------------ */ | |
63 | |
64 HINSTANCE hInstance; | |
65 DWORD idInst = 0; | |
66 | |
67 /* -- Function Declarations ------------------------------------------------- */ | |
68 | |
69 HDDEDATA CALLBACK ddeCallback (UINT uType, UINT uFmt, HCONV hconv, | |
70 HSZ hsz1, HSZ hsz2, HDDEDATA hdata, | |
71 DWORD dwData1, DWORD dwData2); | |
72 | |
73 int WINAPI WinMain (HINSTANCE hInst, | |
74 HINSTANCE hPrev, | |
75 LPSTR lpCmdLine, | |
76 int nCmdShow); | |
77 | |
78 static HCONV openConversation (void); | |
79 static void closeConversation (HCONV hConv); | |
80 static int doFile (HCONV hConv, LPSTR lpszFileName1, LPSTR lpszFileName2); | |
81 static int parseCommandLine (HCONV hConv, LPSTR lpszCommandLine); | |
82 | |
83 /* -- Function Definitions -------------------------------------------------- */ | |
84 | |
85 /* | |
86 * Name : ddeCallback | |
87 * Function: Gets called by DDEML. | |
88 * | |
89 */ | |
90 | |
91 HDDEDATA CALLBACK | |
92 ddeCallback (UINT uType, UINT uFmt, HCONV hconv, | |
93 HSZ hsz1, HSZ hsz2, HDDEDATA hdata, | |
94 DWORD dwData1, DWORD dwData2) | |
95 { | |
96 return (HDDEDATA) NULL; | |
97 } | |
98 | |
99 /* | |
100 * Name : WinMain | |
101 * Function: The program's entry point function. | |
102 * | |
103 */ | |
104 | |
105 int WINAPI | |
106 WinMain (HINSTANCE hInst, | |
107 HINSTANCE hPrev, | |
108 LPSTR lpCmdLine, | |
109 int nCmdShow) | |
110 { | |
111 HCONV hConv; | |
112 int ret = 0; | |
113 UINT uiRet; | |
5030 | 114 |
853 | 115 /* Initialise the DDEML library */ |
116 uiRet = DdeInitialize (&idInst, | |
117 (PFNCALLBACK) ddeCallback, | |
118 APPCMD_CLIENTONLY | |
119 |CBF_FAIL_ALLSVRXACTIONS, | |
120 0); | |
121 | |
122 if (uiRet != DMLERR_NO_ERROR) | |
123 { | |
124 MessageBox (NULL, "Could not initialise DDE management library.", | |
125 "winclient", MB_ICONEXCLAMATION | MB_OK); | |
126 | |
127 return 1; | |
128 } | |
129 | |
130 /* Open a conversation */ | |
131 hConv = openConversation (); | |
132 | |
133 if (hConv) | |
134 { | |
135 /* OK. Next, we need to parse the command line. */ | |
136 ret = parseCommandLine (hConv, lpCmdLine); | |
137 | |
138 /* Close the conversation */ | |
139 closeConversation (hConv); | |
140 } | |
5030 | 141 |
853 | 142 DdeUninitialize (idInst); |
143 | |
144 return ret; | |
145 } | |
146 | |
147 /* | |
148 * Name : openConversation | |
149 * Function: Start a conversation. | |
150 * | |
151 */ | |
152 | |
153 static HCONV | |
154 openConversation (void) | |
155 { | |
156 HSZ hszService = NULL, hszTopic = NULL; | |
157 HCONV hConv = NULL; | |
158 | |
159 /* Get the application (service) name */ | |
160 hszService = DdeCreateStringHandle (idInst, | |
161 SERVICE_NAME, | |
162 CP_WINANSI); | |
163 | |
164 if (!hszService) | |
165 { | |
166 MessageBox (NULL, "Could not create string handle for service.", | |
167 "winclient", MB_ICONEXCLAMATION | MB_OK); | |
168 | |
169 goto error; | |
170 } | |
5030 | 171 |
853 | 172 /* Get the topic name */ |
173 hszTopic = DdeCreateStringHandle (idInst, | |
174 TOPIC_NAME, | |
175 CP_WINANSI); | |
176 | |
177 if (!hszTopic) | |
178 { | |
179 MessageBox (NULL, "Could not create string handle for topic.", | |
180 "winclient", MB_ICONEXCLAMATION | MB_OK); | |
181 | |
182 goto error; | |
183 } | |
184 | |
185 /* Try to connect */ | |
186 hConv = DdeConnect (idInst, hszService, hszTopic, NULL); | |
187 | |
188 if (!hConv) | |
189 { | |
190 STARTUPINFO sti; | |
191 PROCESS_INFORMATION pi; | |
192 int n; | |
5030 | 193 |
853 | 194 /* Try to start the program */ |
195 ZeroMemory (&sti, sizeof (sti)); | |
196 sti.cb = sizeof (sti); | |
5030 | 197 if (!CreateProcess (NULL, GENERIC_PROGRAM, NULL, NULL, FALSE, 0, |
198 NULL, NULL, &sti, &pi) && | |
199 !CreateProcess (NULL, VERSIONED_PROGRAM, NULL, NULL, FALSE, 0, | |
853 | 200 NULL, NULL, &sti, &pi)) |
201 { | |
202 MessageBox (NULL, "Could not start process.", | |
203 "winclient", MB_ICONEXCLAMATION | MB_OK); | |
204 | |
205 goto error; | |
206 } | |
207 | |
208 /* Wait for the process to enter an idle state */ | |
209 WaitForInputIdle (pi.hProcess, MAX_INPUT_IDLE_WAIT); | |
210 | |
211 /* Close the handles */ | |
212 CloseHandle (pi.hThread); | |
213 CloseHandle (pi.hProcess); | |
5030 | 214 |
853 | 215 /* Try to connect */ |
4464
61aff09a7589
Increase DDE connection retries because waiting for XEmacs to start
Vin Shelton <acs@xemacs.org>
parents:
2993
diff
changeset
|
216 for (n = 0; n < CONNECT_RETRIES; n++) |
853 | 217 { |
218 Sleep (CONNECT_DELAY); | |
5030 | 219 |
853 | 220 hConv = DdeConnect (idInst, hszService, hszTopic, NULL); |
221 | |
222 if (hConv) | |
223 break; | |
224 } | |
225 | |
226 if (!hConv) | |
227 { | |
228 /* Still couldn't connect. */ | |
229 MessageBox (NULL, "Could not connect to DDE server.", | |
230 "winclient", MB_ICONEXCLAMATION | MB_OK); | |
231 | |
232 goto error; | |
233 } | |
234 } | |
235 | |
236 /* Release the string handles */ | |
237 DdeFreeStringHandle (idInst, hszService); | |
238 DdeFreeStringHandle (idInst, hszTopic); | |
239 | |
240 return hConv; | |
5030 | 241 |
853 | 242 error: |
243 if (hConv) | |
244 DdeDisconnect (hConv); | |
245 if (hszService) | |
246 DdeFreeStringHandle (idInst, hszService); | |
247 if (hszTopic) | |
248 DdeFreeStringHandle (idInst, hszTopic); | |
249 | |
250 return NULL; | |
251 } | |
252 | |
253 /* | |
254 * Name : closeConversation | |
255 * Function: Close a conversation. | |
256 * | |
257 */ | |
258 | |
259 static void | |
260 closeConversation (HCONV hConv) | |
261 { | |
262 /* Shut down */ | |
263 DdeDisconnect (hConv); | |
264 } | |
265 | |
266 /* | |
267 * Name : doFile | |
268 * Function: Process a file. | |
269 * | |
270 */ | |
271 | |
272 int | |
273 doFile (HCONV hConv, LPSTR lpszFileName1, LPSTR lpszFileName2) | |
274 { | |
275 char *buf = NULL; | |
276 unsigned len; | |
5030 | 277 |
853 | 278 /* Calculate the buffer length */ |
279 len = strlen (lpszFileName1) + strlen (lpszFileName2) | |
280 + strlen (COMMAND_FORMAT); | |
5030 | 281 |
853 | 282 /* Allocate a buffer */ |
283 buf = (char *) xmalloc (len); | |
284 | |
285 if (!buf) | |
286 { | |
287 MessageBox (NULL, "Not enough memory.", | |
288 "winclient", MB_ICONEXCLAMATION | MB_OK); | |
289 | |
290 return 1; | |
291 } | |
292 | |
293 /* Build the command */ | |
294 len = wsprintf (buf, COMMAND_FORMAT, lpszFileName1, lpszFileName2); | |
5030 | 295 len++; |
853 | 296 |
297 /* OK. We're connected. Send the message. */ | |
5921
68639fb08af8
no obvious 64 vs. 32 pblms in compilation, but still crashing in event-msw
Henry Thompson <ht@markup.co.uk>
parents:
5402
diff
changeset
|
298 DdeClientTransaction ((LPBYTE)buf, len, hConv, NULL, |
853 | 299 0, XTYP_EXECUTE, TRANSACTION_TIMEOUT, NULL); |
300 | |
301 free (buf); | |
5030 | 302 |
853 | 303 return 0; |
304 } | |
305 | |
306 /* | |
307 * Name : getNextArg | |
308 * Function: Retrieve the next command line argument. | |
309 * | |
310 */ | |
311 | |
312 static char * | |
313 getNextArg (const char **ptr, unsigned *len) | |
314 { | |
315 int in_quotes = 0, quit = 0, all_in_quotes = 0; | |
316 const char *p = *ptr, *start; | |
317 char *buf = NULL; | |
318 unsigned length = 0; | |
319 | |
320 /* Skip whitespace */ | |
321 while (*p && isspace (*p)) | |
322 p++; | |
323 | |
324 /* If this is the end, return NULL */ | |
325 if (!*p) | |
326 return NULL; | |
5030 | 327 |
853 | 328 /* Remember where we are */ |
329 start = p; | |
5030 | 330 |
853 | 331 /* Find the next whitespace character outside quotes */ |
332 if (*p == '"') | |
333 all_in_quotes = 1; | |
5030 | 334 |
853 | 335 while (*p && !quit) |
336 { | |
337 switch (*p) | |
338 { | |
339 case '"': | |
340 in_quotes = 1 - in_quotes; | |
341 p++; | |
342 break; | |
343 | |
344 case '\\': | |
345 if (!in_quotes) | |
346 all_in_quotes = 0; | |
5030 | 347 |
853 | 348 p++; |
349 | |
350 if (!*p) | |
351 break; | |
352 | |
353 p++; | |
354 break; | |
355 | |
356 default: | |
357 if (isspace (*p) && !in_quotes) | |
358 quit = 1; | |
359 else if (!in_quotes) | |
360 all_in_quotes = 0; | |
361 | |
362 if (!quit) | |
363 p++; | |
364 } | |
365 } | |
366 | |
367 /* Work out the length */ | |
368 length = p - start; | |
369 | |
370 /* Strip quotes if the argument is completely quoted */ | |
371 if (all_in_quotes) | |
372 { | |
373 start++; | |
374 length -= 2; | |
375 } | |
5030 | 376 |
853 | 377 /* Copy */ |
378 buf = (char *) xmalloc (length + 1); | |
379 | |
380 if (!buf) | |
381 return NULL; | |
5030 | 382 |
853 | 383 strncpy (buf, start, length); |
384 buf[length] = '\0'; | |
385 | |
386 /* Return the pointer and length */ | |
387 *ptr = p; | |
388 *len = length; | |
389 | |
390 return buf; | |
391 } | |
392 | |
393 /* | |
394 * Name : parseCommandLine | |
395 * Function: Process the command line. This program accepts a list of strings | |
396 * : (which may contain wildcards) representing filenames. | |
397 * | |
398 */ | |
399 | |
400 int | |
401 parseCommandLine (HCONV hConv, LPSTR lpszCommandLine) | |
402 { | |
403 char *fullpath, *filepart; | |
404 char *arg; | |
405 unsigned len, pathlen; | |
406 int ret = 0; | |
407 HANDLE hFindFile = NULL; | |
408 WIN32_FIND_DATA wfd; | |
409 | |
410 /* Retrieve arguments */ | |
411 while ((arg = getNextArg ((const char**)&lpszCommandLine, &len)) != NULL) | |
412 { | |
5030 | 413 fullpath = NULL; |
414 #ifdef __CYGWIN__ | |
415 /* If the filename is not an absolute path, | |
416 add the current directory to the pathname */ | |
417 if (*arg != '/') | |
418 { | |
419 len = pathconf(".", _PC_PATH_MAX); | |
420 fullpath = (char *) xmalloc (len+1); | |
421 if (!fullpath) | |
422 { | |
423 MessageBox (NULL, "Not enough memory.", "winclient", | |
424 MB_ICONEXCLAMATION | MB_OK); | |
425 ret = 1; | |
426 break; | |
427 } | |
428 if (!getcwd(fullpath, (size_t)len)) | |
429 { | |
430 MessageBox (NULL, "Could not retrieve current directory.", | |
431 "winclient", MB_ICONEXCLAMATION | MB_OK); | |
432 ret = 1; | |
433 break; | |
434 } | |
435 /* Append trailing slash */ | |
436 strcat(fullpath, "/"); | |
437 ret = doFile (hConv, fullpath, arg); | |
438 } | |
439 else | |
440 { | |
441 /* The arg has already been expanded, so pass it as it is */ | |
442 ret = doFile (hConv, "", arg); | |
443 } | |
444 #else | |
853 | 445 /* First find the canonical path name */ |
446 fullpath = filepart = NULL; | |
447 pathlen = GetFullPathName (arg, 0, fullpath, &filepart); | |
448 | |
449 fullpath = (char *) xmalloc (pathlen); | |
450 | |
451 if (!fullpath) | |
452 { | |
453 MessageBox (NULL, "Not enough memory.", "winclient", | |
454 MB_ICONEXCLAMATION | MB_OK); | |
455 ret = 1; | |
456 break; | |
457 } | |
458 | |
459 GetFullPathName (arg, pathlen, fullpath, &filepart); | |
460 | |
461 /* Find the first matching file */ | |
462 hFindFile = FindFirstFile (arg, &wfd); | |
463 | |
464 if (hFindFile == INVALID_HANDLE_VALUE) | |
465 ret = doFile (hConv, fullpath, ""); | |
466 else | |
467 { | |
468 /* Chop off the file part from the full path name */ | |
469 if (filepart) | |
470 *filepart = '\0'; | |
471 | |
472 /* For each matching file */ | |
473 do | |
474 { | |
475 /* Process it */ | |
476 ret = doFile (hConv, fullpath, wfd.cFileName); | |
477 | |
478 if (ret) | |
479 break; | |
480 } | |
481 while (FindNextFile (hFindFile, &wfd)); | |
482 | |
483 FindClose (hFindFile); | |
484 } | |
5030 | 485 #endif |
853 | 486 /* Release the path name buffers */ |
5030 | 487 if (fullpath) |
488 free (fullpath); | |
853 | 489 free (arg); |
490 | |
491 if (ret) | |
492 break; | |
493 } | |
494 | |
495 return ret; | |
496 } | |
497 | |
498 static void | |
499 fatal (const char *s1, const char *s2) | |
500 { | |
501 error (s1, s2); | |
502 exit (1); | |
503 } | |
504 | |
505 /* Print error message. `s1' is printf control string, `s2' is arg for it. */ | |
506 static void | |
507 error (const char* s1, const char* s2) | |
508 { | |
509 fprintf (stderr, "winclient: "); | |
510 fprintf (stderr, s1, s2); | |
511 fprintf (stderr, "\n"); | |
512 } | |
513 | |
514 /* Like malloc but get fatal error if memory is exhausted. */ | |
515 | |
516 static void * | |
517 xmalloc (size_t size) | |
518 { | |
519 void *result = malloc (size); | |
520 if (result == NULL) | |
521 fatal ("virtual memory exhausted", (char *) 0); | |
522 return result; | |
523 } |