[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Emacs-diffs] trunk r115490: Support MS-Windows file names that use char
From: |
Eli Zaretskii |
Subject: |
[Emacs-diffs] trunk r115490: Support MS-Windows file names that use characters outside of ANSI codepage. |
Date: |
Thu, 12 Dec 2013 18:23:11 +0000 |
User-agent: |
Bazaar (2.6b2) |
------------------------------------------------------------
revno: 115490 [merge]
revision-id: address@hidden
parent: address@hidden
parent: address@hidden
fixes bug: http://debbugs.gnu.org/7100
committer: Eli Zaretskii <address@hidden>
branch nick: trunk
timestamp: Thu 2013-12-12 20:19:10 +0200
message:
Support MS-Windows file names that use characters outside of ANSI codepage.
src/w32.c (get_file_security, set_file_security)
(create_symbolic_link): Separate pointers and boolean flags for
ANSI and Unicode APIs. Use the latter if w32_unicode_filenames is
non-zero, else the former.
(codepage_for_filenames, filename_to_utf16, )
(filename_from_utf16, filename_to_ansi, filename_from_ansi): New
functions.
(init_user_info): Allow $HOME and $SHELL to include non-ANSI
characters.
(normalize_filename): Lose the DBCS code, now works on UTF-8.
Accept only one argument; all callers changed.
(dostounix_filename): Remove the second argument, now works in
UTF-8. All callers changed.
(parse_root): Lose DBCS code.
(get_long_basename, w32_get_short_filename, init_environment)
(GetCachedVolumeInformation, sys_readdir, open_unc_volume)
(read_unc_volume, logon_network_drive, faccessat, sys_chdir)
(sys_chmod, sys_creat, sys_fopen, sys_link, sys_mkdir, sys_open)
(sys_rename_replace, sys_rmdir, sys_unlink, stat_worker, utime)
(is_symlink, readlink, chase_symlinks, w32_delayed_load): Work in
Unicode mode if w32_unicode_filenames is non-zero, in ANSI mode
otherwise.
(ansi_encode_filename): New function.
(get_emacs_configuration, get_emacs_configuration_options):
Functions deleted.
(add_volume_info, GetCachedVolumeInformation): Run the input file
name through unixtodos_filename, to ensure it is stored and
referenced in canonical form.
(get_volume_info): Lose the DBCS code, now works in UTF-8.
(logon_network_drive, sys_link, utime): Improve error handling.
(sys_access): New function.
(hashval, generate_inode_val): Unused functions deleted.
(symlink, readlink, readlinkat): Lose DBCS code, now works in UTF-8.
(check_windows_init_file): Convert error message from UTF-8 to
ANSI codepage, for display in the message box.
(globals_of_w32): Set w32_unicode_filenames according to the OS
version.
src/w32term.c (construct_drag_n_drop): Work in Unicode mode when
w32_unicode_filenames is non-zero, ANSI mode otherwise.
(syms_of_w32term): Declare w32-unicode-filenames.
src/w32proc.c (new_child, delete_child): Remove code that handled
unused pending_deletion and input_file members of the child struct.
(create_child, sys_spawnve): Convert all file names to ANSI
codepage. Use ANSI APIs explicitly; forcibly fail if any file
name cannot be encoded in ANSI codepage. Don't use
unixtodos_filename, mirror slashes by hand.
(record_infile, record_pending_deletion): Functions deleted.
(Fw32_short_file_name): Call w32_get_short_filename instead of
GetShortPathName.
src/w32notify.c (add_watch): Work in Unicode mode when
w32_unicode_filenames is non-zero, ANSI mode otherwise.
(Fw32notify_add_watch): Rewrite to avoid using GetFullPathName;
instead, do the same with Lisp primitives.
src/w32fns.c (file_dialog_callback, Fx_file_dialog)
(Fsystem_move_file_to_trash, Fw32_shell_execute)
(Ffile_system_info, Fdefault_printer_name): Work in Unicode mode
when w32_unicode_filenames is non-zero, ANSI mode otherwise.
(Fw32_shell_execute): Improve error reporting.
(Fdefault_printer_name): Ifdef away for Cygwin.
src/w32.h (struct _child_process): Remove input_file and
pending_deletion members that are no longer used.
(dostounix_filename, w32_get_short_filename, filename_from_ansi)
(filename_to_ansi, filename_from_utf16, filename_to_utf16)
(ansi_encode_filename): New and updated prototypes.
src/unexw32.c (open_input_file, open_output_file, unexec): Use ANSI
APIs explicitly.
(unexec): Don't use dostounix_filename, it expects a file name in
UTF-8. Instead, mirror backslashes by hand. Convert NEW_NAME to
ANSI encoding.
src/fileio.c (Ffile_name_directory, file_name_as_directory)
(directory_file_name, Fexpand_file_name)
(Fsubstitute_in_file_name) [WINDOWSNT]: Adapt to the change in
arguments of dostounix_filename.
(Fexpand_file_name) [WINDOWSNT]: Convert value of $HOME to UTF-8.
use MAX_UTF8_PATH for size of file-name strings.
(emacs_readlinkat): Build an explicitly unibyte string for file
names.
(syms_of_fileio) <file-name-coding-system>
default-file-name-coding-system>: Mention MS-Windows peculiarities.
src/emacs.c (init_cmdargs) [WINDOWSNT]: Convert argv[0] to UTF-8.
(main) [WINDOWSNT]: Convert the argv[] elements that are files or
directories to UTF-8.
(decode_env_path) [WINDOWSNT]: Convert file names taken from the
environment, and each element of the input PATH, to UTF-8.
src/dired.c (file_attributes): Use build_unibyte_string explicitly
to make Lisp strings from user and group names.
src/coding.h (ENCODE_FILE, DECODE_FILE): Just call encode_file and
decode_file.
src/coding.c (decode_file_name, encode_file_name): New functions.
src/termcap.c (tgetent): Adapt to the change in arguments of
dostounix_filename.
src/sysdep.c (sys_subshell) [WINDOWSNT]: Use MAX_UTF8_PATH for file
names.
src/msdos.c (dostounix_filename, init_environment): Adapt to the
change in arguments of dostounix_filename.
src/image.c (xpm_load, tiff_load, gif_load, imagemagick_load)
[WINDOWSNT]: Encode file names passed to the image libraries in
ANSI codepage.
src/gnutls.c (Fgnutls_boot): Encode all file names passed to GnuTLS.
[WINDOWSNT]: Convert file names to the current ANSI codepage.
src/filelock.c (lock_file) [WINDOWSNT]: Adapt to the change in
arguments of dostounix_filename.
nt/inc/ms-w32.h (MAX_UTF8_PATH): New macro.
(opendir, closedir, readdir, seekdir): Redirect to replacement
functions.
nt/inc/dirent.h: Make d_name[] be MAXNAMELEN*4 characters long.
lisp/term/w32-win.el (w32-handle-dropped-file):
lisp/startup.el (normal-top-level):
lisp/net/browse-url.el (browse-url-file-url):
lisp/dnd.el (dnd-get-local-file-name): On MS-Windows, encode and
decode file names using 'utf-8' rather than
file-name-coding-system.
doc/emacs/mule.texi (File Name Coding): Document file-name encoding
peculiarities on MS-Windows.
doc/lispref/nonascii.texi (Encoding and I/O): Document file-name encoding
peculiarities on MS-Windows.
etc/NEWS: Mention support on MS-Windows of file names outside of the
current locale.
modified:
doc/emacs/ChangeLog changelog-20091113204419-o5vbwnq5f7feedwu-6227
doc/emacs/mule.texi mule.texi-20091113204419-o5vbwnq5f7feedwu-6270
doc/lispref/ChangeLog changelog-20091113204419-o5vbwnq5f7feedwu-6155
doc/lispref/nonascii.texi
nonascii.texi-20091113204419-o5vbwnq5f7feedwu-6202
etc/ChangeLog changelog-20091113204419-o5vbwnq5f7feedwu-1485
etc/NEWS news-20100311060928-aoit31wvzf25yr1z-1
lisp/ChangeLog changelog-20091113204419-o5vbwnq5f7feedwu-1432
lisp/dnd.el dnd.el-20091113204419-o5vbwnq5f7feedwu-3401
lisp/net/browse-url.el
browseurl.el-20091113204419-o5vbwnq5f7feedwu-1785
lisp/startup.el startup.el-20091113204419-o5vbwnq5f7feedwu-260
lisp/term/w32-win.el w32win.el-20091113204419-o5vbwnq5f7feedwu-943
nt/ChangeLog changelog-20091113204419-o5vbwnq5f7feedwu-1545
nt/inc/dirent.h ndir.h-20091113204419-o5vbwnq5f7feedwu-417
nt/inc/ms-w32.h msw32.h-20091113204419-o5vbwnq5f7feedwu-807
src/ChangeLog changelog-20091113204419-o5vbwnq5f7feedwu-1438
src/coding.c coding.c-20091113204419-o5vbwnq5f7feedwu-1077
src/coding.h coding.h-20091113204419-o5vbwnq5f7feedwu-1078
src/dired.c dired.c-20091113204419-o5vbwnq5f7feedwu-171
src/emacs.c emacs.c-20091113204419-o5vbwnq5f7feedwu-241
src/fileio.c fileio.c-20091113204419-o5vbwnq5f7feedwu-210
src/filelock.c filelock.c-20091113204419-o5vbwnq5f7feedwu-179
src/gnutls.c gnutls.c-20100926054902-dzayyj6wycit6kzn-3
src/image.c image.c-20091113204419-o5vbwnq5f7feedwu-2969
src/msdos.c msdos.c-20091113204419-o5vbwnq5f7feedwu-656
src/msdos.h msdos.h-20091113204419-o5vbwnq5f7feedwu-657
src/sysdep.c sysdep.c-20091113204419-o5vbwnq5f7feedwu-448
src/termcap.c termcap.c-20091113204419-o5vbwnq5f7feedwu-775
src/unexw32.c unexw32.c-20091113204419-o5vbwnq5f7feedwu-887
src/w32.c w32.c-20091113204419-o5vbwnq5f7feedwu-808
src/w32.h w32.h-20091113204419-o5vbwnq5f7feedwu-809
src/w32fns.c w32fns.c-20091113204419-o5vbwnq5f7feedwu-945
src/w32notify.c w32notify.c-20121007123611-xyh65j2ka2vm684f-1
src/w32proc.c w32proc.c-20091113204419-o5vbwnq5f7feedwu-814
src/w32term.c w32term.c-20091113204419-o5vbwnq5f7feedwu-950
=== modified file 'doc/emacs/ChangeLog'
--- a/doc/emacs/ChangeLog 2013-12-12 03:37:38 +0000
+++ b/doc/emacs/ChangeLog 2013-12-12 18:19:10 +0000
@@ -1,3 +1,8 @@
+2013-12-12 Eli Zaretskii <address@hidden>
+
+ * mule.texi (File Name Coding): Document file-name encoding
+ peculiarities on MS-Windows.
+
2013-12-12 Glenn Morris <address@hidden>
* emacs.texi: Sync direntry with info/dir version.
=== modified file 'doc/emacs/mule.texi'
--- a/doc/emacs/mule.texi 2013-07-31 13:11:47 +0000
+++ b/doc/emacs/mule.texi 2013-12-07 16:51:33 +0000
@@ -1130,6 +1130,21 @@
file names are not encoded specially; they appear in the file system
using the internal Emacs representation.
address@hidden file-name encoding, MS-Windows
address@hidden w32-unicode-filenames
+ When Emacs runs on MS-Windows versions that are descendants of the
+NT family (Windows 2000, XP, Vista, Windows 7, and Windows 8), the
+value of @code{file-name-coding-system} is largely ignored, as Emacs
+by default uses APIs that allow to pass Unicode file names directly.
+By contrast, on Windows 9X, file names are encoded using
address@hidden, which should be set to the codepage
+(@pxref{Coding Systems, codepage}) pertinent for the current system
+locale. The value of the variable @code{w32-unicode-filenames}
+controls whether Emacs uses the Unicode APIs when it calls OS
+functions that accept file names. This variable is set by the startup
+code to @code{nil} on Windows 9X, and to @code{t} on newer versions of
+MS-Windows.
+
@strong{Warning:} if you change @code{file-name-coding-system} (or the
language environment) in the middle of an Emacs session, problems can
result if you have already visited files whose names were encoded using
=== modified file 'doc/lispref/ChangeLog'
--- a/doc/lispref/ChangeLog 2013-12-12 03:37:38 +0000
+++ b/doc/lispref/ChangeLog 2013-12-12 18:19:10 +0000
@@ -1,3 +1,8 @@
+2013-12-12 Eli Zaretskii <address@hidden>
+
+ * nonascii.texi (Encoding and I/O): Document file-name encoding
+ peculiarities on MS-Windows.
+
2013-12-12 Glenn Morris <address@hidden>
* elisp.texi: Sync direntry with info/dir version.
=== modified file 'doc/lispref/nonascii.texi'
--- a/doc/lispref/nonascii.texi 2013-10-25 12:23:07 +0000
+++ b/doc/lispref/nonascii.texi 2013-12-07 16:51:33 +0000
@@ -1108,6 +1108,16 @@
an error. If such a problem happens, use @kbd{C-x C-w} to specify a
new file name for that buffer.
address@hidden file-name encoding, MS-Windows
+ On Windows 2000 and later, Emacs by default uses Unicode APIs to
+pass file names to the OS, so the value of
address@hidden is largely ignored. Lisp applications
+that need to encode or decode file names on the Lisp level should use
address@hidden coding-system when @code{system-type} is
address@hidden; the conversion of UTF-8 encoded file names to the
+encoding appropriate for communicating with the OS is performed
+internally by Emacs.
+
@node Lisp and Coding Systems
@subsection Coding Systems in Lisp
=== modified file 'etc/ChangeLog'
--- a/etc/ChangeLog 2013-11-23 14:19:32 +0000
+++ b/etc/ChangeLog 2013-12-12 18:19:10 +0000
@@ -1,3 +1,8 @@
+2013-12-12 Eli Zaretskii <address@hidden>
+
+ * NEWS: Mention support on MS-Windows of file names outside of the
+ current locale.
+
2013-11-23 Xue Fuqiao <address@hidden>
* TODO: Minor update.
=== modified file 'etc/NEWS'
--- a/etc/NEWS 2013-12-12 00:42:16 +0000
+++ b/etc/NEWS 2013-12-12 18:19:10 +0000
@@ -1001,6 +1001,14 @@
need to set any variables due to this change.)
+++
+** Emacs on Windows 2000 and later can now access files and directories
+whose names cannot be encoded in the current system codepage.
+
+The new variable `w32-unicode-filenames' controls this feature: if it
+is t, Emacs uses Unicode APIs to pass file names to system calls,
+which lifts the limitation of file names to the current locale.
+
++++
** The "generate a backtrace on fatal error" feature now works on MS Windows.
The backtrace is written to the 'emacs_backtrace.txt' file in the
directory where Emacs was running.
=== modified file 'lisp/ChangeLog'
--- a/lisp/ChangeLog 2013-12-12 05:37:09 +0000
+++ b/lisp/ChangeLog 2013-12-12 18:19:10 +0000
@@ -1,3 +1,12 @@
+2013-12-12 Eli Zaretskii <address@hidden>
+
+ * term/w32-win.el (w32-handle-dropped-file):
+ * startup.el (normal-top-level):
+ * net/browse-url.el (browse-url-file-url):
+ * dnd.el (dnd-get-local-file-name): On MS-Windows, encode and
+ decode file names using 'utf-8' rather than
+ file-name-coding-system.
+
2013-12-12 Fabián Ezequiel Gallina <address@hidden>
* progmodes/python.el (python-indent-context)
=== modified file 'lisp/dnd.el'
--- a/lisp/dnd.el 2013-01-01 09:11:05 +0000
+++ b/lisp/dnd.el 2013-12-03 12:21:13 +0000
@@ -152,10 +152,13 @@
(let ((f (cond ((string-match "^file:///" uri) ; XDND format.
(substring uri (1- (match-end 0))))
((string-match "^file:" uri) ; Old KDE, Motif, Sun
- (substring uri (match-end 0))))))
- (and f (setq f (decode-coding-string (dnd-unescape-uri f)
- (or file-name-coding-system
-
default-file-name-coding-system))))
+ (substring uri (match-end 0)))))
+ (coding (if (equal system-type 'windows-nt)
+ ;; W32 pretends that file names are UTF-8 encoded.
+ 'utf-8
+ (or file-name-coding-system
+ default-file-name-coding-system))))
+ (and f (setq f (decode-coding-string (dnd-unescape-uri f) coding)))
(when (and f must-exist (not (file-readable-p f)))
(setq f nil))
f))
=== modified file 'lisp/net/browse-url.el'
--- a/lisp/net/browse-url.el 2013-09-05 03:30:07 +0000
+++ b/lisp/net/browse-url.el 2013-12-03 12:21:13 +0000
@@ -723,9 +723,12 @@
(defun browse-url-file-url (file)
"Return the URL corresponding to FILE.
Use variable `browse-url-filename-alist' to map filenames to URLs."
- (let ((coding (and (default-value 'enable-multibyte-characters)
- (or file-name-coding-system
- default-file-name-coding-system))))
+ (let ((coding (if (equal system-type 'windows-nt)
+ ;; W32 pretends that file names are UTF-8 encoded.
+ 'utf-8
+ (and (default-value 'enable-multibyte-characters)
+ (or file-name-coding-system
+ default-file-name-coding-system)))))
(if coding (setq file (encode-coding-string file coding))))
(setq file (browse-url-url-encode-chars file "[*\"()',=;?% ]"))
(dolist (map browse-url-filename-alist)
=== modified file 'lisp/startup.el'
--- a/lisp/startup.el 2013-12-01 02:04:46 +0000
+++ b/lisp/startup.el 2013-12-09 17:33:01 +0000
@@ -533,43 +533,45 @@
;; for many other file-name variables and directory lists, so it
;; is important to decode it ASAP.
(when locale-coding-system
- (save-excursion
- (dolist (elt (buffer-list))
- (set-buffer elt)
- (if default-directory
- (setq default-directory
- (decode-coding-string default-directory
- locale-coding-system t)))))
+ (let ((coding (if (eq system-type 'windows-nt)
+ ;; MS-Windows build converts all file names to
+ ;; UTF-8 during startup.
+ 'utf-8
+ locale-coding-system)))
+ (save-excursion
+ (dolist (elt (buffer-list))
+ (set-buffer elt)
+ (if default-directory
+ (setq default-directory
+ (decode-coding-string default-directory coding t)))))
- ;; Decode all the important variables and directory lists, now
- ;; that we know the locale's encoding. This is because the
- ;; values of these variables are until here unibyte undecoded
- ;; strings created by build_unibyte_string. data-directory in
- ;; particular is used to construct many other standard directory
- ;; names, so it must be decoded ASAP.
- ;; Note that charset-map-path cannot be decoded here, since we
- ;; could then be trapped in infinite recursion below, when we
- ;; load subdirs.el, because encoding a directory name might need
- ;; to load a charset map, which will want to encode
- ;; charset-map-path, which will want to load the same charset
- ;; map... So decoding of charset-map-path is delayed until
- ;; further down below.
- (dolist (pathsym '(load-path exec-path))
- (let ((path (symbol-value pathsym)))
- (if (listp path)
- (set pathsym (mapcar (lambda (dir)
- (decode-coding-string
- dir
- locale-coding-system t))
- path)))))
- (dolist (filesym '(data-directory doc-directory exec-directory
- installation-directory
- invocation-directory invocation-name
- source-directory
- shared-game-score-directory))
- (let ((file (symbol-value filesym)))
- (if (stringp file)
- (set filesym (decode-coding-string file locale-coding-system
t))))))
+ ;; Decode all the important variables and directory lists, now
+ ;; that we know the locale's encoding. This is because the
+ ;; values of these variables are until here unibyte undecoded
+ ;; strings created by build_unibyte_string. data-directory in
+ ;; particular is used to construct many other standard
+ ;; directory names, so it must be decoded ASAP. Note that
+ ;; charset-map-path cannot be decoded here, since we could
+ ;; then be trapped in infinite recursion below, when we load
+ ;; subdirs.el, because encoding a directory name might need to
+ ;; load a charset map, which will want to encode
+ ;; charset-map-path, which will want to load the same charset
+ ;; map... So decoding of charset-map-path is delayed until
+ ;; further down below.
+ (dolist (pathsym '(load-path exec-path))
+ (let ((path (symbol-value pathsym)))
+ (if (listp path)
+ (set pathsym (mapcar (lambda (dir)
+ (decode-coding-string dir coding t))
+ path)))))
+ (dolist (filesym '(data-directory doc-directory exec-directory
+ installation-directory
+ invocation-directory invocation-name
+ source-directory
+ shared-game-score-directory))
+ (let ((file (symbol-value filesym)))
+ (if (stringp file)
+ (set filesym (decode-coding-string file coding t)))))))
(let ((dir default-directory))
(with-current-buffer "*Messages*"
@@ -599,12 +601,13 @@
;; need for encoding them are already loaded, we are ready to
;; decode charset-map-path.
(if (listp charset-map-path)
- (setq charset-map-path
- (mapcar (lambda (dir)
- (decode-coding-string
- dir
- locale-coding-system t))
- charset-map-path)))
+ (let ((coding (if (eq system-type 'windows-nt)
+ 'utf-8
+ locale-coding-system)))
+ (setq charset-map-path
+ (mapcar (lambda (dir)
+ (decode-coding-string dir coding t))
+ charset-map-path))))
(setq default-directory (abbreviate-file-name default-directory))
(let ((old-face-font-rescale-alist face-font-rescale-alist))
(unwind-protect
=== modified file 'lisp/term/w32-win.el'
--- a/lisp/term/w32-win.el 2013-11-01 09:04:16 +0000
+++ b/lisp/term/w32-win.el 2013-12-03 12:21:13 +0000
@@ -110,8 +110,13 @@
(let ((f (if (eq system-type 'cygwin)
(cygwin-convert-file-name-from-windows file-name t)
(subst-char-in-string ?\\ ?/ file-name)))
- (coding (or file-name-coding-system
- default-file-name-coding-system)))
+ (coding (if (eq system-type 'windows-nt)
+ ;; Native w32 build pretends that its file names
+ ;; are encoded in UTF-8, and converts to the
+ ;; appropriate encoding internally.
+ 'utf-8
+ (or file-name-coding-system
+ default-file-name-coding-system))))
(setq file-name
(mapconcat 'url-hexify-string
=== modified file 'nt/ChangeLog'
--- a/nt/ChangeLog 2013-11-27 06:15:06 +0000
+++ b/nt/ChangeLog 2013-12-12 18:19:10 +0000
@@ -1,3 +1,10 @@
+2013-12-12 Eli Zaretskii <address@hidden>
+
+ * inc/ms-w32.h (MAX_UTF8_PATH): New macro.
+ (opendir, closedir, readdir, seekdir): Redirect to replacement
+ functions.
+ * inc/dirent.h: Make d_name[] be MAXNAMELEN*4 characters long.
+
2013-11-27 Glenn Morris <address@hidden>
* README.W32:
=== modified file 'nt/inc/dirent.h'
--- a/nt/inc/dirent.h 2013-10-12 13:11:14 +0000
+++ b/nt/inc/dirent.h 2013-11-09 12:49:02 +0000
@@ -40,7 +40,7 @@
__int64 d_time_write;
_fsize_t d_size;
#endif
- char d_name[MAXNAMLEN+1]; /* name of file */
+ char d_name[MAXNAMLEN * 4 + 1]; /* name of file */
};
typedef struct
=== modified file 'nt/inc/ms-w32.h'
--- a/nt/inc/ms-w32.h 2013-11-29 01:22:40 +0000
+++ b/nt/inc/ms-w32.h 2013-12-07 17:21:57 +0000
@@ -152,6 +152,9 @@
#define MAXPATHLEN _MAX_PATH
#endif
+/* This is used to hold UTF-8 encoded file names. */
+#define MAX_UTF8_PATH (MAXPATHLEN * 4)
+
#ifdef HAVE_NTGUI
# ifndef HAVE_WINDOW_SYSTEM
# define HAVE_WINDOW_SYSTEM 1
@@ -218,6 +221,14 @@
#define strerror sys_strerror
#undef unlink
#define unlink sys_unlink
+#undef opendir
+#define opendir sys_opendir
+#undef closedir
+#define closedir sys_closedir
+#undef readdir
+#define readdir sys_readdir
+#undef seekdir
+#define seekdir sys_seekdir
/* This prototype is needed because some files include config.h
_after_ the standard headers, so sys_unlink gets no prototype from
stdio.h or io.h. */
=== modified file 'src/ChangeLog'
--- a/src/ChangeLog 2013-12-12 14:26:06 +0000
+++ b/src/ChangeLog 2013-12-12 18:19:10 +0000
@@ -1,3 +1,128 @@
+2013-12-12 Eli Zaretskii <address@hidden>
+
+ Support file names on MS-Windows that use characters outside of
+ the current system codepage. (Bug#7100)
+
+ * w32.c (get_file_security, set_file_security)
+ (create_symbolic_link): Separate pointers and boolean flags for
+ ANSI and Unicode APIs. Use the latter if w32_unicode_filenames is
+ non-zero, else the former.
+ (codepage_for_filenames, filename_to_utf16, )
+ (filename_from_utf16, filename_to_ansi, filename_from_ansi): New
+ functions.
+ (init_user_info): Allow $HOME and $SHELL to include non-ANSI
+ characters.
+ (normalize_filename): Lose the DBCS code, now works on UTF-8.
+ Accept only one argument; all callers changed.
+ (dostounix_filename): Remove the second argument, now works in
+ UTF-8. All callers changed.
+ (parse_root): Lose DBCS code.
+ (get_long_basename, w32_get_short_filename, init_environment)
+ (GetCachedVolumeInformation, sys_readdir, open_unc_volume)
+ (read_unc_volume, logon_network_drive, faccessat, sys_chdir)
+ (sys_chmod, sys_creat, sys_fopen, sys_link, sys_mkdir, sys_open)
+ (sys_rename_replace, sys_rmdir, sys_unlink, stat_worker, utime)
+ (is_symlink, readlink, chase_symlinks, w32_delayed_load): Work in
+ Unicode mode if w32_unicode_filenames is non-zero, in ANSI mode
+ otherwise.
+ (ansi_encode_filename): New function.
+ (get_emacs_configuration, get_emacs_configuration_options):
+ Functions deleted.
+ (add_volume_info, GetCachedVolumeInformation): Run the input file
+ name through unixtodos_filename, to ensure it is stored and
+ referenced in canonical form.
+ (get_volume_info): Lose the DBCS code, now works in UTF-8.
+ (logon_network_drive, sys_link, utime): Improve error handling.
+ (sys_access): New function.
+ (hashval, generate_inode_val): Unused functions deleted.
+ (symlink, readlink, readlinkat): Lose DBCS code, now works in UTF-8.
+ (check_windows_init_file): Convert error message from UTF-8 to
+ ANSI codepage, for display in the message box.
+ (globals_of_w32): Set w32_unicode_filenames according to the OS
+ version.
+
+ * w32term.c (construct_drag_n_drop): Work in Unicode mode when
+ w32_unicode_filenames is non-zero, ANSI mode otherwise.
+ (syms_of_w32term): Declare w32-unicode-filenames.
+
+ * w32proc.c (new_child, delete_child): Remove code that handled
+ unused pending_deletion and input_file members of the child struct.
+ (create_child, sys_spawnve): Convert all file names to ANSI
+ codepage. Use ANSI APIs explicitly; forcibly fail if any file
+ name cannot be encoded in ANSI codepage. Don't use
+ unixtodos_filename, mirror slashes by hand.
+ (record_infile, record_pending_deletion): Functions deleted.
+ (Fw32_short_file_name): Call w32_get_short_filename instead of
+ GetShortPathName.
+
+ * w32notify.c (add_watch): Work in Unicode mode when
+ w32_unicode_filenames is non-zero, ANSI mode otherwise.
+ (Fw32notify_add_watch): Rewrite to avoid using GetFullPathName;
+ instead, do the same with Lisp primitives.
+
+ * w32fns.c (file_dialog_callback, Fx_file_dialog)
+ (Fsystem_move_file_to_trash, Fw32_shell_execute)
+ (Ffile_system_info, Fdefault_printer_name): Work in Unicode mode
+ when w32_unicode_filenames is non-zero, ANSI mode otherwise.
+ (Fw32_shell_execute): Improve error reporting.
+ (Fdefault_printer_name): Ifdef away for Cygwin.
+
+ * w32.h (struct _child_process): Remove input_file and
+ pending_deletion members that are no longer used.
+ (dostounix_filename, w32_get_short_filename, filename_from_ansi)
+ (filename_to_ansi, filename_from_utf16, filename_to_utf16)
+ (ansi_encode_filename): New and updated prototypes.
+
+ * unexw32.c (open_input_file, open_output_file, unexec): Use ANSI
+ APIs explicitly.
+ (unexec): Don't use dostounix_filename, it expects a file name in
+ UTF-8. Instead, mirror backslashes by hand. Convert NEW_NAME to
+ ANSI encoding.
+
+ * fileio.c (Ffile_name_directory, file_name_as_directory)
+ (directory_file_name, Fexpand_file_name)
+ (Fsubstitute_in_file_name) [WINDOWSNT]: Adapt to the change in
+ arguments of dostounix_filename.
+ (Fexpand_file_name) [WINDOWSNT]: Convert value of $HOME to UTF-8.
+ use MAX_UTF8_PATH for size of file-name strings.
+ (emacs_readlinkat): Build an explicitly unibyte string for file
+ names.
+ (syms_of_fileio) <file-name-coding-system>
+ default-file-name-coding-system>: Mention MS-Windows peculiarities.
+
+ * emacs.c (init_cmdargs) [WINDOWSNT]: Convert argv[0] to UTF-8.
+ (main) [WINDOWSNT]: Convert the argv[] elements that are files or
+ directories to UTF-8.
+ (decode_env_path) [WINDOWSNT]: Convert file names taken from the
+ environment, and each element of the input PATH, to UTF-8.
+
+ * dired.c (file_attributes): Use build_unibyte_string explicitly
+ to make Lisp strings from user and group names.
+
+ * coding.h (ENCODE_FILE, DECODE_FILE): Just call encode_file and
+ decode_file.
+
+ * coding.c (decode_file_name, encode_file_name): New functions.
+
+ * termcap.c (tgetent): Adapt to the change in arguments of
+ dostounix_filename.
+
+ * sysdep.c (sys_subshell) [WINDOWSNT]: Use MAX_UTF8_PATH for file
+ names.
+
+ * msdos.c (dostounix_filename, init_environment): Adapt to the
+ change in arguments of dostounix_filename.
+
+ * image.c (xpm_load, tiff_load, gif_load, imagemagick_load)
+ [WINDOWSNT]: Encode file names passed to the image libraries in
+ ANSI codepage.
+
+ * gnutls.c (Fgnutls_boot): Encode all file names passed to GnuTLS.
+ [WINDOWSNT]: Convert file names to the current ANSI codepage.
+
+ * filelock.c (lock_file) [WINDOWSNT]: Adapt to the change in
+ arguments of dostounix_filename.
+
2013-12-12 Dmitry Antipov <address@hidden>
* font.h (struct font_entity) [HAVE_NS]: New field to record
=== modified file 'src/coding.c'
--- a/src/coding.c 2013-11-18 16:29:49 +0000
+++ b/src/coding.c 2013-11-18 16:45:48 +0000
@@ -9490,6 +9490,55 @@
return code_convert_string (string, coding_system, Qt, encodep, 0, 1);
}
+/* Encode or decode a file name, to or from a unibyte string suitable
+ for passing to C library functions. */
+Lisp_Object
+decode_file_name (Lisp_Object fname)
+{
+#ifdef WINDOWSNT
+ /* The w32 build pretends to use UTF-8 for file-name encoding, and
+ converts the file names either to UTF-16LE or to the system ANSI
+ codepage internally, depending on the underlying OS; see w32.c. */
+ if (! NILP (Fcoding_system_p (Qutf_8)))
+ return code_convert_string_norecord (fname, Qutf_8, 0);
+ return fname;
+#else /* !WINDOWSNT */
+ if (! NILP (Vfile_name_coding_system))
+ return code_convert_string_norecord (fname, Vfile_name_coding_system, 0);
+ else if (! NILP (Vdefault_file_name_coding_system))
+ return code_convert_string_norecord (fname,
+ Vdefault_file_name_coding_system, 0);
+ else
+ return fname;
+#endif
+}
+
+Lisp_Object
+encode_file_name (Lisp_Object fname)
+{
+ /* This is especially important during bootstrap and dumping, when
+ file-name encoding is not yet known, and therefore any non-ASCII
+ file names are unibyte strings, and could only be thrashed if we
+ try to encode them. */
+ if (!STRING_MULTIBYTE (fname))
+ return fname;
+#ifdef WINDOWSNT
+ /* The w32 build pretends to use UTF-8 for file-name encoding, and
+ converts the file names either to UTF-16LE or to the system ANSI
+ codepage internally, depending on the underlying OS; see w32.c. */
+ if (! NILP (Fcoding_system_p (Qutf_8)))
+ return code_convert_string_norecord (fname, Qutf_8, 1);
+ return fname;
+#else /* !WINDOWSNT */
+ if (! NILP (Vfile_name_coding_system))
+ return code_convert_string_norecord (fname, Vfile_name_coding_system, 1);
+ else if (! NILP (Vdefault_file_name_coding_system))
+ return code_convert_string_norecord (fname,
+ Vdefault_file_name_coding_system, 1);
+ else
+ return fname;
+#endif
+}
DEFUN ("decode-coding-string", Fdecode_coding_string, Sdecode_coding_string,
2, 4, 0,
=== modified file 'src/coding.h'
--- a/src/coding.h 2013-11-04 17:30:33 +0000
+++ b/src/coding.h 2013-11-18 16:45:48 +0000
@@ -670,27 +670,13 @@
(code) = (s1 << 8) | s2; \
} while (0)
-/* Encode the file name NAME using the specified coding system for
- file names, if any. If NAME is a unibyte string, return NAME. */
-#define ENCODE_FILE(name) \
- (! STRING_MULTIBYTE (name) \
- ? name \
- : (! NILP (Vfile_name_coding_system) \
- ? code_convert_string_norecord (name, Vfile_name_coding_system, 1) \
- : (! NILP (Vdefault_file_name_coding_system) \
- ? code_convert_string_norecord (name,
Vdefault_file_name_coding_system, 1) \
- : name)))
-
+/* Encode the file name NAME using the specified coding system
+ for file names, if any. */
+#define ENCODE_FILE(NAME) encode_file_name (NAME)
/* Decode the file name NAME using the specified coding system
for file names, if any. */
-#define DECODE_FILE(name) \
- (! NILP (Vfile_name_coding_system) \
- ? code_convert_string_norecord (name, Vfile_name_coding_system, 0) \
- : (! NILP (Vdefault_file_name_coding_system)
\
- ? code_convert_string_norecord (name, Vdefault_file_name_coding_system,
0) \
- : name))
-
+#define DECODE_FILE(NAME) decode_file_name (NAME)
/* Encode the string STR using the specified coding system
for system functions, if any. */
@@ -718,6 +704,8 @@
Lisp_Object, bool, bool, bool);
extern Lisp_Object code_convert_string_norecord (Lisp_Object, Lisp_Object,
bool);
+extern Lisp_Object encode_file_name (Lisp_Object);
+extern Lisp_Object decode_file_name (Lisp_Object);
extern Lisp_Object raw_text_coding_system (Lisp_Object);
extern Lisp_Object coding_inherit_eol_type (Lisp_Object, Lisp_Object);
extern Lisp_Object complement_process_encoding_system (Lisp_Object);
=== modified file 'src/dired.c'
--- a/src/dired.c 2013-09-21 11:48:19 +0000
+++ b/src/dired.c 2013-11-18 14:29:23 +0000
@@ -958,11 +958,11 @@
unblock_input ();
}
if (uname)
- values[2] = DECODE_SYSTEM (build_string (uname));
+ values[2] = DECODE_SYSTEM (build_unibyte_string (uname));
else
values[2] = make_fixnum_or_float (s.st_uid);
if (gname)
- values[3] = DECODE_SYSTEM (build_string (gname));
+ values[3] = DECODE_SYSTEM (build_unibyte_string (gname));
else
values[3] = make_fixnum_or_float (s.st_gid);
=== modified file 'src/emacs.c'
--- a/src/emacs.c 2013-12-08 12:18:13 +0000
+++ b/src/emacs.c 2013-12-09 17:20:34 +0000
@@ -36,6 +36,7 @@
#ifdef WINDOWSNT
#include <fcntl.h>
#include <sys/socket.h>
+#include <mbstring.h>
#include "w32.h"
#include "w32heap.h"
#endif
@@ -393,7 +394,20 @@
initial_argv = argv;
initial_argc = argc;
+#ifdef WINDOWSNT
+ /* Must use argv[0] converted to UTF-8, as it begets many standard
+ file and directory names. */
+ {
+ char argv0[MAX_UTF8_PATH];
+
+ if (filename_from_ansi (argv[0], argv0) == 0)
+ raw_name = build_unibyte_string (argv0);
+ else
+ raw_name = build_unibyte_string (argv[0]);
+ }
+#else
raw_name = build_unibyte_string (argv[0]);
+#endif
/* Add /: to the front of the name
if it would otherwise be treated as magic. */
@@ -795,6 +809,14 @@
if (argmatch (argv, argc, "-chdir", "--chdir", 4, &ch_to_dir, &skip_args))
{
+#ifdef WINDOWSNT
+ /* argv[] array is kept in its original ANSI codepage encoding,
+ we need to convert to UTF-8, for chdir to work. */
+ char newdir[MAX_UTF8_PATH];
+
+ filename_from_ansi (ch_to_dir, newdir);
+ ch_to_dir = newdir;
+#endif
original_pwd = get_current_dir_name ();
if (chdir (ch_to_dir) != 0)
{
@@ -1539,7 +1561,16 @@
char *file;
/* Handle -l loadup, args passed by Makefile. */
if (argmatch (argv, argc, "-l", "--load", 3, &file, &skip_args))
- Vtop_level = list2 (intern_c_string ("load"), build_string (file));
+ {
+#ifdef WINDOWSNT
+ char file_utf8[MAX_UTF8_PATH];
+
+ if (filename_from_ansi (file, file_utf8) == 0)
+ file = file_utf8;
+#endif
+ Vtop_level = list2 (intern_c_string ("load"),
+ build_unibyte_string (file));
+ }
/* Unless next switch is -nl, load "loadup.el" first thing. */
if (! no_loadup)
Vtop_level = list2 (intern_c_string ("load"),
@@ -2185,9 +2216,15 @@
Lisp_Object empty_element = empty ? Qnil : build_string (".");
#ifdef WINDOWSNT
bool defaulted = 0;
- const char *emacs_dir = egetenv ("emacs_dir");
static const char *emacs_dir_env = "%emacs_dir%/";
const size_t emacs_dir_len = strlen (emacs_dir_env);
+ const char *edir = egetenv ("emacs_dir");
+ char emacs_dir[MAX_UTF8_PATH];
+
+ /* egetenv looks in process-environment, which holds the variables
+ in their original system-locale encoding. We need emacs_dir to
+ be in UTF-8. */
+ filename_from_ansi (edir, emacs_dir);
#endif
/* It's okay to use getenv here, because this function is only used
@@ -2208,9 +2245,44 @@
/* Ensure values from the environment use the proper directory separator. */
if (path)
{
- char *path_copy = alloca (strlen (path) + 1);
- strcpy (path_copy, path);
- dostounix_filename (path_copy, 0);
+ char *path_copy;
+
+#ifdef WINDOWSNT
+ char *path_utf8, *q, *d;
+ int cnv_result;
+
+ /* Convert each element of PATH to UTF-8. */
+ p = path_copy = alloca (strlen (path) + 1);
+ strcpy (path_copy, path);
+ d = path_utf8 = alloca (4 * strlen (path) + 1);
+ *d = '\0';
+ do {
+ q = _mbschr (p, SEPCHAR);
+ if (q)
+ *q = '\0';
+ cnv_result = filename_from_ansi (p, d);
+ if (q)
+ {
+ *q++ = SEPCHAR;
+ p = q;
+ /* If conversion of this PATH elements fails, make sure
+ destination pointer will stay put, thus effectively
+ ignoring the offending element. */
+ if (cnv_result == 0)
+ {
+ d += strlen (d);
+ *d++ = SEPCHAR;
+ }
+ }
+ else if (cnv_result != 0 && d > path_utf8)
+ d[-1] = '\0'; /* remove last semi-colon and null-terminate PATH */
+ } while (q);
+ path_copy = path_utf8;
+#else /* MSDOS */
+ path_copy = alloca (strlen (path) + 1);
+ strcpy (path_copy, path);
+#endif
+ dostounix_filename (path_copy);
path = path_copy;
}
#endif
=== modified file 'src/fileio.c'
--- a/src/fileio.c 2013-11-27 16:08:53 +0000
+++ b/src/fileio.c 2013-12-12 18:19:10 +0000
@@ -460,7 +460,8 @@
strcat (res, "/");
beg = res;
p = beg + strlen (beg);
- dostounix_filename (beg, 0);
+ dostounix_filename (beg);
+ /* FIXME: Figure out the multibyte vs unibyte stuff here. */
tem_fn = make_specified_string (beg, -1, p - beg,
STRING_MULTIBYTE (filename));
}
@@ -471,7 +472,7 @@
else if (STRING_MULTIBYTE (filename))
{
tem_fn = make_specified_string (beg, -1, p - beg, 1);
- dostounix_filename (SSDATA (tem_fn), 1);
+ dostounix_filename (SSDATA (tem_fn));
#ifdef WINDOWSNT
if (!NILP (Vw32_downcase_file_names))
tem_fn = Fdowncase (tem_fn);
@@ -479,7 +480,7 @@
}
else
{
- dostounix_filename (beg, 0);
+ dostounix_filename (beg);
tem_fn = make_specified_string (beg, -1, p - beg, 0);
}
return tem_fn;
@@ -583,7 +584,7 @@
dst[srclen++] = DIRECTORY_SEP;
dst[srclen] = 0;
#ifdef DOS_NT
- dostounix_filename (dst, multibyte);
+ dostounix_filename (dst);
#endif
return srclen;
}
@@ -652,7 +653,7 @@
memcpy (dst, src, srclen);
dst[srclen] = 0;
#ifdef DOS_NT
- dostounix_filename (dst, multibyte);
+ dostounix_filename (dst);
#endif
return srclen;
}
@@ -1101,7 +1102,7 @@
#ifdef DOS_NT
/* Make sure directories are all separated with /, but
avoid allocation of a new string when not required. */
- dostounix_filename (nm, multibyte);
+ dostounix_filename (nm);
#ifdef WINDOWSNT
if (IS_DIRECTORY_SEP (nm[1]))
{
@@ -1162,7 +1163,18 @@
nm++;
/* `egetenv' may return a unibyte string, which will bite us since
we expect the directory to be multibyte. */
- tem = build_string (newdir);
+#ifdef WINDOWSNT
+ if (newdir[0])
+ {
+ char newdir_utf8[MAX_UTF8_PATH];
+
+ filename_from_ansi (newdir, newdir_utf8);
+ tem = build_string (newdir_utf8);
+ }
+ else
+#else
+ tem = build_string (newdir);
+#endif
if (multibyte && !STRING_MULTIBYTE (tem))
{
hdir = DECODE_FILE (tem);
@@ -1286,6 +1298,11 @@
indirectly by prepending newdir to nm if necessary, and using
cwd (or the wd of newdir's drive) as the new newdir. */
char *adir;
+#ifdef WINDOWSNT
+ const int adir_size = MAX_UTF8_PATH;
+#else
+ const int adir_size = MAXPATHLEN + 1;
+#endif
if (IS_DRIVE (newdir[0]) && IS_DEVICE_SEP (newdir[1]))
{
@@ -1301,14 +1318,14 @@
strcat (tmp, nm);
nm = tmp;
}
- adir = alloca (MAXPATHLEN + 1);
+ adir = alloca (adir_size);
if (drive)
{
if (!getdefdir (c_toupper (drive) - 'A' + 1, adir))
strcpy (adir, "/");
}
else
- getcwd (adir, MAXPATHLEN + 1);
+ getcwd (adir, adir_size);
if (multibyte)
{
Lisp_Object tem = build_string (adir);
@@ -1479,7 +1496,7 @@
target[1] = ':';
}
result = make_specified_string (target, -1, o - target, multibyte);
- dostounix_filename (SSDATA (result), multibyte);
+ dostounix_filename (SSDATA (result));
#ifdef WINDOWSNT
if (!NILP (Vw32_downcase_file_names))
result = Fdowncase (result);
@@ -1763,7 +1780,7 @@
nm = xlispstrdupa (filename);
#ifdef DOS_NT
- dostounix_filename (nm, multibyte);
+ dostounix_filename (nm);
substituted = (memcmp (nm, SDATA (filename), SBYTES (filename)) != 0);
#endif
endp = nm + SBYTES (filename);
@@ -2661,9 +2678,9 @@
if (!buf)
return Qnil;
- val = build_string (buf);
+ val = build_unibyte_string (buf);
if (buf[0] == '/' && strchr (buf, ':'))
- val = concat2 (build_string ("/:"), val);
+ val = concat2 (build_unibyte_string ("/:"), val);
if (buf != readlink_buf)
xfree (buf);
val = DECODE_FILE (val);
@@ -5858,7 +5875,11 @@
DEFVAR_LISP ("file-name-coding-system", Vfile_name_coding_system,
doc: /* Coding system for encoding file names.
-If it is nil, `default-file-name-coding-system' (which see) is used. */);
+If it is nil, `default-file-name-coding-system' (which see) is used.
+
+On MS-Windows, the value of this variable is largely ignored if
+\`w32-unicode-filenames' (which see) is non-nil. Emacs on Windows
+behaves as if file names were encoded in `utf-8'. */);
Vfile_name_coding_system = Qnil;
DEFVAR_LISP ("default-file-name-coding-system",
@@ -5869,7 +5890,11 @@
This variable is set/changed by the command `set-language-environment'.
User should not set this variable manually,
instead use `file-name-coding-system' to get a constant encoding
-of file names regardless of the current language environment. */);
+of file names regardless of the current language environment.
+
+On MS-Windows, the value of this variable is largely ignored if
+\`w32-unicode-filenames' (which see) is non-nil. Emacs on Windows
+behaves as if file names were encoded in `utf-8'. */);
Vdefault_file_name_coding_system = Qnil;
DEFSYM (Qformat_decode, "format-decode");
=== modified file 'src/filelock.c'
--- a/src/filelock.c 2013-09-23 07:12:01 +0000
+++ b/src/filelock.c 2013-11-02 13:03:32 +0000
@@ -689,7 +689,7 @@
/* Ensure we have only '/' separators, to avoid problems with
looking (inside fill_in_lock_file_name) for backslashes in file
names encoded by some DBCS codepage. */
- dostounix_filename (SSDATA (fn), 1);
+ dostounix_filename (SSDATA (fn));
#endif
encoded_fn = ENCODE_FILE (fn);
=== modified file 'src/gnutls.c'
--- a/src/gnutls.c 2013-11-30 13:31:39 +0000
+++ b/src/gnutls.c 2013-12-07 17:21:57 +0000
@@ -21,6 +21,7 @@
#include "lisp.h"
#include "process.h"
+#include "coding.h"
#ifdef HAVE_GNUTLS
#include <gnutls/gnutls.h>
@@ -899,6 +900,13 @@
{
GNUTLS_LOG2 (1, max_log_level, "setting the trustfile: ",
SSDATA (trustfile));
+ trustfile = ENCODE_FILE (trustfile);
+#ifdef WINDOWSNT
+ /* Since GnuTLS doesn't support UTF-8 or UTF-16 encoded
+ file names on Windows, we need to re-encode the file
+ name using the current ANSI codepage. */
+ trustfile = ansi_encode_filename (trustfile);
+#endif
ret = fn_gnutls_certificate_set_x509_trust_file
(x509_cred,
SSDATA (trustfile),
@@ -921,6 +929,10 @@
{
GNUTLS_LOG2 (1, max_log_level, "setting the CRL file: ",
SSDATA (crlfile));
+ crlfile = ENCODE_FILE (crlfile);
+#ifdef WINDOWSNT
+ crlfile = ansi_encode_filename (crlfile);
+#endif
ret = fn_gnutls_certificate_set_x509_crl_file
(x509_cred, SSDATA (crlfile), file_format);
@@ -944,6 +956,12 @@
SSDATA (keyfile));
GNUTLS_LOG2 (1, max_log_level, "setting the client cert file: ",
SSDATA (certfile));
+ keyfile = ENCODE_FILE (keyfile);
+ certfile = ENCODE_FILE (certfile);
+#ifdef WINDOWSNT
+ keyfile = ansi_encode_filename (keyfile);
+ certfile = ansi_encode_filename (certfile);
+#endif
ret = fn_gnutls_certificate_set_x509_key_file
(x509_cred, SSDATA (certfile), SSDATA (keyfile), file_format);
=== modified file 'src/image.c'
--- a/src/image.c 2013-12-01 14:34:05 +0000
+++ b/src/image.c 2013-12-07 17:21:57 +0000
@@ -3590,6 +3590,12 @@
}
#ifdef HAVE_NTGUI
+#ifdef WINDOWSNT
+ /* FILE is encoded in UTF-8, but image libraries on Windows
+ support neither UTF-8 nor UTF-16 encoded file names. So we
+ need to re-encode it in ANSI. */
+ file = ansi_encode_filename (file);
+#endif
/* XpmReadFileToPixmap is not available in the Windows port of
libxpm. But XpmReadFileToImage almost does what we want. */
rc = fn_XpmReadFileToImage (&hdc, SDATA (file),
@@ -6968,6 +6974,9 @@
image_error ("Cannot find image file `%s'", specified_file, Qnil);
return 0;
}
+#ifdef WINDOWSNT
+ file = ansi_encode_filename (file);
+#endif
/* Try to open the image file. */
tiff = fn_TIFFOpen (SSDATA (file), "r");
@@ -7353,6 +7362,9 @@
image_error ("Cannot find image file `%s'", specified_file, Qnil);
return 0;
}
+#ifdef WINDOWSNT
+ file = ansi_encode_filename (file);
+#endif
/* Open the GIF file. */
#if GIFLIB_MAJOR < 5
@@ -8479,6 +8491,9 @@
image_error ("Cannot find image file `%s'", file_name, Qnil);
return 0;
}
+#ifdef WINDOWSNT
+ file = ansi_encode_filename (file);
+#endif
success_p = imagemagick_load_image (f, img, 0, 0, SSDATA (file));
}
/* Else its not a file, its a lisp object. Load the image from a
=== modified file 'src/msdos.c'
--- a/src/msdos.c 2013-11-04 06:09:03 +0000
+++ b/src/msdos.c 2013-11-18 16:45:48 +0000
@@ -3295,7 +3295,7 @@
/* Destructively turn backslashes into slashes. */
void
-dostounix_filename (char *p, int ignore)
+dostounix_filename (char *p)
{
msdos_downcase_filename (p);
@@ -3559,7 +3559,7 @@
if (!s) s = "c:/command.com";
t = alloca (strlen (s) + 1);
strcpy (t, s);
- dostounix_filename (t, 0);
+ dostounix_filename (t);
setenv ("SHELL", t, 0);
/* PATH is also downcased and backslashes mirrored. */
@@ -3569,7 +3569,7 @@
/* Current directory is always considered part of MsDos's path but it is
not normally mentioned. Now it is. */
strcat (strcpy (t, ".;"), s);
- dostounix_filename (t, 0); /* Not a single file name, but this should work.
*/
+ dostounix_filename (t); /* Not a single file name, but this should work. */
setenv ("PATH", t, 1);
/* In some sense all dos users have root privileges, so... */
=== modified file 'src/msdos.h'
--- a/src/msdos.h 2013-09-13 15:03:51 +0000
+++ b/src/msdos.h 2013-11-02 13:03:32 +0000
@@ -29,7 +29,7 @@
int getdefdir (int, char*);
void unixtodos_filename (char *);
-void dostounix_filename (char *, int);
+void dostounix_filename (char *);
char *rootrelativepath (char *);
void init_environment (int, char **, int);
void internal_terminal_init (void);
=== modified file 'src/sysdep.c'
--- a/src/sysdep.c 2013-10-07 08:05:00 +0000
+++ b/src/sysdep.c 2013-10-26 12:14:33 +0000
@@ -464,7 +464,11 @@
{
#ifdef DOS_NT /* Demacs 1.1.2 91/10/20 Manabu Higashida */
int st;
+#ifdef MSDOS
char oldwd[MAXPATHLEN+1]; /* Fixed length is safe on MSDOS. */
+#else
+ char oldwd[MAX_UTF8_PATH];
+#endif
#endif
pid_t pid;
int status;
=== modified file 'src/termcap.c'
--- a/src/termcap.c 2013-08-11 01:30:20 +0000
+++ b/src/termcap.c 2013-11-02 13:03:32 +0000
@@ -393,7 +393,7 @@
if (termcap_name && (*termcap_name == '\\'
|| *termcap_name == '/'
|| termcap_name[1] == ':'))
- dostounix_filename (termcap_name, 0);
+ dostounix_filename (termcap_name);
#endif
filep = termcap_name && valid_filename_p (termcap_name);
=== modified file 'src/unexw32.c'
--- a/src/unexw32.c 2013-04-16 18:08:03 +0000
+++ b/src/unexw32.c 2013-12-06 15:55:08 +0000
@@ -120,6 +120,8 @@
/* File handling. */
+/* Implementation note: this and the next functions work with ANSI
+ codepage encoded file names! */
int
open_input_file (file_data *p_file, char *filename)
{
@@ -128,8 +130,8 @@
void *file_base;
unsigned long size, upper_size;
- file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
- OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+ file = CreateFileA (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (file == INVALID_HANDLE_VALUE)
return FALSE;
@@ -166,9 +168,9 @@
creating it, all the emacs-XX.YY.ZZ.nn.exe end up being hard
links to the same file, which defeats the purpose of these hard
links: being able to run previous builds. */
- DeleteFile (filename);
- file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
- CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
+ DeleteFileA (filename);
+ file = CreateFileA (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (file == INVALID_HANDLE_VALUE)
return FALSE;
@@ -722,23 +724,30 @@
unexec (const char *new_name, const char *old_name)
{
file_data in_file, out_file;
- char out_filename[MAX_PATH], in_filename[MAX_PATH];
+ char out_filename[MAX_PATH], in_filename[MAX_PATH], new_name_a[MAX_PATH];
unsigned long size;
char *p;
char *q;
/* Ignore old_name, and get our actual location from the OS. */
- if (!GetModuleFileName (NULL, in_filename, MAX_PATH))
+ if (!GetModuleFileNameA (NULL, in_filename, MAX_PATH))
abort ();
- dostounix_filename (in_filename, 0);
+
+ /* Can't use dostounix_filename here, since that needs its file name
+ argument encoded in UTF-8. */
+ for (p = in_filename; *p; p = CharNextA (p))
+ if (*p == '\\')
+ *p = '/';
+
strcpy (out_filename, in_filename);
+ filename_to_ansi (new_name, new_name_a);
/* Change the base of the output filename to match the requested name. */
if ((p = strrchr (out_filename, '/')) == NULL)
abort ();
/* The filenames have already been expanded, and will be in Unix
format, so it is safe to expect an absolute name. */
- if ((q = strrchr (new_name, '/')) == NULL)
+ if ((q = strrchr (new_name_a, '/')) == NULL)
abort ();
strcpy (p, q);
=== modified file 'src/w32.c'
--- a/src/w32.c 2013-11-04 06:09:03 +0000
+++ b/src/w32.c 2013-12-09 20:21:58 +0000
@@ -248,7 +248,7 @@
static int restore_privilege (TOKEN_PRIVILEGES *);
static BOOL WINAPI revert_to_self (void);
-extern int sys_access (const char *, int);
+static int sys_access (const char *, int);
extern void *e_malloc (size_t);
extern int sys_select (int, SELECT_TYPE *, SELECT_TYPE *, SELECT_TYPE *,
struct timespec *, void *);
@@ -273,7 +273,8 @@
static BOOL g_b_init_get_sid_sub_authority;
static BOOL g_b_init_get_sid_sub_authority_count;
static BOOL g_b_init_get_security_info;
-static BOOL g_b_init_get_file_security;
+static BOOL g_b_init_get_file_security_w;
+static BOOL g_b_init_get_file_security_a;
static BOOL g_b_init_get_security_descriptor_owner;
static BOOL g_b_init_get_security_descriptor_group;
static BOOL g_b_init_is_valid_sid;
@@ -292,12 +293,14 @@
static BOOL g_b_init_copy_sid;
static BOOL g_b_init_get_native_system_info;
static BOOL g_b_init_get_system_times;
-static BOOL g_b_init_create_symbolic_link;
+static BOOL g_b_init_create_symbolic_link_w;
+static BOOL g_b_init_create_symbolic_link_a;
static BOOL g_b_init_get_security_descriptor_dacl;
static BOOL g_b_init_convert_sd_to_sddl;
static BOOL g_b_init_convert_sddl_to_sd;
static BOOL g_b_init_is_valid_security_descriptor;
-static BOOL g_b_init_set_file_security;
+static BOOL g_b_init_set_file_security_w;
+static BOOL g_b_init_set_file_security_a;
static BOOL g_b_init_get_adapters_info;
/*
@@ -327,12 +330,8 @@
#ifdef _UNICODE
const char * const LookupAccountSid_Name = "LookupAccountSidW";
-const char * const GetFileSecurity_Name = "GetFileSecurityW";
-const char * const SetFileSecurity_Name = "SetFileSecurityW";
#else
const char * const LookupAccountSid_Name = "LookupAccountSidA";
-const char * const GetFileSecurity_Name = "GetFileSecurityA";
-const char * const SetFileSecurity_Name = "SetFileSecurityA";
#endif
typedef BOOL (WINAPI * LookupAccountSid_Proc) (
LPCTSTR lpSystemName,
@@ -356,14 +355,24 @@
PACL *ppDacl,
PACL *ppSacl,
PSECURITY_DESCRIPTOR *ppSecurityDescriptor);
-typedef BOOL (WINAPI * GetFileSecurity_Proc) (
- LPCTSTR lpFileName,
- SECURITY_INFORMATION RequestedInformation,
- PSECURITY_DESCRIPTOR pSecurityDescriptor,
- DWORD nLength,
- LPDWORD lpnLengthNeeded);
-typedef BOOL (WINAPI *SetFileSecurity_Proc) (
- LPCTSTR lpFileName,
+typedef BOOL (WINAPI * GetFileSecurityW_Proc) (
+ LPCWSTR lpFileName,
+ SECURITY_INFORMATION RequestedInformation,
+ PSECURITY_DESCRIPTOR pSecurityDescriptor,
+ DWORD nLength,
+ LPDWORD lpnLengthNeeded);
+typedef BOOL (WINAPI * GetFileSecurityA_Proc) (
+ LPCSTR lpFileName,
+ SECURITY_INFORMATION RequestedInformation,
+ PSECURITY_DESCRIPTOR pSecurityDescriptor,
+ DWORD nLength,
+ LPDWORD lpnLengthNeeded);
+typedef BOOL (WINAPI *SetFileSecurityW_Proc) (
+ LPCWSTR lpFileName,
+ SECURITY_INFORMATION SecurityInformation,
+ PSECURITY_DESCRIPTOR pSecurityDescriptor);
+typedef BOOL (WINAPI *SetFileSecurityA_Proc) (
+ LPCSTR lpFileName,
SECURITY_INFORMATION SecurityInformation,
PSECURITY_DESCRIPTOR pSecurityDescriptor);
typedef BOOL (WINAPI * GetSecurityDescriptorOwner_Proc) (
@@ -425,9 +434,13 @@
LPFILETIME lpIdleTime,
LPFILETIME lpKernelTime,
LPFILETIME lpUserTime);
-typedef BOOLEAN (WINAPI *CreateSymbolicLink_Proc) (
- LPTSTR lpSymlinkFileName,
- LPTSTR lpTargetFileName,
+typedef BOOLEAN (WINAPI *CreateSymbolicLinkW_Proc) (
+ LPCWSTR lpSymlinkFileName,
+ LPCWSTR lpTargetFileName,
+ DWORD dwFlags);
+typedef BOOLEAN (WINAPI *CreateSymbolicLinkA_Proc) (
+ LPCSTR lpSymlinkFileName,
+ LPCSTR lpTargetFileName,
DWORD dwFlags);
typedef BOOL (WINAPI
*ConvertStringSecurityDescriptorToSecurityDescriptor_Proc) (
LPCTSTR StringSecurityDescriptor,
@@ -679,64 +692,121 @@
}
static BOOL WINAPI
-get_file_security (LPCTSTR lpFileName,
+get_file_security (const char *lpFileName,
SECURITY_INFORMATION RequestedInformation,
PSECURITY_DESCRIPTOR pSecurityDescriptor,
DWORD nLength,
LPDWORD lpnLengthNeeded)
{
- static GetFileSecurity_Proc s_pfn_Get_File_Security = NULL;
+ static GetFileSecurityA_Proc s_pfn_Get_File_SecurityA = NULL;
+ static GetFileSecurityW_Proc s_pfn_Get_File_SecurityW = NULL;
HMODULE hm_advapi32 = NULL;
if (is_windows_9x () == TRUE)
{
errno = ENOTSUP;
return FALSE;
}
- if (g_b_init_get_file_security == 0)
- {
- g_b_init_get_file_security = 1;
- hm_advapi32 = LoadLibrary ("Advapi32.dll");
- s_pfn_Get_File_Security =
- (GetFileSecurity_Proc) GetProcAddress (
- hm_advapi32, GetFileSecurity_Name);
- }
- if (s_pfn_Get_File_Security == NULL)
- {
- errno = ENOTSUP;
- return FALSE;
- }
- return (s_pfn_Get_File_Security (lpFileName, RequestedInformation,
- pSecurityDescriptor, nLength,
- lpnLengthNeeded));
+ if (w32_unicode_filenames)
+ {
+ wchar_t filename_w[MAX_PATH];
+
+ if (g_b_init_get_file_security_w == 0)
+ {
+ g_b_init_get_file_security_w = 1;
+ hm_advapi32 = LoadLibrary ("Advapi32.dll");
+ s_pfn_Get_File_SecurityW =
+ (GetFileSecurityW_Proc) GetProcAddress (hm_advapi32,
+ "GetFileSecurityW");
+ }
+ if (s_pfn_Get_File_SecurityW == NULL)
+ {
+ errno = ENOTSUP;
+ return FALSE;
+ }
+ filename_to_utf16 (lpFileName, filename_w);
+ return (s_pfn_Get_File_SecurityW (filename_w, RequestedInformation,
+ pSecurityDescriptor, nLength,
+ lpnLengthNeeded));
+ }
+ else
+ {
+ char filename_a[MAX_PATH];
+
+ if (g_b_init_get_file_security_a == 0)
+ {
+ g_b_init_get_file_security_a = 1;
+ hm_advapi32 = LoadLibrary ("Advapi32.dll");
+ s_pfn_Get_File_SecurityA =
+ (GetFileSecurityA_Proc) GetProcAddress (hm_advapi32,
+ "GetFileSecurityA");
+ }
+ if (s_pfn_Get_File_SecurityA == NULL)
+ {
+ errno = ENOTSUP;
+ return FALSE;
+ }
+ filename_to_ansi (lpFileName, filename_a);
+ return (s_pfn_Get_File_SecurityA (filename_a, RequestedInformation,
+ pSecurityDescriptor, nLength,
+ lpnLengthNeeded));
+ }
}
static BOOL WINAPI
-set_file_security (LPCTSTR lpFileName,
+set_file_security (const char *lpFileName,
SECURITY_INFORMATION SecurityInformation,
PSECURITY_DESCRIPTOR pSecurityDescriptor)
{
- static SetFileSecurity_Proc s_pfn_Set_File_Security = NULL;
+ static SetFileSecurityW_Proc s_pfn_Set_File_SecurityW = NULL;
+ static SetFileSecurityA_Proc s_pfn_Set_File_SecurityA = NULL;
HMODULE hm_advapi32 = NULL;
if (is_windows_9x () == TRUE)
{
errno = ENOTSUP;
return FALSE;
}
- if (g_b_init_set_file_security == 0)
- {
- g_b_init_set_file_security = 1;
- hm_advapi32 = LoadLibrary ("Advapi32.dll");
- s_pfn_Set_File_Security =
- (SetFileSecurity_Proc) GetProcAddress (
- hm_advapi32, SetFileSecurity_Name);
- }
- if (s_pfn_Set_File_Security == NULL)
- {
- errno = ENOTSUP;
- return FALSE;
- }
- return (s_pfn_Set_File_Security (lpFileName, SecurityInformation,
- pSecurityDescriptor));
+ if (w32_unicode_filenames)
+ {
+ wchar_t filename_w[MAX_PATH];
+
+ if (g_b_init_set_file_security_w == 0)
+ {
+ g_b_init_set_file_security_w = 1;
+ hm_advapi32 = LoadLibrary ("Advapi32.dll");
+ s_pfn_Set_File_SecurityW =
+ (SetFileSecurityW_Proc) GetProcAddress (hm_advapi32,
+ "SetFileSecurityW");
+ }
+ if (s_pfn_Set_File_SecurityW == NULL)
+ {
+ errno = ENOTSUP;
+ return FALSE;
+ }
+ filename_to_utf16 (lpFileName, filename_w);
+ return (s_pfn_Set_File_SecurityW (filename_w, SecurityInformation,
+ pSecurityDescriptor));
+ }
+ else
+ {
+ char filename_a[MAX_PATH];
+
+ if (g_b_init_set_file_security_a == 0)
+ {
+ g_b_init_set_file_security_a = 1;
+ hm_advapi32 = LoadLibrary ("Advapi32.dll");
+ s_pfn_Set_File_SecurityA =
+ (SetFileSecurityA_Proc) GetProcAddress (hm_advapi32,
+ "SetFileSecurityA");
+ }
+ if (s_pfn_Set_File_SecurityA == NULL)
+ {
+ errno = ENOTSUP;
+ return FALSE;
+ }
+ filename_to_ansi (lpFileName, filename_a);
+ return (s_pfn_Set_File_SecurityA (filename_a, SecurityInformation,
+ pSecurityDescriptor));
+ }
}
static BOOL WINAPI
@@ -973,11 +1043,12 @@
}
static BOOLEAN WINAPI
-create_symbolic_link (LPTSTR lpSymlinkFilename,
- LPTSTR lpTargetFileName,
+create_symbolic_link (LPCSTR lpSymlinkFilename,
+ LPCSTR lpTargetFileName,
DWORD dwFlags)
{
- static CreateSymbolicLink_Proc s_pfn_Create_Symbolic_Link = NULL;
+ static CreateSymbolicLinkW_Proc s_pfn_Create_Symbolic_LinkW = NULL;
+ static CreateSymbolicLinkA_Proc s_pfn_Create_Symbolic_LinkA = NULL;
BOOLEAN retval;
if (is_windows_9x () == TRUE)
@@ -985,39 +1056,74 @@
errno = ENOSYS;
return 0;
}
- if (g_b_init_create_symbolic_link == 0)
- {
- g_b_init_create_symbolic_link = 1;
-#ifdef _UNICODE
- s_pfn_Create_Symbolic_Link =
- (CreateSymbolicLink_Proc)GetProcAddress (GetModuleHandle
("kernel32.dll"),
- "CreateSymbolicLinkW");
-#else
- s_pfn_Create_Symbolic_Link =
- (CreateSymbolicLink_Proc)GetProcAddress (GetModuleHandle
("kernel32.dll"),
- "CreateSymbolicLinkA");
-#endif
- }
- if (s_pfn_Create_Symbolic_Link == NULL)
- {
- errno = ENOSYS;
- return 0;
- }
-
- retval = s_pfn_Create_Symbolic_Link (lpSymlinkFilename, lpTargetFileName,
- dwFlags);
- /* If we were denied creation of the symlink, try again after
- enabling the SeCreateSymbolicLinkPrivilege for our process. */
- if (!retval)
- {
- TOKEN_PRIVILEGES priv_current;
-
- if (enable_privilege (SE_CREATE_SYMBOLIC_LINK_NAME, TRUE, &priv_current))
- {
- retval = s_pfn_Create_Symbolic_Link (lpSymlinkFilename,
lpTargetFileName,
- dwFlags);
- restore_privilege (&priv_current);
- revert_to_self ();
+ if (w32_unicode_filenames)
+ {
+ wchar_t symfn_w[MAX_PATH], tgtfn_w[MAX_PATH];
+
+ if (g_b_init_create_symbolic_link_w == 0)
+ {
+ g_b_init_create_symbolic_link_w = 1;
+ s_pfn_Create_Symbolic_LinkW =
+ (CreateSymbolicLinkW_Proc)GetProcAddress (GetModuleHandle
("kernel32.dll"),
+ "CreateSymbolicLinkW");
+ }
+ if (s_pfn_Create_Symbolic_LinkW == NULL)
+ {
+ errno = ENOSYS;
+ return 0;
+ }
+
+ filename_to_utf16 (lpSymlinkFilename, symfn_w);
+ filename_to_utf16 (lpTargetFileName, tgtfn_w);
+ retval = s_pfn_Create_Symbolic_LinkW (symfn_w, tgtfn_w, dwFlags);
+ /* If we were denied creation of the symlink, try again after
+ enabling the SeCreateSymbolicLinkPrivilege for our process. */
+ if (!retval)
+ {
+ TOKEN_PRIVILEGES priv_current;
+
+ if (enable_privilege (SE_CREATE_SYMBOLIC_LINK_NAME, TRUE,
+ &priv_current))
+ {
+ retval = s_pfn_Create_Symbolic_LinkW (symfn_w, tgtfn_w, dwFlags);
+ restore_privilege (&priv_current);
+ revert_to_self ();
+ }
+ }
+ }
+ else
+ {
+ char symfn_a[MAX_PATH], tgtfn_a[MAX_PATH];
+
+ if (g_b_init_create_symbolic_link_a == 0)
+ {
+ g_b_init_create_symbolic_link_a = 1;
+ s_pfn_Create_Symbolic_LinkA =
+ (CreateSymbolicLinkA_Proc)GetProcAddress (GetModuleHandle
("kernel32.dll"),
+ "CreateSymbolicLinkA");
+ }
+ if (s_pfn_Create_Symbolic_LinkA == NULL)
+ {
+ errno = ENOSYS;
+ return 0;
+ }
+
+ filename_to_ansi (lpSymlinkFilename, symfn_a);
+ filename_to_ansi (lpTargetFileName, tgtfn_a);
+ retval = s_pfn_Create_Symbolic_LinkA (symfn_a, tgtfn_a, dwFlags);
+ /* If we were denied creation of the symlink, try again after
+ enabling the SeCreateSymbolicLinkPrivilege for our process. */
+ if (!retval)
+ {
+ TOKEN_PRIVILEGES priv_current;
+
+ if (enable_privilege (SE_CREATE_SYMBOLIC_LINK_NAME, TRUE,
+ &priv_current))
+ {
+ retval = s_pfn_Create_Symbolic_LinkA (symfn_a, tgtfn_a, dwFlags);
+ restore_privilege (&priv_current);
+ revert_to_self ();
+ }
}
}
return retval;
@@ -1182,7 +1288,297 @@
return -1;
}
-static char startup_dir[MAXPATHLEN];
+
+
+/* Here's an overview of how the Windows build supports file names
+ that cannot be encoded by the current system codepage.
+
+ From the POV of Lisp and layers of C code above the functions here,
+ Emacs on Windows pretends that its file names are encoded in UTF-8;
+ see encode_file and decode_file on coding.c. Any file name that is
+ passed as a unibyte string to C functions defined here is assumed
+ to be in UTF-8 encoding. Any file name returned by functions
+ defined here must be in UTF-8 encoding, with only a few exceptions
+ reserved for a couple of special cases. (Be sure to use
+ MAX_UTF8_PATH for char arrays that store UTF-8 encoded file names,
+ as they can be much longer than MAX_PATH!)
+
+ The UTF-8 encoded file names cannot be passed to system APIs, as
+ Windows does not support that. Therefore, they are converted
+ either to UTF-16 or to the ANSI codepage, depending on the value of
+ w32-unicode-filenames, before calling any system APIs or CRT library
+ functions. The default value of that variable is determined by the
+ OS on which Emacs runs: nil on Windows 9X and t otherwise, but the
+ user can change that default (although I don't see why would she
+ want to).
+
+ The 4 functions defined below, filename_to_utf16, filename_to_ansi,
+ filename_from_utf16, and filename_from_ansi, are the workhorses of
+ these conversions. They rely on Windows native APIs
+ MultiByteToWideChar and WideCharToMultiByte; we cannot use
+ functions from coding.c here, because they allocate memory, which
+ is a bad idea on the level of libc, which is what the functions
+ here emulate. (If you worry about performance due to constant
+ conversion back and forth from UTF-8 to UTF-16, then don't: first,
+ it was measured to take only a few microseconds on a not-so-fast
+ machine, and second, that's exactly what the ANSI APIs we used
+ before do anyway, because they are just thin wrappers around the
+ Unicode APIs.)
+
+ The variables file-name-coding-system and default-file-name-coding-system
+ still exist, but are actually used only when a file name needs to
+ be converted to the ANSI codepage. This happens all the time when
+ w32-unicode-filenames is nil, but can also happen from time to time
+ when it is t. Otherwise, these variables have no effect on file-name
+ encoding when w32-unicode-filenames is t; this is similar to
+ selection-coding-system.
+
+ This arrangement works very well, but it has a few gotchas and
+ limitations:
+
+ . Lisp code that encodes or decodes file names manually should
+ normally use 'utf-8' as the coding-system on Windows,
+ disregarding file-name-coding-system. This is a somewhat
+ unpleasant consequence, but it cannot be avoided. Fortunately,
+ very few Lisp packages need to do that.
+
+ More generally, passing to library functions (e.g., fopen or
+ opendir) file names already encoded in the ANSI codepage is
+ explictly *verboten*, as all those functions, as shadowed and
+ emulated here, assume they will receive UTF-8 encoded file names.
+
+ For the same reasons, no CRT function or Win32 API can be called
+ directly in Emacs sources, without either converting the file
+ name sfrom UTF-8 to either UTF-16 or ANSI codepage, or going
+ through some shadowing function defined here.
+
+ . Environment variables stored in Vprocess_environment are encoded
+ in the ANSI codepage, so if getenv/egetenv is used for a variable
+ whose value is a file name or a list of directories, it needs to
+ be converted to UTF-8, before it is used as argument to functions
+ or decoded into a Lisp string.
+
+ . File names passed to external libraries, like the image libraries
+ and GnuTLS, need special handling. These libraries generally
+ don't support UTF-16 or UTF-8 file names, so they must get file
+ names encoded in the ANSI codepage. To facilitate using these
+ libraries with file names that are not encodable in the ANSI
+ codepage, use the function ansi_encode_filename, which will try
+ to use the short 8+3 alias of a file name if that file name is
+ not encodable in the ANSI codepage. See image.c and gnutls.c for
+ examples of how this should be done.
+
+ . Running subprocesses in non-ASCII directories and with non-ASCII
+ file arguments is limited to the current codepage (even though
+ Emacs is perfectly capable of finding an executable program file
+ even in a directory whose name cannot be encoded in the curreent
+ codepage). This is because the command-line arguments are
+ encoded _before_ they get to the w32-specific level, and the
+ encoding is not known in advance (it doesn't have to be the
+ current ANSI codepage), so w32proc.c functions cannot re-encode
+ them in UTF-16. This should be fixed, but will also require
+ changes in cmdproxy. The current limitation is not terribly bad
+ anyway, since very few, if any, Windows console programs that are
+ likely to be invoked by Emacs support UTF-16 encoded command
+ lines.
+
+ . For similar reasons, server.el and emacsclient are also limited
+ to the current ANSI codepage for now.
+
+ . Emacs itself can only handle command-line arguments encoded in
+ the current codepage.
+
+ . Turning on w32-unicode-filename on Windows 9X (if it at all
+ works) requires UNICOWS.DLL, which is currently loaded only in a
+ GUI session. */
+
+
+
+/* Converting file names from UTF-8 to either UTF-16 or the ANSI
+ codepage defined by file-name-coding-system. */
+
+/* Current codepage for encoding file names. */
+static int file_name_codepage;
+
+/* Produce a Windows ANSI codepage suitable for encoding file names.
+ Return the information about that codepage in CP_INFO. */
+static int
+codepage_for_filenames (CPINFO *cp_info)
+{
+ /* A simple cache to avoid calling GetCPInfo every time we need to
+ encode/decode a file name. The file-name encoding is not
+ supposed to be changed too frequently, if ever. */
+ static Lisp_Object last_file_name_encoding;
+ static CPINFO cp;
+ Lisp_Object current_encoding;
+
+ current_encoding = Vfile_name_coding_system;
+ if (NILP (current_encoding))
+ current_encoding = Vdefault_file_name_coding_system;
+
+ if (!EQ (last_file_name_encoding, current_encoding))
+ {
+ /* Default to the current ANSI codepage. */
+ file_name_codepage = w32_ansi_code_page;
+
+ if (NILP (current_encoding))
+ {
+ char *cpname = SDATA (SYMBOL_NAME (current_encoding));
+ char *cp = NULL, *end;
+ int cpnum;
+
+ if (strncmp (cpname, "cp", 2) == 0)
+ cp = cpname + 2;
+ else if (strncmp (cpname, "windows-", 8) == 0)
+ cp = cpname + 8;
+
+ if (cp)
+ {
+ end = cp;
+ cpnum = strtol (cp, &end, 10);
+ if (cpnum && *end == '\0' && end - cp >= 2)
+ file_name_codepage = cpnum;
+ }
+ }
+
+ if (!file_name_codepage)
+ file_name_codepage = CP_ACP; /* CP_ACP = 0, but let's not assume that */
+
+ if (!GetCPInfo (file_name_codepage, &cp))
+ {
+ file_name_codepage = CP_ACP;
+ if (!GetCPInfo (file_name_codepage, &cp))
+ emacs_abort ();
+ }
+ }
+ if (cp_info)
+ *cp_info = cp;
+
+ return file_name_codepage;
+}
+
+int
+filename_to_utf16 (const char *fn_in, wchar_t *fn_out)
+{
+ int result = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, fn_in, -1,
+ fn_out, MAX_PATH);
+
+ if (!result)
+ {
+ DWORD err = GetLastError ();
+
+ switch (err)
+ {
+ case ERROR_INVALID_FLAGS:
+ case ERROR_INVALID_PARAMETER:
+ errno = EINVAL;
+ break;
+ case ERROR_INSUFFICIENT_BUFFER:
+ case ERROR_NO_UNICODE_TRANSLATION:
+ default:
+ errno = ENOENT;
+ break;
+ }
+ return -1;
+ }
+ return 0;
+}
+
+int
+filename_from_utf16 (const wchar_t *fn_in, char *fn_out)
+{
+ int result = WideCharToMultiByte (CP_UTF8, 0, fn_in, -1,
+ fn_out, MAX_UTF8_PATH, NULL, NULL);
+
+ if (!result)
+ {
+ DWORD err = GetLastError ();
+
+ switch (err)
+ {
+ case ERROR_INVALID_FLAGS:
+ case ERROR_INVALID_PARAMETER:
+ errno = EINVAL;
+ break;
+ case ERROR_INSUFFICIENT_BUFFER:
+ case ERROR_NO_UNICODE_TRANSLATION:
+ default:
+ errno = ENOENT;
+ break;
+ }
+ return -1;
+ }
+ return 0;
+}
+
+int
+filename_to_ansi (const char *fn_in, char *fn_out)
+{
+ wchar_t fn_utf16[MAX_PATH];
+
+ if (filename_to_utf16 (fn_in, fn_utf16) == 0)
+ {
+ int result;
+ int codepage = codepage_for_filenames (NULL);
+
+ result = WideCharToMultiByte (codepage, 0, fn_utf16, -1,
+ fn_out, MAX_PATH, NULL, NULL);
+ if (!result)
+ {
+ DWORD err = GetLastError ();
+
+ switch (err)
+ {
+ case ERROR_INVALID_FLAGS:
+ case ERROR_INVALID_PARAMETER:
+ errno = EINVAL;
+ break;
+ case ERROR_INSUFFICIENT_BUFFER:
+ case ERROR_NO_UNICODE_TRANSLATION:
+ default:
+ errno = ENOENT;
+ break;
+ }
+ return -1;
+ }
+ return 0;
+ }
+ return -1;
+}
+
+int
+filename_from_ansi (const char *fn_in, char *fn_out)
+{
+ wchar_t fn_utf16[MAX_PATH];
+ int codepage = codepage_for_filenames (NULL);
+ int result = MultiByteToWideChar (codepage, MB_ERR_INVALID_CHARS, fn_in, -1,
+ fn_utf16, MAX_PATH);
+
+ if (!result)
+ {
+ DWORD err = GetLastError ();
+
+ switch (err)
+ {
+ case ERROR_INVALID_FLAGS:
+ case ERROR_INVALID_PARAMETER:
+ errno = EINVAL;
+ break;
+ case ERROR_INSUFFICIENT_BUFFER:
+ case ERROR_NO_UNICODE_TRANSLATION:
+ default:
+ errno = ENOENT;
+ break;
+ }
+ return -1;
+ }
+ return filename_from_utf16 (fn_utf16, fn_out);
+}
+
+
+
+/* The directory where we started, in UTF-8. */
+static char startup_dir[MAX_UTF8_PATH];
/* Get the current working directory. */
char *
@@ -1374,8 +1770,8 @@
static char dflt_passwd_name[PASSWD_FIELD_SIZE];
static char dflt_passwd_passwd[PASSWD_FIELD_SIZE];
static char dflt_passwd_gecos[PASSWD_FIELD_SIZE];
-static char dflt_passwd_dir[PASSWD_FIELD_SIZE];
-static char dflt_passwd_shell[PASSWD_FIELD_SIZE];
+static char dflt_passwd_dir[MAX_UTF8_PATH];
+static char dflt_passwd_shell[MAX_UTF8_PATH];
static struct passwd dflt_passwd =
{
@@ -1556,15 +1952,32 @@
}
dflt_group.gr_gid = dflt_passwd.pw_gid;
- /* Ensure HOME and SHELL are defined. */
- if (getenv ("HOME") == NULL)
- emacs_abort ();
- if (getenv ("SHELL") == NULL)
- emacs_abort ();
-
/* Set dir and shell from environment variables. */
- strcpy (dflt_passwd.pw_dir, getenv ("HOME"));
- strcpy (dflt_passwd.pw_shell, getenv ("SHELL"));
+ if (w32_unicode_filenames)
+ {
+ wchar_t *home = _wgetenv (L"HOME");
+ wchar_t *shell = _wgetenv (L"SHELL");
+
+ /* Ensure HOME and SHELL are defined. */
+ if (home == NULL)
+ emacs_abort ();
+ if (shell == NULL)
+ emacs_abort ();
+ filename_from_utf16 (home, dflt_passwd.pw_dir);
+ filename_from_utf16 (shell, dflt_passwd.pw_shell);
+ }
+ else
+ {
+ char *home = getenv ("HOME");
+ char *shell = getenv ("SHELL");
+
+ if (home == NULL)
+ emacs_abort ();
+ if (shell == NULL)
+ emacs_abort ();
+ filename_from_ansi (home, dflt_passwd.pw_dir);
+ filename_from_ansi (shell, dflt_passwd.pw_shell);
+ }
xfree (buf);
if (token)
@@ -1584,93 +1997,32 @@
srand (seed);
}
-/* Current codepage for encoding file names. */
-static int file_name_codepage;
-
/* Return the maximum length in bytes of a multibyte character
sequence encoded in the current ANSI codepage. This is required to
correctly walk the encoded file names one character at a time. */
static int
max_filename_mbslen (void)
{
- /* A simple cache to avoid calling GetCPInfo every time we need to
- normalize a file name. The file-name encoding is not supposed to
- be changed too frequently, if ever. */
- static Lisp_Object last_file_name_encoding;
- static int last_max_mbslen;
- Lisp_Object current_encoding;
-
- current_encoding = Vfile_name_coding_system;
- if (NILP (current_encoding))
- current_encoding = Vdefault_file_name_coding_system;
-
- if (!EQ (last_file_name_encoding, current_encoding))
- {
- CPINFO cp_info;
-
- last_file_name_encoding = current_encoding;
- /* Default to the current ANSI codepage. */
- file_name_codepage = w32_ansi_code_page;
- if (!NILP (current_encoding))
- {
- char *cpname = SDATA (SYMBOL_NAME (current_encoding));
- char *cp = NULL, *end;
- int cpnum;
-
- if (strncmp (cpname, "cp", 2) == 0)
- cp = cpname + 2;
- else if (strncmp (cpname, "windows-", 8) == 0)
- cp = cpname + 8;
-
- if (cp)
- {
- end = cp;
- cpnum = strtol (cp, &end, 10);
- if (cpnum && *end == '\0' && end - cp >= 2)
- file_name_codepage = cpnum;
- }
- }
-
- if (!file_name_codepage)
- file_name_codepage = CP_ACP; /* CP_ACP = 0, but let's not assume that */
-
- if (!GetCPInfo (file_name_codepage, &cp_info))
- {
- file_name_codepage = CP_ACP;
- if (!GetCPInfo (file_name_codepage, &cp_info))
- emacs_abort ();
- }
- last_max_mbslen = cp_info.MaxCharSize;
- }
-
- return last_max_mbslen;
+ CPINFO cp_info;
+
+ codepage_for_filenames (&cp_info);
+ return cp_info.MaxCharSize;
}
-/* Normalize filename by converting all path separators to
- the specified separator. Also conditionally convert upper
- case path name components to lower case. */
+/* Normalize filename by converting in-place all of its path
+ separators to the separator specified by PATH_SEP. */
static void
-normalize_filename (register char *fp, char path_sep, int multibyte)
+normalize_filename (register char *fp, char path_sep)
{
- char sep;
- char *elem, *p2;
- int dbcs_p = max_filename_mbslen () > 1;
-
- /* Multibyte file names are in the Emacs internal representation, so
- we can traverse them by bytes with no problems. */
- if (multibyte)
- dbcs_p = 0;
+ char *p2;
/* Always lower-case drive letters a-z, even if the filesystem
preserves case in filenames.
This is so filenames can be compared by string comparison
functions that are case-sensitive. Even case-preserving filesystems
do not distinguish case in drive letters. */
- if (dbcs_p)
- p2 = CharNextExA (file_name_codepage, fp, 0);
- else
- p2 = fp + 1;
+ p2 = fp + 1;
if (*p2 == ':' && *fp >= 'A' && *fp <= 'Z')
{
@@ -1678,68 +2030,26 @@
fp += 2;
}
- if (multibyte || NILP (Vw32_downcase_file_names))
+ while (*fp)
{
- while (*fp)
- {
- if (*fp == '/' || *fp == '\\')
- *fp = path_sep;
- if (!dbcs_p)
- fp++;
- else
- fp = CharNextExA (file_name_codepage, fp, 0);
- }
- return;
+ if ((*fp == '/' || *fp == '\\') && *fp != path_sep)
+ *fp = path_sep;
+ fp++;
}
-
- sep = path_sep; /* convert to this path separator */
- elem = fp; /* start of current path element */
-
- do {
- if (*fp >= 'a' && *fp <= 'z')
- elem = 0; /* don't convert this element */
-
- if (*fp == 0 || *fp == ':')
- {
- sep = *fp; /* restore current separator (or 0) */
- *fp = '/'; /* after conversion of this element */
- }
-
- if (*fp == '/' || *fp == '\\')
- {
- if (elem && elem != fp)
- {
- *fp = 0; /* temporary end of string */
- _mbslwr (elem); /* while we convert to lower case */
- }
- *fp = sep; /* convert (or restore) path separator */
- elem = fp + 1; /* next element starts after separator */
- sep = path_sep;
- }
- if (*fp)
- {
- if (!dbcs_p)
- fp++;
- else
- fp = CharNextExA (file_name_codepage, fp, 0);
- }
- } while (*fp);
}
-/* Destructively turn backslashes into slashes. MULTIBYTE non-zero
- means the file name is a multibyte string in Emacs's internal
- representation. */
+/* Destructively turn backslashes into slashes. */
void
-dostounix_filename (register char *p, int multibyte)
+dostounix_filename (register char *p)
{
- normalize_filename (p, '/', multibyte);
+ normalize_filename (p, '/');
}
/* Destructively turn slashes into backslashes. */
void
unixtodos_filename (register char *p)
{
- normalize_filename (p, '\\', 0);
+ normalize_filename (p, '\\');
}
/* Remove all CR's that are followed by a LF.
@@ -1772,9 +2082,9 @@
/* Parse the root part of file name, if present. Return length and
optionally store pointer to char after root. */
static int
-parse_root (char * name, char ** pPath)
+parse_root (const char * name, const char ** pPath)
{
- char * start = name;
+ const char * start = name;
if (name == NULL)
return 0;
@@ -1790,17 +2100,13 @@
else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
{
int slashes = 2;
- int dbcs_p = max_filename_mbslen () > 1;
name += 2;
do
{
if (IS_DIRECTORY_SEP (*name) && --slashes == 0)
break;
- if (dbcs_p)
- name = CharNextExA (file_name_codepage, name, 0);
- else
- name++;
+ name++;
}
while ( *name );
if (IS_DIRECTORY_SEP (name[0]))
@@ -1817,23 +2123,63 @@
static int
get_long_basename (char * name, char * buf, int size)
{
- WIN32_FIND_DATA find_data;
- HANDLE dir_handle;
+ HANDLE dir_handle = INVALID_HANDLE_VALUE;
+ char fname_utf8[MAX_UTF8_PATH];
int len = 0;
+ int cstatus = -1;
- /* must be valid filename, no wild cards or other invalid characters */
- if (_mbspbrk (name, "*?|<>\""))
+ /* Must be valid filename, no wild cards or other invalid characters. */
+ if (strpbrk (name, "*?|<>\""))
return 0;
- dir_handle = FindFirstFile (name, &find_data);
+ if (w32_unicode_filenames)
+ {
+ wchar_t fname_utf16[MAX_PATH];
+ WIN32_FIND_DATAW find_data_wide;
+
+ filename_to_utf16 (name, fname_utf16);
+ dir_handle = FindFirstFileW (fname_utf16, &find_data_wide);
+ if (dir_handle != INVALID_HANDLE_VALUE)
+ cstatus = filename_from_utf16 (find_data_wide.cFileName, fname_utf8);
+ }
+ else
+ {
+ char fname_ansi[MAX_PATH];
+ WIN32_FIND_DATAA find_data_ansi;
+
+ filename_to_ansi (name, fname_ansi);
+ /* If the ANSI name includes ? characters, it is not encodable
+ in the ANSI codepage. In that case, we deliver the question
+ marks to the caller; calling FindFirstFileA in this case
+ could return some unrelated file name in the same
+ directory. */
+ if (_mbspbrk (fname_ansi, "?"))
+ {
+ /* Find the basename of fname_ansi. */
+ char *p = strrchr (fname_ansi, '\\');
+
+ if (!p)
+ p = fname_ansi;
+ else
+ p++;
+ cstatus = filename_from_ansi (p, fname_utf8);
+ }
+ else
+ {
+ dir_handle = FindFirstFileA (fname_ansi, &find_data_ansi);
+ if (dir_handle != INVALID_HANDLE_VALUE)
+ cstatus = filename_from_ansi (find_data_ansi.cFileName, fname_utf8);
+ }
+ }
+
+ if (cstatus == 0 && (len = strlen (fname_utf8)) < size)
+ memcpy (buf, fname_utf8, len + 1);
+ else
+ len = 0;
+
if (dir_handle != INVALID_HANDLE_VALUE)
- {
- if ((len = strlen (find_data.cFileName)) < size)
- memcpy (buf, find_data.cFileName, len + 1);
- else
- len = 0;
- FindClose (dir_handle);
- }
+ FindClose (dir_handle);
+
return len;
}
@@ -1843,12 +2189,12 @@
{
char * o = buf;
char * p;
- char * q;
- char full[ MAX_PATH ];
+ const char * q;
+ char full[ MAX_UTF8_PATH ];
int len;
len = strlen (name);
- if (len >= MAX_PATH)
+ if (len >= MAX_UTF8_PATH)
return FALSE;
/* Use local copy for destructive modification. */
@@ -1856,7 +2202,7 @@
unixtodos_filename (full);
/* Copy root part verbatim. */
- len = parse_root (full, &p);
+ len = parse_root (full, (const char **)&p);
memcpy (o, full, len);
o += len;
*o = '\0';
@@ -1865,7 +2211,7 @@
while (p != NULL && *p)
{
q = p;
- p = _mbschr (q, '\\');
+ p = strchr (q, '\\');
if (p) *p = '\0';
len = get_long_basename (full, o, size);
if (len > 0)
@@ -1889,6 +2235,59 @@
return TRUE;
}
+unsigned int
+w32_get_short_filename (char * name, char * buf, int size)
+{
+ if (w32_unicode_filenames)
+ {
+ wchar_t name_utf16[MAX_PATH], short_name[MAX_PATH];
+ unsigned int retval;
+
+ filename_to_utf16 (name, name_utf16);
+ retval = GetShortPathNameW (name_utf16, short_name, size);
+ if (retval && retval < size)
+ filename_from_utf16 (short_name, buf);
+ return retval;
+ }
+ else
+ {
+ char name_ansi[MAX_PATH];
+
+ filename_to_ansi (name, name_ansi);
+ return GetShortPathNameA (name_ansi, buf, size);
+ }
+}
+
+/* Re-encode FILENAME, a UTF-8 encoded unibyte string, using the
+ MS-Windows ANSI codepage. If FILENAME includes characters not
+ supported by the ANSI codepage, return the 8+3 alias of FILENAME,
+ if it exists. This is needed because the w32 build wants to
+ support file names outside of the system locale, but image
+ libraries typically don't support wide (a.k.a. "Unicode") APIs
+ required for that. */
+
+Lisp_Object
+ansi_encode_filename (Lisp_Object filename)
+{
+ Lisp_Object encoded_filename;
+ char fname[MAX_PATH];
+
+ filename_to_ansi (SSDATA (filename), fname);
+ if (_mbspbrk (fname, "?"))
+ {
+ char shortname[MAX_PATH];
+
+ if (w32_get_short_filename (SDATA (filename), shortname, MAX_PATH))
+ {
+ dostounix_filename (shortname);
+ encoded_filename = build_string (shortname);
+ }
+ }
+ else
+ encoded_filename = build_unibyte_string (fname);
+ return encoded_filename;
+}
+
static int
is_unc_volume (const char *filename)
{
@@ -1897,7 +2296,7 @@
if (!IS_DIRECTORY_SEP (ptr[0]) || !IS_DIRECTORY_SEP (ptr[1]) || !ptr[2])
return 0;
- if (_mbspbrk (ptr + 2, "*?|<>\"\\/"))
+ if (strpbrk (ptr + 2, "*?|<>\"\\/"))
return 0;
return 1;
@@ -1997,8 +2396,8 @@
return (NULL);
}
-char *get_emacs_configuration (void);
-
+/* The argv[] array holds ANSI-encoded strings, and so this function
+ works with ANS_encoded strings. */
void
init_environment (char ** argv)
{
@@ -2010,6 +2409,13 @@
const int imax = sizeof (tempdirs) / sizeof (tempdirs[0]);
+ /* Implementation note: This function explicitly works with ANSI
+ file names, not with UTF-8 encoded file names. This is because
+ this function pushes variables into the Emacs's environment, and
+ the environment variables are always assumed to be in the
+ locale-specific encoding. Do NOT call any functions that accept
+ UTF-8 file names from this function! */
+
/* Make sure they have a usable $TMPDIR. Many Emacs functions use
temporary files and assume "/tmp" if $TMPDIR is unset, which
will break on DOS/Windows. Refuse to work if we cannot find
@@ -2025,8 +2431,8 @@
The only way to be really sure is to actually create a file and
see if it succeeds. But I think that's too much to ask. */
- /* MSVCRT's _access crashes with D_OK. */
- if (tmp && faccessat (AT_FDCWD, tmp, D_OK, AT_EACCESS) == 0)
+ /* MSVCRT's _access crashes with D_OK, so we use our replacement. */
+ if (tmp && sys_access (tmp, D_OK) == 0)
{
char * var = alloca (strlen (tmp) + 8);
sprintf (var, "TMPDIR=%s", tmp);
@@ -2088,7 +2494,7 @@
/* For backwards compatibility, check if a .emacs file exists in C:/
If not, then we can try to default to the appdata directory under the
user's profile, which is more likely to be writable. */
- if (faccessat (AT_FDCWD, "C:/.emacs", F_OK, AT_EACCESS) != 0)
+ if (sys_access ("C:/.emacs", F_OK) != 0)
{
HRESULT profile_result;
/* Dynamically load ShGetFolderPath, as it won't exist on versions
@@ -2135,7 +2541,7 @@
char *p;
char modname[MAX_PATH];
- if (!GetModuleFileName (NULL, modname, MAX_PATH))
+ if (!GetModuleFileNameA (NULL, modname, MAX_PATH))
emacs_abort ();
if ((p = _mbsrchr (modname, '\\')) == NULL)
emacs_abort ();
@@ -2164,33 +2570,6 @@
_putenv (strdup (buf));
}
}
- /* Handle running emacs from the build directory: src/oo-spd/i386/ */
-
- /* FIXME: should use substring of get_emacs_configuration ().
- But I don't think the Windows build supports alpha, mips etc
- anymore, so have taken the easy option for now. */
- else if (p && (xstrcasecmp (p, "\\i386") == 0
- || xstrcasecmp (p, "\\AMD64") == 0))
- {
- *p = 0;
- p = _mbsrchr (modname, '\\');
- if (p != NULL)
- {
- *p = 0;
- p = _mbsrchr (modname, '\\');
- if (p && xstrcasecmp (p, "\\src") == 0)
- {
- char buf[SET_ENV_BUF_SIZE];
-
- *p = 0;
- for (p = modname; *p; p = CharNext (p))
- if (*p == '\\') *p = '/';
-
- _snprintf (buf, sizeof (buf)-1, "emacs_dir=%s", modname);
- _putenv (strdup (buf));
- }
- }
- }
}
for (i = 0; i < N_ENV_VARS; i++)
@@ -2226,8 +2605,7 @@
strcpy (&fname[pend - pstart + 1], "cmdproxy.exe");
ExpandEnvironmentStrings ((LPSTR) fname, bufc,
sizeof (bufc));
- if (faccessat (AT_FDCWD, bufc, F_OK, AT_EACCESS)
- == 0)
+ if (sys_access (bufc, F_OK) == 0)
{
lpval = bufc;
dwType = REG_SZ;
@@ -2307,13 +2685,27 @@
/* Remember the initial working directory for getcwd. */
/* FIXME: Do we need to resolve possible symlinks in startup_dir?
Does it matter anywhere in Emacs? */
- if (!GetCurrentDirectory (MAXPATHLEN, startup_dir))
- emacs_abort ();
+ if (w32_unicode_filenames)
+ {
+ wchar_t wstartup_dir[MAX_PATH];
+
+ if (!GetCurrentDirectoryW (MAX_PATH, wstartup_dir))
+ emacs_abort ();
+ filename_from_utf16 (wstartup_dir, startup_dir);
+ }
+ else
+ {
+ char astartup_dir[MAX_PATH];
+
+ if (!GetCurrentDirectoryA (MAX_PATH, astartup_dir))
+ emacs_abort ();
+ filename_from_ansi (astartup_dir, startup_dir);
+ }
{
static char modname[MAX_PATH];
- if (!GetModuleFileName (NULL, modname, MAX_PATH))
+ if (!GetModuleFileNameA (NULL, modname, MAX_PATH))
emacs_abort ();
argv[0] = modname;
}
@@ -2331,170 +2723,18 @@
char *
emacs_root_dir (void)
{
- static char root_dir[FILENAME_MAX];
+ static char root_dir[MAX_UTF8_PATH];
const char *p;
p = getenv ("emacs_dir");
if (p == NULL)
emacs_abort ();
- strcpy (root_dir, p);
+ filename_from_ansi (p, root_dir);
root_dir[parse_root (root_dir, NULL)] = '\0';
- dostounix_filename (root_dir, 0);
+ dostounix_filename (root_dir);
return root_dir;
}
-/* We don't have scripts to automatically determine the system configuration
- for Emacs before it's compiled, and we don't want to have to make the
- user enter it, so we define EMACS_CONFIGURATION to invoke this runtime
- routine. */
-
-char *
-get_emacs_configuration (void)
-{
- char *arch, *oem, *os;
- int build_num;
- static char configuration_buffer[32];
-
- /* Determine the processor type. */
- switch (get_processor_type ())
- {
-
-#ifdef PROCESSOR_INTEL_386
- case PROCESSOR_INTEL_386:
- case PROCESSOR_INTEL_486:
- case PROCESSOR_INTEL_PENTIUM:
-#ifdef _WIN64
- arch = "amd64";
-#else
- arch = "i386";
-#endif
- break;
-#endif
-#ifdef PROCESSOR_AMD_X8664
- case PROCESSOR_AMD_X8664:
- arch = "amd64";
- break;
-#endif
-
-#ifdef PROCESSOR_MIPS_R2000
- case PROCESSOR_MIPS_R2000:
- case PROCESSOR_MIPS_R3000:
- case PROCESSOR_MIPS_R4000:
- arch = "mips";
- break;
-#endif
-
-#ifdef PROCESSOR_ALPHA_21064
- case PROCESSOR_ALPHA_21064:
- arch = "alpha";
- break;
-#endif
-
- default:
- arch = "unknown";
- break;
- }
-
- /* Use the OEM field to reflect the compiler/library combination. */
-#ifdef _MSC_VER
-#define COMPILER_NAME "msvc"
-#else
-#ifdef __GNUC__
-#define COMPILER_NAME "mingw"
-#else
-#define COMPILER_NAME "unknown"
-#endif
-#endif
- oem = COMPILER_NAME;
-
- switch (osinfo_cache.dwPlatformId) {
- case VER_PLATFORM_WIN32_NT:
- os = "nt";
- build_num = osinfo_cache.dwBuildNumber;
- break;
- case VER_PLATFORM_WIN32_WINDOWS:
- if (osinfo_cache.dwMinorVersion == 0) {
- os = "windows95";
- } else {
- os = "windows98";
- }
- build_num = LOWORD (osinfo_cache.dwBuildNumber);
- break;
- case VER_PLATFORM_WIN32s:
- /* Not supported, should not happen. */
- os = "windows32s";
- build_num = LOWORD (osinfo_cache.dwBuildNumber);
- break;
- default:
- os = "unknown";
- build_num = 0;
- break;
- }
-
- if (osinfo_cache.dwPlatformId == VER_PLATFORM_WIN32_NT) {
- sprintf (configuration_buffer, "%s-%s-%s%d.%d.%d", arch, oem, os,
- get_w32_major_version (), get_w32_minor_version (), build_num);
- } else {
- sprintf (configuration_buffer, "%s-%s-%s.%d", arch, oem, os, build_num);
- }
-
- return configuration_buffer;
-}
-
-char *
-get_emacs_configuration_options (void)
-{
- static char *options_buffer;
- char cv[32]; /* Enough for COMPILER_VERSION. */
- char *options[] = {
- cv, /* To be filled later. */
-#ifdef EMACSDEBUG
- " --no-opt",
-#endif
-#ifdef ENABLE_CHECKING
- " --enable-checking",
-#endif
- /* configure.bat already sets USER_CFLAGS and USER_LDFLAGS
- with a starting space to save work here. */
-#ifdef USER_CFLAGS
- " --cflags", USER_CFLAGS,
-#endif
-#ifdef USER_LDFLAGS
- " --ldflags", USER_LDFLAGS,
-#endif
- NULL
- };
- size_t size = 0;
- int i;
-
-/* Work out the effective configure options for this build. */
-#ifdef _MSC_VER
-#define COMPILER_VERSION "--with-msvc (%d.%02d)", _MSC_VER / 100,
_MSC_VER % 100
-#else
-#ifdef __GNUC__
-#define COMPILER_VERSION "--with-gcc (%d.%d)", __GNUC__, __GNUC_MINOR__
-#else
-#define COMPILER_VERSION ""
-#endif
-#endif
-
- if (_snprintf (cv, sizeof (cv) - 1, COMPILER_VERSION) < 0)
- return "Error: not enough space for compiler version";
- cv[sizeof (cv) - 1] = '\0';
-
- for (i = 0; options[i]; i++)
- size += strlen (options[i]);
-
- options_buffer = xmalloc (size + 1);
- options_buffer[0] = '\0';
-
- for (i = 0; options[i]; i++)
- strcat (options_buffer, options[i]);
-
- return options_buffer;
-}
-
-
#include <sys/timeb.h>
/* Emulate gettimeofday (Ulrich Leodolter, 1/11/95). */
@@ -2649,6 +2889,7 @@
add_volume_info (char * root_dir, volume_info_data * info)
{
info->root_dir = xstrdup (root_dir);
+ unixtodos_filename (info->root_dir);
info->next = volume_cache;
volume_cache = info;
}
@@ -2661,14 +2902,30 @@
GetCachedVolumeInformation (char * root_dir)
{
volume_info_data * info;
- char default_root[ MAX_PATH ];
+ char default_root[ MAX_UTF8_PATH ];
+ char name[MAX_PATH+1];
+ char type[MAX_PATH+1];
/* NULL for root_dir means use root from current directory. */
if (root_dir == NULL)
{
- if (GetCurrentDirectory (MAX_PATH, default_root) == 0)
- return NULL;
- parse_root (default_root, &root_dir);
+ if (w32_unicode_filenames)
+ {
+ wchar_t curdirw[MAX_PATH];
+
+ if (GetCurrentDirectoryW (MAX_PATH, curdirw) == 0)
+ return NULL;
+ filename_from_utf16 (curdirw, default_root);
+ }
+ else
+ {
+ char curdira[MAX_PATH];
+
+ if (GetCurrentDirectoryA (MAX_PATH, curdira) == 0)
+ return NULL;
+ filename_from_ansi (curdira, default_root);
+ }
+ parse_root (default_root, (const char **)&root_dir);
*root_dir = 0;
root_dir = default_root;
}
@@ -2707,20 +2964,47 @@
if (info == NULL || ! VOLINFO_STILL_VALID (root_dir, info))
{
- char name[ 256 ];
DWORD serialnum;
DWORD maxcomp;
DWORD flags;
- char type[ 256 ];
/* Info is not cached, or is stale. */
- if (!GetVolumeInformation (root_dir,
- name, sizeof (name),
- &serialnum,
- &maxcomp,
- &flags,
- type, sizeof (type)))
- return NULL;
+ if (w32_unicode_filenames)
+ {
+ wchar_t root_w[MAX_PATH];
+ wchar_t name_w[MAX_PATH+1];
+ wchar_t type_w[MAX_PATH+1];
+
+ filename_to_utf16 (root_dir, root_w);
+ if (!GetVolumeInformationW (root_w,
+ name_w, sizeof (name_w),
+ &serialnum,
+ &maxcomp,
+ &flags,
+ type_w, sizeof (type_w)))
+ return NULL;
+ /* Hmm... not really 100% correct, as these 2 are not file
+ names... */
+ filename_from_utf16 (name_w, name);
+ filename_from_utf16 (type_w, type);
+ }
+ else
+ {
+ char root_a[MAX_PATH];
+ char name_a[MAX_PATH+1];
+ char type_a[MAX_PATH+1];
+
+ filename_to_ansi (root_dir, root_a);
+ if (!GetVolumeInformationA (root_a,
+ name_a, sizeof (name_a),
+ &serialnum,
+ &maxcomp,
+ &flags,
+ type_a, sizeof (type_a)))
+ return NULL;
+ filename_from_ansi (name_a, name);
+ filename_from_ansi (type_a, type);
+ }
/* Cache the volume information for future use, overwriting existing
entry if present. */
@@ -2736,6 +3020,7 @@
}
info->name = xstrdup (name);
+ unixtodos_filename (info->name);
info->serialnum = serialnum;
info->maxcomp = maxcomp;
info->flags = flags;
@@ -2758,52 +3043,22 @@
static int
get_volume_info (const char * name, const char ** pPath)
{
- char temp[MAX_PATH];
+ char temp[MAX_UTF8_PATH];
char *rootname = NULL; /* default to current volume */
volume_info_data * info;
+ int root_len = parse_root (name, pPath);
if (name == NULL)
return FALSE;
- /* Find the root name of the volume if given. */
- if (isalpha (name[0]) && name[1] == ':')
- {
- rootname = temp;
- temp[0] = *name++;
- temp[1] = *name++;
- temp[2] = '\\';
- temp[3] = 0;
- }
- else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
- {
- char *str = temp;
- int slashes = 4;
- int dbcs_p = max_filename_mbslen () > 1;
-
- rootname = temp;
- do
- {
- if (IS_DIRECTORY_SEP (*name) && --slashes == 0)
- break;
- if (!dbcs_p)
- *str++ = *name++;
- else
- {
- const char *p = name;
-
- name = CharNextExA (file_name_codepage, name, 0);
- memcpy (str, p, name - p);
- str += name - p;
- }
- }
- while ( *name );
-
- *str++ = '\\';
- *str = 0;
- }
-
- if (pPath)
- *pPath = name;
+ /* Copy the root name of the volume, if given. */
+ if (root_len)
+ {
+ strncpy (temp, name, root_len);
+ temp[root_len] = '\0';
+ unixtodos_filename (temp);
+ rootname = temp;
+ }
info = GetCachedVolumeInformation (rootname);
if (info != NULL)
@@ -2826,18 +3081,19 @@
return FALSE;
}
-/* Map filename to a valid 8.3 name if necessary.
- The result is a pointer to a static buffer, so CAVEAT EMPTOR! */
+/* Convert all slashes in a filename to backslashes, and map filename
+ to a valid 8.3 name if necessary. The result is a pointer to a
+ static buffer, so CAVEAT EMPTOR! */
const char *
map_w32_filename (const char * name, const char ** pPath)
{
- static char shortname[MAX_PATH];
+ static char shortname[MAX_UTF8_PATH];
char * str = shortname;
char c;
char * path;
const char * save_name = name;
- if (strlen (name) >= MAX_PATH)
+ if (strlen (name) >= sizeof (shortname))
{
/* Return a filename which will cause callers to fail. */
strcpy (shortname, "?");
@@ -2904,7 +3160,7 @@
str[-1] = c; /* replace last character of part */
/* FALLTHRU */
default:
- if ( left )
+ if ( left && 'A' <= c && c <= 'Z' )
{
*str++ = tolower (c); /* map to lower case (looks nicer) */
left--;
@@ -2939,25 +3195,31 @@
xstrcasecmp (p, ".cmd") == 0));
}
-/* Emulate the Unix directory procedures opendir, closedir,
- and readdir. We can't use the procedures supplied in sysdep.c,
- so we provide them here. */
+/* Emulate the Unix directory procedures opendir, closedir, and
+ readdir. We rename them to sys_* names because some versions of
+ MinGW startup code call opendir and readdir to glob wildcards, and
+ the code that calls them doesn't grok UTF-8 encoded file names we
+ produce in dirent->d_name[]. */
struct dirent dir_static; /* simulated directory contents */
static HANDLE dir_find_handle = INVALID_HANDLE_VALUE;
static int dir_is_fat;
-static char dir_pathname[MAXPATHLEN+1];
-static WIN32_FIND_DATA dir_find_data;
+static char dir_pathname[MAX_UTF8_PATH];
+static WIN32_FIND_DATAW dir_find_data_w;
+static WIN32_FIND_DATAA dir_find_data_a;
+#define DIR_FIND_DATA_W 1
+#define DIR_FIND_DATA_A 2
+static int last_dir_find_data = -1;
/* Support shares on a network resource as subdirectories of a read-only
root directory. */
static HANDLE wnet_enum_handle = INVALID_HANDLE_VALUE;
static HANDLE open_unc_volume (const char *);
-static char *read_unc_volume (HANDLE, char *, int);
+static void *read_unc_volume (HANDLE, wchar_t *, char *, int);
static void close_unc_volume (HANDLE);
DIR *
-opendir (const char *filename)
+sys_opendir (const char *filename)
{
DIR *dirp;
@@ -2986,8 +3248,8 @@
dirp->dd_loc = 0;
dirp->dd_size = 0;
- strncpy (dir_pathname, map_w32_filename (filename, NULL), MAXPATHLEN);
- dir_pathname[MAXPATHLEN] = '\0';
+ strncpy (dir_pathname, map_w32_filename (filename, NULL), MAX_UTF8_PATH - 1);
+ dir_pathname[MAX_UTF8_PATH - 1] = '\0';
/* Note: We don't support symlinks to file names on FAT volumes.
Doing so would mean punishing 99.99% of use cases by resolving
all the possible symlinks in FILENAME, recursively. */
@@ -2997,7 +3259,7 @@
}
void
-closedir (DIR *dirp)
+sys_closedir (DIR *dirp)
{
/* If we have a find-handle open, close it. */
if (dir_find_handle != INVALID_HANDLE_VALUE)
@@ -3014,52 +3276,65 @@
}
struct dirent *
-readdir (DIR *dirp)
+sys_readdir (DIR *dirp)
{
int downcase = !NILP (Vw32_downcase_file_names);
if (wnet_enum_handle != INVALID_HANDLE_VALUE)
{
if (!read_unc_volume (wnet_enum_handle,
- dir_find_data.cFileName,
+ dir_find_data_w.cFileName,
+ dir_find_data_a.cFileName,
MAX_PATH))
return NULL;
}
/* If we aren't dir_finding, do a find-first, otherwise do a find-next. */
else if (dir_find_handle == INVALID_HANDLE_VALUE)
{
- char filename[MAXNAMLEN + 3];
+ char filename[MAX_UTF8_PATH + 2];
int ln;
- int dbcs_p = max_filename_mbslen () > 1;
strcpy (filename, dir_pathname);
ln = strlen (filename) - 1;
- if (!dbcs_p)
- {
- if (!IS_DIRECTORY_SEP (filename[ln]))
- strcat (filename, "\\");
- }
- else
- {
- char *end = filename + ln + 1;
- char *last_char = CharPrevExA (file_name_codepage, filename, end, 0);
-
- if (!IS_DIRECTORY_SEP (*last_char))
- strcat (filename, "\\");
- }
+ if (!IS_DIRECTORY_SEP (filename[ln]))
+ strcat (filename, "\\");
strcat (filename, "*");
/* Note: No need to resolve symlinks in FILENAME, because
FindFirst opens the directory that is the target of a
symlink. */
- dir_find_handle = FindFirstFile (filename, &dir_find_data);
+ if (w32_unicode_filenames)
+ {
+ wchar_t fnw[MAX_PATH];
+
+ filename_to_utf16 (filename, fnw);
+ dir_find_handle = FindFirstFileW (fnw, &dir_find_data_w);
+ }
+ else
+ {
+ char fna[MAX_PATH];
+
+ filename_to_ansi (filename, fna);
+ /* If FILENAME is not representable by the current ANSI
+ codepage, we don't want FindFirstFileA to interpret the
+ '?' characters as a wildcard. */
+ if (_mbspbrk (fna, "?"))
+ dir_find_handle = INVALID_HANDLE_VALUE;
+ else
+ dir_find_handle = FindFirstFileA (fna, &dir_find_data_a);
+ }
if (dir_find_handle == INVALID_HANDLE_VALUE)
return NULL;
}
+ else if (w32_unicode_filenames)
+ {
+ if (!FindNextFileW (dir_find_handle, &dir_find_data_w))
+ return NULL;
+ }
else
{
- if (!FindNextFile (dir_find_handle, &dir_find_data))
+ if (!FindNextFileA (dir_find_handle, &dir_find_data_a))
return NULL;
}
@@ -3067,112 +3342,155 @@
value returned by stat(). */
dir_static.d_ino = 1;
- strcpy (dir_static.d_name, dir_find_data.cFileName);
-
- /* If the file name in cFileName[] includes `?' characters, it means
- the original file name used characters that cannot be represented
- by the current ANSI codepage. To avoid total lossage, retrieve
- the short 8+3 alias of the long file name. */
- if (_mbspbrk (dir_static.d_name, "?"))
- {
- strcpy (dir_static.d_name, dir_find_data.cAlternateFileName);
- downcase = 1; /* 8+3 aliases are returned in all caps */
- }
+ if (w32_unicode_filenames)
+ {
+ if (downcase || dir_is_fat)
+ {
+ wchar_t tem[MAX_PATH];
+
+ wcscpy (tem, dir_find_data_w.cFileName);
+ CharLowerW (tem);
+ filename_from_utf16 (tem, dir_static.d_name);
+ }
+ else
+ filename_from_utf16 (dir_find_data_w.cFileName, dir_static.d_name);
+ last_dir_find_data = DIR_FIND_DATA_W;
+ }
+ else
+ {
+ char tem[MAX_PATH];
+
+ /* If the file name in cFileName[] includes `?' characters, it
+ means the original file name used characters that cannot be
+ represented by the current ANSI codepage. To avoid total
+ lossage, retrieve the short 8+3 alias of the long file
+ name. */
+ if (_mbspbrk (dir_find_data_a.cFileName, "?"))
+ {
+ strcpy (tem, dir_find_data_a.cAlternateFileName);
+ /* 8+3 aliases are returned in all caps, which could break
+ various alists that look at filenames' extensions. */
+ downcase = 1;
+ }
+ else if (downcase || dir_is_fat)
+ strcpy (tem, dir_find_data_a.cFileName);
+ else
+ filename_from_ansi (dir_find_data_a.cFileName, dir_static.d_name);
+ if (downcase || dir_is_fat)
+ {
+ _mbslwr (tem);
+ filename_from_ansi (tem, dir_static.d_name);
+ }
+ last_dir_find_data = DIR_FIND_DATA_A;
+ }
+
dir_static.d_namlen = strlen (dir_static.d_name);
- dir_static.d_reclen = sizeof (struct dirent) - MAXNAMLEN + 3 +
+ dir_static.d_reclen = sizeof (struct dirent) - MAX_UTF8_PATH + 3 +
dir_static.d_namlen - dir_static.d_namlen % 4;
- /* If the file name in cFileName[] includes `?' characters, it means
- the original file name used characters that cannot be represented
- by the current ANSI codepage. To avoid total lossage, retrieve
- the short 8+3 alias of the long file name. */
- if (_mbspbrk (dir_find_data.cFileName, "?"))
- {
- strcpy (dir_static.d_name, dir_find_data.cAlternateFileName);
- /* 8+3 aliases are returned in all caps, which could break
- various alists that look at filenames' extensions. */
- downcase = 1;
- }
- else
- strcpy (dir_static.d_name, dir_find_data.cFileName);
- dir_static.d_namlen = strlen (dir_static.d_name);
- if (dir_is_fat)
- _mbslwr (dir_static.d_name);
- else if (downcase)
- {
- register char *p;
- int dbcs_p = max_filename_mbslen () > 1;
- for (p = dir_static.d_name; *p; )
- {
- if (*p >= 'a' && *p <= 'z')
- break;
- if (dbcs_p)
- p = CharNextExA (file_name_codepage, p, 0);
- else
- p++;
- }
- if (!*p)
- _mbslwr (dir_static.d_name);
- }
-
return &dir_static;
}
static HANDLE
open_unc_volume (const char *path)
{
- NETRESOURCE nr;
+ const char *fn = map_w32_filename (path, NULL);
+ DWORD result;
HANDLE henum;
- int result;
-
- nr.dwScope = RESOURCE_GLOBALNET;
- nr.dwType = RESOURCETYPE_DISK;
- nr.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER;
- nr.dwUsage = RESOURCEUSAGE_CONTAINER;
- nr.lpLocalName = NULL;
- nr.lpRemoteName = (LPSTR)map_w32_filename (path, NULL);
- nr.lpComment = NULL;
- nr.lpProvider = NULL;
-
- result = WNetOpenEnum (RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
- RESOURCEUSAGE_CONNECTABLE, &nr, &henum);
-
+
+ if (w32_unicode_filenames)
+ {
+ NETRESOURCEW nrw;
+ wchar_t fnw[MAX_PATH];
+
+ nrw.dwScope = RESOURCE_GLOBALNET;
+ nrw.dwType = RESOURCETYPE_DISK;
+ nrw.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER;
+ nrw.dwUsage = RESOURCEUSAGE_CONTAINER;
+ nrw.lpLocalName = NULL;
+ filename_to_utf16 (fn, fnw);
+ nrw.lpRemoteName = fnw;
+ nrw.lpComment = NULL;
+ nrw.lpProvider = NULL;
+
+ result = WNetOpenEnumW (RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
+ RESOURCEUSAGE_CONNECTABLE, &nrw, &henum);
+ }
+ else
+ {
+ NETRESOURCEA nra;
+ char fna[MAX_PATH];
+
+ nra.dwScope = RESOURCE_GLOBALNET;
+ nra.dwType = RESOURCETYPE_DISK;
+ nra.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER;
+ nra.dwUsage = RESOURCEUSAGE_CONTAINER;
+ nra.lpLocalName = NULL;
+ filename_to_ansi (fn, fna);
+ nra.lpRemoteName = fna;
+ nra.lpComment = NULL;
+ nra.lpProvider = NULL;
+
+ result = WNetOpenEnumA (RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
+ RESOURCEUSAGE_CONNECTABLE, &nra, &henum);
+ }
if (result == NO_ERROR)
return henum;
else
return INVALID_HANDLE_VALUE;
}
-static char *
-read_unc_volume (HANDLE henum, char *readbuf, int size)
+static void *
+read_unc_volume (HANDLE henum, wchar_t *fname_w, char *fname_a, int size)
{
DWORD count;
int result;
+ char *buffer;
DWORD bufsize = 512;
- char *buffer;
- char *ptr;
- int dbcs_p = max_filename_mbslen () > 1;
+ void *retval;
count = 1;
- buffer = alloca (bufsize);
- result = WNetEnumResource (henum, &count, buffer, &bufsize);
- if (result != NO_ERROR)
- return NULL;
+ if (w32_unicode_filenames)
+ {
+ wchar_t *ptrw;
- /* WNetEnumResource returns \\resource\share...skip forward to "share". */
- ptr = ((LPNETRESOURCE) buffer)->lpRemoteName;
- ptr += 2;
- if (!dbcs_p)
- while (*ptr && !IS_DIRECTORY_SEP (*ptr)) ptr++;
+ bufsize *= 2;
+ buffer = alloca (bufsize);
+ result = WNetEnumResourceW (henum, &count, buffer, &bufsize);
+ if (result != NO_ERROR)
+ return NULL;
+ /* WNetEnumResource returns \\resource\share...skip forward to "share".
*/
+ ptrw = ((LPNETRESOURCEW) buffer)->lpRemoteName;
+ ptrw += 2;
+ while (*ptrw && *ptrw != L'/' && *ptrw != L'\\') ptrw++;
+ ptrw++;
+ wcsncpy (fname_w, ptrw, size);
+ retval = fname_w;
+ }
else
{
- while (*ptr && !IS_DIRECTORY_SEP (*ptr))
- ptr = CharNextExA (file_name_codepage, ptr, 0);
+ int dbcs_p = max_filename_mbslen () > 1;
+ char *ptra;
+
+ buffer = alloca (bufsize);
+ result = WNetEnumResourceA (henum, &count, buffer, &bufsize);
+ if (result != NO_ERROR)
+ return NULL;
+ ptra = ((LPNETRESOURCEA) buffer)->lpRemoteName;
+ ptra += 2;
+ if (!dbcs_p)
+ while (*ptra && !IS_DIRECTORY_SEP (*ptra)) ptra++;
+ else
+ {
+ while (*ptra && !IS_DIRECTORY_SEP (*ptra))
+ ptra = CharNextExA (file_name_codepage, ptra, 0);
+ }
+ ptra++;
+ strncpy (fname_a, ptra, size);
+ retval = fname_a;
}
- ptr++;
- strncpy (readbuf, ptr, size);
- return readbuf;
+ return retval;
}
static void
@@ -3203,13 +3521,12 @@
static void
logon_network_drive (const char *path)
{
- NETRESOURCE resource;
- char share[MAX_PATH];
+ char share[MAX_UTF8_PATH];
int n_slashes;
char drive[4];
UINT drvtype;
char *p;
- int dbcs_p;
+ DWORD val;
if (IS_DIRECTORY_SEP (path[0]) && IS_DIRECTORY_SEP (path[1]))
drvtype = DRIVE_REMOTE;
@@ -3229,28 +3546,70 @@
return;
n_slashes = 2;
- strncpy (share, path, MAX_PATH);
+ strncpy (share, path, MAX_UTF8_PATH);
/* Truncate to just server and share name. */
- dbcs_p = max_filename_mbslen () > 1;
- for (p = share + 2; *p && p < share + MAX_PATH; )
+ for (p = share + 2; *p && p < share + MAX_UTF8_PATH; p++)
{
if (IS_DIRECTORY_SEP (*p) && ++n_slashes > 3)
{
*p = '\0';
break;
}
- if (dbcs_p)
- p = CharNextExA (file_name_codepage, p, 0);
- else
- p++;
- }
-
- resource.dwType = RESOURCETYPE_DISK;
- resource.lpLocalName = NULL;
- resource.lpRemoteName = share;
- resource.lpProvider = NULL;
-
- WNetAddConnection2 (&resource, NULL, NULL, CONNECT_INTERACTIVE);
+ }
+
+ if (w32_unicode_filenames)
+ {
+ NETRESOURCEW resourcew;
+ wchar_t share_w[MAX_PATH];
+
+ resourcew.dwScope = RESOURCE_GLOBALNET;
+ resourcew.dwType = RESOURCETYPE_DISK;
+ resourcew.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE;
+ resourcew.dwUsage = RESOURCEUSAGE_CONTAINER;
+ resourcew.lpLocalName = NULL;
+ filename_to_utf16 (share, share_w);
+ resourcew.lpRemoteName = share_w;
+ resourcew.lpProvider = NULL;
+
+ val = WNetAddConnection2W (&resourcew, NULL, NULL, CONNECT_INTERACTIVE);
+ }
+ else
+ {
+ NETRESOURCEA resourcea;
+ char share_a[MAX_PATH];
+
+ resourcea.dwScope = RESOURCE_GLOBALNET;
+ resourcea.dwType = RESOURCETYPE_DISK;
+ resourcea.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE;
+ resourcea.dwUsage = RESOURCEUSAGE_CONTAINER;
+ resourcea.lpLocalName = NULL;
+ filename_to_ansi (share, share_a);
+ resourcea.lpRemoteName = share_a;
+ resourcea.lpProvider = NULL;
+
+ val = WNetAddConnection2A (&resourcea, NULL, NULL, CONNECT_INTERACTIVE);
+ }
+
+ switch (val)
+ {
+ case NO_ERROR:
+ case ERROR_ALREADY_ASSIGNED:
+ break;
+ case ERROR_ACCESS_DENIED:
+ case ERROR_LOGON_FAILURE:
+ errno = EACCES;
+ break;
+ case ERROR_BUSY:
+ errno = EAGAIN;
+ break;
+ case ERROR_BAD_NET_NAME:
+ case ERROR_NO_NET_OR_BAD_PATH:
+ case ERROR_NO_NETWORK:
+ case ERROR_CANCELLED:
+ default:
+ errno = ENOENT;
+ break;
+ }
}
/* Emulate faccessat(2). */
@@ -3278,7 +3637,22 @@
&& (flags & AT_SYMLINK_NOFOLLOW) == 0)
path = chase_symlinks (path);
- if ((attributes = GetFileAttributes (path)) == -1)
+ if (w32_unicode_filenames)
+ {
+ wchar_t path_w[MAX_PATH];
+
+ filename_to_utf16 (path, path_w);
+ attributes = GetFileAttributesW (path_w);
+ }
+ else
+ {
+ char path_a[MAX_PATH];
+
+ filename_to_ansi (path, path_a);
+ attributes = GetFileAttributesA (path_a);
+ }
+
+ if (attributes == -1)
{
DWORD w32err = GetLastError ();
@@ -3326,6 +3700,62 @@
return 0;
}
+/* A version of 'access' to be used locally with file names in
+ locale-specific encoding. Does not resolve symlinks and does not
+ support file names on FAT12 and FAT16 volumes, but that's OK, since
+ we only invoke this function for files inside the Emacs source or
+ installation tree, on directories (so any symlinks should have the
+ directory bit set), and on short file names such as "C:/.emacs". */
+static int
+sys_access (const char *fname, int mode)
+{
+ char fname_copy[MAX_PATH], *p;
+ DWORD attributes;
+
+ strcpy (fname_copy, fname);
+ /* Do the equivalent of unixtodos_filename. */
+ for (p = fname_copy; *p; p = CharNext (p))
+ if (*p == '/')
+ *p = '\\';
+
+ if ((attributes = GetFileAttributesA (fname_copy)) == -1)
+ {
+ DWORD w32err = GetLastError ();
+
+ switch (w32err)
+ {
+ case ERROR_INVALID_NAME:
+ case ERROR_BAD_PATHNAME:
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_BAD_NETPATH:
+ errno = ENOENT;
+ break;
+ default:
+ errno = EACCES;
+ break;
+ }
+ return -1;
+ }
+ if ((mode & X_OK) != 0
+ && !(is_exec (fname_copy)
+ || (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0))
+ {
+ errno = EACCES;
+ return -1;
+ }
+ if ((mode & W_OK) != 0 && (attributes & FILE_ATTRIBUTE_READONLY) != 0)
+ {
+ errno = EACCES;
+ return -1;
+ }
+ if ((mode & D_OK) != 0 && (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+ {
+ errno = EACCES;
+ return -1;
+ }
+ return 0;
+}
+
/* Shadow some MSVC runtime functions to map requests for long filenames
to reasonable short names if necessary. This was originally added to
permit running Emacs on NT 3.1 on a FAT partition, which doesn't support
@@ -3334,20 +3764,63 @@
int
sys_chdir (const char * path)
{
- return _chdir (map_w32_filename (path, NULL));
+ path = map_w32_filename (path, NULL);
+ if (w32_unicode_filenames)
+ {
+ wchar_t newdir_w[MAX_PATH];
+
+ if (filename_to_utf16 (path, newdir_w) == 0)
+ return _wchdir (newdir_w);
+ return -1;
+ }
+ else
+ {
+ char newdir_a[MAX_PATH];
+
+ if (filename_to_ansi (path, newdir_a) == 0)
+ return _chdir (newdir_a);
+ return -1;
+ }
}
int
sys_chmod (const char * path, int mode)
{
path = chase_symlinks (map_w32_filename (path, NULL));
- return _chmod (path, mode);
+ if (w32_unicode_filenames)
+ {
+ wchar_t path_w[MAX_PATH];
+
+ filename_to_utf16 (path, path_w);
+ return _wchmod (path_w, mode);
+ }
+ else
+ {
+ char path_a[MAX_PATH];
+
+ filename_to_ansi (path, path_a);
+ return _chmod (path_a, mode);
+ }
}
int
sys_creat (const char * path, int mode)
{
- return _creat (map_w32_filename (path, NULL), mode);
+ path = map_w32_filename (path, NULL);
+ if (w32_unicode_filenames)
+ {
+ wchar_t path_w[MAX_PATH];
+
+ filename_to_utf16 (path, path_w);
+ return _wcreat (path_w, mode);
+ }
+ else
+ {
+ char path_a[MAX_PATH];
+
+ filename_to_ansi (path, path_a);
+ return _creat (path_a, mode);
+ }
}
FILE *
@@ -3387,7 +3860,21 @@
}
else break;
- fd = _open (map_w32_filename (path, NULL), oflag | _O_NOINHERIT, 0644);
+ path = map_w32_filename (path, NULL);
+ if (w32_unicode_filenames)
+ {
+ wchar_t path_w[MAX_PATH];
+
+ filename_to_utf16 (path, path_w);
+ fd = _wopen (path_w, oflag | _O_NOINHERIT, 0644);
+ }
+ else
+ {
+ char path_a[MAX_PATH];
+
+ filename_to_ansi (path, path_a);
+ fd = _open (path_a, oflag | _O_NOINHERIT, 0644);
+ }
if (fd < 0)
return NULL;
@@ -3400,7 +3887,9 @@
{
HANDLE fileh;
int result = -1;
- char oldname[MAX_PATH], newname[MAX_PATH];
+ char oldname[MAX_UTF8_PATH], newname[MAX_UTF8_PATH];
+ wchar_t oldname_w[MAX_PATH];
+ char oldname_a[MAX_PATH];
if (old == NULL || new == NULL)
{
@@ -3411,8 +3900,18 @@
strcpy (oldname, map_w32_filename (old, NULL));
strcpy (newname, map_w32_filename (new, NULL));
- fileh = CreateFile (oldname, 0, 0, NULL, OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ if (w32_unicode_filenames)
+ {
+ filename_to_utf16 (oldname, oldname_w);
+ fileh = CreateFileW (oldname_w, 0, 0, NULL, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ }
+ else
+ {
+ filename_to_ansi (oldname, oldname_a);
+ fileh = CreateFileA (oldname_a, 0, 0, NULL, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ }
if (fileh != INVALID_HANDLE_VALUE)
{
int wlen;
@@ -3429,7 +3928,10 @@
WCHAR wbuffer[MAX_PATH]; /* extra space for link name */
} data;
- wlen = MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED, newname, -1,
+ /* We used to pass MB_PRECOMPOSED as the 2nd arg here, but MSDN
+ indicates that flag is unsupported for CP_UTF8, and OTOH says
+ it is the default anyway. */
+ wlen = MultiByteToWideChar (CP_UTF8, 0, newname, -1,
data.wid.cStreamName, MAX_PATH);
if (wlen > 0)
{
@@ -3453,9 +3955,40 @@
}
else
{
- /* Should try mapping GetLastError to errno; for now just
- indicate a general error (eg. links not supported). */
- errno = EINVAL; // perhaps EMLINK?
+ DWORD err = GetLastError ();
+ DWORD attributes;
+
+ switch (err)
+ {
+ case ERROR_ACCESS_DENIED:
+ /* This is what happens when OLDNAME is a directory,
+ since Windows doesn't support hard links to
+ directories. Posix says to set errno to EPERM in
+ that case. */
+ if (w32_unicode_filenames)
+ attributes = GetFileAttributesW (oldname_w);
+ else
+ attributes = GetFileAttributesA (oldname_a);
+ if (attributes != -1
+ && (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
+ errno = EPERM;
+ else if (attributes == -1
+ && is_unc_volume (oldname)
+ && unc_volume_file_attributes (oldname) != -1)
+ errno = EPERM;
+ else
+ errno = EACCES;
+ break;
+ case ERROR_TOO_MANY_LINKS:
+ errno = EMLINK;
+ break;
+ case ERROR_NOT_SAME_DEVICE:
+ errno = EXDEV;
+ break;
+ default:
+ errno = EINVAL;
+ break;
+ }
}
}
@@ -3470,7 +4003,22 @@
int
sys_mkdir (const char * path)
{
- return _mkdir (map_w32_filename (path, NULL));
+ path = map_w32_filename (path, NULL);
+
+ if (w32_unicode_filenames)
+ {
+ wchar_t path_w[MAX_PATH];
+
+ filename_to_utf16 (path, path_w);
+ return _wmkdir (path_w);
+ }
+ else
+ {
+ char path_a[MAX_PATH];
+
+ filename_to_ansi (path, path_a);
+ return _mkdir (path_a);
+ }
}
int
@@ -3479,13 +4027,29 @@
const char* mpath = map_w32_filename (path, NULL);
int res = -1;
- /* If possible, try to open file without _O_CREAT, to be able to
- write to existing hidden and system files. Force all file
- handles to be non-inheritable. */
- if ((oflag & (_O_CREAT | _O_EXCL)) != (_O_CREAT | _O_EXCL))
- res = _open (mpath, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode);
- if (res < 0)
- res = _open (mpath, oflag | _O_NOINHERIT, mode);
+ if (w32_unicode_filenames)
+ {
+ wchar_t mpath_w[MAX_PATH];
+
+ filename_to_utf16 (mpath, mpath_w);
+ /* If possible, try to open file without _O_CREAT, to be able to
+ write to existing hidden and system files. Force all file
+ handles to be non-inheritable. */
+ if ((oflag & (_O_CREAT | _O_EXCL)) != (_O_CREAT | _O_EXCL))
+ res = _wopen (mpath_w, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode);
+ if (res < 0)
+ res = _wopen (mpath_w, oflag | _O_NOINHERIT, mode);
+ }
+ else
+ {
+ char mpath_a[MAX_PATH];
+
+ filename_to_ansi (mpath, mpath_a);
+ if ((oflag & (_O_CREAT | _O_EXCL)) != (_O_CREAT | _O_EXCL))
+ res = _open (mpath_a, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode);
+ if (res < 0)
+ res = _open (mpath_a, oflag | _O_NOINHERIT, mode);
+ }
return res;
}
@@ -3555,9 +4119,10 @@
sys_rename_replace (const char *oldname, const char *newname, BOOL force)
{
BOOL result;
- char temp[MAX_PATH];
+ char temp[MAX_UTF8_PATH], temp_a[MAX_PATH];;
int newname_dev;
int oldname_dev;
+ bool have_temp_a = false;
/* MoveFile on Windows 95 doesn't correctly change the short file name
alias in a number of circumstances (it is not easy to predict when
@@ -3582,17 +4147,20 @@
char * o;
char * p;
int i = 0;
+ char oldname_a[MAX_PATH];
oldname = map_w32_filename (oldname, NULL);
- if ((o = strrchr (oldname, '\\')))
+ filename_to_ansi (oldname, oldname_a);
+ filename_to_ansi (temp, temp_a);
+ if ((o = strrchr (oldname_a, '\\')))
o++;
else
- o = (char *) oldname;
+ o = (char *) oldname_a;
- if ((p = strrchr (temp, '\\')))
+ if ((p = strrchr (temp_a, '\\')))
p++;
else
- p = temp;
+ p = temp_a;
do
{
@@ -3600,12 +4168,13 @@
seems to make the second rename work properly. */
sprintf (p, "_.%s.%u", o, i);
i++;
- result = rename (oldname, temp);
+ result = rename (oldname_a, temp_a);
}
/* This loop must surely terminate! */
while (result < 0 && errno == EEXIST);
if (result < 0)
return -1;
+ have_temp_a = true;
}
/* If FORCE, emulate Unix behavior - newname is deleted if it already exists
@@ -3624,41 +4193,81 @@
/* volume_info is set indirectly by map_w32_filename. */
newname_dev = volume_info.serialnum;
- result = rename (temp, newname);
-
- if (result < 0 && force)
- {
- DWORD w32err = GetLastError ();
-
- if (errno == EACCES
- && newname_dev != oldname_dev)
- {
- /* The implementation of `rename' on Windows does not return
- errno = EXDEV when you are moving a directory to a
- different storage device (ex. logical disk). It returns
- EACCES instead. So here we handle such situations and
- return EXDEV. */
- DWORD attributes;
-
- if ((attributes = GetFileAttributes (temp)) != -1
- && (attributes & FILE_ATTRIBUTE_DIRECTORY))
- errno = EXDEV;
- }
- else if (errno == EEXIST)
- {
- if (_chmod (newname, 0666) != 0)
- return result;
- if (_unlink (newname) != 0)
- return result;
- result = rename (temp, newname);
- }
- else if (w32err == ERROR_PRIVILEGE_NOT_HELD
- && is_symlink (temp))
- {
- /* This is Windows prohibiting the user from creating a
- symlink in another place, since that requires
- privileges. */
- errno = EPERM;
+ if (w32_unicode_filenames)
+ {
+ wchar_t temp_w[MAX_PATH], newname_w[MAX_PATH];
+
+ filename_to_utf16 (temp, temp_w);
+ filename_to_utf16 (newname, newname_w);
+ result = _wrename (temp_w, newname_w);
+ if (result < 0 && force)
+ {
+ DWORD w32err = GetLastError ();
+
+ if (errno == EACCES
+ && newname_dev != oldname_dev)
+ {
+ /* The implementation of `rename' on Windows does not return
+ errno = EXDEV when you are moving a directory to a
+ different storage device (ex. logical disk). It returns
+ EACCES instead. So here we handle such situations and
+ return EXDEV. */
+ DWORD attributes;
+
+ if ((attributes = GetFileAttributesW (temp_w)) != -1
+ && (attributes & FILE_ATTRIBUTE_DIRECTORY))
+ errno = EXDEV;
+ }
+ else if (errno == EEXIST)
+ {
+ if (_wchmod (newname_w, 0666) != 0)
+ return result;
+ if (_wunlink (newname_w) != 0)
+ return result;
+ result = _wrename (temp_w, newname_w);
+ }
+ else if (w32err == ERROR_PRIVILEGE_NOT_HELD
+ && is_symlink (temp))
+ {
+ /* This is Windows prohibiting the user from creating a
+ symlink in another place, since that requires
+ privileges. */
+ errno = EPERM;
+ }
+ }
+ }
+ else
+ {
+ char newname_a[MAX_PATH];
+
+ if (!have_temp_a)
+ filename_to_ansi (temp, temp_a);
+ filename_to_ansi (newname, newname_a);
+ result = rename (temp_a, newname_a);
+ if (result < 0 && force)
+ {
+ DWORD w32err = GetLastError ();
+
+ if (errno == EACCES
+ && newname_dev != oldname_dev)
+ {
+ DWORD attributes;
+
+ if ((attributes = GetFileAttributesA (temp_a)) != -1
+ && (attributes & FILE_ATTRIBUTE_DIRECTORY))
+ errno = EXDEV;
+ }
+ else if (errno == EEXIST)
+ {
+ if (_chmod (newname_a, 0666) != 0)
+ return result;
+ if (_unlink (newname_a) != 0)
+ return result;
+ result = rename (temp_a, newname_a);
+ }
+ else if (w32err == ERROR_PRIVILEGE_NOT_HELD
+ && is_symlink (temp))
+ errno = EPERM;
}
}
@@ -3674,7 +4283,22 @@
int
sys_rmdir (const char * path)
{
- return _rmdir (map_w32_filename (path, NULL));
+ path = map_w32_filename (path, NULL);
+
+ if (w32_unicode_filenames)
+ {
+ wchar_t path_w[MAX_PATH];
+
+ filename_to_utf16 (path, path_w);
+ return _wrmdir (path_w);
+ }
+ else
+ {
+ char path_a[MAX_PATH];
+
+ filename_to_ansi (path, path_a);
+ return _rmdir (path_a);
+ }
}
int
@@ -3682,9 +4306,23 @@
{
path = map_w32_filename (path, NULL);
- /* On Unix, unlink works without write permission. */
- _chmod (path, 0666);
- return _unlink (path);
+ if (w32_unicode_filenames)
+ {
+ wchar_t path_w[MAX_PATH];
+
+ filename_to_utf16 (path, path_w);
+ /* On Unix, unlink works without write permission. */
+ _wchmod (path_w, 0666);
+ return _wunlink (path_w);
+ }
+ else
+ {
+ char path_a[MAX_PATH];
+
+ filename_to_ansi (path, path_a);
+ _chmod (path_a, 0666);
+ return _unlink (path_a);
+ }
}
static FILETIME utc_base_ft;
@@ -3752,49 +4390,6 @@
pft->dwLowDateTime = tmp.LowPart;
}
-#if 0
-/* No reason to keep this; faking inode values either by hashing or even
- using the file index from GetInformationByHandle, is not perfect and
- so by default Emacs doesn't use the inode values on Windows.
- Instead, we now determine file-truename correctly (except for
- possible drive aliasing etc). */
-
-/* Modified version of "PJW" algorithm (see the "Dragon" compiler book). */
-static unsigned
-hashval (const unsigned char * str)
-{
- unsigned h = 0;
- while (*str)
- {
- h = (h << 4) + *str++;
- h ^= (h >> 28);
- }
- return h;
-}
-
-/* Return the hash value of the canonical pathname, excluding the
- drive/UNC header, to get a hopefully unique inode number. */
-static DWORD
-generate_inode_val (const char * name)
-{
- char fullname[ MAX_PATH ];
- char * p;
- unsigned hash;
-
- /* Get the truly canonical filename, if it exists. (Note: this
- doesn't resolve aliasing due to subst commands, or recognize hard
- links. */
- if (!w32_get_long_filename ((char *)name, fullname, MAX_PATH))
- emacs_abort ();
-
- parse_root (fullname, &p);
- /* Normal W32 filesystems are still case insensitive. */
- _strlwr (p);
- return hashval (p);
-}
-
-#endif
-
static PSECURITY_DESCRIPTOR
get_file_security_desc_by_handle (HANDLE h)
{
@@ -4001,18 +4596,21 @@
}
/* If this is non-zero, the caller wants accurate information about
- file's owner and group, which could be expensive to get. */
+ file's owner and group, which could be expensive to get. dired.c
+ uses this flag when needed for the job at hand. */
int w32_stat_get_owner_group;
/* MSVC stat function can't cope with UNC names and has other bugs, so
replace it with our own. This also allows us to calculate consistent
- inode values and owner/group without hacks in the main Emacs code. */
+ inode values and owner/group without hacks in the main Emacs code,
+ and support file names encoded in UTF-8. */
static int
stat_worker (const char * path, struct stat * buf, int follow_symlinks)
{
char *name, *save_name, *r;
- WIN32_FIND_DATA wfd;
+ WIN32_FIND_DATAW wfd_w;
+ WIN32_FIND_DATAA wfd_a;
HANDLE fh;
unsigned __int64 fake_inode = 0;
int permission;
@@ -4024,7 +4622,8 @@
DWORD access_rights = 0;
DWORD fattrs = 0, serialnum = 0, fs_high = 0, fs_low = 0, nlinks = 1;
FILETIME ctime, atime, wtime;
- int dbcs_p;
+ wchar_t name_w[MAX_PATH];
+ char name_a[MAX_PATH];
if (path == NULL || buf == NULL)
{
@@ -4034,11 +4633,8 @@
save_name = name = (char *) map_w32_filename (path, &path);
/* Must be valid filename, no wild cards or other invalid
- characters. We use _mbspbrk to support multibyte strings that
- might look to strpbrk as if they included literal *, ?, and other
- characters mentioned below that are disallowed by Windows
- filesystems. */
- if (_mbspbrk (name, "*?|<>\""))
+ characters. */
+ if (strpbrk (name, "*?|<>\""))
{
errno = ENOENT;
return -1;
@@ -4089,13 +4685,26 @@
if (is_windows_9x () != TRUE)
access_rights |= READ_CONTROL;
- fh = CreateFile (name, access_rights, 0, NULL, OPEN_EXISTING,
- file_flags, NULL);
- /* If CreateFile fails with READ_CONTROL, try again with zero as
- access rights. */
- if (fh == INVALID_HANDLE_VALUE && access_rights)
- fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING,
- file_flags, NULL);
+ if (w32_unicode_filenames)
+ {
+ filename_to_utf16 (name, name_w);
+ fh = CreateFileW (name_w, access_rights, 0, NULL, OPEN_EXISTING,
+ file_flags, NULL);
+ /* If CreateFile fails with READ_CONTROL, try again with
+ zero as access rights. */
+ if (fh == INVALID_HANDLE_VALUE && access_rights)
+ fh = CreateFileW (name_w, 0, 0, NULL, OPEN_EXISTING,
+ file_flags, NULL);
+ }
+ else
+ {
+ filename_to_ansi (name, name_a);
+ fh = CreateFileA (name_a, access_rights, 0, NULL, OPEN_EXISTING,
+ file_flags, NULL);
+ if (fh == INVALID_HANDLE_VALUE && access_rights)
+ fh = CreateFileA (name_a, 0, 0, NULL, OPEN_EXISTING,
+ file_flags, NULL);
+ }
if (fh == INVALID_HANDLE_VALUE)
goto no_true_file_attributes;
@@ -4222,7 +4831,6 @@
did not ask for extra precision, resolving symlinks will fly
in the face of that request, since the user then wants the
lightweight version of the code. */
- dbcs_p = max_filename_mbslen () > 1;
rootdir = (path >= save_name + len - 1
&& (IS_DIRECTORY_SEP (*path) || *path == 0));
@@ -4250,19 +4858,8 @@
}
else if (rootdir)
{
- if (!dbcs_p)
- {
- if (!IS_DIRECTORY_SEP (name[len-1]))
- strcat (name, "\\");
- }
- else
- {
- char *end = name + len;
- char *n = CharPrevExA (file_name_codepage, name, end, 0);
-
- if (!IS_DIRECTORY_SEP (*n))
- strcat (name, "\\");
- }
+ if (!IS_DIRECTORY_SEP (name[len-1]))
+ strcat (name, "\\");
if (GetDriveType (name) < 2)
{
errno = ENOENT;
@@ -4274,51 +4871,65 @@
}
else
{
- if (!dbcs_p)
- {
- if (IS_DIRECTORY_SEP (name[len-1]))
- name[len - 1] = 0;
- }
- else
- {
- char *end = name + len;
- char *n = CharPrevExA (file_name_codepage, name, end, 0);
+ int have_wfd = -1;
- if (IS_DIRECTORY_SEP (*n))
- *n = 0;
- }
+ if (IS_DIRECTORY_SEP (name[len-1]))
+ name[len - 1] = 0;
/* (This is hacky, but helps when doing file completions on
network drives.) Optimize by using information available from
active readdir if possible. */
len = strlen (dir_pathname);
- if (!dbcs_p)
- {
- if (IS_DIRECTORY_SEP (dir_pathname[len-1]))
- len--;
- }
- else
- {
- char *end = dir_pathname + len;
- char *n = CharPrevExA (file_name_codepage, dir_pathname, end, 0);
-
- if (IS_DIRECTORY_SEP (*n))
- len--;
- }
+ if (IS_DIRECTORY_SEP (dir_pathname[len-1]))
+ len--;
if (dir_find_handle != INVALID_HANDLE_VALUE
+ && last_dir_find_data != -1
&& !(is_a_symlink && follow_symlinks)
- && strnicmp (save_name, dir_pathname, len) == 0
+ /* The 2 file-name comparisons below support only ASCII
+ characters, and will lose (compare not equal) when
+ the file names include non-ASCII charcaters that are
+ the same but for the case. However, doing this
+ properly involves: (a) converting both file names to
+ UTF-16, (b) lower-casing both names using CharLowerW,
+ and (c) comparing the results; this would be quite a
+ bit slower, whereas Plan B is for users who want
+ lightweight albeit inaccurate version of 'stat'. */
+ && c_strncasecmp (save_name, dir_pathname, len) == 0
&& IS_DIRECTORY_SEP (name[len])
&& xstrcasecmp (name + len + 1, dir_static.d_name) == 0)
{
+ have_wfd = last_dir_find_data;
/* This was the last entry returned by readdir. */
- wfd = dir_find_data;
+ if (last_dir_find_data == DIR_FIND_DATA_W)
+ wfd_w = dir_find_data_w;
+ else
+ wfd_a = dir_find_data_a;
}
else
{
logon_network_drive (name);
- fh = FindFirstFile (name, &wfd);
+ if (w32_unicode_filenames)
+ {
+ filename_to_utf16 (name, name_w);
+ fh = FindFirstFileW (name_w, &wfd_w);
+ have_wfd = DIR_FIND_DATA_W;
+ }
+ else
+ {
+ filename_to_ansi (name, name_a);
+ /* If NAME includes characters not representable by
+ the current ANSI codepage, filename_to_ansi
+ usually replaces them with a '?'. We don't want
+ to let FindFirstFileA interpret those as widlcards,
+ and "succeed", returning us data from some random
+ file in the same directory. */
+ if (_mbspbrk (name_a, "?"))
+ fh = INVALID_HANDLE_VALUE;
+ else
+ fh = FindFirstFileA (name_a, &wfd_a);
+ have_wfd = DIR_FIND_DATA_A;
+ }
if (fh == INVALID_HANDLE_VALUE)
{
errno = ENOENT;
@@ -4328,12 +4939,24 @@
}
/* Note: if NAME is a symlink, the information we get from
FindFirstFile is for the symlink, not its target. */
- fattrs = wfd.dwFileAttributes;
- ctime = wfd.ftCreationTime;
- atime = wfd.ftLastAccessTime;
- wtime = wfd.ftLastWriteTime;
- fs_high = wfd.nFileSizeHigh;
- fs_low = wfd.nFileSizeLow;
+ if (have_wfd == DIR_FIND_DATA_W)
+ {
+ fattrs = wfd_w.dwFileAttributes;
+ ctime = wfd_w.ftCreationTime;
+ atime = wfd_w.ftLastAccessTime;
+ wtime = wfd_w.ftLastWriteTime;
+ fs_high = wfd_w.nFileSizeHigh;
+ fs_low = wfd_w.nFileSizeLow;
+ }
+ else
+ {
+ fattrs = wfd_a.dwFileAttributes;
+ ctime = wfd_a.ftCreationTime;
+ atime = wfd_a.ftLastAccessTime;
+ wtime = wfd_a.ftLastWriteTime;
+ fs_high = wfd_a.nFileSizeHigh;
+ fs_low = wfd_a.nFileSizeLow;
+ }
fake_inode = 0;
nlinks = 1;
serialnum = volume_info.serialnum;
@@ -4348,18 +4971,6 @@
get_file_owner_and_group (NULL, buf);
}
-#if 0
- /* Not sure if there is any point in this. */
- if (!NILP (Vw32_generate_fake_inodes))
- fake_inode = generate_inode_val (name);
- else if (fake_inode == 0)
- {
- /* For want of something better, try to make everything unique. */
- static DWORD gen_num = 0;
- fake_inode = ++gen_num;
- }
-#endif
-
buf->st_ino = fake_inode;
buf->st_dev = serialnum;
@@ -4418,7 +5029,7 @@
FIXME: Add proper support for fdopendir, fstatat, readlinkat.
Gnulib does this and can serve as a model. */
- char fullname[MAX_PATH];
+ char fullname[MAX_UTF8_PATH];
if (fd != AT_FDCWD)
{
@@ -4563,13 +5174,32 @@
times = &deftime;
}
- /* Need write access to set times. */
- fh = CreateFile (name, FILE_WRITE_ATTRIBUTES,
- /* If NAME specifies a directory, FILE_SHARE_DELETE
- allows other processes to delete files inside it,
- while we have the directory open. */
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ if (w32_unicode_filenames)
+ {
+ wchar_t name_utf16[MAX_PATH];
+
+ if (filename_to_utf16 (name, name_utf16) != 0)
+ return -1; /* errno set by filename_to_utf16 */
+
+ /* Need write access to set times. */
+ fh = CreateFileW (name_utf16, FILE_WRITE_ATTRIBUTES,
+ /* If NAME specifies a directory, FILE_SHARE_DELETE
+ allows other processes to delete files inside it,
+ while we have the directory open. */
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ }
+ else
+ {
+ char name_ansi[MAX_PATH];
+
+ if (filename_to_ansi (name, name_ansi) != 0)
+ return -1; /* errno set by filename_to_ansi */
+
+ fh = CreateFileA (name_ansi, FILE_WRITE_ATTRIBUTES,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ }
if (fh != INVALID_HANDLE_VALUE)
{
convert_from_time_t (times->actime, &atime);
@@ -4584,7 +5214,31 @@
}
else
{
- errno = EINVAL;
+ DWORD err = GetLastError ();
+
+ switch (err)
+ {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ case ERROR_INVALID_DRIVE:
+ case ERROR_BAD_NETPATH:
+ case ERROR_DEV_NOT_EXIST:
+ /* ERROR_INVALID_NAME is the error CreateFile sets when the
+ file name includes ?s, i.e. translation to ANSI failed. */
+ case ERROR_INVALID_NAME:
+ errno = ENOENT;
+ break;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ errno = ENFILE;
+ break;
+ case ERROR_ACCESS_DENIED:
+ case ERROR_SHARING_VIOLATION:
+ errno = EACCES;
+ break;
+ default:
+ errno = EINVAL;
+ break;
+ }
return -1;
}
return 0;
@@ -4599,10 +5253,9 @@
int
symlink (char const *filename, char const *linkname)
{
- char linkfn[MAX_PATH], *tgtfn;
+ char linkfn[MAX_UTF8_PATH], *tgtfn;
DWORD flags = 0;
int dir_access, filename_ends_in_slash;
- int dbcs_p;
/* Diagnostics follows Posix as much as possible. */
if (filename == NULL || linkname == NULL)
@@ -4615,7 +5268,7 @@
errno = ENOENT;
return -1;
}
- if (strlen (filename) > MAX_PATH || strlen (linkname) > MAX_PATH)
+ if (strlen (filename) > MAX_UTF8_PATH || strlen (linkname) > MAX_UTF8_PATH)
{
errno = ENAMETOOLONG;
return -1;
@@ -4628,8 +5281,6 @@
return -1;
}
- dbcs_p = max_filename_mbslen () > 1;
-
/* Note: since empty FILENAME was already rejected, we can safely
refer to FILENAME[1]. */
if (!(IS_DIRECTORY_SEP (filename[0]) || IS_DEVICE_SEP (filename[1])))
@@ -4641,24 +5292,11 @@
directory where the Emacs process runs. Note that
make-symbolic-link always makes sure LINKNAME is a fully
expanded file name. */
- char tem[MAX_PATH];
+ char tem[MAX_UTF8_PATH];
char *p = linkfn + strlen (linkfn);
- if (!dbcs_p)
- {
- while (p > linkfn && !IS_ANY_SEP (p[-1]))
- p--;
- }
- else
- {
- char *p1 = CharPrevExA (file_name_codepage, linkfn, p, 0);
-
- while (p > linkfn && !IS_ANY_SEP (*p1))
- {
- p = p1;
- p1 = CharPrevExA (file_name_codepage, linkfn, p1, 0);
- }
- }
+ while (p > linkfn && !IS_ANY_SEP (p[-1]))
+ p--;
if (p > linkfn)
strncpy (tem, linkfn, p - linkfn);
tem[p - linkfn] = '\0';
@@ -4673,15 +5311,7 @@
exist, but ends in a slash, we create a symlink to directory. If
FILENAME exists and is a directory, we always create a symlink to
directory. */
- if (!dbcs_p)
- filename_ends_in_slash = IS_DIRECTORY_SEP (filename[strlen (filename) -
1]);
- else
- {
- const char *end = filename + strlen (filename);
- const char *n = CharPrevExA (file_name_codepage, filename, end, 0);
-
- filename_ends_in_slash = IS_DIRECTORY_SEP (*n);
- }
+ filename_ends_in_slash = IS_DIRECTORY_SEP (filename[strlen (filename) - 1]);
if (dir_access == 0 || filename_ends_in_slash)
flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
@@ -4751,10 +5381,23 @@
is_symlink (const char *filename)
{
DWORD attrs;
- WIN32_FIND_DATA wfd;
+ wchar_t filename_w[MAX_PATH];
+ char filename_a[MAX_PATH];
+ WIN32_FIND_DATAW wfdw;
+ WIN32_FIND_DATAA wfda;
HANDLE fh;
+ int attrs_mean_symlink;
- attrs = GetFileAttributes (filename);
+ if (w32_unicode_filenames)
+ {
+ filename_to_utf16 (filename, filename_w);
+ attrs = GetFileAttributesW (filename_w);
+ }
+ else
+ {
+ filename_to_ansi (filename, filename_a);
+ attrs = GetFileAttributesA (filename_a);
+ }
if (attrs == -1)
{
DWORD w32err = GetLastError ();
@@ -4777,12 +5420,30 @@
if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
return 0;
logon_network_drive (filename);
- fh = FindFirstFile (filename, &wfd);
+ if (w32_unicode_filenames)
+ {
+ fh = FindFirstFileW (filename_w, &wfdw);
+ attrs_mean_symlink =
+ (wfdw.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0
+ && (wfdw.dwReserved0 & IO_REPARSE_TAG_SYMLINK) ==
IO_REPARSE_TAG_SYMLINK;
+ }
+ else if (_mbspbrk (filename_a, "?"))
+ {
+ /* filename_to_ansi failed to convert the file name. */
+ errno = ENOENT;
+ return 0;
+ }
+ else
+ {
+ fh = FindFirstFileA (filename_a, &wfda);
+ attrs_mean_symlink =
+ (wfda.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0
+ && (wfda.dwReserved0 & IO_REPARSE_TAG_SYMLINK) ==
IO_REPARSE_TAG_SYMLINK;
+ }
if (fh == INVALID_HANDLE_VALUE)
return 0;
FindClose (fh);
- return (wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0
- && (wfd.dwReserved0 & IO_REPARSE_TAG_SYMLINK) ==
IO_REPARSE_TAG_SYMLINK;
+ return attrs_mean_symlink;
}
/* If NAME identifies a symbolic link, copy into BUF the file name of
@@ -4799,6 +5460,7 @@
int restore_privs = 0;
HANDLE sh;
ssize_t retval;
+ char resolved[MAX_UTF8_PATH];
if (name == NULL)
{
@@ -4813,7 +5475,7 @@
path = map_w32_filename (name, NULL);
- if (strlen (path) > MAX_PATH)
+ if (strlen (path) > MAX_UTF8_PATH)
{
errno = ENAMETOOLONG;
return -1;
@@ -4843,9 +5505,26 @@
e.g. 'C:\Users\All Users', GENERIC_READ fails with
ERROR_ACCESS_DENIED. Zero seems to work just fine, both for file
and directory symlinks. */
- sh = CreateFile (path, 0, 0, NULL, OPEN_EXISTING,
- FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
- NULL);
+ if (w32_unicode_filenames)
+ {
+ wchar_t path_w[MAX_PATH];
+
+ filename_to_utf16 (path, path_w);
+ sh = CreateFileW (path_w, 0, 0, NULL, OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT
+ | FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ }
+ else
+ {
+ char path_a[MAX_PATH];
+
+ filename_to_ansi (path, path_a);
+ sh = CreateFileA (path_a, 0, 0, NULL, OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT
+ | FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ }
if (sh != INVALID_HANDLE_VALUE)
{
BYTE reparse_buf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
@@ -4864,89 +5543,27 @@
reparse_data, then convert it to multibyte encoding in
the current locale's codepage. */
WCHAR *lwname;
- BYTE lname[MAX_PATH];
- USHORT lname_len;
+ size_t lname_size;
USHORT lwname_len =
reparse_data->SymbolicLinkReparseBuffer.PrintNameLength;
WCHAR *lwname_src =
reparse_data->SymbolicLinkReparseBuffer.PathBuffer
+
reparse_data->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR);
- /* This updates file_name_codepage which we need below. */
- int dbcs_p = max_filename_mbslen () > 1;
+ size_t size_to_copy = buf_size;
/* According to MSDN, PrintNameLength does not include the
terminating null character. */
lwname = alloca ((lwname_len + 1) * sizeof(WCHAR));
memcpy (lwname, lwname_src, lwname_len);
lwname[lwname_len/sizeof(WCHAR)] = 0; /* null-terminate */
-
- lname_len = WideCharToMultiByte (file_name_codepage, 0, lwname, -1,
- lname, MAX_PATH, NULL, NULL);
- if (!lname_len)
- {
- /* WideCharToMultiByte failed. */
- DWORD w32err1 = GetLastError ();
-
- switch (w32err1)
- {
- case ERROR_INSUFFICIENT_BUFFER:
- errno = ENAMETOOLONG;
- break;
- case ERROR_INVALID_PARAMETER:
- errno = EFAULT;
- break;
- case ERROR_NO_UNICODE_TRANSLATION:
- errno = ENOENT;
- break;
- default:
- errno = EINVAL;
- break;
- }
- }
- else
- {
- size_t size_to_copy = buf_size;
- BYTE *p = lname, *p2;
- BYTE *pend = p + lname_len;
-
- /* Normalize like dostounix_filename does, but we don't
- want to assume that lname is null-terminated. */
- if (dbcs_p)
- p2 = CharNextExA (file_name_codepage, p, 0);
- else
- p2 = p + 1;
- if (*p && *p2 == ':' && *p >= 'A' && *p <= 'Z')
- {
- *p += 'a' - 'A';
- p += 2;
- }
- while (p <= pend)
- {
- if (*p == '\\')
- *p = '/';
- if (dbcs_p)
- {
- p = CharNextExA (file_name_codepage, p, 0);
- /* CharNextExA doesn't advance at null character. */
- if (!*p)
- break;
- }
- else
- ++p;
- }
- /* Testing for null-terminated LNAME is paranoia:
- WideCharToMultiByte should always return a
- null-terminated string when its 4th argument is -1
- and its 3rd argument is null-terminated (which they
- are, see above). */
- if (lname[lname_len - 1] == '\0')
- lname_len--;
- if (lname_len <= buf_size)
- size_to_copy = lname_len;
- strncpy (buf, lname, size_to_copy);
- /* Success! */
- retval = size_to_copy;
- }
+ filename_from_utf16 (lwname, resolved);
+ dostounix_filename (resolved);
+ lname_size = strlen (resolved) + 1;
+ if (lname_size <= buf_size)
+ size_to_copy = lname_size;
+ strncpy (buf, resolved, size_to_copy);
+ /* Success! */
+ retval = size_to_copy;
}
CloseHandle (sh);
}
@@ -4985,7 +5602,7 @@
{
/* Rely on a hack: an open directory is modeled as file descriptor 0,
as in fstatat. FIXME: Add proper support for readlinkat. */
- char fullname[MAX_PATH];
+ char fullname[MAX_UTF8_PATH];
if (fd != AT_FDCWD)
{
@@ -5024,41 +5641,45 @@
static char *
chase_symlinks (const char *file)
{
- static char target[MAX_PATH];
- char link[MAX_PATH];
+ static char target[MAX_UTF8_PATH];
+ char link[MAX_UTF8_PATH];
+ wchar_t target_w[MAX_PATH], link_w[MAX_PATH];
+ char target_a[MAX_PATH], link_a[MAX_PATH];
ssize_t res, link_len;
int loop_count = 0;
- int dbcs_p;
if (is_windows_9x () == TRUE || !is_symlink (file))
return (char *)file;
- if ((link_len = GetFullPathName (file, MAX_PATH, link, NULL)) == 0)
- return (char *)file;
-
- dbcs_p = max_filename_mbslen () > 1;
+ if (w32_unicode_filenames)
+ {
+ wchar_t file_w[MAX_PATH];
+
+ filename_to_utf16 (file, file_w);
+ if (GetFullPathNameW (file_w, MAX_PATH, link_w, NULL) == 0)
+ return (char *)file;
+ filename_from_utf16 (link_w, link);
+ }
+ else
+ {
+ char file_a[MAX_PATH];
+
+ filename_to_ansi (file, file_a);
+ if (GetFullPathNameA (file_a, MAX_PATH, link_a, NULL) == 0)
+ return (char *)file;
+ filename_from_ansi (link_a, link);
+ }
+ link_len = strlen (link);
+
target[0] = '\0';
do {
/* Remove trailing slashes, as we want to resolve the last
non-trivial part of the link name. */
- if (!dbcs_p)
- {
- while (link_len > 3 && IS_DIRECTORY_SEP (link[link_len-1]))
- link[link_len--] = '\0';
- }
- else if (link_len > 3)
- {
- char *n = CharPrevExA (file_name_codepage, link, link + link_len, 0);
-
- while (n >= link + 2 && IS_DIRECTORY_SEP (*n))
- {
- n[1] = '\0';
- n = CharPrevExA (file_name_codepage, link, n, 0);
- }
- }
-
- res = readlink (link, target, MAX_PATH);
+ while (link_len > 3 && IS_DIRECTORY_SEP (link[link_len-1]))
+ link[link_len--] = '\0';
+
+ res = readlink (link, target, MAX_UTF8_PATH);
if (res > 0)
{
target[res] = '\0';
@@ -5069,27 +5690,28 @@
the symlink, then copy the result back to target. */
char *p = link + link_len;
- if (!dbcs_p)
- {
- while (p > link && !IS_ANY_SEP (p[-1]))
- p--;
- }
- else
- {
- char *p1 = CharPrevExA (file_name_codepage, link, p, 0);
-
- while (p > link && !IS_ANY_SEP (*p1))
- {
- p = p1;
- p1 = CharPrevExA (file_name_codepage, link, p1, 0);
- }
- }
+ while (p > link && !IS_ANY_SEP (p[-1]))
+ p--;
strcpy (p, target);
strcpy (target, link);
}
/* Resolve any "." and ".." to get a fully-qualified file name
in link[] again. */
- link_len = GetFullPathName (target, MAX_PATH, link, NULL);
+ if (w32_unicode_filenames)
+ {
+ filename_to_utf16 (target, target_w);
+ link_len = GetFullPathNameW (target_w, MAX_PATH, link_w, NULL);
+ if (link_len > 0)
+ filename_from_utf16 (link_w, link);
+ }
+ else
+ {
+ filename_to_ansi (target, target_a);
+ link_len = GetFullPathNameA (target_a, MAX_PATH, link_a, NULL);
+ if (link_len > 0)
+ filename_from_ansi (link_a, link);
+ }
+ link_len = strlen (link);
}
} while (res > 0 && link_len > 0 && ++loop_count <= 100);
@@ -5206,7 +5828,11 @@
}
}
else if (err == ERROR_FILE_NOT_FOUND
- || err == ERROR_PATH_NOT_FOUND)
+ || err == ERROR_PATH_NOT_FOUND
+ /* ERROR_INVALID_NAME is what we get if
+ w32-unicode-filenames is nil and the file cannot
+ be encoded in the current ANSI codepage. */
+ || err == ERROR_INVALID_NAME)
errno = ENOENT;
else
errno = EIO;
@@ -5284,7 +5910,7 @@
e = errno;
errno = 0;
- if (!set_file_security ((char *)fname, flags, (PSECURITY_DESCRIPTOR)acl))
+ if (!set_file_security (fname, flags, (PSECURITY_DESCRIPTOR)acl))
{
err = GetLastError ();
@@ -5317,7 +5943,12 @@
acl_free (current_acl);
}
}
- else if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND)
+ else if (err == ERROR_FILE_NOT_FOUND
+ || err == ERROR_PATH_NOT_FOUND
+ /* ERROR_INVALID_NAME is what we get if
+ w32-unicode-filenames is nil and the file cannot be
+ encoded in the current ANSI codepage. */
+ || err == ERROR_INVALID_NAME)
errno = ENOENT;
else
errno = EACCES;
@@ -5350,7 +5981,7 @@
struct allocator const *alloc,
ssize_t (*preadlinkat) (int, char const *, char *, size_t))
{
- char linkname[MAX_PATH];
+ char linkname[MAX_UTF8_PATH];
ssize_t link_size;
link_size = preadlinkat (fd, filename, linkname, sizeof(linkname));
@@ -7753,7 +8384,7 @@
HMODULE
w32_delayed_load (Lisp_Object library_id)
{
- HMODULE library_dll = NULL;
+ HMODULE dll_handle = NULL;
CHECK_SYMBOL (library_id);
@@ -7766,26 +8397,56 @@
if (CONSP (dlls))
for (dlls = XCDR (dlls); CONSP (dlls); dlls = XCDR (dlls))
{
- CHECK_STRING_CAR (dlls);
- if ((library_dll = LoadLibrary (SDATA (XCAR (dlls)))))
- {
- char name[MAX_PATH];
- DWORD len;
-
- len = GetModuleFileNameA (library_dll, name, sizeof (name));
- found = Fcons (XCAR (dlls),
- (len > 0)
- /* Possibly truncated */
- ? make_specified_string (name, -1, len, 1)
- : Qnil);
- break;
- }
- }
+ Lisp_Object dll = XCAR (dlls);
+ char name[MAX_UTF8_PATH];
+ DWORD res = -1;
+
+ CHECK_STRING (dll);
+ dll = ENCODE_FILE (dll);
+ if (w32_unicode_filenames)
+ {
+ wchar_t name_w[MAX_PATH];
+
+ filename_to_utf16 (SSDATA (dll), name_w);
+ dll_handle = LoadLibraryW (name_w);
+ if (dll_handle)
+ {
+ res = GetModuleFileNameW (dll_handle, name_w,
+ sizeof (name_w));
+ if (res > 0)
+ filename_from_utf16 (name_w, name);
+ }
+ }
+ else
+ {
+ char name_a[MAX_PATH];
+
+ filename_to_ansi (SSDATA (dll), name_a);
+ dll_handle = LoadLibraryA (name_a);
+ if (dll_handle)
+ {
+ res = GetModuleFileNameA (dll_handle, name_a,
+ sizeof (name_a));
+ if (res > 0)
+ filename_from_ansi (name_a, name);
+ }
+ }
+ if (dll_handle)
+ {
+ ptrdiff_t len = strlen (name);
+ found = Fcons (dll,
+ (res > 0)
+ /* Possibly truncated */
+ ? make_specified_string (name, -1, len, 1)
+ : Qnil);
+ break;
+ }
+ }
Fput (library_id, QCloaded_from, found);
}
- return library_dll;
+ return dll_handle;
}
@@ -7804,6 +8465,12 @@
Lisp_Object init_file;
int fd;
+ /* Implementation note: this function runs early during Emacs
+ startup, before startup.el is run. So Vload_path is still in
+ its initial unibyte form, but it holds UTF-8 encoded file
+ names, since init_callproc was already called. So we do not
+ need to ENCODE_FILE here, but we do need to convert the file
+ names from UTF-8 to ANSI. */
init_file = build_string ("term/w32-win");
fd = openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil);
if (fd < 0)
@@ -7814,6 +8481,8 @@
char *buffer = alloca (1024
+ strlen (init_file_name)
+ strlen (load_path));
+ char *msg = buffer;
+ int needed;
sprintf (buffer,
"The Emacs Windows initialization file \"%s.el\" "
@@ -7825,8 +8494,27 @@
"not unpacked properly.\nSee the README.W32 file in the "
"top-level Emacs directory for more information.",
init_file_name, load_path);
+ needed = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, buffer,
+ -1, NULL, 0);
+ if (needed > 0)
+ {
+ wchar_t *msg_w = alloca ((needed + 1) * sizeof (wchar_t));
+
+ MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, buffer, -1,
+ msg_w, needed);
+ needed = WideCharToMultiByte (CP_ACP, 0, msg_w, -1,
+ NULL, 0, NULL, NULL);
+ if (needed > 0)
+ {
+ char *msg_a = alloca (needed + 1);
+
+ WideCharToMultiByte (CP_ACP, 0, msg_w, -1, msg_a, needed,
+ NULL, NULL);
+ msg = msg_a;
+ }
+ }
MessageBox (NULL,
- buffer,
+ msg,
"Emacs Abort Dialog",
MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
/* Use the low-level system abort. */
@@ -8002,7 +8690,8 @@
g_b_init_get_sid_sub_authority = 0;
g_b_init_get_sid_sub_authority_count = 0;
g_b_init_get_security_info = 0;
- g_b_init_get_file_security = 0;
+ g_b_init_get_file_security_w = 0;
+ g_b_init_get_file_security_a = 0;
g_b_init_get_security_descriptor_owner = 0;
g_b_init_get_security_descriptor_group = 0;
g_b_init_is_valid_sid = 0;
@@ -8021,12 +8710,14 @@
g_b_init_get_length_sid = 0;
g_b_init_get_native_system_info = 0;
g_b_init_get_system_times = 0;
- g_b_init_create_symbolic_link = 0;
+ g_b_init_create_symbolic_link_w = 0;
+ g_b_init_create_symbolic_link_a = 0;
g_b_init_get_security_descriptor_dacl = 0;
g_b_init_convert_sd_to_sddl = 0;
g_b_init_convert_sddl_to_sd = 0;
g_b_init_is_valid_security_descriptor = 0;
- g_b_init_set_file_security = 0;
+ g_b_init_set_file_security_w = 0;
+ g_b_init_set_file_security_a = 0;
g_b_init_get_adapters_info = 0;
num_of_processors = 0;
/* The following sets a handler for shutdown notifications for
@@ -8040,6 +8731,14 @@
/* Reset, in case it has some value inherited from dump time. */
w32_stat_get_owner_group = 0;
+
+ /* If w32_unicode_filenames is non-zero, we will be using Unicode
+ (a.k.a. "wide") APIs to invoke functions that accept file
+ names. */
+ if (is_windows_9x ())
+ w32_unicode_filenames = 0;
+ else
+ w32_unicode_filenames = 1;
}
/* For make-serial-process */
=== modified file 'src/w32.h'
--- a/src/w32.h 2013-08-29 15:32:04 +0000
+++ b/src/w32.h 2013-12-03 15:29:48 +0000
@@ -103,12 +103,6 @@
OVERLAPPED ovl_read;
/* Used for async write operations on serial comm ports. */
OVERLAPPED ovl_write;
- /* Input file, if any, for this subprocess. Should only be non-NULL
- for async subprocesses. */
- char *input_file;
- /* If non-zero, the subprocess input file is temporary and should be
- deleted when the subprocess exits. */
- int pending_deletion;
} child_process;
#define MAXDESC FD_SETSIZE
@@ -152,6 +146,9 @@
/* Get long (aka "true") form of file name, if it exists. */
extern BOOL w32_get_long_filename (char * name, char * buf, int size);
+/* Get the short (a.k.a. "8+3") form of a file name. */
+extern unsigned int w32_get_short_filename (char *, char *, int);
+
/* Prepare our standard handles for proper inheritance by child processes. */
extern void prepare_standard_handles (int in, int out,
int err, HANDLE handles[4]);
@@ -181,8 +178,14 @@
extern void check_windows_init_file (void);
extern void syms_of_ntproc (void);
extern void syms_of_ntterm (void);
-extern void dostounix_filename (register char *, int);
+extern void dostounix_filename (register char *);
extern void unixtodos_filename (register char *);
+extern int filename_from_ansi (const char *, char *);
+extern int filename_to_ansi (const char *, char *);
+extern int filename_from_utf16 (const wchar_t *, char *);
+extern int filename_to_utf16 (const char *, wchar_t *);
+extern Lisp_Object ansi_encode_filename (Lisp_Object);
+
extern BOOL init_winsock (int load_now);
extern void srandom (int);
extern int random (void);
@@ -194,14 +197,10 @@
extern void set_process_dir (char *);
extern int sys_spawnve (int, char *, char **, char **);
extern void register_child (pid_t, int);
-extern void record_infile (pid_t, char *);
-extern void record_pending_deletion (char *);
extern void sys_sleep (int);
extern int sys_link (const char *, const char *);
-
-
#ifdef HAVE_GNUTLS
#include <gnutls/gnutls.h>
=== modified file 'src/w32fns.c'
--- a/src/w32fns.c 2013-12-02 17:28:17 +0000
+++ b/src/w32fns.c 2013-12-11 17:17:49 +0000
@@ -51,6 +51,7 @@
#ifdef WINDOWSNT
#include "w32heap.h"
+#include <mbstring.h>
#endif /* WINDOWSNT */
#if CYGWIN
@@ -6305,18 +6306,31 @@
{
if (msg == WM_NOTIFY)
{
+ OFNOTIFYW * notify_w = (OFNOTIFYW *)lParam;
+ OFNOTIFYA * notify_a = (OFNOTIFYA *)lParam;
+ int dropdown_changed;
+ int dir_index;
#ifdef NTGUI_UNICODE
- OFNOTIFYW * notify = (OFNOTIFYW *)lParam;
+ const int use_unicode = 1;
#else /* !NTGUI_UNICODE */
- OFNOTIFYA * notify = (OFNOTIFYA *)lParam;
+ int use_unicode = w32_unicode_filenames;
#endif /* NTGUI_UNICODE */
+
/* Detect when the Filter dropdown is changed. */
- if (notify->hdr.code == CDN_TYPECHANGE
- || notify->hdr.code == CDN_INITDONE)
+ if (use_unicode)
+ dropdown_changed =
+ notify_w->hdr.code == CDN_TYPECHANGE
+ || notify_w->hdr.code == CDN_INITDONE;
+ else
+ dropdown_changed =
+ notify_a->hdr.code == CDN_TYPECHANGE
+ || notify_a->hdr.code == CDN_INITDONE;
+ if (dropdown_changed)
{
HWND dialog = GetParent (hwnd);
HWND edit_control = GetDlgItem (dialog, FILE_NAME_TEXT_FIELD);
HWND list = GetDlgItem (dialog, FILE_NAME_LIST);
+ int hdr_code;
/* At least on Windows 7, the above attempt to get the window handle
to the File Name Text Field fails. The following code does the
@@ -6334,10 +6348,24 @@
}
/* Directories is in index 2. */
- if (notify->lpOFN->nFilterIndex == 2)
- {
- CommDlg_OpenSave_SetControlText (dialog, FILE_NAME_TEXT_FIELD,
- GUISTR ("Current Directory"));
+ if (use_unicode)
+ {
+ dir_index = notify_w->lpOFN->nFilterIndex;
+ hdr_code = notify_w->hdr.code;
+ }
+ else
+ {
+ dir_index = notify_a->lpOFN->nFilterIndex;
+ hdr_code = notify_a->hdr.code;
+ }
+ if (dir_index == 2)
+ {
+ if (use_unicode)
+ SendMessageW (dialog, CDM_SETCONTROLTEXT, FILE_NAME_TEXT_FIELD,
+ (LPARAM)L"Current Directory");
+ else
+ SendMessageA (dialog, CDM_SETCONTROLTEXT, FILE_NAME_TEXT_FIELD,
+ (LPARAM)"Current Directory");
EnableWindow (edit_control, FALSE);
/* Note that at least on Windows 7, the above call to EnableWindow
disables the window that would ordinarily have focus. If we
@@ -6345,16 +6373,21 @@
no man's land and the user will be unable to tab through the
dialog box (pressing tab will only result in a beep).
Avoid that problem by setting focus to the list here. */
- if (notify->hdr.code == CDN_INITDONE)
+ if (hdr_code == CDN_INITDONE)
SetFocus (list);
}
else
{
/* Don't override default filename on init done. */
- if (notify->hdr.code == CDN_TYPECHANGE)
- CommDlg_OpenSave_SetControlText (dialog,
- FILE_NAME_TEXT_FIELD,
- GUISTR (""));
+ if (hdr_code == CDN_TYPECHANGE)
+ {
+ if (use_unicode)
+ SendMessageW (dialog, CDM_SETCONTROLTEXT,
+ FILE_NAME_TEXT_FIELD, (LPARAM)L"");
+ else
+ SendMessageA (dialog, CDM_SETCONTROLTEXT,
+ FILE_NAME_TEXT_FIELD, (LPARAM)"");
+ }
EnableWindow (edit_control, TRUE);
}
}
@@ -6374,8 +6407,8 @@
(Lisp_Object prompt, Lisp_Object dir, Lisp_Object default_filename,
Lisp_Object mustmatch, Lisp_Object only_dir_p)
{
/* Filter index: 1: All Files, 2: Directories only */
- static const guichar_t filter[] =
- GUISTR ("All Files (*.*)\0*.*\0Directories\0*|*\0");
+ static const wchar_t filter_w[] = L"All Files
(*.*)\0*.*\0Directories\0*|*\0";
+ static const char filter_a[] = "All Files (*.*)\0*.*\0Directories\0*|*\0";
Lisp_Object filename = default_filename;
struct frame *f = SELECTED_FRAME ();
@@ -6388,25 +6421,36 @@
enough struct for the new dialog to trick GetOpenFileName into
giving us the new dialogs on newer versions of Windows. */
struct {
-#ifdef NTGUI_UNICODE
OPENFILENAMEW details;
-#else /* !NTGUI_UNICODE */
+#if _WIN32_WINNT < 0x500 /* < win2k */
+ PVOID pvReserved;
+ DWORD dwReserved;
+ DWORD FlagsEx;
+#endif /* < win2k */
+ } new_file_details_w;
+
+#ifdef NTGUI_UNICODE
+ wchar_t filename_buf_w[32*1024 + 1]; // NT kernel maximum
+ OPENFILENAMEW * file_details_w = &new_file_details_w.details;
+ const int use_unicode = 1;
+#else /* not NTGUI_UNICODE */
+ struct {
OPENFILENAMEA details;
-#endif /* NTGUI_UNICODE */
-
#if _WIN32_WINNT < 0x500 /* < win2k */
PVOID pvReserved;
DWORD dwReserved;
DWORD FlagsEx;
#endif /* < win2k */
- } new_file_details;
-
-#ifdef NTGUI_UNICODE
- wchar_t filename_buf[32*1024 + 1]; // NT kernel maximum
- OPENFILENAMEW * file_details = &new_file_details.details;
-#else /* not NTGUI_UNICODE */
- char filename_buf[MAX_PATH + 1];
- OPENFILENAMEA * file_details = &new_file_details.details;
+ } new_file_details_a;
+ wchar_t filename_buf_w[MAX_PATH + 1], dir_w[MAX_PATH];
+ char filename_buf_a[MAX_PATH + 1], dir_a[MAX_PATH];
+ OPENFILENAMEW * file_details_w = &new_file_details_w.details;
+ OPENFILENAMEA * file_details_a = &new_file_details_a.details;
+ int use_unicode = w32_unicode_filenames;
+ wchar_t *prompt_w;
+ char *prompt_a;
+ int len;
+ char fname_ret[MAX_UTF8_PATH];
#endif /* NTGUI_UNICODE */
struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5, gcpro6;
@@ -6452,6 +6496,10 @@
to_unicode (prompt, &prompt);
to_unicode (dir, &dir);
to_unicode (filename, &filename);
+ if (SBYTES (filename) + 1 > sizeof (filename_buf_w))
+ report_file_error ("filename too long", default_filename);
+
+ memcpy (filename_buf_w, SDATA (filename), SBYTES (filename) + 1);
#else /* !NTGUI_UNICODE */
prompt = ENCODE_FILE (prompt);
dir = ENCODE_FILE (dir);
@@ -6462,6 +6510,49 @@
unixtodos_filename (SDATA (dir));
filename = Fcopy_sequence (filename);
unixtodos_filename (SDATA (filename));
+ if (SBYTES (filename) >= MAX_UTF8_PATH)
+ report_file_error ("filename too long", default_filename);
+ if (w32_unicode_filenames)
+ {
+ filename_to_utf16 (SSDATA (dir), dir_w);
+ if (filename_to_utf16 (SSDATA (filename), filename_buf_w) != 0)
+ {
+ /* filename_to_utf16 sets errno to ENOENT when the file
+ name is too long or cannot be converted to UTF-16. */
+ if (errno == ENOENT && filename_buf_w[MAX_PATH - 1] != 0)
+ report_file_error ("filename too long", default_filename);
+ }
+ len = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS,
+ SSDATA (prompt), -1, NULL, 0);
+ if (len > 32768)
+ len = 32768;
+ prompt_w = alloca (len * sizeof (wchar_t));
+ MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS,
+ SSDATA (prompt), -1, prompt_w, len);
+ }
+ else
+ {
+ filename_to_ansi (SSDATA (dir), dir_a);
+ if (filename_to_ansi (SSDATA (filename), filename_buf_a) != '\0')
+ {
+ /* filename_to_ansi sets errno to ENOENT when the file
+ name is too long or cannot be converted to UTF-16. */
+ if (errno == ENOENT && filename_buf_a[MAX_PATH - 1] != 0)
+ report_file_error ("filename too long", default_filename);
+ }
+ len = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS,
+ SSDATA (prompt), -1, NULL, 0);
+ if (len > 32768)
+ len = 32768;
+ prompt_w = alloca (len * sizeof (wchar_t));
+ MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS,
+ SSDATA (prompt), -1, prompt_w, len);
+ len = WideCharToMultiByte (CP_ACP, 0, prompt_w, -1, NULL, 0, NULL,
NULL);
+ if (len > 32768)
+ len = 32768;
+ prompt_a = alloca (len);
+ WideCharToMultiByte (CP_ACP, 0, prompt_w, -1, prompt_a, len, NULL,
NULL);
+ }
#endif /* NTGUI_UNICODE */
/* Fill in the structure for the call to GetOpenFileName below.
@@ -6470,48 +6561,88 @@
builds, we tell the OS we're using an old version of the
structure if the OS isn't new enough to support the newer
version. */
- memset (&new_file_details, 0, sizeof (new_file_details));
-
- if (w32_major_version > 4 && w32_major_version < 95)
- file_details->lStructSize = sizeof (new_file_details);
+ if (use_unicode)
+ {
+ memset (&new_file_details_w, 0, sizeof (new_file_details_w));
+ if (w32_major_version > 4 && w32_major_version < 95)
+ file_details_w->lStructSize = sizeof (new_file_details_w);
+ else
+ file_details_w->lStructSize = sizeof (*file_details_w);
+ /* Set up the inout parameter for the selected file name. */
+ file_details_w->lpstrFile = filename_buf_w;
+ file_details_w->nMaxFile =
+ sizeof (filename_buf_w) / sizeof (*filename_buf_w);
+ file_details_w->hwndOwner = FRAME_W32_WINDOW (f);
+ /* Undocumented Bug in Common File Dialog:
+ If a filter is not specified, shell links are not resolved. */
+ file_details_w->lpstrFilter = filter_w;
+#ifdef NTGUI_UNICODE
+ file_details_w->lpstrInitialDir = (wchar_t*) SDATA (dir);
+ file_details_w->lpstrTitle = (guichar_t*) SDATA (prompt);
+#else
+ file_details_w->lpstrInitialDir = dir_w;
+ file_details_w->lpstrTitle = prompt_w;
+#endif
+ file_details_w->nFilterIndex = NILP (only_dir_p) ? 1 : 2;
+ file_details_w->Flags = (OFN_HIDEREADONLY | OFN_NOCHANGEDIR
+ | OFN_EXPLORER | OFN_ENABLEHOOK);
+ if (!NILP (mustmatch))
+ {
+ /* Require that the path to the parent directory exists. */
+ file_details_w->Flags |= OFN_PATHMUSTEXIST;
+ /* If we are looking for a file, require that it exists. */
+ if (NILP (only_dir_p))
+ file_details_w->Flags |= OFN_FILEMUSTEXIST;
+ }
+ }
+#ifndef NTGUI_UNICODE
else
- file_details->lStructSize = sizeof (*file_details);
-
- /* Set up the inout parameter for the selected file name. */
- if (SBYTES (filename) + 1 > sizeof (filename_buf))
- report_file_error ("filename too long", default_filename);
-
- memcpy (filename_buf, SDATA (filename), SBYTES (filename) + 1);
- file_details->lpstrFile = filename_buf;
- file_details->nMaxFile = sizeof (filename_buf) / sizeof (*filename_buf);
-
- file_details->hwndOwner = FRAME_W32_WINDOW (f);
- /* Undocumented Bug in Common File Dialog:
- If a filter is not specified, shell links are not resolved. */
- file_details->lpstrFilter = filter;
- file_details->lpstrInitialDir = (guichar_t*) SDATA (dir);
- file_details->lpstrTitle = (guichar_t*) SDATA (prompt);
- file_details->nFilterIndex = NILP (only_dir_p) ? 1 : 2;
- file_details->Flags = (OFN_HIDEREADONLY | OFN_NOCHANGEDIR
- | OFN_EXPLORER | OFN_ENABLEHOOK);
-
- if (!NILP (mustmatch))
{
- /* Require that the path to the parent directory exists. */
- file_details->Flags |= OFN_PATHMUSTEXIST;
- /* If we are looking for a file, require that it exists. */
- if (NILP (only_dir_p))
- file_details->Flags |= OFN_FILEMUSTEXIST;
+ memset (&new_file_details_a, 0, sizeof (new_file_details_a));
+ if (w32_major_version > 4 && w32_major_version < 95)
+ file_details_a->lStructSize = sizeof (new_file_details_a);
+ else
+ file_details_a->lStructSize = sizeof (*file_details_a);
+ file_details_a->lpstrFile = filename_buf_a;
+ file_details_a->nMaxFile =
+ sizeof (filename_buf_a) / sizeof (*filename_buf_a);
+ file_details_a->hwndOwner = FRAME_W32_WINDOW (f);
+ file_details_a->lpstrFilter = filter_a;
+ file_details_a->lpstrInitialDir = dir_a;
+ file_details_a->lpstrTitle = prompt_a;
+ file_details_a->nFilterIndex = NILP (only_dir_p) ? 1 : 2;
+ file_details_a->Flags = (OFN_HIDEREADONLY | OFN_NOCHANGEDIR
+ | OFN_EXPLORER | OFN_ENABLEHOOK);
+ if (!NILP (mustmatch))
+ {
+ /* Require that the path to the parent directory exists. */
+ file_details_a->Flags |= OFN_PATHMUSTEXIST;
+ /* If we are looking for a file, require that it exists. */
+ if (NILP (only_dir_p))
+ file_details_a->Flags |= OFN_FILEMUSTEXIST;
+ }
}
+#endif /* !NTGUI_UNICODE */
{
int count = SPECPDL_INDEX ();
/* Prevent redisplay. */
specbind (Qinhibit_redisplay, Qt);
block_input ();
- file_details->lpfnHook = file_dialog_callback;
-
- file_opened = GUI_FN (GetOpenFileName) (file_details);
+ if (use_unicode)
+ {
+ file_details_w->lpfnHook = file_dialog_callback;
+
+ file_opened = GetOpenFileNameW (file_details_w);
+ }
+#ifndef NTGUI_UNICODE
+ else
+ {
+ file_details_a->lpfnHook = file_dialog_callback;
+
+ file_opened = GetOpenFileNameA (file_details_a);
+ }
+#endif /* !NTGUI_UNICODE */
unblock_input ();
unbind_to (count, Qnil);
}
@@ -6520,10 +6651,14 @@
{
/* Get an Emacs string from the value Windows gave us. */
#ifdef NTGUI_UNICODE
- filename = from_unicode_buffer (filename_buf);
+ filename = from_unicode_buffer (filename_buf_w);
#else /* !NTGUI_UNICODE */
- dostounix_filename (filename_buf, 0);
- filename = DECODE_FILE (build_string (filename_buf));
+ if (use_unicode)
+ filename_from_utf16 (filename_buf_w, fname_ret);
+ else
+ filename_from_ansi (filename_buf_a, fname_ret);
+ dostounix_filename (fname_ret);
+ filename = DECODE_FILE (build_unibyte_string (fname_ret));
#endif /* NTGUI_UNICODE */
#ifdef CYGWIN
@@ -6532,10 +6667,12 @@
/* Strip the dummy filename off the end of the string if we
added it to select a directory. */
- if (file_details->nFilterIndex == 2)
- {
- filename = Ffile_name_directory (filename);
- }
+ if (use_unicode && file_details_w->nFilterIndex == 2
+#ifndef NTGUI_UNICODE
+ || !use_unicode && file_details_a->nFilterIndex == 2
+#endif
+ )
+ filename = Ffile_name_directory (filename);
}
/* User canceled the dialog without making a selection. */
else if (!CommDlgExtendedError ())
@@ -6582,38 +6719,80 @@
operation = intern ("delete-directory");
filename = Fdirectory_file_name (filename);
}
+
+ /* Must have fully qualified file names for moving files to Recycle
+ Bin. */
filename = Fexpand_file_name (filename, Qnil);
handler = Ffind_file_name_handler (filename, operation);
if (!NILP (handler))
return call2 (handler, operation, filename);
-
- encoded_file = ENCODE_FILE (filename);
-
- {
- const char * path;
- SHFILEOPSTRUCT file_op;
- char tmp_path[MAX_PATH + 1];
-
- path = map_w32_filename (SDATA (encoded_file), NULL);
-
- /* On Windows, write permission is required to delete/move files. */
- _chmod (path, 0666);
-
- memset (tmp_path, 0, sizeof (tmp_path));
- strcpy (tmp_path, path);
-
- memset (&file_op, 0, sizeof (file_op));
- file_op.hwnd = HWND_DESKTOP;
- file_op.wFunc = FO_DELETE;
- file_op.pFrom = tmp_path;
- file_op.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_ALLOWUNDO
- | FOF_NOERRORUI | FOF_NO_CONNECTED_ELEMENTS;
- file_op.fAnyOperationsAborted = FALSE;
-
- if (SHFileOperation (&file_op) != 0)
- report_file_error ("Removing old name", list1 (filename));
- }
+ else
+ {
+ const char * path;
+ int result;
+
+ encoded_file = ENCODE_FILE (filename);
+
+ path = map_w32_filename (SDATA (encoded_file), NULL);
+
+ /* The Unicode version of SHFileOperation is not supported on
+ Windows 9X. */
+ if (w32_unicode_filenames && os_subtype != OS_9X)
+ {
+ SHFILEOPSTRUCTW file_op_w;
+ /* We need one more element beyond MAX_PATH because this is
+ a list of file names, with the last element double-null
+ terminated. */
+ wchar_t tmp_path_w[MAX_PATH + 1];
+
+ memset (tmp_path_w, 0, sizeof (tmp_path_w));
+ filename_to_utf16 (path, tmp_path_w);
+
+ /* On Windows, write permission is required to delete/move files. */
+ _wchmod (tmp_path_w, 0666);
+
+ memset (&file_op_w, 0, sizeof (file_op_w));
+ file_op_w.hwnd = HWND_DESKTOP;
+ file_op_w.wFunc = FO_DELETE;
+ file_op_w.pFrom = tmp_path_w;
+ file_op_w.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_ALLOWUNDO
+ | FOF_NOERRORUI | FOF_NO_CONNECTED_ELEMENTS;
+ file_op_w.fAnyOperationsAborted = FALSE;
+
+ result = SHFileOperationW (&file_op_w);
+ }
+ else
+ {
+ SHFILEOPSTRUCTA file_op_a;
+ char tmp_path_a[MAX_PATH + 1];
+
+ memset (tmp_path_a, 0, sizeof (tmp_path_a));
+ filename_to_ansi (path, tmp_path_a);
+
+ /* If a file cannot be represented in ANSI codepage, don't
+ let them inadvertently delete other files because some
+ characters are interpreted as a wildcards. */
+ if (_mbspbrk (tmp_path_a, "?*"))
+ result = ERROR_FILE_NOT_FOUND;
+ else
+ {
+ _chmod (tmp_path_a, 0666);
+
+ memset (&file_op_a, 0, sizeof (file_op_a));
+ file_op_a.hwnd = HWND_DESKTOP;
+ file_op_a.wFunc = FO_DELETE;
+ file_op_a.pFrom = tmp_path_a;
+ file_op_a.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_ALLOWUNDO
+ | FOF_NOERRORUI | FOF_NO_CONNECTED_ELEMENTS;
+ file_op_a.fAnyOperationsAborted = FALSE;
+
+ result = SHFileOperationA (&file_op_a);
+ }
+ }
+ if (result != 0)
+ report_file_error ("Removing old name", list1 (filename));
+ }
return Qnil;
}
@@ -6688,38 +6867,159 @@
6 - start minimized */)
(Lisp_Object operation, Lisp_Object document, Lisp_Object parameters,
Lisp_Object show_flag)
{
- Lisp_Object current_dir;
char *errstr;
+ Lisp_Object current_dir = BVAR (current_buffer, directory);;
+ wchar_t *doc_w = NULL, *params_w = NULL, *ops_w = NULL;
+ intptr_t result;
+#ifndef CYGWIN
+ int use_unicode = w32_unicode_filenames;
+ char *doc_a = NULL, *params_a = NULL, *ops_a = NULL;
+#endif
CHECK_STRING (document);
- /* Encode filename, current directory and parameters. */
- current_dir = BVAR (current_buffer, directory);
-
#ifdef CYGWIN
current_dir = Fcygwin_convert_file_name_to_windows (current_dir, Qt);
if (STRINGP (document))
document = Fcygwin_convert_file_name_to_windows (document, Qt);
-#endif /* CYGWIN */
+ /* Encode filename, current directory and parameters. */
current_dir = GUI_ENCODE_FILE (current_dir);
if (STRINGP (document))
- document = GUI_ENCODE_FILE (document);
+ {
+ document = GUI_ENCODE_FILE (document);
+ doc_w = GUI_SDATA (document);
+ }
if (STRINGP (parameters))
- parameters = GUI_ENCODE_SYSTEM (parameters);
-
- if ((int) GUI_FN (ShellExecute) (NULL,
- (STRINGP (operation) ?
- GUI_SDATA (operation) : NULL),
- GUI_SDATA (document),
- (STRINGP (parameters) ?
- GUI_SDATA (parameters) : NULL),
- GUI_SDATA (current_dir),
- (INTEGERP (show_flag) ?
- XINT (show_flag) : SW_SHOWDEFAULT))
- > 32)
+ {
+ parameters = GUI_ENCODE_SYSTEM (parameters);
+ params_w = GUI_SDATA (parameters);
+ }
+ if (STRINGP (operation))
+ {
+ operation = GUI_ENCODE_SYSTEM (operation);
+ ops_w = GUI_SDATA (operation);
+ }
+ result = (intptr_t) ShellExecuteW (NULL, ops_w, doc_w, params_w,
+ GUI_SDATA (current_dir),
+ (INTEGERP (show_flag)
+ ? XINT (show_flag) : SW_SHOWDEFAULT));
+#else /* !CYGWIN */
+ if (use_unicode)
+ {
+ wchar_t document_w[MAX_PATH], current_dir_w[MAX_PATH];
+
+ /* Encode filename, current directory and parameters, and
+ convert operation to UTF-16. */
+ current_dir = ENCODE_FILE (current_dir);
+ filename_to_utf16 (SSDATA (current_dir), current_dir_w);
+ if (STRINGP (document))
+ {
+ document = ENCODE_FILE (document);
+ filename_to_utf16 (SSDATA (document), document_w);
+ doc_w = document_w;
+ }
+ if (STRINGP (parameters))
+ {
+ int len;
+
+ parameters = ENCODE_SYSTEM (parameters);
+ len = MultiByteToWideChar (CP_ACP, MB_ERR_INVALID_CHARS,
+ SSDATA (parameters), -1, NULL, 0);
+ if (len > 32768)
+ len = 32768;
+ params_w = alloca (len * sizeof (wchar_t));
+ MultiByteToWideChar (CP_ACP, MB_ERR_INVALID_CHARS,
+ SSDATA (parameters), -1, params_w, len);
+ }
+ if (STRINGP (operation))
+ {
+ /* Assume OPERATION is pure ASCII. */
+ const char *s = SSDATA (operation);
+ wchar_t *d;
+ int len = SBYTES (operation) + 1;
+
+ if (len > 32768)
+ len = 32768;
+ d = ops_w = alloca (len * sizeof (wchar_t));
+ while (d < ops_w + len - 1)
+ *d++ = *s++;
+ *d = 0;
+ }
+ result = (intptr_t) ShellExecuteW (NULL, ops_w, doc_w, params_w,
+ current_dir_w,
+ (INTEGERP (show_flag)
+ ? XINT (show_flag) : SW_SHOWDEFAULT));
+ }
+ else
+ {
+ char document_a[MAX_PATH], current_dir_a[MAX_PATH];
+
+ current_dir = ENCODE_FILE (current_dir);
+ filename_to_ansi (SSDATA (current_dir), current_dir_a);
+ if (STRINGP (document))
+ {
+ ENCODE_FILE (document);
+ filename_to_ansi (SSDATA (document), document_a);
+ doc_a = document_a;
+ }
+ if (STRINGP (parameters))
+ {
+ int len;
+
+ parameters = ENCODE_SYSTEM (parameters);
+ params_a = SSDATA (parameters);
+ }
+ if (STRINGP (operation))
+ {
+ /* Assume OPERATION is pure ASCII. */
+ ops_a = SSDATA (operation);
+ }
+ result = (intptr_t) ShellExecuteA (NULL, ops_a, doc_a, params_a,
+ current_dir_a,
+ (INTEGERP (show_flag)
+ ? XINT (show_flag) : SW_SHOWDEFAULT));
+ }
+#endif /* !CYGWIN */
+
+ if (result > 32)
return Qt;
- errstr = w32_strerror (0);
+
+ switch (result)
+ {
+ case SE_ERR_ACCESSDENIED:
+ errstr = w32_strerror (ERROR_ACCESS_DENIED);
+ break;
+ case SE_ERR_ASSOCINCOMPLETE:
+ case SE_ERR_NOASSOC:
+ errstr = w32_strerror (ERROR_NO_ASSOCIATION);
+ break;
+ case SE_ERR_DDEBUSY:
+ case SE_ERR_DDEFAIL:
+ errstr = w32_strerror (ERROR_DDE_FAIL);
+ break;
+ case SE_ERR_DDETIMEOUT:
+ errstr = w32_strerror (ERROR_TIMEOUT);
+ break;
+ case SE_ERR_DLLNOTFOUND:
+ errstr = w32_strerror (ERROR_DLL_NOT_FOUND);
+ break;
+ case SE_ERR_FNF:
+ errstr = w32_strerror (ERROR_FILE_NOT_FOUND);
+ break;
+ case SE_ERR_OOM:
+ errstr = w32_strerror (ERROR_NOT_ENOUGH_MEMORY);
+ break;
+ case SE_ERR_PNF:
+ errstr = w32_strerror (ERROR_PATH_NOT_FOUND);
+ break;
+ case SE_ERR_SHARE:
+ errstr = w32_strerror (ERROR_SHARING_VIOLATION);
+ break;
+ default:
+ errstr = w32_strerror (0);
+ break;
+ }
/* The error string might be encoded in the locale's encoding. */
if (!NILP (Vlocale_coding_system))
{
@@ -7132,14 +7432,23 @@
added rather late on. */
{
HMODULE hKernel = GetModuleHandle ("kernel32");
- BOOL (*pfn_GetDiskFreeSpaceEx)
+ BOOL (*pfn_GetDiskFreeSpaceExW)
+ (wchar_t *, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER)
+ = (void *) GetProcAddress (hKernel, "GetDiskFreeSpaceExW");
+ BOOL (*pfn_GetDiskFreeSpaceExA)
(char *, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER)
- = (void *) GetProcAddress (hKernel, "GetDiskFreeSpaceEx");
+ = (void *) GetProcAddress (hKernel, "GetDiskFreeSpaceExA");
+ bool have_pfn_GetDiskFreeSpaceEx =
+ (w32_unicode_filenames && pfn_GetDiskFreeSpaceExW
+ || !w32_unicode_filenames && pfn_GetDiskFreeSpaceExA);
/* On Windows, we may need to specify the root directory of the
volume holding FILENAME. */
- char rootname[MAX_PATH];
+ char rootname[MAX_UTF8_PATH];
+ wchar_t rootname_w[MAX_PATH];
+ char rootname_a[MAX_PATH];
char *name = SDATA (encoded);
+ BOOL result;
/* find the root name of the volume if given */
if (isalpha (name[0]) && name[1] == ':')
@@ -7165,7 +7474,12 @@
*str = 0;
}
- if (pfn_GetDiskFreeSpaceEx)
+ if (w32_unicode_filenames)
+ filename_to_utf16 (rootname, rootname_w);
+ else
+ filename_to_ansi (rootname, rootname_a);
+
+ if (have_pfn_GetDiskFreeSpaceEx)
{
/* Unsigned large integers cannot be cast to double, so
use signed ones instead. */
@@ -7173,10 +7487,17 @@
LARGE_INTEGER freebytes;
LARGE_INTEGER totalbytes;
- if (pfn_GetDiskFreeSpaceEx (rootname,
- (ULARGE_INTEGER *)&availbytes,
- (ULARGE_INTEGER *)&totalbytes,
- (ULARGE_INTEGER *)&freebytes))
+ if (w32_unicode_filenames)
+ result = pfn_GetDiskFreeSpaceExW (rootname_w,
+ (ULARGE_INTEGER *)&availbytes,
+ (ULARGE_INTEGER *)&totalbytes,
+ (ULARGE_INTEGER *)&freebytes);
+ else
+ result = pfn_GetDiskFreeSpaceExA (rootname_a,
+ (ULARGE_INTEGER *)&availbytes,
+ (ULARGE_INTEGER *)&totalbytes,
+ (ULARGE_INTEGER *)&freebytes);
+ if (result)
value = list3 (make_float ((double) totalbytes.QuadPart),
make_float ((double) freebytes.QuadPart),
make_float ((double) availbytes.QuadPart));
@@ -7188,11 +7509,19 @@
DWORD free_clusters;
DWORD total_clusters;
- if (GetDiskFreeSpace (rootname,
- §ors_per_cluster,
- &bytes_per_sector,
- &free_clusters,
- &total_clusters))
+ if (w32_unicode_filenames)
+ result = GetDiskFreeSpaceW (rootname_w,
+ §ors_per_cluster,
+ &bytes_per_sector,
+ &free_clusters,
+ &total_clusters);
+ else
+ result = GetDiskFreeSpaceA (rootname_a,
+ §ors_per_cluster,
+ &bytes_per_sector,
+ &free_clusters,
+ &total_clusters);
+ if (result)
value = list3 (make_float ((double) total_clusters
* sectors_per_cluster * bytes_per_sector),
make_float ((double) free_clusters
@@ -7207,6 +7536,7 @@
#endif /* WINDOWSNT */
+#ifdef WINDOWSNT
DEFUN ("default-printer-name", Fdefault_printer_name, Sdefault_printer_name,
0, 0, 0, doc: /* Return the name of Windows default printer device. */)
(void)
@@ -7214,8 +7544,11 @@
static char pname_buf[256];
int err;
HANDLE hPrn;
- PRINTER_INFO_2 *ppi2 = NULL;
+ PRINTER_INFO_2W *ppi2w = NULL;
+ PRINTER_INFO_2A *ppi2a = NULL;
DWORD dwNeeded = 0, dwReturned = 0;
+ char server_name[MAX_UTF8_PATH], share_name[MAX_UTF8_PATH];
+ char port_name[MAX_UTF8_PATH];
/* Retrieve the default string from Win.ini (the registry).
* String will be in form "printername,drivername,portname".
@@ -7227,55 +7560,89 @@
/* We want to know more than the printer name */
if (!OpenPrinter (pname_buf, &hPrn, NULL))
return Qnil;
- GetPrinter (hPrn, 2, NULL, 0, &dwNeeded);
+ /* GetPrinterW is not supported by unicows.dll. */
+ if (w32_unicode_filenames && os_subtype != OS_9X)
+ GetPrinterW (hPrn, 2, NULL, 0, &dwNeeded);
+ else
+ GetPrinterA (hPrn, 2, NULL, 0, &dwNeeded);
if (dwNeeded == 0)
{
ClosePrinter (hPrn);
return Qnil;
}
- /* Allocate memory for the PRINTER_INFO_2 struct */
- ppi2 = xmalloc (dwNeeded);
- if (!ppi2)
- {
- ClosePrinter (hPrn);
- return Qnil;
- }
/* Call GetPrinter again with big enough memory block. */
- err = GetPrinter (hPrn, 2, (LPBYTE)ppi2, dwNeeded, &dwReturned);
- ClosePrinter (hPrn);
- if (!err)
- {
- xfree (ppi2);
- return Qnil;
- }
-
- if (ppi2)
- {
- if (ppi2->Attributes & PRINTER_ATTRIBUTE_SHARED && ppi2->pServerName)
- {
- /* a remote printer */
- if (*ppi2->pServerName == '\\')
- snprintf (pname_buf, sizeof (pname_buf), "%s\\%s",
ppi2->pServerName,
- ppi2->pShareName);
- else
- snprintf (pname_buf, sizeof (pname_buf), "\\\\%s\\%s",
ppi2->pServerName,
- ppi2->pShareName);
- pname_buf[sizeof (pname_buf) - 1] = '\0';
- }
- else
- {
- /* a local printer */
- strncpy (pname_buf, ppi2->pPortName, sizeof (pname_buf));
- pname_buf[sizeof (pname_buf) - 1] = '\0';
- /* `pPortName' can include several ports, delimited by ','.
- * we only use the first one. */
- strtok (pname_buf, ",");
- }
- xfree (ppi2);
- }
-
- return build_string (pname_buf);
+ if (w32_unicode_filenames && os_subtype != OS_9X)
+ {
+ /* Allocate memory for the PRINTER_INFO_2 struct. */
+ ppi2w = xmalloc (dwNeeded);
+ err = GetPrinterW (hPrn, 2, (LPBYTE)ppi2w, dwNeeded, &dwReturned);
+ ClosePrinter (hPrn);
+ if (!err)
+ {
+ xfree (ppi2w);
+ return Qnil;
+ }
+
+ if ((ppi2w->Attributes & PRINTER_ATTRIBUTE_SHARED)
+ && ppi2w->pServerName)
+ {
+ filename_from_utf16 (ppi2w->pServerName, server_name);
+ filename_from_utf16 (ppi2w->pShareName, share_name);
+ }
+ else
+ {
+ server_name[0] = '\0';
+ filename_from_utf16 (ppi2w->pPortName, port_name);
+ }
+ }
+ else
+ {
+ ppi2a = xmalloc (dwNeeded);
+ err = GetPrinterA (hPrn, 2, (LPBYTE)ppi2a, dwNeeded, &dwReturned);
+ ClosePrinter (hPrn);
+ if (!err)
+ {
+ xfree (ppi2a);
+ return Qnil;
+ }
+
+ if ((ppi2a->Attributes & PRINTER_ATTRIBUTE_SHARED)
+ && ppi2a->pServerName)
+ {
+ filename_from_ansi (ppi2a->pServerName, server_name);
+ filename_from_ansi (ppi2a->pShareName, share_name);
+ }
+ else
+ {
+ server_name[0] = '\0';
+ filename_from_ansi (ppi2a->pPortName, port_name);
+ }
+ }
+
+ if (server_name[0])
+ {
+ /* a remote printer */
+ if (server_name[0] == '\\')
+ snprintf (pname_buf, sizeof (pname_buf), "%s\\%s", server_name,
+ share_name);
+ else
+ snprintf (pname_buf, sizeof (pname_buf), "\\\\%s\\%s", server_name,
+ share_name);
+ pname_buf[sizeof (pname_buf) - 1] = '\0';
+ }
+ else
+ {
+ /* a local printer */
+ strncpy (pname_buf, port_name, sizeof (pname_buf));
+ pname_buf[sizeof (pname_buf) - 1] = '\0';
+ /* `pPortName' can include several ports, delimited by ','.
+ * we only use the first one. */
+ strtok (pname_buf, ",");
+ }
+
+ return DECODE_FILE (build_unibyte_string (pname_buf));
}
+#endif /* WINDOWSNT */
/* Equivalent of strerror for W32 error codes. */
@@ -7946,9 +8313,9 @@
#ifdef WINDOWSNT
defsubr (&Sfile_system_info);
-#endif
-
defsubr (&Sdefault_printer_name);
+#endif
+
defsubr (&Sset_message_beep);
hourglass_hwnd = NULL;
=== modified file 'src/w32notify.c'
--- a/src/w32notify.c 2013-11-28 19:40:15 +0000
+++ b/src/w32notify.c 2013-12-08 18:29:12 +0000
@@ -105,7 +105,7 @@
OVERLAPPED *io_info; /* the OVERLAPPED structure for async I/O */
BOOL subtree; /* whether to watch subdirectories */
DWORD filter; /* bit mask for events to watch */
- char *watchee; /* the file we are interested in */
+ char *watchee; /* the file we are interested in, UTF-8 encoded */
HANDLE dir; /* handle to the watched directory */
HANDLE thr; /* handle to the thread that watches */
volatile int terminate; /* if non-zero, request for the thread to terminate
*/
@@ -332,15 +332,43 @@
if (!file)
return NULL;
- hdir = CreateFile (parent_dir,
- FILE_LIST_DIRECTORY,
- /* FILE_SHARE_DELETE doesn't preclude other
- processes from deleting files inside
- parent_dir. */
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- NULL, OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
- NULL);
+ if (w32_unicode_filenames)
+ {
+ wchar_t dir_w[MAX_PATH], file_w[MAX_PATH];
+
+ filename_to_utf16 (parent_dir, dir_w);
+ if (*file)
+ filename_to_utf16 (file, file_w);
+ else
+ file_w[0] = 0;
+
+ hdir = CreateFileW (dir_w,
+ FILE_LIST_DIRECTORY,
+ /* FILE_SHARE_DELETE doesn't preclude other
+ processes from deleting files inside
+ parent_dir. */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ NULL, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
+ NULL);
+ }
+ else
+ {
+ char dir_a[MAX_PATH], file_a[MAX_PATH];
+
+ filename_to_ansi (parent_dir, dir_a);
+ if (*file)
+ filename_to_ansi (file, file_a);
+ else
+ file_a[0] = '\0';
+
+ hdir = CreateFileA (dir_a,
+ FILE_LIST_DIRECTORY,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ NULL, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
+ NULL);
+ }
if (hdir == INVALID_HANDLE_VALUE)
return NULL;
@@ -490,9 +518,7 @@
generate notifications correctly, though. */)
(Lisp_Object file, Lisp_Object filter, Lisp_Object callback)
{
- Lisp_Object encoded_file, watch_object, watch_descriptor;
- char parent_dir[MAX_PATH], *basename;
- size_t fn_len;
+ Lisp_Object dirfn, basefn, watch_object, watch_descriptor;
DWORD flags;
BOOL subdirs = FALSE;
struct notification *dirwatch = NULL;
@@ -510,49 +536,33 @@
Qnil);
}
- /* We need a full absolute file name of FILE, and we need to remove
- any trailing slashes from it, so that GetFullPathName below gets
- the basename part correctly. */
- file = Fdirectory_file_name (Fexpand_file_name (file, Qnil));
- encoded_file = ENCODE_FILE (file);
-
- fn_len = GetFullPathName (SDATA (encoded_file), MAX_PATH, parent_dir,
- &basename);
- if (!fn_len)
- {
- errstr = w32_strerror (0);
- errno = EINVAL;
- if (!NILP (Vlocale_coding_system))
- lisp_errstr
- = code_convert_string_norecord (build_unibyte_string (errstr),
- Vlocale_coding_system, 0);
- else
- lisp_errstr = build_string (errstr);
- report_file_error ("GetFullPathName failed",
- Fcons (lisp_errstr, Fcons (file, Qnil)));
- }
/* filenotify.el always passes us a directory, either the parent
directory of a file to be watched, or the directory to be
watched. */
- if (file_directory_p (parent_dir))
- basename = "";
- else
+ file = Fdirectory_file_name (Fexpand_file_name (file, Qnil));
+ if (NILP (Ffile_directory_p (file)))
{
/* This should only happen if we are called directly, not via
- filenotify.el. If BASENAME is NULL, the argument was the
- root directory on its drive. */
- if (basename)
- basename[-1] = '\0';
- else
+ filenotify.el. If BASEFN is empty, the argument was the root
+ directory on its drive. */
+ dirfn = ENCODE_FILE (Ffile_name_directory (file));
+ basefn = ENCODE_FILE (Ffile_name_nondirectory (file));
+ if (*SDATA (basefn) == '\0')
subdirs = TRUE;
}
+ else
+ {
+ dirfn = ENCODE_FILE (file);
+ basefn = Qnil;
+ }
if (!NILP (Fmember (Qsubtree, filter)))
subdirs = TRUE;
flags = filter_list_to_flags (filter);
- dirwatch = add_watch (parent_dir, basename, subdirs, flags);
+ dirwatch = add_watch (SSDATA (dirfn), NILP (basefn) ? "" : SSDATA (basefn),
+ subdirs, flags);
if (!dirwatch)
{
DWORD err = GetLastError ();
=== modified file 'src/w32proc.c'
--- a/src/w32proc.c 2013-11-28 19:40:15 +0000
+++ b/src/w32proc.c 2013-12-07 17:21:57 +0000
@@ -30,6 +30,7 @@
#include <fcntl.h>
#include <signal.h>
#include <sys/file.h>
+#include <mbstring.h>
/* must include CRT headers *before* config.h */
#include <config.h>
@@ -861,8 +862,6 @@
cp->pid = -1;
cp->procinfo.hProcess = NULL;
cp->status = STATUS_READ_ERROR;
- cp->input_file = NULL;
- cp->pending_deletion = 0;
/* use manual reset event so that select() will function properly */
cp->char_avail = CreateEvent (NULL, TRUE, FALSE, NULL);
@@ -911,21 +910,6 @@
if (!CHILD_ACTIVE (cp) && cp->procinfo.hProcess == NULL)
return;
- /* Delete the child's temporary input file, if any, that is pending
- deletion. */
- if (cp->input_file)
- {
- if (cp->pending_deletion)
- {
- if (unlink (cp->input_file))
- DebPrint (("delete_child.unlink (%s) failed, errno: %d\n",
- cp->input_file, errno));
- cp->pending_deletion = 0;
- }
- xfree (cp->input_file);
- cp->input_file = NULL;
- }
-
/* reap thread if necessary */
if (cp->thrd)
{
@@ -1073,9 +1057,10 @@
return 0;
}
-/* To avoid Emacs changing directory, we just record here the directory
- the new process should start in. This is set just before calling
- sys_spawnve, and is not generally valid at any other time. */
+/* To avoid Emacs changing directory, we just record here the
+ directory the new process should start in. This is set just before
+ calling sys_spawnve, and is not generally valid at any other time.
+ Note that this directory's name is UTF-8 encoded. */
static char * process_dir;
static BOOL
@@ -1088,7 +1073,8 @@
SECURITY_DESCRIPTOR sec_desc;
#endif
DWORD flags;
- char dir[ MAXPATHLEN ];
+ char dir[ MAX_PATH ];
+ char *p;
if (cp == NULL) emacs_abort ();
@@ -1118,16 +1104,22 @@
sec_attrs.lpSecurityDescriptor = NULL /* &sec_desc */;
sec_attrs.bInheritHandle = FALSE;
- strcpy (dir, process_dir);
- unixtodos_filename (dir);
+ filename_to_ansi (process_dir, dir);
+ /* Can't use unixtodos_filename here, since that needs its file name
+ argument encoded in UTF-8. OTOH, process_dir, which _is_ in
+ UTF-8, points, to the directory computed by our caller, and we
+ don't want to modify that, either. */
+ for (p = dir; *p; p = CharNextA (p))
+ if (*p == '/')
+ *p = '\\';
flags = (!NILP (Vw32_start_process_share_console)
? CREATE_NEW_PROCESS_GROUP
: CREATE_NEW_CONSOLE);
if (NILP (Vw32_start_process_inherit_error_mode))
flags |= CREATE_DEFAULT_ERROR_MODE;
- if (!CreateProcess (exe, cmdline, &sec_attrs, NULL, TRUE,
- flags, env, dir, &start, &cp->procinfo))
+ if (!CreateProcessA (exe, cmdline, &sec_attrs, NULL, TRUE,
+ flags, env, dir, &start, &cp->procinfo))
goto EH_Fail;
cp->pid = (int) cp->procinfo.dwProcessId;
@@ -1182,45 +1174,6 @@
fd_info[fd].cp = cp;
}
-/* Record INFILE as an input file for process PID. */
-void
-record_infile (pid_t pid, char *infile)
-{
- child_process *cp;
-
- /* INFILE should never be NULL, since xstrdup would have signaled
- memory full condition in that case, see callproc.c where this
- function is called. */
- eassert (infile);
-
- cp = find_child_pid ((DWORD)pid);
- if (cp == NULL)
- {
- DebPrint (("record_infile is unable to find pid %lu\n", pid));
- return;
- }
-
- cp->input_file = infile;
-}
-
-/* Mark the input file INFILE of the corresponding subprocess as
- temporary, to be deleted when the subprocess exits. */
-void
-record_pending_deletion (char *infile)
-{
- child_process *cp;
-
- eassert (infile);
-
- for (cp = child_procs + (child_proc_count-1); cp >= child_procs; cp--)
- if (CHILD_ACTIVE (cp)
- && cp->input_file && xstrcasecmp (cp->input_file, infile) == 0)
- {
- cp->pending_deletion = 1;
- break;
- }
-}
-
/* Called from waitpid when a process exits. */
static void
reap_subprocess (child_process *cp)
@@ -1427,6 +1380,8 @@
# define IMAGE_OPTIONAL_HEADER32 IMAGE_OPTIONAL_HEADER
#endif
+/* Implementation note: This function works with file names encoded in
+ the current ANSI codepage. */
static void
w32_executable_type (char * filename,
int * is_dos_app,
@@ -1617,6 +1572,7 @@
char *sepchars = " \t*?";
/* This is for native w32 apps; modified below for Cygwin apps. */
char escape_char = '\\';
+ char cmdname_a[MAX_PATH];
/* We don't care about the other modes */
if (mode != _P_NOWAIT)
@@ -1625,12 +1581,15 @@
return -1;
}
- /* Handle executable names without an executable suffix. */
- program = build_string (cmdname);
- if (NILP (Ffile_executable_p (program)))
+ /* Handle executable names without an executable suffix. The caller
+ already searched exec-path and verified the file is executable,
+ but start-process doesn't do that for file names that are already
+ absolute. So we double-check this here, just in case. */
+ if (faccessat (AT_FDCWD, cmdname, X_OK, AT_EACCESS) != 0)
{
struct gcpro gcpro1;
+ program = build_string (cmdname);
full = Qnil;
GCPRO1 (program);
openp (Vexec_path, program, Vexec_suffixes, &full, make_number (X_OK));
@@ -1640,12 +1599,27 @@
errno = EINVAL;
return -1;
}
- program = full;
+ program = ENCODE_FILE (full);
+ cmdname = SDATA (program);
}
/* make sure argv[0] and cmdname are both in DOS format */
- cmdname = SDATA (program);
unixtodos_filename (cmdname);
+ /* argv[0] was encoded by caller using ENCODE_FILE, so it is in
+ UTF-8. All the other arguments are encoded by ENCODE_SYSTEM or
+ some such, and are in some ANSI codepage. We need to have
+ argv[0] encoded in ANSI codepage. */
+ filename_to_ansi (cmdname, cmdname_a);
+ /* We explicitly require that the command's file name be encodable
+ in the current ANSI codepage, because we will be invoking it via
+ the ANSI APIs. */
+ if (_mbspbrk (cmdname_a, "?"))
+ {
+ errno = ENOENT;
+ return -1;
+ }
+ /* From here on, CMDNAME is an ANSI-encoded string. */
+ cmdname = cmdname_a;
argv[0] = cmdname;
/* Determine whether program is a 16-bit DOS executable, or a 32-bit Windows
@@ -1663,7 +1637,9 @@
while leaving the real app name as argv[0]. */
if (is_dos_app)
{
- cmdname = alloca (MAXPATHLEN);
+ char *p;
+
+ cmdname = alloca (MAX_PATH);
if (egetenv ("CMDPROXY"))
strcpy (cmdname, egetenv ("CMDPROXY"));
else
@@ -1671,7 +1647,12 @@
strcpy (cmdname, SDATA (Vinvocation_directory));
strcat (cmdname, "cmdproxy.exe");
}
- unixtodos_filename (cmdname);
+
+ /* Can't use unixtodos_filename here, since that needs its file
+ name argument encoded in UTF-8. */
+ for (p = cmdname; *p; p = CharNextA (p))
+ if (*p == '/')
+ *p = '\\';
}
/* we have to do some conjuring here to put argv and envp into the
@@ -2673,10 +2654,11 @@
filename = Fexpand_file_name (filename, Qnil);
/* luckily, this returns the short version of each element in the path. */
- if (GetShortPathName (SDATA (ENCODE_FILE (filename)), shortname, MAX_PATH)
== 0)
+ if (w32_get_short_filename (SDATA (ENCODE_FILE (filename)),
+ shortname, MAX_PATH) == 0)
return Qnil;
- dostounix_filename (shortname, 0);
+ dostounix_filename (shortname);
/* No need to DECODE_FILE, because 8.3 names are pure ASCII. */
return build_string (shortname);
@@ -2690,7 +2672,7 @@
All path elements in FILENAME are converted to their long names. */)
(Lisp_Object filename)
{
- char longname[ MAX_PATH ];
+ char longname[ MAX_UTF8_PATH ];
int drive_only = 0;
CHECK_STRING (filename);
@@ -2702,10 +2684,11 @@
/* first expand it. */
filename = Fexpand_file_name (filename, Qnil);
- if (!w32_get_long_filename (SDATA (ENCODE_FILE (filename)), longname,
MAX_PATH))
+ if (!w32_get_long_filename (SDATA (ENCODE_FILE (filename)), longname,
+ MAX_UTF8_PATH))
return Qnil;
- dostounix_filename (longname, 0);
+ dostounix_filename (longname);
/* If we were passed only a drive, make sure that a slash is not appended
for consistency with directories. Allow for drive mapping via SUBST
@@ -2713,7 +2696,7 @@
if (drive_only && longname[1] == ':' && longname[2] == '/' && !longname[3])
longname[2] = '\0';
- return DECODE_FILE (build_string (longname));
+ return DECODE_FILE (build_unibyte_string (longname));
}
DEFUN ("w32-set-process-priority", Fw32_set_process_priority,
=== modified file 'src/w32term.c'
--- a/src/w32term.c 2013-12-11 15:06:04 +0000
+++ b/src/w32term.c 2013-12-11 17:06:29 +0000
@@ -52,6 +52,7 @@
#include "keymap.h"
#ifdef WINDOWSNT
+#include "w32.h" /* for filename_from_utf16, filename_from_ansi */
#include "w32heap.h"
#endif
@@ -3128,7 +3129,14 @@
HDROP hdrop;
POINT p;
WORD num_files;
- guichar_t *name;
+ wchar_t name_w[MAX_PATH];
+#ifdef NTGUI_UNICODE
+ const int use_unicode = 1;
+#else
+ int use_unicode = w32_unicode_filenames;
+ char name_a[MAX_PATH];
+ char file[MAX_UTF8_PATH];
+#endif
int i, len;
result->kind = DRAG_N_DROP_EVENT;
@@ -3153,17 +3161,30 @@
for (i = 0; i < num_files; i++)
{
- len = GUI_FN (DragQueryFile) (hdrop, i, NULL, 0);
- if (len <= 0)
- continue;
-
- name = alloca ((len + 1) * sizeof (*name));
- GUI_FN (DragQueryFile) (hdrop, i, name, len + 1);
+ if (use_unicode)
+ {
+ eassert (DragQueryFileW (hdrop, i, NULL, 0) < MAX_PATH);
+ /* If DragQueryFile returns zero, it failed to fetch a file
+ name. */
+ if (DragQueryFileW (hdrop, i, name_w, MAX_PATH) == 0)
+ continue;
#ifdef NTGUI_UNICODE
- files = Fcons (from_unicode_buffer (name), files);
+ files = Fcons (from_unicode_buffer (name_w), files);
#else
- files = Fcons (DECODE_FILE (build_string (name)), files);
+ filename_from_utf16 (name_w, file);
+ files = Fcons (DECODE_FILE (build_unibyte_string (file)), files);
#endif /* NTGUI_UNICODE */
+ }
+#ifndef NTGUI_UNICODE
+ else
+ {
+ eassert (DragQueryFileA (hdrop, i, NULL, 0) < MAX_PATH);
+ if (DragQueryFileA (hdrop, i, name_a, MAX_PATH) == 0)
+ continue;
+ filename_from_ansi (name_a, file);
+ files = Fcons (DECODE_FILE (build_unibyte_string (file)), files);
+ }
+#endif
}
DragFinish (hdrop);
@@ -6640,6 +6661,18 @@
With MS Windows or Nextstep, the value is t. */);
Vx_toolkit_scroll_bars = Qt;
+ DEFVAR_BOOL ("w32-unicode-filenames",
+ w32_unicode_filenames,
+ doc: /* Non-nil means use Unicode APIs when passing file names to the OS.
+A value of nil means file names passed to the OS APIs and returned
+from those APIs are encoded/decoded using the ANSI codepage
+specified by `file-name-coding-system'.
+
+This variable is set to non-nil by default when Emacs runs on Windows
+systems of the NT family, including W2K, XP, Vista, Windows 7 and
+Windows 8. It is set to nil on Windows 9X. */);
+ w32_unicode_filenames = 0;
+
/* Tell Emacs about this window system. */
Fprovide (Qw32, Qnil);
}
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Emacs-diffs] trunk r115490: Support MS-Windows file names that use characters outside of ANSI codepage.,
Eli Zaretskii <=