442
|
1 /* Utility routines for XEmacs on Windows 9x, NT and Cygwin.
|
2367
|
2 Copyright (C) 2000, 2001, 2002, 2004 Ben Wing.
|
442
|
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 the Free
|
|
18 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
19 02111-1307, USA. */
|
|
20
|
|
21 #include <config.h>
|
|
22 #include "lisp.h"
|
|
23
|
|
24 #include "buffer.h"
|
771
|
25 #include "console-msw.h"
|
2526
|
26 #include "hash.h"
|
|
27 #include "profile.h"
|
611
|
28
|
771
|
29 #include "sysfile.h"
|
|
30 #include "sysproc.h"
|
859
|
31 #include "syssignal.h"
|
611
|
32 #include "systime.h"
|
442
|
33
|
2367
|
34
|
|
35
|
|
36 /*
|
|
37
|
|
38 Info on Windows issues:
|
|
39
|
|
40 (Info-goto-node "(internals)Interface to MS Windows")
|
|
41
|
|
42 ------- @file{src/config.h}.in vs. @file{nt/xemacs.mak} -------
|
|
43
|
|
44 See @file{src/config.h.in} more more info.
|
|
45 */
|
|
46
|
771
|
47 /* Control conversion of upper case file names to lower case.
|
|
48 nil means no, t means yes. */
|
|
49 Lisp_Object Vmswindows_downcase_file_names;
|
|
50
|
2526
|
51 struct hash_table *mswindows_read_link_hash;
|
|
52
|
771
|
53 int mswindows_windows9x_p;
|
2526
|
54 Boolint mswindows_shortcuts_are_symlinks;
|
771
|
55
|
442
|
56 pfSwitchToThread_t xSwitchToThread;
|
|
57
|
771
|
58 pfNetUserEnum_t xNetUserEnum;
|
|
59 pfNetApiBufferFree_t xNetApiBufferFree;
|
|
60
|
|
61 /* Convert a filename in standard Win32 format into our internal format
|
|
62 (which may be significantly different if we're running on Cygwin), and
|
|
63 turn it into a file: URL. Return a newly malloc()ed string.
|
442
|
64
|
771
|
65 #### This comes from code that just prepended `file:', which is not
|
|
66 good. See comment in mswindows_dde_callback(), case XTYP_EXECUTE.
|
|
67 */
|
867
|
68 Ibyte *
|
|
69 urlify_filename (Ibyte *filename)
|
771
|
70 {
|
867
|
71 Ibyte *pseudo_url;
|
771
|
72
|
|
73 WIN32_TO_LOCAL_FILE_FORMAT (filename, filename);
|
867
|
74 pseudo_url = xnew_array (Ibyte, 5 + qxestrlen (filename) + 1);
|
2367
|
75 qxestrcpy_ascii (pseudo_url, "file:");
|
771
|
76 qxestrcat (pseudo_url, filename);
|
|
77 /* URL's only have /, no backslash */
|
|
78 for (filename = pseudo_url; *filename; filename++)
|
|
79 {
|
|
80 if (*filename == '\\')
|
|
81 *filename = '/';
|
|
82 }
|
442
|
83
|
771
|
84 return pseudo_url;
|
|
85 }
|
531
|
86
|
826
|
87 /* Convert a Win32 file name in tstr format into a local-format file name
|
|
88 in internal format. */
|
|
89
|
442
|
90 Lisp_Object
|
826
|
91 tstr_to_local_file_format (Extbyte *path)
|
442
|
92 {
|
867
|
93 Ibyte *ttlff;
|
771
|
94
|
826
|
95 TSTR_TO_C_STRING (path, ttlff);
|
771
|
96 WIN32_TO_LOCAL_FILE_FORMAT (ttlff, ttlff);
|
|
97
|
|
98 return build_intstring (ttlff);
|
|
99 }
|
|
100
|
|
101 /* Normalize filename by converting all path separators to the specified
|
|
102 separator. Also conditionally convert all-upper-case path name
|
|
103 components to lower case. Return a newly malloc()ed string.
|
|
104 */
|
|
105
|
867
|
106 Ibyte *
|
|
107 mswindows_canonicalize_filename (Ibyte *name)
|
771
|
108 {
|
867
|
109 Ibyte *fp = name;
|
771
|
110 DECLARE_EISTRING (newname);
|
|
111 DECLARE_EISTRING (component);
|
|
112 int do_casefrob = 1;
|
442
|
113
|
771
|
114 /* Always lower-case drive letters a-z, even if the filesystem
|
|
115 preserves case in filenames.
|
|
116 This is so filenames can be compared by string comparison
|
|
117 functions that are case-sensitive. Even case-preserving filesystems
|
|
118 do not distinguish case in drive letters. */
|
|
119 if (name[0] >= 'A' && name[0] <= 'Z' && name[1] == ':')
|
|
120 {
|
|
121 eicat_ch (newname, name[0] + 'a' - 'A');
|
|
122 eicat_ch (newname, ':');
|
|
123 fp += 2;
|
|
124 }
|
|
125
|
|
126 while (1)
|
|
127 {
|
867
|
128 Ichar ch = itext_ichar (fp);
|
771
|
129 if (LOWERCASEP (0, ch))
|
|
130 do_casefrob = 0; /* don't convert this element */
|
442
|
131
|
771
|
132 if (ch == 0 || IS_ANY_SEP (ch))
|
|
133 {
|
|
134 if (do_casefrob && !NILP (Vmswindows_downcase_file_names))
|
|
135 eilwr (component);
|
|
136 do_casefrob = 1;
|
|
137 eicat_ei (newname, component);
|
|
138 eireset (component);
|
|
139 if (IS_DIRECTORY_SEP (ch))
|
|
140 eicat_ch (newname, DIRECTORY_SEP);
|
|
141 else if (ch)
|
|
142 eicat_ch (newname, ch);
|
|
143 else
|
|
144 break;
|
|
145 }
|
|
146 else
|
|
147 eicat_ch (component, ch);
|
|
148
|
867
|
149 INC_IBYTEPTR (fp);
|
771
|
150 }
|
|
151
|
|
152 return eicpyout_malloc (newname, 0);
|
442
|
153 }
|
|
154
|
814
|
155 Extbyte *
|
|
156 mswindows_get_module_file_name (void)
|
|
157 {
|
|
158 Extbyte *path = NULL;
|
|
159 int bufsize = 4096;
|
|
160 int cchpathsize;
|
|
161
|
|
162 while (1)
|
|
163 {
|
|
164 path = (Extbyte *) xrealloc (path, bufsize * XETCHAR_SIZE);
|
|
165 cchpathsize = qxeGetModuleFileName (NULL, path, bufsize);
|
|
166 if (!cchpathsize)
|
|
167 return 0;
|
|
168 if (cchpathsize + 1 <= bufsize)
|
|
169 break;
|
|
170 bufsize *= 2;
|
|
171 }
|
|
172
|
|
173 return path;
|
|
174 }
|
|
175
|
442
|
176 static void
|
|
177 init_potentially_nonexistent_functions (void)
|
|
178 {
|
771
|
179 HMODULE h_kernel = qxeGetModuleHandle (XETEXT ("kernel32"));
|
531
|
180 /* the following does not seem to get mapped in automatically */
|
771
|
181 HMODULE h_netapi = qxeLoadLibrary (XETEXT ("netapi32.dll"));
|
442
|
182
|
|
183 if (h_kernel)
|
|
184 {
|
|
185 xSwitchToThread =
|
|
186 (pfSwitchToThread_t) GetProcAddress (h_kernel, "SwitchToThread");
|
|
187 }
|
|
188
|
531
|
189 if (h_netapi)
|
|
190 {
|
|
191 xNetUserEnum =
|
|
192 (pfNetUserEnum_t) GetProcAddress (h_netapi, "NetUserEnum");
|
|
193 xNetApiBufferFree =
|
|
194 (pfNetApiBufferFree_t) GetProcAddress (h_netapi, "NetApiBufferFree");
|
|
195 }
|
442
|
196 }
|
|
197
|
771
|
198 static Lisp_Object
|
|
199 mswindows_lisp_error_1 (int errnum, int no_recurse)
|
|
200 {
|
|
201 LPTSTR lpMsgBuf;
|
|
202 Lisp_Object result;
|
867
|
203 Ibyte *inres;
|
771
|
204 Bytecount len;
|
|
205 int i;
|
|
206
|
|
207 /* The docs for FormatMessage say:
|
|
208
|
|
209 If you pass a specific LANGID in this parameter, FormatMessage
|
|
210 will return a message for that LANGID only. If the function
|
|
211 cannot find a message for that LANGID, it returns
|
|
212 ERROR_RESOURCE_LANG_NOT_FOUND. If you pass in zero, FormatMessage
|
|
213 looks for a message for LANGIDs in the following order:
|
|
214
|
|
215 Language neutral
|
|
216 Thread LANGID, based on the thread's locale value
|
|
217 User default LANGID, based on the user's default locale value
|
|
218 System default LANGID, based on the system default locale value
|
|
219 US English
|
|
220
|
|
221 If FormatMessage doesn't find a message for any of the preceding
|
|
222 LANGIDs, it returns any language message string that is present. If
|
|
223 that fails, it returns ERROR_RESOURCE_LANG_NOT_FOUND. (Note, this is
|
|
224 returned through GetLastError(), not the return value.)
|
|
225
|
|
226 #### what the hell is "language neutral"? i can find no info on this.
|
|
227 so let's do our own language first.
|
|
228 */
|
|
229
|
|
230 for (i = 0; ; i++)
|
|
231 {
|
|
232 int lang = 0;
|
|
233 int retval;
|
|
234
|
|
235 switch (i)
|
|
236 {
|
|
237 #ifdef MULE
|
|
238 /* Urk! Windows 95 doesn't let you set the thread locale!
|
|
239 so we have to maintain our own. */
|
|
240 case 0: lang = LANGIDFROMLCID (mswindows_current_locale ()); break;
|
|
241 case 1: lang = 0; break;
|
|
242 #else
|
|
243 case 0: lang = 0; break;
|
|
244 #endif
|
2500
|
245 default: ABORT ();
|
771
|
246 }
|
|
247
|
|
248 retval = qxeFormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER
|
|
249 | FORMAT_MESSAGE_FROM_SYSTEM,
|
|
250 NULL, errnum, lang,
|
|
251 /* yeah, i'm casting a char ** to a char *.
|
|
252 ya gotta problem widdat? */
|
|
253 (Extbyte *) &lpMsgBuf, 0, NULL);
|
|
254
|
|
255 if (!retval)
|
|
256 {
|
|
257 if (lang != 0)
|
|
258 continue;
|
|
259
|
|
260 if (no_recurse)
|
|
261 return emacs_sprintf_string
|
|
262 ("Unknown error code %d (error return %ld from FormatMessage())",
|
|
263 errnum, GetLastError ());
|
|
264 else
|
|
265 return emacs_sprintf_string
|
|
266 ("Unknown error code %d (error return %s from FormatMessage())",
|
|
267 /* It's OK, emacs_sprintf_string disables GC explicitly */
|
|
268 errnum, XSTRING_DATA (mswindows_lisp_error_1 (errnum, 1)));
|
|
269 }
|
|
270 else
|
|
271 break;
|
|
272 }
|
|
273
|
|
274 TSTR_TO_C_STRING (lpMsgBuf, inres);
|
|
275 len = qxestrlen (inres);
|
|
276 /* Messages tend to end with a period and newline */
|
2367
|
277 if (len >= 3 && !qxestrcmp_ascii (inres + len - 3, ".\r\n"))
|
771
|
278 len -= 3;
|
|
279 result = make_string (inres, len);
|
|
280
|
|
281 LocalFree (lpMsgBuf);
|
|
282 return result;
|
|
283 }
|
|
284
|
|
285 Lisp_Object
|
|
286 mswindows_lisp_error (int errnum)
|
|
287 {
|
|
288 return mswindows_lisp_error_1 (errnum, 0);
|
|
289 }
|
|
290
|
|
291 void
|
|
292 mswindows_output_last_error (char *frob)
|
|
293 {
|
|
294 int errval = GetLastError ();
|
|
295 Lisp_Object errmess = mswindows_lisp_error (errval);
|
|
296
|
|
297 stderr_out ("last error during %s is %d: %s\n",
|
|
298 frob, errval, XSTRING_DATA (errmess));
|
|
299 }
|
|
300
|
|
301 DOESNT_RETURN
|
|
302 mswindows_report_process_error (const char *string, Lisp_Object data,
|
|
303 int errnum)
|
|
304 {
|
|
305 report_file_type_error (Qprocess_error, mswindows_lisp_error (errnum),
|
|
306 string, data);
|
|
307 }
|
|
308
|
442
|
309 DEFUN ("mswindows-shell-execute", Fmswindows_shell_execute, 2, 4, 0, /*
|
|
310 Get Windows to perform OPERATION on DOCUMENT.
|
|
311 This is a wrapper around the ShellExecute system function, which
|
|
312 invokes the application registered to handle OPERATION for DOCUMENT.
|
|
313 OPERATION is typically \"open\", \"print\" or \"explore\" (but can be
|
|
314 nil for the default action), and DOCUMENT is typically the name of a
|
|
315 document file or URL, but can also be a program executable to run or
|
|
316 a directory to open in the Windows Explorer.
|
|
317
|
|
318 If DOCUMENT is a program executable, PARAMETERS can be a string
|
|
319 containing command line parameters, but otherwise should be nil.
|
|
320
|
|
321 SHOW-FLAG can be used to control whether the invoked application is hidden
|
|
322 or minimized. If SHOW-FLAG is nil, the application is displayed normally,
|
|
323 otherwise it is an integer representing a ShowWindow flag:
|
|
324
|
|
325 0 - start hidden
|
|
326 1 - start normally
|
|
327 3 - start maximized
|
|
328 6 - start minimized
|
|
329 */
|
|
330 (operation, document, parameters, show_flag))
|
|
331 {
|
|
332 /* Encode filename and current directory. */
|
|
333 Lisp_Object current_dir = Ffile_name_directory (document);
|
|
334 int ret;
|
|
335
|
|
336 CHECK_STRING (document);
|
|
337
|
|
338 if (NILP (current_dir))
|
|
339 current_dir = current_buffer->directory;
|
|
340
|
771
|
341 {
|
|
342 Extbyte *opext = NULL;
|
|
343 Extbyte *parmext = NULL;
|
|
344 Extbyte *path = NULL;
|
|
345 Extbyte *doc = NULL;
|
442
|
346
|
771
|
347 if (STRINGP (operation))
|
|
348 LISP_STRING_TO_TSTR (operation, opext);
|
2526
|
349 /* #### What about path names, which may be links? */
|
771
|
350 if (STRINGP (parameters))
|
|
351 LISP_STRING_TO_TSTR (parameters, parmext);
|
|
352 if (STRINGP (current_dir))
|
|
353 LOCAL_FILE_FORMAT_TO_TSTR (current_dir, path);
|
826
|
354 if (STRINGP (document))
|
|
355 LOCAL_FILE_FORMAT_MAYBE_URL_TO_TSTR (document, doc);
|
442
|
356
|
771
|
357 ret = (int) qxeShellExecute (NULL, opext, doc, parmext, path,
|
|
358 (INTP (show_flag) ?
|
|
359 XINT (show_flag) : SW_SHOWDEFAULT));
|
|
360 }
|
442
|
361
|
771
|
362 if (ret <= 32)
|
|
363 {
|
|
364 /* Convert to more standard errors */
|
|
365 #define FROB(a, b) if (ret == a) ret = b
|
|
366 FROB (SE_ERR_ACCESSDENIED, ERROR_ACCESS_DENIED);
|
|
367 FROB (SE_ERR_ASSOCINCOMPLETE, ERROR_NO_ASSOCIATION);
|
|
368 FROB (SE_ERR_DDEBUSY, ERROR_DDE_FAIL);
|
|
369 FROB (SE_ERR_DDEFAIL, ERROR_DDE_FAIL);
|
|
370 FROB (SE_ERR_DDETIMEOUT, ERROR_DDE_FAIL);
|
|
371 FROB (SE_ERR_DLLNOTFOUND, ERROR_DLL_NOT_FOUND);
|
|
372 FROB (SE_ERR_FNF, ERROR_FILE_NOT_FOUND);
|
|
373 FROB (SE_ERR_NOASSOC, ERROR_NO_ASSOCIATION);
|
|
374 FROB (SE_ERR_OOM, ERROR_NOT_ENOUGH_MEMORY);
|
|
375 FROB (SE_ERR_PNF, ERROR_PATH_NOT_FOUND);
|
|
376 FROB (SE_ERR_SHARE, ERROR_SHARING_VIOLATION);
|
|
377 #undef FROB
|
|
378
|
|
379 mswindows_report_process_error ("Running ShellExecute",
|
|
380 ret == ERROR_PATH_NOT_FOUND ?
|
|
381 list4 (Qunbound, operation, document,
|
|
382 current_dir) :
|
|
383 list3 (Qunbound, operation, document),
|
|
384 ret);
|
|
385 }
|
442
|
386
|
771
|
387 return Qt;
|
442
|
388 }
|
|
389
|
673
|
390 #ifdef CYGWIN
|
|
391 DEFUN ("mswindows-cygwin-to-win32-path", Fmswindows_cygwin_to_win32_path, 1, 1, 0, /*
|
|
392 Get the cygwin environment to convert the Unix PATH to win32 format.
|
|
393 No expansion is performed, all conversion is done by the cygwin runtime.
|
|
394 */
|
|
395 (path))
|
|
396 {
|
867
|
397 Ibyte *p;
|
673
|
398 CHECK_STRING (path);
|
|
399
|
|
400 /* There appears to be a bug in the cygwin conversion routines in
|
|
401 that they are not idempotent. */
|
|
402 p = XSTRING_DATA (path);
|
|
403 if (isalpha (p[0]) && (IS_DEVICE_SEP (p[1])))
|
|
404 return path;
|
|
405
|
|
406 /* Use mule and cygwin-safe APIs top get at file data. */
|
771
|
407 LOCAL_TO_WIN32_FILE_FORMAT (p, p);
|
|
408 return build_intstring (p);
|
673
|
409 }
|
|
410 #endif
|
|
411
|
2526
|
412 struct read_link_hash
|
|
413 {
|
|
414 Ibyte *resolved;
|
|
415 DWORD ticks;
|
|
416 };
|
|
417
|
|
418 static Ibyte *
|
|
419 mswindows_read_link_1 (const Ibyte *fname)
|
|
420 {
|
|
421 #ifdef NO_CYGWIN_COM_SUPPORT
|
|
422 return NULL;
|
|
423 #else
|
|
424 Ibyte *retval = NULL;
|
|
425 Extbyte *fnameext;
|
|
426 HANDLE fh;
|
|
427 struct read_link_hash *rlh;
|
|
428 DWORD ticks;
|
|
429
|
|
430 /* The call below to resolve a link is rather time-consuming.
|
|
431 I tried implementing a simple cache based on creation and write time
|
|
432 of the file, but that didn't help enough -- maybe 30% faster but still
|
|
433 a lot of time spent here. So just do something cheesy and don't
|
|
434 check again if we've recently (< a second) done so. */
|
|
435
|
|
436 if (!mswindows_read_link_hash)
|
|
437 mswindows_read_link_hash = make_string_hash_table (1000);
|
|
438 C_STRING_TO_TSTR (fname, fnameext);
|
|
439
|
|
440 /* See if we can find a cached value. */
|
|
441
|
|
442 /* The intermediate cast fools gcc into not outputting strict-aliasing
|
|
443 complaints */
|
|
444 ticks = GetTickCount ();
|
|
445 if (!gethash (fname, mswindows_read_link_hash,
|
|
446 (const void **) (void *) &rlh))
|
|
447 {
|
|
448 rlh = xnew_and_zero (struct read_link_hash);
|
|
449 puthash (qxestrdup (fname), rlh, mswindows_read_link_hash);
|
|
450 }
|
|
451 else if (ticks - rlh->ticks < 1000)
|
|
452 {
|
|
453 return rlh->resolved ? qxestrdup (rlh->resolved) : NULL;
|
|
454 }
|
|
455
|
|
456 rlh->ticks = ticks;
|
|
457
|
|
458 /* Retrieve creation/write time of link file. */
|
|
459
|
|
460 /* No access rights required to get info. */
|
|
461 if ((fh = qxeCreateFile (fnameext, 0, 0, NULL, OPEN_EXISTING, 0, NULL))
|
|
462 == INVALID_HANDLE_VALUE)
|
|
463 {
|
|
464 CloseHandle (fh);
|
|
465 return NULL;
|
|
466 }
|
|
467
|
|
468 CloseHandle (fh);
|
|
469
|
|
470 /* ####
|
|
471
|
|
472 Note the following in the docs:
|
|
473
|
|
474 Note: The IShellLink interface has an ANSI version
|
|
475 (IShellLinkA) and a Unicode version (IShellLinkW). The
|
|
476 version that will be used depends on whether you compile
|
|
477 for ANSI or Unicode. However, Microsoft® Windows 95 and
|
|
478 Microsoft® Windows 98 only support IShellLinkA.
|
|
479
|
|
480 We haven't yet implemented COM support in the
|
|
481 Unicode-splitting library. I don't quite understand how
|
|
482 COM works yet, but it looks like what's happening is
|
|
483 that the ShellLink class implements both the IShellLinkA
|
|
484 and IShellLinkW interfaces. To make this work at
|
|
485 run-time, we have to do something like this:
|
|
486
|
|
487 -- define a new interface qxeIShellLink that uses
|
|
488 Extbyte * instead of LPSTR or LPWSTR. (not totally
|
|
489 necessary since Extbyte * == LPSTR).
|
|
490
|
|
491 -- define a new class qxeShellLink that implements
|
|
492 qxeIShellLink. the methods on this class need to create
|
|
493 a shadow ShellLink object to do all the real work, and
|
|
494 call the corresponding function from either the
|
|
495 IShellLinkA or IShellLinkW interfaces on this object,
|
|
496 depending on whether XEUNICODE_P is defined.
|
|
497
|
|
498 -- with appropriate preprocessor magic, of course, we
|
|
499 could make things appear transparent; but we've decided
|
|
500 not to do preprocessor magic for the moment.
|
|
501 */
|
|
502
|
|
503 /* #### Not Unicode-split for the moment; we have to do it
|
|
504 ourselves. */
|
|
505 if (XEUNICODE_P)
|
|
506 {
|
|
507 IShellLinkW *psl;
|
|
508
|
|
509 if (CoCreateInstance (
|
|
510 XECOMID (CLSID_ShellLink),
|
|
511 NULL,
|
|
512 CLSCTX_INPROC_SERVER,
|
|
513 XECOMID (IID_IShellLinkW),
|
|
514 &VOIDP_CAST (psl)) == S_OK)
|
|
515 {
|
|
516 IPersistFile *ppf;
|
|
517
|
|
518 if (XECOMCALL2 (psl, QueryInterface,
|
|
519 XECOMID (IID_IPersistFile),
|
|
520 &VOIDP_CAST (ppf)) == S_OK)
|
|
521 {
|
|
522 Extbyte *fname_unicode;
|
|
523 WIN32_FIND_DATAW wfd;
|
|
524 LPWSTR resolved = alloca_array (WCHAR, PATH_MAX_EXTERNAL + 1);
|
|
525
|
|
526 /* Always Unicode. Not obvious from the
|
|
527 IPersistFile documentation, but look under
|
|
528 "Shell Link" for example code. */
|
|
529 fname_unicode = fnameext;
|
|
530
|
|
531 if (XECOMCALL2 (ppf, Load,
|
|
532 (LPWSTR) fname_unicode,
|
|
533 STGM_READ) == S_OK &&
|
|
534 /* #### YUCK! Docs read
|
|
535
|
|
536 cchMaxPath
|
|
537
|
|
538 Maximum number of bytes to copy to the buffer pointed
|
|
539 to by the pszFile parameter.
|
|
540
|
|
541 But "cch" means "count of characters", not bytes.
|
|
542 I'll assume the doc writers messed up and the
|
|
543 programmer was correct. Also, this approach is safe
|
|
544 even if it's actually the other way around. */
|
|
545 #if defined (CYGWIN_HEADERS) && W32API_INSTALLED_VER < W32API_VER(2,2)
|
|
546 /* Another Cygwin prototype error,
|
|
547 fixed in v2.2 of w32api */
|
|
548 XECOMCALL4 (psl, GetPath, (LPSTR) resolved,
|
|
549 PATH_MAX_EXTERNAL, &wfd, 0)
|
|
550 #else
|
|
551 XECOMCALL4 (psl, GetPath, resolved,
|
|
552 PATH_MAX_EXTERNAL, &wfd, 0)
|
|
553 #endif
|
|
554 == S_OK)
|
|
555 TSTR_TO_C_STRING_MALLOC (resolved, retval);
|
|
556
|
|
557 XECOMCALL0 (ppf, Release);
|
|
558 }
|
|
559
|
|
560 XECOMCALL0 (psl, Release);
|
|
561 }
|
|
562 }
|
|
563 else
|
|
564 {
|
|
565 IShellLinkA *psl;
|
|
566
|
|
567 if (CoCreateInstance (
|
|
568 XECOMID (CLSID_ShellLink),
|
|
569 NULL,
|
|
570 CLSCTX_INPROC_SERVER,
|
|
571 XECOMID (IID_IShellLinkA),
|
|
572 &VOIDP_CAST (psl)) == S_OK)
|
|
573 {
|
|
574 IPersistFile *ppf;
|
|
575
|
|
576 if (XECOMCALL2 (psl, QueryInterface,
|
|
577 XECOMID (IID_IPersistFile),
|
|
578 &VOIDP_CAST (ppf)) == S_OK)
|
|
579 {
|
|
580 Extbyte *fname_unicode;
|
|
581 WIN32_FIND_DATAA wfd;
|
|
582 LPSTR resolved = alloca_array (CHAR, PATH_MAX_EXTERNAL + 1);
|
|
583
|
|
584 /* Always Unicode. Not obvious from the
|
|
585 IPersistFile documentation, but look under
|
|
586 "Shell Link" for example code. */
|
|
587 C_STRING_TO_EXTERNAL (fname, fname_unicode,
|
|
588 Qmswindows_unicode);
|
|
589
|
|
590 if (XECOMCALL2 (ppf, Load,
|
|
591 (LPWSTR) fname_unicode,
|
|
592 STGM_READ) == S_OK
|
|
593 && XECOMCALL4 (psl, GetPath, resolved,
|
|
594 PATH_MAX_EXTERNAL, &wfd, 0) == S_OK)
|
|
595 TSTR_TO_C_STRING_MALLOC (resolved, retval);
|
|
596
|
|
597 XECOMCALL0 (ppf, Release);
|
|
598 }
|
|
599
|
|
600 XECOMCALL0 (psl, Release);
|
|
601 }
|
|
602 }
|
|
603
|
|
604 /* Cache newly found value */
|
|
605 if (rlh->resolved)
|
|
606 xfree (rlh->resolved, Ibyte *);
|
|
607 rlh->resolved = retval ? qxestrdup (retval) : NULL;
|
|
608
|
|
609 return retval;
|
|
610 #endif /* NO_CYGWIN_COM_SUPPORT */
|
|
611 }
|
|
612
|
|
613 /* Resolve a file that may be a shortcut. Accepts either a file ending
|
|
614 with .LNK or without the ending. If a shortcut is found, returns
|
|
615 a value that you must xfree(); otherwise NULL. */
|
|
616
|
|
617 Ibyte *
|
|
618 mswindows_read_link (const Ibyte *fname)
|
|
619 {
|
|
620 int len = qxestrlen (fname);
|
|
621 if (len > 4 && !qxestrcasecmp_ascii (fname + len - 4, ".LNK"))
|
|
622 return mswindows_read_link_1 (fname);
|
|
623 else
|
|
624 {
|
|
625 DECLARE_EISTRING (name2);
|
|
626
|
|
627 eicpy_rawz (name2, fname);
|
|
628 eicat_ascii (name2, ".LNK");
|
|
629 return mswindows_read_link_1 (eidata (name2));
|
|
630 }
|
|
631 }
|
|
632
|
|
633
|
613
|
634 #if defined (WIN32_NATIVE) || defined (CYGWIN_BROKEN_SIGNALS)
|
|
635
|
|
636 /* setitimer() does not exist on native MS Windows, and appears broken
|
|
637 on Cygwin (random lockups when BROKEN_SIGIO is defined), so we
|
|
638 emulate in both cases by using multimedia timers. Furthermore,
|
|
639 the lockups still occur on Cygwin even when we do nothing but
|
|
640 use the standard signalling mechanism -- so we have to emulate
|
|
641 that, too. (But only for timeouts -- we have to use the standard
|
|
642 mechanism for SIGCHLD. Yuck.)
|
|
643 */
|
|
644
|
|
645 /*--------------------------------------------------------------------*/
|
|
646 /* Signal support */
|
|
647 /*--------------------------------------------------------------------*/
|
|
648
|
|
649 #define sigmask(nsig) (1U << nsig)
|
|
650
|
|
651 /* We can support as many signals as fit into word */
|
|
652 #define SIG_MAX 32
|
|
653
|
|
654 /* Signal handlers. Initial value = 0 = SIG_DFL */
|
|
655 static mswindows_sighandler signal_handlers[SIG_MAX] = {0};
|
|
656
|
|
657 /* Signal block mask: bit set to 1 means blocked */
|
|
658 unsigned signal_block_mask = 0;
|
|
659
|
|
660 /* Signal pending mask: bit set to 1 means sig is pending */
|
|
661 unsigned signal_pending_mask = 0;
|
|
662
|
|
663 mswindows_sighandler
|
|
664 mswindows_sigset (int nsig, mswindows_sighandler handler)
|
|
665 {
|
|
666 /* We delegate some signals to the system function */
|
|
667 if (nsig == SIGFPE || nsig == SIGABRT || nsig == SIGINT)
|
|
668 return signal (nsig, handler);
|
|
669
|
|
670 if (nsig < 0 || nsig > SIG_MAX)
|
|
671 {
|
|
672 errno = EINVAL;
|
|
673 return NULL;
|
|
674 }
|
|
675
|
|
676 /* Store handler ptr */
|
|
677 {
|
|
678 mswindows_sighandler old_handler = signal_handlers[nsig];
|
|
679 signal_handlers[nsig] = handler;
|
|
680 return old_handler;
|
|
681 }
|
|
682 }
|
|
683
|
|
684 int
|
|
685 mswindows_sighold (int nsig)
|
|
686 {
|
|
687 if (nsig < 0 || nsig > SIG_MAX)
|
|
688 return errno = EINVAL;
|
|
689
|
|
690 signal_block_mask |= sigmask (nsig);
|
|
691 return 0;
|
|
692 }
|
|
693
|
|
694 int
|
|
695 mswindows_sigrelse (int nsig)
|
|
696 {
|
|
697 if (nsig < 0 || nsig > SIG_MAX)
|
|
698 return errno = EINVAL;
|
|
699
|
|
700 signal_block_mask &= ~sigmask (nsig);
|
|
701
|
|
702 if (signal_pending_mask & sigmask (nsig))
|
|
703 mswindows_raise (nsig);
|
|
704
|
|
705 return 0;
|
|
706 }
|
|
707
|
|
708 int
|
2286
|
709 mswindows_sigpause (int UNUSED (nsig))
|
613
|
710 {
|
|
711 /* This is currently not called, because the only call to sigpause
|
|
712 inside XEmacs is with SIGCHLD parameter. Just in case, we put an
|
2286
|
713 assert here, so anyone who adds a call to sigpause will be surprised
|
613
|
714 (or surprise someone else...) */
|
|
715 assert (0);
|
|
716 return 0;
|
|
717 }
|
|
718
|
|
719 int
|
|
720 mswindows_raise (int nsig)
|
|
721 {
|
|
722 /* We delegate some raises to the system routine */
|
|
723 if (nsig == SIGFPE || nsig == SIGABRT || nsig == SIGINT)
|
|
724 return raise (nsig);
|
|
725
|
|
726 if (nsig < 0 || nsig > SIG_MAX)
|
|
727 return errno = EINVAL;
|
|
728
|
|
729 /* If the signal is blocked, remember to issue later */
|
|
730 if (signal_block_mask & sigmask (nsig))
|
|
731 {
|
|
732 signal_pending_mask |= sigmask (nsig);
|
|
733 return 0;
|
|
734 }
|
|
735
|
|
736 if (signal_handlers[nsig] == SIG_IGN)
|
|
737 return 0;
|
|
738
|
|
739 if (signal_handlers[nsig] != SIG_DFL)
|
|
740 {
|
|
741 (*signal_handlers[nsig]) (nsig);
|
|
742 return 0;
|
|
743 }
|
|
744
|
|
745 /* Default signal actions */
|
|
746 if (nsig == SIGALRM || nsig == SIGPROF)
|
|
747 exit (3);
|
|
748
|
|
749 /* Other signals are ignored by default */
|
|
750 return 0;
|
|
751 }
|
|
752
|
611
|
753
|
|
754 /*--------------------------------------------------------------------*/
|
|
755 /* Async timers */
|
|
756 /*--------------------------------------------------------------------*/
|
|
757
|
|
758 /* We emulate two timers, one for SIGALRM, another for SIGPROF.
|
|
759
|
|
760 itimerproc() function has an implementation limitation: it does
|
|
761 not allow to set *both* interval and period. If an attempt is
|
|
762 made to set both, and then they are unequal, the function
|
|
763 asserts.
|
|
764
|
|
765 Minimum timer resolution on Win32 systems varies, and is greater
|
|
766 than or equal than 1 ms. The resolution is always wrapped not to
|
|
767 attempt to get below the system defined limit.
|
|
768 */
|
|
769
|
|
770 /* Timer precision, denominator of one fraction: for 100 ms
|
|
771 interval, request 10 ms precision
|
|
772 */
|
|
773 const int setitimer_helper_timer_prec = 10;
|
|
774
|
|
775 /* Last itimervals, as set by calls to setitimer */
|
|
776 static struct itimerval it_alarm;
|
|
777 static struct itimerval it_prof;
|
|
778
|
|
779 /* Timer IDs as returned by MM */
|
|
780 MMRESULT tid_alarm = 0;
|
|
781 MMRESULT tid_prof = 0;
|
|
782
|
|
783 static void CALLBACK
|
2286
|
784 setitimer_helper_proc (UINT UNUSED (uID), UINT UNUSED (uMsg), DWORD dwUser,
|
|
785 DWORD UNUSED (dw1), DWORD UNUSED (dw2))
|
611
|
786 {
|
|
787 /* Just raise the signal indicated by the dwUser parameter */
|
|
788 mswindows_raise (dwUser);
|
|
789 }
|
|
790
|
|
791 /* Divide time in ms specified by IT by DENOM. Return 1 ms
|
|
792 if division results in zero */
|
|
793 static UINT
|
853
|
794 setitimer_helper_period (const struct itimerval *it, UINT denom)
|
611
|
795 {
|
|
796 static TIMECAPS time_caps;
|
|
797
|
|
798 UINT res;
|
853
|
799 const struct timeval *tv =
|
611
|
800 (it->it_value.tv_sec == 0 && it->it_value.tv_usec == 0)
|
|
801 ? &it->it_interval : &it->it_value;
|
|
802
|
|
803 /* Zero means stop timer */
|
|
804 if (tv->tv_sec == 0 && tv->tv_usec == 0)
|
|
805 return 0;
|
|
806
|
|
807 /* Convert to ms and divide by denom */
|
|
808 res = (tv->tv_sec * 1000 + (tv->tv_usec + 500) / 1000) / denom;
|
|
809
|
|
810 /* Converge to minimum timer resolution */
|
|
811 if (time_caps.wPeriodMin == 0)
|
|
812 timeGetDevCaps (&time_caps, sizeof(time_caps));
|
|
813
|
|
814 if (res < time_caps.wPeriodMin)
|
|
815 res = time_caps.wPeriodMin;
|
|
816
|
|
817 return res;
|
|
818 }
|
|
819
|
|
820 static int
|
853
|
821 setitimer_helper (const struct itimerval *itnew,
|
|
822 struct itimerval *itold, struct itimerval *itcurrent,
|
|
823 MMRESULT *tid, DWORD sigkind)
|
611
|
824 {
|
|
825 UINT delay, resolution, event_type;
|
|
826
|
|
827 /* First stop the old timer */
|
|
828 if (*tid)
|
|
829 {
|
|
830 timeKillEvent (*tid);
|
|
831 timeEndPeriod (setitimer_helper_period (itcurrent,
|
|
832 setitimer_helper_timer_prec));
|
|
833 *tid = 0;
|
|
834 }
|
|
835
|
|
836 /* Return old itimerval if requested */
|
|
837 if (itold)
|
|
838 *itold = *itcurrent;
|
|
839
|
|
840 *itcurrent = *itnew;
|
|
841
|
|
842 /* Determine if to start new timer */
|
|
843 delay = setitimer_helper_period (itnew, 1);
|
|
844 if (delay)
|
|
845 {
|
|
846 resolution = setitimer_helper_period (itnew,
|
|
847 setitimer_helper_timer_prec);
|
|
848 event_type = (itnew->it_value.tv_sec == 0 &&
|
|
849 itnew->it_value.tv_usec == 0)
|
|
850 ? TIME_ONESHOT : TIME_PERIODIC;
|
|
851 timeBeginPeriod (resolution);
|
|
852 *tid = timeSetEvent (delay, resolution, setitimer_helper_proc, sigkind,
|
|
853 event_type);
|
|
854 }
|
|
855
|
|
856 return !delay || *tid;
|
|
857 }
|
|
858
|
|
859 int
|
|
860 mswindows_setitimer (int kind, const struct itimerval *itnew,
|
|
861 struct itimerval *itold)
|
|
862 {
|
|
863 /* In this version, both interval and value are allowed
|
|
864 only if they are equal. */
|
|
865 assert ((itnew->it_value.tv_sec == 0 && itnew->it_value.tv_usec == 0)
|
|
866 || (itnew->it_interval.tv_sec == 0 &&
|
|
867 itnew->it_interval.tv_usec == 0)
|
|
868 || (itnew->it_value.tv_sec == itnew->it_interval.tv_sec &&
|
|
869 itnew->it_value.tv_usec == itnew->it_interval.tv_usec));
|
|
870
|
|
871 if (kind == ITIMER_REAL)
|
|
872 return setitimer_helper (itnew, itold, &it_alarm, &tid_alarm, SIGALRM);
|
|
873 else if (kind == ITIMER_PROF)
|
|
874 return setitimer_helper (itnew, itold, &it_prof, &tid_prof, SIGPROF);
|
|
875 else
|
|
876 return errno = EINVAL;
|
|
877 }
|
|
878
|
613
|
879 #endif /* defined (WIN32_NATIVE) || defined (CYGWIN_BROKEN_SIGNALS) */
|
|
880
|
611
|
881
|
442
|
882 void
|
|
883 syms_of_win32 (void)
|
|
884 {
|
|
885 DEFSUBR (Fmswindows_shell_execute);
|
673
|
886 #ifdef CYGWIN
|
|
887 DEFSUBR (Fmswindows_cygwin_to_win32_path);
|
|
888 #endif
|
442
|
889 }
|
|
890
|
|
891 void
|
771
|
892 vars_of_win32 (void)
|
|
893 {
|
2526
|
894 DEFVAR_LISP ("mswindows-downcase-file-names",
|
|
895 &Vmswindows_downcase_file_names /*
|
771
|
896 Non-nil means convert all-upper case file names to lower case.
|
|
897 This applies when performing completions and file name expansion.
|
|
898 */ );
|
|
899 Vmswindows_downcase_file_names = Qnil;
|
2526
|
900
|
|
901 DEFVAR_BOOL ("mswindows-shortcuts-are-symlinks",
|
|
902 &mswindows_shortcuts_are_symlinks /*
|
|
903 Non-nil means shortcuts (.LNK files) are treated as symbolic links.
|
|
904 This works also for symlinks created under Cygwin, because they use .LNK
|
|
905 files to implement symbolic links.
|
|
906 */ );
|
|
907 mswindows_shortcuts_are_symlinks = 1;
|
771
|
908 }
|
|
909
|
|
910 void
|
442
|
911 init_win32 (void)
|
|
912 {
|
|
913 init_potentially_nonexistent_functions ();
|
|
914 }
|
771
|
915
|
|
916 void
|
2367
|
917 init_win32_very_very_early (void)
|
771
|
918 {
|
|
919 mswindows_windows9x_p = GetVersion () & 0x80000000;
|
|
920 }
|