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 }