------------------------------------------------------------ revno: 115504 committer: Dmitry Gutov branch nick: trunk timestamp: Fri 2013-12-13 06:14:17 +0200 message: Make blink-matching-paren perform blinking without moving the cursor * lisp/faces.el (paren-showing-faces, show-paren-match) (show-paren-mismatch): Move from paren.el. * lisp/simple.el (blink-matching--overlay): New variable. (blink-matching-open): Instead of moving point, highlight the matching paren with an overlay (http://lists.gnu.org/archive/html/emacs-devel/2013-12/msg00333.html). diff: === modified file 'lisp/ChangeLog' --- lisp/ChangeLog 2013-12-13 01:54:09 +0000 +++ lisp/ChangeLog 2013-12-13 04:14:17 +0000 @@ -1,3 +1,13 @@ +2013-12-13 Dmitry Gutov + + * simple.el (blink-matching--overlay): New variable. + (blink-matching-open): Instead of moving point, highlight the + matching paren with an overlay + (http://lists.gnu.org/archive/html/emacs-devel/2013-12/msg00333.html). + + * faces.el (paren-showing-faces, show-paren-match) + (show-paren-mismatch): Move from paren.el. + 2013-12-13 Leo Liu * indent.el (indent-region): Disable progress reporter in === modified file 'lisp/faces.el' --- lisp/faces.el 2013-11-08 17:26:03 +0000 +++ lisp/faces.el 2013-12-13 04:14:17 +0000 @@ -2573,6 +2573,30 @@ "Face for displaying the currently selected item in TTY menus." :group 'basic-faces) +(defgroup paren-showing-faces nil + "Faces used to highlight paren matches." + :group 'paren-showing + :group 'faces + :version "22.1") + +(defface show-paren-match + '((((class color) (background light)) + :background "turquoise") ; looks OK on tty (becomes cyan) + (((class color) (background dark)) + :background "steelblue3") ; looks OK on tty (becomes blue) + (((background dark)) + :background "grey50") + (t + :background "gray")) + "Face used for a matching paren." + :group 'paren-showing-faces) + +(defface show-paren-mismatch + '((((class color)) (:foreground "white" :background "purple")) + (t (:inverse-video t))) + "Face used for a mismatching paren." + :group 'paren-showing-faces) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Manipulating font names. === modified file 'lisp/paren.el' --- lisp/paren.el 2013-05-30 15:44:54 +0000 +++ lisp/paren.el 2013-12-13 04:14:17 +0000 @@ -72,30 +72,8 @@ :group 'paren-showing :version "20.3") -(defgroup paren-showing-faces nil - "Group for faces of Show Paren mode." - :group 'paren-showing - :group 'faces - :version "22.1") - -(defface show-paren-match - '((((class color) (background light)) - :background "turquoise") ; looks OK on tty (becomes cyan) - (((class color) (background dark)) - :background "steelblue3") ; looks OK on tty (becomes blue) - (((background dark)) - :background "grey50") - (t - :background "gray")) - "Show Paren mode face used for a matching paren." - :group 'paren-showing-faces) (define-obsolete-face-alias 'show-paren-match-face 'show-paren-match "22.1") -(defface show-paren-mismatch - '((((class color)) (:foreground "white" :background "purple")) - (t (:inverse-video t))) - "Show Paren mode face used for a mismatching paren." - :group 'paren-showing-faces) (define-obsolete-face-alias 'show-paren-mismatch-face 'show-paren-mismatch "22.1") === modified file 'lisp/simple.el' --- lisp/simple.el 2013-12-13 01:03:04 +0000 +++ lisp/simple.el 2013-12-13 04:14:17 +0000 @@ -6308,8 +6308,15 @@ START can be nil, if it was not found. The function should return non-nil if the two tokens do not match.") +(defvar blink-matching--overlay + (let ((ol (make-overlay (point) (point) nil t))) + (overlay-put ol 'face 'show-paren-match) + (delete-overlay ol) + ol) + "Overlay used to highlight the matching paren.") + (defun blink-matching-open () - "Move cursor momentarily to the beginning of the sexp before point." + "Momentarily highlight the beginning of the sexp before point." (interactive) (when (and (not (bobp)) blink-matching-paren) @@ -6351,13 +6358,17 @@ (message "No matching parenthesis found")))) ((not blinkpos) nil) ((pos-visible-in-window-p blinkpos) - ;; Matching open within window, temporarily move to blinkpos but only - ;; if `blink-matching-paren-on-screen' is non-nil. + ;; Matching open within window, temporarily highlight char + ;; after blinkpos but only if `blink-matching-paren-on-screen' + ;; is non-nil. (and blink-matching-paren-on-screen (not show-paren-mode) - (save-excursion - (goto-char blinkpos) - (sit-for blink-matching-delay)))) + (unwind-protect + (progn + (move-overlay blink-matching--overlay blinkpos (1+ blinkpos) + (current-buffer)) + (sit-for blink-matching-delay)) + (delete-overlay blink-matching--overlay)))) (t (save-excursion (goto-char blinkpos) ------------------------------------------------------------ revno: 115503 committer: Paul Eggert branch nick: trunk timestamp: Thu 2013-12-12 19:44:59 -0800 message: * gnutls.c, gnutls.h (emacs_gnutls_record_check_pending): Return ptrdiff_t, not int, since it's a buffer size. Reindent/reparen some macros to a more Gnuish style. diff: === modified file 'src/ChangeLog' --- src/ChangeLog 2013-12-12 19:23:25 +0000 +++ src/ChangeLog 2013-12-13 03:44:59 +0000 @@ -1,3 +1,9 @@ +2013-12-13 Paul Eggert + + * gnutls.c, gnutls.h (emacs_gnutls_record_check_pending): + Return ptrdiff_t, not int, since it's a buffer size. + Reindent/reparen some macros to a more Gnuish style. + 2013-12-12 Paul Eggert Avoid undefined behavior with huge regexp interval counts. === modified file 'src/gnutls.c' --- src/gnutls.c 2013-12-07 17:21:57 +0000 +++ src/gnutls.c 2013-12-13 03:44:59 +0000 @@ -365,7 +365,7 @@ return ret; } -int +ptrdiff_t emacs_gnutls_record_check_pending (gnutls_session_t state) { return fn_gnutls_record_check_pending (state); === modified file 'src/gnutls.h' --- src/gnutls.h 2013-10-17 06:42:21 +0000 +++ src/gnutls.h 2013-12-13 03:44:59 +0000 @@ -45,25 +45,38 @@ GNUTLS_STAGE_READY } gnutls_initstage_t; -#define GNUTLS_EMACS_ERROR_NOT_LOADED GNUTLS_E_APPLICATION_ERROR_MIN + 1 +#define GNUTLS_EMACS_ERROR_NOT_LOADED (GNUTLS_E_APPLICATION_ERROR_MIN + 1) #define GNUTLS_EMACS_ERROR_INVALID_TYPE GNUTLS_E_APPLICATION_ERROR_MIN #define GNUTLS_INITSTAGE(proc) (XPROCESS (proc)->gnutls_initstage) -#define GNUTLS_PROCESS_USABLE(proc) (GNUTLS_INITSTAGE(proc) >= GNUTLS_STAGE_READY) - -#define GNUTLS_LOG(level, max, string) do { if (level <= max) { gnutls_log_function (level, "(Emacs) " string); } } while (0) - -#define GNUTLS_LOG2(level, max, string, extra) do { if (level <= max) { gnutls_log_function2 (level, "(Emacs) " string, extra); } } while (0) - -#define GNUTLS_LOG2i(level, max, string, extra) do { if (level <= max) { gnutls_log_function2i (level, "(Emacs) " string, extra); } } while (0) +#define GNUTLS_PROCESS_USABLE(proc) \ + (GNUTLS_INITSTAGE (proc) >= GNUTLS_STAGE_READY) + +#define GNUTLS_LOG(level, max, string) \ + do { \ + if ((level) <= (max)) \ + gnutls_log_function (level, "(Emacs) " string); \ + } while (0) + +#define GNUTLS_LOG2(level, max, string, extra) \ + do { \ + if ((level) <= (max)) \ + gnutls_log_function2 (level, "(Emacs) " string, extra); \ + } while (0) + +#define GNUTLS_LOG2i(level, max, string, extra) \ + do { \ + if ((level) <= (max)) \ + gnutls_log_function2i (level, "(Emacs) " string, extra); \ + } while (0) extern ptrdiff_t emacs_gnutls_write (struct Lisp_Process *proc, const char *buf, ptrdiff_t nbyte); extern ptrdiff_t emacs_gnutls_read (struct Lisp_Process *proc, char *buf, ptrdiff_t nbyte); -extern int emacs_gnutls_record_check_pending (gnutls_session_t state); +extern ptrdiff_t emacs_gnutls_record_check_pending (gnutls_session_t state); #ifdef WINDOWSNT extern void emacs_gnutls_transport_set_errno (gnutls_session_t state, int err); #endif ------------------------------------------------------------ revno: 115502 fixes bug: http://debbugs.gnu.org/16108 committer: Leo Liu branch nick: trunk timestamp: Fri 2013-12-13 09:54:09 +0800 message: * bindings.el (visual-order-cursor-movement): Fix version. * indent.el (indent-region): Disable progress reporter in minibuffer. diff: === modified file 'lisp/ChangeLog' --- lisp/ChangeLog 2013-12-13 01:03:04 +0000 +++ lisp/ChangeLog 2013-12-13 01:54:09 +0000 @@ -1,3 +1,10 @@ +2013-12-13 Leo Liu + + * indent.el (indent-region): Disable progress reporter in + minibuffer. (Bug#16108) + + * bindings.el (visual-order-cursor-movement): Fix version. + 2013-12-13 Fabián Ezequiel Gallina * progmodes/python.el (python-pdbtrack-stacktrace-info-regexp): === modified file 'lisp/bindings.el' --- lisp/bindings.el 2013-12-13 01:03:04 +0000 +++ lisp/bindings.el 2013-12-13 01:54:09 +0000 @@ -710,7 +710,7 @@ :type '(choice (const :tag "Logical-order cursor movement" nil) (const :tag "Visual-order cursor movement" t)) :group 'display - :version "24.5") + :version "24.4") (defun right-char (&optional n) "Move point N characters to the right (to the left if N is negative). === modified file 'lisp/indent.el' --- lisp/indent.el 2013-10-17 19:31:11 +0000 +++ lisp/indent.el 2013-12-13 01:54:09 +0000 @@ -481,13 +481,14 @@ (save-excursion (setq end (copy-marker end)) (goto-char start) - (let ((pr (make-progress-reporter "Indenting region..." (point) end))) - (while (< (point) end) - (or (and (bolp) (eolp)) - (indent-according-to-mode)) + (let ((pr (unless (minibufferp) + (make-progress-reporter "Indenting region..." (point) end)))) + (while (< (point) end) + (or (and (bolp) (eolp)) + (indent-according-to-mode)) (forward-line 1) - (progress-reporter-update pr (point))) - (progress-reporter-done pr) + (and pr (progress-reporter-update pr (point)))) + (and pr (progress-reporter-done pr)) (move-marker end nil))))) ;; In most cases, reindenting modifies the buffer, but it may also ;; leave it unmodified, in which case we have to deactivate the mark ------------------------------------------------------------ revno: 115501 fixes bug: http://debbugs.gnu.org/14397 committer: Juri Linkov branch nick: trunk timestamp: Fri 2013-12-13 03:03:04 +0200 message: * lisp/simple.el : Remove key bindings duplicated with bindings.el. diff: === modified file 'lisp/ChangeLog' --- lisp/ChangeLog 2013-12-13 00:56:12 +0000 +++ lisp/ChangeLog 2013-12-13 01:03:04 +0000 @@ -7,6 +7,11 @@ 2013-12-13 Juri Linkov + * simple.el : Remove key bindings duplicated + with bindings.el. (Bug#14397) + +2013-12-13 Juri Linkov + * comint.el (comint-mode-map): Replace `delete-char' with `delete-forward-char'. (Bug#16109) === modified file 'lisp/bindings.el' --- lisp/bindings.el 2013-12-11 00:45:10 +0000 +++ lisp/bindings.el 2013-12-13 01:03:04 +0000 @@ -1069,7 +1069,7 @@ (kp-enter enter) (kp-decimal ?.) (kp-0 ?0) (kp-1 ?1) (kp-2 ?2) (kp-3 ?3) (kp-4 ?4) (kp-5 ?5) (kp-6 ?6) (kp-7 ?7) (kp-8 ?8) (kp-9 ?9) - (kp-add +) (kp-subtract -) (kp-multiply *) (kp-divide /)))) + (kp-add ?+) (kp-subtract ?-) (kp-multiply ?*) (kp-divide ?/)))) (dolist (pair keys) (dolist (mod modifiers) (define-key function-key-map === modified file 'lisp/simple.el' --- lisp/simple.el 2013-12-11 14:22:26 +0000 +++ lisp/simple.el 2013-12-13 01:03:04 +0000 @@ -7128,17 +7128,11 @@ (normal (nth 1 keypad-normal))) (put keypad 'ascii-character normal) (define-key function-key-map (vector keypad) (vector normal)))) - '((kp-0 ?0) (kp-1 ?1) (kp-2 ?2) (kp-3 ?3) (kp-4 ?4) - (kp-5 ?5) (kp-6 ?6) (kp-7 ?7) (kp-8 ?8) (kp-9 ?9) - (kp-space ?\s) + ;; See also kp-keys bound in bindings.el. + '((kp-space ?\s) (kp-tab ?\t) (kp-enter ?\r) - (kp-multiply ?*) - (kp-add ?+) (kp-separator ?,) - (kp-subtract ?-) - (kp-decimal ?.) - (kp-divide ?/) (kp-equal ?=) ;; Do the same for various keys that are represented as symbols under ;; GUIs but naturally correspond to characters. ------------------------------------------------------------ revno: 115500 fixes bug: http://debbugs.gnu.org/15378 committer: Fabián Ezequiel Gallina branch nick: trunk timestamp: Thu 2013-12-12 21:56:12 -0300 message: * lisp/progmodes/python.el (python-pdbtrack-stacktrace-info-regexp): Also match after beginning of line. (python-pdbtrack-set-tracked-buffer): Fix logic for remote files. Thanks to Russell Sim. diff: === modified file 'lisp/ChangeLog' --- lisp/ChangeLog 2013-12-13 00:47:18 +0000 +++ lisp/ChangeLog 2013-12-13 00:56:12 +0000 @@ -1,3 +1,10 @@ +2013-12-13 Fabián Ezequiel Gallina + + * progmodes/python.el (python-pdbtrack-stacktrace-info-regexp): + Also match after beginning of line. + (python-pdbtrack-set-tracked-buffer): Fix logic for remote + files. Thanks to Russell Sim. (Bug#15378) + 2013-12-13 Juri Linkov * comint.el (comint-mode-map): Replace `delete-char' with === modified file 'lisp/progmodes/python.el' --- lisp/progmodes/python.el 2013-12-12 23:32:05 +0000 +++ lisp/progmodes/python.el 2013-12-13 00:56:12 +0000 @@ -2482,7 +2482,7 @@ :safe 'booleanp) (defcustom python-pdbtrack-stacktrace-info-regexp - "^> \\([^\"(<]+\\)(\\([0-9]+\\))\\([?a-zA-Z0-9_<>]+\\)()" + "> \\([^\"(<]+\\)(\\([0-9]+\\))\\([?a-zA-Z0-9_<>]+\\)()" "Regular Expression matching stacktrace information. Used to extract the current line and module being inspected." :type 'string @@ -2501,7 +2501,9 @@ "Set the buffer for FILE-NAME as the tracked buffer. Internally it uses the `python-pdbtrack-tracked-buffer' variable. Returns the tracked buffer." - (let ((file-buffer (get-file-buffer file-name))) + (let ((file-buffer (get-file-buffer + (concat (file-remote-p default-directory) + file-name)))) (if file-buffer (setq python-pdbtrack-tracked-buffer file-buffer) (setq file-buffer (find-file-noselect file-name)) ------------------------------------------------------------ revno: 115499 committer: Paul Eggert branch nick: trunk timestamp: Thu 2013-12-12 16:51:47 -0800 message: Fix bug in previous regex.c change, which broke a\{2,}. diff: === modified file 'src/regex.c' --- src/regex.c 2013-12-12 19:23:25 +0000 +++ src/regex.c 2013-12-13 00:51:47 +0000 @@ -3311,15 +3311,15 @@ GET_INTERVAL_COUNT (lower_bound); if (c == ',') - { - GET_INTERVAL_COUNT (upper_bound); - if (upper_bound < lower_bound) - FREE_STACK_RETURN (REG_BADBR); - } + GET_INTERVAL_COUNT (upper_bound); else /* Interval such as `{1}' => match exactly once. */ upper_bound = lower_bound; + if (lower_bound < 0 + || (0 <= upper_bound && upper_bound < lower_bound)) + FREE_STACK_RETURN (REG_BADBR); + if (!(syntax & RE_NO_BK_BRACES)) { if (c != '\\') ------------------------------------------------------------ revno: 115498 fixes bug: http://debbugs.gnu.org/16109 committer: Juri Linkov branch nick: trunk timestamp: Fri 2013-12-13 02:47:18 +0200 message: * lisp/comint.el (comint-mode-map): Replace `delete-char' with `delete-forward-char'. diff: === modified file 'lisp/ChangeLog' --- lisp/ChangeLog 2013-12-12 23:32:05 +0000 +++ lisp/ChangeLog 2013-12-13 00:47:18 +0000 @@ -1,3 +1,8 @@ +2013-12-13 Juri Linkov + + * comint.el (comint-mode-map): Replace `delete-char' with + `delete-forward-char'. (Bug#16109) + 2013-12-12 Fabián Ezequiel Gallina * progmodes/python.el (python-indent-calculate-indentation): Fix === modified file 'lisp/comint.el' --- lisp/comint.el 2013-12-11 00:11:26 +0000 +++ lisp/comint.el 2013-12-13 00:47:18 +0000 @@ -460,10 +460,10 @@ (define-key map "\e\C-l" 'comint-show-output) (define-key map "\C-m" 'comint-send-input) (define-key map "\C-d" 'comint-delchar-or-maybe-eof) - ;; The following two are standardly aliased to C-d, + ;; The following two are standardly bound to delete-forward-char, ;; but they should never do EOF, just delete. - (define-key map [delete] 'delete-char) - (define-key map [kp-delete] 'delete-char) + (define-key map [delete] 'delete-forward-char) + (define-key map [kp-delete] 'delete-forward-char) (define-key map "\C-c " 'comint-accumulate) (define-key map "\C-c\C-x" 'comint-get-next-from-history) (define-key map "\C-c\C-a" 'comint-bol-or-process-mark) ------------------------------------------------------------ revno: 115497 fixes bug: http://debbugs.gnu.org/15731 committer: Fabián Ezequiel Gallina branch nick: trunk timestamp: Thu 2013-12-12 20:32:05 -0300 message: * lisp/progmodes/python.el (python-indent-calculate-indentation): Fix de-denters cornercase. * test/automated/python-tests.el (python-indent-dedenters-2): New test. diff: === modified file 'lisp/ChangeLog' --- lisp/ChangeLog 2013-12-12 19:47:11 +0000 +++ lisp/ChangeLog 2013-12-12 23:32:05 +0000 @@ -1,3 +1,8 @@ +2013-12-12 Fabián Ezequiel Gallina + + * progmodes/python.el (python-indent-calculate-indentation): Fix + de-denters cornercase. (Bug#15731) + 2013-12-12 Stefan Monnier * emacs-lisp/nadvice.el: Add `depth' property to manage ordering. === modified file 'lisp/progmodes/python.el' --- lisp/progmodes/python.el 2013-12-12 05:37:09 +0000 +++ lisp/progmodes/python.el 2013-12-12 23:32:05 +0000 @@ -780,19 +780,31 @@ ;; indentation, in the case current line starts with a ;; `python-indent-dedenters' de-indent one level. (`after-line - (- - (save-excursion - (goto-char context-start) - (current-indentation)) - (if (or (save-excursion - (back-to-indentation) - (looking-at (regexp-opt python-indent-dedenters))) - (save-excursion - (python-util-forward-comment -1) - (python-nav-beginning-of-statement) - (looking-at (regexp-opt python-indent-block-enders)))) - python-indent-offset - 0))) + (let* ((pair (save-excursion + (goto-char context-start) + (cons + (current-indentation) + (python-info-beginning-of-block-p)))) + (context-indentation (car pair)) + (after-block-start-p (cdr pair)) + (adjustment + (if (or (save-excursion + (back-to-indentation) + (and + ;; De-indent only when dedenters are not + ;; next to a block start. This allows + ;; one-liner constructs such as: + ;; if condition: print "yay" + ;; else: print "wry" + (not after-block-start-p) + (looking-at (regexp-opt python-indent-dedenters)))) + (save-excursion + (python-util-forward-comment -1) + (python-nav-beginning-of-statement) + (looking-at (regexp-opt python-indent-block-enders)))) + python-indent-offset + 0))) + (- context-indentation adjustment))) ;; When inside of a string, do nothing. just use the current ;; indentation. XXX: perhaps it would be a good idea to ;; invoke standard text indentation here === modified file 'test/ChangeLog' --- test/ChangeLog 2013-12-12 20:07:40 +0000 +++ test/ChangeLog 2013-12-12 23:32:05 +0000 @@ -1,5 +1,9 @@ 2013-12-12 Fabián Ezequiel Gallina + * automated/python-tests.el (python-indent-dedenters-2): New test. + +2013-12-12 Fabián Ezequiel Gallina + * automated/python-tests.el (python-indent-after-comment-1) (python-indent-after-comment-2): New tests. === modified file 'test/automated/python-tests.el' --- test/automated/python-tests.el 2013-12-12 05:37:09 +0000 +++ test/automated/python-tests.el 2013-12-12 23:32:05 +0000 @@ -458,6 +458,28 @@ (should (eq (car (python-indent-context)) 'after-beginning-of-block)) (should (= (python-indent-calculate-indentation) 12)))) +(ert-deftest python-indent-dedenters-2 () + "Check one-liner block special case.." + (python-tests-with-temp-buffer + " +cond = True +if cond: + + if cond: print 'True' +else: print 'False' + +else: + return +" + (python-tests-look-at "else: print 'False'") + ;; When a block has code after ":" it's just considered a simple + ;; line as that's a common thing to happen in one-liners. + (should (eq (car (python-indent-context)) 'after-line)) + (should (= (python-indent-calculate-indentation) 4)) + (python-tests-look-at "else:") + (should (eq (car (python-indent-context)) 'after-line)) + (should (= (python-indent-calculate-indentation) 0)))) + (ert-deftest python-indent-after-backslash-1 () "The most common case." (python-tests-with-temp-buffer ------------------------------------------------------------ revno: 115496 committer: Glenn Morris branch nick: trunk timestamp: Thu 2013-12-12 17:41:31 -0500 message: Generate info/dir from .texi files rather than .info files * Makefile.in (install-info): Handle missing info/dir. (info_dir_deps): New variable. (${srcdir}/info/dir): Depend on .texi files rather than .info files. (check-info): Update topics. * build-aux/make-info-dir: Use .texi files rather than .info files. Update topics. * doc/lispintro/emacs-lisp-intro.texi: * doc/lispref/elisp.texi: Tweak dircategory (all the others use "Emacs" + lower-case). diff: === modified file 'ChangeLog' --- ChangeLog 2013-12-12 09:39:13 +0000 +++ ChangeLog 2013-12-12 22:41:31 +0000 @@ -1,5 +1,12 @@ 2013-12-12 Glenn Morris + * Makefile.in (install-info): Handle missing info/dir. + (info_dir_deps): New variable. + (${srcdir}/info/dir): Depend on .texi files rather than .info files. + (check-info): Update topics. + * build-aux/make-info-dir: Use .texi files rather than .info files. + Update topics. + * Makefile.in (install-info): Remove some useless subshells. Stop keeping info/dir in the repository. === modified file 'Makefile.in' --- Makefile.in 2013-12-12 09:08:26 +0000 +++ Makefile.in 2013-12-12 22:41:31 +0000 @@ -643,6 +643,9 @@ ## If info/dir is missing, but we have install-info, we should let ## that handle it. If info/dir is present and we do not have install-info, ## we should check for missing entries and add them by hand. +## +## FIXME: +## If HAVE_MAKEINFO = no and there are no info files, do not install info/dir. install-info: info umask 022; ${MKDIR_P} "$(DESTDIR)${infodir}" -unset CDPATH; \ @@ -652,6 +655,7 @@ true; \ else \ [ -f "$(DESTDIR)${infodir}/dir" ] || \ + [ ! -f ${srcdir}/info/dir ] || \ ${INSTALL_DATA} ${srcdir}/info/dir "$(DESTDIR)${infodir}/dir"; \ info_misc=`cd doc/misc && $(QUIET_SUBMAKE) $(MAKE) -s echo-info`; \ cd ${srcdir}/info ; \ @@ -961,12 +965,21 @@ info-dir: ${srcdir}/info/dir +## Not strictly necessary, but speeds things up a bit by stopping +## the info-dir rule from running when not needed. +## Hopefully doc/misc/*.texi is not too long for some systems? +info_dir_deps = ${srcdir}/build-aux/dir_top \ + ${srcdir}/doc/emacs/emacs.texi \ + ${srcdir}/doc/lispintro/emacs-lisp-intro.texi \ + ${srcdir}/doc/lispref/elisp.texi ${srcdir}/doc/misc/*.texi + ## It would be much simpler if info/dir was only created in the ## installation location by the install-info rule, but we also ## need one in the source directory for people running uninstalled. -## FIXME Change make-info-dir to use texi files rather than info-files, -## depend on ${srcdir}/doc/*/*.texi here rather than info-real. -${srcdir}/info/dir: info-real ${srcdir}/build-aux/dir_top +## FIXME it would be faster to use the install-info program if we have it, +## but then we would need to depend on info-real, which would +## slow down parallelization. +${srcdir}/info/dir: ${info_dir_deps} tempfile=info-dir.$$$$; \ rm -f $${tempfile}; \ thisdir=`pwd`; \ @@ -1051,7 +1064,7 @@ esac ; \ cat=`sed -n 's/^INFO-DIR-SECTION //p' $${file}`; \ case $${cat} in \ - "Texinfo documentation system" | "Emacs"| "GNU Emacs Lisp" | \ + "Texinfo documentation system" | "Emacs"| "Emacs lisp" | \ "Emacs editing modes" | "Emacs network features" | \ "Emacs misc features" | "Emacs lisp libraries" ) : ;; \ *) bad="$${bad} $${file}" ;; \ === modified file 'build-aux/make-info-dir' --- build-aux/make-info-dir 2013-12-12 08:54:21 +0000 +++ build-aux/make-info-dir 2013-12-12 22:41:31 +0000 @@ -60,7 +60,13 @@ ## FIXME inefficient looping. -for topic in "Texinfo documentation system" "Emacs" "GNU Emacs Lisp" \ +## What we should do is loop once over files, collecting topic and +## direntry information for each. Then loop over topics and write +## out the results. But that seems to require associative arrays, +## and I do not know how to do that with portable sh. +## Could use Emacs instead of sh, but till now info generation does +## not require Emacs to have been built. +for topic in "Texinfo documentation system" "Emacs" "Emacs lisp" \ "Emacs editing modes" "Emacs network features" "Emacs misc features" \ "Emacs lisp libraries"; do @@ -68,19 +74,25 @@ $topic EOF - for file in info/*.info; do + ## Bit faster than doc/*/*.texi. + for file in doc/emacs/emacs.texi doc/lispintro/emacs-lisp-intro.texi \ + doc/lispref/elisp.texi doc/misc/*.texi; do ## FIXME do not ignore w32 if OS is w32. case $file in - *-xtra.info|*efaq-w32.info) continue ;; + *-xtra.texi|*efaq-w32.texi|*doclicense.texi) continue ;; esac - dircat=`sed -n -e 's/^INFO-DIR-SECTION //p' $file` + dircat=`sed -n -e 's/@value{emacsname}/Emacs/' -e 's/^@dircategory //p' $file` ## TODO warn about unknown topics. + ## (check-info in top-level Makefile does that.) test "$dircat" = "$topic" || continue - sed -n -e '/^START-INFO-DIR-ENTRY/,/^END-INFO-DIR-ENTRY/ s/^\([^SE]\)/\1/p' \ + + sed -n -e 's/@value{emacsname}/Emacs/' \ + -e 's/@acronym{\([A-Z]*\)}/\1/' \ + -e '/^@direntry/,/^@end direntry/ s/^\([^@]\)/\1/p' \ $file >> $outfile done === modified file 'doc/lispintro/ChangeLog' --- doc/lispintro/ChangeLog 2013-12-12 03:37:38 +0000 +++ doc/lispintro/ChangeLog 2013-12-12 22:41:31 +0000 @@ -1,5 +1,7 @@ 2013-12-12 Glenn Morris + * emacs-lisp-intro.texi: Tweak dircategory. + * emacs-lisp-intro.texi: Sync direntry with info/dir version. 2013-12-02 Paul Eggert === modified file 'doc/lispintro/emacs-lisp-intro.texi' --- doc/lispintro/emacs-lisp-intro.texi 2013-12-12 03:37:38 +0000 +++ doc/lispintro/emacs-lisp-intro.texi 2013-12-12 22:41:31 +0000 @@ -96,7 +96,7 @@ @c ---------------------------------------------------- -@dircategory GNU Emacs Lisp +@dircategory Emacs lisp @direntry * Emacs Lisp Intro: (eintr). A simple introduction to Emacs Lisp programming. @end direntry === modified file 'doc/lispref/ChangeLog' --- doc/lispref/ChangeLog 2013-12-12 18:19:10 +0000 +++ doc/lispref/ChangeLog 2013-12-12 22:41:31 +0000 @@ -1,3 +1,7 @@ +2013-12-12 Glenn Morris + + * elisp.texi: Tweak dircategory. + 2013-12-12 Eli Zaretskii * nonascii.texi (Encoding and I/O): Document file-name encoding === modified file 'doc/lispref/elisp.texi' --- doc/lispref/elisp.texi 2013-12-12 03:37:38 +0000 +++ doc/lispref/elisp.texi 2013-12-12 22:41:31 +0000 @@ -117,7 +117,7 @@ @documentencoding UTF-8 -@dircategory GNU Emacs Lisp +@dircategory Emacs lisp @direntry * Elisp: (elisp). The Emacs Lisp Reference Manual. @end direntry ------------------------------------------------------------ revno: 115495 [merge] committer: David Engster branch nick: trunk timestamp: Thu 2013-12-12 23:09:15 +0100 message: Merge from CEDET upstream. diff: === modified file 'admin/ChangeLog' --- admin/ChangeLog 2013-12-12 08:54:21 +0000 +++ admin/ChangeLog 2013-12-12 22:09:15 +0000 @@ -1,3 +1,18 @@ +2013-12-12 David Engster + + * grammars/c.by (expr-binop): Add MOD. + (variablearg): Add 'opt-assign'. + (variablearg, varnamelist): Add default values so that it can be + later expanded into the tag. + (opt-stuff-after-symbol): Rename to 'brackets-after-symbol' and + remove empty match. + (multi-stage-dereference): Adapt to above rename. + (unaryexpression): Use 'symbol' instead of 'namespace-symbol', + since the latter also leads to an empty match at the end which + would make this too greedy. + (variablearg-opt-name): Support parsing of function pointers + inside an argument list. + 2013-12-12 Glenn Morris * update_autogen (info_dir): === modified file 'admin/grammars/c.by' --- admin/grammars/c.by 2013-11-30 02:06:34 +0000 +++ admin/grammars/c.by 2013-12-12 21:33:06 +0000 @@ -901,8 +901,8 @@ ;; I should store more in this def, but leave it simple for now. ;; Klaus Berndl: const and volatile can be written after the type! variablearg - : declmods typeformbase cv-declmods opt-ref variablearg-opt-name - ( VARIABLE-TAG (list $5) $2 nil + : declmods typeformbase cv-declmods opt-ref variablearg-opt-name opt-assign + ( VARIABLE-TAG (list (append $5 ,$6)) $2 nil :constant-flag (if (member "const" (append $1 $3)) t nil) :typemodifiers (delete "const" (append $1 $3)) :reference (car ,$4) @@ -912,6 +912,8 @@ variablearg-opt-name : varname ( ,$1 ) + | semantic-list arg-list + ( (car ( EXPAND $1 function-pointer )) $2) ;; Klaus Berndl: This allows variableargs without a arg-name being ;; parsed correct even if there several pointers (*) | opt-stars @@ -926,9 +928,9 @@ varnamelist : opt-ref varname varname-opt-initializer COMA varnamelist - ( ,(cons $2 $5) ) + ( ,(cons (append $2 $3) $5) ) | opt-ref varname varname-opt-initializer - ( $2 ) + ( (append $2 $3) ) ; ;; Klaus Berndl: Is necessary to parse stuff like @@ -1152,16 +1154,15 @@ : open-paren typeformbase close-paren ; -opt-stuff-after-symbol +brackets-after-symbol : PAREN_BLCK | BRACK_BLCK - | ;; EMPTY ; multi-stage-dereference - : namespace-symbol opt-stuff-after-symbol PERIOD multi-stage-dereference ;; method call - | namespace-symbol opt-stuff-after-symbol MINUS GREATER multi-stage-dereference ;;method call - | namespace-symbol opt-stuff-after-symbol + : namespace-symbol brackets-after-symbol PERIOD multi-stage-dereference ;; method call + | namespace-symbol brackets-after-symbol MINUS GREATER multi-stage-dereference ;;method call + | namespace-symbol brackets-after-symbol ; string-seq @@ -1187,6 +1188,7 @@ | AMPERSAND | OR OR | OR + | MOD ;; There are more. ; @@ -1204,8 +1206,7 @@ | multi-stage-dereference | NEW multi-stage-dereference | NEW builtintype-types semantic-list - ;; Klaus Berndl: symbol -> namespace-symbol! - | namespace-symbol + | symbol ;; Klaus Berndl: C/C++ allows sequences of strings which are ;; concatenated by the precompiler to one string | string-seq === modified file 'lisp/cedet/ChangeLog' --- lisp/cedet/ChangeLog 2013-11-30 02:06:34 +0000 +++ lisp/cedet/ChangeLog 2013-12-12 21:33:06 +0000 @@ -1,3 +1,128 @@ +2013-12-12 David Engster + + * semantic/analyze.el + (semantic-analyze-find-tag-sequence-default): Always add scope to + the local miniscope for each type. Otherwise, structure tags are + not analyzed correctly. Also, always search the extended + miniscope even when not dealing with types. + + * semantic/ctxt.el (semantic-get-local-variables-default): Also + try to parse local variables for buffers which are currently + marked as unparseable. Otherwise, it is often impossible to + complete local variables. + + * semantic/scope.el (semantic-analyze-scoped-types-default): If we + cannot find a type in the typecache, also look into the the types + we already found. This is necessary since in C++, a 'using + namespace' can be dependend on a previous one. + (semantic-completable-tags-from-type): When creating the list of + completable types, pull in types which are referenced through + 'using' statements, and also preserve their filenames. + + * semanitc/bovine/c.el (semantic/analyze/refs): Require. + (semantic-analyze-tag-references): New override. Mainly copied + from the default implementation, but if nothing could be found (or + just the tag itself), drop all namespaces from the scope and + search again. This is necessary for implementations which are + defined outside of the namespace and only pull those in through + 'using' statements. + (semantic-ctxt-scoped-types): Go through all tags around point and + search them for using statements. In the case for using + statements outside of function scope, append them in the correct + order instead of using 'cons'. This is important since using + statements may depend on previous ones. + (semantic-expand-c-tag-namelist): Do not try to parse struct + definitions as default values. The grammar parser seems to return + the point positions slightly differently (as a cons instead of a + list). Also, set parent for typedefs to 'nil'. It does not + really make sense to set a parent class for typedefs, and it can + also lead to endless loops when calculating scope. + (semantic-c-reconstitute-token): Change handling of function + pointers; instead of seeing them as variables, handle them as + functions with a 'function-pointer' attribute. Also, correctly + deal with function pointers as function arguments. + (semantic-c-reconstitute-function-arglist): New function to parse + function pointers inside an argument list. + (semantic-format-tag-name): Use 'function-pointer' attribute + instead of the old 'functionpointer-flag'. + (semantic-cpp-lexer): Use new `semantic-lex-spp-paren-or-list'. + + * semantic/bovine/gcc.el (semantic-gcc-setup): Add 'features.h' to + the list of files whose preprocessor symbols are included. This + pulls in things like __USE_POSIX and similar. + + * semantic/format.el (semantic-format-tag-prototype-default): + Display default values if available. + + * semantic/analyze/refs.el (semantic-analyze-refs-impl) + (semantic-analyze-refs-proto): Add 'default-value' as ignorable in + call to `semantic-tag-similar-p'. + + * semantic/db-mode.el (semanticdb-semantic-init-hook-fcn): Always + set buffer for `semanticdb-current-table'. + + * semantic/db.el (semanticdb-table::semanticdb-refresh-table): The + previous change turned up a bug in this method. Since the current + table now correctly has a buffer set, the first clause in the + `cond' would be taken, but there was a `save-excursion' missing. + + * semantic/lex-spp.el (semantic-c-end-of-macro): Declare. + (semantic-lex-spp-token-macro-to-macro-stream): Deal with macros + which open/close a scope. For this, leave an overlay if we + encounter a single open paren and return a semantic-list in the + lexer. When this list gets expanded, retrieve the old position + from the overlay. See the comments in the function for further + details. + (semantic-lex-spp-find-closing-macro): New function to find the + next macro which closes scope (i.e., has a closing paren). + (semantic-lex-spp-replace-or-symbol-or-keyword): Go to end of + closing macro if necessary. + (semantic-lex-spp-paren-or-list): New lexer to specially deal with + parens in macro definitions. + + * semantic/decorate/mode.el (semantic-decoration-mode): Do not + decorate available tags immediately but in an idle timer, since + EDE will usually not be activated yet, which will make it + impossible to find project includes. + + * semantic/decorate/include.el + (semantic-decoration-on-includes-highlight-default): Remove + 'unloaded' from throttle when decorating includes, otherwise all + would be loaded. Rename 'table' to 'currenttable' to make things + clearer. + + * ede/linux.el (cl): Require during compile. + +2013-12-12 Lluís Vilanova + + * ede/linux.el (project-linux-build-directory-default) + (project-linux-architecture-default): Add customizable variables. + (ede-linux-project): Add additional slots to track Linux-specific + information (out-of-tree build directory and selected + architecture). + (ede-linux--get-build-directory, ede-linux--get-archs) + (ede-linux--detect-architecture, ede-linux--get-architecture) + (ede-linux--include-path): Added function to detect Linux-specific + information. + (ede-linux-load): Set new Linux-specific information when creating + a project. + (ede-expand-filename-impl): Use new and more accurate include + information. + +2013-12-12 Eric Ludlam + + * semantic/scope.el (semantic-calculate-scope): Return a clone of + the scopecache, so that everyone is working with its own (shallow) + copy. Otherwise, if one caller is resetting the scope, it would + be reset for all others working with the scope cache as well. + +2013-12-12 Alex Ott + + * ede/generic.el (project-run-target): Remove incorrect require. + + * semantic/format.el (semantic-format-tag-prototype-default): Use + concat only for strings. + 2013-11-30 Glenn Morris Stop keeping (most) generated cedet grammar files in the repository. === modified file 'lisp/cedet/ede/generic.el' --- lisp/cedet/ede/generic.el 2013-01-01 09:11:05 +0000 +++ lisp/cedet/ede/generic.el 2013-12-12 21:33:06 +0000 @@ -360,7 +360,6 @@ (defmethod project-run-target ((target ede-generic-target)) "Run the current project derived from TARGET." - (require 'ede-shell) (let* ((proj (ede-target-parent target)) (config (ede-generic-get-configuration proj)) (run (concat "./" (oref config :run-command))) === modified file 'lisp/cedet/ede/linux.el' --- lisp/cedet/ede/linux.el 2013-01-01 09:11:05 +0000 +++ lisp/cedet/ede/linux.el 2013-12-12 21:33:06 +0000 @@ -32,6 +32,8 @@ ;; * Add texinfo lookup options. ;; * Add website +(eval-when-compile (require 'cl)) + (require 'ede) (require 'ede/make) @@ -46,6 +48,19 @@ :group 'ede :version "24.3") +(defcustom project-linux-build-directory-default 'ask + "Build directory." + :group 'project-linux + :type '(choice (const :tag "Same as source directory" 'same) + (const :tag "Ask the user" 'ask))) + +(defcustom project-linux-architecture-default 'ask + "Target architecture to assume when not auto-detected." + :group 'project-linux + :type '(choice (string :tag "Architecture name") + (const :tag "Ask the user" 'ask))) + + (defcustom project-linux-compile-target-command (concat ede-make-command " -k -C %s SUBDIRS=%s") "*Default command used to compile a target." :group 'project-linux @@ -109,10 +124,100 @@ (defclass ede-linux-project (ede-project eieio-instance-tracker) ((tracking-symbol :initform 'ede-linux-project-list) - ) + (build-directory :initarg :build-directory + :type string + :documentation "Build directory.") + (architecture :initarg :architecture + :type string + :documentation "Target architecture.") + (include-path :initarg :include-path + :type list + :documentation "Include directories. +Contains both common and target architecture-specific directories.")) "Project Type for the Linux source code." :method-invocation-order :depth-first) + +(defun ede-linux--get-build-directory (dir) + "Detect build directory for sources in DIR. +If DIR has not been used as a build directory, fall back to +`project-linux-build-directory-default'." + (or + ;; detected build on source directory + (and (file-exists-p (expand-file-name ".config" dir)) dir) + ;; use configuration + (case project-linux-build-directory-default + (same dir) + (ask (read-directory-name "Select Linux' build directory: " dir))))) + + +(defun ede-linux--get-archs (dir) + "Returns a list of architecture names found in DIR." + (let ((archs-dir (expand-file-name "arch" dir)) + archs) + (when (file-directory-p archs-dir) + (mapc (lambda (elem) + (when (and + (not (string= elem ".")) + (not (string= elem "..")) + (not (string= elem "x86_64")) ; has no separate sources + (file-directory-p + (expand-file-name elem archs-dir))) + (add-to-list 'archs elem t))) + (directory-files archs-dir))) + archs)) + + +(defun ede-linux--detect-architecture (dir) + "Try to auto-detect the architecture as configured in DIR. +DIR is Linux' build directory. If it cannot be auto-detected, +returns `project-linux-architecture-default'." + (let ((archs-dir (expand-file-name "arch" dir)) + (archs (ede-linux--get-archs dir)) + arch found) + (or (and + archs + ;; Look for /arch//include/generated + (progn + (while (and archs (not found)) + (setq arch (car archs)) + (when (file-directory-p + (expand-file-name (concat arch "/include/generated") + archs-dir)) + (setq found arch)) + (setq archs (cdr archs))) + found)) + project-linux-architecture-default))) + +(defun ede-linux--get-architecture (dir bdir) + "Try to auto-detect the architecture as configured in BDIR. +Uses `ede-linux--detect-architecture' for the auto-detection. If +the result is `ask', let the user choose from architectures found +in DIR." + (let ((arch (ede-linux--detect-architecture bdir))) + (case arch + (ask + (completing-read "Select target architecture: " + (ede-linux--get-archs dir))) + (t arch)))) + + +(defun ede-linux--include-path (dir bdir arch) + "Returns a list with include directories. +Returned directories might not exist, since they are not created +until Linux is built for the first time." + (map 'list + (lambda (elem) (format (concat (car elem) "/" (cdr elem)) arch)) + ;; XXX: taken from the output of "make V=1" + (list (cons dir "arch/%s/include") + (cons bdir "arch/%s/include/generated") + (cons dir "include") + (cons bdir "include") + (cons dir "arch/%s/include/uapi") + (cons bdir "arch/%s/include/generated/uapi") + (cons dir "include/uapi") + (cons bdir "include/generated/uapi")))) + ;;;###autoload (defun ede-linux-load (dir &optional rootproj) "Return an Linux Project object if there is a match. @@ -121,15 +226,20 @@ ROOTPROJ is nil, since there is only one project." (or (ede-linux-file-existing dir) ;; Doesn't already exist, so let's make one. - (let ((proj (ede-linux-project - "Linux" - :name "Linux" - :version (ede-linux-version dir) - :directory (file-name-as-directory dir) - :file (expand-file-name "scripts/ver_linux" - dir)))) - (ede-add-project-to-global-list proj)) - )) + (let* ((bdir (ede-linux--get-build-directory dir)) + (arch (ede-linux--get-architecture dir bdir)) + (include-path (ede-linux--include-path dir bdir arch)) + (proj (ede-linux-project + "Linux" + :name "Linux" + :version (ede-linux-version dir) + :directory (file-name-as-directory dir) + :file (expand-file-name "scripts/ver_linux" + dir) + :build-directory bdir + :architecture arch + :include-path include-path))) + (ede-add-project-to-global-list proj)))) ;;;###autoload (ede-add-project-autoload @@ -245,18 +355,23 @@ "Within this project PROJ, find the file NAME. Knows about how the Linux source tree is organized." (let* ((ext (file-name-extension name)) - (root (ede-project-root proj)) - (dir (ede-project-root-directory root)) - (F (cond - ((not ext) nil) - ((string-match "h" ext) - (or (ede-linux-file-exists-name name dir "") - (ede-linux-file-exists-name name dir "include")) - ) - ((string-match "txt" ext) - (ede-linux-file-exists-name name dir "Documentation")) - (t nil))) - ) + (root (ede-project-root proj)) + (dir (ede-project-root-directory root)) + (bdir (oref proj build-directory)) + (F (cond + ((not ext) nil) + ((string-match "h" ext) + (let ((dirs (oref proj include-path)) + found) + (while (and dirs (not found)) + (setq found + (or (ede-linux-file-exists-name name bdir (car dirs)) + (ede-linux-file-exists-name name dir (car dirs)))) + (setq dirs (cdr dirs))) + found)) + ((string-match "txt" ext) + (ede-linux-file-exists-name name dir "Documentation")) + (t nil)))) (or F (call-next-method)))) (defmethod project-compile-project ((proj ede-linux-project) === modified file 'lisp/cedet/semantic/analyze.el' --- lisp/cedet/semantic/analyze.el 2013-03-21 22:11:03 +0000 +++ lisp/cedet/semantic/analyze.el 2013-12-12 21:33:06 +0000 @@ -295,18 +295,10 @@ ;; In some cases the found TMP is a type, ;; and we can use it directly. (cond ((semantic-tag-of-class-p tmp 'type) - ;; update the miniscope when we need to analyze types directly. - (when miniscope - (let ((rawscope - (apply 'append - (mapcar 'semantic-tag-type-members - tagtype)))) - (oset miniscope fullscope rawscope))) - ;; Now analyze the type to remove metatypes. (or (semantic-analyze-type tmp miniscope) tmp)) (t - (semantic-analyze-tag-type tmp scope)))) + (semantic-analyze-tag-type tmp miniscope)))) (typefile (when tmptype (semantic-tag-file-name tmptype))) @@ -336,6 +328,11 @@ (semantic--tag-put-property tmp :filename fname)) (setq tag (cons tmp tag)) (setq tagtype (cons tmptype tagtype)) + (when miniscope + (let ((rawscope + (apply 'append + (mapcar 'semantic-tag-type-members tagtype)))) + (oset miniscope fullscope rawscope))) ) (setq s (cdr s))) === modified file 'lisp/cedet/semantic/analyze/refs.el' --- lisp/cedet/semantic/analyze/refs.el 2013-01-01 09:11:05 +0000 +++ lisp/cedet/semantic/analyze/refs.el 2013-12-12 21:33:06 +0000 @@ -118,7 +118,8 @@ (semantic-tag-similar-p tag aT :prototype-flag :parent - :typemodifiers)) + :typemodifiers + :default-value)) (when in-buffer (save-excursion (semantic-go-to-tag aT aDB))) (push aT impl)))) allhits) @@ -141,7 +142,8 @@ (semantic-tag-similar-p tag aT :prototype-flag :parent - :typemodifiers)) + :typemodifiers + :default-value)) (when in-buffer (save-excursion (semantic-go-to-tag aT aDB))) (push aT proto)))) allhits) === modified file 'lisp/cedet/semantic/bovine/c.el' --- lisp/cedet/semantic/bovine/c.el 2013-06-02 13:33:09 +0000 +++ lisp/cedet/semantic/bovine/c.el 2013-12-12 21:33:06 +0000 @@ -27,6 +27,7 @@ (require 'semantic) (require 'semantic/analyze) +(require 'semantic/analyze/refs) (require 'semantic/bovine) (require 'semantic/bovine/gcc) (require 'semantic/idle) @@ -812,7 +813,7 @@ ;; semantic-lex-spp-replace-or-symbol-or-keyword semantic-lex-symbol-or-keyword semantic-lex-charquote - semantic-lex-paren-or-list + semantic-lex-spp-paren-or-list semantic-lex-close-paren semantic-lex-ignore-comments semantic-lex-punctuation @@ -1118,7 +1119,8 @@ (semantic-tag-new-variable (car cur) ;name ty ;type - (if default + (if (and default + (listp (cdr default))) (buffer-substring-no-properties (car default) (car (cdr default)))) :constant-flag (semantic-tag-variable-constant-p tag) @@ -1173,11 +1175,7 @@ (nth 1 (car names)) ; name "typedef" (semantic-tag-type-members tag) - ;; parent is just the name of what - ;; is passed down as a tag. - (list - (semantic-tag-name - (semantic-tag-type-superclasses tag))) + nil :pointer (let ((stars (car (car (car names))))) (if (= stars 0) nil stars)) @@ -1227,6 +1225,45 @@ name (delete "" ans)))) +(define-mode-local-override semantic-analyze-tag-references c-mode (tag &optional db) + "Analyze the references for TAG. +Returns a class with information about TAG. + +Optional argument DB is a database. It will be used to help +locate TAG. + +Use `semantic-analyze-current-tag' to debug this fcn." + (when (not (semantic-tag-p tag)) (signal 'wrong-type-argument (list 'semantic-tag-p tag))) + (let ((allhits nil) + (scope nil) + (refs nil)) + (save-excursion + (semantic-go-to-tag tag db) + (setq scope (semantic-calculate-scope)) + + (setq allhits (semantic--analyze-refs-full-lookup tag scope t)) + + (when (or (zerop (semanticdb-find-result-length allhits)) + (and (= (semanticdb-find-result-length allhits) 1) + (eq (car (semanticdb-find-result-nth allhits 0)) tag))) + ;; It found nothing or only itself - not good enough. As a + ;; last resort, let's remove all namespaces from the scope and + ;; search again. + (oset scope parents + (let ((parents (oref scope parents)) + newparents) + (dolist (cur parents) + (unless (string= (semantic-tag-type cur) "namespace") + (push cur newparents))) + (reverse newparents))) + (setq allhits (semantic--analyze-refs-full-lookup tag scope t))) + + (setq refs (semantic-analyze-references (semantic-tag-name tag) + :tag tag + :tagdb db + :scope scope + :rawsearchdata allhits))))) + (defun semantic-c-reconstitute-token (tokenpart declmods typedecl) "Reconstitute a token TOKENPART with DECLMODS and TYPEDECL. This is so we don't have to match the same starting text several times. @@ -1258,7 +1295,8 @@ (nth 10 tokenpart) ; initializers ) (not (car (nth 3 tokenpart))))) - (fcnpointer (string-match "^\\*" (car tokenpart))) + (fcnpointer (and (> (length (car tokenpart)) 0) + (= (aref (car tokenpart) 0) ?*))) (fnname (if fcnpointer (substring (car tokenpart) 1) (car tokenpart))) @@ -1266,70 +1304,80 @@ nil t)) ) - (if fcnpointer - ;; Function pointers are really variables. - (semantic-tag-new-variable - fnname - typedecl - nil - ;; It is a function pointer - :functionpointer-flag t - ) - ;; The function - (semantic-tag-new-function - fnname - (or typedecl ;type - (cond ((car (nth 3 tokenpart) ) - "void") ; Destructors have no return? - (constructor - ;; Constructors return an object. - (semantic-tag-new-type - ;; name - (or (car semantic-c-classname) - (let ((split (semantic-analyze-split-name-c-mode - (car (nth 2 tokenpart))))) - (if (stringp split) split - (car (last split))))) - ;; type - (or (cdr semantic-c-classname) - "class") - ;; members - nil - ;; parents - nil - )) - (t "int"))) - (nth 4 tokenpart) ;arglist - :constant-flag (if (member "const" declmods) t nil) - :typemodifiers (delete "const" declmods) - :parent (car (nth 2 tokenpart)) - :destructor-flag (if (car (nth 3 tokenpart) ) t) - :constructor-flag (if constructor t) - :pointer (nth 7 tokenpart) - :operator-flag operator - ;; Even though it is "throw" in C++, we use - ;; `throws' as a common name for things that toss - ;; exceptions about. - :throws (nth 5 tokenpart) - ;; Reentrant is a C++ thingy. Add it here - :reentrant-flag (if (member "reentrant" (nth 6 tokenpart)) t) - ;; A function post-const is funky. Try stuff - :methodconst-flag (if (member "const" (nth 6 tokenpart)) t) - ;; prototypes are functions w/ no body - :prototype-flag (if (nth 8 tokenpart) t) - ;; Pure virtual - :pure-virtual-flag (if (eq (nth 8 tokenpart) :pure-virtual-flag) t) - ;; Template specifier. - :template-specifier (nth 9 tokenpart) - ))) - ) - )) + ;; The function + (semantic-tag-new-function + fnname + (or typedecl ;type + (cond ((car (nth 3 tokenpart) ) + "void") ; Destructors have no return? + (constructor + ;; Constructors return an object. + (semantic-tag-new-type + ;; name + (or (car semantic-c-classname) + (let ((split (semantic-analyze-split-name-c-mode + (car (nth 2 tokenpart))))) + (if (stringp split) split + (car (last split))))) + ;; type + (or (cdr semantic-c-classname) + "class") + ;; members + nil + ;; parents + nil + )) + (t "int"))) + ;; Argument list can contain things like function pointers + (semantic-c-reconstitute-function-arglist (nth 4 tokenpart)) + :constant-flag (if (member "const" declmods) t nil) + :typemodifiers (delete "const" declmods) + :parent (car (nth 2 tokenpart)) + :destructor-flag (if (car (nth 3 tokenpart) ) t) + :constructor-flag (if constructor t) + :function-pointer fcnpointer + :pointer (nth 7 tokenpart) + :operator-flag operator + ;; Even though it is "throw" in C++, we use + ;; `throws' as a common name for things that toss + ;; exceptions about. + :throws (nth 5 tokenpart) + ;; Reentrant is a C++ thingy. Add it here + :reentrant-flag (if (member "reentrant" (nth 6 tokenpart)) t) + ;; A function post-const is funky. Try stuff + :methodconst-flag (if (member "const" (nth 6 tokenpart)) t) + ;; prototypes are functions w/ no body + :prototype-flag (if (nth 8 tokenpart) t) + ;; Pure virtual + :pure-virtual-flag (if (eq (nth 8 tokenpart) :pure-virtual-flag) t) + ;; Template specifier. + :template-specifier (nth 9 tokenpart)))))) (defun semantic-c-reconstitute-template (tag specifier) "Reconstitute the token TAG with the template SPECIFIER." (semantic-tag-put-attribute tag :template (or specifier "")) tag) +(defun semantic-c-reconstitute-function-arglist (arglist) + "Reconstitute the argument list of a function. +This currently only checks if the function expects a function +pointer as argument." + (let (result) + (dolist (arg arglist) + ;; Names starting with a '*' denote a function pointer + (if (and (> (length (semantic-tag-name arg)) 0) + (= (aref (semantic-tag-name arg) 0) ?*)) + (setq result + (append result + (list + (semantic-tag-new-function + (substring (semantic-tag-name arg) 1) + (semantic-tag-type arg) + (cadr (semantic-tag-attributes arg)) + :function-pointer t)))) + (setq result (append result (list arg))))) + result)) + ;;; Override methods & Variables ;; @@ -1338,7 +1386,7 @@ "Convert TAG to a string that is the print name for TAG. Optional PARENT and COLOR are ignored." (let ((name (semantic-format-tag-name-default tag parent color)) - (fnptr (semantic-tag-get-attribute tag :functionpointer-flag)) + (fnptr (semantic-tag-get-attribute tag :function-pointer)) ) (if (not fnptr) name @@ -1823,31 +1871,31 @@ (let ((idx 0) (len (semanticdb-find-result-length tmp))) (while (< idx len) - (setq tagreturn (cons (semantic-tag-type (car (semanticdb-find-result-nth tmp idx))) tagreturn)) - (setq idx (1+ idx))) - ) - ;; Use the encompassed types around point to also look for using statements. - ;;(setq tagreturn (cons "bread_name" tagreturn)) - (while (cdr tagsaroundpoint) ; don't search the last one - (setq tmp (semantic-find-tags-by-class 'using (semantic-tag-components (car tagsaroundpoint)))) - (dolist (T tmp) - (setq tagreturn (cons (semantic-tag-type T) tagreturn)) - ) - (setq tagsaroundpoint (cdr tagsaroundpoint)) - ) - ;; If in a function... - (when (and (semantic-tag-of-class-p (car tagsaroundpoint) 'function) - ;; ...search for using statements in the local scope... - (setq tmp (semantic-find-tags-by-class - 'using - (semantic-get-local-variables)))) - ;; ... and add them. - (setq tagreturn - (append tagreturn - (mapcar 'semantic-tag-type tmp)))) + (setq tagreturn + (append tagreturn (list (semantic-tag-type + (car (semanticdb-find-result-nth tmp idx)))))) + (setq idx (1+ idx)))) + ;; Use the encompassed types around point to also look for using + ;; statements. If we deal with types, search inside members; for + ;; functions, we have to call `semantic-get-local-variables' to + ;; parse inside the function's body. + (dolist (cur tagsaroundpoint) + (cond + ((and (eq (semantic-tag-class cur) 'type) + (setq tmp (semantic-find-tags-by-class + 'using + (semantic-tag-components (car tagsaroundpoint))))) + (dolist (T tmp) + (setq tagreturn (cons (semantic-tag-type T) tagreturn)))) + ((and (semantic-tag-of-class-p (car (last tagsaroundpoint)) 'function) + (setq tmp (semantic-find-tags-by-class + 'using + (semantic-get-local-variables)))) + (setq tagreturn + (append tagreturn + (mapcar 'semantic-tag-type tmp)))))) ;; Return the stuff - tagreturn - )) + tagreturn)) (define-mode-local-override semantic-ctxt-imported-packages c++-mode (&optional point) "Return the list of using tag types in scope of POINT." === modified file 'lisp/cedet/semantic/bovine/gcc.el' --- lisp/cedet/semantic/bovine/gcc.el 2013-03-21 22:11:03 +0000 +++ lisp/cedet/semantic/bovine/gcc.el 2013-12-12 21:33:06 +0000 @@ -210,7 +210,8 @@ (semantic-add-system-include D 'c-mode)) (dolist (D (semantic-gcc-get-include-paths "c++")) (semantic-add-system-include D 'c++-mode) - (let ((cppconfig (list (concat D "/bits/c++config.h") (concat D "/sys/cdefs.h")))) + (let ((cppconfig (list (concat D "/bits/c++config.h") (concat D "/sys/cdefs.h") + (concat D "/features.h")))) (dolist (cur cppconfig) ;; Presumably there will be only one of these files in the try-paths list... (when (file-readable-p cur) === modified file 'lisp/cedet/semantic/ctxt.el' --- lisp/cedet/semantic/ctxt.el 2013-06-18 02:06:33 +0000 +++ lisp/cedet/semantic/ctxt.el 2013-12-12 21:33:06 +0000 @@ -168,8 +168,7 @@ to collect tags, such as local variables or prototypes." ;; This assumes a bovine parser. Make sure we don't do ;; anything in that case. - (when (and semantic--parse-table (not (eq semantic--parse-table t)) - (not (semantic-parse-tree-unparseable-p))) + (when (and semantic--parse-table (not (eq semantic--parse-table t))) (let ((vars (semantic-get-cache-data 'get-local-variables))) (if vars (progn === modified file 'lisp/cedet/semantic/db-mode.el' --- lisp/cedet/semantic/db-mode.el 2013-10-20 09:51:21 +0000 +++ lisp/cedet/semantic/db-mode.el 2013-12-12 21:33:06 +0000 @@ -105,7 +105,8 @@ (oset ctbl major-mode major-mode) ;; Local state (setq semanticdb-current-table ctbl) - ;; Try to swap in saved tags + (oset ctbl buffer (current-buffer)) + ;; Try to swap in saved tags (if (or (not (slot-boundp ctbl 'tags)) (not (oref ctbl tags)) (/= (or (oref ctbl pointmax) 0) (point-max)) ) @@ -133,7 +134,6 @@ (semantic--set-buffer-cache (oref ctbl tags)) ;; Don't need it to be dirty. Set dirty due to hooks from above. (oset ctbl dirty nil) ;; Special case here. - (oset ctbl buffer (current-buffer)) ;; Bind into the buffer. (semantic--tag-link-cache-to-buffer) ) === modified file 'lisp/cedet/semantic/db.el' --- lisp/cedet/semantic/db.el 2013-11-30 02:06:34 +0000 +++ lisp/cedet/semantic/db.el 2013-12-12 21:33:06 +0000 @@ -560,8 +560,9 @@ ;; ;; Already in a buffer, just do it. ((semanticdb-in-buffer-p obj) - (semanticdb-set-buffer obj) - (semantic-fetch-tags)) + (save-excursion + (semanticdb-set-buffer obj) + (semantic-fetch-tags))) ;; ;; Not in a buffer. Forcing a load. (force === modified file 'lisp/cedet/semantic/decorate/include.el' --- lisp/cedet/semantic/decorate/include.el 2013-03-21 22:11:03 +0000 +++ lisp/cedet/semantic/decorate/include.el 2013-12-12 21:33:06 +0000 @@ -335,6 +335,9 @@ (defun semantic-decoration-on-includes-highlight-default (tag) "Highlight the include TAG to show that semantic can't find it." (let* ((file (semantic-dependency-tag-file tag)) + ;; Don't actually load includes + (semanticdb-find-default-throttle + (remq 'unloaded semanticdb-find-default-throttle)) (table (semanticdb-find-table-for-include tag (current-buffer))) (face nil) (map nil) @@ -365,8 +368,8 @@ (semanticdb-cache-get table 'semantic-decoration-unparsed-include-cache) ;; Add a dependency. - (let ((table semanticdb-current-table)) - (semanticdb-add-reference table tag)) + (let ((currenttable semanticdb-current-table)) + (semanticdb-add-reference currenttable tag)) ) )) === modified file 'lisp/cedet/semantic/decorate/mode.el' --- lisp/cedet/semantic/decorate/mode.el 2013-07-29 20:26:19 +0000 +++ lisp/cedet/semantic/decorate/mode.el 2013-12-12 21:33:06 +0000 @@ -275,7 +275,13 @@ 'semantic-decorate-tags-after-full-reparse nil t) ;; Add decorations to available tags. The above hooks ensure ;; that new tags will be decorated when they become available. - (semantic-decorate-add-decorations (semantic-fetch-available-tags))) + ;; However, don't do this immediately, because EDE will be + ;; activated later by find-file-hook, and includes might not + ;; be found yet. + (run-with-idle-timer + 0.1 nil + (lambda () + (semantic-decorate-add-decorations (semantic-fetch-available-tags))))) ;; Remove decorations from available tags. (semantic-decorate-clear-decorations (semantic-fetch-available-tags)) ;; Cleanup any leftover crap too. === modified file 'lisp/cedet/semantic/format.el' --- lisp/cedet/semantic/format.el 2013-01-01 09:11:05 +0000 +++ lisp/cedet/semantic/format.el 2013-12-12 21:33:06 +0000 @@ -499,7 +499,12 @@ (setq r (concat r "[]") deref (1- deref))) r))) - ) + (default (when (eq class 'variable) + (let ((defval + (semantic-tag-get-attribute tag :default-value))) + (when (and defval (stringp defval)) + (concat "[=" defval "]"))))) + ) (if args (setq args (concat " " @@ -512,7 +517,8 @@ (if type (concat type " ")) name (or args "") - (or array "")))) + (or array "") + (or default "")))) ;;;###autoload (define-overloadable-function semantic-format-tag-concise-prototype (tag &optional parent color) === modified file 'lisp/cedet/semantic/lex-spp.el' --- lisp/cedet/semantic/lex-spp.el 2013-01-01 09:11:05 +0000 +++ lisp/cedet/semantic/lex-spp.el 2013-12-12 21:33:06 +0000 @@ -70,6 +70,8 @@ (require 'semantic) (require 'semantic/lex) +(declare-function semantic-c-end-of-macro "semantic/bovine/c") + ;;; Code: (defvar semantic-lex-spp-macro-symbol-obarray nil "Table of macro keywords used by the Semantic Preprocessor. @@ -527,16 +529,54 @@ ;; ;; Nested token FOO shows up in the table of macros, and gets replace ;; inline. This is the same as case 2. - - (let ((arglist (semantic-lex-spp-macro-with-args val)) - (argalist nil) - (val-tmp nil) - (v nil) - ) + ;; + ;; CASE 5: Macros which open a scope without closing it + ;; + ;; #define __NAMESPACE_STD namespace std { + ;; #define __NAMESPACE_END } + ;; ==> + ;; ((NAMESPACE "namespace" 140 . 149) + ;; (symbol "std" 150 . 153) + ;; (open-paren "{" 154 . 155)) + ;; + ;; Note that we get a single 'open-paren' instead of a + ;; 'semantic-list', which is because we use + ;; 'semantic-lex-spp-paren-or-list' instead of + ;; 'semantic-lex-paren-or-list' in our spp-lexer. To keep things + ;; reasonably simple, we assume that such an open scope will always + ;; be closed by another macro (see + ;; `semantic-lex-spp-find-closing-macro'). We generate a + ;; 'semantic-list' to this closing macro, and we leave an overlay + ;; which contains information how far we got into the macro's + ;; stream (since it might open several scopes). + + (let* ((arglist (semantic-lex-spp-macro-with-args val)) + (argalist nil) + (val-tmp nil) + (v nil) + (sppov (semantic-lex-spp-get-overlay beg)) + (sppinfo (when sppov (overlay-get sppov 'semantic-spp)))) + + ;; First, check if we were already here and left information + (when sppinfo + ;; Advance in the tokens as far as we got last time + (when (numberp (car sppinfo)) + (while (and val + (>= (car sppinfo) (car (last (car val))))) + (setq val (cdr val)))) + ;; And push an open paren + (semantic-lex-push-token + (semantic-lex-token 'open-paren beg (1+ beg) "{")) + (setq semantic-lex-current-depth (1+ semantic-lex-current-depth)) + (unless val + ;; We reached the end of this macro, so delete overlay + (delete-overlay sppov))) + ;; CASE 2: Dealing with the arg list. - (when arglist + (when (and val arglist) ;; Skip the arg list. - (setq val (cdr val)) + (when (eq (caar val) 'spp-arg-list) + (setq val (cdr val))) ;; Push args into the replacement list. (let ((AV argvalues)) @@ -616,7 +656,32 @@ (semantic-lex-push-token (semantic-lex-token (semantic-lex-token-class v) beg end txt)) ) - + ;; CASE 5: Macro which opens a scope + ((eq (semantic-lex-token-class v) 'open-paren) + ;; We assume that the scope will be closed by another macro. + ;; (Everything else would be a terrible idea anyway.) + (let* ((endpoint (semantic-lex-spp-find-closing-macro)) + (ov (when endpoint + (or sppov + (make-overlay beg end))))) + (when ov + ;; Generate a semantic-list which spans to the end of + ;; the closing macro + (semantic-lex-push-token + (semantic-lex-token 'semantic-list beg endpoint)) + ;; The rest of the current macro's stream will be parsed + ;; next time. + (setq val-tmp nil) + ;; Store our current state were we are in the macro and + ;; the endpoint. + (overlay-put ov 'semantic-spp + (cons (car (last v)) endpoint))))) + ((eq (semantic-lex-token-class v) 'close-paren) + ;; Macro which closes a scope + ;; Just push the close paren, but also decrease depth + (semantic-lex-push-token + (semantic-lex-token 'close-paren beg end txt)) + (setq semantic-lex-current-depth (1- semantic-lex-current-depth))) ;; CASE 1: Just another token in the stream. (t ;; Nothing new. @@ -652,6 +717,37 @@ txt "")) +(defun semantic-lex-spp-find-closing-macro () + "Find next macro which closes a scope through a close-paren. +Returns position with the end of that macro." + (let ((macros (semantic-lex-spp-macros)) + (cmacro-regexp "\\(") + (case-fold-search nil)) + ;; Build a regexp which search for all macros with a closing + ;; paren, and search for it. + (dolist (cur macros) + (let ((stream (symbol-value cur))) + (when (and (listp stream) (listp (car stream))) + (while stream + (if (and (eq (caar stream) 'close-paren) + (string= (nth 1 (car stream)) "}")) + (setq cmacro-regexp (concat cmacro-regexp (symbol-name cur) "\\|") + stream nil) + (setq stream (cdr-safe stream))))))) + (when cmacro-regexp + (save-excursion + (when (re-search-forward + (concat (substring cmacro-regexp 0 -2) "\\)[^0-9a-zA-Z_]") nil t) + (point)))))) + +(defun semantic-lex-spp-get-overlay (&optional point) + "Return first overlay which has a 'semantic-spp property." + (let ((overlays (overlays-at (or point (point))))) + (while (and overlays + (null (overlay-get (car overlays) 'semantic-spp))) + (setq overlays (cdr overlays))) + (car-safe overlays))) + ;;; Macro Merging ;; ;; Used when token streams from different macros include each other. @@ -824,8 +920,46 @@ "\\(\\sw\\|\\s_\\)+" (let ((str (match-string 0)) (beg (match-beginning 0)) - (end (match-end 0))) - (semantic-lex-spp-analyzer-push-tokens-for-symbol str beg end))) + (end (match-end 0)) + sppov) + (semantic-lex-spp-analyzer-push-tokens-for-symbol str beg end) + (when (setq sppov (semantic-lex-spp-get-overlay beg)) + (setq semantic-lex-end-point (cdr (overlay-get sppov 'semantic-spp)))))) + +(define-lex-regex-analyzer semantic-lex-spp-paren-or-list + "Detect open parenthesis. +Contrary to `semantic-lex-paren-or-list', this will push a single +open-paren onto the stream if no closing paren can be found. +This is important for macros which open a scope which is closed +by another macro." + "\\s(" + (if (or (not semantic-lex-maximum-depth) + (< semantic-lex-current-depth semantic-lex-maximum-depth)) + (progn + (setq semantic-lex-current-depth (1+ semantic-lex-current-depth)) + (semantic-lex-push-token + (semantic-lex-token + 'open-paren (match-beginning 0) (match-end 0)))) + (save-excursion + (let ((start (match-beginning 0)) + (end (match-end 0)) + (peom (save-excursion (semantic-c-end-of-macro) (point)))) + (condition-case nil + (progn + ;; This will throw an error if no closing paren can be found. + (forward-list 1) + (when (> (point) peom) + ;; If we have left the macro, this is the wrong closing + ;; paren, so error out as well. + (error "")) + (semantic-lex-push-token + (semantic-lex-token + 'semantic-list start (point)))) + (error + ;; Only push a single open-paren. + (semantic-lex-push-token + (semantic-lex-token + 'open-paren start end)))))))) ;;; ANALYZERS FOR NEW MACROS ;; === modified file 'lisp/cedet/semantic/scope.el' --- lisp/cedet/semantic/scope.el 2013-01-01 09:11:05 +0000 +++ lisp/cedet/semantic/scope.el 2013-12-12 21:33:06 +0000 @@ -195,12 +195,18 @@ ;; Get this thing as a tag (let ((tmp (cond ((stringp (car sp)) - (semanticdb-typecache-find (car sp))) - ;(semantic-analyze-find-tag (car sp) 'type)) + (or (semanticdb-typecache-find (car sp)) + ;; If we did not find it in the typecache, + ;; look in the tags we found so far + (car (semantic-deep-find-tags-by-name + (car sp) + code-scoped-types)))) ((semantic-tag-p (car sp)) (if (semantic-tag-prototype-p (car sp)) - (semanticdb-typecache-find (semantic-tag-name (car sp))) - ;;(semantic-analyze-find-tag (semantic-tag-name (car sp)) 'type) + (or (semanticdb-typecache-find (semantic-tag-name (car sp))) + (car (semantic-deep-find-tags-by-name + (semantic-tag-name (car sp)) + code-scoped-types))) (car sp))) (t nil)))) (when tmp @@ -506,10 +512,33 @@ (leftover nil) ) (dolist (S allslots) - (when (or (not (semantic-tag-of-class-p S 'function)) - (not (semantic-tag-function-parent S))) - (setq leftover (cons S leftover))) - ) + ;; We have to specially deal with 'using' tags here, since those + ;; pull in namespaces or classes into the current scope. + ;; (Should this go into c.el? If so, into which override?) + (if (semantic-tag-of-class-p S 'using) + (let* ((fullname (semantic-analyze-unsplit-name + (list (semantic-tag-name type) + (semantic-tag-name S)))) + ;; Search the typecache, first for the unqualified name + (usingtype (or + (semanticdb-typecache-find (semantic-tag-name S)) + ;; If that didn't return anything, use + ;; fully qualified name + (semanticdb-typecache-find fullname))) + (filename (when usingtype (semantic-tag-file-name usingtype)))) + (when usingtype + ;; Use recursion to examine that namespace or class + (let ((tags (semantic-completable-tags-from-type usingtype))) + (if filename + ;; If we have a filename, copy the tags with it + (dolist (cur tags) + (setq leftover (cons (semantic-tag-copy cur nil filename) + leftover))) + ;; Otherwise just run with it + (setq leftover (append tags leftover)))))) + (when (or (not (semantic-tag-of-class-p S 'function)) + (not (semantic-tag-function-parent S))) + (setq leftover (cons S leftover))))) (nreverse leftover))) (defun semantic-analyze-scoped-type-parts (type &optional scope noinherit protection) @@ -734,8 +763,9 @@ (when (called-interactively-p 'any) (require 'eieio-datadebug) (data-debug-show scopecache)) - ;; Return ourselves - scopecache)))) + ;; Return ourselves, but make a clone first so that the caller + ;; can reset the scope cache without affecting others. + (clone scopecache))))) (defun semantic-scope-find (name &optional class scope-in) "Find the tag with NAME, and optional CLASS in the current SCOPE-IN. ------------------------------------------------------------ revno: 115494 committer: Eli Zaretskii branch nick: trunk timestamp: Thu 2013-12-12 22:52:38 +0200 message: Remove stale comment from fileio.c. diff: === modified file 'src/fileio.c' --- src/fileio.c 2013-12-12 18:19:10 +0000 +++ src/fileio.c 2013-12-12 20:52:38 +0000 @@ -461,7 +461,6 @@ beg = res; p = beg + strlen (beg); dostounix_filename (beg); - /* FIXME: Figure out the multibyte vs unibyte stuff here. */ tem_fn = make_specified_string (beg, -1, p - beg, STRING_MULTIBYTE (filename)); } ------------------------------------------------------------ revno: 115493 committer: Paul Eggert branch nick: trunk timestamp: Thu 2013-12-12 12:07:40 -0800 message: Spelling fixes. diff: === modified file 'src/w32.c' --- src/w32.c 2013-12-09 20:21:58 +0000 +++ src/w32.c 2013-12-12 20:07:40 +0000 @@ -1344,7 +1344,7 @@ 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 + explicitly *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 @@ -1371,7 +1371,7 @@ . 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 + even in a directory whose name cannot be encoded in the current 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 @@ -4887,7 +4887,7 @@ && !(is_a_symlink && follow_symlinks) /* 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 file names include non-ASCII characters 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, @@ -4921,7 +4921,7 @@ /* 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, + to let FindFirstFileA interpret those as wildcards, and "succeed", returning us data from some random file in the same directory. */ if (_mbspbrk (name_a, "?")) === modified file 'test/ChangeLog' --- test/ChangeLog 2013-12-12 05:37:09 +0000 +++ test/ChangeLog 2013-12-12 20:07:40 +0000 @@ -621,7 +621,7 @@ * automated/add-log-tests.el, automated/advice-tests.el: * automated/imenu-test.el, automated/package-x-test.el: * automated/python-tests.el, automated/ruby-mode-tests.el: - * automated/xml-parse-tests.el: Explictly require ert. + * automated/xml-parse-tests.el: Explicitly require ert. 2013-07-08 Kenichi Handa ------------------------------------------------------------ revno: 115492 committer: Stefan Monnier branch nick: trunk timestamp: Thu 2013-12-12 14:47:11 -0500 message: * lisp/emacs-lisp/nadvice.el: Add `depth' property to manage ordering. (advice--make): Pay attention to `depth'. (advice--make-1): Don't autoload commands eagerly. * lisp/emacs-lisp/elp.el (elp-instrument-function): * lisp/emacs-lisp/trace.el (trace-function-internal): * lisp/emacs-lisp/debug.el (debug-on-entry): Keep them "first". * lisp/iswitchb.el (iswitchb-mode): Don't belittle ido. diff: === modified file 'lisp/ChangeLog' --- lisp/ChangeLog 2013-12-12 18:19:10 +0000 +++ lisp/ChangeLog 2013-12-12 19:47:11 +0000 @@ -1,3 +1,14 @@ +2013-12-12 Stefan Monnier + + * emacs-lisp/nadvice.el: Add `depth' property to manage ordering. + (advice--make): Pay attention to `depth'. + (advice--make-1): Don't autoload commands eagerly. + * emacs-lisp/elp.el (elp-instrument-function): + * emacs-lisp/trace.el (trace-function-internal): + * emacs-lisp/debug.el (debug-on-entry): Keep them "first". + + * iswitchb.el (iswitchb-mode): Don't belittle ido. + 2013-12-12 Eli Zaretskii * term/w32-win.el (w32-handle-dropped-file): @@ -15,8 +26,8 @@ 2013-12-12 Nathan Trapuzzano (tiny change) - * progmodes/python.el (python-indent-calculate-indentation): When - determining indentation, don't treat "return", "pass", etc., as + * progmodes/python.el (python-indent-calculate-indentation): + When determining indentation, don't treat "return", "pass", etc., as operators when they are just string constituents. (Bug#15812) 2013-12-12 Juri Linkov @@ -56,7 +67,7 @@ 2013-12-11 Stefan Monnier * emacs-lisp/smie.el (smie-indent--hanging-p): Don't bother matching - comment-start-skip, since it fails when that uses submatch 1 (bug#16041). + comment-start-skip, which fails when that uses submatch 1 (bug#16041). * emulation/cua-base.el (cua-paste): Add `delete-selection' property instead of deleting the selection "by hand" (bug#16098). @@ -137,8 +148,8 @@ 2013-12-10 Teodor Zlatanov - * emacs-lisp/package.el (package-keyword-button-action): Remove - finder.el require dependency. + * emacs-lisp/package.el (package-keyword-button-action): + Remove finder.el require dependency. 2013-12-09 Teodor Zlatanov @@ -165,13 +176,13 @@ 2013-12-09 Cameron Desautels (tiny change) - * progmodes/ruby-mode.el (ruby-forward-string): Document. Handle - caret-delimited strings (Bug#16079). + * progmodes/ruby-mode.el (ruby-forward-string): Document. + Handle caret-delimited strings (Bug#16079). 2013-12-09 Dmitry Gutov - * progmodes/ruby-mode.el (ruby-accurate-end-of-block): When - `ruby-use-smie' is t, use `smie-forward-sexp' instead of + * progmodes/ruby-mode.el (ruby-accurate-end-of-block): + When `ruby-use-smie' is t, use `smie-forward-sexp' instead of `ruby-parse-partial' (Bug#16078). 2013-12-09 Leo Liu @@ -186,8 +197,8 @@ "default" is actually a key in an object literal. (js--same-line): New function. (js--multi-line-declaration-indentation): Use it. - (js--indent-in-array-comp, js--array-comp-indentation): New - functions. + (js--indent-in-array-comp, js--array-comp-indentation): + New functions. (js--proper-indentation): Use them, to handle array comprehension continuations. === modified file 'lisp/emacs-lisp/debug.el' --- lisp/emacs-lisp/debug.el 2013-12-08 21:23:50 +0000 +++ lisp/emacs-lisp/debug.el 2013-12-12 19:47:11 +0000 @@ -798,7 +798,8 @@ (not (special-form-p symbol)))) t nil nil (symbol-name fn))) (list (if (equal val "") fn (intern val))))) - (advice-add function :before #'debug--implement-debug-on-entry) + (advice-add function :before #'debug--implement-debug-on-entry + '((depth . -100))) function) (defun debug--function-list () === modified file 'lisp/emacs-lisp/elp.el' --- lisp/emacs-lisp/elp.el 2013-01-02 16:13:04 +0000 +++ lisp/emacs-lisp/elp.el 2013-12-12 19:47:11 +0000 @@ -251,7 +251,7 @@ ;; Set the symbol's new profiling function definition to run ;; ELP wrapper. (advice-add funsym :around (elp--make-wrapper funsym) - `((name . ,elp--advice-name))))) + `((name . ,elp--advice-name) (depth . -99))))) (defun elp--instrumented-p (sym) (advice-member-p elp--advice-name sym)) === modified file 'lisp/emacs-lisp/nadvice.el' --- lisp/emacs-lisp/nadvice.el 2013-11-17 23:11:27 +0000 +++ lisp/emacs-lisp/nadvice.el 2013-12-12 19:47:11 +0000 @@ -129,8 +129,6 @@ ;; TODO: make it so that interactive spec can be a constant which ;; dynamically checks the advice--car/cdr to do its job. ;; For that, advice-eval-interactive-spec needs to be more faithful. - ;; FIXME: The calls to interactive-form below load autoloaded functions - ;; too eagerly. (let ((fspec (cadr (interactive-form function)))) (when (eq 'function (car-safe fspec)) ;; Macroexpanded lambda? (setq fspec (nth 1 fspec))) @@ -147,19 +145,29 @@ (apply #'make-byte-code 128 byte-code (vector #'apply function main props) stack-depth advice--docstring - (when (or (commandp function) (commandp main)) - (list (advice--make-interactive-form - function main)))))) + (and (or (commandp function) (commandp main)) + (not (and (symbolp main) ;; Don't autoload too eagerly! + (autoloadp (symbol-function main)))) + (list (advice--make-interactive-form + function main)))))) (when adv-sig (puthash advice adv-sig advertised-signature-table)) advice)) (defun advice--make (where function main props) "Build a function value that adds FUNCTION to MAIN at WHERE. WHERE is a symbol to select an entry in `advice--where-alist'." - (let ((desc (assq where advice--where-alist))) - (unless desc (error "Unknown add-function location `%S'" where)) - (advice--make-1 (nth 1 desc) (nth 2 desc) - function main props))) + (let ((fd (or (cdr (assq 'depth props)) 0)) + (md (if (advice--p main) + (or (cdr (assq 'depth (advice--props main))) 0)))) + (if (and md (> fd md)) + ;; `function' should go deeper. + (let ((rest (advice--make where function (advice--cdr main) props))) + (advice--make-1 (aref main 1) (aref main 3) + (advice--car main) rest (advice--props main))) + (let ((desc (assq where advice--where-alist))) + (unless desc (error "Unknown add-function location `%S'" where)) + (advice--make-1 (nth 1 desc) (nth 2 desc) + function main props))))) (defun advice--member-p (function name definition) (let ((found nil)) @@ -216,8 +224,6 @@ ;;;###autoload (defmacro add-function (where place function &optional props) ;; TODO: - ;; - provide some kind of control over ordering. E.g. debug-on-entry, ELP - ;; and tracing want to stay first. ;; - maybe let `where' specify some kind of predicate and use it ;; to implement things like mode-local or eieio-defmethod. ;; Of course, that only makes sense if the predicates of all advices can @@ -245,6 +251,10 @@ PROPS is an alist of additional properties, among which the following have a special meaning: - `name': a string or symbol. It can be used to refer to this piece of advice. +- `depth': a number indicating a preference w.r.t ordering. + The default depth is 0. By convention, a depth of 100 means that + the advice should be innermost (i.e. at the end of the list), + whereas a depth of -100 means that the advice should be outermost. If PLACE is a simple variable, only its global value will be affected. Use (local 'VAR) if you want to apply FUNCTION to VAR buffer-locally. === modified file 'lisp/emacs-lisp/trace.el' --- lisp/emacs-lisp/trace.el 2013-12-11 14:27:15 +0000 +++ lisp/emacs-lisp/trace.el 2013-12-12 19:47:11 +0000 @@ -256,7 +256,7 @@ function :around (trace-make-advice function (or buffer trace-buffer) background (or context (lambda () ""))) - `((name . ,trace-advice-name)))) + `((name . ,trace-advice-name) (depth . -100)))) (defun trace-is-traced (function) (advice-member-p trace-advice-name function)) === modified file 'lisp/iswitchb.el' --- lisp/iswitchb.el 2013-11-11 04:50:56 +0000 +++ lisp/iswitchb.el 2013-12-12 19:47:11 +0000 @@ -1427,7 +1427,8 @@ (add-hook 'minibuffer-setup-hook 'iswitchb-minibuffer-setup) (remove-hook 'minibuffer-setup-hook 'iswitchb-minibuffer-setup))) -(make-obsolete 'iswitchb-mode 'icomplete-mode "24.4") +(make-obsolete 'iswitchb-mode + "use icomplete-mode or ido-mode instead" "24.4") (provide 'iswitchb) ------------------------------------------------------------ revno: 115491 committer: Paul Eggert branch nick: trunk timestamp: Thu 2013-12-12 11:23:25 -0800 message: Avoid undefined behavior with huge regexp interval counts. * regex.c (GET_INTERVAL_COUNT): Rename from 'GET_UNSIGNED_NUMBER', since it's now specialized to interval counts. All uses changed. Do not assume wrapraound on signed integer overflow. (regex_compile): Simplify based on the above changes. diff: === modified file 'src/ChangeLog' --- src/ChangeLog 2013-12-12 18:19:10 +0000 +++ src/ChangeLog 2013-12-12 19:23:25 +0000 @@ -1,3 +1,11 @@ +2013-12-12 Paul Eggert + + Avoid undefined behavior with huge regexp interval counts. + * regex.c (GET_INTERVAL_COUNT): Rename from 'GET_UNSIGNED_NUMBER', + since it's now specialized to interval counts. All uses changed. + Do not assume wrapraound on signed integer overflow. + (regex_compile): Simplify based on the above changes. + 2013-12-12 Eli Zaretskii Support file names on MS-Windows that use characters outside of === modified file 'src/regex.c' --- src/regex.c 2013-12-01 22:33:13 +0000 +++ src/regex.c 2013-12-12 19:23:25 +0000 @@ -1989,7 +1989,7 @@ #endif /* emacs */ /* Get the next unsigned number in the uncompiled pattern. */ -#define GET_UNSIGNED_NUMBER(num) \ +#define GET_INTERVAL_COUNT(num) \ do { \ if (p == pend) \ FREE_STACK_RETURN (REG_EBRACE); \ @@ -1998,13 +1998,11 @@ PATFETCH (c); \ while ('0' <= c && c <= '9') \ { \ - int prev; \ if (num < 0) \ num = 0; \ - prev = num; \ + if (RE_DUP_MAX / 10 - (RE_DUP_MAX % 10 < c - '0') < num) \ + FREE_STACK_RETURN (REG_BADBR); \ num = num * 10 + c - '0'; \ - if (num / 10 != prev) \ - FREE_STACK_RETURN (REG_BADBR); \ if (p == pend) \ FREE_STACK_RETURN (REG_EBRACE); \ PATFETCH (c); \ @@ -3310,18 +3308,18 @@ beg_interval = p; - GET_UNSIGNED_NUMBER (lower_bound); + GET_INTERVAL_COUNT (lower_bound); if (c == ',') - GET_UNSIGNED_NUMBER (upper_bound); + { + GET_INTERVAL_COUNT (upper_bound); + if (upper_bound < lower_bound) + FREE_STACK_RETURN (REG_BADBR); + } else /* Interval such as `{1}' => match exactly once. */ upper_bound = lower_bound; - if (lower_bound < 0 || upper_bound > RE_DUP_MAX - || (upper_bound >= 0 && lower_bound > upper_bound)) - FREE_STACK_RETURN (REG_BADBR); - if (!(syntax & RE_NO_BK_BRACES)) { if (c != '\\') ------------------------------------------------------------ revno: 115490 [merge] fixes bug: http://debbugs.gnu.org/7100 committer: Eli Zaretskii 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) 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. diff: === modified file 'doc/emacs/ChangeLog' --- doc/emacs/ChangeLog 2013-12-12 03:37:38 +0000 +++ doc/emacs/ChangeLog 2013-12-12 18:19:10 +0000 @@ -1,3 +1,8 @@ +2013-12-12 Eli Zaretskii + + * mule.texi (File Name Coding): Document file-name encoding + peculiarities on MS-Windows. + 2013-12-12 Glenn Morris * emacs.texi: Sync direntry with info/dir version. === modified file 'doc/emacs/mule.texi' --- doc/emacs/mule.texi 2013-07-31 13:11:47 +0000 +++ 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. +@cindex file-name encoding, MS-Windows +@vindex 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 +@code{file-name-coding-system}, 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' --- doc/lispref/ChangeLog 2013-12-12 03:37:38 +0000 +++ doc/lispref/ChangeLog 2013-12-12 18:19:10 +0000 @@ -1,3 +1,8 @@ +2013-12-12 Eli Zaretskii + + * nonascii.texi (Encoding and I/O): Document file-name encoding + peculiarities on MS-Windows. + 2013-12-12 Glenn Morris * elisp.texi: Sync direntry with info/dir version. === modified file 'doc/lispref/nonascii.texi' --- doc/lispref/nonascii.texi 2013-10-25 12:23:07 +0000 +++ 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. +@cindex 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 +@code{file-name-coding-system} is largely ignored. Lisp applications +that need to encode or decode file names on the Lisp level should use +@code{utf-8} coding-system when @code{system-type} is +@code{windows-nt}; 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' --- etc/ChangeLog 2013-11-23 14:19:32 +0000 +++ etc/ChangeLog 2013-12-12 18:19:10 +0000 @@ -1,3 +1,8 @@ +2013-12-12 Eli Zaretskii + + * NEWS: Mention support on MS-Windows of file names outside of the + current locale. + 2013-11-23 Xue Fuqiao * TODO: Minor update. === modified file 'etc/NEWS' --- etc/NEWS 2013-12-12 00:42:16 +0000 +++ 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' --- lisp/ChangeLog 2013-12-12 05:37:09 +0000 +++ lisp/ChangeLog 2013-12-12 18:19:10 +0000 @@ -1,3 +1,12 @@ +2013-12-12 Eli Zaretskii + + * 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 * progmodes/python.el (python-indent-context) === modified file 'lisp/dnd.el' --- lisp/dnd.el 2013-01-01 09:11:05 +0000 +++ 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' --- lisp/net/browse-url.el 2013-09-05 03:30:07 +0000 +++ 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' --- lisp/startup.el 2013-12-01 02:04:46 +0000 +++ 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' --- lisp/term/w32-win.el 2013-11-01 09:04:16 +0000 +++ 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' --- nt/ChangeLog 2013-11-27 06:15:06 +0000 +++ nt/ChangeLog 2013-12-12 18:19:10 +0000 @@ -1,3 +1,10 @@ +2013-12-12 Eli Zaretskii + + * 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 * README.W32: === modified file 'nt/inc/dirent.h' --- nt/inc/dirent.h 2013-10-12 13:11:14 +0000 +++ 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' --- nt/inc/ms-w32.h 2013-11-29 01:22:40 +0000 +++ 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' --- src/ChangeLog 2013-12-12 14:26:06 +0000 +++ src/ChangeLog 2013-12-12 18:19:10 +0000 @@ -1,3 +1,128 @@ +2013-12-12 Eli Zaretskii + + 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) + 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 * font.h (struct font_entity) [HAVE_NS]: New field to record === modified file 'src/coding.c' --- src/coding.c 2013-11-18 16:29:49 +0000 +++ 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' --- src/coding.h 2013-11-04 17:30:33 +0000 +++ 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' --- src/dired.c 2013-09-21 11:48:19 +0000 +++ 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' --- src/emacs.c 2013-12-08 12:18:13 +0000 +++ src/emacs.c 2013-12-09 17:20:34 +0000 @@ -36,6 +36,7 @@ #ifdef WINDOWSNT #include #include +#include #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' --- src/fileio.c 2013-11-27 16:08:53 +0000 +++ 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' --- src/filelock.c 2013-09-23 07:12:01 +0000 +++ 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' --- src/gnutls.c 2013-11-30 13:31:39 +0000 +++ 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 @@ -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' --- src/image.c 2013-12-01 14:34:05 +0000 +++ 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' --- src/msdos.c 2013-11-04 06:09:03 +0000 +++ 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' --- src/msdos.h 2013-09-13 15:03:51 +0000 +++ 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' --- src/sysdep.c 2013-10-07 08:05:00 +0000 +++ 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' --- src/termcap.c 2013-08-11 01:30:20 +0000 +++ 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' --- src/unexw32.c 2013-04-16 18:08:03 +0000 +++ 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' --- src/w32.c 2013-11-04 06:09:03 +0000 +++ 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 /* 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' --- src/w32.h 2013-08-29 15:32:04 +0000 +++ 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 === modified file 'src/w32fns.c' --- src/w32fns.c 2013-12-02 17:28:17 +0000 +++ src/w32fns.c 2013-12-11 17:17:49 +0000 @@ -51,6 +51,7 @@ #ifdef WINDOWSNT #include "w32heap.h" +#include #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' --- src/w32notify.c 2013-11-28 19:40:15 +0000 +++ 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' --- src/w32proc.c 2013-11-28 19:40:15 +0000 +++ src/w32proc.c 2013-12-07 17:21:57 +0000 @@ -30,6 +30,7 @@ #include #include #include +#include /* must include CRT headers *before* config.h */ #include @@ -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' --- src/w32term.c 2013-12-11 15:06:04 +0000 +++ 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); } ------------------------------------------------------------ revno: 115489 committer: Dmitry Antipov branch nick: trunk timestamp: Thu 2013-12-12 18:26:06 +0400 message: * font.h (struct font_entity) [HAVE_NS]: New field to record font driver which was used to create this entity. (struct font) [HAVE_WINDOW_SYSTEM]: New field to record frame where the font was opened. (font_close_object): Add prototype. * font.c (font_make_entity) [HAVE_NS]: Zero out driver field. (font_close_object): Not static any more. Lost frame arg. Adjust comment and users. * alloc.c (cleanup_vector): Call font_close_object to adjust per-frame font counters correctly. If HAVE_NS, also call driver-specific cleanup for font-entity objects. * ftfont.c (ftfont_open): * nsfont.m (nsfont_open): * w32font.c (w32font_open_internal): * xfont.c (xfont_open): * xftfont.c (xftfont_open): Save frame pointer in font object. * macfont.m (macfont_open): Likewise. (macfont_descriptor_entity): Save driver pointer to be able to call its free_entity routine when font-entity is swept. * ftxfont.c (ftxfont_open): Add eassert because frame pointer should be saved by ftfont_driver.open. diff: === modified file 'src/ChangeLog' --- src/ChangeLog 2013-12-12 08:39:41 +0000 +++ src/ChangeLog 2013-12-12 14:26:06 +0000 @@ -1,5 +1,29 @@ 2013-12-12 Dmitry Antipov + * font.h (struct font_entity) [HAVE_NS]: New field to record + font driver which was used to create this entity. + (struct font) [HAVE_WINDOW_SYSTEM]: New field to record + frame where the font was opened. + (font_close_object): Add prototype. + * font.c (font_make_entity) [HAVE_NS]: Zero out driver field. + (font_close_object): Not static any more. Lost frame arg. + Adjust comment and users. + * alloc.c (cleanup_vector): Call font_close_object to adjust + per-frame font counters correctly. If HAVE_NS, also call + driver-specific cleanup for font-entity objects. + * ftfont.c (ftfont_open): + * nsfont.m (nsfont_open): + * w32font.c (w32font_open_internal): + * xfont.c (xfont_open): + * xftfont.c (xftfont_open): Save frame pointer in font object. + * macfont.m (macfont_open): Likewise. + (macfont_descriptor_entity): Save driver pointer to be able + to call its free_entity routine when font-entity is swept. + * ftxfont.c (ftxfont_open): Add eassert because frame + pointer should be saved by ftfont_driver.open. + +2013-12-12 Dmitry Antipov + * xterm.c (x_make_frame_visible): Restore hack which is needed when input polling is used. This is still meaningful for Cygwin, see http://lists.gnu.org/archive/html/emacs-devel/2013-12/msg00351.html. === modified file 'src/alloc.c' --- src/alloc.c 2013-12-09 08:23:01 +0000 +++ src/alloc.c 2013-12-12 14:26:06 +0000 @@ -2874,10 +2874,22 @@ static void cleanup_vector (struct Lisp_Vector *vector) { - if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_FONT) - && ((vector->header.size & PSEUDOVECTOR_SIZE_MASK) - == FONT_OBJECT_MAX)) - ((struct font *) vector)->driver->close ((struct font *) vector); + if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_FONT)) + { + ptrdiff_t size = vector->header.size & PSEUDOVECTOR_SIZE_MASK; + Lisp_Object obj = make_lisp_ptr (vector, Lisp_Vectorlike); + + if (size == FONT_OBJECT_MAX) + font_close_object (obj); +#ifdef HAVE_NS + else if (size == FONT_ENTITY_MAX) + { + struct font_entity *entity = (struct font_entity *) vector; + if (entity->driver && entity->driver->free_entity) + entity->driver->free_entity (obj); + } +#endif /* HAVE_NS */ + } } /* Reclaim space used by unmarked vectors. */ === modified file 'src/font.c' --- src/font.c 2013-12-10 03:36:36 +0000 +++ src/font.c 2013-12-12 14:26:06 +0000 @@ -173,6 +173,9 @@ allocate_pseudovector (VECSIZE (struct font_entity), FONT_ENTITY_MAX, PVEC_FONT)); XSETFONT (font_entity, entity); +#ifdef HAVE_NS + entity->driver = NULL; +#endif return font_entity; } @@ -2881,10 +2884,10 @@ } -/* Close FONT_OBJECT that is opened on frame F. */ +/* Close FONT_OBJECT. */ -static void -font_close_object (struct frame *f, Lisp_Object font_object) +void +font_close_object (Lisp_Object font_object) { struct font *font = XFONT_OBJECT (font_object); @@ -2894,8 +2897,9 @@ FONT_ADD_LOG ("close", font_object, Qnil); font->driver->close (font); #ifdef HAVE_WINDOW_SYSTEM - eassert (FRAME_DISPLAY_INFO (f)->n_fonts); - FRAME_DISPLAY_INFO (f)->n_fonts--; + eassert (font->frame); + eassert (FRAME_DISPLAY_INFO (font->frame)->n_fonts); + FRAME_DISPLAY_INFO (font->frame)->n_fonts--; #endif } @@ -4548,11 +4552,11 @@ } DEFUN ("close-font", Fclose_font, Sclose_font, 1, 2, 0, - doc: /* Close FONT-OBJECT. */) + doc: /* Close FONT-OBJECT. Optional FRAME is unused. */) (Lisp_Object font_object, Lisp_Object frame) { CHECK_FONT_OBJECT (font_object); - font_close_object (decode_live_frame (frame), font_object); + font_close_object (font_object); return Qnil; } @@ -4887,7 +4891,7 @@ /* As font_object is still in FONT_OBJLIST of the entity, we can't close it now. Perhaps, we should manage font-objects by `reference-count'. */ - font_close_object (f, font_object); + font_close_object (font_object); #endif return info; } === modified file 'src/font.h' --- src/font.h 2013-12-04 13:35:41 +0000 +++ src/font.h 2013-12-12 14:26:06 +0000 @@ -265,6 +265,9 @@ { struct vectorlike_header header; Lisp_Object props[FONT_ENTITY_MAX]; +#ifdef HAVE_NS + struct font_driver *driver; +#endif }; /* A value which may appear in the member `encoding' of struct font @@ -316,6 +319,9 @@ #if defined (HAVE_WINDOW_SYSTEM) + /* The frame where the font was opened. */ + struct frame *frame; + /* Vertical pixel width of the underline. If is zero if that information is not in the font. */ int underline_thickness; @@ -735,6 +741,7 @@ extern Lisp_Object font_make_entity (void); extern Lisp_Object font_make_object (int, Lisp_Object, int); +extern void font_close_object (Lisp_Object); extern Lisp_Object find_font_encoding (Lisp_Object); extern int font_registry_charsets (Lisp_Object, struct charset **, === modified file 'src/ftfont.c' --- src/ftfont.c 2013-12-04 13:35:41 +0000 +++ src/ftfont.c 2013-12-12 14:26:06 +0000 @@ -1236,6 +1236,7 @@ ASET (font_object, FONT_FILE_INDEX, filename); ASET (font_object, FONT_FORMAT_INDEX, ftfont_font_format (NULL, filename)); font = XFONT_OBJECT (font_object); + font->frame = f; ftfont_info = (struct ftfont_info *) font; ftfont_info->ft_size = ft_face->size; ftfont_info->index = XINT (idx); === modified file 'src/ftxfont.c' --- src/ftxfont.c 2013-10-29 16:08:08 +0000 +++ src/ftxfont.c 2013-12-12 14:26:06 +0000 @@ -255,6 +255,7 @@ if (NILP (font_object)) return Qnil; font = XFONT_OBJECT (font_object); + eassert (font->frame == f); font->driver = &ftxfont_driver; return font_object; } === modified file 'src/macfont.m' --- src/macfont.m 2013-11-07 22:21:08 +0000 +++ src/macfont.m 2013-12-12 14:26:06 +0000 @@ -882,6 +882,7 @@ CFStringRef name; entity = font_make_entity (); + XFONT_ENTITY (entity)->driver = &macfont_driver; ASET (entity, FONT_TYPE_INDEX, macfont_driver.type); ASET (entity, FONT_REGISTRY_INDEX, Qiso10646_1); @@ -2491,6 +2492,7 @@ ASET (font_object, FONT_FULLNAME_INDEX, AREF (font_object, FONT_NAME_INDEX)); font = XFONT_OBJECT (font_object); + font->frame = f; font->pixel_size = size; font->driver = &macfont_driver; font->encoding_charset = font->repertory_charset = -1; === modified file 'src/nsfont.m' --- src/nsfont.m 2013-12-04 13:08:30 +0000 +++ src/nsfont.m 2013-12-12 14:26:06 +0000 @@ -805,6 +805,7 @@ return Qnil; /* FIXME: other terms do, but return Qnil causes segfault */ } + font->frame = f; font_info->glyphs = xzalloc (0x100 * sizeof *font_info->glyphs); font_info->metrics = xzalloc (0x100 * sizeof *font_info->metrics); === modified file 'src/w32font.c' --- src/w32font.c 2013-12-04 13:08:30 +0000 +++ src/w32font.c 2013-12-12 14:26:06 +0000 @@ -999,6 +999,7 @@ = DECODE_SYSTEM (build_string (logfont.lfFaceName)); } + font->frame = f; font->max_width = w32_font->metrics.tmMaxCharWidth; /* Parts of Emacs display assume that height = ascent + descent... so height is defined later, after ascent and descent. === modified file 'src/xfont.c' --- src/xfont.c 2013-11-04 06:09:03 +0000 +++ src/xfont.c 2013-12-12 14:26:06 +0000 @@ -806,6 +806,7 @@ ASET (font_object, FONT_FILE_INDEX, Qnil); ASET (font_object, FONT_FORMAT_INDEX, Qx); font = XFONT_OBJECT (font_object); + font->frame = f; ((struct xfont_info *) font)->xfont = xfont; ((struct xfont_info *) font)->display = FRAME_X_DISPLAY (f); font->pixel_size = pixel_size; === modified file 'src/xftfont.c' --- src/xftfont.c 2013-10-27 05:30:34 +0000 +++ src/xftfont.c 2013-12-12 14:26:06 +0000 @@ -365,6 +365,7 @@ ASET (font_object, FONT_FORMAT_INDEX, ftfont_font_format (xftfont->pattern, filename)); font = XFONT_OBJECT (font_object); + font->frame = f; font->pixel_size = size; font->driver = &xftfont_driver; font->encoding_charset = font->repertory_charset = -1; ------------------------------------------------------------ revno: 115488 committer: Michael Albinus branch nick: trunk timestamp: Thu 2013-12-12 10:57:56 +0100 message: * tramp.texi (direntry): Use ssh but rsh. (all): Encode all environment variable names with @env{...}. (Bug Reports): Refer to Testing node. diff: === modified file 'doc/misc/ChangeLog' --- doc/misc/ChangeLog 2013-12-12 03:37:38 +0000 +++ doc/misc/ChangeLog 2013-12-12 09:57:56 +0000 @@ -1,3 +1,9 @@ +2013-12-12 Michael Albinus + + * tramp.texi (direntry): Use ssh but rsh. + (all): Encode all environment variable names with @env{...}. + (Bug Reports): Refer to Testing node. + 2013-12-12 Glenn Morris * autotype.texi, cc-mode.texi, ediff.texi, ert.texi: === modified file 'doc/misc/tramp.texi' --- doc/misc/tramp.texi 2013-12-12 03:37:38 +0000 +++ doc/misc/tramp.texi 2013-12-12 09:57:56 +0000 @@ -84,7 +84,7 @@ @dircategory @value{emacsname} network features @direntry * TRAMP: (tramp). Transparent Remote Access, Multiple Protocol - @value{emacsname} remote file access via rsh and rcp. + @value{emacsname} remote file access via ssh and scp. @end direntry @titlepage @@ -240,6 +240,7 @@ @end detailmenu @end menu + @node Overview @chapter An overview of @value{tramp} @cindex overview @@ -497,6 +498,7 @@ @include trampinst.texi @end ifset + @node Configuration @chapter Configuring @value{tramp} for use @cindex configuration @@ -1005,7 +1007,7 @@ Android devices. The Android Debug Bridge must be installed locally. Some GNU/Linux distributions offer it for installation, otherwise it can be installed as part of the Android SDK. If the @command{adb} -program is not found via the @code{$PATH} environment variable, the +program is not found via the @env{PATH} environment variable, the variable @var{tramp-adb-program} must point to its absolute path. Tramp does not connect Android devices to @command{adb}. This must be @@ -2083,7 +2085,7 @@ with @samp{192.168.0.26} being the IP address of your Android device (@pxref{Predefined connection information}). -The user settings for the @code{$PATH} environment variable must be +The user settings for the @env{PATH} environment variable must be preserved. It has also been reported, that the commands in @file{/system/xbin} are better suited than the ones in @file{/system/bin}. Add these setting: @@ -2918,6 +2920,13 @@ @uref{http://lists.gnu.org/mailman/listinfo/tramp-devel/, the @value{tramp} Mail Subscription Page}. +@ifset emacs +@ifset installchapter +Before sending a bug report, you could check whether @value{tramp} +works at all. Run the test suite on your local machine, @ref{Testing}. +@end ifset +@end ifset + @findex tramp-bug To report a bug in @value{tramp}, you should execute @kbd{M-x tramp-bug}. This will automatically generate a buffer with the details @@ -3917,18 +3926,22 @@ @end ifset @end itemize + @node GNU Free Documentation License @appendix GNU Free Documentation License @include doclicense.texi + @node Function Index @unnumbered Function Index @printindex fn + @node Variable Index @unnumbered Variable Index @printindex vr + @node Concept Index @unnumbered Concept Index @printindex cp ------------------------------------------------------------ revno: 115487 committer: Glenn Morris branch nick: trunk timestamp: Thu 2013-12-12 01:39:13 -0800 message: * make-dist: Distribute new build-aux files. diff: === modified file 'ChangeLog' --- ChangeLog 2013-12-12 09:02:27 +0000 +++ ChangeLog 2013-12-12 09:39:13 +0000 @@ -12,6 +12,8 @@ Instead of info/dir entries, check @dircategory in info/*.info. * make-dist: Use `info' rule rather than `info-real'. No more info/COPYING (not even the right license for info/ files). + Distribute new build-aux files. + * info/: Remove from repository. 2013-12-11 Glenn Morris === modified file 'make-dist' --- make-dist 2013-12-12 08:54:21 +0000 +++ make-dist 2013-12-12 09:39:13 +0000 @@ -340,10 +340,12 @@ ln Makefile.in ../${tempdir}/leim/Makefile.in ln leim-ext.el ../${tempdir}/leim/leim-ext.el) +## FIXME Can we not just use the "find -type f" method for this one? echo "Making links to \`build-aux'" (cd build-aux ln compile config.guess config.sub depcomp msys-to-w32 ../${tempdir}/build-aux - ln install-sh missing move-if-change update-subdirs ../${tempdir}/build-aux) + ln install-sh missing move-if-change update-subdirs ../${tempdir}/build-aux + ln dir_top make-info-dir ../${tempdir}/build-aux) echo "Making links to \`build-aux/snippet'" (cd build-aux/snippet ------------------------------------------------------------ revno: 115486 committer: Glenn Morris branch nick: trunk timestamp: Thu 2013-12-12 01:20:37 -0800 message: * .bzrignore: Fix comment typo. diff: === modified file '.bzrignore' --- .bzrignore 2013-12-12 08:54:21 +0000 +++ .bzrignore 2013-12-12 09:20:37 +0000 @@ -82,7 +82,7 @@ !etc/refcards/Makefile etc/refcards/*.aux etc/refcards/*.log -# FIXME this ignore info and everything in it. +# FIXME this ignores info and everything in it. # It would be better to only ignore: # info/ (i.e., just the directory) and info/*.info. info/ ------------------------------------------------------------ Use --include-merged or -n0 to see merged revisions.